go: lex.sx — decimal float + imaginary literals + 22 tests [consumes-lex]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 36s

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 .<digit>
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) <noreply@anthropic.com>
This commit is contained in:
2026-05-27 07:16:56 +00:00
parent fe614fc531
commit e60c74f8c3
5 changed files with 111 additions and 20 deletions

View File

@@ -8,6 +8,8 @@
;; "keyword" — one of the 25 Go keywords ;; "keyword" — one of the 25 Go keywords
;; "int" — integer literals (decimal, 0x.. hex, 0b.. binary, 0o.. octal, ;; "int" — integer literals (decimal, 0x.. hex, 0b.. binary, 0o.. octal,
;; legacy 0123 octal; underscores between digits allowed) ;; 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 "..." ;; "string" — interpreted string literals "..."
;; "rune" — rune literals 'x' (single char + simple escapes) ;; "rune" — rune literals 'x' (single char + simple escapes)
;; "op" — operators & punctuation; :value is the literal text ;; "op" — operators & punctuation; :value is the literal text
@@ -16,7 +18,7 @@
;; ;;
;; ASI (Go spec § Semicolons): a newline (or EOF, or a block comment ;; ASI (Go spec § Semicolons): a newline (or EOF, or a block comment
;; containing a newline) emits a ";semi" if the previous emitted token's ;; 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, ++, --, ), ], }}. ;; {break, continue, fallthrough, return, ++, --, ), ], }}.
;; ;;
;; All scanner locals are gl- prefixed: SX host primitives (peek/emit/etc.) ;; All scanner locals are gl- prefixed: SX host primitives (peek/emit/etc.)
@@ -57,6 +59,8 @@
(define go-asi-ops (list "++" "--" ")" "]" "}")) (define go-asi-ops (list "++" "--" ")" "]" "}"))
(define go-asi-lit-types (list "ident" "int" "float" "imag" "string" "rune"))
(define (define
go-asi-trigger? go-asi-trigger?
(fn (fn
@@ -67,10 +71,7 @@
(let (let
((ty (get tok :type)) (v (get tok :value))) ((ty (get tok :type)) (v (get tok :value)))
(or (or
(= ty "ident") (some (fn (lt) (= lt ty)) go-asi-lit-types)
(= ty "int")
(= ty "string")
(= ty "rune")
(and (= ty "keyword") (some (fn (k) (= k v)) go-asi-keywords)) (and (= ty "keyword") (some (fn (k) (= k v)) go-asi-keywords))
(and (= ty "op") (some (fn (o) (= o v)) go-asi-ops))))))) (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) "_"))) (and (< pos src-len) (or (digit? (gl-cur)) (= (gl-cur) "_")))
(gl-advance! 1) (gl-advance! 1)
(gl-read-digit-run! digit?)))) (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 (define
gl-read-number! gl-read-number!
(fn (fn
() ()
(cond (cond
(and (= (gl-cur) ".") (lex-digit? (gl-peek 1)))
(do
(gl-advance! 1)
(gl-read-digit-run! lex-digit?)
(gl-finish-number! true))
(and (and
(= (gl-cur) "0") (= (gl-cur) "0")
(or (or
(= (gl-peek 1) "x") (= (gl-peek 1) "x")
(= (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 (and
(= (gl-cur) "0") (= (gl-cur) "0")
(or (or
(= (gl-peek 1) "b") (= (gl-peek 1) "b")
(= (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 (and
(= (gl-cur) "0") (= (gl-cur) "0")
(or (or
(= (gl-peek 1) "o") (= (gl-peek 1) "o")
(= (gl-peek 1) "O"))) (= (gl-peek 1) "O")))
(do (gl-advance! 2) (gl-read-digit-run! gl-oct-digit?)) (do
:else (gl-read-digit-run! lex-digit?)))) (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 (define
gl-read-string! gl-read-string!
(fn (fn
@@ -371,9 +412,14 @@
(lex-digit? (gl-cur)) (lex-digit? (gl-cur))
(do (do
(let (let
((start pos)) ((start pos) (typ (gl-read-number!)))
(gl-read-number!) (gl-emit! typ (slice src start pos) start))
(gl-emit! "int" (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-scan!))
(= (gl-cur) "\"") (= (gl-cur) "\"")
(let (let

View File

@@ -1,9 +1,9 @@
{ {
"language": "go", "language": "go",
"total_pass": 92, "total_pass": 114,
"total": 92, "total": 114,
"suites": [ "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":"parse","pass":0,"total":0,"status":"pending"},
{"name":"types","pass":0,"total":0,"status":"pending"}, {"name":"types","pass":0,"total":0,"status":"pending"},
{"name":"eval","pass":0,"total":0,"status":"pending"}, {"name":"eval","pass":0,"total":0,"status":"pending"},

View File

@@ -1,10 +1,10 @@
# Go-on-SX Scoreboard # Go-on-SX Scoreboard
**Total: 92 / 92 tests passing** **Total: 114 / 114 tests passing**
| | Suite | Pass | Total | | | Suite | Pass | Total |
|---|---|---|---| |---|---|---|---|
| ✅ | lex | 92 | 92 | | ✅ | lex | 114 | 114 |
| ⬜ | parse | 0 | 0 | | ⬜ | parse | 0 | 0 |
| ⬜ | types | 0 | 0 | | ⬜ | types | 0 | 0 |
| ⬜ | eval | 0 | 0 | | ⬜ | eval | 0 | 0 |

View File

@@ -74,7 +74,7 @@
(go-test "int: bigger" (tok-values "123456") (list "123456" "\n" nil)) (go-test "int: bigger" (tok-values "123456") (list "123456" "\n" nil))
(go-test "int: type" (tok-types "42") (list "int" "semi" "eof")) (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 lower" (tok-values "0x1f") (list "0x1f" "\n" nil))
(go-test "int: hex upper-x" (tok-values "0X1F") (list "0X1F" "\n" nil)) (go-test "int: hex upper-x" (tok-values "0X1F") (list "0X1F" "\n" nil))
(go-test (go-test
@@ -105,6 +105,43 @@
(tok-types "0xFF + 1") (tok-types "0xFF + 1")
(list "int" "op" "int" "semi" "eof")) (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 ─────────────────────────────────────────────── ;; ── string literals ───────────────────────────────────────────────
(go-test "string: empty" (tok-values "\"\"") (list "" "\n" nil)) (go-test "string: empty" (tok-values "\"\"") (list "" "\n" nil))
(go-test "string: hello" (tok-values "\"hello\"") (list "hello" "\n" nil)) (go-test "string: hello" (tok-values "\"hello\"") (list "hello" "\n" nil))
@@ -170,6 +207,7 @@
(tok-types "x\ny") (tok-types "x\ny")
(list "ident" "semi" "ident" "semi" "eof")) (list "ident" "semi" "ident" "semi" "eof"))
(go-test "ASI: after int" (tok-types "42\n") (list "int" "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 (go-test
"ASI: after string" "ASI: after string"
(tok-types "\"hi\"\n") (tok-types "\"hi\"\n")

View File

@@ -141,12 +141,13 @@ Progress-log line → push `origin/loops/go`.
- [x] **Automatic semicolon insertion** (Go spec § Semicolons) — newline, - [x] **Automatic semicolon insertion** (Go spec § Semicolons) — newline,
EOF, and block-comment-with-newline trigger `;` after EOF, and block-comment-with-newline trigger `;` after
ident/int/string/rune/{break,continue,fallthrough,return}/{++,--,),],}}. 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 `` `...` `` - [ ] Raw string literals `` `...` ``
- [x] Hex/octal/binary integer literals (0x… 0o… 0b…) + underscores - [x] Hex/octal/binary integer literals (0x… 0o… 0b…) + underscores
(legacy 0123 octal also accepted; consumes lex-hex-digit?) (legacy 0123 octal also accepted; consumes lex-hex-digit?)
- [ ] Full operator set audit (47 distinct per Go spec) - [ ] 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`) ⬜ ### Phase 2 — Parser (`lib/go/parse.sx`) ⬜
- Consume `lib/guest/core/pratt.sx` + `lib/guest/core/ast.sx`. Chisel notes - 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._ _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..`, - 2026-05-27 — Phase 1 cont.: prefixed integer literals (`0x..`, `0X..`,
`0b..`, `0B..`, `0o..`, `0O..`, legacy `0123`) + underscore separators `0b..`, `0B..`, `0o..`, `0O..`, legacy `0123`) + underscore separators
in any digit run. Dispatch in `gl-read-number!`; consumes in any digit run. Dispatch in `gl-read-number!`; consumes