diff --git a/lib/go/parse.sx b/lib/go/parse.sx index 6ca89765..db19823a 100644 --- a/lib/go/parse.sx +++ b/lib/go/parse.sx @@ -64,11 +64,32 @@ (= ty "ident") (do (gp-advance!) (ast-var v)) :else nil)))) + (define + gp-unary-ops + ;; Go spec § Operators: prefix unary, all higher precedence than + ;; any binary operator. <- is the channel receive form (send is a + ;; statement, not an expression, so never appears here as binary). + (list "+" "-" "!" "^" "*" "&" "<-")) + (define + gp-parse-unary + (fn + () + (let ((tok (gp-cur))) + (cond + (and (= (get tok :type) "op") + (some (fn (u) (= u (get tok :value))) gp-unary-ops)) + (do + (gp-advance!) + (let ((operand (gp-parse-unary))) + (cond + (= operand nil) nil + :else (ast-app (ast-var (get tok :value)) (list operand))))) + :else (gp-parse-primary))))) (define gp-parse-expr (fn (min-prec) - (let ((left (gp-parse-primary))) (gp-pratt-loop left min-prec)))) + (let ((left (gp-parse-unary))) (gp-pratt-loop left min-prec)))) (define gp-pratt-loop (fn diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index c871ab09..2bd75d6d 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,10 +1,10 @@ { "language": "go", - "total_pass": 155, - "total": 155, + "total_pass": 166, + "total": 166, "suites": [ {"name":"lex","pass":129,"total":129,"status":"ok"}, - {"name":"parse","pass":26,"total":26,"status":"ok"}, + {"name":"parse","pass":37,"total":37,"status":"ok"}, {"name":"types","pass":0,"total":0,"status":"pending"}, {"name":"eval","pass":0,"total":0,"status":"pending"}, {"name":"runtime","pass":0,"total":0,"status":"pending"}, diff --git a/lib/go/scoreboard.md b/lib/go/scoreboard.md index 6b526bbb..cd6f620f 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,11 +1,11 @@ # Go-on-SX Scoreboard -**Total: 155 / 155 tests passing** +**Total: 166 / 166 tests passing** | | Suite | Pass | Total | |---|---|---|---| | ✅ | lex | 129 | 129 | -| ✅ | parse | 26 | 26 | +| ✅ | parse | 37 | 37 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | | ⬜ | runtime | 0 | 0 | diff --git a/lib/go/tests/parse.sx b/lib/go/tests/parse.sx index 052074c5..17cc99c4 100644 --- a/lib/go/tests/parse.sx +++ b/lib/go/tests/parse.sx @@ -111,6 +111,67 @@ (ast-app (ast-var "|") (list (ast-var "a") (ast-var "b"))) (ast-var "c")))) +(go-parse-test + "unary: -a" + (go-parse "-a") + (ast-app (ast-var "-") (list (ast-var "a")))) + +(go-parse-test + "unary: +a" + (go-parse "+a") + (ast-app (ast-var "+") (list (ast-var "a")))) + +(go-parse-test + "unary: !x" + (go-parse "!x") + (ast-app (ast-var "!") (list (ast-var "x")))) + +(go-parse-test + "unary: ^x (bitwise NOT)" + (go-parse "^x") + (ast-app (ast-var "^") (list (ast-var "x")))) + +(go-parse-test + "unary: *p (pointer deref)" + (go-parse "*p") + (ast-app (ast-var "*") (list (ast-var "p")))) + +(go-parse-test + "unary: &v (address-of)" + (go-parse "&v") + (ast-app (ast-var "&") (list (ast-var "v")))) + +(go-parse-test + "unary: <-ch (channel recv)" + (go-parse "<-ch") + (ast-app (ast-var "<-") (list (ast-var "ch")))) + +(go-parse-test + "unary: -1 (on literal)" + (go-parse "-1") + (ast-app (ast-var "-") (list (ast-literal "1")))) + +(go-parse-test + "unary: !!x (chained, right-recursive)" + (go-parse "!!x") + (ast-app + (ast-var "!") + (list (ast-app (ast-var "!") (list (ast-var "x")))))) + +(go-parse-test + "unary: -a + b → ((-a) + b) — unary tighter than binary" + (go-parse "-a + b") + (ast-app + (ast-var "+") + (list (ast-app (ast-var "-") (list (ast-var "a"))) (ast-var "b")))) + +(go-parse-test + "unary: a + -b → unary applies to RHS" + (go-parse "a + -b") + (ast-app + (ast-var "+") + (list (ast-var "a") (ast-app (ast-var "-") (list (ast-var "b")))))) + (go-parse-test "non-primary: '+'" (go-parse "+") nil) (go-parse-test "non-primary: empty" (go-parse "") nil) diff --git a/plans/go-on-sx.md b/plans/go-on-sx.md index bd7a8e44..5d7215b5 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -160,7 +160,9 @@ Progress-log line → push `origin/loops/go`. - [x] Binary operators (Pratt precedence climbing using `pratt-op-lookup` + Go precedence table). Operator app emitted as `(ast-app (ast-var OP) [LHS RHS])`; left-assoc raises right-min by 1. -- [ ] Unary operators (`!x`, `-x`, `^x`, `*p`, `&v`, `<-ch`). +- [x] Unary operators (`+x`, `-x`, `!x`, `^x`, `*p`, `&v`, `<-ch`). + `gp-parse-unary` recursive, sits between `gp-parse-expr` and + `gp-parse-primary`; right-associative chains (`!!x`). - [ ] Function calls `f(a, b)` and member access `x.field`. - [ ] Index `x[i]` and slice `x[a:b]`/`x[a:b:c]`. - [ ] Type assertion `v.(T)`. @@ -176,7 +178,7 @@ Progress-log line → push `origin/loops/go`. assign, short-decl `:=`, send `ch <- v`, recv `<-ch`. - [ ] End-to-end: hello-world, fibonacci, FizzBuzz, goroutine ping-pong, struct + method. -- **Acceptance:** parse/ suite at 80+ tests. Current: 26/26. +- **Acceptance:** parse/ suite at 80+ tests. Current: 37/37. ### Phase 3 — Bidirectional type checker, MVP (`lib/go/types.sx`) ⬜ - **Independent implementation.** Do NOT use lib/guest/static-types- @@ -435,6 +437,13 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`. _Newest first. Append one dated entry per commit._ +- 2026-05-27 — Phase 2 cont.: unary prefix operators (`+`, `-`, `!`, `^`, + `*`, `&`, `<-`). `gp-parse-unary` is recursive (`!!x`) and sits between + `gp-parse-expr` and `gp-parse-primary` so unary always binds tighter + than any binary. Symbols `+ - * & ^` are shared with binary; the + positional split (expression-start vs mid-expression) disambiguates + cleanly without lookback. Unary nodes are single-arg `ast-app`. +11 + tests, parse 37/37, total 166/166. `[nothing]` — pure Go parser work. - 2026-05-27 — Phase 2 cont.: binary operators via Pratt precedence climbing. `gp-pratt-loop` consumes `pratt-op-lookup` against `go-precedence-table`; left-assoc bumps right-min by 1, right-assoc