src/java/mantra/symbols/VariableSymbol.java
public class VariableSymbol extends BaseSymbol {
	public boolean isConstant;
	public VariableSymbol(Scope scope, String name, Type type) { super(scope, name, type); }
}
src/java/mantra/codegen/model/MMethod.java
public class MMethod extends MMember {
	public FunctionSymbol sym;
	public String name;
	@NestedModel public List<MArg> args = new ArrayList<MArg>();
	public String javaRetType;

	public MMethod(FunctionSymbol sym) {
		this.sym = sym;
		this.name = sym.name;
		for (Arg a : sym.args) {
			args.add( new MArg(a) );
		}
		javaRetType = JavaGenerator.asJavaType(sym.retType);
	}
}
src/java/mantra/Tool.java
public class Tool {
	public static final String VERSION = "0.1";
	public static final String MANTRA_EXTENSION = ".om";

	public static enum OptionArgType { NONE, STRING } // NONE implies boolean
	public static class Option {
		String fieldName;
		String name;
		OptionArgType argType;
		String description;

		public Option(String fieldName, String name, String description) {
			this(fieldName, name, OptionArgType.NONE, description);
		}

		public Option(String fieldName, String name, OptionArgType argType, String description) {
			this.fieldName = fieldName;
			this.name = name;
			this.argType = argType;
			this.description = description;
		}
	}

	public static Option[] optionDefs = {
		new Option("compileOnly",		"-c", OptionArgType.STRING, "compile to Java only (default)"),
		new Option("outputDirectory",	"-d", OptionArgType.STRING, "root directory to generate files"),
		new Option("warnings_are_errors", "-Werror", "treat warnings as errors"),
		new Option("msgFormat",			"-message-format", OptionArgType.STRING, "specify output style for messages in mantra, gnu, vs2005"),
	};

	// option fields
	public boolean compileOnly = false;
	public boolean warnings_are_errors = false;
	public String msgFormat = "mantra";
	public String outputDirectory;

	public ErrorManager errMgr;

	List<MantraToolListener> listeners = new CopyOnWriteArrayList<MantraToolListener>();
	/** Track separately so if someone adds a listener, it's the only one
	 *  instead of it and the default stderr listener.
	 */
	DefaultToolListener defaultListener = new DefaultToolListener(this);

	public final String[] args;

	protected List<String> mantraFiles = new ArrayList<String>();

	public Tool(String[] args) {
		this.args = args;
		errMgr = new ErrorManager(this);
		errMgr.setFormat(msgFormat);
		handleArgs();
	}

	public static void main(String[] args) {
		Tool antlr = new Tool(args);
		if ( args.length == 0 ) { antlr.help(); antlr.exit(0); }
		antlr.handleArgs();
		antlr.processMantraFiles();
	}

	public void processMantraFiles() {
		for (String f : mantraFiles) {
			try {
				processMantraFile(f);
			}
			catch (IOException ioe) {
				//errMgr.toolError(ErrorType.CANNOT_WRITE_FILE);
				ioe.printStackTrace(System.err);
			}
		}
	}

	public void processMantraFile(String fileName) throws IOException {
		Pair<ParseTree,Parser> r = parseMantraFile(fileName);
		ParseTree tree = r.a;
		Parser parser = r.b;

		// def scopes and symbols
		DefScopesAndSymbols def = new DefScopesAndSymbols(this, tree, parser);
		ParseTreeWalker.DEFAULT.walk(def, tree);

		VerifyListener ref = new VerifyListener();
		ParseTreeWalker.DEFAULT.walk(ref, tree);

		ComputeTypes types = new ComputeTypes(parser);
		ParseTreeWalker.DEFAULT.walk(types, tree);

		// Check type compatibility, promoting if necessary
		CheckTypes chk = new CheckTypes();
		ParseTreeWalker.DEFAULT.walk(chk, tree);

		// Semantic checks

		// Build model of translated code
		ModelBuilder builder = new ModelBuilder(this, tree, parser, fileName);
		ParseTreeWalker.DEFAULT.walk(builder, tree);

		// Generate translation, store in file
		JavaGenerator gen = new JavaGenerator(this);
		ST st = gen.translate(builder.getModel());
		System.out.println(st.render());
	}

	public Pair<ParseTree,Parser> parseMantraFile(String fileName) throws IOException {
		ANTLRInputStream input = new ANTLRFileStream(fileName);
		MantraLexer lexer = new MantraLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		MantraParser parser = new MantraParser(tokens);
		ParserRuleContext tree = parser.compilationUnit();
		tree.inspect(parser);
		return new Pair<ParseTree,Parser>(tree, parser);
	}

	public Writer getOutputFileWriter(String fileName) throws IOException {
		if (outputDirectory == null) {
			return new StringWriter();
		}

		File odir = new File(outputDirectory);
		if (!odir.exists()) {
			odir.mkdirs();
		}
		File outputFile = new File(outputDirectory, fileName);
		FileWriter fw = new FileWriter(outputFile);
		return new BufferedWriter(fw);
	}

	// listener / error support

	public void addListener(MantraToolListener tl) {
		if ( tl!=null ) listeners.add(tl);
	}
	public void removeListener(MantraToolListener tl) { listeners.remove(tl); }
	public void removeListeners() { listeners.clear(); }
	public List<MantraToolListener> getListeners() { return listeners; }

	public void info(String msg) {
		if ( listeners.isEmpty() ) {
			defaultListener.info(msg);
			return;
		}
		for (MantraToolListener l : listeners) l.info(msg);
	}
	public void error(MantraMessage msg) {
		if ( listeners.isEmpty() ) {
			defaultListener.error(msg);
			return;
		}
		for (MantraToolListener l : listeners) l.error(msg);
	}
	public void warning(MantraMessage msg) {
		if ( listeners.isEmpty() ) {
			defaultListener.warning(msg);
		}
		else {
			for (MantraToolListener l : listeners) l.warning(msg);
		}

		if (warnings_are_errors) {
			errMgr.emit(ErrorType.WARNING_TREATED_AS_ERROR, new MantraMessage(ErrorType.WARNING_TREATED_AS_ERROR));
		}
	}

	public void help() {
		info("Mantra Compiler  Version " + Tool.VERSION);
		for (Option o : optionDefs) {
			String name = o.name + (o.argType!=OptionArgType.NONE? " ___" : "");
			String s = String.format(" %-19s %s", name, o.description);
			info(s);
		}
	}

	public void version() {
		info("Mantra Compiler  Version " + VERSION);
	}

	public void exit(int e) { System.exit(e); }

	public void panic() { throw new Error("Mantra panic"); }

	protected void handleArgs() {
		int i=0;
		while ( args!=null && i<args.length ) {
			String arg = args[i];
			i++;
			if ( arg.charAt(0)!='-' ) { // file name
				if ( !mantraFiles.contains(arg) ) mantraFiles.add(arg);
				continue;
			}
			boolean found = false;
			for (Option o : optionDefs) {
				if ( arg.equals(o.name) ) {
					found = true;
					String argValue = null;
					if ( o.argType==OptionArgType.STRING ) {
						argValue = args[i];
						i++;
					}
					// use reflection to set field
					Class<? extends Tool> c = this.getClass();
					try {
						Field f = c.getField(o.fieldName);
						if ( argValue==null ) {
							if ( arg.startsWith("-no-") ) f.setBoolean(this, false);
							else f.setBoolean(this, true);
						}
						else f.set(this, argValue);
					}
					catch (Exception e) {
						errMgr.toolError(ErrorType.INTERNAL_ERROR, "can't access field "+o.fieldName);
					}
				}
			}
			if ( !found ) {
				errMgr.toolError(ErrorType.INVALID_CMDLINE_ARG, arg);
			}
		}
	}
}
src/java/mantra/codegen/model/MField.java
public class MField extends MMember {
	public VariableSymbol sym;
	public String name;
	public String javaType;

	public MField(VariableSymbol sym) {
		this.sym = sym;
		this.name = sym.name;
		javaType = JavaGenerator.asJavaType(sym.type);
	}
}
src/java/mantra/codegen/model/MFile.java
public class MFile extends OutputModelObject {
	public String mantraFileName;
	public String packageName;

	@NestedModel public MAggregate aggregate;

	public MFile(String mantraFileName, String packageName, MAggregate aggregate) {
		this.mantraFileName = mantraFileName;
		this.packageName = packageName;
		this.aggregate = aggregate;
	}
}
src/java/mantra/symbols/FunctionSymbol.java
public class FunctionSymbol extends ScopedSymbol {
	public Type retType;
	public List<Arg> args;

	public FunctionSymbol(Scope enclosingScope, String name,
						  List<Arg> args, Type retType)
	{
        super(name, enclosingScope);
		this.retType = retType;
    }

    public String toString() {
        return name+"("+ Utils.stripBrackets(symbols.keySet().toString())+")";
    }
}
src/java/mantra/codegen/model/MClass.java
public class MClass extends MAggregate {
	@NestedModel public List<MMember> members = new ArrayList<MMember>();

	public MClass(ClassSymbol sym) {
		super(sym);
	}

	public void addMember(MMember m) {
		if ( m!=null ) {
			members.add(m);
		}
	}
}
src/java/mantra/codegen/model/MMember.java
public abstract class MMember extends OutputModelObject {
}
src/java/mantra/semantics/SetScopeListener.java
public class SetScopeListener extends MantraBaseListener {
	public Scope currentScope; // pick up scope from tree as we descend and ascend

