From ae17059201602d9c497dfd3d9e39900ec3176a7a Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Sat, 30 Aug 2025 11:19:10 +0200 Subject: [PATCH] Simplified ssa and expression packages --- src/expression/Parse_test.go | 1 + src/expression/operator.go | 79 ++++++++++++++++----------------- src/expression/write.go | 6 +-- src/ssa/BinaryOp.go | 3 +- src/ssa/UnaryOp.go | 3 +- src/token/Kind.go | 86 ++++++++++++++++++++++++++++++++++++ src/token/Kind_test.go | 43 ++++++++++++++++++ src/token/List.go | 4 ++ src/token/List_test.go | 13 ++++++ src/token/Source_test.go | 14 ++++++ 10 files changed, 205 insertions(+), 47 deletions(-) create mode 100644 src/token/Source_test.go diff --git a/src/expression/Parse_test.go b/src/expression/Parse_test.go index af2f6a8..d4738ce 100644 --- a/src/expression/Parse_test.go +++ b/src/expression/Parse_test.go @@ -101,6 +101,7 @@ func TestParse(t *testing.T) { {"Function calls 25", "a(1-2*3)", "(λ a (- 1 (* 2 3)))"}, {"Function calls 26", "1+2*a()+4", "(+ (+ 1 (* 2 (λ a))) 4)"}, {"Function calls 27", "a(b,c)*2+15*4", "(+ (* (λ a b c) 2) (* 15 4))"}, + {"Function calls 28", "a(,)", "(λ a )"}, {"Package function calls", "a.b(c)", "(λ (. a b) c)"}, {"Package function calls 2", "a.b(c,d)", "(λ (. a b) c d)"}, diff --git a/src/expression/operator.go b/src/expression/operator.go index 4bf6795..30f8334 100644 --- a/src/expression/operator.go +++ b/src/expression/operator.go @@ -8,7 +8,6 @@ import ( // operator represents an operator for mathematical expressions. type operator struct { - Symbol string Precedence int8 Operands int8 } @@ -16,48 +15,48 @@ type operator struct { // Operators defines the Operators used in the language. // The number corresponds to the operator priority and can not be zero. var Operators = [64]operator{ - token.Struct: {"$", 13, 1}, - token.Dot: {".", 13, 2}, - token.Call: {"λ", 12, 1}, - token.Array: {"@", 12, 2}, - token.Negate: {"-", 11, 1}, - token.Not: {"!", 11, 1}, - token.Mul: {"*", 10, 2}, - token.Div: {"/", 10, 2}, - token.Mod: {"%", 10, 2}, - token.Add: {"+", 9, 2}, - token.Sub: {"-", 9, 2}, - token.Shr: {">>", 8, 2}, - token.Shl: {"<<", 8, 2}, - token.And: {"&", 7, 2}, - token.Xor: {"^", 6, 2}, - token.Or: {"|", 5, 2}, + token.Struct: {13, 1}, + token.Dot: {13, 2}, + token.Call: {12, 1}, + token.Array: {12, 2}, + token.Negate: {11, 1}, + token.Not: {11, 1}, + token.Mul: {10, 2}, + token.Div: {10, 2}, + token.Mod: {10, 2}, + token.Add: {9, 2}, + token.Sub: {9, 2}, + token.Shr: {8, 2}, + token.Shl: {8, 2}, + token.And: {7, 2}, + token.Xor: {6, 2}, + token.Or: {5, 2}, - token.Greater: {">", 4, 2}, - token.Less: {"<", 4, 2}, - token.GreaterEqual: {">=", 4, 2}, - token.LessEqual: {"<=", 4, 2}, - token.Equal: {"==", 3, 2}, - token.NotEqual: {"!=", 3, 2}, - token.LogicalAnd: {"&&", 2, 2}, - token.LogicalOr: {"||", 1, 2}, + token.Greater: {4, 2}, + token.Less: {4, 2}, + token.GreaterEqual: {4, 2}, + token.LessEqual: {4, 2}, + token.Equal: {3, 2}, + token.NotEqual: {3, 2}, + token.LogicalAnd: {2, 2}, + token.LogicalOr: {1, 2}, - token.Range: {"..", 0, 2}, - token.Separator: {",", 0, 2}, + token.Range: {0, 2}, + token.Separator: {0, 2}, - token.Assign: {"=", math.MinInt8, 2}, - token.Define: {":=", math.MinInt8, 2}, - token.AddAssign: {"+=", math.MinInt8, 2}, - token.SubAssign: {"-=", math.MinInt8, 2}, - token.MulAssign: {"*=", math.MinInt8, 2}, - token.DivAssign: {"/=", math.MinInt8, 2}, - token.ModAssign: {"%=", math.MinInt8, 2}, - token.AndAssign: {"&=", math.MinInt8, 2}, - token.OrAssign: {"|=", math.MinInt8, 2}, - token.XorAssign: {"^=", math.MinInt8, 2}, - token.ShrAssign: {">>=", math.MinInt8, 2}, - token.ShlAssign: {"<<=", math.MinInt8, 2}, - token.FieldAssign: {":", math.MinInt8, 2}, + token.Assign: {math.MinInt8, 2}, + token.Define: {math.MinInt8, 2}, + token.AddAssign: {math.MinInt8, 2}, + token.SubAssign: {math.MinInt8, 2}, + token.MulAssign: {math.MinInt8, 2}, + token.DivAssign: {math.MinInt8, 2}, + token.ModAssign: {math.MinInt8, 2}, + token.AndAssign: {math.MinInt8, 2}, + token.OrAssign: {math.MinInt8, 2}, + token.XorAssign: {math.MinInt8, 2}, + token.ShrAssign: {math.MinInt8, 2}, + token.ShlAssign: {math.MinInt8, 2}, + token.FieldAssign: {math.MinInt8, 2}, } func numOperands(symbol token.Kind) int { diff --git a/src/expression/write.go b/src/expression/write.go index 57edca3..114a5da 100644 --- a/src/expression/write.go +++ b/src/expression/write.go @@ -17,11 +17,11 @@ func (expr *Expression) write(builder *strings.Builder, source []byte) { switch expr.Token.Kind { case token.Call: - builder.WriteString(Operators[token.Call].Symbol) + builder.WriteString(token.Call.String()) case token.Array: - builder.WriteString(Operators[token.Array].Symbol) + builder.WriteString(token.Array.String()) case token.Struct: - builder.WriteString(Operators[token.Struct].Symbol) + builder.WriteString(token.Struct.String()) default: builder.WriteString(expr.Token.String(source)) } diff --git a/src/ssa/BinaryOp.go b/src/ssa/BinaryOp.go index 6e30bd5..124507b 100644 --- a/src/ssa/BinaryOp.go +++ b/src/ssa/BinaryOp.go @@ -3,7 +3,6 @@ package ssa import ( "fmt" - "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) @@ -55,7 +54,7 @@ func (op *BinaryOp) Replace(old Value, new Value) { // String returns a human-readable representation of the binary operation. func (op *BinaryOp) String() string { - return fmt.Sprintf("%p %s %p", op.Left, expression.Operators[op.Op].Symbol, op.Right) + return fmt.Sprintf("%p %s %p", op.Left, op.Op, op.Right) } // Type returns the type of the result of the binary operation. diff --git a/src/ssa/UnaryOp.go b/src/ssa/UnaryOp.go index 063002f..fb1a2d8 100644 --- a/src/ssa/UnaryOp.go +++ b/src/ssa/UnaryOp.go @@ -3,7 +3,6 @@ package ssa import ( "fmt" - "git.urbach.dev/cli/q/src/expression" "git.urbach.dev/cli/q/src/token" "git.urbach.dev/cli/q/src/types" ) @@ -46,7 +45,7 @@ func (u *UnaryOp) Replace(old Value, new Value) { // String returns a human-readable representation of the unary operation. func (u *UnaryOp) String() string { - return fmt.Sprintf("%s(%s)", expression.Operators[u.Op].Symbol, u.Operand) + return fmt.Sprintf("%s(%s)", u.Op, u.Operand) } // Type returns the type of the operand. diff --git a/src/token/Kind.go b/src/token/Kind.go index 597975c..e2ca91f 100644 --- a/src/token/Kind.go +++ b/src/token/Kind.go @@ -113,4 +113,90 @@ func (k Kind) IsOperator() bool { // IsUnaryOperator returns true if the token is a unary operator. func (k Kind) IsUnaryOperator() bool { return k > ___UNARY___ && k < ___END_UNARY___ +} + +// String returns a human-readable representation of the token kind. +func (k Kind) String() string { + switch k { + case Struct: + return "$" + case Dot: + return "." + case Call: + return "λ" + case Array: + return "@" + case Negate: + return "-" + case Not: + return "!" + case Mul: + return "*" + case Div: + return "/" + case Mod: + return "%" + case Add: + return "+" + case Sub: + return "-" + case Shr: + return ">>" + case Shl: + return "<<" + case And: + return "&" + case Xor: + return "^" + case Or: + return "|" + case Greater: + return ">" + case Less: + return "<" + case GreaterEqual: + return ">=" + case LessEqual: + return "<=" + case Equal: + return "==" + case NotEqual: + return "!=" + case LogicalAnd: + return "&&" + case LogicalOr: + return "||" + case Range: + return ".." + case Separator: + return "," + case Assign: + return "=" + case Define: + return ":=" + case AddAssign: + return "+=" + case SubAssign: + return "-=" + case MulAssign: + return "*=" + case DivAssign: + return "/=" + case ModAssign: + return "%=" + case AndAssign: + return "&=" + case OrAssign: + return "|=" + case XorAssign: + return "^=" + case ShrAssign: + return ">>=" + case ShlAssign: + return "<<=" + case FieldAssign: + return ":" + default: + return "" + } } \ No newline at end of file diff --git a/src/token/Kind_test.go b/src/token/Kind_test.go index 62301b0..fa29717 100644 --- a/src/token/Kind_test.go +++ b/src/token/Kind_test.go @@ -14,4 +14,47 @@ func TestTokenGroups(t *testing.T) { assert.True(t, token.Not.IsUnaryOperator()) assert.True(t, token.Number.IsNumeric()) assert.True(t, token.Equal.IsComparison()) +} + +func TestTokenKindString(t *testing.T) { + assert.Equal(t, token.Struct.String(), "$") + assert.Equal(t, token.Dot.String(), ".") + assert.Equal(t, token.Call.String(), "λ") + assert.Equal(t, token.Array.String(), "@") + assert.Equal(t, token.Negate.String(), "-") + assert.Equal(t, token.Not.String(), "!") + assert.Equal(t, token.Mul.String(), "*") + assert.Equal(t, token.Div.String(), "/") + assert.Equal(t, token.Mod.String(), "%") + assert.Equal(t, token.Add.String(), "+") + assert.Equal(t, token.Sub.String(), "-") + assert.Equal(t, token.Shr.String(), ">>") + assert.Equal(t, token.Shl.String(), "<<") + assert.Equal(t, token.And.String(), "&") + assert.Equal(t, token.Xor.String(), "^") + assert.Equal(t, token.Or.String(), "|") + assert.Equal(t, token.Greater.String(), ">") + assert.Equal(t, token.Less.String(), "<") + assert.Equal(t, token.GreaterEqual.String(), ">=") + assert.Equal(t, token.LessEqual.String(), "<=") + assert.Equal(t, token.Equal.String(), "==") + assert.Equal(t, token.NotEqual.String(), "!=") + assert.Equal(t, token.LogicalAnd.String(), "&&") + assert.Equal(t, token.LogicalOr.String(), "||") + assert.Equal(t, token.Range.String(), "..") + assert.Equal(t, token.Separator.String(), ",") + assert.Equal(t, token.Assign.String(), "=") + assert.Equal(t, token.Define.String(), ":=") + assert.Equal(t, token.AddAssign.String(), "+=") + assert.Equal(t, token.SubAssign.String(), "-=") + assert.Equal(t, token.MulAssign.String(), "*=") + assert.Equal(t, token.DivAssign.String(), "/=") + assert.Equal(t, token.ModAssign.String(), "%=") + assert.Equal(t, token.AndAssign.String(), "&=") + assert.Equal(t, token.OrAssign.String(), "|=") + assert.Equal(t, token.XorAssign.String(), "^=") + assert.Equal(t, token.ShrAssign.String(), ">>=") + assert.Equal(t, token.ShlAssign.String(), "<<=") + assert.Equal(t, token.FieldAssign.String(), ":") + assert.Equal(t, token.Invalid.String(), "") } \ No newline at end of file diff --git a/src/token/List.go b/src/token/List.go index f1d9296..1809d53 100644 --- a/src/token/List.go +++ b/src/token/List.go @@ -71,6 +71,10 @@ func (list List) Split(yield func(Position, List) bool) { // String returns the concatenated token strings. func (list List) String(source []byte) string { + if len(list) == 0 { + return "" + } + start := list[0].Position end := list[len(list)-1].End() return unsafe.String(unsafe.SliceData(source[start:end]), end-start) diff --git a/src/token/List_test.go b/src/token/List_test.go index 06889f7..26b693a 100644 --- a/src/token/List_test.go +++ b/src/token/List_test.go @@ -58,6 +58,19 @@ func TestSplitGroups(t *testing.T) { assert.DeepEqual(t, parameters, []string{"f(1,2)", "g(3,4)"}) } +func TestSplitGroupsTrail(t *testing.T) { + src := []byte("f(1,2),") + tokens := token.Tokenize(src) + tokens = tokens[:len(tokens)-1] + parameters := []string{} + + for _, param := range tokens.Split { + parameters = append(parameters, param.String(src)) + } + + assert.DeepEqual(t, parameters, []string{"f(1,2)", ""}) +} + func TestSplitSingle(t *testing.T) { src := []byte("123") tokens := token.Tokenize(src) diff --git a/src/token/Source_test.go b/src/token/Source_test.go new file mode 100644 index 0000000..5d9b4bb --- /dev/null +++ b/src/token/Source_test.go @@ -0,0 +1,14 @@ +package token_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/token" + "git.urbach.dev/go/assert" +) + +func TestSource(t *testing.T) { + data := []byte("123") + source := token.Source{StartPos: 0, EndPos: 1} + assert.Equal(t, source.StringFrom(data), "1") +} \ No newline at end of file -- 2.51.2