feat(struct): add StructDef and dot-access MVP#41
Conversation
Move reserved type-name source to a shared types package and use it from parser + compiler validations. Also reject reserved function names (e.g. Int/I64) while keeping such identifiers valid for variables.
…ecks Introduce StructStatement as a first-class code statement and wire it through parser/codecompiler/compiler flows. Track immutable names via Code.ConstNames for shared redeclaration and CFG write-protection checks. Allow repeated struct type definitions when headers match, and reject only conflicting headers across parse and merged compile validation. Refactor codeparser const-binding validation to operate on identifier lists directly (remove temporary ConstStatement wrapper), and align struct-header comparison helper naming. Update parser/compiler tests for repeated vs conflicting struct definitions and struct statement expectations.
Update StructLiteral.String to print multiline output with indented header and row blocks. Add parser assertion for struct statement string rendering. Validated with go test -race ./parser ./compiler and go test ./...
Remove the defensive nil check when emitting struct row expressions in StructLiteral.String. Validated with go test -race ./parser ./compiler and go test ./...
Use singular Row for Phase 1 struct literals to reflect single-instance semantics and reduce ambiguity with future struct arrays. Update parser/compiler/solver/cfg references and struct parser tests accordingly. Validated with go test -race ./parser ./compiler and go test ./...
Remove trivial isGlobalConst helper and perform ConstNames lookup directly inside isDefined. No behavior change; validated with go test ./compiler.
Guard unknown-header validation when the first-seen struct entry has empty headers. Remove dead seenHeaders tracking in codecompiler and drop unreachable lexer dot-number branch. Add parser regression test for empty init before full struct definition.
Rename validateAndTrackStructHeaders to validateStructHeaders for readability. No behavior changes.
Redesign struct validation to use a clean two-pass architecture:
- Pass 1: find canonical definition (max-header statement, first-seen wins)
- Pass 2: validate all statements against canonical def (field existence + order)
Key changes:
- Replace structDef with Struct directly, eliminating duplicate type
- Add FieldSet (map[string]struct{}) to Struct for O(1) field lookups
- Move struct field validation from parser to compiler (semantic concern)
- Remove validateFuncDefs (parser owns reserved-name validation)
- Separate checkFieldOrder from validateStructUsage for clarity
- Fix equal-width subset ordering bug (defer conflict checks to pass 2)
- Simplify getStructSchema to pure StructCache lookup
- Use zero values for missing fields instead of definition defaults
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract struct constant compilation into compileStructConst method - Remove tautological onElse != nil check in bounds.go - Use early continue to flatten if/else in struct field loop - Run gofmt Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move all reserved-name validation from parser to compiler, following the production compiler pattern where the parser is purely syntactic and semantic checks live in the compiler phase. - Move `reservedTypeNames` map and `IsReservedTypeName` from `types/` package into `compiler/types.go`; delete `types/` package - Add `Compiler.rejectReservedName` method for unified error reporting - Add `CodeCompiler.validateReservedNames` covering constants, struct type names, struct binding names, and function names - Add `ScriptCompiler.validateReservedNames` covering script variables - Add `Struct.FieldIndex` helper; reorder field-order check before field-type check in `validateStructHeaders` - Use `prior := len(c.Errors)` pattern in `validateStructDefs` to avoid false positives from earlier validation errors - Add unit tests for all reserved-name branches (const, func, struct binding, struct type, script variable) - Remove reserved-name checks and tests from parser Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Code Review:
|
…, and dot-spacing - checkFieldTypes: allow int→float promotion to match codegen widening - demangleIdent: restrict _ consumption to n-prefix case only, preventing greedy merging of adjacent nominal types (e.g., PersonAnimal) - demangleNominalType: delegate to demangleIdent for full mixed-identifier support (ASCII + Unicode segments like foo_π, πbar) - parseDotPostfix: reject whitespace after '.' in field access (p. name) - Extract demangleIdentSegment to reduce cognitive complexity Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- TypeStructLiteral: initialize FieldSet to match buildStructDef, preventing potential nil-map panic if future code reads FieldSet on solver-derived types - validateStructStmt: emit "struct X redefined with different fields" when same-arity definitions have unknown fields, instead of misleading per-field errors - checkFieldOrder: clarify message to "fields reordered ... all fields must match definition order" - Code.Merge: document last-writer-wins behavior on Struct.Map Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove dead empty-fields guard and switch to compact space-separated
format (e.g. Point{x:I64 y:F64}) consistent with Array.String().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Structs can now be printed directly with `p` or as format markers
("-p", "x=-p y=99"). Output format:
Person
:name age height
Tejas 35 184.5
The struct block ends with \n so it naturally separates from
following values in mixed prints (p, 99) without needing extra
printf calls or format-string splitting.
Changes:
- compiler/format.go: structFormatArgs builds the multi-line format
string (Name\n :fields\n values\n); parseFormatting routes
StructKind to structFormatArgs; defaultSpecifier returns %s for structs
- compiler/compiler.go: appendPrintSymbol handles StructKind by
appending the struct format (no spec+space, no flush needed)
- tests/struct: add print test cases and expected output
- parser/scriptparser_test.go: extend dot-expression whitespace tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use FLOAT directly for dot-prefixed numeric literals. This keeps bare '.' as PERIOD while making the token type match the only valid outcome for inputs like '.5'.
Reject comma-separated struct value rows with an explicit parser error and simplify the row parser now that commas are no longer accepted. Also reuse a shared line-continuation helper across struct and array header/row parsing.
Inline struct literal construction and remove redundant newline/indent expectPeek checks in parseStructLiteralStatement while preserving the existing bare-definition cases.
Summary
StructDefsupport in.ptusing header-row + single value row syntax.sptdot field access (p.name) with parser/type-solver/compiler supportextractvalueCode.Struct(parallel toCode.ConstandCode.Func)Struct_namespace prefix)Syntax covered
p = Person :name age height "Tejas" 35 184.5Implementation notes
.tokenization with.5float compatibilityStructLiteral,DotExpression,StructDefcontainerTests
go test ./...python3 test.py tests/structpython3 test.py