feat: compile third test program

This commit is contained in:
2026-04-29 15:37:21 +01:00
parent e7b58ad256
commit ce695c7ace
@@ -32,6 +32,16 @@ func segmentFromVariableKind(kind table.VariableKind) code.Segment {
return -1 return -1
} }
func isMethodName(tokens []tokenizer.Token, identifier string) bool {
for index, token := range tokens {
if token.Value == identifier && index >= 2 && tokens[index-2].Value == "method" {
return true
}
}
return false
}
func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int) error { func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int) error {
if tokens[*index].Type == tokenizer.SYMBOL && slices.Contains([]string{"-", "~"}, tokens[*index].Value) { if tokens[*index].Type == tokenizer.SYMBOL && slices.Contains([]string{"-", "~"}, tokens[*index].Value) {
op := tokens[*index].Value op := tokens[*index].Value
@@ -66,11 +76,11 @@ func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int)
if tokens[*index].Value == "this" { if tokens[*index].Value == "this" {
variable, found := table.GetVariable([]*map[string]table.Variable{&subroutineSymbolTable}, tokens[*index].Value) variable, found := table.GetVariable([]*map[string]table.Variable{&subroutineSymbolTable}, tokens[*index].Value)
if !found { if found {
return errors.New("Invalid identifier!")
}
code.WritePush(output, segmentFromVariableKind(variable.Kind), variable.Count) code.WritePush(output, segmentFromVariableKind(variable.Kind), variable.Count)
} else {
code.WritePush(output, code.POINTER, 0)
}
} }
if tokens[*index].Value == "true" { if tokens[*index].Value == "true" {
@@ -112,7 +122,7 @@ func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int)
var identifier string var identifier string
if found { if found && !slices.Contains([]string{"(", "."}, tokens[(*index)+1].Value) {
code.WritePush(output, segmentFromVariableKind(variable.Kind), variable.Count) code.WritePush(output, segmentFromVariableKind(variable.Kind), variable.Count)
} else { } else {
identifier = tokens[*index].Value identifier = tokens[*index].Value
@@ -282,10 +292,19 @@ func compileVariableDeclaration(output *strings.Builder, tokens []tokenizer.Toke
func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, index *int, identifier string) error { func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, index *int, identifier string) error {
var class string var class string
var function string var function string
var subroutine string
if tokens[*index].Value == "." { instanceCallingMethod := false
switch tokens[*index].Value {
case ".":
variable, found := table.GetVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, identifier)
if found {
class = variable.Type
} else {
class = identifier class = identifier
}
(*index)++ (*index)++
@@ -293,9 +312,23 @@ func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, in
return errors.New("Invalid subroutine name!") return errors.New("Invalid subroutine name!")
} }
function = class + "." + tokens[*index].Value subroutine = tokens[*index].Value
function = class + "." + subroutine
// This assumes that the called subroutine is a method for now.
if found {
code.WritePush(output, segmentFromVariableKind(variable.Kind), variable.Count)
instanceCallingMethod = true
}
(*index)++ (*index)++
case "(":
subroutine = identifier
function = className + "." + identifier
if isMethodName(tokens, subroutine) {
code.WritePush(output, code.POINTER, 0)
}
} }
if tokens[*index].Value != "(" { if tokens[*index].Value != "(" {
@@ -314,6 +347,10 @@ func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, in
return errors.New("Missing subroutine call closing parenthese!") return errors.New("Missing subroutine call closing parenthese!")
} }
if isMethodName(tokens, subroutine) || instanceCallingMethod {
args++
}
code.WriteCall(output, function, args) code.WriteCall(output, function, args)
(*index)++ (*index)++
@@ -375,7 +412,6 @@ func compileLetStatement(output *strings.Builder, tokens []tokenizer.Token, inde
} }
func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error {
endLabel := "IF_END_" + fmt.Sprint(ifLabelIndex)
elseLabel := "IF_ELSE_" + fmt.Sprint(ifLabelIndex) elseLabel := "IF_ELSE_" + fmt.Sprint(ifLabelIndex)
ifLabelIndex++ ifLabelIndex++
@@ -415,14 +451,16 @@ func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index
return err return err
} }
code.WriteGoto(output, endLabel)
if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" {
return errors.New("Missing if statement closing curly brace!") return errors.New("Missing if statement closing curly brace!")
} }
(*index)++ (*index)++
if tokens[*index].Value != "else" {
code.WriteLabel(output, elseLabel)
}
if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "else" { if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "else" {
(*index)++ (*index)++
@@ -445,8 +483,6 @@ func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index
(*index)++ (*index)++
} }
code.WriteLabel(output, endLabel)
return nil return nil
} }
@@ -593,7 +629,7 @@ func compileStatements(output *strings.Builder, tokens []tokenizer.Token, index
return compileStatements(output, tokens, index) return compileStatements(output, tokens, index)
} }
func compileSubroutineBody(output *strings.Builder, tokens []tokenizer.Token, index *int, function string, isMethod bool) error { func compileSubroutineBody(output *strings.Builder, tokens []tokenizer.Token, index *int, function string, isMethod bool, isConstructor bool) error {
if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "var" { if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "var" {
if err := compileVariableDeclaration(output, tokens, index); err != nil { if err := compileVariableDeclaration(output, tokens, index); err != nil {
return err return err
@@ -602,6 +638,12 @@ func compileSubroutineBody(output *strings.Builder, tokens []tokenizer.Token, in
code.WriteFunction(output, className+"."+function, table.CountVariables(&subroutineSymbolTable, table.VAR)+1) code.WriteFunction(output, className+"."+function, table.CountVariables(&subroutineSymbolTable, table.VAR)+1)
if isConstructor {
code.WritePush(output, code.CONSTANT, table.CountVariables(&classSymbolTable, table.FIELD)+1)
code.WriteCall(output, "Memory.alloc", 1)
code.WritePop(output, code.POINTER, 0)
}
if isMethod { if isMethod {
code.WritePush(output, code.ARGUMENT, 0) code.WritePush(output, code.ARGUMENT, 0)
code.WritePop(output, code.POINTER, 0) code.WritePop(output, code.POINTER, 0)
@@ -622,6 +664,7 @@ func compileSubroutineDeclaration(output *strings.Builder, tokens []tokenizer.To
} }
isMethod := tokens[*index].Value == "method" isMethod := tokens[*index].Value == "method"
isConstructor := tokens[*index].Value == "constructor"
(*index)++ (*index)++
@@ -668,7 +711,7 @@ func compileSubroutineDeclaration(output *strings.Builder, tokens []tokenizer.To
(*index)++ (*index)++
if err := compileSubroutineBody(output, tokens, index, function, isMethod); err != nil { if err := compileSubroutineBody(output, tokens, index, function, isMethod, isConstructor); err != nil {
return err return err
} }