spec/ebnf: macroexpand order, set!, letfn primitive, numeric tower
Bring the formal definition in line with this session's language work: - grammar.ebnf: numbers are a real tower (exact integer / Ratio / double); the M suffix reads a real BigDecimal, N an exact integer (drop the stale Janet note). - 02-reader S5: M is a real java.math.BigDecimal with scale-insensitive equality. - 03-special-forms: document the read -> macroexpand -> analyze order (macros expand before special-form dispatch); special-form heads are not shadowable but macros are and value-position locals may be named like a special; set! on a var sets the innermost binding (else root); letfn is a primitive with letrec* semantics.
This commit is contained in:
parent
212cd0399a
commit
b9ab750983
3 changed files with 25 additions and 10 deletions
|
|
@ -50,11 +50,12 @@ collection = list | vector | map ;
|
||||||
nil = "nil" ;
|
nil = "nil" ;
|
||||||
boolean = "true" | "false" ;
|
boolean = "true" | "false" ;
|
||||||
|
|
||||||
(* Numbers. Jolt accepts Clojure's numeric literal syntaxes, but — since Jolt
|
(* Numbers. Jolt carries a real numeric tower (JVM parity): an integer literal
|
||||||
numbers are Janet ints/doubles — it has no distinct bignum, ratio or
|
reads as an exact integer (arbitrary precision), a ratio a/b as an exact
|
||||||
BigDecimal types: the BigInt suffix N and BigDecimal suffix M are read as the
|
Ratio, a decimal/exponent literal as a double. The BigDecimal suffix M reads
|
||||||
plain number, a ratio a/b is read as its double quotient, and radixed
|
as a real BigDecimal (unscaled x 10^-scale) — 1.5M, 0.0M, 3M; class is
|
||||||
integers are computed by base. The symbolic floats ##Inf/##-Inf/##NaN are
|
java.math.BigDecimal. The BigInt suffix N reads as an exact integer. Radixed
|
||||||
|
integers are computed by base; the symbolic floats ##Inf/##-Inf/##NaN are
|
||||||
also read. (No octal-with-leading-0 literal.) *)
|
also read. (No octal-with-leading-0 literal.) *)
|
||||||
number = symbolic-value
|
number = symbolic-value
|
||||||
| [ sign ] , ( radix-int | ratio | hex-int | decimal ) ;
|
| [ sign ] , ( radix-int | ratio | hex-int | decimal ) ;
|
||||||
|
|
@ -62,10 +63,10 @@ sign = "+" | "-" ;
|
||||||
integer = digit , { digit } ;
|
integer = digit , { digit } ;
|
||||||
hex-int = "0" , ( "x" | "X" ) , hex-digit , { hex-digit } , [ "N" ] ;
|
hex-int = "0" , ( "x" | "X" ) , hex-digit , { hex-digit } , [ "N" ] ;
|
||||||
radix-int = integer , ( "r" | "R" ) , alnum , { alnum } ; (* base 2..36: 2r1010, 16rFF, 36rZ *)
|
radix-int = integer , ( "r" | "R" ) , alnum , { alnum } ; (* base 2..36: 2r1010, 16rFF, 36rZ *)
|
||||||
ratio = integer , "/" , integer ; (* read as a double quotient *)
|
ratio = integer , "/" , integer ; (* exact Ratio *)
|
||||||
decimal = integer , [ "." , digit , { digit } ] , [ exponent ] , [ num-suffix ] ;
|
decimal = integer , [ "." , digit , { digit } ] , [ exponent ] , [ num-suffix ] ;
|
||||||
exponent = ( "e" | "E" ) , [ sign ] , digit , { digit } ;
|
exponent = ( "e" | "E" ) , [ sign ] , digit , { digit } ;
|
||||||
num-suffix = "N" | "M" ; (* BigInt / BigDecimal in Clojure; plain number in Jolt *)
|
num-suffix = "N" | "M" ; (* N = exact integer (BigInt); M = BigDecimal *)
|
||||||
symbolic-value = "##Inf" | "##-Inf" | "##NaN" ;
|
symbolic-value = "##Inf" | "##-Inf" | "##NaN" ;
|
||||||
digit = "0".."9" ;
|
digit = "0".."9" ;
|
||||||
hex-digit = digit | "a".."f" | "A".."F" ;
|
hex-digit = digit | "a".."f" | "A".."F" ;
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,10 @@ exponent := [eE] ['+'|'-'] digits
|
||||||
- S5. Trailing `N` (BigInt) and `M` (BigDecimal) suffixes are part of the
|
- S5. Trailing `N` (BigInt) and `M` (BigDecimal) suffixes are part of the
|
||||||
grammar; their value semantics are the §4 numeric-tower question.
|
grammar; their value semantics are the §4 numeric-tower question.
|
||||||
Implementations without those towers SHOULD read them as the nearest
|
Implementations without those towers SHOULD read them as the nearest
|
||||||
numeric type and MUST document the choice.
|
numeric type and MUST document the choice. The Chez host carries the full
|
||||||
|
tower: `N` reads as an exact integer (arbitrary precision) and `M` as a real
|
||||||
|
BigDecimal — `1.5M`, `0.0M`, `3M` — with value equality ignoring scale
|
||||||
|
(`1.0M = 1.00M`), `(class 1.5M)` ⇒ `java.math.BigDecimal`, and `decimal?` true.
|
||||||
|
|
||||||
### Symbols and keywords
|
### Symbols and keywords
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,17 @@ syntax is specified here, their behavior is host-defined.
|
||||||
|
|
||||||
Special-form head symbols are not shadowable: a binding named `if` does not
|
Special-form head symbols are not shadowable: a binding named `if` does not
|
||||||
change the meaning of `(if ...)` in operator position. ⚠ This matches the
|
change the meaning of `(if ...)` in operator position. ⚠ This matches the
|
||||||
reference; it differs from Scheme.
|
reference; it differs from Scheme. A local *may* legally be named like a special
|
||||||
|
form and used in value position (`(let [if 5] if)` ⇒ `5`); only operator
|
||||||
|
position is reserved. **Macros, unlike special forms, ARE shadowable** by a local
|
||||||
|
(`(let [when (fn ...)] (when 1 2))` calls the local).
|
||||||
|
|
||||||
|
A list form in operator position is resolved in this order (the canonical
|
||||||
|
read → **macroexpand** → analyze pipeline): a local binding shadows everything;
|
||||||
|
otherwise a macro head is expanded and the result re-analyzed; otherwise a
|
||||||
|
special-form head is parsed by rule; otherwise the form is a function
|
||||||
|
application. Macroexpansion therefore happens *before* special-form dispatch, so
|
||||||
|
a macro is never mistaken for a special form (and vice versa).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -135,10 +145,11 @@ S1–S3, E1 → jolt `forms-spec` let group; clojure-test-suite
|
||||||
| `do` | empty `(do)` → nil; top-level `do` splices for compilation units (important and under-documented) |
|
| `do` | empty `(do)` → nil; top-level `do` splices for compilation units (important and under-documented) |
|
||||||
| `fn*` | arities, variadic `&`, closure capture, self-name, simple-symbol params only, recur target |
|
| `fn*` | arities, variadic `&`, closure capture, self-name, simple-symbol params only, recur target |
|
||||||
| `loop*` | recur arity must match bindings; recur rebinds in place |
|
| `loop*` | recur arity must match bindings; recur rebinds in place |
|
||||||
|
| `letfn` | mutually-recursive local fns (`letrec*` semantics — a fn body sees every binding, not only earlier ones). jolt treats `letfn` as a primitive special, not the reference's `letfn` macro → `letfn*` indirection; behavior is identical |
|
||||||
| `recur` | tail-position rule (normative definition of tail position needed), across `if`/`do`/`let*`/`try` interactions |
|
| `recur` | tail-position rule (normative definition of tail position needed), across `if`/`do`/`let*`/`try` interactions |
|
||||||
| `quote` | self-evaluation table: which literals are self-evaluating unquoted |
|
| `quote` | self-evaluation table: which literals are self-evaluating unquoted |
|
||||||
| `var` | `#'` reader sugar; resolution at compile time |
|
| `var` | `#'` reader sugar; resolution at compile time |
|
||||||
| `throw` | any value vs Throwable — host question; jolt/cljs allow data, reference requires Throwable → classification needed |
|
| `throw` | any value vs Throwable — host question; jolt/cljs allow data, reference requires Throwable → classification needed |
|
||||||
| `try/catch/finally` | catch dispatch order, `:default`-style catch-all is a dialect extension (⚠ divergence note), finally evaluation guarantees, value of try |
|
| `try/catch/finally` | catch dispatch order, `:default`-style catch-all is a dialect extension (⚠ divergence note), finally evaluation guarantees, value of try |
|
||||||
| `set!` | host-dependent (dynamic vars + host fields) |
|
| `set!` | `(set! *var* val)` sets the var's innermost thread binding, else its root, and returns val (implemented); a local (deftype mutable field) or host `(.-field obj)` target is host-dependent |
|
||||||
| `.` / `new` | syntax only; behavior host-defined |
|
| `.` / `new` | syntax only; behavior host-defined |
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue