package de.skuzzle.polly.core.parser.ast.declarations; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import de.skuzzle.polly.core.parser.ParseException; import de.skuzzle.polly.core.parser.ParserProperties; import de.skuzzle.polly.core.parser.Position; import de.skuzzle.polly.core.parser.ast.Identifier; import de.skuzzle.polly.core.parser.ast.ResolvableIdentifier; import de.skuzzle.polly.core.parser.ast.declarations.types.Substitution; import de.skuzzle.polly.core.parser.ast.declarations.types.Type; import de.skuzzle.polly.core.parser.ast.expressions.Braced; import de.skuzzle.polly.core.parser.ast.expressions.VarAccess; import de.skuzzle.polly.core.parser.ast.expressions.literals.NumberLiteral; import de.skuzzle.polly.core.parser.ast.expressions.literals.StringLiteral; import de.skuzzle.polly.core.parser.ast.lang.Cast; import de.skuzzle.polly.core.parser.ast.lang.Operator.OpType; import de.skuzzle.polly.core.parser.ast.lang.functions.Comp; import de.skuzzle.polly.core.parser.ast.lang.functions.Day; import de.skuzzle.polly.core.parser.ast.lang.functions.FoldLeft; import de.skuzzle.polly.core.parser.ast.lang.functions.Id; import de.skuzzle.polly.core.parser.ast.lang.functions.Sort; import de.skuzzle.polly.core.parser.ast.lang.operators.BinaryArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.BinaryBooleanArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.BinaryStringArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.Conditional; import de.skuzzle.polly.core.parser.ast.lang.operators.DateArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.DateTimespanArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.ListIndex; import de.skuzzle.polly.core.parser.ast.lang.operators.NumberTimespanArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.RandomListIndex; import de.skuzzle.polly.core.parser.ast.lang.operators.Relational; import de.skuzzle.polly.core.parser.ast.lang.operators.ReverseList; import de.skuzzle.polly.core.parser.ast.lang.operators.ReverseString; import de.skuzzle.polly.core.parser.ast.lang.operators.StringIndex; import de.skuzzle.polly.core.parser.ast.lang.operators.TernaryDotDot; import de.skuzzle.polly.core.parser.ast.lang.operators.TimespanArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.TransposeOperator; import de.skuzzle.polly.core.parser.ast.lang.operators.UnaryArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.UnaryBooleanArithmetic; import de.skuzzle.polly.core.parser.ast.lang.operators.UnaryList; import de.skuzzle.polly.core.parser.ast.lang.operators.UnaryTimespan; import de.skuzzle.polly.core.parser.ast.visitor.ASTTraversalException; import de.skuzzle.polly.core.parser.ast.visitor.Unparser; import de.skuzzle.polly.core.parser.problems.ProblemReporter; import de.skuzzle.polly.core.parser.problems.Problems; import de.skuzzle.polly.tools.strings.StringUtils; /** * <p>This class represents a hierarchical store for variable declarations. Every * namespace has a parent namespace, except the NATIVE namespace. Namespaces that are not * created with a parent will automatically have the PUBLIC namespace as parent. The * NATIVE space contains all native operator and constant declarations that can be * used, the PUBLIC space will contain declarations declared public.</p> * * <p>For each variable name can be stored a list of declarations. Declarations with * the same name must have distinct unique types. Resolving of a variable name will first * search the current namespace and then all parent namespaces.</p> * * <p>Namespaces can be obtained using any of the <code>forName</code> methods. The * returned namespace instance will store all declared variables into a file with the * name of the namespace.</p> * * @author Simon Taddiken */ public class Namespace { /** * Percentage of word length that will be used as levenshtein threshold in * {@link #findSimilar(String, Namespace)}. */ private final static float LEVENSHTEIN_THRESHOLD_PERCENT = 0.6f; /** Name of the public namespace */ public final static String PUBLIC_NAMESPACE_NAME = "~public"; public static File declarationFolder; public static synchronized void setDeclarationFolder(File declarationFolder) { Namespace.declarationFolder = declarationFolder; } /** * Private namespace extension that will store the namespace's contents to file * whenever a new variable is declared or deleted. * * @author Simon Taddiken */ private final static class StorableNamespace extends Namespace { private String fileName; public StorableNamespace(String fileName, Namespace parent) { super(parent); this.fileName = fileName; } @Override public synchronized void declare(Declaration decl) throws ASTTraversalException { super.declare(decl); if (decl.isNative()) { return; } try { this.store(); } catch (IOException ignore) { throw new ASTTraversalException(decl.getPosition(), "Deklaration konnte nicht gespeichert werden"); } } @Override protected void delete(Iterator<Declaration> it) { super.delete(it); try { this.store(); } catch (IOException e) { e.printStackTrace(); } } /** * Stores the content of this namespace (no parent namespaces) to a file * in the folder set by {@link Namespace#setDeclarationFolder(File)}. * * @throws IOException If storing of the namespace fails. */ public void store() throws IOException { if (declarationFolder == null) { throw new IOException("declaration folder has not been set"); } PrintWriter pw = null; try { pw = new PrintWriter(new File(declarationFolder, this.fileName)); final Unparser up = new Unparser(pw); for (final List<Declaration> decls : this.decls.values()) { for (final Declaration decl : decls) { new Braced(decl.getExpression().getPosition(), decl.getExpression()).visit(up); pw.print("->"); if (decl.isPublic()) { pw.print("public "); } if (decl.getName().wasEscaped()) { pw.print("\\"); } pw.println(decl.getName().getId()); } } } catch (ASTTraversalException e) { throw new RuntimeException(e); } finally { if (pw != null) { pw.close(); } } } } /** * Helper class that encapsulates the result of a Levenshtein comparison: the distance * and the actual found string. * @author Simon Taddiken */ private final static class LevenshteinResult implements Comparable<LevenshteinResult> { private int distance; public String string; public LevenshteinResult(int distance, String string) { super(); this.distance = distance; this.string = string; } @Override public int compareTo(LevenshteinResult o) { return Integer.compare(this.distance, o.distance); } } /** * <p>Tries to find declarations that have a name similar to the given string. All * declarations in all parent namespaces, beginning at the given one are searched. * All declarations that have a levenshtein distance lower than * {@link #LEVENSHTEIN_THRESHOLD_PERCENT} of the given string's length will be * returned. If no such declaration was found, the resulting list will be empty.</p> * * <p>The resulting list will be sorted by the distance. The declaration with the * lowest distance to the given string will be on first place and so on.</p> * * @param given The string to compare with all declarations. * @param nspace The root namespace to search. All parent namespaces are searched * as well. * @return A collection of similar declaration names. * @see StringUtils */ private final static List<String> findSimilar(String given, Namespace nspace) { final int threshold = Math.max(1, (int) Math.round(given.length() * LEVENSHTEIN_THRESHOLD_PERCENT)); final List<LevenshteinResult> results = new ArrayList<Namespace.LevenshteinResult>(); for(Namespace space = nspace; space != null; space = space.parent) { final List<Declaration> decls = space.decls.get(given); if (decls == null) { continue; } for (final Declaration decl : decls) { final String name = decl.getName().getId(); // only take valid identifiers into account (this will skip operator // declarations. if (!Character.isJavaIdentifierPart(name.charAt(0))) { continue; } final int dist = StringUtils.getLevenshteinDistance(name, given); if (dist <= threshold) { results.add(new LevenshteinResult(dist, decl.getName().getId())); } } } Collections.sort(results); final List<String> stringResults = new ArrayList<String>(results.size()); for (final LevenshteinResult lr : results) { stringResults.add(lr.string); } return stringResults; } /** * This is the root namespace of all user namespaces. It contains all operator and * native function declarations. */ private final static Namespace NATIVE = new Namespace(null) { @Override public void declare(Declaration decl) throws ASTTraversalException { if (!decl.isNative()) { throw new IllegalArgumentException( "NATIVE namespace must only contain native declarations."); } super.declare(decl); } @Override protected void delete(Iterator<Declaration> it) { throw new UnsupportedOperationException( "can not delete from native namespace."); } }; // initialize native declarations static { try { final Declaration pi = new Declaration(Position.NONE, new Identifier(Position.NONE, "pi"), new NumberLiteral(Position.NONE, Math.PI)); pi.setNative(true); final Declaration e = new Declaration(Position.NONE, new Identifier(Position.NONE, "e"), new NumberLiteral(Position.NONE, Math.E)); e.setNative(true); NATIVE.declare(pi); NATIVE.declare(e); // casting ops NATIVE.declare(new Cast(OpType.STRING, Type.STRING).createDeclaration()); NATIVE.declare(new Cast(OpType.NUMBER, Type.NUM).createDeclaration()); NATIVE.declare(new Cast(OpType.DATE, Type.DATE).createDeclaration()); NATIVE.declare(new Cast(OpType.TIMESPAN, Type.TIMESPAN).createDeclaration()); NATIVE.declare(new Cast(OpType.CHANNEL, Type.CHANNEL).createDeclaration()); NATIVE.declare(new Cast(OpType.USER, Type.USER).createDeclaration()); // relational ops NATIVE.declare(new Relational(OpType.EQ).createDeclaration()); NATIVE.declare(new Relational(OpType.NEQ).createDeclaration()); NATIVE.declare(new Relational(OpType.LT).createDeclaration()); NATIVE.declare(new Relational(OpType.ELT).createDeclaration()); NATIVE.declare(new Relational(OpType.GT).createDeclaration()); NATIVE.declare(new Relational(OpType.EGT).createDeclaration()); // Arithmetic binary ops NATIVE.declare(new BinaryArithmetic(OpType.ADD).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.SUB).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.MUL).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.DIV).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.INTDIV).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.INT_AND).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.INT_OR).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.MOD).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.POWER).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.LEFT_SHIFT).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.RIGHT_SHIFT).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.URIGHT_SHIFT).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.RADIX).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.MIN).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.MAX).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.XOR).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.ATAN2).createDeclaration()); NATIVE.declare(new BinaryArithmetic(OpType.HYPOT).createDeclaration()); // boolean arithmetic NATIVE.declare(new BinaryBooleanArithmetic(OpType.BOOLEAN_AND).createDeclaration()); NATIVE.declare(new BinaryBooleanArithmetic(OpType.XOR).createDeclaration()); NATIVE.declare(new BinaryBooleanArithmetic(OpType.BOOLEAN_OR).createDeclaration()); NATIVE.declare(new BinaryBooleanArithmetic(OpType.IMPLICATION).createDeclaration()); NATIVE.declare(new BinaryBooleanArithmetic(OpType.EQUIVALENCE).createDeclaration()); NATIVE.declare(new BinaryBooleanArithmetic(OpType.AND_OR).createDeclaration()); // boolean unary NATIVE.declare(new UnaryBooleanArithmetic(OpType.EXCLAMATION).createDeclaration()); // Arithmetic timespan and date binary ops NATIVE.declare(new TimespanArithmetic(OpType.ADD).createDeclaration()); NATIVE.declare(new TimespanArithmetic(OpType.SUB).createDeclaration()); NATIVE.declare(new DateTimespanArithmetic(OpType.ADD).createDeclaration()); NATIVE.declare(new DateTimespanArithmetic(OpType.SUB).createDeclaration()); NATIVE.declare(new DateArithmetic(OpType.SUB).createDeclaration()); NATIVE.declare(new UnaryTimespan().createDeclaration()); NATIVE.declare(new NumberTimespanArithmetic().createDeclaration()); // String operators NATIVE.declare(new BinaryStringArithmetic(OpType.ADD).createDeclaration()); NATIVE.declare(new StringIndex().createDeclaration()); NATIVE.declare(new ReverseString().createDeclaration()); // Arithmetic unary ops NATIVE.declare(new UnaryArithmetic(OpType.EXCLAMATION).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.SUB).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.LOG).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.LN).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.SQRT).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.CEIL).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.FLOOR).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.ROUND).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.SIG).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.COS).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.SIN).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.TAN).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.ACOS).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.ATAN).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.ASIN).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.ABS).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.TO_DEGREES).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.TO_RADIANS).createDeclaration()); NATIVE.declare(new UnaryArithmetic(OpType.EXP).createDeclaration()); // list unary op NATIVE.declare(new UnaryList(OpType.EXCLAMATION).createDeclaration()); NATIVE.declare(new ListIndex(OpType.INDEX).createDeclaration()); NATIVE.declare(new RandomListIndex(OpType.QUEST_EXCL).createDeclaration()); NATIVE.declare(new RandomListIndex(OpType.QUESTION).createDeclaration()); NATIVE.declare(new ReverseList().createDeclaration()); // DOTDOT NATIVE.declare(new TernaryDotDot().createDeclaration()); // ternary ops NATIVE.declare(new Conditional(OpType.IF).createDeclaration()); // FUNCTIONS NATIVE.declare(new FoldLeft().createDeclaration()); NATIVE.declare(new Id().createDeclaration()); NATIVE.declare(new de.skuzzle.polly.core.parser.ast.lang.functions.Map().createDeclaration()); NATIVE.declare(new Comp().createDeclaration()); NATIVE.declare(new Sort().createDeclaration()); NATIVE.declare(new Day().createDeclaration()); NATIVE.declare(new TransposeOperator().createDeclaration()); } catch (ASTTraversalException e) { throw new ExceptionInInitializerError(e); } } /** Contains all user namespaces mapped to their user name */ private final static Map<String, Namespace> ROOTS = new HashMap<String, Namespace>(); /** Namespace for public declarations. */ private final static Namespace PUBLIC = new StorableNamespace(PUBLIC_NAMESPACE_NAME + ".decl", NATIVE); static { ROOTS.put(PUBLIC_NAMESPACE_NAME, PUBLIC); } /** * Declares a variable in PUBLIC namespace. * * @param decl The declaration to add. * @throws ASTTraversalException If declaring fails. */ public final static void declarePublic(Declaration decl) throws ASTTraversalException { PUBLIC.declare(decl); } /** * Deletes a declaration from PUBLIC namespace. * * @param id Name of the declaration to delete. * @return Number of deleted declarations. */ public final static int deletePublic(Identifier id) { return PUBLIC.delete(id); } /** * Gets the toplevel namespace with the given name. If no namespace with that name * exists, it is created. * * @param name The name of the namespace to retrieve. * @return The namespace. */ public final static Namespace forName(String name) { Namespace check = ROOTS.get(name); if (check == null) { check = new StorableNamespace(name + ".decl", PUBLIC); ROOTS.put(name, check); } return check; } /** * Gets the toplevel namespace with the given name. If no namespace with that name * exists, it is created. * * @param name The name of the namespace to retrieve. * @return The namespace. */ public final static Namespace forName(Identifier name) { return forName(name.getId()); } /** * Checks whether a root namespace with given name exists. * * @param name name to check. * @return <code>true</code> if a namespace with that name exists. */ public final static boolean exists(String name) { return ROOTS.containsKey(name); } /** * Checks whether a root namespace with given name exists. * * @param name name to check. * @return <code>true</code> if a namespace with that name exists. */ public final static boolean exists(Identifier name) { return exists(name.getId()); } /** Declarations by name in this namespace */ protected final Map<String, List<Declaration>> decls; /** Parent of this namespace. <code>null</code> for root namespace. */ protected final Namespace parent; /** * Creates a new namespace which parent namespace is the given one. * * @param parent Parent namespace. */ public Namespace(Namespace parent) { this.parent = parent; this.decls = new HashMap<String, List<Declaration>>(); } /** * Creates a new Namespace that contains the same declarations as this one. Each * parent namespace is copied as well. That means any changes made to the derived * namespace are <b>not</b> reflected to the original space. This excludes * modifications made to the stored declarations, as they are the same! * * @return A new {@link Namespace}. */ public Namespace derive() { if (this.parent == null) { return null; } final Namespace result = new Namespace(this.parent.derive()); result.decls.putAll(this.decls); return result; } /** * Creates a new Namespace which contains the same declarations as this one. The * new namespace will have the given namespace as parent. * * @param parent The parent namespace for the new namespace. * @return A new {@link Namespace}. */ public Namespace derive(Namespace parent) { if (parent == this) { throw new IllegalArgumentException("namespace can not be its own parent"); } final Namespace result = new Namespace(parent); result.decls.putAll(this.decls); return result; } /** * Creates a new sub namespace of this one and returns it. * * @return The new namespace. */ public Namespace enter() { final Namespace ns = new Namespace(this); return ns; } /** * Gets the parent namespace of this namespace. Returns <code>null</code> for the * root namespace. * * @return The parent namespace. */ public Namespace getParent() { return this.parent; } /** * Gets all declarations of the current level. * * @return Map of declarations. */ public Map<String, List<Declaration>> getDeclarations() { return this.decls; } /** * Declares a new variable in this namespace. * * @param decl The function to declare. * @throws ASTTraversalException If a function with the same name and signature * already exists in this namespace. */ public void declare(Declaration decl) throws ASTTraversalException { /*if (decl.getType() == Type.UNKNOWN) { throw new IllegalStateException( "cannot declare variable with unresolved type: " + decl); }*/ List<Declaration> d = this.decls.get(decl.getName().getId()); if (d == null) { d = new ArrayList<Declaration>(); this.decls.put(decl.getName().getId(), d); } // overloading is only allowed for native declarations, so check if decl with that // name already exists final Iterator<Declaration> it = d.iterator(); while (it.hasNext()) { final Declaration existing = it.next(); if (!decl.isNative()) { if (existing.getName().getId().equals(decl.getName().getId())) { this.delete(it); } } else { // native declarations can be overloaded, but must have distinct types /*if (Type.tryUnify(decl.getType(), existing.getType())) { throw new ASTTraversalException(decl.getPosition(), "Deklaration mit identischem Typ existiert bereits."); }*/ } } d.add(decl); } protected void delete(Iterator<Declaration> it) { it.remove(); } /** * Removes all declarations in this namespace with the given name. * * @param id Name of the declaration to delete. * @return How many declarations have been removed. */ public int delete(Identifier id) { List<Declaration> decls = this.decls.get(id.getId()); if (decls == null) { return 0; } final Iterator<Declaration> it = decls.iterator(); int i = 0; while (it.hasNext()) { final Declaration d = it.next(); if (!d.isNative()) { this.delete(it); ++i; } } return i; } /** * Resolves the first declaration with the given name that is found. This will search * all parent name spaces too and return when the first matching declaration was * found. * * @param name Name of declaration to resolved. * @return The resolved declaration. * @throws ASTTraversalException If declaration does not exist or multiple * declarations with that name exists in current level. */ public Declaration resolveFirst(ResolvableIdentifier name) throws ASTTraversalException { for (Namespace space = this; space != null; space = space.parent) { final List<Declaration> decls = space.decls.get(name.getId()); if (decls == null || decls.isEmpty()) { continue; } else if (decls.size() != 1) { throw new ASTTraversalException(name.getPosition(), "Ambiguous"); } return decls.get(0); } if (ParserProperties.should(ParserProperties.REPORT_UNKNOWN_VARIABLES)) { throw new ASTTraversalException(name.getPosition(), "Unbekannt: " + name.getId()); } else { return new Declaration(name.getPosition(), name, new StringLiteral(name.getPosition(), name.getId())); } } /** * <p>Tries to resolve a declaration with the given name and type. If * none was found, <code>null</code> is returned. This will return the first * matching declaration in this or any of the parent namespaces.</p> * * <p>If a matching declaration was found, it will, before being returned, stored * in the given identifiers declaration attribute.</p> * * @param name The name of the variable to resolve. * @param signature The signature of the variable to resolve. * @return The resolved declaration or <code>null</code> if non was found. */ public Declaration tryResolve(ResolvableIdentifier name, Type signature) { for(Namespace space = this; space != null; space = space.parent) { final List<Declaration> decls = space.decls.get(name.getId()); if (decls == null) { continue; } for (Declaration decl : decls) { if (Type.tryUnify(signature, decl.getType())) { name.setDeclaration(decl); return decl; } } } if (ParserProperties.should(ParserProperties.REPORT_UNKNOWN_VARIABLES)) { return null; } else { final Declaration result = new Declaration(name.getPosition(), name, new StringLiteral(name.getPosition(), name.getId())); name.setDeclaration(result); return result; } } /** * Looks up all declarations with the given name in this and all parent namespaces. * * @param name The name to lookup. * @return Collection of matching declarations. */ public Collection<Declaration> lookupAll(ResolvableIdentifier name) { final List<Declaration> result = new ArrayList<Declaration>(); for(Namespace space = this; space != null; space = space.parent) { final List<Declaration> decls = space.decls.get(name.getId()); if (decls == null) { continue; } result.addAll(decls); } return result; } /** * Looks up all possible declared types for the given identifier. Declarations on a * higher level are ignored if a declaration with identical type is found on a lower * level. Additionally, this method replaces all type variables with fresh ones for * all declarations that are no formal parameter. * * @param access VarAccess to resolve. * @param reporter Problem reporter to use when variable does not exist. * @return A set of all declarations with matching names. * @throws DeclarationException If no variable with matching name was found. * @throws ParseException May be thrown by problem reporter. */ public Set<Type> lookupFresh(VarAccess access, ProblemReporter reporter) throws DeclarationException, ParseException { final ResolvableIdentifier name = access.getIdentifier(); final Set<Type> result = new HashSet<Type>(); for(Namespace space = this; space != null; space = space.parent) { final List<Declaration> decls = space.decls.get(name.getId()); if (decls == null) { continue; } for (Declaration decl : decls) { if (decl.getName().equals(name)) { // check if we already found a declaration with the same type on a // lower declaration level. If so, the current declaration will be // ignored. That will have the effect that declarations on lower // levels override those on a higher level. /*for (final Type alreadyFound : result) { if (Type.tryUnify(alreadyFound, decl.getType())) { // continue with next declaration and ignore this one //continue ignore; } }*/ final Type fresh = decl.getType().subst(Substitution.fresh()); result.add(fresh); name.addDeclaration(decl); decl.onLookup(access); } } } if (result.isEmpty()) { if (ParserProperties.should(ParserProperties.REPORT_UNKNOWN_VARIABLES)) { reporter.semanticProblem(Problems.UNKNOWN_VAR, access.getPosition(), access.getIdentifier().getId()); } else { return Collections.singleton(Type.STRING); } } return result; } @Override public String toString() { final StringBuilder b = new StringBuilder(); int level = 0; for(Namespace space = this; space != null; space = space.parent) { b.append("Level: "); b.append(level++); b.append("\n"); for (final List<Declaration> decls : space.decls.values()) { final List<Declaration> copy = new ArrayList<Declaration>(decls); Collections.sort(copy); for (final Declaration decl : copy) { b.append(" '"); b.append(decl.getName()); b.append("'"); b.append(" = "); if (!decl.isNative()) { b.append(Unparser.toString(decl.getExpression())); b.append(" "); } b.append(decl.getType()); b.append("\n"); } } } return b.toString(); } }