Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modelcc.parser.fence.FenceConstraintEnforcer Maven / Gradle / Ivy
Go to download
ModelCC is a model-based parser generator (a.k.a. compiler compiler) that decouples language specification from language processing, avoiding some of the problems caused by grammar-driven parser generators. ModelCC receives a conceptual model as input, along with constraints that annotate it. It is then able to create a parser for the desired textual language and the generated parser fully automates the instantiation of the language conceptual model. ModelCC also includes a built-in reference resolution mechanism that results in abstract syntax graphs, rather than mere abstract syntax trees.
/*
* ModelCC, distributed under ModelCC Shared Software License, www.modelcc.org
*/
package org.modelcc.parser.fence;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.modelcc.language.syntax.ObjectWrapper;
import org.modelcc.language.syntax.ParserMetadata;
import org.modelcc.language.syntax.Rule;
import org.modelcc.language.syntax.RuleSymbol;
import org.modelcc.language.syntax.Symbol;
import org.modelcc.language.syntax.InputSymbol;
import org.modelcc.language.syntax.SyntaxConstraints;
/**
* Fence Constraint Enforcer
*
* @author Luis Quesada ([email protected] ), refactored by Fernando Berzal ([email protected] )
*/
public class FenceConstraintEnforcer implements Serializable
{
private ParsedGraph pg;
private SyntaxGraph sg;
private ParserMetadata metadata;
private Map> objectMetadata;
private SyntaxConstraints constraints;
private FenceAssociativityConstraints associativities;
private FenceCompositionConstraints compositions;
private FencePrecedenceConstraints precedences;
private int id;
private Set symbols;
private Set history;
private Map> startingAt;
private Map> usedIn;
private Map> symbolMap;
private Map> tupleMap;
private Map> grammarRules;
// Constructors
public FenceConstraintEnforcer (ParserMetadata metadata, SyntaxConstraints constraints)
{
this(metadata,constraints,null);
}
public FenceConstraintEnforcer (ParserMetadata metadata, SyntaxConstraints constraints, Map> objectMetadata)
{
this.metadata = metadata;
this.constraints = constraints;
this.objectMetadata = objectMetadata;
}
private void initCollections()
{
symbols = new HashSet();
startingAt = new HashMap>();
usedIn = new HashMap>();
history = new HashSet();
symbolMap = new HashMap>();
tupleMap = new HashMap>();
id = 0;
// Constraints: associativity, composition & precedence
associativities = new FenceAssociativityConstraints(pg.getGrammar(), constraints);
compositions = new FenceCompositionConstraints(constraints);
precedences = new FencePrecedenceConstraints(this,constraints);
// Hash table for grammar rules
grammarRules = new HashMap>();
for (Rule r: pg.getGrammar().getRules()) {
Object e = r.getLeft().getType();
Set sr = grammarRules.get(e);
if (sr == null) {
sr = new HashSet();
grammarRules.put(e,sr);
}
sr.add(r);
}
// Object metadata
if (objectMetadata!=null) {
objectMetadata.put("startIndex", new HashMap());
objectMetadata.put("endIndex", new HashMap());
objectMetadata.put("symbol", new HashMap());
}
}
// Accessors
public SyntaxGraph getSyntaxGraph ()
{
return sg;
}
public List getSymbolsAt (int index)
{
return startingAt.get(index);
}
// Constraint enforcement
/**
* Enforce constraints on the parsed graph obtained from the lexical graph.
* @param pg the parsed graph.
* @return an abstract syntax graph.
*/
public SyntaxGraph enforce (ParsedGraph pg)
{
this.pg = pg;
this.sg = new SyntaxGraph();
initCollections();
// Potential start symbols
Set potentialstart = new HashSet();
// Recursively expand potential starting symbols
for (ParsedSymbol ps: pg.getStart())
potentialstart.addAll(expand(ps));
// Calculate start and used symbols
for (Symbol s: potentialstart) {
// LZ: No preceding nor following symbols
if (pg.getPreceding(s.getInputSymbol()) == null && pg.getFollowing(s.getInputSymbol()) == null) {
sg.addStart(s);
addSymbol(s);
}
// FB: Start & end indexes
// pg.getStartPositions() & pg.getEndPositions();
}
precedences.filter();
return sg;
}
// Symbol building
/**
* Symbol builder (builds a symbol, fills its data, and validates it).
* @param r rule
* @param s symbol to be built.
* @return true if the symbol is valid, false if not
*/
private boolean build(Rule r,Symbol s)
{
return metadata.getBuilder().build(r, s, metadata);
}
/**
* Token builder (builds a token symbol, fills its data, and validates it).
* @param s symbol to be built.
* @return true if the symbol is valid, false if not
*/
private boolean buildToken (Symbol t)
{
metadata.getMap().put( t.getUserData(),
new ObjectWrapper(t.getUserData(),metadata.getModel(),t.getInputSymbol().getString()));
return true;
}
/**
* Empty symbol builder (builds an empty symbol, fills its "data," and validates it).
* @param s symbol to be built.
* @return true if the symbol is valid, false if not
*/
private boolean buildEmptySymbol (Symbol s)
{
return metadata.getBuilder().build(pg.getGrammar().getEmptyRule(s.getType()),s,metadata);
}
/**
* Symbol postbuilder (builds a symbol, fills its data, and validates it).
* @param r rule.
* @param s symbol to be built.
* @return true if the symbol is valid, false if not
*/
protected boolean postBuild (Rule r, Symbol s)
{
metadata.setUsed(usedIn);
return r.getPostBuilder().build(s,metadata);
}
/**
* Recursively add the symbols used by a symbol
* @param s the symbol
*/
protected void addSymbol (Symbol s)
{
sg.add(s);
for (Symbol x: s.getContents()) {
addSymbol(x);
}
}
/**
* Recursively remove symbol
* @param s the symbol
*/
protected void removeSymbol (Symbol s)
{
sg.remove(s);
if (usedIn.get(s) != null) {
if (usedIn.get(s).size()>=1)
for (Symbol current : usedIn.get(s)) {
removeSymbol(current);
}
}
}
/**
* Expand a parsed symbol.
* @param ps the parsed symbol to be expanded.
* @return a set of symbols expanded from the parsed symbol.
*/
private Set expand (ParsedSymbol ps)
{
Set ma = symbolMap.get(ps);
if (ma != null) // Memoization
return ma;
Set hs = new HashSet();
if (ps.isToken()) {
// If the symbol to be expanded is a token, generate the token.
Symbol s = new Symbol(id,ps);
if (buildToken(s)) {
id++;
storeSymbol(s);
hs.add(s);
}
} else if (ps.getStartIndex()==ps.getEndIndex() && ps.getStartIndex()==-1) {
// If the symbol corresponds to an empty start symbol, generate the token.
Symbol s = new Symbol(id,ps);
if (buildEmptySymbol(s)) {
id++;
storeSymbol(s);
hs.add(s);
}
} else {
if (history.contains(ps))
return Collections.EMPTY_SET; // == new HashSet();
history.add(ps);
Set tuples = searchAllTuples(ps);
for (Tuple tuple: tuples)
expandSymbol(hs,ps,tuple,0,new ArrayList(),new ArrayList());
hs = precedences.select(hs);
}
symbolMap.put(ps,hs);
return hs;
}
private Set searchAllTuples(ParsedSymbol ps)
{
Set ma = tupleMap.get(ps);
if (ma != null)
return ma;
Set ets = new HashSet();
Set rules = grammarRules.get(ps.getType());
if (rules != null) {
Set pss = pg.getSymbolsStartingAt(ps.getStartIndex());
for (Rule r: rules) {
Tuple et = new Tuple(r);
for (ParsedSymbol ps2: pss) {
// If that symbol is not the same symbol as this one and ends before this one
if ((!ps2.equals(ps)) && (ps2.getEndIndex()<=ps.getEndIndex())) {
// ... and is the next symbol of the rule and does not consist of only this one.
if (r.getRight().size()!=1 || ps2.getEndIndex()==ps.getEndIndex())
if (matches(ps2.getType(),r,0)) {
searchTuples(ps,ets,r,0,ps2,et);
}
}
}
}
}
associativities.searchTuples(ps, ets);
tupleMap.put(ps, ets);
return ets;
}
private void searchTuples (
InputSymbol ps,
Set tuples,
Rule r,
int i,
ParsedSymbol ps2,
Tuple act )
{
if (i >= r.getRight().size()) {
int lastIndex = -1;
for (int j = act.getSymbolCount()-1;lastIndex == -1 && j >= 0;j--) {
InputSymbol par = act.getSymbol(j);
if (par != null)
lastIndex = par.getEndIndex();
}
if (lastIndex == ps.getEndIndex())
tuples.add(act);
} else {
// Si el siguiente de la regla est? en g.getEmptyRules(), hacer searchTuples con i+1
if (pg.getGrammar().isNullable(r.getRight().get(i).getType())) {
Set rules = pg.getGrammar().getEmptyRules(r.getRight().get(i).getType());
if (rules==null) {
Tuple n = new Tuple(r);
n.addSymbols(act.getSymbols());
n.addSymbol(null);
searchTuples(ps,tuples,r,i+1,ps2,n);
} else {
for (Object id: rules) {
ParsedSymbol nps = new ParsedSymbol(id,-1,-1,"");
Tuple n = new Tuple(r);
n.addSymbols(act.getSymbols());
n.addSymbol(nps);
searchTuples(ps,tuples,r,i+1,ps2,n);
}
}
}
if (ps2 != null) {
if (r.getRight().get(i).getType().equals(ps2.getType())) {
Tuple n = new Tuple(r);
n.addSymbols(act.getSymbols());
n.addSymbol(ps2);
Set pss = pg.getFollowing(ps2);
if (pss != null) {
for (ParsedSymbol psn: pss) {
if ((!psn.equals(ps)) && (psn.getEndIndex()<=ps.getEndIndex())) {
searchTuples(ps,tuples,r,i+1,psn,n);
}
}
}
searchTuples(ps,tuples,r,i+1,null,n);
}
}
}
}
private void expandSymbol (
Set ret,
ParsedSymbol ps,
Tuple tuple,
int index,
List content,
List elements )
{
Rule rule = tuple.getRule();
if (index >= rule.getRight().size()) {
if (content.get(content.size()-1).getEndIndex()<=ps.getEndIndex()) {
Symbol s = new Symbol(id,ps,rule,elements,content);
id++;
if (associativities.isAssociate(s))
associativities.associate(s);
if (!isInhibited(rule,s)) {
if (build(s.getRule(),s)) {
if (rule.getRight().size() == 1)
if (associativities.isAssociated(content.get(0)))
associativities.associate(s);
storeSymbol(s);
for (int j = 0;j < s.size();j++)
addUses(s,s.getContent(j));
ret.add(s);
precedences.process(rule, s);
}
}
}
} else {
if (tuple.getSymbol(index) == null) {
expandSymbol(ret,ps,tuple,index+1,content,elements);
} else {
// Update content and element lists without creating copies of them
elements.add(rule.getRight().get(index));
content.add(null);
for (Symbol s: expand(tuple.getSymbol(index))) {
content.set(content.size()-1, s);
expandSymbol(ret, ps, tuple, index+1, content, elements);
}
elements.remove(elements.size()-1);
content.remove(content.size()-1);
}
}
}
/**
* Check if a rule should be inhibited
* @param r Rule
* @param s Symbol
* @return true when the rule is inhibited
*/
private boolean isInhibited (Rule r, Symbol s)
{
boolean inhibited = false;
if (!inhibited)
inhibited = associativities.inhibit(r, s);
if (!inhibited)
inhibited = compositions.inhibit(r, s);
return inhibited;
}
/**
* Check if a symbol matches a rule at the given position
* @param type the type of the symbol
* @param r the rule
* @param index the position within the rule
* @return
*/
private boolean matches (Object type, Rule r, int index)
{
int i = index;
while (i < r.getRight().size()) {
if (type.equals(r.getRight().get(i).getType()))
return true;
else if(pg.getGrammar().isNullable(r.getRight().get(i).getType()))
i++;
else
return false;
}
return false;
}
// Parsing metadata
private void addUses (Symbol s, Symbol get)
{
Set useds = usedIn.get(get);
if (useds == null) {
useds = new HashSet();
usedIn.put(get,useds);
}
useds.add(s);
}
private void storeSymbol(Symbol s)
{
symbols.add(s);
storeMetadata(s);
// Symbols starting at the same index
List at = startingAt.get(s.getStartIndex());
if (at==null) {
at = new ArrayList();
startingAt.put(s.getStartIndex(), at);
}
at.add(s);
}
private void storeMetadata(Symbol symbol)
{
if (objectMetadata!=null) {
Object value = symbol.getUserData();
objectMetadata.get("startIndex").put(value, symbol.getStartIndex());
objectMetadata.get("endIndex").put(value, symbol.getEndIndex());
objectMetadata.get("symbol").put(value, symbol);
for (int i = 0;i < symbol.size();i++)
storeMetadata(symbol.getContent(i));
}
}
}