All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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.

The newest version!
/*
 * 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));
    	}
    }
}