/******************************************************************************* * Copyright (c) 2008 Scott Stanchfield. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Based on the ANTLR parser generator by Terence Parr, http://antlr.org * Ric Klaren <klaren@cs.utwente.nl> * Scott Stanchfield - Modifications for XML Parsing *******************************************************************************/ package com.javadude.antxr; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /**A list of alternatives and info contained in * the rule definition. */ public class RuleBlock extends AlternativeBlock { protected String ruleName; protected String argAction = null; // string for rule arguments [...] protected String throwsSpec = null; protected String returnAction = null;// string for rule return type(s) <...> protected RuleEndElement endNode; // which node ends this rule? // Generate literal-testing code for lexer rule? protected boolean testLiterals = false; List<AlternativeElement> labeledElements; // List of labeled elements found in this rule // This is a list of AlternativeElement (or subclass) protected boolean[] lock; // for analysis; used to avoid infinite loops // 1..k protected Lookahead cache[];// Each rule can cache it's lookahead computation. // This cache contains an epsilon // imaginary token if the FOLLOW is required. No // FOLLOW information is cached here. // The FIRST(rule) is stored in this cache; 1..k // This set includes FIRST of all alts. Map<String, ExceptionSpec> exceptionSpecs; // table of String-to-ExceptionSpec. // grammar-settable options protected boolean defaultErrorHandler = true; protected String ignoreRule = null; /** Construct a named rule. */ public RuleBlock(Grammar g, String r) { super(g); ruleName = r; labeledElements = new ArrayList<AlternativeElement>(); cache = new Lookahead[g.maxk + 1]; exceptionSpecs = new HashMap<String, ExceptionSpec>(); setAutoGen(g instanceof ParserGrammar); } /** Construct a named rule with line number information */ public RuleBlock(Grammar g, String r, int line, boolean doAutoGen_) { this(g, r); this.line = line; setAutoGen(doAutoGen_); } public void addExceptionSpec(ExceptionSpec ex) { if (findExceptionSpec(ex.label) != null) { if (ex.label != null) { grammar.antxrTool.error("Rule '" + ruleName + "' already has an exception handler for label: " + ex.label); } else { grammar.antxrTool.error("Rule '" + ruleName + "' already has an exception handler"); } } else { exceptionSpecs.put((ex.label == null ? "" : ex.label.getText()), ex); } } public ExceptionSpec findExceptionSpec(Token theLabel) { return exceptionSpecs.get(theLabel == null ? "" : theLabel.getText()); } public ExceptionSpec findExceptionSpec(String theLabel) { return exceptionSpecs.get(theLabel == null ? "" : theLabel); } @Override public void generate() { grammar.generator.gen(this); } public boolean getDefaultErrorHandler() { return defaultErrorHandler; } public RuleEndElement getEndElement() { return endNode; } public String getIgnoreRule() { return ignoreRule; } public String getRuleName() { return ruleName; } public boolean getTestLiterals() { return testLiterals; } public boolean isLexerAutoGenRule() { return ruleName.equals("nextToken"); } @Override public Lookahead look(int k) { return grammar.theLLkAnalyzer.look(k, this); } @Override public void prepareForAnalysis() { super.prepareForAnalysis(); lock = new boolean[grammar.maxk + 1]; } // rule option values public void setDefaultErrorHandler(boolean value) { defaultErrorHandler = value; } public void setEndElement(RuleEndElement re) { endNode = re; } @Override public void setOption(Token key, Token value) { if (key.getText().equals("defaultErrorHandler")) { if (value.getText().equals("true")) { defaultErrorHandler = true; } else if (value.getText().equals("false")) { defaultErrorHandler = false; } else { grammar.antxrTool.error("Value for defaultErrorHandler must be true or false", grammar.getFilename(), key.getLine(), key.getColumn()); } } else if (key.getText().equals("testLiterals")) { if (!(grammar instanceof LexerGrammar)) { grammar.antxrTool.error("testLiterals option only valid for lexer rules", grammar.getFilename(), key.getLine(), key.getColumn()); } else { if (value.getText().equals("true")) { testLiterals = true; } else if (value.getText().equals("false")) { testLiterals = false; } else { grammar.antxrTool.error("Value for testLiterals must be true or false", grammar.getFilename(), key.getLine(), key.getColumn()); } } } else if (key.getText().equals("ignore")) { if (!(grammar instanceof LexerGrammar)) { grammar.antxrTool.error("ignore option only valid for lexer rules", grammar.getFilename(), key.getLine(), key.getColumn()); } else { ignoreRule = value.getText(); } } else if (key.getText().equals("paraphrase")) { if (!(grammar instanceof LexerGrammar)) { grammar.antxrTool.error("paraphrase option only valid for lexer rules", grammar.getFilename(), key.getLine(), key.getColumn()); } else { // find token def associated with this rule TokenSymbol ts = grammar.tokenManager.getTokenSymbol(ruleName); if (ts == null) { grammar.antxrTool.fatalError("panic: cannot find token associated with rule " + ruleName); return; } ts.setParaphrase(value.getText()); } } else if (key.getText().equals("generateAmbigWarnings")) { if (value.getText().equals("true")) { generateAmbigWarnings = true; } else if (value.getText().equals("false")) { generateAmbigWarnings = false; } else { grammar.antxrTool.error("Value for generateAmbigWarnings must be true or false", grammar.getFilename(), key.getLine(), key.getColumn()); } } else if ("xmlTag".equals(key.getText())) { // strip off quotes String text = value.getText(); int begin = 0; int end = text.length(); if (text.startsWith("\"")) { begin++; } if (text.startsWith("<")) { begin++; } if (text.endsWith("\"")) { end--; } if (text.endsWith(">")) { end--; } text = text.substring(begin,end); grammar.xmlRuleTagMap.put(this, resolveXMLName(grammar, text)); } else { grammar.antxrTool.error("Invalid rule option: " + key.getText(), grammar.getFilename(), key.getLine(), key.getColumn()); } } /** * Resolve an XML tag name by looking it up in the namespace mapping * @param grammar The grammar being compiled * @param tagName The xml tag name * @return The fully-qualified tagname with the prefix replaced */ public String resolveXMLName(Grammar theGrammar, String tagName) { int colon = tagName.lastIndexOf(':'); if (colon != -1 && colon != tagName.length()-1) { String prefix = tagName.substring(0,colon); String tag = tagName.substring(colon+1); String namespace = theGrammar.namespaceMap.get(prefix); if (namespace != null) { return namespace + ":" + tag; } } return tagName; } @Override public String toString() { String s = " FOLLOW={"; Lookahead lookaheadCache[] = endNode.cache; int k = grammar.maxk; boolean allNull = true; for (int j = 1; j <= k; j++) { if (lookaheadCache[j] == null) { continue; } s += lookaheadCache[j].toString(",", grammar.tokenManager.getVocabulary()); allNull = false; if (j < k && lookaheadCache[j + 1] != null) { s += ";"; } } s += "}"; if (allNull) { s = ""; } return ruleName + ": " + super.toString() + " ;" + s; } }