Draft language v0.2: arrays, main(args), string conversions, f-strings - #22
Draft language v0.2: arrays, main(args), string conversions, f-strings#22yunabe wants to merge 2 commits into
Conversation
Rewrites LANGUAGE_SPEC.md as the version 0.2 draft (the v0.1
specification is preserved at the v0.1.0 tag; a status note marks the
draft as unimplemented). The scope is the candidate list accumulated
during v0.1 development:
- Array types []T over primitive elements: fixed length, reference
semantics, always non-null; creation via element lists and the
[value; count] repeat form (empty literals need an expected type);
zero-based i64 indexing with runtime bounds errors; element
assignment including compound forms; no array operators, equality,
or printing in this version.
- for over arrays, sharing the range loop's variable rules.
- fn main(args: []string) receiving all arguments verbatim, alongside
the typed-parameter form.
- string-to-primitive conversions using exactly the main-argument
grammar, failing with a runtime error, making string(x)/i64(s)
inverses for integers.
- Interpolated strings: f-prefixed, ${expression} holes only, defined
by desugaring to string(...) concatenation; \$ escapes a dollar and
a bare $ is reserved (a compile-time error).
- len as a second compiler builtin (arrays only, expression-valued).
- Section 13 pruned to what remains outside 0.2, gaining nested
arrays, string length/indexing, $name shorthand, and non-fatal
string parsing.
README points at the tagged v0.1 spec and marks the draft; CHANGELOG
gains an Unreleased entry. Specification only - no compiler changes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review pushback: the nesting restriction saved almost nothing. The draft grammar already parsed chained indexing and nested literals; the real implementation delta is an interned recursive array type and a TypeSpec row for array-of-array tokens, while the element-kind dispatch in the emitter is needed for []string regardless. Excluding nesting bought ~35 lines at the cost of a non-orthogonal special case. Array types now compose ([][]i64 is a jagged .NET long[][]; types are structural), indexing and assignment targets chain (grid[i][j]), and the repeat form's reference-sharing consequence is spelled out with the [[0; 3]; 2] example and the fill-in-a-loop idiom. Rectangular multi-dimensional arrays move to section 13. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| Element-list form `[e1, e2, …, en]`: | ||
|
|
||
| - The elements are evaluated left to right. | ||
| - When an expected array type `[]T` is available (Section 7), every element takes `T` as its expected type and must have type `T`. |
There was a problem hiding this comment.
The expected-type model is incomplete here. This rule makes let names: []string = [] valid, but Section 7 still says that an expected type applies only to a literal of the same numeric category. The repeat form also never says whether an expected []T is propagated to value, so a common case such as let xs: []i32 = [0; n] is currently undefined (or would infer []i64). Please update Section 7 to include array creation expressions and explicitly define contextual typing for the repeat form, including recursive propagation for nested arrays.
| - A `string` converts to itself. | ||
|
|
||
| Converting a `string` to another type is not supported in version 0.1. | ||
| A `string` may be converted to any other primitive type. The text is parsed with exactly the grammar of a `main` argument of that type (Section 5.1): culture-invariant, optional sign and exponent for numerics, finite values only, case-insensitive `true`/`false` for `bool`, surrounding white space ignored. A string that fails to parse is a **runtime error** — consistent with the other checked conversions in this section — so `i64("12")` is `12` and `i64(string(x)) == x` holds for every integer `x`, while `i64("12.5")` and `bool("yes")` fail at runtime: |
There was a problem hiding this comment.
This sentence contains two conflicting claims. First, “optional sign and exponent for numerics” implies that an integer conversion such as i64("1e2") accepts an exponent, whereas Section 5.1 permits exponents only for floating-point arguments; consider saying “an optional sign for numerics and an optional exponent for floating-point values.” Second, i64(string(x)) == x is not well-typed for an i32 value because mixed-width numeric comparisons are prohibited. Either restrict x to i64, or state the round trip using the matching integer type.
Summary
Starts the v0.2 cycle the same way v0.1 started: specification first. LANGUAGE_SPEC.md on
mainbecomes the v0.2 draft — a status note marks it unimplemented and points at thev0.1.0tag, which preserves the released v0.1 spec+compiler pair. Scope is the candidate list we accumulated during v0.1 development; everything else stays in §13.Arrays (§3.1, §4.5, §8.4–8.6, §9.3, §10.2, §11)
[]Tover any element type — arrays compose, so[][]i64is a jagged .NETlong[][]with chained indexinggrid[i][j](revised after review: the nesting restriction was a non-orthogonal special case that saved almost nothing). Fixed length, reference semantics, never null, mutable elements; the repeat form's sharing consequence ([[0; 3]; 2]shares one inner array) is specified with the fill-in-a-loop idiom.[e1, …, en](with an expected[]T, elements force toT; without one, elements resolve independently and must agree — so[]without an expectation is a compile-time error) and the repeat form[value; count](valuethencount, each once; negative count is a runtime error).i64indexing (an expected-type position, like range endpoints) with runtime bounds errors on reads and writes; assignment targets extend toname[index] op= value.for x in arrayshares the range loop's variable rules; elements are read per iteration.len(array) -> i64as a second compiler builtin — expression-valued, unlikeprint;lenon strings deliberately stays out (§13).==/!=andprint(array)— reference-vs-content ambiguity is postponed rather than answered wrongly.fn main(args: []string)(§5.1)Receives all arguments verbatim (no program name, no parsing, no usage exit), alongside — not replacing — the typed-parameter form.
[]stringis the only array type allowed onmainand cannot be mixed with typed parameters.String conversions (§7)
i32/i64/f32/f64/bool(string)parse with exactly the §5.1 argument grammar (invariant, finite-only, case-insensitive bool) and fail with a runtime error — consistent with the section's other checked conversions, and makingi64(string(x)) == xhold for integers. Themain(args)+i64(arg)combination closes the loop that motivated typed main. A non-fataltry-style form is a §13 candidate.Interpolated strings (§4.6)
f"x = ${x}", defined entirely by desugaring: text segments concatenated withstring(hole)conversions, left to right. Per the earlier discussion:${expr}only — a bare$is a compile-time error (reserving$nameshorthand, listed in §13),\$escapes — and plain strings are untouched ($stays ordinary,\$invalid there). Holes take any primitive-typed expression, nested f-strings included.Grammar (§12)
typegains the[]prefix;postfixindexing joins precedence level 1;array-expression;assignment-target; theforrange clause becomes optional;interpolated-stringjoinsliteralwith a lexical note.Specification only — no compiler changes (
dotnet test: 421 passed / 1 skipped, untouched). README's note now distinguishes "implemented v0.1" from "drafted v0.2", and CHANGELOG gains an Unreleased entry.Points I'd flag for review as the debatable calls: reference semantics (vs. disallowing array assignment), the
[value; count]evaluation order and its nested-array sharing, banning bare$,lenrejecting strings, and keeping array equality/printing out entirely.🤖 Generated with Claude Code