/** * Copyright 2004-2016 Riccardo Solmi. All rights reserved. * This file is part of the Whole Platform. * * The Whole Platform is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The Whole Platform is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the Whole Platform. If not, see <http://www.gnu.org/licenses/>. */ package org.whole.lang.grammars.parsers; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.whole.lang.builders.IBuilder; import org.whole.lang.builders.IBuilderOperation; import org.whole.lang.builders.ModelBuilderOperation; import org.whole.lang.commons.builders.ICommonsBuilder; import org.whole.lang.commons.reflect.CommonsLanguageKit; import org.whole.lang.grammars.model.As; import org.whole.lang.grammars.model.Bound; import org.whole.lang.grammars.model.ByDelimiter; import org.whole.lang.grammars.model.ByLines; import org.whole.lang.grammars.model.BySize; import org.whole.lang.grammars.model.Choose; import org.whole.lang.grammars.model.CompiledPattern; import org.whole.lang.grammars.model.Concatenate; import org.whole.lang.grammars.model.DataTerminal; import org.whole.lang.grammars.model.Empty; import org.whole.lang.grammars.model.LiteralTerminal; import org.whole.lang.grammars.model.NonTerminal; import org.whole.lang.grammars.model.Optional; import org.whole.lang.grammars.model.Predicate; import org.whole.lang.grammars.model.Production; import org.whole.lang.grammars.model.Repeat; import org.whole.lang.grammars.model.Rule; import org.whole.lang.grammars.model.Size; import org.whole.lang.grammars.model.Split; import org.whole.lang.grammars.model.Template; import org.whole.lang.grammars.model.When; import org.whole.lang.grammars.reflect.GrammarsEntityDescriptorEnum; import org.whole.lang.grammars.reflect.GrammarsFeatureDescriptorEnum; import org.whole.lang.grammars.reflect.GrammarsLanguageKit; import org.whole.lang.grammars.visitors.GrammarsIdentityVisitor; import org.whole.lang.grammars.visitors.GrammarsTraverseAllVisitor; import org.whole.lang.iterators.AbstractPatternFilterIterator; import org.whole.lang.iterators.IEntityIterator; import org.whole.lang.iterators.IteratorFactory; import org.whole.lang.matchers.Matcher; import org.whole.lang.model.EnumType; import org.whole.lang.parsers.Lexer; import org.whole.lang.parsers.ParseException; import org.whole.lang.reflect.EntityDescriptor; import org.whole.lang.templates.ModelTemplate; import org.whole.lang.text.reflect.TextEntityDescriptorEnum; import org.whole.lang.util.DataTypeUtils; import org.whole.lang.util.EntityUtils; /** * @author Enrico Persiani */ public class GenericPredictiveParser extends AbstractPredictiveParser { private GenericMatcherVisitor genericMatcher; private GenericParserVisitor genericParser; private Map<String, Production> productionsMap; private IBuilder builder; private ICommonsBuilder cb; public GenericPredictiveParser(CharSequence source, String grammarUri, IBuilderOperation op) { this(Lexer.createLexer(source), grammarUri, op); } public GenericPredictiveParser(Lexer lexer, String grammarUri, IBuilderOperation op) { super(lexer, grammarUri); genericMatcher = new GenericMatcherVisitor(); genericParser = new GenericParserVisitor(); productionsMap = new HashMap<String, Production>(); if (ModelBuilderOperation.ID.equals(op.wGetOperationId())) { //FIXME workaround, should use a generic builder builder = op.wGetBuilder(GrammarsLanguageKit.URI, false); } else builder = op.wGetBuilder(); cb = (ICommonsBuilder) op.wGetBuilder(CommonsLanguageKit.URI); AbstractPatternFilterIterator<Production> i = IteratorFactory.<Production>descendantOrSelfMatcherIterator().withPattern(GrammarsEntityDescriptorEnum.Production); i.reset(this.grammar); for (Production production : i) productionsMap.put(production.getName().getValue(), production); productionsMap = Collections.unmodifiableMap(productionsMap); } private boolean isLexicon(Production production) { return grammar.getLexicalStructure().wContains(production); } private Production getProduction(NonTerminal nt) { return getProduction(nt.getValue()); } private Production getProduction(String nt) { return productionsMap.get(nt); } public void wParseStartSymbol() { Production production = getProduction(grammar.getStartSymbol()); if (production == null) throw createParseError("mising start symbol production"); assert !isLexicon(production); wParse(production); } public void wParse(EntityDescriptor<?> ed) { wParse(ed.getName()); } public void wParse(String nt) { wParse(getProduction(nt)); } protected void wParse(Rule rule) { rule.accept(genericParser); } @Override protected int wMatch(Rule rule) { return genericMatcher.match(rule); } public class GenericParserVisitor extends GrammarsIdentityVisitor { private EntityDescriptor<?> productionED; @Override public void visit(Production entity) { Template template = entity.getTemplate(); if (EntityUtils.isStageUpFragment(template))//TODO check for template interpretation new ModelTemplate(template).apply(builder); else { productionED = ed(entity.getName().getValue()); if (EntityUtils.isData(productionED)) entity.getRule().accept(this); else if (!productionED.isAbstract()) { builder.wEntity_(productionED); entity.getRule().accept(this); builder._wEntity(productionED); } else entity.getRule().accept(this); productionED = null; } } @Override public void visit(NonTerminal entity) { EntityDescriptor<?> productionED = this.productionED; Production production = getProduction(entity); if (isLexicon(production)) { if (Matcher.match(GrammarsEntityDescriptorEnum.DataTerminal, production.getRule()) && Matcher.match(GrammarsEntityDescriptorEnum.Production, entity.wGetParent())) production.getRule().accept(this); else wMatch(production); } else production.accept(this); this.productionED = productionED; } @Override public void visit(Choose entity) { int prediction = wPredict(entity); if (isMatch(prediction)) ((Rule) entity.wGet(indexOfMatch(prediction))).accept(this); else throw createParseError("missing choose step"); } @Override public void visit(Concatenate entity) { for (int i=0, size=entity.wSize(); i<size; i++) ((Rule) entity.wGet(i)).accept(this); } @Override public void visit(Repeat entity) { int lowerBoundValue = entity.getLowerBound().getValue(); Bound upperBound = entity.getUpperBound(); int upperBoundValue = Matcher.match(GrammarsEntityDescriptorEnum.Unbounded, upperBound) ? -1 : ((Size) upperBound).getValue(); Rule rule = entity.getRule(); Rule separator = entity.getSeparator(); Rule effectiveSeparator = Matcher.matchImpl(GrammarsEntityDescriptorEnum.NonTerminal, separator) ? getProduction((NonTerminal) separator).getRule() : separator; boolean useSeparator = !EntityUtils.isResolver(separator) && !Matcher.isAssignableAsIsFrom(GrammarsEntityDescriptorEnum.EpsilonRule, effectiveSeparator); int count = 0; Rule nextPredictionRule = useSeparator ? separator : rule; while (count != upperBoundValue) { // always true if upperBoundValue is negative if (count >= lowerBoundValue && (count == 0 && !isConsumingMatch(wPredict(rule)) || count > 0 && !isConsumingMatch(wPredict(nextPredictionRule)))) break; if (useSeparator && count > 0) separator.accept(this); rule.accept(this); count++; } } @Override public void visit(Optional entity) { Rule rule = entity.getRule(); if (isConsumingMatch(wPredict(rule))) rule.accept(this); } @Override public void visit(When entity) { if (isMatch(wPredict(entity))) entity.getRule().accept(this); else throw createParseError("cannot apply when rule"); } @Override public void visit(LiteralTerminal entity) { throw createParseError("unreachable code"); //FIXME better error handling } private String lastPatternMatched; @Override public void visit(CompiledPattern entity) { lastPatternMatched = nextToken(entity.getValue()); } @Override public void visit(DataTerminal entity) { entity.getPattern().accept(this); try { DataTypeUtils.buildFromPersistenceString(builder, productionED, lastPatternMatched); } catch (IllegalArgumentException e) { cb.StageDownFragment_(); builder.wEntity(TextEntityDescriptorEnum.Text, lastPatternMatched); cb._StageDownFragment(); } } @Override public void visit(Empty entity) { // do nothing } @Override public void visit(As entity) { if (productionED.getDataKind().isEnumValue()) { EnumType<?> dataEnumType = productionED.getDataEnumType(); entity.getRule().accept(this); builder.wEntity(productionED, dataEnumType.valueOf(entity.getName().getValue())); } else { builder.wFeature(fd(entity.getName().getValue())); entity.getRule().accept(this); } } private int lastHorizon; @Override public void visit(ByDelimiter entity) { lastHorizon = setHorizonByDelimiter(((CompiledPattern) entity.getDelimiter()).getValue()); } @Override public void visit(ByLines entity) { lastHorizon = setHorizonByLines(entity.getValue()); } @Override public void visit(BySize entity) { lastHorizon = setHorizonBySize(entity.getValue()); } @Override public void visit(Split entity) { entity.getSplitter().accept(this); int horizon = lastHorizon; entity.getRule().accept(this); if (hitEnd()) setHorizonPosition(horizon, true); else throw createParseError("bad horizon constraint"); } } public class GenericMatcherVisitor extends GrammarsTraverseAllVisitor { private int lastPrediction; public int match(Rule rule) { lastPrediction = 0; rule.accept(wGetVisitor1()); return lastPrediction; } @Override public void visit(Production entity) { entity.getRule().accept(wGetVisitor1()); } @Override public void visit(NonTerminal entity) { int oldLastPrediction = lastPrediction; getProduction(entity).accept(wGetVisitor1()); lastPrediction = oldLastPrediction; } @Override public void visit(Choose entity) { Lexer.Memento memento = mark(); for (int i=0, size=entity.wSize(); i<size; i++) { try { ((Rule) entity.wGet(i)).accept(wGetVisitor1()); lastPrediction = i; return; } catch (ParseException e) { reset(memento); } catch (Exception e) { reset(memento); } } throw createParseError("cannot apply choose rule"); } @Override public void visit(Repeat entity) { int lowerBoundValue = entity.getLowerBound().getValue(); Bound upperBound = entity.getUpperBound(); int upperBoundValue = Matcher.match(GrammarsEntityDescriptorEnum.Unbounded, upperBound) ? -1 : ((Size) upperBound).getValue(); Rule rule = entity.getRule(); Rule separator = entity.getSeparator(); Rule effectiveSeparator = Matcher.matchImpl(GrammarsEntityDescriptorEnum.NonTerminal, separator) ? getProduction((NonTerminal) separator).getRule() : separator; boolean useSeparator = !EntityUtils.isResolver(separator) && !Matcher.isAssignableAsIsFrom(GrammarsEntityDescriptorEnum.EpsilonRule, effectiveSeparator); int count = 0; while (true) { boolean added = false; Lexer.Memento memento = mark(); try { rule.accept(wGetVisitor1()); count++; added = true; if (count == upperBoundValue || (count >= lowerBoundValue && isSame(memento))) // always false if upperBoundValue is negative break; if (useSeparator) { memento = mark(); separator.accept(wGetVisitor1()); } } catch (ParseException e) { if (useSeparator && !added && count > 0) throw createParseError("cannot apply repeat rule"); if (count < lowerBoundValue) throw createParseError("lower bound not reached in repeat rule"); reset(memento); break; } } } @Override public void visit(Optional entity) { Lexer.Memento memento = mark(); try { entity.getRule().accept(wGetVisitor1()); } catch (ParseException e) { reset(memento); } } protected void evaluate(Predicate predicate) { IEntityIterator<Predicate> iterator; switch (predicate.wGetEntityOrd()) { case GrammarsEntityDescriptorEnum.And_ord: iterator = IteratorFactory.<Predicate>childIterator(); iterator.reset(predicate); while (iterator.hasNext()) { Lexer.Memento memento = mark(); evaluate(iterator.next()); reset(memento); } break; case GrammarsEntityDescriptorEnum.Or_ord: iterator = IteratorFactory.<Predicate>childIterator(); iterator.reset(predicate); while (iterator.hasNext()) { Lexer.Memento memento = mark(); try { evaluate(iterator.next()); return; } catch (Exception e) { reset(memento); } } throw createParseError("cannot evaluate predicate"); case GrammarsEntityDescriptorEnum.Not_ord: try { evaluate((Predicate) predicate.wGet(GrammarsFeatureDescriptorEnum.predicate)); } catch (Exception e) { break; } throw createParseError("cannot evaluate predicate"); default: ((Rule) predicate).accept(this); } } @Override public void visit(When entity) { Lexer.Memento memento = mark(); //++ evaluate(entity.getPredicate()); reset(memento);//++ entity.getRule().accept(this);//++ remove to obtain strict when semantics } @Override public void visit(LiteralTerminal entity) { entity.getPattern().accept(wGetVisitor1()); } @Override public void visit(DataTerminal entity) { entity.getPattern().accept(wGetVisitor1()); } @Override public void visit(Empty entity) { // always match } @Override public void visit(CompiledPattern entity) { nextToken(entity.getValue()); } @Override public void visit(As entity) { entity.getRule().accept(wGetVisitor1()); } private int lastHorizon; @Override public void visit(ByDelimiter entity) { lastHorizon = setHorizonByDelimiter(((CompiledPattern) entity.getDelimiter()).getValue()); } @Override public void visit(ByLines entity) { lastHorizon = setHorizonByLines(entity.getValue()); } @Override public void visit(BySize entity) { lastHorizon = setHorizonBySize(entity.getValue()); } @Override public void visit(Split entity) { entity.getSplitter().accept(wGetVisitor1()); int horizon = lastHorizon; entity.getRule().accept(wGetVisitor1()); if (hitEnd()) setHorizonPosition(horizon, true); else throw createParseError("bad horizon constraint"); } } }