* net/loveruby/cflat/compiler/TypeChecker.java: fork DereferenceChecker, to reduce catch(SemanticError).

* net/loveruby/cflat/compiler/DereferenceChecker.java: new class.
* net/loveruby/cflat/ast/ExprNode.java (isAssignable, isDereferable): catch SemanticError which may occuer on type.isXXXX.
* net/loveruby/cflat/compiler/Visitor.java: refactoring: rename method: #resolve -> #visitNode.
* net/loveruby/cflat/compiler/Visitor.java: refactoring: rename method: #resolveNodeList -> #visitNodeList.
* net/loveruby/cflat/compiler/JumpResolver.java: ditto.
* net/loveruby/cflat/compiler/LocalReferenceResolver.java: ditto.
* net/loveruby/cflat/compiler/TypeResolver.java: ditto.
* net/loveruby/cflat/compiler/TypeChecker.java: ditto.
* net/loveruby/cflat/compiler/Compiler.java: parse command line options more precisely.
* net/loveruby/cflat/compiler/Compiler.java: new option --compile.
* net/loveruby/cflat/compiler/Compiler.java: new option --dump-reference.
* net/loveruby/cflat/compiler/Compiler.java: new option --dump-semantic.
* net/loveruby/cflat/ast/VariableNode.java (dump): show resolved or not.
* net/loveruby/cflat/ast/TypeNode.java (dump): ditto.
* net/loveruby/cflat/ast/Dumper.java (printMember): ditto.


git-svn-id: file:///Users/aamine/c/gitwork/public/cbc/trunk@3819 1b9489fe-b721-0410-924e-b54b9192deb8
This commit is contained in:
Minero Aoki 2008-01-12 19:48:31 +00:00
parent a88b9a2bca
commit fd5caed6fc
13 changed files with 420 additions and 182 deletions

View File

@ -1,3 +1,46 @@
Sun Jan 13 04:48:28 2008 Minero Aoki <aamine@loveruby.net>
* net/loveruby/cflat/compiler/TypeChecker.java: fork
DereferenceChecker, to reduce catch(SemanticError).
* net/loveruby/cflat/compiler/DereferenceChecker.java: new class.
* net/loveruby/cflat/ast/ExprNode.java (isAssignable,
isDereferable): catch SemanticError which may occuer on
type.isXXXX.
* net/loveruby/cflat/compiler/Visitor.java: refactoring: rename
method: #resolve -> #visitNode.
* net/loveruby/cflat/compiler/Visitor.java: refactoring: rename
method: #resolveNodeList -> #visitNodeList.
* net/loveruby/cflat/compiler/JumpResolver.java: ditto.
* net/loveruby/cflat/compiler/LocalReferenceResolver.java: ditto.
* net/loveruby/cflat/compiler/TypeResolver.java: ditto.
* net/loveruby/cflat/compiler/TypeChecker.java: ditto.
* net/loveruby/cflat/compiler/Compiler.java: parse command line
options more precisely.
* net/loveruby/cflat/compiler/Compiler.java: new option --compile.
* net/loveruby/cflat/compiler/Compiler.java: new option
--dump-reference.
* net/loveruby/cflat/compiler/Compiler.java: new option
--dump-semantic.
* net/loveruby/cflat/ast/VariableNode.java (dump): show resolved
or not.
* net/loveruby/cflat/ast/TypeNode.java (dump): ditto.
* net/loveruby/cflat/ast/Dumper.java (printMember): ditto.
Sun Jan 13 04:47:12 2008 Minero Aoki <aamine@loveruby.net>
* net/loveruby/cflat/type/Type.java: make #isCompatible and

6
ToDo
View File

@ -181,8 +181,10 @@
- --dump-tokens
- --dump-ast
- print node location in error message
* parse command line option more precisely
* --dump-semantic
- parse command line option more precisely
- --dump-reference
- --dump-semantic
* add standard load path
* generate IR
* --dump-ir
* control flow graph

View File

