summaryrefslogtreecommitdiffstats
path: root/source/compiler-core/slang-json-parser.cpp
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2021-05-27 15:38:52 -0400
committerGitHub <noreply@github.com>2021-05-27 12:38:52 -0700
commit15f5cffc3026a5faed7046ae7d75ec6b56cf4a4c (patch)
treee6c59288e453ac3eb788068a155e7aa607399009 /source/compiler-core/slang-json-parser.cpp
parent969943f4b751d3cad8ac548f9cf0f65406935bad (diff)
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
Diffstat (limited to 'source/compiler-core/slang-json-parser.cpp')
-rw-r--r--source/compiler-core/slang-json-parser.cpp384
1 files changed, 384 insertions, 0 deletions
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