Add AST package: Node interface, NumberLit, BinaryExpr
- Sealed Node interface with unexported marker method
- NumberLit holds float64 value
- BinaryExpr holds Op (token.Type), Left and Right children
- Tests verify interface satisfaction, data integrity, and nesting
diff --git a/ast/ast.go b/ast/ast.go
new file mode 100644
index 0000000..f1e953d
--- /dev/null
+++ b/ast/ast.go
@@ -0,0 +1,26 @@
+package ast
+
+import "matheval/token"
+
+// Node is the interface all AST nodes implement.
+// The unexported marker method seals the interface — only types
+// in this package can implement it.
+type Node interface {
+ node() // sealed marker
+}
+
+// NumberLit represents a numeric literal (e.g. 3.14).
+type NumberLit struct {
+ Value float64
+}
+
+func (*NumberLit) node() {}
+
+// BinaryExpr represents a binary operation (e.g. 1 + 2).
+type BinaryExpr struct {
+ Op token.Type // Plus, Minus, Star, Slash
+ Left Node
+ Right Node
+}
+
+func (*BinaryExpr) node() {}
diff --git a/ast/ast_test.go b/ast/ast_test.go
new file mode 100644
index 0000000..2af413c
--- /dev/null
+++ b/ast/ast_test.go
@@ -0,0 +1,64 @@
+package ast
+
+import (
+ "matheval/token"
+ "testing"
+)
+
+// Compile-time check: both types satisfy Node.
+var (
+ _ Node = (*NumberLit)(nil)
+ _ Node = (*BinaryExpr)(nil)
+)
+
+func TestNumberLit(t *testing.T) {
+ n := &NumberLit{Value: 3.14}
+ if n.Value != 3.14 {
+ t.Fatalf("expected 3.14, got %f", n.Value)
+ }
+}
+
+func TestBinaryExpr(t *testing.T) {
+ left := &NumberLit{Value: 1}
+ right := &NumberLit{Value: 2}
+ expr := &BinaryExpr{
+ Op: token.Plus,
+ Left: left,
+ Right: right,
+ }
+
+ if expr.Op != token.Plus {
+ t.Fatalf("expected Plus, got %v", expr.Op)
+ }
+ if expr.Left != left {
+ t.Fatal("Left child mismatch")
+ }
+ if expr.Right != right {
+ t.Fatal("Right child mismatch")
+ }
+}
+
+func TestNestedBinaryExpr(t *testing.T) {
+ // Represents: (1 + 2) * 3
+ tree := &BinaryExpr{
+ Op: token.Star,
+ Left: &BinaryExpr{
+ Op: token.Plus,
+ Left: &NumberLit{Value: 1},
+ Right: &NumberLit{Value: 2},
+ },
+ Right: &NumberLit{Value: 3},
+ }
+
+ if tree.Op != token.Star {
+ t.Fatalf("expected Star, got %v", tree.Op)
+ }
+
+ inner, ok := tree.Left.(*BinaryExpr)
+ if !ok {
+ t.Fatal("Left should be *BinaryExpr")
+ }
+ if inner.Op != token.Plus {
+ t.Fatalf("expected Plus, got %v", inner.Op)
+ }
+}