diff --git a/internal/code/code.go b/internal/code/code.go index 4d13796..45e9732 100644 --- a/internal/code/code.go +++ b/internal/code/code.go @@ -54,7 +54,7 @@ func SegmentText(segment Segment) string { } func WritePush(output *strings.Builder, segment Segment, index int) error { - if _, err := output.WriteString("push " + SegmentText(segment) + " " + fmt.Sprint(index)); err != nil { + if _, err := output.WriteString("push " + SegmentText(segment) + " " + fmt.Sprint(index) + "\n"); err != nil { return err } @@ -62,7 +62,7 @@ func WritePush(output *strings.Builder, segment Segment, index int) error { } func WritePop(output *strings.Builder, segment Segment, index int) error { - if _, err := output.WriteString("pop " + SegmentText(segment) + " " + fmt.Sprint(index)); err != nil { + if _, err := output.WriteString("pop " + SegmentText(segment) + " " + fmt.Sprint(index) + "\n"); err != nil { return err } @@ -93,7 +93,7 @@ func WriteArithmeticLogical(output *strings.Builder, command ArithmeticLogicalCo commandText = "not" } - if _, err := output.WriteString(commandText); err != nil { + if _, err := output.WriteString(commandText + "\n"); err != nil { return err } @@ -101,7 +101,7 @@ func WriteArithmeticLogical(output *strings.Builder, command ArithmeticLogicalCo } func WriteLabel(output *strings.Builder, label string) error { - if _, err := output.WriteString("label " + label); err != nil { + if _, err := output.WriteString("label " + label + "\n"); err != nil { return err } @@ -109,7 +109,7 @@ func WriteLabel(output *strings.Builder, label string) error { } func WriteGoto(output *strings.Builder, label string) error { - if _, err := output.WriteString("goto " + label); err != nil { + if _, err := output.WriteString("goto " + label + "\n"); err != nil { return err } @@ -117,7 +117,7 @@ func WriteGoto(output *strings.Builder, label string) error { } func WriteIfGoto(output *strings.Builder, label string) error { - if _, err := output.WriteString("if-goto " + label); err != nil { + if _, err := output.WriteString("if-goto " + label + "\n"); err != nil { return err } @@ -125,7 +125,7 @@ func WriteIfGoto(output *strings.Builder, label string) error { } func WriteCall(output *strings.Builder, function string, args int) error { - if _, err := output.WriteString("call " + function + " " + fmt.Sprint(args)); err != nil { + if _, err := output.WriteString("call " + function + " " + fmt.Sprint(args) + "\n"); err != nil { return err } @@ -133,7 +133,7 @@ func WriteCall(output *strings.Builder, function string, args int) error { } func WriteFunction(output *strings.Builder, function string, params int) error { - if _, err := output.WriteString("function " + function + " " + fmt.Sprint(params)); err != nil { + if _, err := output.WriteString("function " + function + " " + fmt.Sprint(params) + "\n"); err != nil { return err } @@ -141,7 +141,7 @@ func WriteFunction(output *strings.Builder, function string, params int) error { } func WriteReturn(output *strings.Builder) error { - if _, err := output.WriteString("return"); err != nil { + if _, err := output.WriteString("return" + "\n"); err != nil { return err } diff --git a/internal/compilation-engine/compilation-engine.go b/internal/compilation-engine/compilation-engine.go index 4d1e120..d1dc3c7 100644 --- a/internal/compilation-engine/compilation-engine.go +++ b/internal/compilation-engine/compilation-engine.go @@ -6,8 +6,7 @@ import ( "strconv" "strings" - "fmt" - + "github.com/hazemKrimi/jack-compiler/internal/code" "github.com/hazemKrimi/jack-compiler/internal/symbol-table" "github.com/hazemKrimi/jack-compiler/internal/tokenizer" ) @@ -15,223 +14,44 @@ import ( var className string var classSymbolTable, subroutineSymbolTable map[string]table.Variable -func writeToken(output *strings.Builder, token tokenizer.Token, index *int) error { - tokenDefinition := "<" + token.XML + "> " - - if token.Type == tokenizer.IDENTIFIER { - variable, found := table.GetVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, token.Value) - - if found { - tokenDefinition += "" - tokenDefinition += "name: " + token.Value + ", " - tokenDefinition += "type: " + variable.Type + ", " - tokenDefinition += "kind: " + fmt.Sprint(variable.Kind) + ", " - tokenDefinition += "count: " + fmt.Sprint(variable.Count) + ", " - tokenDefinition += "declared: " + strconv.FormatBool(variable.IsDeclared) + ", " - tokenDefinition += "used: " + strconv.FormatBool(variable.IsUsed) - tokenDefinition += "\n" - } else { - tokenDefinition += token.Value - } - } else { - tokenDefinition += token.Value - } - - tokenDefinition += " \n" - - if _, err := output.WriteString(tokenDefinition); err != nil { - return err - } - - (*index)++ - - return nil -} - -func compileClassVarDec(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - if tokens[*index].Type != tokenizer.KEYWORD || !slices.Contains([]string{"static", "field"}, tokens[*index].Value) { - return nil - } - - var kind table.VariableKind - - if tokens[*index].Value == "static" { - kind = table.STATIC - } else { - kind = table.FIELD - } - - output.WriteString("\n") - writeToken(output, tokens[*index], index) - - if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) && !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { - return errors.New("Invalid variable type name!") - } - - variableType := tokens[*index].Value - - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid variable name!") - } - - table.AppendVariable(&classSymbolTable, tokens[*index].Value, variableType, kind) - writeToken(output, tokens[*index], index) - - for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid variable name!") - } - - table.AppendVariable(&classSymbolTable, tokens[*index].Value, variableType, kind) - writeToken(output, tokens[*index], index) - } - - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { - return errors.New("Missing semicolon!") - } - - writeToken(output, tokens[*index], index) - output.WriteString("\n") - - return compileClassVarDec(output, tokens, index) -} - -func compileParameterList(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) || !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { - return nil - } - - variableType := tokens[*index].Value - kind := table.ARG - - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid variable name!") - } - - table.AppendVariable(&subroutineSymbolTable, tokens[*index].Value, variableType, kind) - writeToken(output, tokens[*index], index) - - if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { - writeToken(output, tokens[*index], index) - - return compileParameterList(output, tokens, index) - } - - return nil -} - -func compileVariableDeclaration(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "var" { - return nil - } - - output.WriteString("\n") - - writeToken(output, tokens[*index], index) - - if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) && !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { - return errors.New("Invalid variable type name!") - } - - variableType := tokens[*index].Value - kind := table.VAR - - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid variable name!") - } - - table.AppendVariable(&subroutineSymbolTable, tokens[*index].Value, variableType, kind) - writeToken(output, tokens[*index], index) - - for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid variable name!") - } - - writeToken(output, tokens[*index], index) - } - - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { - return errors.New("Missing semicolon!") - } - - writeToken(output, tokens[*index], index) - output.WriteString("\n") - - return compileVariableDeclaration(output, tokens, index) -} - -func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - if tokens[*index].Value == "." { - writeToken(output, tokens[*index], index) - - if tokens[*index].Type != tokenizer.IDENTIFIER { - return errors.New("Invalid subroutine name!") - } - - writeToken(output, tokens[*index], index) - } - - if tokens[*index].Value != "(" { - return errors.New("Missing subroutine call opening parenthese!") - } - - writeToken(output, tokens[*index], index) - - output.WriteString("\n") - - if err := compileExpressionList(output, tokens, index); err != nil { - return err - } - - output.WriteString("\n") - - if tokens[*index].Value != ")" { - return errors.New("Missing subroutine call closing parenthese!") - } - - writeToken(output, tokens[*index], index) - - return nil -} - func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - output.WriteString("\n") - if tokens[*index].Type == tokenizer.SYMBOL && slices.Contains([]string{"-", "~"}, tokens[*index].Value) { - writeToken(output, tokens[*index], index) + op := tokens[*index].Value + + (*index)++ if err := compileTerm(output, tokens, index); err != nil { return err } - output.WriteString("\n") + switch op { + case "-": + code.WriteArithmeticLogical(output, code.NEG) + case "~": + code.WriteArithmeticLogical(output, code.NOT) + } + return nil } if slices.Contains([]tokenizer.TokenType{tokenizer.INT_CONST, tokenizer.STR_CONST}, tokens[*index].Type) || slices.Contains([]string{"true", "false", "null", "this"}, tokens[*index].Value) { - if tokens[*index].Value == "this" { - table.UseVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, tokens[*index].Value) + if tokens[*index].Type == tokenizer.INT_CONST { + number, err := strconv.ParseInt(tokens[*index].Value, 10, 32) + + if err != nil { + return err + } + + code.WritePush(output, code.CONSTANT, int(number)) } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ return nil } if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "(" { - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -241,18 +61,16 @@ func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int) return errors.New("Invalid term!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ return nil } if tokens[*index].Type == tokenizer.IDENTIFIER { - table.UseVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, tokens[*index].Value) - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Value == "[" { - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -262,52 +80,212 @@ func compileTerm(output *strings.Builder, tokens []tokenizer.Token, index *int) return errors.New("Invalid term!") } - writeToken(output, tokens[*index], index) + (*index)++ } else if slices.Contains([]string{"(", "."}, tokens[*index].Value) { - if err := compileSubroutineCall(output, tokens, index); err != nil { + if err := compileSubroutineCall(output, tokens, index, tokens[*index].Value); err != nil { return err } } - - output.WriteString("\n") } return nil } func compileExpression(output *strings.Builder, tokens []tokenizer.Token, index *int) error { - output.WriteString("\n") - if err := compileTerm(output, tokens, index); err != nil { return err } - if slices.Contains([]string{"+", "-", "*", "/", "&", "|", "<", ">", "="}, tokens[*index].Value) { - writeToken(output, tokens[*index], index) + if slices.Contains([]string{"+", "-", "*", "/", "&", "|", "<", ">", "="}, tokens[*index].Value) { + op := tokens[*index].Value + + (*index)++ if err := compileTerm(output, tokens, index); err != nil { return err } - } - output.WriteString("\n") + switch op { + case "+": + code.WriteArithmeticLogical(output, code.ADD) + case "-": + code.WriteArithmeticLogical(output, code.SUB) + case "*": + code.WriteCall(output, "Math.multiply", 2) + case "/": + code.WriteCall(output, "Math.divide", 2) + case "&": + code.WriteArithmeticLogical(output, code.AND) + case "|": + code.WriteArithmeticLogical(output, code.OR) + case "<": + code.WriteArithmeticLogical(output, code.LT) + case ">": + code.WriteArithmeticLogical(output, code.GT) + case "=": + code.WriteArithmeticLogical(output, code.EQ) + } + } return nil } -func compileExpressionList(output *strings.Builder, tokens []tokenizer.Token, index *int) error { +func compileExpressionList(output *strings.Builder, tokens []tokenizer.Token, index *int) (int, error) { + args := 0 + if slices.Contains([]tokenizer.TokenType{tokenizer.IDENTIFIER, tokenizer.INT_CONST, tokenizer.STR_CONST}, tokens[*index].Type) || slices.Contains([]string{"true", "false", "null", "this", "~", "-", "("}, tokens[*index].Value) { if err := compileExpression(output, tokens, index); err != nil { - return err + return 0, err } + args++ + if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { - writeToken(output, tokens[*index], index) + (*index)++ - return compileExpressionList(output, tokens, index) + more, err := compileExpressionList(output, tokens, index) + + if err != nil { + return 0, err + } + + args += more } } + return args, nil +} + +func compileParameterList(output *strings.Builder, tokens []tokenizer.Token, index *int) (int, error) { + params := 0 + + if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) || !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { + return 0, nil + } + + variableType := tokens[*index].Value + kind := table.ARG + + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return 0, errors.New("Invalid variable name!") + } + + table.AppendVariable(&subroutineSymbolTable, tokens[*index].Value, variableType, kind) + (*index)++ + params++ + + if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { + (*index)++ + + more, err := compileParameterList(output, tokens, index) + + if err != nil { + return 0, err + } + + params += more + } + + return params, nil +} + +func compileVariableDeclaration(output *strings.Builder, tokens []tokenizer.Token, index *int) error { + if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "var" { + return nil + } + + (*index)++ + + if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) && !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { + return errors.New("Invalid variable type name!") + } + + variableType := tokens[*index].Value + kind := table.VAR + + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return errors.New("Invalid variable name!") + } + + table.AppendVariable(&subroutineSymbolTable, tokens[*index].Value, variableType, kind) + (*index)++ + + for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return errors.New("Invalid variable name!") + } + + (*index)++ + } + + if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { + return errors.New("Missing semicolon!") + } + + (*index)++ + + return compileVariableDeclaration(output, tokens, index) +} + +func compileSubroutineCall(output *strings.Builder, tokens []tokenizer.Token, index *int, identifier string) error { + var class string + var function string + + isMethod := false + + if tokens[*index].Value == "." { + variable, found := table.GetVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, identifier) + + if found { + code.WritePush(output, code.ARGUMENT, 0) + + isMethod = true + class = variable.Type + } else { + class = identifier + } + + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return errors.New("Invalid subroutine name!") + } + + function = class + "." + tokens[*index].Value + + (*index)++ + } + + if tokens[*index].Value != "(" { + return errors.New("Missing subroutine call opening parenthese!") + } + + (*index)++ + + args, err := compileExpressionList(output, tokens, index) + + if err != nil { + return err + } + + if tokens[*index].Value != ")" { + return errors.New("Missing subroutine call closing parenthese!") + } + + if isMethod { + args++ + } + + code.WriteCall(output, function, args) + + (*index)++ + return nil } @@ -316,19 +294,17 @@ func compileLetStatement(output *strings.Builder, tokens []tokenizer.Token, inde return errors.New("Invalid let statement!") } - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } table.UseVariable([]*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}, tokens[*index].Value) - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Value == "[" { - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -338,14 +314,14 @@ func compileLetStatement(output *strings.Builder, tokens []tokenizer.Token, inde return errors.New("Invalid expression!") } - writeToken(output, tokens[*index], index) + (*index)++ } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "=" { return errors.New("Missing assignment!") } - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -355,8 +331,7 @@ func compileLetStatement(output *strings.Builder, tokens []tokenizer.Token, inde return errors.New("Missing semicolon!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ return nil } @@ -366,15 +341,13 @@ func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index return errors.New("Invalid if statement!") } - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing if statement opening parenthese!") } - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -384,53 +357,44 @@ func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index return errors.New("Missing if statement closing parenthese!") } - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing if statement opening curly brace!") } - writeToken(output, tokens[*index], index) - - output.WriteString("\n") + (*index)++ if err := compileStatements(output, tokens, index); err != nil { return err } - output.WriteString("\n") - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing if statement closing curly brace!") } - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "else" { - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing if statement opening curly brace!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ if err := compileStatements(output, tokens, index); err != nil { return err } - output.WriteString("\n") - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing if statement closing curly brace!") } - writeToken(output, tokens[*index], index) + (*index)++ } - output.WriteString("\n") - return nil } @@ -439,15 +403,13 @@ func compileWhileStatement(output *strings.Builder, tokens []tokenizer.Token, in return errors.New("Invalid while statement!") } - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing while statement opening parenthese!") } - writeToken(output, tokens[*index], index) + (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err @@ -457,29 +419,23 @@ func compileWhileStatement(output *strings.Builder, tokens []tokenizer.Token, in return errors.New("Missing while statement closing parenthese!") } - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing while statement opening curly brace!") } - writeToken(output, tokens[*index], index) - - output.WriteString("\n") + (*index)++ if err := compileStatements(output, tokens, index); err != nil { return err } - output.WriteString("\n") - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing while statement closing curly brace!") } - writeToken(output, tokens[*index], index) - - output.WriteString("\n") + (*index)++ return nil } @@ -489,17 +445,17 @@ func compileDoStatement(output *strings.Builder, tokens []tokenizer.Token, index return errors.New("Invalid do statement!") } - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } - writeToken(output, tokens[*index], index) + identifier := tokens[*index].Value - if err := compileSubroutineCall(output, tokens, index); err != nil { + (*index)++ + + if err := compileSubroutineCall(output, tokens, index, identifier); err != nil { return err } @@ -507,8 +463,8 @@ func compileDoStatement(output *strings.Builder, tokens []tokenizer.Token, index return errors.New("Missing semicolon!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ + code.WritePop(output, code.TEMP, 0) return nil } @@ -518,22 +474,22 @@ func compileReturnStatement(output *strings.Builder, tokens []tokenizer.Token, i return errors.New("Invalid return statement!") } - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER, tokenizer.INT_CONST, tokenizer.STR_CONST}, tokens[*index].Type) { if err := compileExpression(output, tokens, index); err != nil { return err } + } else { + code.WritePush(output, code.CONSTANT, 0) } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + code.WriteReturn(output) + (*index)++ return nil } @@ -578,14 +534,10 @@ func compileSubroutineBody(output *strings.Builder, tokens []tokenizer.Token, in } } - output.WriteString("\n") - if err := compileStatements(output, tokens, index); err != nil { return err } - output.WriteString("\n") - return nil } @@ -598,55 +550,58 @@ func compileSubroutineDeclaration(output *strings.Builder, tokens []tokenizer.To isMethod := tokens[*index].Value == "method" - output.WriteString("\n") - - writeToken(output, tokens[*index], index) + (*index)++ if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) && !slices.Contains([]string{"void", "int", "char", "boolean"}, tokens[*index].Value) { return errors.New("Invalid subroutine return type!") } - writeToken(output, tokens[*index], index) + (*index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid subroutine name!") } - writeToken(output, tokens[*index], index) + function := tokens[*index].Value + + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing subroutine opening parenthese!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + (*index)++ if isMethod { variableType := className kind := table.ARG table.AppendVariable(&subroutineSymbolTable, "this", variableType, kind) - table.WriteImplicitThis(output, []*map[string]table.Variable{&subroutineSymbolTable, &classSymbolTable}) } - if err := compileParameterList(output, tokens, index); err != nil { + params, err := compileParameterList(output, tokens, index) + + if err != nil { return err } - output.WriteString("\n") - if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ")" { return errors.New("Missing subroutine closing parenthese!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") + if isMethod { + params++ + } + + code.WriteFunction(output, className+"."+function, params) + + (*index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing subroutine opening curly brace!") } - writeToken(output, tokens[*index], index) + (*index)++ if err := compileSubroutineBody(output, tokens, index); err != nil { return err @@ -656,25 +611,71 @@ func compileSubroutineDeclaration(output *strings.Builder, tokens []tokenizer.To return errors.New("Missing subroutine closing curly brace!") } - writeToken(output, tokens[*index], index) - output.WriteString("\n") - output.WriteString("\n") + (*index)++ return compileSubroutineDeclaration(output, tokens, index) } +func compileClassVarDec(output *strings.Builder, tokens []tokenizer.Token, index *int) error { + if tokens[*index].Type != tokenizer.KEYWORD || !slices.Contains([]string{"static", "field"}, tokens[*index].Value) { + return nil + } + + var kind table.VariableKind + + if tokens[*index].Value == "static" { + kind = table.STATIC + } else { + kind = table.FIELD + } + + (*index)++ + + if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER}, tokens[*index].Type) && !slices.Contains([]string{"int", "char", "boolean"}, tokens[*index].Value) { + return errors.New("Invalid variable type name!") + } + + variableType := tokens[*index].Value + + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return errors.New("Invalid variable name!") + } + + table.AppendVariable(&classSymbolTable, tokens[*index].Value, variableType, kind) + (*index)++ + + for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { + (*index)++ + + if tokens[*index].Type != tokenizer.IDENTIFIER { + return errors.New("Invalid variable name!") + } + + table.AppendVariable(&classSymbolTable, tokens[*index].Value, variableType, kind) + (*index)++ + } + + if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { + return errors.New("Missing semicolon!") + } + + (*index)++ + + return compileClassVarDec(output, tokens, index) +} + func compileClass(output *strings.Builder, tokens []tokenizer.Token) error { index := 0 classSymbolTable = make(map[string]table.Variable) - output.WriteString("\n") - if tokens[index].Type != tokenizer.KEYWORD || tokens[index].Value != "class" { return errors.New("Jack file must contain one class!") } - writeToken(output, tokens[index], &index) + index++ if tokens[index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid class name!") @@ -682,13 +683,13 @@ func compileClass(output *strings.Builder, tokens []tokenizer.Token) error { className = tokens[index].Value - writeToken(output, tokens[index], &index) + index++ if tokens[index].Type != tokenizer.SYMBOL || tokens[index].Value != "{" { return errors.New("Missing class opening curly brace!") } - writeToken(output, tokens[index], &index) + index++ if err := compileClassVarDec(output, tokens, &index); err != nil { return err @@ -702,8 +703,7 @@ func compileClass(output *strings.Builder, tokens []tokenizer.Token) error { return errors.New("Missing class closing curly brace!") } - writeToken(output, tokens[index], &index) - output.WriteString("\n") + index++ return nil } diff --git a/internal/symbol-table/symbol-table.go b/internal/symbol-table/symbol-table.go index 04c2108..f59c2f8 100644 --- a/internal/symbol-table/symbol-table.go +++ b/internal/symbol-table/symbol-table.go @@ -1,11 +1,5 @@ package table -import ( - "fmt" - "strconv" - "strings" -) - type VariableKind int const ( @@ -59,27 +53,6 @@ func UseVariable(symbolTables []*map[string]Variable, name string) { } } -func WriteImplicitThis(output *strings.Builder, symbolTables []*map[string]Variable) error { - variable, found := GetVariable(symbolTables, "this") - - if found { - tokenDefinition := " " - tokenDefinition += "name: this, " - tokenDefinition += "type: " + variable.Type + ", " - tokenDefinition += "kind: " + fmt.Sprint(variable.Kind) + ", " - tokenDefinition += "count: " + fmt.Sprint(variable.Count) + ", " - tokenDefinition += "declared: " + strconv.FormatBool(variable.IsDeclared) + ", " - tokenDefinition += "used: " + strconv.FormatBool(variable.IsUsed) - tokenDefinition += "\n" - - if _, err := output.WriteString(tokenDefinition); err != nil { - return err - } - } - - return nil -} - func AppendVariable(symbolTable *map[string]Variable, name string, variableType string, kind VariableKind) { (*symbolTable)[name] = Variable{Type: variableType, Kind: kind, Count: CountVariables(symbolTable, kind) + 1, IsDeclared: true} } diff --git a/internal/tokenizer/tokenizer.go b/internal/tokenizer/tokenizer.go index dcad85a..1201dca 100644 --- a/internal/tokenizer/tokenizer.go +++ b/internal/tokenizer/tokenizer.go @@ -75,20 +75,7 @@ type Token struct { func NewToken(value string, tokenType TokenType) Token { switch tokenType { case SYMBOL: - var symbol string - - switch value { - case "<": - symbol = "<" - case ">": - symbol = ">" - case "&": - symbol = "&" - default: - symbol = value - } - - return Token{Value: symbol, Type: tokenType, XML: "symbol"} + return Token{Value: value, Type: tokenType, XML: "symbol"} case KEYWORD: return Token{Value: value, Type: tokenType, XML: "keyword"} case IDENTIFIER: diff --git a/main.go b/main.go index cc73bab..eaf62b9 100644 --- a/main.go +++ b/main.go @@ -32,7 +32,7 @@ func process(inputPath string) error { return err } - outputPath := strings.Replace(inputPath, ".jack", ".xml", 1) + outputPath := strings.Replace(inputPath, ".jack", ".vm", 1) if err := os.WriteFile(outputPath, []byte(compiled), 0644); err != nil { return err