	@Override
	public void enterPackageDef(@NotNull MantraParser.PackageDefContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitCompilationUnit(@NotNull MantraParser.CompilationUnitContext ctx) {
		if ( currentScope!=GLOBALS ) {
			exitScope(); // pop package scope (if any)
		}
	}

	@Override
	public void enterClazz(@NotNull MantraParser.ClazzContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitClazz(@NotNull MantraParser.ClazzContext ctx) {
		exitScope();
	}

	@Override
	public void enterInterfaze(@NotNull MantraParser.InterfazeContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitInterfaze(@NotNull MantraParser.InterfazeContext ctx) {
		exitScope();
	}

	@Override
	public void enterEnumDef(@NotNull MantraParser.EnumDefContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitEnumDef(@NotNull MantraParser.EnumDefContext ctx) {
		exitScope();
	}

	@Override
	public void enterFunction(@NotNull MantraParser.FunctionContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitFunction(@NotNull MantraParser.FunctionContext ctx) {
		exitScope();
	}

	@Override
	public void enterBlock(@NotNull MantraParser.BlockContext ctx) {
		currentScope = ctx.scope; // pick up scope
	}

	@Override
	public void exitBlock(@NotNull MantraParser.BlockContext ctx) {
		exitScope();
	}

	public void exitScope() {
		if ( currentScope!=null ) {
			currentScope = currentScope.getEnclosingScope();
		}
	}
}
src/java/mantra/symbols/ClassSymbol.java
public class ClassSymbol extends ScopedSymbol implements TypeName {
    /** This is the superclass not enclosingScope field. We still record
     *  the enclosing scope so we can push in and pop out of class defs.
     */
    ClassSymbol superClass;

    public ClassSymbol(Scope enclosingScope, String name, ClassSymbol superClass) {
        super(name, enclosingScope);
        this.superClass = superClass;
    }

    public Scope getParentScope() {
        if ( superClass==null ) return enclosingScope; // globals
        return superClass; // if not root object, return super
    }

    /** For a.b, only look in a's class hierarchy to resolve b, not globals */
    public Symbol resolveMember(String name) {
        Symbol s = symbols.get(name);
        if ( s!=null ) return s;
        // if not here, check just the superclass chain
        if ( superClass != null ) {
            return superClass.resolveMember(name);
        }
        return null; // not found
    }

    public String toString() {
        return "class "+name+":{"+
               Utils.stripBrackets(symbols.keySet().toString())+"}";
    }
}


Question: package mantra.codegen;
import mantra.MantraParser;
import mantra.Tool;
import mantra.codegen.model.MClass;
import mantra.codegen.model.MField;
import mantra.codegen.model.MFile;
import mantra.codegen.model.MMember;
import mantra.codegen.model.MMethod;
import mantra.semantics.SetScopeListener;
import mantra.symbols.ClassSymbol;
import mantra.symbols.FunctionSymbol;
import mantra.symbols.VariableSymbol;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.misc.NotNull;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.xpath.XPath;
import java.util.Collection;
import static mantra.MantraParser.*;




/** Construct an output model for a single Mantra file. Only a single
 *  aggregate (class, interface, enum) can be defined within a single file.
 *  Must play well in Java universe.
 */
public class ModelBuilder extends SetScopeListener {
	public Tool tool;
	public ParseTree tree;
	public Parser parser;

	public MFile fileModel;
	public String packageName;
	public String mantraFileName;

	public MClass classInFile;

	public ModelBuilder(Tool tool, ParseTree tree, Parser parser, String mantraFileName) {
		this.tool = tool;
		this.tree = tree;
		this.parser = parser;
		this.mantraFileName = mantraFileName;
	}

	public MFile getModel() {
		return fileModel;
	}

	// NOTE: make sure to call super.exitXXX and enterXXX so we get scopes set

	@Override
	public void exitCompilationUnit(@NotNull CompilationUnitContext ctx) {
		super.exitCompilationUnit(ctx);
		fileModel = new MFile(mantraFileName, packageName, classInFile);
	}

	@Override
	public void exitPackageDef(@NotNull PackageDefContext ctx) {
		super.exitPackageDef(ctx);
		packageName = ctx.packageName().getText();
	}

	@Override
	public void enterClazz(@NotNull ClazzContext ctx) {
		super.enterClazz(ctx);
		ClassSymbol s = (ClassSymbol)currentScope.resolve(ctx.ID().getText());
		classInFile = new MClass(s);
	}

	@Override
	public void exitVarField(@NotNull VarFieldContext ctx) {
		DeclContext decl = ctx.getRuleContext(DeclContext.class, 0);
		String fieldName = decl.ID().getText();
		VariableSymbol s = (VariableSymbol)currentScope.resolve(fieldName);
		MField f = new MField(s);
		classInFile.addMember(f);
	}

	@Override
	public void exitMemberFunction(@NotNull MemberFunctionContext ctx) {
		super.exitMemberFunction(ctx);
		Collection<ParseTree> fheads = XPath.findAll(ctx, "//functionHead", parser);
		FunctionHeadContext fhead = (FunctionHeadContext)fheads.iterator().next();
Answer: 		FunctionSymbol s = (FunctionSymbol)currentScope.resolve(fhead.ID().getText());