From e60c74f8c3ddceebef16792ba70a78687d7f1706 Mon Sep 17 00:00:00 2001 From: giles Date: Wed, 27 May 2026 07:16:56 +0000 Subject: [PATCH] =?UTF-8?q?go:=20lex.sx=20=E2=80=94=20decimal=20float=20+?= =?UTF-8?q?=20imaginary=20literals=20+=2022=20tests=20[consumes-lex]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds Go float and imaginary literal forms per Go spec § Floating-point literals and § Imaginary literals: 3.14 .5 1. 1e10 1.5e-3 2.0e+2 1E5 (floats) 2i 3.14i 1e2i (imag) gl-read-number! returns one of "int" / "float" / "imag"; gl-finish-number! factors out the post-mantissa exponent + 'i' suffix logic so the int / float / leading-dot-float paths all share it. scan! adds a . branch ahead of the operator matcher so '.5' tokenises as float. ASI trigger list extended to include float + imag (Go spec § Semicolons: all literal types trigger). Greedy-grammar pin (a single test '1.method' lexes as float ident), since the Go spec says the '.' after a digit always belongs to the number, never to a following identifier. Hex floats (0x1.fp0) deferred — not commonly used. lex 114/114. Co-Authored-By: Claude Opus 4.7 (1M context) --- lib/go/lex.sx | 70 ++++++++++++++++++++++++++++++++++-------- lib/go/scoreboard.json | 6 ++-- lib/go/scoreboard.md | 4 +-- lib/go/tests/lex.sx | 40 +++++++++++++++++++++++- plans/go-on-sx.md | 11 +++++-- 5 files changed, 111 insertions(+), 20 deletions(-) diff --git a/lib/go/lex.sx b/lib/go/lex.sx index ca523a5c..d46ca11d 100644 --- a/lib/go/lex.sx +++ b/lib/go/lex.sx @@ -8,6 +8,8 @@ ;; "keyword" — one of the 25 Go keywords ;; "int" — integer literals (decimal, 0x.. hex, 0b.. binary, 0o.. octal, ;; legacy 0123 octal; underscores between digits allowed) +;; "float" — decimal float literals (3.14, .5, 1., 1e10, 1.5e-3, 1E5) +;; "imag" — imaginary literals (2i, 3.14i, 1e2i) ;; "string" — interpreted string literals "..." ;; "rune" — rune literals 'x' (single char + simple escapes) ;; "op" — operators & punctuation; :value is the literal text @@ -16,7 +18,7 @@ ;; ;; ASI (Go spec § Semicolons): a newline (or EOF, or a block comment ;; containing a newline) emits a ";semi" if the previous emitted token's -;; type is ident/int/string/rune, or its value is one of +;; type is ident/int/float/imag/string/rune, or its value is one of ;; {break, continue, fallthrough, return, ++, --, ), ], }}. ;; ;; All scanner locals are gl- prefixed: SX host primitives (peek/emit/etc.) @@ -57,6 +59,8 @@ (define go-asi-ops (list "++" "--" ")" "]" "}")) +(define go-asi-lit-types (list "ident" "int" "float" "imag" "string" "rune")) + (define go-asi-trigger? (fn @@ -67,10 +71,7 @@ (let ((ty (get tok :type)) (v (get tok :value))) (or - (= ty "ident") - (= ty "int") - (= ty "string") - (= ty "rune") + (some (fn (lt) (= lt ty)) go-asi-lit-types) (and (= ty "keyword") (some (fn (k) (= k v)) go-asi-keywords)) (and (= ty "op") (some (fn (o) (= o v)) go-asi-ops))))))) @@ -143,30 +144,70 @@ (and (< pos src-len) (or (digit? (gl-cur)) (= (gl-cur) "_"))) (gl-advance! 1) (gl-read-digit-run! digit?)))) + (define + gl-finish-number! + (fn + (has-fraction?) + (let + ((typ (if has-fraction? "float" "int"))) + (when + (or (= (gl-cur) "e") (= (gl-cur) "E")) + (gl-advance! 1) + (when + (or (= (gl-cur) "+") (= (gl-cur) "-")) + (gl-advance! 1)) + (gl-read-digit-run! lex-digit?) + (set! typ "float")) + (cond + (= (gl-cur) "i") + (do (gl-advance! 1) "imag") + :else typ)))) (define gl-read-number! (fn () (cond + (and (= (gl-cur) ".") (lex-digit? (gl-peek 1))) + (do + (gl-advance! 1) + (gl-read-digit-run! lex-digit?) + (gl-finish-number! true)) (and (= (gl-cur) "0") (or (= (gl-peek 1) "x") (= (gl-peek 1) "X"))) - (do (gl-advance! 2) (gl-read-digit-run! lex-hex-digit?)) + (do + (gl-advance! 2) + (gl-read-digit-run! lex-hex-digit?) + "int") (and (= (gl-cur) "0") (or (= (gl-peek 1) "b") (= (gl-peek 1) "B"))) - (do (gl-advance! 2) (gl-read-digit-run! gl-bin-digit?)) + (do + (gl-advance! 2) + (gl-read-digit-run! gl-bin-digit?) + "int") (and (= (gl-cur) "0") (or (= (gl-peek 1) "o") (= (gl-peek 1) "O"))) - (do (gl-advance! 2) (gl-read-digit-run! gl-oct-digit?)) - :else (gl-read-digit-run! lex-digit?)))) + (do + (gl-advance! 2) + (gl-read-digit-run! gl-oct-digit?) + "int") + :else (do + (gl-read-digit-run! lex-digit?) + (cond + (and (= (gl-cur) ".") (not (= (gl-peek 1) "."))) + (do + (gl-advance! 1) + (gl-read-digit-run! lex-digit?) + (gl-finish-number! true)) + :else (gl-finish-number! false)))))) (define gl-read-string! (fn @@ -371,9 +412,14 @@ (lex-digit? (gl-cur)) (do (let - ((start pos)) - (gl-read-number!) - (gl-emit! "int" (slice src start pos) start)) + ((start pos) (typ (gl-read-number!))) + (gl-emit! typ (slice src start pos) start)) + (gl-scan!)) + (and (= (gl-cur) ".") (lex-digit? (gl-peek 1))) + (do + (let + ((start pos) (typ (gl-read-number!))) + (gl-emit! typ (slice src start pos) start)) (gl-scan!)) (= (gl-cur) "\"") (let diff --git a/lib/go/scoreboard.json b/lib/go/scoreboard.json index 679a6267..b9b2ad42 100644 --- a/lib/go/scoreboard.json +++ b/lib/go/scoreboard.json @@ -1,9 +1,9 @@ { "language": "go", - "total_pass": 92, - "total": 92, + "total_pass": 114, + "total": 114, "suites": [ - {"name":"lex","pass":92,"total":92,"status":"ok"}, + {"name":"lex","pass":114,"total":114,"status":"ok"}, {"name":"parse","pass":0,"total":0,"status":"pending"}, {"name":"types","pass":0,"total":0,"status":"pending"}, {"name":"eval","pass":0,"total":0,"status":"pending"}, diff --git a/lib/go/scoreboard.md b/lib/go/scoreboard.md index 5104d7db..d16a8b55 100644 --- a/lib/go/scoreboard.md +++ b/lib/go/scoreboard.md @@ -1,10 +1,10 @@ # Go-on-SX Scoreboard -**Total: 92 / 92 tests passing** +**Total: 114 / 114 tests passing** | | Suite | Pass | Total | |---|---|---|---| -| ✅ | lex | 92 | 92 | +| ✅ | lex | 114 | 114 | | ⬜ | parse | 0 | 0 | | ⬜ | types | 0 | 0 | | ⬜ | eval | 0 | 0 | diff --git a/lib/go/tests/lex.sx b/lib/go/tests/lex.sx index be78e0c9..d4677b12 100644 --- a/lib/go/tests/lex.sx +++ b/lib/go/tests/lex.sx @@ -74,7 +74,7 @@ (go-test "int: bigger" (tok-values "123456") (list "123456" "\n" nil)) (go-test "int: type" (tok-types "42") (list "int" "semi" "eof")) -;; ── integer literals — prefixed + underscores (Go spec § Integer literals) +;; ── integer literals — prefixed + underscores ───────────────────── (go-test "int: hex lower" (tok-values "0x1f") (list "0x1f" "\n" nil)) (go-test "int: hex upper-x" (tok-values "0X1F") (list "0X1F" "\n" nil)) (go-test @@ -105,6 +105,43 @@ (tok-types "0xFF + 1") (list "int" "op" "int" "semi" "eof")) +;; ── float literals (Go spec § Floating-point literals) ──────────── +(go-test "float: simple" (tok-values "3.14") (list "3.14" "\n" nil)) +(go-test "float: trailing dot" (tok-values "1.") (list "1." "\n" nil)) +(go-test "float: leading dot" (tok-values ".5") (list ".5" "\n" nil)) +(go-test "float: exp lower" (tok-values "1e10") (list "1e10" "\n" nil)) +(go-test "float: exp upper" (tok-values "1E5") (list "1E5" "\n" nil)) +(go-test "float: exp negative" (tok-values "1.5e-3") (list "1.5e-3" "\n" nil)) +(go-test "float: exp positive" (tok-values "2.0e+2") (list "2.0e+2" "\n" nil)) +(go-test "float: zero" (tok-values "0.0") (list "0.0" "\n" nil)) +(go-test "float: dot-only-exp" (tok-values ".5e2") (list ".5e2" "\n" nil)) +(go-test "float: underscore" (tok-values "1_000.5") (list "1_000.5" "\n" nil)) +(go-test "float: type" (tok-types "3.14") (list "float" "semi" "eof")) +(go-test + "float: trailing dot type" + (tok-types "1.") + (list "float" "semi" "eof")) +(go-test + "float: exp-only type" + (tok-types "1e10") + (list "float" "semi" "eof")) +(go-test + "float: then +" + (tok-types "3.14 + 0.1") + (list "float" "op" "float" "semi" "eof")) +(go-test + "float: greedy 1.method" + (tok-types "1.method") + (list "float" "ident" "semi" "eof")) + +;; ── imaginary literals (Go spec § Imaginary literals) ───────────── +(go-test "imag: int i" (tok-values "2i") (list "2i" "\n" nil)) +(go-test "imag: float i" (tok-values "3.14i") (list "3.14i" "\n" nil)) +(go-test "imag: exp i" (tok-values "1e2i") (list "1e2i" "\n" nil)) +(go-test "imag: int-i type" (tok-types "2i") (list "imag" "semi" "eof")) +(go-test "imag: float-i type" (tok-types "3.14i") (list "imag" "semi" "eof")) +(go-test "imag: ASI at newline" (tok-types "1i\n") (list "imag" "semi" "eof")) + ;; ── string literals ─────────────────────────────────────────────── (go-test "string: empty" (tok-values "\"\"") (list "" "\n" nil)) (go-test "string: hello" (tok-values "\"hello\"") (list "hello" "\n" nil)) @@ -170,6 +207,7 @@ (tok-types "x\ny") (list "ident" "semi" "ident" "semi" "eof")) (go-test "ASI: after int" (tok-types "42\n") (list "int" "semi" "eof")) +(go-test "ASI: after float" (tok-types "3.14\n") (list "float" "semi" "eof")) (go-test "ASI: after string" (tok-types "\"hi\"\n") diff --git a/plans/go-on-sx.md b/plans/go-on-sx.md index 6d222ac8..637d3a8b 100644 --- a/plans/go-on-sx.md +++ b/plans/go-on-sx.md @@ -141,12 +141,13 @@ Progress-log line → push `origin/loops/go`. - [x] **Automatic semicolon insertion** (Go spec § Semicolons) — newline, EOF, and block-comment-with-newline trigger `;` after ident/int/string/rune/{break,continue,fallthrough,return}/{++,--,),],}}. -- [ ] Float / imaginary literals +- [x] Float / imaginary literals (decimal floats: `3.14 .5 1. 1e10 1.5e-3`; + imag: `2i 3.14i 1e2i`; hex floats `0x1.fp0` deferred) - [ ] Raw string literals `` `...` `` - [x] Hex/octal/binary integer literals (0x… 0o… 0b…) + underscores (legacy 0123 octal also accepted; consumes lex-hex-digit?) - [ ] Full operator set audit (47 distinct per Go spec) -- **Acceptance:** lex/ suite at 50+ tests. Current: 92/92. +- **Acceptance:** lex/ suite at 50+ tests. Current: 114/114. ### Phase 2 — Parser (`lib/go/parse.sx`) ⬜ - Consume `lib/guest/core/pratt.sx` + `lib/guest/core/ast.sx`. Chisel notes @@ -405,6 +406,12 @@ _(none yet)_ _Newest first. Append one dated entry per commit._ +- 2026-05-27 — Phase 1 cont.: decimal float + imaginary literals. + `3.14`, `.5`, `1.`, `1e10`, `1.5e-3`, `2i`, `3.14i`. `gl-finish-number!` + handles exponent + `i` suffix; `gl-read-number!` returns the type + string (int/float/imag). ASI trigger list extended to float/imag. + Greedy-grammar pin: `1.method` lexes as `float ident`. Hex floats + (`0x1.fp0`) deferred. +22 tests, lex 114/114. `[consumes-lex]`. - 2026-05-27 — Phase 1 cont.: prefixed integer literals (`0x..`, `0X..`, `0b..`, `0B..`, `0o..`, `0O..`, legacy `0123`) + underscore separators in any digit run. Dispatch in `gl-read-number!`; consumes