| /* |
| * Copyright 2009-2012 by The Regents of the University of California |
| * Licensed 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 from |
| * |
| * 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 edu.uci.ics.asterix.lexergenerator; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import edu.uci.ics.asterix.lexergenerator.rules.*; |
| |
| public class LexerNode { |
| private static String TOKEN_PREFIX = "TOKEN_"; |
| private LinkedHashMap<Rule, LexerNode> actions = new LinkedHashMap<Rule, LexerNode>(); |
| private String finalTokenName; |
| private Set<String> ongoingParsing = new HashSet<String>(); |
| |
| public LexerNode clone() { |
| LexerNode node = new LexerNode(); |
| node.finalTokenName = this.finalTokenName; |
| for (Map.Entry<Rule, LexerNode> entry : this.actions.entrySet()) { |
| node.actions.put(entry.getKey().clone(), entry.getValue().clone()); |
| } |
| for (String ongoing : this.ongoingParsing) { |
| node.ongoingParsing.add(ongoing); |
| } |
| return node; |
| } |
| |
| public void add(Rule newRule) { |
| if (actions.get(newRule) == null) { |
| actions.put(newRule, new LexerNode()); |
| } |
| } |
| |
| public void append(Rule newRule) { |
| if (actions.size() == 0) { |
| add(newRule); |
| } else { |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| action.getValue().append(newRule); |
| } |
| if (actions.containsKey(new RuleEpsilon())) { |
| actions.remove(new RuleEpsilon()); |
| add(newRule); |
| } |
| } |
| } |
| |
| public void merge(LexerNode newNode) throws Exception { |
| for (Map.Entry<Rule, LexerNode> action : newNode.actions.entrySet()) { |
| if (this.actions.get(action.getKey()) == null) { |
| this.actions.put(action.getKey(), action.getValue()); |
| } else { |
| this.actions.get(action.getKey()).merge(action.getValue()); |
| } |
| } |
| if (newNode.finalTokenName != null) { |
| if (this.finalTokenName == null) { |
| this.finalTokenName = newNode.finalTokenName; |
| } else { |
| throw new Exception("Rule conflict between: " + this.finalTokenName + " and " + newNode.finalTokenName); |
| } |
| } |
| for (String ongoing : newNode.ongoingParsing) { |
| this.ongoingParsing.add(ongoing); |
| } |
| } |
| |
| public void append(LexerNode node) throws Exception { |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| if (action.getKey() instanceof RuleEpsilon) |
| continue; |
| action.getValue().append(node); |
| } |
| if (actions.containsKey(new RuleEpsilon())) { |
| actions.remove(new RuleEpsilon()); |
| merge(node.clone()); |
| } |
| if (actions.size() == 0 || finalTokenName != null) { |
| finalTokenName = null; |
| merge(node.clone()); |
| } |
| } |
| |
| public void appendTokenName(String name) { |
| if (actions.size() == 0) { |
| this.finalTokenName = name; |
| } else { |
| ongoingParsing.add(TOKEN_PREFIX + name); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| action.getValue().appendTokenName(name); |
| } |
| } |
| } |
| |
| public LexerNode removeTokensName() { |
| this.finalTokenName = null; |
| this.ongoingParsing.clear(); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| action.getValue().removeTokensName(); |
| } |
| return this; |
| } |
| |
| public String toString() { |
| StringBuilder result = new StringBuilder(); |
| if (finalTokenName != null) |
| result.append("! "); |
| if (actions.size() == 1) |
| result.append(actions.keySet().toArray()[0].toString() + actions.values().toArray()[0].toString()); |
| if (actions.size() > 1) { |
| result.append(" ( "); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| if (result.length() != 3) { |
| result.append(" || "); |
| } |
| result.append(action.getKey().toString()); |
| result.append(action.getValue().toString()); |
| } |
| result.append(" ) "); |
| } |
| return result.toString(); |
| } |
| |
| public String toJava() { |
| StringBuffer result = new StringBuffer(); |
| if (numberOfRuleChar() > 2) { |
| result.append(toJavaSingleCharRules()); |
| result.append(toJavaComplexRules(false)); |
| } else { |
| result.append(toJavaComplexRules(true)); |
| } |
| if (this.finalTokenName != null) { |
| result.append("return " + TOKEN_PREFIX + finalTokenName + ";\n"); |
| } else if (ongoingParsing != null) { |
| String ongoingParsingArgs = collectionJoin(ongoingParsing, ','); |
| result.append("return parseError(" + ongoingParsingArgs + ");\n"); |
| } |
| return result.toString(); |
| } |
| |
| private int numberOfRuleChar() { |
| int singleCharRules = 0; |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| if (action.getKey() instanceof RuleChar) |
| singleCharRules++; |
| } |
| return singleCharRules; |
| } |
| |
| private String toJavaSingleCharRules() { |
| StringBuffer result = new StringBuffer(); |
| result.append("switch(currentChar){\n"); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| if (action.getKey() instanceof RuleChar) { |
| RuleChar rule = (RuleChar) action.getKey(); |
| result.append("case '" + rule.expectedChar() + "':\n"); |
| result.append(rule.javaAction()).append("\n"); |
| result.append(action.getValue().toJava()); |
| } |
| } |
| result.append("}\n"); |
| return result.toString(); |
| } |
| |
| private String toJavaComplexRules(boolean all) { |
| StringBuffer result = new StringBuffer(); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| if (!all && action.getKey() instanceof RuleChar) |
| continue; |
| if (action.getKey() instanceof RuleEpsilon) |
| continue; |
| String act = action.getKey().javaAction(); |
| if (act.length() > 0) { |
| act = "\n" + act; |
| } |
| result.append(action.getKey().javaMatch(act + "\n" + action.getValue().toJava())); |
| } |
| return result.toString(); |
| } |
| |
| public void expandFirstAction(LinkedHashMap<String, Token> tokens) throws Exception { |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| Rule first = action.getKey(); |
| if (first instanceof RulePartial) { |
| if (tokens.get(((RulePartial) first).getPartial()) == null) { |
| throw new Exception("Cannot find a token used as part of another definition, missing token: " |
| + ((RulePartial) first).getPartial()); |
| } |
| actions.remove(first); |
| LexerNode node = tokens.get(((RulePartial) first).getPartial()).getNode().clone(); |
| merge(node); |
| } |
| } |
| } |
| |
| public Set<String> neededAuxFunctions() { |
| HashSet<String> partials = new HashSet<String>(); |
| for (Map.Entry<Rule, LexerNode> action : actions.entrySet()) { |
| Rule rule = action.getKey(); |
| if (rule instanceof RulePartial) { |
| partials.add(((RulePartial) rule).getPartial()); |
| } |
| partials.addAll(action.getValue().neededAuxFunctions()); |
| } |
| return partials; |
| } |
| |
| public String toJavaAuxFunction() { |
| String oldFinalTokenName = finalTokenName; |
| if (oldFinalTokenName == null) |
| finalTokenName = "AUX_NOT_FOUND"; |
| String result = toJava(); |
| finalTokenName = oldFinalTokenName; |
| return result; |
| } |
| |
| private String collectionJoin(Collection<String> collection, char c) { |
| StringBuilder ongoingParsingArgs = new StringBuilder(); |
| for (String token : collection) { |
| ongoingParsingArgs.append(token); |
| ongoingParsingArgs.append(c); |
| } |
| if (ongoingParsing.size() > 0) { |
| ongoingParsingArgs.deleteCharAt(ongoingParsingArgs.length() - 1); |
| } |
| return ongoingParsingArgs.toString(); |
| } |
| } |