/** * Copyright (c) 2009-2011, The HATS Consortium. All rights reserved. * This file is licensed under the terms of the Modified BSD License. */ package abs.frontend.parser; import abs.frontend.ast.*; import java.util.Map; import java.util.HashMap; import java.util.LinkedList; /** * Preprocesses the AST directly after it has been parsed, before any name and type analysis. * Typically, syntactic sugar is eliminated in this phase * Currently the following things are done: * * - selector names of constructors are transformed to functions * * @author Jan Schäfer * */ public class ASTPreProcessor { public final static String FUNCTIONSELECTOR = "selector"; public CompilationUnit preprocess(CompilationUnit unit) { for (ModuleDecl d : unit.getModuleDecls()) { preprocess(d); } return unit; } private void preprocess(ModuleDecl moduleDecl) { for (Decl decl : moduleDecl.getDecls()) { if (decl.isDataType()) { DataTypeDecl dtd = (DataTypeDecl) decl; for (FunctionDecl fd : createSelectorFunctions(dtd, false)) { moduleDecl.addDeclNoTransform(fd); } } } } public LinkedList<FunctionDecl> createSelectorFunctionsForDeltaApplication(DataTypeDecl dtd) { return createSelectorFunctions(dtd, true); } private List<Pattern> makePatternList(int constructorArgs, int numArg) { List<Pattern> patternList = new List<Pattern>(); for (int i = 0; i < constructorArgs; i++) { if (i == numArg) patternList.add(new PatternVar(new PatternVarDecl("res"))); else patternList.add(new UnderscorePattern()); } return patternList; } private FunctionDef makeAccessorFunction(LinkedList<DataDeclarationArg> args) { List<CaseBranch> branches = new List<CaseBranch>(); for (DataDeclarationArg d : args) { String constructorName = d.getConstructor().getName(); List<Pattern> pattern = makePatternList(d.getConstructor().getNumConstructorArg(), d.getPosition()); branches.add(new CaseBranch(new ConstructorPattern(constructorName, pattern), new VarUse("res"))); } return new ExpFunctionDef(new CaseExp(new VarUse("data"), branches)); } /** * Given a data decleration, map each argument name to the corresponding * DataDeclarationArg, containing the data constructor, the argument and * the position of the argument. */ private Map<String, LinkedList<DataDeclarationArg>> makeConstructorMap(DataTypeDecl dtd) { Map<String, LinkedList<DataDeclarationArg>> constructors = new HashMap<String, LinkedList<DataDeclarationArg>>(); for (DataConstructor c : dtd.getDataConstructors()) { int argpos = 0; for (ConstructorArg ca : c.getConstructorArgs()) { if (ca.hasSelectorName()) { String name = ca.getSelectorName().getName(); LinkedList<DataDeclarationArg> tmp = constructors.get(name); if (tmp == null) { tmp = new LinkedList<DataDeclarationArg>(); constructors.put(name, tmp); } tmp.add(new DataDeclarationArg(c, ca, argpos)); } argpos++; } } return constructors; } /** * Given a data decleration, generate selector functions, e.g. * * <pre> * data Foo = Bar(Bool isTrue, String name) | Baz(String name); * </pre> * * creates: * <pre> * def Bool isTrue(Foo data) = * case data { * Bar(res,_) => res; * }; * * def String name(Foo data) = * case data { * Bar(_,res) => res; * Baz(res) => res; * }; * <pre> */ private LinkedList<FunctionDecl> createSelectorFunctions(DataTypeDecl dtd, boolean delta) { // We need to know what data constructor and what argument inside that // data constructors a name corresponds to. Map<String, LinkedList<DataDeclarationArg>> constructors = makeConstructorMap(dtd); LinkedList<FunctionDecl> fds = new LinkedList<FunctionDecl>(); // Make an accessor function for every argument name in the declaration for (String name : constructors.keySet()) { // Make the function body, containing a single case statement with // a branch for each constructor FunctionDef funDef = makeAccessorFunction(constructors.get(name)); // the type parameters of the function List<TypeParameterDecl> typeParams; // the type of the parameter of the function TypeUse paramType; if (dtd instanceof ParametricDataTypeDecl) { ParametricDataTypeDecl pdtd = (ParametricDataTypeDecl) dtd; typeParams = delta ? pdtd.getTypeParameterList().treeCopyNoTransform() : pdtd.getTypeParameterList(); List<TypeUse> typeParams2 = new List<TypeUse>(); for (TypeParameterDecl p : typeParams) { typeParams2.add(p.getType().toUse()); } paramType = new ParametricDataTypeUse(pdtd.getName(), new List<Annotation>(), typeParams2); } else { typeParams = new List<TypeParameterDecl>(); paramType = dtd.getType().toUse(); } List<ParamDecl> parameters = new List<ParamDecl>() .add(new ParamDecl("data",paramType,new List<Annotation>())); // Get an arbitrary constructor argument, which is used to decide // the return type of the function (if there are mismatching types, // then let the type checker catch it). ConstructorArg ca = constructors.get(name).element().getArg(); // the complete function definition FunctionDecl fd = new ParametricFunctionDecl( name, // function name (TypeUse)ca.getTypeUse().copy(), // type parameters, // parameters funDef, new List<Annotation>(), // annotations typeParams ); // annotate that this function is a selector function // such that backends will know about it fd.addAnnotation(new Annotation(new StringLiteral(FUNCTIONSELECTOR))); for (DataDeclarationArg d : constructors.get(name)) { setAllPositionsFromNode(fd, d.getArg()); } fds.add(fd); } return fds; } /** * A utility class for storing an constructor argument, along with the * enclosing constructor and the position. */ private class DataDeclarationArg { private final DataConstructor constructor; private final ConstructorArg arg; private final int position; public DataDeclarationArg(DataConstructor constructor, ConstructorArg arg, int position) { this.constructor = constructor; this.arg = arg; this.position = position; } public DataConstructor getConstructor(){ return constructor; } public ConstructorArg getArg(){ return arg; } public int getPosition(){ return position; } } /** * recursively set the position of this ast node and its childs */ private void setAllPositionsFromNode(ASTNode<?> node, ASTNode<?> fromNode) { node.setPositionFromNode(fromNode); for (int i=0; i < node.getNumChildNoTransform(); i++) { ASTNode<?> child = node.getChildNoTransform(i); setAllPositionsFromNode(child, fromNode); } } }