package utils import ( "reflect" "testing" ) func TestParseParams(t *testing.T) { tests := []struct { name string input string want []string wantErr bool }{ { name: "simple params", input: "BYTE x 10", want: []string{"BYTE", "x", "10"}, }, { name: "with quoted string", input: `PRINT "hello world"`, want: []string{"PRINT", `"hello world"`}, }, { name: "multiple spaces", input: "WORD y 20", want: []string{"WORD", "y", "20"}, }, { name: "empty string", input: "", want: []string{}, }, { name: "only spaces", input: " ", want: []string{}, }, { name: "unterminated string", input: `PRINT "hello`, wantErr: true, }, { name: "string with spaces inside", input: `CALL print ( "hello world" )`, want: []string{"CALL", "print", "(", `"hello world"`, ")"}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := ParseParams(tt.input) if (err != nil) != tt.wantErr { t.Errorf("ParseParams() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && !reflect.DeepEqual(got, tt.want) { t.Errorf("ParseParams() = %v, want %v", got, tt.want) } }) } } func TestNormalizeSpaces(t *testing.T) { tests := []struct { name string input string want string }{ { name: "multiple spaces", input: "BYTE x 10", want: "BYTE x 10", }, { name: "preserves string spaces", input: `PRINT "hello world"`, want: `PRINT "hello world"`, }, { name: "leading trailing spaces", input: " BYTE x ", want: "BYTE x", }, { name: "tabs", input: "BYTE\t\tx", want: "BYTE x", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := NormalizeSpaces(tt.input) if got != tt.want { t.Errorf("NormalizeSpaces() = %q, want %q", got, tt.want) } }) } } func TestIsStringLiteral(t *testing.T) { tests := []struct { input string want bool }{ {`"hello"`, true}, {`"hello world"`, true}, {`""`, true}, {`"`, false}, {`hello`, false}, {`'hello'`, false}, {`hello"`, false}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := IsStringLiteral(tt.input) if got != tt.want { t.Errorf("IsStringLiteral(%q) = %v, want %v", tt.input, got, tt.want) } }) } } func TestStripQuotes(t *testing.T) { tests := []struct { input string want string }{ {`"hello"`, "hello"}, {`"hello world"`, "hello world"}, {`""`, ""}, {`hello`, "hello"}, {`"`, `"`}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := StripQuotes(tt.input) if got != tt.want { t.Errorf("StripQuotes(%q) = %q, want %q", tt.input, got, tt.want) } }) } } func TestValidateIdentifier(t *testing.T) { tests := []struct { input string want bool }{ {"x", true}, {"_temp", true}, {"myVar123", true}, {"_", true}, {"var_name", true}, {"123abc", false}, {"", false}, {"my-var", false}, {"my var", false}, {"my.var", false}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := ValidateIdentifier(tt.input) if got != tt.want { t.Errorf("ValidateIdentifier(%q) = %v, want %v", tt.input, got, tt.want) } }) } } func TestEvaluateExpression(t *testing.T) { // Simple constant lookup for testing constLookup := func(name string) (int64, bool) { constants := map[string]int64{ "MAXVAL": 255, "BASE": 100, "OFFSET": 10, } val, ok := constants[name] return val, ok } tests := []struct { name string expr string lookup ConstantLookup want int64 wantErr bool }{ // Decimal {name: "simple decimal", expr: "42", want: 42}, {name: "decimal zero", expr: "0", want: 0}, // Hex {name: "hex lowercase", expr: "$ff", want: 255}, {name: "hex uppercase", expr: "$FF", want: 255}, {name: "hex zero", expr: "$00", want: 0}, {name: "hex word", expr: "$C000", want: 0xC000}, // Binary {name: "binary 8bit", expr: "%11111111", want: 255}, {name: "binary zero", expr: "%00000000", want: 0}, {name: "binary mixed", expr: "%10101010", want: 0xAA}, // Addition {name: "add decimal", expr: "10+20", want: 30}, {name: "add hex", expr: "$10+$20", want: 0x30}, {name: "add binary", expr: "%1010+%0101", want: 15}, {name: "add multiple", expr: "10+20+30", want: 60}, // Subtraction {name: "subtract", expr: "100-25", want: 75}, {name: "subtract chain", expr: "100-20-10", want: 70}, // Multiplication {name: "multiply", expr: "10*5", want: 50}, {name: "multiply chain", expr: "2*3*4", want: 24}, // Division {name: "divide", expr: "100/5", want: 20}, {name: "divide chain", expr: "100/5/2", want: 10}, // Binary OR {name: "binary or", expr: "$F0|$0F", want: 0xFF}, {name: "binary or multiple", expr: "1|2|4", want: 7}, // Binary AND {name: "binary and", expr: "$FF&$0F", want: 0x0F}, {name: "binary and multiple", expr: "255&127&63", want: 63}, // Mixed operators {name: "mixed math", expr: "10+20*2", want: 60}, // left-to-right: (10+20)*2 {name: "mixed bitwise", expr: "255&$F0|$0F", want: 0xFF}, // Constants {name: "constant", expr: "MAXVAL", lookup: constLookup, want: 255}, {name: "constant add", expr: "BASE+OFFSET", lookup: constLookup, want: 110}, {name: "constant unknown", expr: "UNKNOWN", lookup: constLookup, wantErr: true}, {name: "constant no lookup", expr: "MAXVAL", wantErr: true}, // Spaces {name: "spaces around ops", expr: "10 + 20", want: 30}, {name: "spaces everywhere", expr: " 100 - 20 ", want: 80}, // Errors {name: "empty", expr: "", wantErr: true}, {name: "invalid hex", expr: "$ZZ", wantErr: true}, {name: "invalid binary", expr: "!22", wantErr: true}, {name: "division by zero", expr: "10/0", wantErr: true}, {name: "trailing operator", expr: "10+", wantErr: true}, {name: "leading operator", expr: "+10", wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := EvaluateExpression(tt.expr, tt.lookup) if (err != nil) != tt.wantErr { t.Errorf("EvaluateExpression() error = %v, wantErr %v", err, tt.wantErr) return } if !tt.wantErr && got != tt.want { t.Errorf("EvaluateExpression(%q) = %v, want %v", tt.expr, got, tt.want) } }) } }