/** * Copyright 2015 Santhosh Kumar Tekuri * * The JLibs authors license this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package jlibs.nblr.codegen; import jlibs.core.annotation.processing.Printer; import jlibs.nblr.actions.EventAction; import jlibs.nblr.actions.PublishAction; import jlibs.nblr.codegen.java.SyntaxClass; import jlibs.nblr.matchers.Any; import jlibs.nblr.matchers.Matcher; import jlibs.nblr.matchers.Not; import jlibs.nblr.matchers.Range; import jlibs.nblr.rules.*; import jlibs.nbp.NBParser; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import static jlibs.core.annotation.processing.Printer.MINUS; import static jlibs.core.annotation.processing.Printer.PLUS; /** * @author Santhosh Kumar T */ public class Decision{ public final State state; public Matcher matchers[]; public Path path; public boolean fallback; public Decision(State state, Path route){ this.state = state; this.fallback = route.fallback(); Path paths[] = route.route(); matchers = new Matcher[paths.length]; for(int i=0; i<matchers.length; i++) matchers[i] = paths[i].matcher(); path = paths[0]; int index; for(index=1; index<path.size(); index++){ Object obj = path.get(index); if(obj instanceof Edge){ Edge edge = (Edge)obj; if(edge.ruleTarget!=null){ index++; break; }else if(edge.matcher!=null){ path.travelWithoutMatching(); index = path.size(); break; } }else if(obj instanceof Node){ Node node = (Node)obj; if(node==state.ruleMethod.rule.node || node.junction()) break; } } if(index!=path.size()){ if(path.get(index) instanceof Node){ while(index!=path.size()-1) path.remove(index+1); } } } private String ruleID(){ return "RULE_"+state.ruleMethod.rule.name.toUpperCase(); } private String exiting(String rule, int state){ return state==-1 ? "" : "exiting("+rule+", "+state+");"; } public boolean isEmpty(){ if(matchers[0]!=null) return false; for(int i=0; i<path.size(); i++){ Object obj = path.get(i); if(obj instanceof Node){ Node node = (Node)obj; if(i<path.size()-1 || node.outgoing.size()==0){ // !lastNode || sinkNode if(node.action!=null || node.name!=null) return false; } }else if(obj instanceof Edge){ Edge edge = (Edge)obj; if(edge.matcher!=null || edge.ruleTarget!=null) return false; } } return true; } public void collapse(){ for(Object obj: path){ if(obj instanceof Edge){ Edge edge = (Edge)obj; for(Edge outgoing: edge.target.outgoing()) outgoing.setSource(edge.source); for(Edge incoming: edge.target.incoming()){ if(incoming!=edge) incoming.setTarget(edge.source); } if(state.ruleMethod.rule.node==edge.target) state.ruleMethod.rule.node = edge.source; edge.delete(); } } } @Override public boolean equals(Object obj){ if(obj instanceof Decision){ Decision that = (Decision)obj; return Arrays.equals(this.matchers, that.matchers) && this.path.equals(that.path); }else return false; } private int indexOfedgeWithRule(){ for(int i=0; i<path.size(); i++){ Object obj = path.get(i); if(obj instanceof Edge){ Edge edge = (Edge)obj; if(edge.ruleTarget!=null) return i; } } return -1; } public Edge edgeWithRule(){ int index = indexOfedgeWithRule(); return index==-1 ? null : (Edge)path.get(index); } private Node stateAfterRule(Edge edgeWithRule){ if(new Routes(state.ruleMethod.rule, edgeWithRule.target).isEOF()) return null; else return edgeWithRule.target; } private int idAfterRule(Edge edgeWithRule){ Node stateAfterRule = stateAfterRule(edgeWithRule); return stateAfterRule==null ? -1 : stateAfterRule.stateID; } private String ruleName(Edge edgeWithRule){ String ruleName = edgeWithRule.ruleTarget.rule.name; if(!SyntaxClass.DEBUGGABLE && Node.DYNAMIC_STRING_MATCH.equals(edgeWithRule.source.name)) ruleName = "DYNAMIC_STRING_MATCH"; return ruleName; } private String ruleID(Edge edgeWithRule){ return "RULE_"+ruleName(edgeWithRule).toUpperCase(); } public void computeNextStates(ArrayList<Node> statesVisited, LinkedHashSet<Node> statesPending){ Node target; Edge edgeWithRule = edgeWithRule(); if(edgeWithRule!=null) target = stateAfterRule(edgeWithRule); else{ target = returnTarget(); if(id(target)==-1) target = null; } if(target!=null && !statesVisited.contains(target)) statesPending.add(target); } public Node returnTarget(){ return (Node)path.get(path.size()-1); } public boolean isLoop(){ return matchers.length==1 && path.size()>1 && state.fromNode==returnTarget(); } public boolean usesFinishAll(){ return isLoop() && !fallback && edgeWithRule()==null; } public boolean readCodePoint(){ for(Matcher matcher: matchers){ if(matcher!=null) return true; } return false; } public boolean readCharacter(){ for(Matcher matcher: matchers){ if(matcher!=null && matcher.clashesWith(Range.SUPPLIMENTAL)) return false; } return true; } public boolean matchesNewLine(){ for(Matcher matcher: matchers){ if(matcher!=null && matcher.clashesWith(Any.NEW_LINE)) return true; } return false; } public String expected(){ StringBuilder builder = new StringBuilder(); for(Matcher matcher: matchers){ if(matcher==null) builder.append("<EOF>"); else if(matcher.name!=null) builder.append('<').append(matcher.name).append('>'); else builder.append(matcher.toString()); } return builder.toString(); } public static final int ADD_CONTINUE = 1; public static final int ADD_RETURN = 2; public static final int GOTO_NEXT_CASE = 4; public static final int GOTO_NEXT_DECISION = 5; public static final int CALL_RULE_AND_CONTINUE = 6; public static final int CALL_RULE_AND_RETURN = 7; public static final int CALL_RULE_AND_NEXT_DECISION = 8; public int returnAction(State nextState){ if(usesFinishAll()) return GOTO_NEXT_DECISION; Node returnTarget = returnTarget(); Edge edge = edgeWithRule(); if(edge ==null){ if(nextState==null || nextState.fromNode!=returnTarget){ return id(returnTarget)==-1 ? ADD_RETURN : ADD_CONTINUE; }else return state.lookAheadRequired() ? ADD_CONTINUE : GOTO_NEXT_CASE; }else{ if(idAfterRule(edge)==-1) return CALL_RULE_AND_RETURN; Node stateAfterRule = stateAfterRule(edge); if(nextState==null || nextState.fromNode!=stateAfterRule) return CALL_RULE_AND_CONTINUE; else return state.lookAheadRequired() ? CALL_RULE_AND_CONTINUE : CALL_RULE_AND_NEXT_DECISION; } } public boolean requiresContinue(State nextState){ int returnAction = returnAction(nextState); return returnAction==ADD_CONTINUE || returnAction==CALL_RULE_AND_CONTINUE; } private void useFinishAll(Printer printer){ Matcher matcher = matchers[0]; String methodName = "";//state.ruleMethod.syntaxClass.addToFinishAll(matcher); String ch = state.lookAheadRequired() ? "ch" : state.readMethod(); String methodCall; if(methodName.equals(SyntaxClass.FINISH_ALL) || methodName.equals(SyntaxClass.FINISH_ALL_OTHER_THAN)){ Any any = (Any)(methodName.equals(SyntaxClass.FINISH_ALL_OTHER_THAN) ? ((Not)matcher).delegate : matcher); methodCall = methodName+"("+ch+", "+Matcher.toJava(any.chars[0])+')'; }else{ if(!matcher.clashesWith(Range.SUPPLIMENTAL) && !matcher.clashesWith(Any.NEW_LINE)) ch = ""; methodCall = "finishAll_"+methodName+"("+ch+")"; } boolean returnValueRequired = false; for(int i = state.decisions.indexOf(this)+1; i<state.decisions.size(); i++){ Decision decision = state.decisions.get(i); if(decision.matchers[0]!=null){ returnValueRequired = true; break; } } if(returnValueRequired) methodCall = "(ch="+methodCall+")"; printer.printlns( "if("+methodCall+"==EOC)", PLUS, state.breakStatement(), MINUS ); } public void generate(Printer printer, State nextState){ if(usesFinishAll()){ useFinishAll(printer); return; } for(int i=0; i<matchers.length; i++) startMatcher(printer, i); addBody(printer, nextState); for(int i=0; i<matchers.length; i++) endMatcher(printer, i); } public static String condition(Matcher matcher, String ch){ String condition = matcher._javaCode(ch); if(matcher.checkFor(NBParser.EOF)){ if(matcher.name==null) condition = '('+condition+')'; condition = "ch!=EOF && "+condition; } return condition; } public void startMatcher(Printer printer, int i){ Matcher matcher = matchers[i]; if(matcher==null) return; boolean useLookAhead = false; if(i==matchers.length-1){ int idecision = state.decisions.indexOf(this); if(idecision!=0 && state.decisions.get(idecision-1).matchers.length>matchers.length) // indeterminateroute useLookAhead = true; }else useLookAhead = true; String ch = useLookAhead ? "la["+i+"]" : "ch"; String condition = condition(matcher, ch); printer.printlns( "if("+condition+"){", PLUS ); } public void addBody(Printer printer, State nextState){ boolean checkStop = generatePath(printer); int returnAction = returnAction(nextState); Node returnTarget = returnTarget(); if(id(returnTarget)!=-1 && returnAction==ADD_CONTINUE) printer.println("state = "+id(returnTarget)+';'); boolean resetLookAhead = false; if(matchers.length>1) resetLookAhead = true; else if(matchers.length==1 && state.lookAheadRequired()){ int lookAheadSize = 0; for(Decision decision: state.decisions){ if(decision==this) break; lookAheadSize = Math.max(lookAheadSize, decision.matchers.length); } if(lookAheadSize>1) resetLookAhead = true; } if(resetLookAhead) printer.println("resetLookAhead();"); switch(returnAction){ case ADD_CONTINUE: if(checkStop) doCheckStop(printer); printer.println("continue;"); break; case ADD_RETURN: if(checkStop && id(returnTarget)!=-1){ printer.printlns( "if(stop)", PLUS, exiting(ruleID(), id(returnTarget)), MINUS ); } if(SyntaxClass.DEBUGGABLE) printer.printlns("handler.currentNode("+ruleID()+", "+returnTarget.id+");"); printer.printlns("return "+(checkStop ? "!stop" : "true")+";"); break; case CALL_RULE_AND_CONTINUE: case CALL_RULE_AND_RETURN: case CALL_RULE_AND_NEXT_DECISION: Edge edgeWithRule = edgeWithRule(); String methodCall; if(!SyntaxClass.DEBUGGABLE && Node.DYNAMIC_STRING_MATCH.equals(edgeWithRule.source.name)) methodCall = "matchString("+id(returnTarget)+", dynamicStringToBeMatched)"; else{ Rule rule = edgeWithRule.ruleTarget.rule; if(rule.id<0) methodCall = "matchString(RULE_"+rule.name.toUpperCase()+", "+id(returnTarget)+", STRING_IDS[-RULE_"+rule.name.toUpperCase()+"])"; else methodCall = rule.name+"("+id(returnTarget)+")"; } if(returnAction!=CALL_RULE_AND_RETURN) printer.println("state = "+idAfterRule(edgeWithRule)+";"); if(checkStop){ printer.printlns( "if(stop){", PLUS, exiting(ruleID(edgeWithRule), id(edgeWithRule.ruleTarget.node())), idAfterRule(edgeWithRule)==-1 ? "return false;" : state.breakStatement(), MINUS, "}else" ); } List<String> methodCallList = new ArrayList<String>(); switch(returnAction){ case CALL_RULE_AND_RETURN: methodCallList.add("return true;"); break; case CALL_RULE_AND_CONTINUE: methodCallList.add("continue;"); break; case CALL_RULE_AND_NEXT_DECISION: break; } List<String> elseList = new ArrayList<String>(); elseList.add(idAfterRule(edgeWithRule)==-1 ? "return false;" : state.breakStatement()); if(methodCallList.size()==0) printer.printlnIf("!"+methodCall, elseList); else printer.printlnIf(methodCall, methodCallList, elseList); break; case GOTO_NEXT_CASE: case GOTO_NEXT_DECISION: printer.println("state = "+id(returnTarget)+";"); if(checkStop) doCheckStop(printer); break; } } private void doCheckStop(Printer printer){ printer.printlns( "if(stop)", PLUS, id(returnTarget())==-1 ? "return false;" : state.breakStatement(), MINUS ); } public void endMatcher(Printer printer, int i){ Matcher matcher = matchers[i]; if(matcher==null) return; printer.printlns( MINUS, "}" ); } // NOTE: don't use for edgeRuleTarget private int id(Node node){ return node==null || node.outgoing.size()==0 ? -1 : node.stateID; } private boolean generatePath(Printer printer){ boolean checkStop = false; int index = -1; for(Object obj: path){ ++index; if(obj instanceof Node){ Node node = (Node)obj; if(index<path.size()-1 || node.outgoing.size()==0){ // !lastNode || sinkNode if(node.action!=null){ if(SyntaxClass.DEBUGGABLE) printer.println("handler.execute("+state.ruleMethod.rule.id+", "+node.stateID+");"); else printer.println(node.action.javaCode()+';'); if(node.action instanceof EventAction || node.action instanceof PublishAction){ if(node.action.toString().startsWith("#")) checkStop = true; } } } }else if(obj instanceof Edge){ Edge edge = (Edge)obj; if(edge.ruleTarget!=null){ // RuleTarget ruleTarget = edge.ruleTarget; // int idAfterRule = idAfterRule(edge); // String ruleName = ruleName(edge); // printer.println("push(RULE_"+ruleName.toUpperCase()+", "+idAfterRule+", "+id(ruleTarget.node())+");"); }else if(edge.matcher!=null){ if(matchers.length==1){ if(!matchers[0].clashesWith(Range.SUPPLIMENTAL) && !matchers[0].clashesWith(Any.NEW_LINE)){ if(edge.source.buffering==Answer.NO){ printer.println("position++;"); continue; }else if(edge.source.buffering==Answer.YES){ printer.println("buffer.append(input[position++]);"); continue; } } printer.println("consume(ch);"); //"+edge.source.buffering); }else printer.println("consume(FROM_LA);"); //"+edge.source.buffering); } } } return checkStop; } }