From 15f5cffc3026a5faed7046ae7d75ec6b56cf4a4c Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Thu, 27 May 2021 15:38:52 -0400 Subject: JSON Parser and Writer (#1859) * #include an absolute path didn't work - because paths were taken to always be relative. * WIP JSONWriter/JSONParser. * Checking different Layout styles for JSON. * Add slang-json-parser.h/.cpp --- source/compiler-core/slang-json-parser.cpp | 384 +++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 source/compiler-core/slang-json-parser.cpp (limited to 'source/compiler-core/slang-json-parser.cpp') diff --git a/source/compiler-core/slang-json-parser.cpp b/source/compiler-core/slang-json-parser.cpp new file mode 100644 index 000000000..478b02fb8 --- /dev/null +++ b/source/compiler-core/slang-json-parser.cpp @@ -0,0 +1,384 @@ +// slang-json-parser.cpp +#include "slang-json-parser.h" + +#include "slang-json-diagnostics.h" + +/* +https://www.json.org/json-en.html +*/ + +namespace Slang { + +SlangResult JSONParser::_parseObject() +{ + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::LBrace)); + + m_listener->startObject(); + + if (m_lexer->advanceIf(JSONTokenType::RBrace)) + { + m_listener->endObject(); + return SLANG_OK; + } + + while (true) + { + JSONToken keyToken; + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::StringLiteral, keyToken)); + m_listener->addLexemeKey(m_lexer->getLexeme(keyToken)); + + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::Colon)); + + SLANG_RETURN_ON_FAIL(_parseValue()); + if (m_lexer->advanceIf(JSONTokenType::Comma)) + { + continue; + } + + break; + } + + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::RBrace)); + m_listener->endObject(); + return SLANG_OK; +} + +SlangResult JSONParser::_parseArray() +{ + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::LBracket)); + + m_listener->startArray(); + + if (m_lexer->advanceIf(JSONTokenType::RBracket)) + { + m_listener->endArray(); + return SLANG_OK; + } + + while (true) + { + SLANG_RETURN_ON_FAIL(_parseValue()); + if (m_lexer->advanceIf(JSONTokenType::Comma)) + { + continue; + } + break; + } + + SLANG_RETURN_ON_FAIL(m_lexer->expect(JSONTokenType::RBracket)); + m_listener->endArray(); + return SLANG_OK; +} + +SlangResult JSONParser::_parseValue() +{ + switch (m_lexer->peekType()) + { + case JSONTokenType::True: + case JSONTokenType::False: + case JSONTokenType::Null: + case JSONTokenType::IntegerLiteral: + case JSONTokenType::FloatLiteral: + case JSONTokenType::StringLiteral: + { + m_listener->addLexemeValue(m_lexer->peekType(), m_lexer->peekLexeme()); + m_lexer->advance(); + return SLANG_OK; + } + case JSONTokenType::LBracket: + { + return _parseArray(); + } + case JSONTokenType::LBrace: + { + return _parseObject(); + } + default: + { + m_sink->diagnose(m_lexer->peekLoc(), JSONDiagnostics::unexpectedToken, getJSONTokenAsText(m_lexer->peekType())); + return SLANG_FAIL; + } + case JSONTokenType::Invalid: + { + // It's a lex error, so just fail + return SLANG_FAIL; + } + } +} + +SlangResult JSONParser::parse(JSONLexer* lexer, SourceView* sourceView, JSONListener* listener, DiagnosticSink* sink) +{ + m_sourceView = sourceView; + m_lexer = lexer; + m_listener = listener; + m_sink = sink; + + SLANG_RETURN_ON_FAIL(_parseValue()); + + return m_lexer->expect(JSONTokenType::EndOfFile); +} + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + JSONWriter + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +Index JSONWriter::_getLineLengthAfterIndent() +{ + if (m_emittedIndent < 0) + { + return 0; + } + + Index lineLength = m_builder.getLength() - m_lineStart; + return lineLength - m_emittedIndent * m_indentCharCount; +} + + +void JSONWriter::_emitIndent() +{ + m_builder.appendRepeatedChar(m_indentChar, m_currentIndent * m_indentCharCount); + m_emittedIndent = m_currentIndent; + SLANG_ASSERT(m_emittedIndent >= 0); +} + +void JSONWriter::_maybeEmitIndent() +{ + if (m_emittedIndent < 0) + { + _emitIndent(); + } +} + +void JSONWriter::_nextLine() +{ + m_builder << "\n"; + m_lineStart = m_builder.getLength(); + m_lineIndex++; + m_emittedIndent = -1; +} + +void JSONWriter::_maybeNextLine() +{ + // Nothing has been emitted, because nothing has been indented, and we must indent before an emit + if (m_emittedIndent < 0) + { + } + else + { + _nextLine(); + } +} + +void JSONWriter::_handleFormat(Location loc) +{ + switch (m_format) + { + case IndentationStyle::Allman: + { + if (isComma(loc)) + { + _maybeNextLine(); + } + else + { + if (isBefore(loc)) + { + _maybeNextLine(); + if (isClose(loc)) + { + _dedent(); + } + } + else + { + _maybeNextLine(); + if (isOpen(loc)) + { + _indent(); + } + } + } + break; + } + case IndentationStyle::KNR: + { + if (isComma(loc)) + { + if (loc == Location::FieldComma || + (m_lineLengthLimit > 0 && _getLineLengthAfterIndent() > m_lineLengthLimit)) + { + _maybeNextLine(); + } + } + else + { + if (isBefore(loc)) + { + if (isClose(loc)) + { + _maybeNextLine(); + _dedent(); + } + } + else + { + _maybeNextLine(); + if (isOpen(loc)) + { + _indent(); + } + } + } + break; + } + } +} + +void JSONWriter::_maybeEmitComma() +{ + if (m_state.m_flags & State::Flag::HasPrevious) + { + _maybeEmitIndent(); + m_builder << ", "; + _handleFormat(Location::Comma); + } +} + +void JSONWriter::_maybeEmitFieldComma() +{ + if (m_state.m_flags & State::Flag::HasPrevious) + { + _maybeEmitIndent(); + m_builder << ", "; + _handleFormat(Location::FieldComma); + } +} + +void JSONWriter::startObject() +{ + SLANG_ASSERT(m_state.canEmitValue()); + + _maybeEmitComma(); + + _handleFormat(Location::BeforeOpenObject); + _maybeEmitIndent(); + m_builder << "{"; + _handleFormat(Location::AfterOpenObject); + + m_state.m_flags |= State::Flag::HasPrevious; + m_state.m_flags &= State::Flag::HasKey; + + m_stack.add(m_state); + + m_state.m_kind = State::Kind::Object; + m_state.m_flags = 0; +} + +void JSONWriter::endObject() +{ + SLANG_ASSERT(m_state.m_kind == State::Kind::Object); + + _handleFormat(Location::BeforeCloseObject); + _maybeEmitIndent(); + m_builder << "}"; + _handleFormat(Location::AfterCloseObject); + + m_state = m_stack.getLast(); + m_stack.removeLast(); +} + +void JSONWriter::startArray() +{ + SLANG_ASSERT(m_state.canEmitValue()); + + _maybeEmitComma(); + + _handleFormat(Location::BeforeOpenArray); + _maybeEmitIndent(); + m_builder << "["; + _handleFormat(Location::AfterOpenArray); + + m_state.m_flags |= State::Flag::HasPrevious; + m_state.m_flags &= State::Flag::HasKey; + + m_stack.add(m_state); + + m_state.m_kind = State::Kind::Array; + m_state.m_flags = 0; +} + +void JSONWriter::endArray() +{ + SLANG_ASSERT(m_state.m_kind == State::Kind::Array); + + _handleFormat(Location::BeforeCloseArray); + _maybeEmitIndent(); + m_builder << "]"; + _handleFormat(Location::AfterCloseArray); + + m_state = m_stack.getLast(); + m_stack.removeLast(); +} + +void JSONWriter::addLexemeKey(const UnownedStringSlice& key) +{ + SLANG_ASSERT(m_state.m_kind == State::Kind::Object && (m_state.m_flags & State::Flag::HasKey) == 0); + + _maybeEmitFieldComma(); + + // It should be quoted + SLANG_ASSERT(key.getLength() >= 2 && key[0] == '"' && key[key.getLength() - 1] == '"'); + + _maybeEmitIndent(); + m_builder << key << " : "; + + m_state.m_flags |= State::Flag::HasKey; + // We don't want it to emit a , after the : + m_state.m_flags &= ~State::Flag::HasPrevious; +} + +void JSONWriter::addLexemeValue(JSONTokenType type, const UnownedStringSlice& value) +{ + SLANG_ASSERT(m_state.canEmitValue()); + + _maybeEmitComma(); + _maybeEmitIndent(); + + switch (type) + { + case JSONTokenType::IntegerLiteral: + case JSONTokenType::FloatLiteral: + case JSONTokenType::StringLiteral: + { + m_builder << value; + break; + } + case JSONTokenType::True: + { + m_builder << UnownedStringSlice::fromLiteral("true"); + break; + } + case JSONTokenType::False: + { + m_builder << UnownedStringSlice::fromLiteral("false"); + break; + } + case JSONTokenType::Null: + { + m_builder << UnownedStringSlice::fromLiteral("null"); + break; + } + default: + { + SLANG_ASSERT(!"Can only emit values"); + } + } + // We have a previous + m_state.m_flags |= State::Flag::HasPrevious; + // We don't have a key + m_state.m_flags &= ~State::Flag::HasKey; +} + +} // namespace Slang -- cgit v1.2.3