@ -58,8 +58,13 @@ public class Dumper {
printPair(name, (t == null ? "null" : t.toString()));
}
public void printMember(String name, String str, boolean isResolved) {
printPair(name, TextUtils.escapeString(str)
+ (isResolved ? " (resolved)" : ""));
}
public void printMember(String name, String str) {
printPair(name, TextUtils.escapeString(str));
printMember(name, str, false);
}
protected void printPair(String name, String value) {
@ -69,7 +74,8 @@ public class Dumper {
public void printMember(String name, TypeNode n) {
printIndent();
stream.println(name + ": " + n.typeRef());
stream.println(name + ": " + n.typeRef()
+ (n.isResolved() ? " (resolved)" : ""));
}
public void printMember(String name, Node n) {

View File

@ -1,6 +1,7 @@
package net.loveruby.cflat.ast;
import net.loveruby.cflat.type.Type;
import net.loveruby.cflat.asm.*;
import net.loveruby.cflat.exception.*;
abstract public class ExprNode extends Node {
public ExprNode() {
@ -10,11 +11,21 @@ abstract public class ExprNode extends Node {
abstract public Type type();
public boolean isCallable() {
return type().isCallable();
try {
return type().isCallable();
}
catch (SemanticError err) {
return false;
}
}
public boolean isDereferable() {
return type().isDereferable();
try {
return type().isDereferable();
}
catch (SemanticError err) {
return false;
}
}
public boolean isConstant() {

View File

@ -15,10 +15,6 @@ public class TypeNode extends Node {
this.type = type;
}
public void resolve(TypeTable tbl) {
// FIXME
}
public TypeRef typeRef() {
return typeRef;
}

View File

@ -17,8 +17,8 @@ public class VariableNode extends ExprNode {
return name;
}
public void setEntity(Entity ent) {
entity = ent;
public boolean isResolved() {
return (entity != null);
}
public Entity entity() {
@ -28,6 +28,10 @@ public class VariableNode extends ExprNode {
return entity;
}
public void setEntity(Entity ent) {
entity = ent;
}
public Type type() {
return entity().type();
}
@ -53,7 +57,7 @@ public class VariableNode extends ExprNode {
}
protected void _dump(Dumper d) {
d.printMember("name", name);
d.printMember("name", name, isResolved());
}
public void accept(ASTVisitor visitor) {

View File

@ -29,36 +29,72 @@ public class Compiler {
errorHandler = h;
}
public void commandMain(String[] args) {
public void commandMain(String[] origArgs) {
// parse options
String mode = null;
List args = listFromArray(origArgs);
ListIterator it = args.listIterator();
while (it.hasNext()) {
String arg = (String)it.next();
if (arg.startsWith("-")) {
if (arg.equals("--compile")
|| arg.equals("--check-syntax")
|| arg.equals("--dump-tokens")
|| arg.equals("--dump-ast")
|| arg.equals("--dump-reference")
|| arg.equals("--dump-semantic")) {
if (mode != null) {
errorExit(mode + " option and " +
arg + " option is exclusive");
}
mode = arg;
it.remove();
}
else if (arg.equals("--help")) {
printUsage(System.out);
System.exit(0);
}
else {
System.err.println("unknown option: " + arg);
printUsage(System.err);
System.exit(1);
}
}
}
if (mode == null) {
mode = "--compile";
}
if (args.size() == 0) errorExit("no input file");
if (args.size() > 1) errorExit("too many input files");
String inputFile = (String)args.get(0);
// execute
try {
if (args.length == 0) errorExit("no argument given");
if (args[0].equals("--dump-tokens")) {
if (args.length != 2) {
errorExit("no file input or too many files");
}
dumpTokensFromFile(args[1]);
if (mode.equals("--compile")) {
compileFile(inputFile);
}
else if (args[0].equals("--dump-ast")) {
if (args.length != 2) {
errorExit("no file input or too many files");
}
dumpASTFromFile(args[1]);
}
else if (args[0].equals("--check-syntax")) {
if (args.length != 2) {
errorExit("no file input or too many files");
}
if (isValidSyntax(args[1])) {
else if (mode.equals("--check-syntax")) {
if (isValidSyntax(inputFile)) {
System.out.println("Syntax OK");
System.exit(0);
} else {
System.exit(1);
}
}
else if (mode.equals("--dump-tokens")) {
dumpTokensFromFile(inputFile);
}
else if (mode.equals("--dump-ast")) {
dumpASTFromFile(inputFile);
}
else if (mode.equals("--dump-reference")) {
dumpReferenceFromFile(inputFile);
}
else if (mode.equals("--dump-semantic")) {
dumpSemanticFromFile(inputFile);
}
else {
if (args.length != 1)
errorExit("no file input or too many files");
compileFile(args[0]);
throw new Error("unknown mode: " + mode);
}
}
catch (CompileException ex) {
@ -67,6 +103,24 @@ public class Compiler {
}
}
protected void printUsage(PrintStream out) {
// --dump-reference is hidden option
out.println("Usage: cbc [option] file");
out.println(" --check-syntax Syntax check only.");
out.println(" --dump-tokens Parses source file and dumps tokens.");
out.println(" --dump-ast Parses source file and dumps AST.");
out.println(" --dump-semantic Check semantics and dumps AST.");
out.println(" --help Prints this message and quit.");
}
private List listFromArray(Object[] a) {
List list = new ArrayList();
for (int i = 0; i < a.length; i++) {
list.add(a[i]);
}
return list;
}
private void errorExit(String msg) {
errorHandler.error(msg);
System.exit(1);
@ -111,9 +165,27 @@ public class Compiler {
parseFile(path).dump();
}
public void dumpReferenceFromFile(String path) throws CompileException {
AST ast = parseFile(path);
TypeTable typeTable = defaultTypeTable();
JumpResolver.resolve(ast, errorHandler);
LocalReferenceResolver.resolve(ast, errorHandler);
TypeResolver.resolve(ast, typeTable, errorHandler);
typeTable.semanticCheck(errorHandler);
DereferenceChecker.check(ast, errorHandler);
ast.dump();
}
public void dumpSemanticFromFile(String path) throws CompileException {
AST ast = parseFile(path);
TypeTable typeTable = defaultTypeTable();
semanticAnalysis(ast, typeTable);
ast.dump();
}
public void compileFile(String path) throws CompileException {
AST ast = parseFile(path);
TypeTable typeTable = TypeTable.ilp32();
TypeTable typeTable = defaultTypeTable();
semanticAnalysis(ast, typeTable);
String asm = CodeGenerator.generate(ast, typeTable, errorHandler);
writeFile(asmFileName(path), asm);
@ -126,9 +198,14 @@ public class Compiler {
LocalReferenceResolver.resolve(ast, errorHandler);
TypeResolver.resolve(ast, typeTable, errorHandler);
typeTable.semanticCheck(errorHandler);
DereferenceChecker.check(ast, errorHandler);
TypeChecker.check(ast, typeTable, errorHandler);
}
private TypeTable defaultTypeTable() {
return TypeTable.ilp32();
}
public AST parseFile(String path) throws CompileException {
return Parser.parseFile(new File(path), loader, errorHandler);
}

View File

@ -0,0 +1,148 @@
package net.loveruby.cflat.compiler;
import net.loveruby.cflat.ast.*;
import net.loveruby.cflat.type.*;
import net.loveruby.cflat.exception.*;
import java.util.*;
class DereferenceChecker extends Visitor {
static public void check(AST ast, ErrorHandler h)
throws SemanticException {
new DereferenceChecker(h).visit(ast);
}
protected ErrorHandler errorHandler;
public DereferenceChecker(ErrorHandler h) {
this.errorHandler = h;
}
protected void check(Node node) {
visitNode(node);
}
public void visit(AST ast) throws SemanticException {
Iterator vars = ast.variables();
while (vars.hasNext()) {
DefinedVariable var = (DefinedVariable)vars.next();
checkVariable(var);
}
Iterator funcs = ast.functions();
while (funcs.hasNext()) {
DefinedFunction f = (DefinedFunction)funcs.next();
check(f.body());
}
if (errorHandler.errorOccured()) {
throw new SemanticException("compile failed.");
}
}
//
// Statements
//
protected void checkVariable(DefinedVariable var) {
if (var.hasInitializer()) {
check(var.initializer());
}
}
//
// Assignment Expressions
//
public void visit(AssignNode node) {
super.visit(node);
checkAssignment(node);
}
public void visit(OpAssignNode node) {
super.visit(node);
checkAssignment(node);
// check as operator
}
protected void checkAssignment(AbstractAssignNode node) {
if (! node.lhs().isAssignable()) {
error(node, "invalid lhs expression");
return;
}
}
//
// Expressions
//
public void visit(FuncallNode node) {
super.visit(node);
if (! node.expr().isCallable()) {
error(node, "calling object is not a function");
return;
}
}
public void visit(ArefNode node) {
super.visit(node);
if (! node.expr().isDereferable()) {
error(node, "is not indexable: " + node.expr().type());
return;
}
check(node.index());
}
public void visit(MemberNode node) {
super.visit(node);
checkMemberRef(node, node.expr().type(), node.member());
}
public void visit(PtrMemberNode node) {
super.visit(node);
if (! node.expr().isDereferable()) {
undereferableError(node, node.expr().type());
return;
}
checkMemberRef(node, node.dereferedType(), node.member());
}
protected void checkMemberRef(Node node, Type t, String memb) {
if (! t.isComplexType()) {
error(node, "is not struct/union: " + t);
return;
}
ComplexType type = t.getComplexType();
if (! type.hasMember(memb)) {
error(node, type.toString() + " does not have member " + memb);
return;
}
}
public void visit(DereferenceNode node) {
super.visit(node);
if (! node.expr().isDereferable()) {
undereferableError(node, node.expr().type());
return;
}
}
public void visit(AddressNode node) {
super.visit(node);
if (! node.expr().isAssignable()) {
error(node, "invalid LHS expression for &");
}
}
//
// Utilities
//
protected void undereferableError(Node n, Type type) {
error(n, "dereferencing non-pointer expression: " + type);
}
protected void warn(Node n, String msg) {
errorHandler.warn(n.location(), msg);
}
protected void error(Node n, String msg) {
errorHandler.error(n.location(), msg);
}
}

View File

@ -7,7 +7,7 @@ import java.util.*;
public class JumpResolver extends Visitor {
static public void resolve(AST ast, ErrorHandler h)
throws SemanticException {
new JumpResolver(h).resolveAST(ast);
new JumpResolver(h).resolve(ast);
}
protected ErrorHandler errorHandler;
@ -19,7 +19,7 @@ public class JumpResolver extends Visitor {
errorHandler = h;
}
public void resolveAST(AST ast) throws SemanticException {
public void resolve(AST ast) throws SemanticException {
breakTargetStack = new LinkedList();
continueTargetStack = new LinkedList();
Iterator funcs = ast.functions();
@ -33,6 +33,10 @@ public class JumpResolver extends Visitor {
}
}
protected void resolve(Node n) {
visitNode(n);
}
private BreakableStmt currentBreakTarget()
throws SemanticException {
if (breakTargetStack.isEmpty()) {
@ -52,7 +56,7 @@ public class JumpResolver extends Visitor {
public void visit(SwitchNode node) {
resolve(node.cond());
breakTargetStack.add(node);
resolveNodeList(node.cases());
visitNodeList(node.cases());
breakTargetStack.removeLast();
}

View File

@ -6,7 +6,7 @@ import java.util.*;
public class LocalReferenceResolver extends Visitor {
static public void resolve(AST ast, ErrorHandler handler)
throws SemanticException {
new LocalReferenceResolver(handler).resolveAST(ast);
new LocalReferenceResolver(handler).resolve(ast);
}
protected ErrorHandler errorHandler;
@ -18,7 +18,11 @@ public class LocalReferenceResolver extends Visitor {
this.errorHandler = h;
}
public void resolveAST(AST ast) throws SemanticException {
protected void resolve(Node n) {
visitNode(n);
}
public void resolve(AST ast) throws SemanticException {
toplevel = ast.scope();
scopeStack = new LinkedList();
scopeStack.add(toplevel);

View File

@ -19,7 +19,7 @@ class TypeChecker extends Visitor {
}
protected void check(Node node) {
resolve(node);
visitNode(node);
}
public void visit(AST ast) throws SemanticException {
@ -70,12 +70,7 @@ class TypeChecker extends Visitor {
Iterator stmts = node.stmts();
while (stmts.hasNext()) {
Node n = (Node)stmts.next();
try {
check(n);
}
catch (SemanticError err) {
// ignore semantic errors
}
check(n);
}
}
@ -85,18 +80,13 @@ class TypeChecker extends Visitor {
return;
}
if (var.hasInitializer()) {
try {
if (isInvalidLHSType(var.type())) {
error(var, "invalid LHS type: " + var.type());
return;
}
check(var.initializer());
var.setInitializer(
checkRHSType(var.initializer(), var.type()));
}
catch (SemanticError err) {
// ignore semantic errors
if (isInvalidLHSType(var.type())) {
error(var, "invalid LHS type: " + var.type());
return;
}
check(var.initializer());
var.setInitializer(
checkRHSType(var.initializer(), var.type()));
}
}
@ -174,10 +164,6 @@ class TypeChecker extends Visitor {
protected void checkAssignment(AbstractAssignNode node) {
check(node.lhs());
check(node.rhs());
if (! node.lhs().isAssignable()) {
error(node, "invalid lhs expression");
return;
}
if (isInvalidLHSType(node.lhs().type())) {
error(node, "invalid lhs type");
return;
@ -354,36 +340,31 @@ class TypeChecker extends Visitor {
// +, -, !, ~
public void visit(UnaryOpNode node) {
check(node.expr());
super.visit(node);
mustBeInteger(node.expr());
}
// ++, --
public void visit(PrefixOpNode node) {
check(node.expr());
super.visit(node);
mustBeScalar(node.expr());
}
// ++, --
public void visit(SuffixOpNode node) {
check(node.expr());
super.visit(node);
mustBeScalar(node.expr());
}
/**
* For EXPR(ARG), checks:
*
* * EXPR is callable (a pointer to a function).
* * The number of argument matches function prototype.
* * ARG matches function prototype.
* * ARG is neither a struct nor an union.
*/
public void visit(FuncallNode node) {
check(node.expr());
if (! node.expr().isCallable()) {
error(node, "calling object is not a function");
return;
}
super.visit(node);
FunctionType type = node.functionType();
if (! type.acceptsArgc(node.numArgs())) {
error(node, "wrong number of argments: " + node.numArgs());
@ -407,65 +388,13 @@ class TypeChecker extends Visitor {
node.replaceArgs(newArgs);
}
/**
* Checks if the type of base expression of EXPR[IDX] is valid.
* EXPR must be an array or a pointer. IDX must be an integer.
*/
public void visit(ArefNode node) {
check(node.expr());
if (! node.expr().isDereferable()) {
error(node, "is not indexable: " + node.expr().type());
return;
}
check(node.index());
super.visit(node);
mustBeInteger(node.index());
}
public void visit(MemberNode node) {
check(node.expr());
checkMemberRef(node, node.expr().type(), node.member());
}
public void visit(PtrMemberNode node) {
check(node.expr());
if (! node.expr().isDereferable()) {
undereferableError(node, node.expr().type());
return;
}
checkMemberRef(node, node.dereferedType(), node.member());
}
protected void checkMemberRef(Node node, Type t, String memb) {
if (! t.isComplexType()) {
error(node, "is not struct/union: " + t);
return;
}
ComplexType type = t.getComplexType();
if (! type.hasMember(memb)) {
error(node, type.toString() + " does not have member " + memb);
return;
}
}
public void visit(DereferenceNode node) {
check(node.expr());
if (! node.expr().isDereferable()) {
undereferableError(node, node.expr().type());
return;
}
}
public void visit(AddressNode node) {
check(node.expr());
Type t = typeTable.pointerTo(node.expr().type());
node.setType(t);
if (! node.expr().isAssignable()) {
error(node, "invalid LHS expression for &");
}
}
public void visit(CastNode node) {
check(node.expr());
super.visit(node);
if (! node.expr().type().isCastableTo(node.type())) {
incompatibleTypeError(node, node.expr().type(), node.type());
}

View File

@ -1,6 +1,7 @@
package net.loveruby.cflat.compiler;
import net.loveruby.cflat.ast.*;
import net.loveruby.cflat.type.*;
import net.loveruby.cflat.exception.*;
import java.util.*;
public class TypeResolver extends Visitor {
@ -19,9 +20,9 @@ public class TypeResolver extends Visitor {
public void resolveProgram(AST ast) {
defineTypes(ast.types());
resolveNodeList(ast.types());
resolveNodeList(ast.declarations());
resolveNodeList(ast.entities());
visitNodeList(ast.types());
visitNodeList(ast.declarations());
visitNodeList(ast.entities());
}
private void defineTypes(Iterator deftypes) {
@ -66,9 +67,7 @@ public class TypeResolver extends Visitor {
public void visit(DefinedVariable var) {
bindType(var.typeNode());
if (var.isInitialized()) {
resolve(var.initializer());
}
super.visit(var); // resolve initializer
}
public void visit(UndefinedVariable var) {
@ -77,8 +76,8 @@ public class TypeResolver extends Visitor {
public void visit(DefinedFunction func) {
resolveFunctionHeader(func);
resolveLocalVariables(func);
resolve(func.body());
//resolveLocalVariables(func);
visitNode(func.body());
}
public void visit(UndefinedFunction func) {
@ -101,6 +100,20 @@ public class TypeResolver extends Visitor {
}
}
public void visit(AddressNode node) {
super.visit(node);
// to avoid SemanticError which occurs when getting type of
// expr which is not assignable.
try {
Type t = typeTable.pointerTo(node.expr().type());
node.setType(t);
}
catch (SemanticError err) {
Type t = typeTable.pointerTo(typeTable.voidType());
node.setType(t);
}
}
public void visit(CastNode node) {
bindType(node.typeNode());
super.visit(node);

View File

@ -6,14 +6,14 @@ abstract public class Visitor implements ASTVisitor {
public Visitor() {
}
protected void resolve(Node node) {
protected void visitNode(Node node) {
node.accept(this);
}
protected void resolveNodeList(Iterator ns) {
protected void visitNodeList(Iterator ns) {
while (ns.hasNext()) {
Node n = (Node)ns.next();
resolve(n);
visitNode(n);
}
}
@ -22,6 +22,9 @@ abstract public class Visitor implements ASTVisitor {
//
public void visit(DefinedVariable var) {
if (var.hasInitializer()) {
visitNode(var.initializer());
}
}
public void visit(UndefinedVariable var) {
@ -50,46 +53,44 @@ abstract public class Visitor implements ASTVisitor {
Iterator vars = node.variables();
while (vars.hasNext()) {
DefinedVariable var = (DefinedVariable)vars.next();
if (var.hasInitializer()) {
resolve(var.initializer());
}
visit(var);
}
resolveNodeList(node.stmts());
visitNodeList(node.stmts());
}
public void visit(IfNode n) {
resolve(n.cond());
resolve(n.thenBody());
visitNode(n.cond());
visitNode(n.thenBody());
if (n.elseBody() != null) {
resolve(n.elseBody());
visitNode(n.elseBody());
}
}
public void visit(SwitchNode n) {
resolve(n.cond());
resolveNodeList(n.cases());
visitNode(n.cond());
visitNodeList(n.cases());
}
public void visit(CaseNode n) {
resolveNodeList(n.values());
resolve(n.body());
visitNodeList(n.values());
visitNode(n.body());
}
public void visit(WhileNode n) {
resolve(n.cond());
resolve(n.body());
visitNode(n.cond());
visitNode(n.body());
}
public void visit(DoWhileNode n) {
resolve(n.body());
resolve(n.cond());
visitNode(n.body());
visitNode(n.cond());
}
public void visit(ForNode n) {
resolve(n.init());
resolve(n.cond());
resolve(n.incr());
resolve(n.body());
visitNode(n.init());
visitNode(n.cond());
visitNode(n.incr());
visitNode(n.body());
}
public void visit(BreakNode n) {
@ -102,12 +103,12 @@ abstract public class Visitor implements ASTVisitor {
}
public void visit(LabelNode n) {
resolve(n.stmt());
visitNode(n.stmt());
}
public void visit(ReturnNode n) {
if (n.expr() != null) {
resolve(n.expr());
visitNode(n.expr());
}
}
@ -116,78 +117,78 @@ abstract public class Visitor implements ASTVisitor {
//
public void visit(CondExprNode n) {
resolve(n.cond());
resolve(n.thenExpr());
visitNode(n.cond());
visitNode(n.thenExpr());
if (n.elseExpr() != null) {
resolve(n.elseExpr());
visitNode(n.elseExpr());
}
}
public void visit(LogicalOrNode node) {
resolve(node.left());
resolve(node.right());
visitNode(node.left());
visitNode(node.right());
}
public void visit(LogicalAndNode node) {
resolve(node.left());
resolve(node.right());
visitNode(node.left());
visitNode(node.right());
}
public void visit(AssignNode n) {
resolve(n.lhs());
resolve(n.rhs());
visitNode(n.lhs());
visitNode(n.rhs());
}
public void visit(OpAssignNode n) {
resolve(n.lhs());
resolve(n.rhs());
visitNode(n.lhs());
visitNode(n.rhs());
}
public void visit(BinaryOpNode n) {
resolve(n.left());
resolve(n.right());
visitNode(n.left());
visitNode(n.right());
}
public void visit(UnaryOpNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(PrefixOpNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(SuffixOpNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(FuncallNode node) {
resolve(node.expr());
resolveNodeList(node.arguments());
visitNode(node.expr());
visitNodeList(node.arguments());
}
public void visit(ArefNode node) {
resolve(node.expr());
resolve(node.index());
visitNode(node.expr());
visitNode(node.index());
}
public void visit(MemberNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(PtrMemberNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(DereferenceNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(AddressNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(CastNode node) {
resolve(node.expr());
visitNode(node.expr());
}
public void visit(VariableNode node) {