|
|
@@ -0,0 +1,207 @@
|
|
|
+package net.ranides.assira.grammar.impl;
|
|
|
+
|
|
|
+import net.ranides.assira.collection.sets.OpenSet;
|
|
|
+import net.ranides.assira.generic.Ref;
|
|
|
+import net.ranides.assira.grammar.G4Node;
|
|
|
+import net.ranides.assira.grammar.G4Parser;
|
|
|
+import net.ranides.assira.reflection.IClass;
|
|
|
+import net.ranides.assira.reflection.IMethod;
|
|
|
+import net.ranides.assira.reflection.IReflectiveException;
|
|
|
+import net.ranides.assira.text.StringUtils;
|
|
|
+import org.antlr.v4.runtime.ANTLRErrorListener;
|
|
|
+import org.antlr.v4.runtime.CharStreams;
|
|
|
+import org.antlr.v4.runtime.CommonTokenStream;
|
|
|
+import org.antlr.v4.runtime.Lexer;
|
|
|
+import org.antlr.v4.runtime.Parser;
|
|
|
+import org.antlr.v4.runtime.RecognitionException;
|
|
|
+import org.antlr.v4.runtime.Recognizer;
|
|
|
+import org.antlr.v4.runtime.atn.ATNConfigSet;
|
|
|
+import org.antlr.v4.runtime.dfa.DFA;
|
|
|
+import org.antlr.v4.runtime.tree.RuleNode;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+import java.util.BitSet;
|
|
|
+import java.util.Set;
|
|
|
+
|
|
|
+public class ParserGenerated implements G4Parser {
|
|
|
+
|
|
|
+ protected final IClass<? extends Lexer> lexer;
|
|
|
+
|
|
|
+ protected final IClass<? extends Parser> parser;
|
|
|
+
|
|
|
+ private final String[] lexerRules;
|
|
|
+
|
|
|
+ private final String[] parserRules;
|
|
|
+
|
|
|
+ private final Set<String> branchValidator;
|
|
|
+
|
|
|
+ public ParserGenerated(IClass<? extends Lexer> lexer, IClass<? extends Parser> parser) {
|
|
|
+ this.lexer = lexer;
|
|
|
+ this.parser = parser;
|
|
|
+
|
|
|
+ this.lexerRules = (String[])lexer.field("ruleNames").get().getStatic();
|
|
|
+ this.parserRules = (String[])parser.field("ruleNames").get().getStatic();
|
|
|
+
|
|
|
+ this.branchValidator = new OpenSet<>();
|
|
|
+ parser.inner().forEach(type -> {
|
|
|
+ String someName = ruleName(type.raw());
|
|
|
+ if(type.parent().outer() == IClass.NULL) {
|
|
|
+ branchValidator.add(someName);
|
|
|
+ } else {
|
|
|
+ String ruleName = ruleName(type.parent().raw());
|
|
|
+ branchValidator.add(NodeSupport.QUALIFIER_NAME + someName);
|
|
|
+ branchValidator.add(ruleName + NodeSupport.QUALIFIER_NAME + someName);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public G4Node parse(String rule, String text) {
|
|
|
+ return new MyRoot(rule, text).tree();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean validateRuleName(String ruleName) {
|
|
|
+ if(ruleName.contains("#")) {
|
|
|
+ return branchValidator.contains(ruleName);
|
|
|
+ } else {
|
|
|
+ return Arrays.asList(parserRules).contains(ruleName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public boolean validateTokenName(String tokenName) {
|
|
|
+ return Arrays.asList(lexerRules).contains(tokenName);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String ruleName(int ruleIndex) {
|
|
|
+ return parserRules[ruleIndex];
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String ruleName(Class<?> ruleType) {
|
|
|
+ return StringUtils.uncapitalize(ruleType.getSimpleName().replaceAll("Context$", ""));
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String tokenName(int tokenIndex) {
|
|
|
+ if(tokenIndex <0 || tokenIndex>=lexerRules.length) {
|
|
|
+ return "?";
|
|
|
+ }
|
|
|
+ return lexerRules[tokenIndex-1];
|
|
|
+ }
|
|
|
+
|
|
|
+ private class MyRoot extends RootAbstract {
|
|
|
+
|
|
|
+ private final NodeForRule tree;
|
|
|
+
|
|
|
+ public MyRoot(String ruleName, String text) {
|
|
|
+ Lexer lexer = ParserGenerated.this.lexer.construct(CharStreams.fromString(text));
|
|
|
+
|
|
|
+ lexer.removeErrorListeners();
|
|
|
+ lexer.addErrorListener(new CustomErrorListener());
|
|
|
+
|
|
|
+ CommonTokenStream tokens = new CommonTokenStream(lexer);
|
|
|
+ Parser interpreter = ParserGenerated.this.parser.construct(tokens);
|
|
|
+
|
|
|
+ // @todo parser builder: with error handlers
|
|
|
+ interpreter.removeErrorListeners();
|
|
|
+ interpreter.addErrorListener(new CustomErrorListener());
|
|
|
+
|
|
|
+ IMethod rule = ParserGenerated.this.parser
|
|
|
+ .method(ruleName)
|
|
|
+ .orElseThrow(() -> new IllegalArgumentException("Unknown rule: " + ruleName));
|
|
|
+
|
|
|
+ try {
|
|
|
+ this.tree = new NodeForRule(this, null, (RuleNode) rule.call(interpreter));
|
|
|
+ } catch(IReflectiveException.CallException e) {
|
|
|
+ throw new IllegalArgumentException(e.getCause());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public G4Parser parser() {
|
|
|
+ return ParserGenerated.this;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public G4Node tree() {
|
|
|
+ return tree;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public String branchName(G4Node g4node) {
|
|
|
+ IClass<?> type = g4node.type();
|
|
|
+ if(type.parent().outer() == IClass.NULL) {
|
|
|
+ return NodeSupport.QUALIFIER_INDEX_DEFAULT;
|
|
|
+ } else {
|
|
|
+ return NodeSupport.QUALIFIER_NAME + ruleName(type.raw());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Ref<G4Node> findLabel(G4Node g4node, String labelName) {
|
|
|
+ Object node = g4node.unwrap();
|
|
|
+ return IClass.typefor(node)
|
|
|
+ .field(labelName)
|
|
|
+ .map(f -> f.get(node))
|
|
|
+ .map(t -> wrap(g4node, t));
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public static final class CustomException extends RuntimeException {
|
|
|
+ public CustomException(String message) {
|
|
|
+ super(message);
|
|
|
+ }
|
|
|
+
|
|
|
+ public CustomException(Throwable cause) {
|
|
|
+ super(cause);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // @todo reasonable error handler
|
|
|
+ public static final class CustomErrorListener implements ANTLRErrorListener {
|
|
|
+ @Override
|
|
|
+ public void syntaxError(Recognizer<?, ?> recognizer,
|
|
|
+ Object offendingSymbol,
|
|
|
+ int line,
|
|
|
+ int charPositionInLine,
|
|
|
+ String msg,
|
|
|
+ RecognitionException e) {
|
|
|
+ throw new CustomException(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void reportAmbiguity(Parser recognizer,
|
|
|
+ DFA dfa,
|
|
|
+ int startIndex,
|
|
|
+ int stopIndex,
|
|
|
+ boolean exact,
|
|
|
+ BitSet ambigAlts,
|
|
|
+ ATNConfigSet configs) {
|
|
|
+ throw new CustomException("reportAmbiguity");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void reportAttemptingFullContext(Parser recognizer,
|
|
|
+ DFA dfa,
|
|
|
+ int startIndex,
|
|
|
+ int stopIndex,
|
|
|
+ BitSet conflictingAlts,
|
|
|
+ ATNConfigSet configs) {
|
|
|
+ throw new CustomException("reportAttemptingFullContext");
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void reportContextSensitivity(Parser recognizer,
|
|
|
+ DFA dfa,
|
|
|
+ int startIndex,
|
|
|
+ int stopIndex,
|
|
|
+ int prediction,
|
|
|
+ ATNConfigSet configs) {
|
|
|
+ throw new CustomException("reportContextSensitivity");
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|