package parser import ( "errors" "slices" "strings" "github.com/hazemKrimi/jack-compiler/internal/tokenizer" ) 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 } output.WriteString(" " + tokens[*index].Value + " \n") *(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!") } if tokens[*index].Type == tokenizer.KEYWORD { output.WriteString(" " + tokens[*index].Value + " \n") } else { output.WriteString(" " + tokens[*index].Value + " \n") } *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ 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 } if tokens[*index].Type == tokenizer.KEYWORD { output.WriteString(" " + tokens[*index].Value + " \n") } else { output.WriteString(" " + tokens[*index].Value + " \n") } *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { output.WriteString(" " + tokens[*index].Value + " \n") *(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(" " + tokens[*index].Value + " \n") *(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!") } if tokens[*index].Type == tokenizer.KEYWORD { output.WriteString(" " + tokens[*index].Value + " \n") } else { output.WriteString(" " + tokens[*index].Value + " \n") } *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ for tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ return compileVariableDeclaration(output, tokens, index) } func compileExpression(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if !slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, tokenizer.IDENTIFIER, tokenizer.INT_CONST, tokenizer.STR_CONST}, tokens[*index].Type) && !slices.Contains([]string{"true", "false", "null", "this"}, tokens[*index].Value) { return errors.New("Invalid expression!") } output.WriteString("\n") output.WriteString("\n") switch tokens[*index].Type { // case tokenizer.SYMBOL: // var value string // // switch tokens[*index].Value { // case "<": // value = "<" // case ">": // value = ">" // case "&": // value = "&" // default: // value = tokens[*index].Value // } // // output.WriteString(" " + value + " \n") case tokenizer.KEYWORD: output.WriteString(" " + tokens[*index].Value + " \n") case tokenizer.IDENTIFIER: output.WriteString(" " + tokens[*index].Value + " \n") case tokenizer.INT_CONST: output.WriteString(" " + tokens[*index].Value + " \n") case tokenizer.STR_CONST: output.WriteString(" " + tokens[*index].Value + " \n") } output.WriteString("\n") output.WriteString("\n") *(index)++ return nil } func compileExpressionList(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if slices.Contains([]tokenizer.TokenType{tokenizer.KEYWORD, 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 } if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "," { output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ return compileExpressionList(output, tokens, index) } } return nil } func compileLetStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "let" { return errors.New("Invalid let statement!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString("\n") output.WriteString("\n") output.WriteString(" " + tokens[*index].Value + " \n") output.WriteString("\n") output.WriteString("\n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "=" { return errors.New("Missing assignment!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ if err := compileExpression(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ return nil } func compileIfStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "if" { return errors.New("Invalid if statement!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing if statement opening parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileExpression(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ")" { return errors.New("Missing if statement closing parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing if statement opening curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileStatements(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing if statement closing curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "else" { output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing if statement opening curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileStatements(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing if statement closing curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ } return nil } func compileWhileStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "while" { return errors.New("Invalid while statement!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing while statement opening parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileExpression(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ")" { return errors.New("Missing while statement closing parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing while statement opening curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileStatements(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing while statement closing curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ return nil } func compileDoStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "do" { return errors.New("Invalid do statement!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type == tokenizer.SYMBOL && tokens[*index].Value == "." { *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid variable name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing subroutine call opening parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileExpressionList(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ")" { return errors.New("Missing subroutine call closing parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ return nil } func compileReturnStatement(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || tokens[*index].Value != "return" { return errors.New("Invalid return statement!") } output.WriteString(" " + tokens[*index].Value + " \n") *(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 } } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ";" { return errors.New("Missing semicolon!") } output.WriteString(" " + tokens[*index].Value + " \n") (*index)++ return nil } func compileStatements(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD { return nil } output.WriteString("<") output.WriteString(tokens[*index].Value) output.WriteString("Statement>\n") switch tokens[*index].Value { case "let": if err := compileLetStatement(output, tokens, index); err != nil { return err } case "if": if err := compileIfStatement(output, tokens, index); err != nil { return err } case "while": if err := compileWhileStatement(output, tokens, index); err != nil { return err } case "do": if err := compileDoStatement(output, tokens, index); err != nil { return err } case "return": if err := compileReturnStatement(output, tokens, index); err != nil { return err } } output.WriteString("\n") return compileStatements(output, tokens, index) } func compileSubroutineBody(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type == tokenizer.KEYWORD && tokens[*index].Value == "var" { output.WriteString("\n") if err := compileVariableDeclaration(output, tokens, index); err != nil { return err } output.WriteString("\n") } output.WriteString("\n") if err := compileStatements(output, tokens, index); err != nil { return err } output.WriteString("\n") return nil } func compileSubroutineDeclaration(output *strings.Builder, tokens []tokenizer.Token, index *int) error { if tokens[*index].Type != tokenizer.KEYWORD || !slices.Contains([]string{"constructor", "method", "function"}, tokens[*index].Value) { return nil } output.WriteString(" " + tokens[*index].Value + " \n") *(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!") } if tokens[*index].Type == tokenizer.KEYWORD { output.WriteString(" " + tokens[*index].Value + " \n") } else { output.WriteString(" " + tokens[*index].Value + " \n") } *(index)++ if tokens[*index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid subroutine name!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "(" { return errors.New("Missing subroutine opening parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ output.WriteString("\n") if err := compileParameterList(output, tokens, index); err != nil { return err } output.WriteString("\n") if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != ")" { return errors.New("Missing subroutine closing parenthese!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ output.WriteString("\n") if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "{" { return errors.New("Missing subroutine opening curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ if err := compileSubroutineBody(output, tokens, index); err != nil { return err } if tokens[*index].Type != tokenizer.SYMBOL || tokens[*index].Value != "}" { return errors.New("Missing subroutine closing curly brace!") } output.WriteString(" " + tokens[*index].Value + " \n") *(index)++ output.WriteString("\n") return compileSubroutineDeclaration(output, tokens, index) } func compileClass(output *strings.Builder, tokens []tokenizer.Token) error { index := 0 output.WriteString("\n") if tokens[index].Type != tokenizer.KEYWORD || tokens[index].Value != "class" { return errors.New("Jack file must contain one class!") } output.WriteString(" " + tokens[index].Value + " \n") index++ if tokens[index].Type != tokenizer.IDENTIFIER { return errors.New("Invalid class name!") } output.WriteString(" " + tokens[index].Value + " \n") index++ if tokens[index].Type != tokenizer.SYMBOL || tokens[index].Value != "{" { return errors.New("Missing class opening curly brace!") } output.WriteString(" " + tokens[index].Value + " \n") index++ output.WriteString("\n") if err := compileClassVarDec(output, tokens, &index); err != nil { return err } output.WriteString("\n") output.WriteString("\n") if err := compileSubroutineDeclaration(output, tokens, &index); err != nil { return err } output.WriteString("\n") if tokens[index].Type != tokenizer.SYMBOL || tokens[index].Value != "}" { return errors.New("Missing class closing curly brace!") } output.WriteString(" " + tokens[index].Value + " \n") output.WriteString("\n") return nil } func ParseTokens(tokens []tokenizer.Token) (string, error) { var output strings.Builder if err := compileClass(&output, tokens); err != nil { return "", err } return output.String(), nil }