Take advantage of keyword tokenization
[yangtools.git] / yang / yang-parser-rfc7950 / src / main / java / org / opendaylight / yangtools / yang / parser / rfc7950 / repo / StatementContextVisitor.java
index aaed74b1c5c1e206225566c9d1eddb947360ab53..8f2659f97ed44f509b9ff07d08ea03b5051dcf92 100644 (file)
@@ -10,26 +10,29 @@ package org.opendaylight.yangtools.yang.parser.rfc7950.repo;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
+import com.google.common.base.VerifyException;
+import java.util.Optional;
+import org.antlr.v4.runtime.Token;
 import org.antlr.v4.runtime.tree.ParseTree;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.KeywordContext;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.ArgumentContext;
+import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.KeywordContext;
+import org.opendaylight.yangtools.yang.parser.antlr.YangStatementParser.StatementContext;
 import org.opendaylight.yangtools.yang.parser.spi.source.DeclarationInTextSource;
 import org.opendaylight.yangtools.yang.parser.spi.source.PrefixToModule;
 import org.opendaylight.yangtools.yang.parser.spi.source.QNameToStatementDefinition;
-import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementWriter.ResumedStatement;
 
 class StatementContextVisitor {
     private final QNameToStatementDefinition stmtDef;
+    private final ArgumentContextUtils utils;
     private final StatementWriter writer;
-    private final YangVersion yangVersion;
     private final PrefixToModule prefixes;
     private final String sourceName;
 
@@ -37,7 +40,7 @@ class StatementContextVisitor {
             final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes, final YangVersion yangVersion) {
         this.writer = requireNonNull(writer);
         this.stmtDef = requireNonNull(stmtDef);
-        this.yangVersion = requireNonNull(yangVersion);
+        this.utils = ArgumentContextUtils.forVersion(yangVersion);
         this.sourceName = sourceName;
         this.prefixes = prefixes;
     }
@@ -53,64 +56,81 @@ class StatementContextVisitor {
      *
      * @param prefixes collection of all relevant prefix mappings supplied for actual parsing phase
      * @param stmtDef collection of all relevant statement definition mappings provided for actual parsing phase
-     * @param keywordText statement keyword text to parse from source
+     * @param keyword statement keyword text to parse from source
      * @param ref Source reference
      * @return valid QName for declared statement to be written, or null
      */
-    QName getValidStatementDefinition(final String keywordText, final StatementSourceReference ref) {
-        final int firstColon = keywordText.indexOf(':');
-        if (firstColon == -1) {
-            final StatementDefinition def = stmtDef.get(QName.create(YangConstants.RFC6020_YIN_NAMESPACE, keywordText));
-            return def != null ? def.getStatementName() : null;
-        }
-
-        SourceException.throwIf(firstColon == keywordText.length() - 1
-                || keywordText.indexOf(':', firstColon + 1) != -1, ref, "Malformed statement '%s'", keywordText);
+    QName getValidStatementDefinition(final KeywordContext keyword, final StatementSourceReference ref) {
+        switch (keyword.getChildCount()) {
+            case 1:
+                final StatementDefinition def = stmtDef.get(QName.create(YangConstants.RFC6020_YIN_MODULE,
+                    keyword.getChild(0).getText()));
+                return def != null ? def.getStatementName() : null;
+            case 3:
+                if (prefixes == null) {
+                    // No prefixes to look up from
+                    return null;
+                }
 
-        if (prefixes == null) {
-            // No prefixes to look up from
-            return null;
-        }
+                final String prefix = keyword.getChild(0).getText();
+                final QNameModule qNameModule = prefixes.get(prefix);
+                if (qNameModule == null) {
+                    // Failed to look the namespace
+                    return null;
+                }
 
-        final String prefix = keywordText.substring(0, firstColon);
-        final QNameModule qNameModule = prefixes.get(prefix);
-        if (qNameModule == null) {
-            // Failed to look the namespace
-            return null;
+                final String localName = keyword.getChild(2).getText();
+                final StatementDefinition foundStmtDef = resolveStatement(qNameModule, localName);
+                return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
+            default:
+                throw new VerifyException("Unexpected shape of " + keyword);
         }
-
-        final String localName = keywordText.substring(firstColon + 1);
-        final StatementDefinition foundStmtDef = resolveStatement(qNameModule, localName);
-        return foundStmtDef != null ? foundStmtDef.getStatementName() : null;
     }
 
     StatementDefinition resolveStatement(final QNameModule module, final String localName) {
         return stmtDef.get(QName.create(module, localName));
     }
 
-    private void processStatement(final int myOffset, final StatementContext ctx) {
-        final String keywordTxt = verifyNotNull(ctx.getChild(KeywordContext.class, 0)).getText();
-        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getStart().getLine(),
-            ctx.getStart().getCharPositionInLine());
-        final QName def = getValidStatementDefinition(keywordTxt, ref);
+    // Normal entry point, checks for potential resume
+    private boolean processStatement(final int myOffset, final StatementContext ctx) {
+        final Optional<? extends ResumedStatement> optResumed = writer.resumeStatement(myOffset);
+        if (optResumed.isPresent()) {
+            final ResumedStatement resumed = optResumed.get();
+            return resumed.isFullyDefined() || doProcessStatement(ctx, resumed.getSourceReference());
+        }
+        return processNewStatement(myOffset, ctx);
+    }
+
+    // Slow-path allocation of a new statement
+    private boolean processNewStatement(final int myOffset, final StatementContext ctx) {
+        final Token start = ctx.getStart();
+        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, start.getLine(),
+            start.getCharPositionInLine());
+        final QName def = getValidStatementDefinition(verifyNotNull(ctx.getChild(KeywordContext.class, 0)), ref);
         if (def == null) {
-            return;
+            return false;
         }
 
         final ArgumentContext argumentCtx = ctx.getChild(ArgumentContext.class, 0);
-        final String argument = argumentCtx == null ? null
-                : ArgumentContextUtils.stringFromStringContext(argumentCtx, yangVersion, ref);
+        final String argument = argumentCtx == null ? null : utils.stringFromStringContext(argumentCtx, ref);
         writer.startStatement(myOffset, def, argument, ref);
+        return doProcessStatement(ctx, ref);
+    }
 
+    // Actual processing
+    private boolean doProcessStatement(final StatementContext ctx, final StatementSourceReference ref) {
+        int childOffset = 0;
+        boolean fullyDefined = true;
         if (ctx.children != null) {
-            int childOffset = 0;
             for (ParseTree s : ctx.children) {
-                if (s instanceof StatementContext) {
-                    processStatement(childOffset++, (StatementContext) s);
+                if (s instanceof StatementContext && !processStatement(childOffset++, (StatementContext) s)) {
+                    fullyDefined = false;
                 }
             }
         }
 
+        writer.storeStatement(childOffset, fullyDefined);
         writer.endStatement(ref);
+        return fullyDefined;
     }
 }