Scope.java

package Hoot.Runtime.Behaviors;

import java.util.*;
import Hoot.Runtime.Values.*;
import Hoot.Runtime.Emissions.*;
import Hoot.Runtime.Faces.Resulting;
import static Hoot.Runtime.Functions.Utils.*;

/**
 * A language scope, and management thereof.
 *
 * @author nik <nikboyd@sonic.net>
 * @see "Copyright 2010,2021 Nikolas S Boyd."
 * @see "Permission is granted to copy this work provided this copyright statement is retained in all copies."
 */
public abstract class Scope extends NamedItem implements Resulting {
    
    static final Deque<Scope> FileScopes = new ArrayDeque<>();
    static void pushFileScope(Scope s) { FileScopes.push(s); }
    public static void popFileScope() { popSafely(FileScopes); }
    public static Scope currentFile() { return peekSafely(FileScopes); }
    public static Scope makeCurrentFile(Scope s) { return makeCurrent(s, FileScopes); }

    static final Deque<Scope> BlockScopes = new ArrayDeque<>();
    static void pushBlockScope(Scope s) { BlockScopes.push(s); }
    public static void popBlockScope() { popSafely(BlockScopes); }
    public static Scope currentBlock() { return peekSafely(BlockScopes); }
    public static Scope makeCurrentBlock(Scope s) { return makeCurrent(s, BlockScopes); }
    
    public static List<Scope> recentBlockScopes() {
        List<Scope> bs = new ArrayList<>(); // scan available block scopes
        BlockScopes.iterator().forEachRemaining((b) -> { 
            if (bs.isEmpty()) bs.add(0, b); else if (!bs.get(0).isMethod()) bs.add(0, b); });
        return reverseList(bs); }

    public static Scope findCurrentMethod() {
        List<Scope> bs = new ArrayList<>(); // scan available block scopes
        BlockScopes.iterator().forEachRemaining((b) -> { if (b.isMethod() && bs.isEmpty()) bs.add(b); });
        return bs.isEmpty()? null: bs.get(0); }

    static void popSafely(Deque<?> s) { if (!s.isEmpty()) s.pop(); }
    static <S extends Scope> S peekSafely(Deque<S> s) { return s.isEmpty()? null: (S)s.peek(); }
    static <S extends Scope>boolean currently(S scope, Deque<S> s) { return peekSafely(s) == scope; }
    static <S extends Scope> S makeCurrent(S scope, Deque<S> s) { if (!currently(scope, s)) s.push(scope); return scope; }

    // make scope stack for files, faces, methods = blocks
    private void pushScope(Scope scope) {
        if (scope.isFile()) pushFileScope(scope);
        if (scope.isBlock() || scope.isMethod()) pushBlockScope(scope);
//        reportScope(scope);
    }

    public Scope popScope() {
        return reportScope(currentScope());
    }

    public Scope(NamedItem parentItem) { super(parentItem); }
    @Override public String description() { return name(); }
    @Override public void clean() { super.clean(); locals().clean(); 
        whisper("cleaned " + description()); }

    protected void currentScope(Scope scope) { }
    public Scope currentScope() { return current(); }
    public Scope makeCurrent() { pushScope(this); return this; }
    public static Scope current() { return nullOr(f -> f.currentScope(), currentFile()); }
    private Scope reportScope(Scope scope) { if (hasAny(scope)) scope.reportScope(); return scope; }
    public Scope reportScope() {
//        report("scope now " + description());
        return this; }

//    public boolean resolves(Named reference) { return false; }
//    public Scope scopeResolving(Named reference) { return containerScope().scopeResolving(reference); }
//    public Class resolveType(Named reference) { return containerScope().resolveType(reference); }
//    public String resolveTypeName(Named reference) { return containerScope().resolveTypeName(reference); }

    protected Table locals = new Table(this);
    public Table locals() { return this.locals; }
    public void addLocal(Variable variable) { locals().addSymbol(variable); }
    public boolean hasLocals() { return !locals().isEmpty(); }
    public boolean hasLocal(String symbolName) { return locals().containsSymbol(symbolName); }
    public Variable localNamed(String symbol) { return locals().symbolNamed(symbol); }

    @Override public Emission emitItem() { return emitScope(); }
    @Override public Emission emitOptimized() { return emitScope(); }
    @Override public Emission emitPrimitive() { return emitScope(); }

    public Emission emitScope() { return null; } // derived classes override this!
    public List<Emission> emitLocalVariables() { return map(locals().definedSymbols(), v -> v.emitLocal()); }

} // Scope