TranslatorState.java

package org.sterling.source.translator;

import static org.sterling.runtime.expression.ExpressionFactory.declaration;

import java.util.*;
import org.sterling.SterlingException;
import org.sterling.runtime.GlobalModule;
import org.sterling.runtime.expression.DeclaredExpression;
import org.sterling.runtime.expression.Expression;
import org.sterling.runtime.expression.ExpressionFactory;
import org.sterling.runtime.expression.Variable;
import org.sterling.source.syntax.SourceNode;

public class TranslatorState {

    private final GlobalModule globalModule;
    private final ModuleBuilder module;
    private final Set<DeclaredExpression> declarations;
    private final Deque<Expression> expressions;
    private final Deque<Scope> scopes;
    private final Deque<SourceNode> trees;
    private final AliasBuilder importBuilder;
    private final Map<String, String> imports;
    private final Deque<ObjectBuilder> objects;

    public TranslatorState(GlobalModule globalModule) {
        this.globalModule = globalModule;
        this.module = new ModuleBuilder();
        this.declarations = new HashSet<>();
        this.expressions = new ArrayDeque<>();
        this.scopes = new ArrayDeque<>();
        this.trees = new ArrayDeque<>();
        this.importBuilder = new AliasBuilder();
        this.imports = new HashMap<>();
        this.objects = new ArrayDeque<>();
        this.scopes.push(new Scope());
    }

    public void acceptImports() {
        for (Alias alias : importBuilder.acceptImports()) {
            imports.put(alias.getAlias(), alias.getIdentifier());
        }
    }

    public void acceptModule() {
        module.acceptModule();
    }

    public Expression acceptObject() {
        return objects.pop().toObject();
    }

    public void addImport(String importName) {
        importBuilder.addImport(importName);
    }

    public void appendFrom(String part) {
        importBuilder.appendFrom(part);
    }

    public void appendModule(String part) {
        module.appendModule(part);
    }

    public void beginObject() {
        objects.push(new ObjectBuilder());
    }

    public void declare(String identifier, Expression expression) throws SterlingException {
        declarations.add(declaration(identifier, expression));
    }

    public void declareMember(String identifier, Expression expression) {
        objects.peek().declareMember(identifier, expression);
    }

    public Variable define(Variable variable) {
        return scope().define(variable);
    }

    public void enterScope() {
        scopes.add(new Scope());
    }

    public Set<DeclaredExpression> getDeclarations() {
        return new HashSet<>(declarations);
    }

    public void leaveScope() {
        scopes.pop();
    }

    public Expression popExpression() {
        return expressions.pop();
    }

    public SourceNode popSource() {
        return trees.pop();
    }

    public void pushExpression(Expression expression) {
        expressions.push(expression);
    }

    public void pushSource(SourceNode tree) {
        trees.push(tree);
    }

    public Expression reference(String identifier) {
        if (scope().has(identifier)) {
            return scope().get(identifier);
        } else {
            return ExpressionFactory.reference(qualifyIdentifier(identifier), globalModule);
        }
    }

    public void setAlias(String alias) {
        importBuilder.setAlias(alias);
    }

    private Scope scope() {
        return scopes.peek();
    }

    private String qualifyDeclaration(String identifier) {
        if (module.isDeclared()) {
            return module.getIdentifier() + "/" + identifier;
        } else {
            return identifier;
        }
    }

    private String qualifyIdentifier(String identifier) {
        if (imports.containsKey(identifier)) {
            return imports.get(identifier);
        } else {
            return qualifyDeclaration(identifier);
        }
    }

    private static final class Scope {

        private final Map<String, Variable> variables;

        public Scope() {
            variables = new HashMap<>();
        }

        public Variable define(Variable variable) {
            variables.put(variable.getIdentifier(), variable);
            return variable;
        }

        public Expression get(String identifier) {
            return variables.get(identifier);
        }

        public boolean has(String identifier) {
            return variables.containsKey(identifier);
        }
    }
}