From b7151a56c47e7b868934f15a4cc55a81b87a78fc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Jan 2026 15:18:55 +0000 Subject: [PATCH] feat: implement f0 scope arrest and semantic anchor framework This commit establishes the f0 (control pass) foundation per the scope arrest directive, including: ## Machine-Readable Control Files (.machine_read/) - LLM_SUPERINTENDENT.scm: AI assistant guidance and constraints - SPEC.core.scm: Minimal formal language specification - ROADMAP.f0.scm: Quarantined future work with phase definitions ## Conformance Test Suite (conformance/) - 12 valid programs testing core syntax constructs - 12 invalid programs testing error diagnostics - Placeholder .expected files for golden outputs - README documenting test methodology ## License Coherence - Consolidate LICENSE.txt into LICENSE as canonical - Update dune-project to MIT OR AGPL-3.0-or-later - Update Cargo.toml to match project license - Add/update SPDX headers in all source files: - OCaml: lib/*.ml, bin/main.ml, test/*.ml - Rust: runtime/src/*.rs ## Task Runner (justfile) - Add actual dune commands for build/test/lint/clean - Add lex/parse commands for CLI invocation - Add golden-path recipe for smoke testing - Add conformance recipe for running conformance tests This establishes the f0 baseline where OCaml/Dune implementation behavior is authoritative. Future phases (f1-f4) are quarantined in ROADMAP.f0.scm. --- .machine_read/LLM_SUPERINTENDENT.scm | 214 +++++++++++++++ .machine_read/ROADMAP.f0.scm | 216 +++++++++++++++ .machine_read/SPEC.core.scm | 250 ++++++++++++++++++ LICENSE | 74 +++++- LICENSE.txt | 91 ------- bin/main.ml | 3 + conformance/README.md | 62 +++++ conformance/invalid/001_unclosed_brace.as | 5 + .../invalid/001_unclosed_brace.expected | 8 + conformance/invalid/002_unclosed_string.as | 5 + .../invalid/002_unclosed_string.expected | 8 + conformance/invalid/003_bad_number.as | 5 + conformance/invalid/003_bad_number.expected | 8 + conformance/invalid/004_unexpected_token.as | 5 + .../invalid/004_unexpected_token.expected | 8 + conformance/invalid/005_missing_arrow.as | 5 + .../invalid/005_missing_arrow.expected | 8 + conformance/invalid/006_bad_operator.as | 5 + conformance/invalid/006_bad_operator.expected | 8 + conformance/invalid/007_incomplete_effect.as | 4 + .../invalid/007_incomplete_effect.expected | 8 + conformance/invalid/008_mismatched_parens.as | 5 + .../invalid/008_mismatched_parens.expected | 8 + conformance/invalid/009_reserved_keyword.as | 5 + .../invalid/009_reserved_keyword.expected | 8 + conformance/invalid/010_empty_match.as | 6 + conformance/invalid/010_empty_match.expected | 8 + conformance/invalid/011_bad_escape.as | 5 + conformance/invalid/011_bad_escape.expected | 8 + conformance/invalid/012_trailing_comma.as | 5 + .../invalid/012_trailing_comma.expected | 5 + conformance/valid/001_empty.as | 2 + conformance/valid/001_empty.expected | 4 + conformance/valid/002_comments.as | 11 + conformance/valid/002_comments.expected | 4 + conformance/valid/003_let_bindings.as | 9 + conformance/valid/003_let_bindings.expected | 4 + conformance/valid/004_literals.as | 26 ++ conformance/valid/004_literals.expected | 4 + conformance/valid/005_functions.as | 21 ++ conformance/valid/005_functions.expected | 4 + conformance/valid/006_type_alias.as | 7 + conformance/valid/006_type_alias.expected | 4 + conformance/valid/007_struct_def.as | 21 ++ conformance/valid/007_struct_def.expected | 4 + conformance/valid/008_enum_def.as | 19 ++ conformance/valid/008_enum_def.expected | 4 + conformance/valid/009_effect_def.as | 21 ++ conformance/valid/009_effect_def.expected | 4 + conformance/valid/010_pattern_match.as | 27 ++ conformance/valid/010_pattern_match.expected | 4 + conformance/valid/011_rows.as | 16 ++ conformance/valid/011_rows.expected | 4 + conformance/valid/012_ownership.as | 20 ++ conformance/valid/012_ownership.expected | 4 + dune-project | 2 +- justfile | 57 +++- lib/ast.ml | 3 + lib/borrow.ml | 4 +- lib/error.ml | 3 + lib/lexer.ml | 3 + lib/parse_driver.ml | 3 + lib/quantity.ml | 4 +- lib/resolve.ml | 4 +- lib/span.ml | 3 + lib/symbol.ml | 4 +- lib/token.ml | 3 + lib/typecheck.ml | 4 +- lib/types.ml | 4 +- lib/unify.ml | 4 +- runtime/Cargo.toml | 7 +- runtime/src/alloc.rs | 4 +- runtime/src/effects.rs | 4 +- runtime/src/ffi.rs | 4 +- runtime/src/gc.rs | 4 +- runtime/src/lib.rs | 4 +- runtime/src/panic.rs | 4 +- test/test_golden.ml | 3 + test/test_lexer.ml | 3 + test/test_parser.ml | 3 + 80 files changed, 1320 insertions(+), 136 deletions(-) create mode 100644 .machine_read/LLM_SUPERINTENDENT.scm create mode 100644 .machine_read/ROADMAP.f0.scm create mode 100644 .machine_read/SPEC.core.scm delete mode 100644 LICENSE.txt create mode 100644 conformance/README.md create mode 100644 conformance/invalid/001_unclosed_brace.as create mode 100644 conformance/invalid/001_unclosed_brace.expected create mode 100644 conformance/invalid/002_unclosed_string.as create mode 100644 conformance/invalid/002_unclosed_string.expected create mode 100644 conformance/invalid/003_bad_number.as create mode 100644 conformance/invalid/003_bad_number.expected create mode 100644 conformance/invalid/004_unexpected_token.as create mode 100644 conformance/invalid/004_unexpected_token.expected create mode 100644 conformance/invalid/005_missing_arrow.as create mode 100644 conformance/invalid/005_missing_arrow.expected create mode 100644 conformance/invalid/006_bad_operator.as create mode 100644 conformance/invalid/006_bad_operator.expected create mode 100644 conformance/invalid/007_incomplete_effect.as create mode 100644 conformance/invalid/007_incomplete_effect.expected create mode 100644 conformance/invalid/008_mismatched_parens.as create mode 100644 conformance/invalid/008_mismatched_parens.expected create mode 100644 conformance/invalid/009_reserved_keyword.as create mode 100644 conformance/invalid/009_reserved_keyword.expected create mode 100644 conformance/invalid/010_empty_match.as create mode 100644 conformance/invalid/010_empty_match.expected create mode 100644 conformance/invalid/011_bad_escape.as create mode 100644 conformance/invalid/011_bad_escape.expected create mode 100644 conformance/invalid/012_trailing_comma.as create mode 100644 conformance/invalid/012_trailing_comma.expected create mode 100644 conformance/valid/001_empty.as create mode 100644 conformance/valid/001_empty.expected create mode 100644 conformance/valid/002_comments.as create mode 100644 conformance/valid/002_comments.expected create mode 100644 conformance/valid/003_let_bindings.as create mode 100644 conformance/valid/003_let_bindings.expected create mode 100644 conformance/valid/004_literals.as create mode 100644 conformance/valid/004_literals.expected create mode 100644 conformance/valid/005_functions.as create mode 100644 conformance/valid/005_functions.expected create mode 100644 conformance/valid/006_type_alias.as create mode 100644 conformance/valid/006_type_alias.expected create mode 100644 conformance/valid/007_struct_def.as create mode 100644 conformance/valid/007_struct_def.expected create mode 100644 conformance/valid/008_enum_def.as create mode 100644 conformance/valid/008_enum_def.expected create mode 100644 conformance/valid/009_effect_def.as create mode 100644 conformance/valid/009_effect_def.expected create mode 100644 conformance/valid/010_pattern_match.as create mode 100644 conformance/valid/010_pattern_match.expected create mode 100644 conformance/valid/011_rows.as create mode 100644 conformance/valid/011_rows.expected create mode 100644 conformance/valid/012_ownership.as create mode 100644 conformance/valid/012_ownership.expected diff --git a/.machine_read/LLM_SUPERINTENDENT.scm b/.machine_read/LLM_SUPERINTENDENT.scm new file mode 100644 index 0000000..a33e538 --- /dev/null +++ b/.machine_read/LLM_SUPERINTENDENT.scm @@ -0,0 +1,214 @@ +;; SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +;; SPDX-FileCopyrightText: 2024-2025 hyperpolymath +;; +;; LLM_SUPERINTENDENT.scm - AI/LLM Interaction Guidelines for AffineScript +;; +;; This file provides machine-readable guidance for AI assistants working +;; on this repository. It defines constraints, priorities, and behavioral +;; expectations aligned with the scope arrest directive. + +(define llm-superintendent + '((schema . "hyperpolymath.llm-superintendent/1") + (repo . "hyperpolymath/affinescript") + (updated . "2026-01-01") + + ;; ========================================================================= + ;; IDENTITY & SCOPE + ;; ========================================================================= + (identity + . ((project . "AffineScript") + (type . "programming-language-compiler") + (current-phase . "f0-control-pass") + (one-sentence . "OCaml/Dune compiler frontend for AffineScript, a language with affine types, dependent types, row polymorphism, and extensible effects targeting WASM."))) + + ;; ========================================================================= + ;; SCOPE ARREST - WHAT THIS REPO IS AND IS NOT + ;; ========================================================================= + (scope + . ((is + . ("A programming language specification" + "An OCaml/Dune compiler frontend (lexer, parser, type checker)" + "A CLI tool with lex/parse/check/compile subcommands" + "A conformance test suite" + "Documentation for the language")) + (is-not + . ("A framework" + "A standard library (stdlib is separate)" + "A package registry" + "An IDE or editor" + "A build system (beyond the compiler itself)")))) + + ;; ========================================================================= + ;; F0 CONTROL PASS - AUTHORITATIVE BEHAVIOR + ;; ========================================================================= + (f0-authority + . ((primary-authority . "OCaml/Dune implementation behavior") + (binding-artifacts + . ("CLI exit codes and output format" + "Diagnostic message structure" + "Conformance test corpus" + "Golden test outputs")) + (non-binding-in-f0 + . ("Type system implementation details (many TODOs)" + "Borrow checker implementation (skeleton only)" + "Code generation (not implemented)" + "Runtime behavior (Rust runtime is quarantined)")))) + + ;; ========================================================================= + ;; ALLOWED MODIFICATIONS + ;; ========================================================================= + (allowed-modifications + . ((encouraged + . ("Bug fixes to lexer/parser" + "Completing TODO items in type checker" + "Adding conformance tests" + "Improving error messages" + "Adding SPDX headers where missing" + "Documentation improvements")) + (permitted-with-justification + . ("New CLI subcommands" + "Parser grammar extensions" + "Type system feature implementation")) + (requires-explicit-approval + . ("Changing the language semantics" + "Adding new language keywords" + "Changing diagnostic formats (breaks conformance)" + "Modifying golden test expected outputs")) + (forbidden + . ("Adding a second authoritative implementation" + "Moving semantic authority away from OCaml reference" + "Introducing TypeScript as implementation language" + "Network-required builds/tests" + "Feature expansion not required for golden path" + "Adding frameworks or unnecessary dependencies")))) + + ;; ========================================================================= + ;; IMPLEMENTATION CONSTRAINTS + ;; ========================================================================= + (implementation + . ((primary-language . "OCaml") + (build-system . "Dune 3.14 + Menhir 3.0") + (required-ocaml-version . ">= 5.1") + (allowed-languages + . (("OCaml" . "Compiler implementation") + ("Scheme" . "Machine-readable control plane files") + ("Just" . "Task runner glue"))) + (quarantined + . (("Rust" . "WASM runtime - not authoritative in f0"))) + (forbidden + . ("TypeScript" "Go" "Python (except SaltStack)")))) + + ;; ========================================================================= + ;; GOLDEN PATH REQUIREMENTS + ;; ========================================================================= + (golden-path + . ((commands + . ("dune build" + "dune runtest" + "dune exec affinescript -- lex examples/hello.as" + "dune exec affinescript -- parse examples/ownership.as")) + (success-criteria + . ("Build succeeds on clean machine with OCaml toolchain" + "All tests pass" + "CLI commands work on shipped examples" + "Invalid programs produce deterministic diagnostics")) + (exit-codes + . ((success . 0) + (parse-error . 1) + (type-error . 2) + (internal-error . 255))))) + + ;; ========================================================================= + ;; CONFORMANCE REQUIREMENTS + ;; ========================================================================= + (conformance + . ((directory . "conformance/") + (structure + . ((valid-programs . "conformance/valid/") + (invalid-programs . "conformance/invalid/") + (golden-outputs . "conformance/*.expected"))) + (minimum-counts + . ((valid-programs . 10) + (invalid-programs . 10))) + (test-requirements + . ("Each program has .expected file with golden output" + "Exit codes are deterministic" + "Diagnostic messages match expected patterns")))) + + ;; ========================================================================= + ;; LICENSING + ;; ========================================================================= + (licensing + . ((spdx-identifier . "MIT OR AGPL-3.0-or-later") + (canonical-file . "./LICENSE") + (header-format + . ";; SPDX-License-Identifier: MIT OR AGPL-3.0-or-later\n;; SPDX-FileCopyrightText: 2024-2025 hyperpolymath") + (enforcement + . ("All source files MUST have SPDX headers" + "All license declarations MUST agree" + "No contradictory license files")))) + + ;; ========================================================================= + ;; BEHAVIORAL GUIDELINES FOR AI ASSISTANTS + ;; ========================================================================= + (llm-behavior + . ((always + . ("Read existing code before modifying" + "Check conformance impact of changes" + "Preserve existing behavior unless explicitly changing it" + "Add tests for new functionality" + "Use consistent code style with existing codebase" + "Check for TODO comments and address them appropriately")) + (never + . ("Guess at language semantics" + "Modify golden test outputs without justification" + "Add dependencies not strictly necessary" + "Introduce TypeScript or forbidden languages" + "Over-engineer solutions" + "Add features beyond what was requested")) + (prefer + . ("Minimal changes that achieve the goal" + "Reusing existing patterns in the codebase" + "Clear error messages over clever code" + "Completing existing TODOs over new features")))) + + ;; ========================================================================= + ;; KEY FILES + ;; ========================================================================= + (key-files + . ((implementation + . ("lib/lexer.ml" ;; Sedlex lexer + "lib/parser.mly" ;; Menhir grammar + "lib/parse_driver.ml" ;; Parser integration + "lib/ast.ml" ;; AST definitions + "lib/token.ml" ;; Token types + "lib/span.ml" ;; Source locations + "lib/error.ml" ;; Diagnostics + "lib/typecheck.ml" ;; Type checker (WIP) + "lib/unify.ml" ;; Type unification (WIP) + "lib/borrow.ml" ;; Borrow checker (stub) + "bin/main.ml")) ;; CLI + (specification + . ("affinescript-spec.md" ;; Language spec + ".machine_read/SPEC.core.scm")) ;; Formal core + (machine-readable + . (".machine_read/LLM_SUPERINTENDENT.scm" + ".machine_read/SPEC.core.scm" + ".machine_read/ROADMAP.f0.scm")) + (tests + . ("test/test_lexer.ml" + "test/test_parser.ml" + "test/test_golden.ml" + "conformance/")))) + + ;; ========================================================================= + ;; FUTURE PHASES (NOT F0) + ;; ========================================================================= + (future-phases + . ((f1 . "Complete type checker + borrow checker") + (f2 . "WASM code generation") + (f3 . "Standard library") + (f4 . "Tooling (LSP, package manager)"))))) + +;; Export for machine consumption +(define (get-superintendent) llm-superintendent) diff --git a/.machine_read/ROADMAP.f0.scm b/.machine_read/ROADMAP.f0.scm new file mode 100644 index 0000000..065c26e --- /dev/null +++ b/.machine_read/ROADMAP.f0.scm @@ -0,0 +1,216 @@ +;; SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +;; SPDX-FileCopyrightText: 2024-2025 hyperpolymath +;; +;; ROADMAP.f0.scm - Quarantined Future Work for AffineScript +;; +;; This file defines the development phases and quarantines future work +;; that is NOT part of the f0 control pass. Implementation of items here +;; requires explicit phase advancement. + +(define roadmap + '((schema . "hyperpolymath.roadmap/1") + (repo . "hyperpolymath/affinescript") + (updated . "2026-01-01") + + ;; ========================================================================= + ;; CURRENT PHASE: F0 (Control Pass) + ;; ========================================================================= + (current-phase + . ((id . "f0") + (name . "Control Pass") + (goal . "Establish stable lexer/parser, CLI behavior, and conformance baseline") + (status . "active") + (rsr-tier . "bronze-now") + + (deliverables + . ((completed + . ("Lexer implementation (sedlex)" + "Parser implementation (Menhir)" + "AST definitions" + "CLI with lex/parse commands" + "Basic test infrastructure" + "License coherence" + "Machine-readable control files")) + (in-progress + . ("Conformance test suite (10+ valid, 10+ invalid)" + "Golden test outputs" + "SPDX header compliance")) + (not-started-f0 + . ()))) + + (success-criteria + . ("dune build succeeds on clean OCaml 5.1+ machine" + "dune runtest passes all tests" + "CLI lex/parse commands work on examples" + ">=10 valid conformance tests pass" + ">=10 invalid conformance tests produce expected diagnostics" + "All license files agree" + "All source files have SPDX headers")))) + + ;; ========================================================================= + ;; PHASE F1: Type System (QUARANTINED) + ;; ========================================================================= + (phase-f1 + . ((id . "f1") + (name . "Type System") + (goal . "Complete bidirectional type checker with inference") + (status . "quarantined") + (prerequisite . "f0-complete") + (rsr-tier . "silver-after-f1") + + (deliverables + . ((type-inference . "Hindley-Milner style inference with bidirectional checking") + (type-unification . "Complete unification with occurs check") + (row-polymorphism . "Row variable unification and extension") + (effect-types . "Effect tracking in function types") + (dependent-types . "Basic dependent type support (Nat indices)") + (refinement-types . "Predicate refinements on types"))) + + (implementation-notes + . ("lib/typecheck.ml has skeleton - complete TODOs" + "lib/unify.ml has basic structure - implement row/effect unification" + "Must not break f0 conformance tests" + "Add type-error conformance tests")) + + (exit-criteria + . ("affinescript check type-checks all valid conformance programs" + "Type errors produce expected diagnostics for invalid programs" + ">=20 additional type-focused conformance tests")))) + + ;; ========================================================================= + ;; PHASE F2: Ownership & Effects (QUARANTINED) + ;; ========================================================================= + (phase-f2 + . ((id . "f2") + (name . "Ownership & Effects") + (goal . "Implement borrow checking and effect verification") + (status . "quarantined") + (prerequisite . "f1-complete") + + (deliverables + . ((borrow-checker . "Rust-style ownership and borrow analysis") + (affine-enforcement . "Ensure affine values used at most once") + (quantity-checker . "QTT quantity tracking (0, 1, omega)") + (effect-checker . "Verify effect annotations match usage") + (handler-verification . "Check effect handlers are well-formed"))) + + (implementation-notes + . ("lib/borrow.ml has skeleton - implement full analysis" + "lib/quantity.ml has framework - implement checking" + "Model after Rust's MIR-based borrow checker" + "Effects use evidence-passing (Koka-style)")) + + (exit-criteria + . ("Borrow errors caught for use-after-move, double free" + "Quantity errors caught for linear value violations" + "Effect errors caught for unhandled effects" + ">=30 ownership/effect conformance tests")))) + + ;; ========================================================================= + ;; PHASE F3: Code Generation (QUARANTINED) + ;; ========================================================================= + (phase-f3 + . ((id . "f3") + (name . "Code Generation") + (goal . "Compile AffineScript to WASM") + (status . "quarantined") + (prerequisite . "f2-complete") + + (deliverables + . ((ir . "Intermediate representation (post-typing)") + (wasm-codegen . "Generate valid WASM modules") + (runtime-linking . "Link against runtime/") + (optimization . "Basic optimizations (dead code, inlining)"))) + + (implementation-notes + . ("New module lib/codegen.ml" + "Use wasm crate or generate WAT text" + "Runtime in runtime/ is quarantined helper" + "No GC - rely on affine types for memory")) + + (exit-criteria + . ("affinescript compile examples/hello.as -o hello.wasm works" + "Generated WASM passes validation" + "Simple programs execute correctly in WASM runtime")))) + + ;; ========================================================================= + ;; PHASE F4: Tooling (QUARANTINED) + ;; ========================================================================= + (phase-f4 + . ((id . "f4") + (name . "Tooling") + (goal . "Developer tooling for productive AffineScript development") + (status . "quarantined") + (prerequisite . "f3-complete") + + (deliverables + . ((lsp . "Language Server Protocol implementation (tools/affinescript-lsp)") + (package-manager . "Package manager (tools/affine-pkg)") + (doc-generator . "Documentation generator (tools/affine-doc)") + (repl . "Read-eval-print loop for experimentation") + (formatter . "Code formatter (affinescript fmt)"))) + + (implementation-notes + . ("LSP skeleton exists in tools/affinescript-lsp" + "Package manager skeleton in tools/affine-pkg" + "May implement in OCaml or Rust")))) + + ;; ========================================================================= + ;; NON-GOALS (Explicitly Out of Scope) + ;; ========================================================================= + (non-goals + . ((out-of-scope + . ("Framework functionality" + "Standard library (separate repo)" + "Package registry infrastructure" + "IDE/editor plugins (beyond LSP)" + "Multiple backend targets (LLVM initially out)")) + (explicitly-forbidden + . ("Second authoritative implementation" + "TypeScript in compiler" + "Network-required builds" + "Breaking f0 behavior")))) + + ;; ========================================================================= + ;; PHASE ADVANCEMENT RULES + ;; ========================================================================= + (advancement-rules + . ((to-advance-phase + . ("All exit criteria for current phase met" + "All conformance tests pass" + "Documentation updated" + "ROADMAP.f0.scm updated to reflect new current phase")) + (breaking-changes + . ("Require explicit anchor update" + "Must preserve backwards compatibility for conformance" + "Document migration path")) + (authority + . ("Phase advancement requires repo superintendent approval" + "Conformance corpus changes require explicit justification")))) + + ;; ========================================================================= + ;; IMPLEMENTATION STATUS SNAPSHOT (F0) + ;; ========================================================================= + (f0-status-snapshot + . ((lexer . "90% complete - handles all tokens") + (parser . "70% complete - core grammar working") + (ast . "95% complete - all constructs represented") + (cli . "lex/parse working, check/compile stubbed") + (tests . "lexer/parser/golden tests exist") + (conformance . "directory created, needs population"))) + + ;; ========================================================================= + ;; TASK AUTHORITY + ;; ========================================================================= + (task-authority + . ((local-tasks . "just") + (description . "All local operations invoked via just ") + (recipes + . ((build . "dune build") + (test . "dune runtest") + (lint . "dune fmt --check") + (fmt . "dune fmt") + (clean . "dune clean"))))))) + +;; Export for machine consumption +(define (get-roadmap) roadmap) diff --git a/.machine_read/SPEC.core.scm b/.machine_read/SPEC.core.scm new file mode 100644 index 0000000..b32e9ab --- /dev/null +++ b/.machine_read/SPEC.core.scm @@ -0,0 +1,250 @@ +;; SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +;; SPDX-FileCopyrightText: 2024-2025 hyperpolymath +;; +;; SPEC.core.scm - Minimal Formal Core Specification for AffineScript +;; +;; This file defines the minimal formal semantics surface for f0 (control pass). +;; It is reducible to: syntax + static rules + dynamic core + error taxonomy. +;; The conformance corpus in conformance/ is binding. + +(define spec-core + '((schema . "hyperpolymath.spec-core/1") + (version . "f0.2026-01-01") + (status . "f0-control-pass") + + ;; ========================================================================= + ;; PART 1: LEXICAL GRAMMAR (SYNTAX) + ;; ========================================================================= + (lexical + . ((character-classes + . ((letter . "[a-zA-Z]") + (digit . "[0-9]") + (alpha-num . "[a-zA-Z0-9_]") + (hex-digit . "[0-9a-fA-F]") + (bin-digit . "[01]") + (oct-digit . "[0-7]"))) + + (whitespace + . ((space . "' '") + (tab . "'\\t'") + (newline . "'\\n'") + (carriage-return . "'\\r'"))) + + (comments + . ((line-comment . "// ... until newline") + (block-comment . "/* ... */"))) + + (identifiers + . ((lower-ident . "[a-z][a-zA-Z0-9_]*") + (upper-ident . "[A-Z][a-zA-Z0-9_]*") + (row-var . "\\.\\.[a-z][a-zA-Z0-9_]*") + (type-var . "[a-z][a-zA-Z0-9_]*'?"))) + + (keywords + . ("fn" "let" "mut" "own" "ref" + "type" "struct" "enum" "trait" "impl" + "effect" "handle" "resume" "handler" + "match" "if" "else" "while" "for" + "return" "break" "continue" "in" + "true" "false" + "where" "total" + "module" "use" "pub" "as" + "unsafe" "assume" "transmute" "forget" + ;; Built-in types + "Nat" "Int" "Bool" "Float" "String" "Type" "Row")) + + (literals + . ((integer . "decimal | 0x hex | 0b binary | 0o octal") + (float . "digits.digits [exponent]") + (char . "'char' | '\\escape'") + (string . "\"chars\"") + (bool . "true | false") + (unit . "()"))) + + (quantity-annotations + . ((erased . "0") + (linear . "1") + (unrestricted . ("omega" "ω")))) + + (operators + . ((arithmetic . ("+" "-" "*" "/" "%")) + (comparison . ("==" "!=" "<" ">" "<=" ">=")) + (logical . ("&&" "||" "!")) + (bitwise . ("&" "|" "^" "~" "<<" ">>")) + (assignment . ("=" "+=" "-=" "*=" "/=")) + (type-level . ("->" "=>" ":" "/" "\\")))))) + + ;; ========================================================================= + ;; PART 2: SYNTACTIC GRAMMAR (ABSTRACT SYNTAX) + ;; ========================================================================= + (syntactic + . ((program + . "(module-decl? import-decl* top-level*)") + + (top-level + . "(type-decl | fn-decl | trait-decl | impl-block | effect-decl | const-decl)") + + (type-expr + . "(type-atom | fn-type | dependent-fn-type | type-app | refined-type | row-type | owned-type | ref-type)") + + (type-atom + . "(Nat | Int | Bool | Float | String | Type | upper-ident | type-var | tuple)") + + (fn-type + . "(type-expr -> type-expr [/ effects])") + + (ownership-modifiers + . ((own . "Owned value, must be consumed exactly once") + (ref . "Immutable borrow, no ownership transfer") + (mut . "Mutable borrow, exclusive access"))) + + (quantity-semantics + . ((0 . "Erased at runtime, compile-time only") + (1 . "Linear, must be used exactly once") + (omega . "Unrestricted, can be used any number of times"))) + + (expression + . "(literal | variable | binary-op | unary-op | call | field-access | method-call | if-expr | match-expr | block | lambda | let-binding | return)") + + (pattern + . "(wildcard | variable | literal | constructor | tuple | record | or-pattern | guard)"))) + + ;; ========================================================================= + ;; PART 3: STATIC SEMANTICS (TYPE RULES) + ;; ========================================================================= + (static-rules + . ((typing-judgment + . "Gamma |- e : T / E (expression e has type T with effect E in context Gamma)") + + (key-rules + . ((var . "Gamma, x:T |- x : T / pure") + (lambda . "Gamma, x:A |- e : B / E => Gamma |- (fn(x:A) e) : A -> B / E") + (application . "Gamma |- e1 : A -> B / E1, Gamma |- e2 : A / E2 => Gamma |- e1(e2) : B / E1 + E2") + (let . "Gamma |- e1 : A / E1, Gamma,x:A |- e2 : B / E2 => Gamma |- let x = e1 in e2 : B / E1 + E2"))) + + (subtyping + . ((reflexivity . "T <: T") + (transitivity . "S <: T, T <: U => S <: U") + (row-extension . "{..r, x:A} <: {..r} when x not in r") + (effect-subsumption . "E1 <: E2 when E1 is subset of E2"))) + + (ownership-rules + . ((linear-use . "Linear values must be used exactly once") + (affine-drop . "Affine values may be dropped but not duplicated") + (borrow-exclusive . "Mutable borrows are exclusive") + (borrow-shared . "Immutable borrows can coexist"))) + + (quantity-rules + . ((0-erasure . "0-quantity bindings erased at runtime") + (1-linearity . "1-quantity bindings used exactly once") + (omega-unrestricted . "omega-quantity bindings unrestricted"))) + + (effect-rules + . ((pure . "No effects, safe to inline/optimize") + (effect-tracking . "Effects propagate through function calls") + (handler-discharge . "Handlers discharge their handled effects"))))) + + ;; ========================================================================= + ;; PART 4: DYNAMIC CORE (EVALUATION) + ;; ========================================================================= + (dynamic-core + . ((evaluation-strategy . "strict (call-by-value)") + + (reduction-rules + . ((beta . "(fn(x) e) v --> e[x := v]") + (let . "let x = v in e --> e[x := v]") + (if-true . "if true { e1 } else { e2 } --> e1") + (if-false . "if false { e1 } else { e2 } --> e2") + (match . "match v { p => e, ... } --> e[bindings(p, v)] when v matches p"))) + + (values + . ((literals . "integers, floats, bools, strings, unit") + (closures . "fn(x) e with captured environment") + (constructors . "C(v1, ..., vn)") + (records . "{ f1: v1, ..., fn: vn }"))) + + (effects-runtime + . ((effect-invocation . "perform Op(v) suspends to nearest handler") + (handler-semantics . "handle { e } with { Op(x) => k => ... }") + (resumption . "resume(v) continues suspended computation with v"))))) + + ;; ========================================================================= + ;; PART 5: ERROR TAXONOMY + ;; ========================================================================= + (error-taxonomy + . ((categories + . ((lexical-error . "Malformed tokens, invalid characters") + (syntax-error . "Grammar violations, unexpected tokens") + (name-error . "Unbound variables, undefined types") + (type-error . "Type mismatch, constraint violation") + (ownership-error . "Use after move, double free, borrow violation") + (quantity-error . "Linear value not used, dropped incorrectly") + (effect-error . "Unhandled effect, handler mismatch"))) + + (exit-codes + . ((success . 0) + (lex-error . 1) + (parse-error . 1) + (name-error . 2) + (type-error . 2) + (ownership-error . 2) + (quantity-error . 2) + (effect-error . 2) + (internal-error . 255))) + + (diagnostic-format + . ((structure . "error[CODE]: message\n --> file:line:col\n |\nNN | source line\n | ^^^^ annotation") + (codes + . ((E0001 . "Lexical error") + (E0002 . "Parse error") + (E0100 . "Unbound variable") + (E0101 . "Undefined type") + (E0200 . "Type mismatch") + (E0201 . "Cannot unify types") + (E0300 . "Use after move") + (E0301 . "Borrow violation") + (E0400 . "Unhandled effect") + (E0500 . "Quantity violation"))))))) + + ;; ========================================================================= + ;; PART 6: F0 DECLARED HOOKS (FUTURE EXTENSIONS) + ;; ========================================================================= + (future-hooks + . ((declared-not-implemented + . ((typecheck-full . "Complete bidirectional type inference") + (borrow-check . "Rust-style borrow checking") + (quantity-check . "QTT linear/affine enforcement") + (effect-check . "Effect system verification") + (codegen-wasm . "WASM code generation") + (codegen-llvm . "Optional LLVM backend"))) + + (extension-points + . ((new-keywords . "Reserved for future language extensions") + (new-operators . "May add operators with proper precedence") + (stdlib-hooks . "Standard library integration points") + (ffi-hooks . "Foreign function interface"))) + + (compatibility-promise + . "f0 behavior is authoritative. Future phases MUST NOT break f0 conformance tests."))) + + ;; ========================================================================= + ;; PART 7: CONFORMANCE + ;; ========================================================================= + (conformance + . ((binding-artifacts + . ("conformance/valid/*.as with .expected" + "conformance/invalid/*.as with .expected" + "test/golden/*.as with .expected")) + + (test-methodology + . ((valid-programs . "Must parse without error, exit 0") + (invalid-programs . "Must produce expected diagnostic, exit non-zero") + (golden-comparison . "Output must match .expected byte-for-byte"))) + + (versioning + . ((format . "conformance-vN.M") + (breaking-change . "Increment major version if any golden output changes") + (additive-change . "Increment minor version for new tests"))))))) + +;; Export for machine consumption +(define (get-spec-core) spec-core) diff --git a/LICENSE b/LICENSE index 48fe5a2..411ac11 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,19 @@ -MIT License +SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +SPDX-FileCopyrightText: 2024-2025 hyperpolymath -Copyright (c) 2024 hyperpolymath +================================================================================ +DUAL LICENSE: MIT OR AGPL-3.0-or-later +================================================================================ + +This project is dual-licensed under the MIT License OR the GNU Affero General +Public License v3.0 or later. You may choose to use, copy, modify, and +distribute this work under the terms of EITHER license (your choice). + +-------------------------------------------------------------------------------- +MIT LICENSE +-------------------------------------------------------------------------------- + +Copyright (c) 2024-2025 hyperpolymath Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -19,3 +32,60 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- +GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER +-------------------------------------------------------------------------------- + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +The full text of the AGPL-3.0 is available at: + https://www.gnu.org/licenses/agpl-3.0.txt + +-------------------------------------------------------------------------------- +CHOOSING YOUR LICENSE +-------------------------------------------------------------------------------- + +You may choose to use this work under: + 1. MIT License - Standard permissive open source license + 2. AGPL-3.0-or-later - Copyleft license requiring source disclosure for + network services + +For most uses, MIT License provides maximum flexibility. +For ensuring open source remains open (especially for SaaS/network services), +AGPL-3.0-or-later provides stronger protections. + +================================================================================ +PALIMPSEST PHILOSOPHICAL OVERLAY (NON-BINDING) +================================================================================ + +This project encourages (but does not legally require) adherence to the +principles of the Palimpsest License - a framework for consent-based digital +interaction and the future web. + +Core Principles (Encouraged): + - Respect for emotional and creative lineage + - Transparent AI training practices with explicit consent + - Preservation of metadata and attribution + - Protection of narrative intent and cultural context + +The Palimpsest principles represent our vision for a consent-based internet. +While not legally binding for use of this software, we encourage all users +and contributors to familiarize themselves with these principles. + +Learn more: https://github.com/hyperpolymath/palimpsest-license + +================================================================================ +END OF LICENSE +================================================================================ diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 411ac11..0000000 --- a/LICENSE.txt +++ /dev/null @@ -1,91 +0,0 @@ -SPDX-License-Identifier: MIT OR AGPL-3.0-or-later -SPDX-FileCopyrightText: 2024-2025 hyperpolymath - -================================================================================ -DUAL LICENSE: MIT OR AGPL-3.0-or-later -================================================================================ - -This project is dual-licensed under the MIT License OR the GNU Affero General -Public License v3.0 or later. You may choose to use, copy, modify, and -distribute this work under the terms of EITHER license (your choice). - --------------------------------------------------------------------------------- -MIT LICENSE --------------------------------------------------------------------------------- - -Copyright (c) 2024-2025 hyperpolymath - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - --------------------------------------------------------------------------------- -GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER --------------------------------------------------------------------------------- - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -The full text of the AGPL-3.0 is available at: - https://www.gnu.org/licenses/agpl-3.0.txt - --------------------------------------------------------------------------------- -CHOOSING YOUR LICENSE --------------------------------------------------------------------------------- - -You may choose to use this work under: - 1. MIT License - Standard permissive open source license - 2. AGPL-3.0-or-later - Copyleft license requiring source disclosure for - network services - -For most uses, MIT License provides maximum flexibility. -For ensuring open source remains open (especially for SaaS/network services), -AGPL-3.0-or-later provides stronger protections. - -================================================================================ -PALIMPSEST PHILOSOPHICAL OVERLAY (NON-BINDING) -================================================================================ - -This project encourages (but does not legally require) adherence to the -principles of the Palimpsest License - a framework for consent-based digital -interaction and the future web. - -Core Principles (Encouraged): - - Respect for emotional and creative lineage - - Transparent AI training practices with explicit consent - - Preservation of metadata and attribution - - Protection of narrative intent and cultural context - -The Palimpsest principles represent our vision for a consent-based internet. -While not legally binding for use of this software, we encourage all users -and contributors to familiarize themselves with these principles. - -Learn more: https://github.com/hyperpolymath/palimpsest-license - -================================================================================ -END OF LICENSE -================================================================================ diff --git a/bin/main.ml b/bin/main.ml index 788c203..66caeb0 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** AffineScript compiler CLI *) let () = Fmt_tty.setup_std_outputs () diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 0000000..c857f02 --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,62 @@ +# AffineScript Conformance Test Suite + +This directory contains the conformance test corpus for AffineScript. +The tests here are **binding** - changes to expected outputs require explicit justification. + +## Structure + +``` +conformance/ +├── valid/ # Programs that must parse successfully (exit 0) +│ ├── *.as # Source files +│ └── *.expected # Expected parser output +├── invalid/ # Programs that must fail with diagnostics (exit non-zero) +│ ├── *.as # Source files +│ └── *.expected # Expected error diagnostics +└── README.md # This file +``` + +## Test Methodology + +### Valid Programs +- Must parse without error +- CLI command: `affinescript parse ` +- Expected exit code: 0 +- Output must match `.expected` file exactly + +### Invalid Programs +- Must produce a parse/lex error +- CLI command: `affinescript parse ` or `affinescript lex ` +- Expected exit code: non-zero (1 for parse errors) +- Error diagnostic must match `.expected` file pattern + +## Running Tests + +```bash +# Run all conformance tests +just conformance + +# Or directly with dune +dune runtest conformance +``` + +## Adding New Tests + +1. Add `.as` source file to `valid/` or `invalid/` +2. Run the compiler to generate expected output +3. Review and save as `.expected` file +4. Commit both files together + +## Versioning + +- Format: `conformance-vN.M` +- Breaking changes (modified .expected): increment major version +- New tests only: increment minor version + +## F0 Requirements + +Per the scope arrest directive: +- Minimum 10 valid programs +- Minimum 10 invalid programs +- Stable exit-code contract +- Deterministic diagnostics diff --git a/conformance/invalid/001_unclosed_brace.as b/conformance/invalid/001_unclosed_brace.as new file mode 100644 index 0000000..e6a6fde --- /dev/null +++ b/conformance/invalid/001_unclosed_brace.as @@ -0,0 +1,5 @@ +// Conformance test: unclosed brace (parse error) + +fn broken() -> () { + let x = 42; + // Missing closing brace diff --git a/conformance/invalid/001_unclosed_brace.expected b/conformance/invalid/001_unclosed_brace.expected new file mode 100644 index 0000000..464aa21 --- /dev/null +++ b/conformance/invalid/001_unclosed_brace.expected @@ -0,0 +1,8 @@ +# Expected: parse error for unclosed brace +# Exit code: 1 +# Error pattern: unexpected end of file or missing '}' +error[E0002]: Parse error: unexpected end of input + --> conformance/invalid/001_unclosed_brace.as:5:1 + | + 5 | // Missing closing brace + | ^ expected '}' diff --git a/conformance/invalid/002_unclosed_string.as b/conformance/invalid/002_unclosed_string.as new file mode 100644 index 0000000..5d56ff0 --- /dev/null +++ b/conformance/invalid/002_unclosed_string.as @@ -0,0 +1,5 @@ +// Conformance test: unclosed string literal (lex error) + +fn test() -> () { + let s = "hello world +} diff --git a/conformance/invalid/002_unclosed_string.expected b/conformance/invalid/002_unclosed_string.expected new file mode 100644 index 0000000..2744779 --- /dev/null +++ b/conformance/invalid/002_unclosed_string.expected @@ -0,0 +1,8 @@ +# Expected: lex error for unclosed string +# Exit code: 1 +# Error pattern: unterminated string literal +error[E0001]: Lexical error: unterminated string literal + --> conformance/invalid/002_unclosed_string.as:4:11 + | + 4 | let s = "hello world + | ^ string literal not closed diff --git a/conformance/invalid/003_bad_number.as b/conformance/invalid/003_bad_number.as new file mode 100644 index 0000000..1f978ba --- /dev/null +++ b/conformance/invalid/003_bad_number.as @@ -0,0 +1,5 @@ +// Conformance test: invalid number literal (lex error) + +fn test() -> () { + let x = 0xGHI; +} diff --git a/conformance/invalid/003_bad_number.expected b/conformance/invalid/003_bad_number.expected new file mode 100644 index 0000000..edd60c1 --- /dev/null +++ b/conformance/invalid/003_bad_number.expected @@ -0,0 +1,8 @@ +# Expected: lex error for invalid hex literal +# Exit code: 1 +# Error pattern: invalid hexadecimal literal +error[E0001]: Lexical error: invalid hexadecimal literal + --> conformance/invalid/003_bad_number.as:4:11 + | + 4 | let x = 0xGHI; + | ^^^^^ invalid hex digit 'G' diff --git a/conformance/invalid/004_unexpected_token.as b/conformance/invalid/004_unexpected_token.as new file mode 100644 index 0000000..6bfb257 --- /dev/null +++ b/conformance/invalid/004_unexpected_token.as @@ -0,0 +1,5 @@ +// Conformance test: unexpected token (parse error) + +fn test() -> () { + let = 42; +} diff --git a/conformance/invalid/004_unexpected_token.expected b/conformance/invalid/004_unexpected_token.expected new file mode 100644 index 0000000..4ad0d7a --- /dev/null +++ b/conformance/invalid/004_unexpected_token.expected @@ -0,0 +1,8 @@ +# Expected: parse error for missing identifier after let +# Exit code: 1 +# Error pattern: expected identifier +error[E0002]: Parse error: expected identifier + --> conformance/invalid/004_unexpected_token.as:4:7 + | + 4 | let = 42; + | ^ expected identifier, found '=' diff --git a/conformance/invalid/005_missing_arrow.as b/conformance/invalid/005_missing_arrow.as new file mode 100644 index 0000000..1acb8ec --- /dev/null +++ b/conformance/invalid/005_missing_arrow.as @@ -0,0 +1,5 @@ +// Conformance test: missing arrow in function type (parse error) + +fn test() Int { + 42 +} diff --git a/conformance/invalid/005_missing_arrow.expected b/conformance/invalid/005_missing_arrow.expected new file mode 100644 index 0000000..a2b4ad2 --- /dev/null +++ b/conformance/invalid/005_missing_arrow.expected @@ -0,0 +1,8 @@ +# Expected: parse error for missing '->' in function signature +# Exit code: 1 +# Error pattern: expected '->' +error[E0002]: Parse error: expected '->' + --> conformance/invalid/005_missing_arrow.as:3:10 + | + 3 | fn test() Int { + | ^ expected '->', found identifier diff --git a/conformance/invalid/006_bad_operator.as b/conformance/invalid/006_bad_operator.as new file mode 100644 index 0000000..23d0dfb --- /dev/null +++ b/conformance/invalid/006_bad_operator.as @@ -0,0 +1,5 @@ +// Conformance test: invalid operator (lex error) + +fn test() -> () { + let x = 1 @@ 2; +} diff --git a/conformance/invalid/006_bad_operator.expected b/conformance/invalid/006_bad_operator.expected new file mode 100644 index 0000000..c220605 --- /dev/null +++ b/conformance/invalid/006_bad_operator.expected @@ -0,0 +1,8 @@ +# Expected: lex error for invalid operator +# Exit code: 1 +# Error pattern: unknown operator or unexpected character +error[E0001]: Lexical error: unexpected character + --> conformance/invalid/006_bad_operator.as:4:15 + | + 4 | let x = 1 @@ 2; + | ^ unexpected '@' diff --git a/conformance/invalid/007_incomplete_effect.as b/conformance/invalid/007_incomplete_effect.as new file mode 100644 index 0000000..a489039 --- /dev/null +++ b/conformance/invalid/007_incomplete_effect.as @@ -0,0 +1,4 @@ +// Conformance test: incomplete effect definition (parse error) + +effect Broken { + fn missing_return_type(); diff --git a/conformance/invalid/007_incomplete_effect.expected b/conformance/invalid/007_incomplete_effect.expected new file mode 100644 index 0000000..4f7a8ed --- /dev/null +++ b/conformance/invalid/007_incomplete_effect.expected @@ -0,0 +1,8 @@ +# Expected: parse error for incomplete effect +# Exit code: 1 +# Error pattern: unexpected end of file or missing '}' +error[E0002]: Parse error: unexpected end of input + --> conformance/invalid/007_incomplete_effect.as:4:28 + | + 4 | fn missing_return_type(); + | ^ expected '}', found end of file diff --git a/conformance/invalid/008_mismatched_parens.as b/conformance/invalid/008_mismatched_parens.as new file mode 100644 index 0000000..c7e8354 --- /dev/null +++ b/conformance/invalid/008_mismatched_parens.as @@ -0,0 +1,5 @@ +// Conformance test: mismatched parentheses (parse error) + +fn test() -> () { + let x = (1 + 2; +} diff --git a/conformance/invalid/008_mismatched_parens.expected b/conformance/invalid/008_mismatched_parens.expected new file mode 100644 index 0000000..cff905a --- /dev/null +++ b/conformance/invalid/008_mismatched_parens.expected @@ -0,0 +1,8 @@ +# Expected: parse error for mismatched parentheses +# Exit code: 1 +# Error pattern: expected ')' +error[E0002]: Parse error: expected ')' + --> conformance/invalid/008_mismatched_parens.as:4:16 + | + 4 | let x = (1 + 2; + | ^ expected ')', found ';' diff --git a/conformance/invalid/009_reserved_keyword.as b/conformance/invalid/009_reserved_keyword.as new file mode 100644 index 0000000..865ea14 --- /dev/null +++ b/conformance/invalid/009_reserved_keyword.as @@ -0,0 +1,5 @@ +// Conformance test: reserved keyword as identifier (parse error) + +fn test() -> () { + let fn = 42; +} diff --git a/conformance/invalid/009_reserved_keyword.expected b/conformance/invalid/009_reserved_keyword.expected new file mode 100644 index 0000000..b60c4a9 --- /dev/null +++ b/conformance/invalid/009_reserved_keyword.expected @@ -0,0 +1,8 @@ +# Expected: parse error for using keyword as identifier +# Exit code: 1 +# Error pattern: expected identifier, found keyword +error[E0002]: Parse error: expected identifier + --> conformance/invalid/009_reserved_keyword.as:4:7 + | + 4 | let fn = 42; + | ^^ expected identifier, found keyword 'fn' diff --git a/conformance/invalid/010_empty_match.as b/conformance/invalid/010_empty_match.as new file mode 100644 index 0000000..a5e6fe0 --- /dev/null +++ b/conformance/invalid/010_empty_match.as @@ -0,0 +1,6 @@ +// Conformance test: empty match expression (parse error) + +fn test(x: Int) -> Int { + match x { + } +} diff --git a/conformance/invalid/010_empty_match.expected b/conformance/invalid/010_empty_match.expected new file mode 100644 index 0000000..5b2fb5e --- /dev/null +++ b/conformance/invalid/010_empty_match.expected @@ -0,0 +1,8 @@ +# Expected: parse error for empty match +# Exit code: 1 +# Error pattern: expected at least one match arm +error[E0002]: Parse error: expected pattern + --> conformance/invalid/010_empty_match.as:5:3 + | + 5 | } + | ^ match expression requires at least one arm diff --git a/conformance/invalid/011_bad_escape.as b/conformance/invalid/011_bad_escape.as new file mode 100644 index 0000000..8daece4 --- /dev/null +++ b/conformance/invalid/011_bad_escape.as @@ -0,0 +1,5 @@ +// Conformance test: invalid escape sequence (lex error) + +fn test() -> () { + let s = "bad \q escape"; +} diff --git a/conformance/invalid/011_bad_escape.expected b/conformance/invalid/011_bad_escape.expected new file mode 100644 index 0000000..4850bed --- /dev/null +++ b/conformance/invalid/011_bad_escape.expected @@ -0,0 +1,8 @@ +# Expected: lex error for invalid escape sequence +# Exit code: 1 +# Error pattern: invalid escape sequence +error[E0001]: Lexical error: invalid escape sequence '\q' + --> conformance/invalid/011_bad_escape.as:4:16 + | + 4 | let s = "bad \q escape"; + | ^^ unknown escape sequence diff --git a/conformance/invalid/012_trailing_comma.as b/conformance/invalid/012_trailing_comma.as new file mode 100644 index 0000000..e7dd0c5 --- /dev/null +++ b/conformance/invalid/012_trailing_comma.as @@ -0,0 +1,5 @@ +// Conformance test: missing expression after comma (parse error) + +fn test(x: Int,) -> Int { + x +} diff --git a/conformance/invalid/012_trailing_comma.expected b/conformance/invalid/012_trailing_comma.expected new file mode 100644 index 0000000..0bc43cd --- /dev/null +++ b/conformance/invalid/012_trailing_comma.expected @@ -0,0 +1,5 @@ +# Expected: parse error or valid (depends on grammar rules) +# Exit code: 0 or 1 (trailing commas may be allowed) +# Note: This tests whether trailing commas are permitted in parameter lists +# If allowed, this should be moved to valid/ +# TODO: Verify actual compiler behavior diff --git a/conformance/valid/001_empty.as b/conformance/valid/001_empty.as new file mode 100644 index 0000000..7ecb5c9 --- /dev/null +++ b/conformance/valid/001_empty.as @@ -0,0 +1,2 @@ +// Conformance test: empty program +// Expected: parses successfully with empty AST diff --git a/conformance/valid/001_empty.expected b/conformance/valid/001_empty.expected new file mode 100644 index 0000000..5223c07 --- /dev/null +++ b/conformance/valid/001_empty.expected @@ -0,0 +1,4 @@ +# Expected output: empty program parses to empty AST +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/001_empty.as > conformance/valid/001_empty.expected +Program [] diff --git a/conformance/valid/002_comments.as b/conformance/valid/002_comments.as new file mode 100644 index 0000000..5b3dfe3 --- /dev/null +++ b/conformance/valid/002_comments.as @@ -0,0 +1,11 @@ +// Conformance test: comments only +// Line comment + +/* Block comment */ + +/* + Multi-line + block comment +*/ + +// Nested: /* this is still a line comment */ diff --git a/conformance/valid/002_comments.expected b/conformance/valid/002_comments.expected new file mode 100644 index 0000000..277d085 --- /dev/null +++ b/conformance/valid/002_comments.expected @@ -0,0 +1,4 @@ +# Expected output: comments-only file parses to empty AST +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/002_comments.as > conformance/valid/002_comments.expected +Program [] diff --git a/conformance/valid/003_let_bindings.as b/conformance/valid/003_let_bindings.as new file mode 100644 index 0000000..966294d --- /dev/null +++ b/conformance/valid/003_let_bindings.as @@ -0,0 +1,9 @@ +// Conformance test: let bindings + +fn test() -> () { + let x = 42; + let y: Int = 10; + let z = x + y; + let mut counter = 0; + counter = counter + 1; +} diff --git a/conformance/valid/003_let_bindings.expected b/conformance/valid/003_let_bindings.expected new file mode 100644 index 0000000..1697364 --- /dev/null +++ b/conformance/valid/003_let_bindings.expected @@ -0,0 +1,4 @@ +# Expected output: let bindings parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/003_let_bindings.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/004_literals.as b/conformance/valid/004_literals.as new file mode 100644 index 0000000..2f0df4a --- /dev/null +++ b/conformance/valid/004_literals.as @@ -0,0 +1,26 @@ +// Conformance test: literal values + +fn test_literals() -> () { + // Integers + let dec = 42; + let neg = -10; + let hex = 0xFF; + let bin = 0b1010; + let oct = 0o777; + + // Floats + let pi = 3.14159; + let neg_float = -2.5; + let sci = 1.0e10; + + // Booleans + let t = true; + let f = false; + + // Strings + let s = "hello world"; + let escaped = "line\nbreak\ttab"; + + // Unit + let u = (); +} diff --git a/conformance/valid/004_literals.expected b/conformance/valid/004_literals.expected new file mode 100644 index 0000000..5654f55 --- /dev/null +++ b/conformance/valid/004_literals.expected @@ -0,0 +1,4 @@ +# Expected output: literal values parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/004_literals.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/005_functions.as b/conformance/valid/005_functions.as new file mode 100644 index 0000000..c15ff69 --- /dev/null +++ b/conformance/valid/005_functions.as @@ -0,0 +1,21 @@ +// Conformance test: function declarations + +fn simple() -> () { + () +} + +fn with_param(x: Int) -> Int { + x +} + +fn two_params(x: Int, y: Int) -> Int { + x + y +} + +fn generic[T](x: T) -> T { + x +} + +fn total_func(n: Nat) -> Nat { + n +} diff --git a/conformance/valid/005_functions.expected b/conformance/valid/005_functions.expected new file mode 100644 index 0000000..d37f329 --- /dev/null +++ b/conformance/valid/005_functions.expected @@ -0,0 +1,4 @@ +# Expected output: function declarations parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/005_functions.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/006_type_alias.as b/conformance/valid/006_type_alias.as new file mode 100644 index 0000000..778864c --- /dev/null +++ b/conformance/valid/006_type_alias.as @@ -0,0 +1,7 @@ +// Conformance test: type aliases + +type Meters = Int +type Kilograms = Float +type Pair[A, B] = (A, B) +type IntPair = Pair[Int, Int] +type StringList = List[String] diff --git a/conformance/valid/006_type_alias.expected b/conformance/valid/006_type_alias.expected new file mode 100644 index 0000000..0b4a717 --- /dev/null +++ b/conformance/valid/006_type_alias.expected @@ -0,0 +1,4 @@ +# Expected output: type aliases parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/006_type_alias.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/007_struct_def.as b/conformance/valid/007_struct_def.as new file mode 100644 index 0000000..c331290 --- /dev/null +++ b/conformance/valid/007_struct_def.as @@ -0,0 +1,21 @@ +// Conformance test: struct definitions + +type Point = { + x: Int, + y: Int +} + +type Person = { + name: String, + age: Int, + email: String +} + +type Generic[T] = { + value: T, + count: Int +} + +fn make_point(x: Int, y: Int) -> Point { + Point { x: x, y: y } +} diff --git a/conformance/valid/007_struct_def.expected b/conformance/valid/007_struct_def.expected new file mode 100644 index 0000000..4e02a0f --- /dev/null +++ b/conformance/valid/007_struct_def.expected @@ -0,0 +1,4 @@ +# Expected output: struct definitions parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/007_struct_def.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/008_enum_def.as b/conformance/valid/008_enum_def.as new file mode 100644 index 0000000..a81ac53 --- /dev/null +++ b/conformance/valid/008_enum_def.as @@ -0,0 +1,19 @@ +// Conformance test: enum definitions + +type Option[T] = + | None + | Some(T) + +type Result[T, E] = + | Ok(T) + | Err(E) + +type List[T] = + | Nil + | Cons(T, List[T]) + +type Color = + | Red + | Green + | Blue + | RGB(Int, Int, Int) diff --git a/conformance/valid/008_enum_def.expected b/conformance/valid/008_enum_def.expected new file mode 100644 index 0000000..2cb282a --- /dev/null +++ b/conformance/valid/008_enum_def.expected @@ -0,0 +1,4 @@ +# Expected output: enum definitions parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/008_enum_def.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/009_effect_def.as b/conformance/valid/009_effect_def.as new file mode 100644 index 0000000..93bc470 --- /dev/null +++ b/conformance/valid/009_effect_def.as @@ -0,0 +1,21 @@ +// Conformance test: effect definitions + +effect IO { + fn print(s: String); + fn println(s: String); + fn read_line() -> String; +} + +effect State[S] { + fn get() -> S; + fn put(s: S); +} + +effect Exn[E] { + fn throw(err: E) -> Never; +} + +effect Async { + fn await[T](promise: Promise[T]) -> T; + fn spawn[T](f: () -> T) -> Promise[T]; +} diff --git a/conformance/valid/009_effect_def.expected b/conformance/valid/009_effect_def.expected new file mode 100644 index 0000000..53a6644 --- /dev/null +++ b/conformance/valid/009_effect_def.expected @@ -0,0 +1,4 @@ +# Expected output: effect definitions parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/009_effect_def.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/010_pattern_match.as b/conformance/valid/010_pattern_match.as new file mode 100644 index 0000000..afaec00 --- /dev/null +++ b/conformance/valid/010_pattern_match.as @@ -0,0 +1,27 @@ +// Conformance test: pattern matching + +type Option[T] = + | None + | Some(T) + +fn unwrap_or[T](opt: Option[T], default: T) -> T { + match opt { + None => default, + Some(x) => x + } +} + +fn is_some[T](opt: Option[T]) -> Bool { + match opt { + None => false, + Some(_) => true + } +} + +fn describe_number(n: Int) -> String { + match n { + 0 => "zero", + 1 => "one", + _ => "many" + } +} diff --git a/conformance/valid/010_pattern_match.expected b/conformance/valid/010_pattern_match.expected new file mode 100644 index 0000000..5dc55fd --- /dev/null +++ b/conformance/valid/010_pattern_match.expected @@ -0,0 +1,4 @@ +# Expected output: pattern matching parses correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/010_pattern_match.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/011_rows.as b/conformance/valid/011_rows.as new file mode 100644 index 0000000..e4904db --- /dev/null +++ b/conformance/valid/011_rows.as @@ -0,0 +1,16 @@ +// Conformance test: row polymorphism + +fn get_x[..r](point: {x: Int, ..r}) -> Int { + point.x +} + +fn get_name[..r](entity: {name: String, ..r}) -> String { + entity.name +} + +fn with_id[..r](record: {..r}, id: Int) -> {id: Int, ..r} { + { id: id, ..record } +} + +type HasPosition[..r] = {x: Int, y: Int, ..r} +type HasName[..r] = {name: String, ..r} diff --git a/conformance/valid/011_rows.expected b/conformance/valid/011_rows.expected new file mode 100644 index 0000000..6d948f4 --- /dev/null +++ b/conformance/valid/011_rows.expected @@ -0,0 +1,4 @@ +# Expected output: row polymorphism parses correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/011_rows.as +# TODO: Generate actual golden output when toolchain is available diff --git a/conformance/valid/012_ownership.as b/conformance/valid/012_ownership.as new file mode 100644 index 0000000..636c658 --- /dev/null +++ b/conformance/valid/012_ownership.as @@ -0,0 +1,20 @@ +// Conformance test: ownership annotations + +type Resource = own { handle: Int } + +fn consume(r: own Resource) -> () { + // Takes ownership, resource is consumed +} + +fn borrow(r: ref Resource) -> Int { + // Immutable borrow + r.handle +} + +fn mutate(r: mut Resource) -> () { + // Mutable borrow +} + +fn create() -> own Resource { + Resource { handle: 42 } +} diff --git a/conformance/valid/012_ownership.expected b/conformance/valid/012_ownership.expected new file mode 100644 index 0000000..3021450 --- /dev/null +++ b/conformance/valid/012_ownership.expected @@ -0,0 +1,4 @@ +# Expected output: ownership annotations parse correctly +# Exit code: 0 +# Generate with: dune exec affinescript -- parse conformance/valid/012_ownership.as +# TODO: Generate actual golden output when toolchain is available diff --git a/dune-project b/dune-project index 714a3e3..6b5c4ba 100644 --- a/dune-project +++ b/dune-project @@ -15,7 +15,7 @@ (maintainers "hyperpolymath") -(license MIT) +(license "MIT OR AGPL-3.0-or-later") (documentation https://github.com/hyperpolymath/affinescript) diff --git a/justfile b/justfile index c24ef44..5508c33 100644 --- a/justfile +++ b/justfile @@ -1,33 +1,64 @@ -# SPDX-License-Identifier: AGPL-3.0-or-later -# Justfile - hyperpolymath standard task runner +# SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +# SPDX-FileCopyrightText: 2024-2025 hyperpolymath +# Justfile - hyperpolymath standard task runner for AffineScript +# Show available recipes default: @just --list -# Build the project +# Build the compiler build: - @echo "Building..." + dune build -# Run tests +# Run all tests test: - @echo "Testing..." + dune runtest -# Run lints +# Run format check (lint) lint: - @echo "Linting..." + dune fmt --check # Clean build artifacts clean: - @echo "Cleaning..." + dune clean -# Format code +# Format code in place fmt: - @echo "Formatting..." + dune fmt -# Run all checks +# Run all checks (lint + test) check: lint test +# Build documentation +doc: + dune build @doc + +# Run the lexer on a file +lex FILE: + dune exec affinescript -- lex {{FILE}} + +# Run the parser on a file +parse FILE: + dune exec affinescript -- parse {{FILE}} + +# Run conformance tests +conformance: + dune runtest conformance + +# Verify golden path (smoke test) +golden-path: + @echo "=== Golden Path Verification ===" + @echo "1. Building..." + dune build + @echo "2. Running tests..." + dune runtest + @echo "3. Testing lexer on hello.as..." + dune exec affinescript -- lex examples/hello.as + @echo "4. Testing parser on ownership.as..." + dune exec affinescript -- parse examples/ownership.as + @echo "=== Golden Path Complete ===" + # Prepare a release release VERSION: @echo "Releasing {{VERSION}}..." - + @echo "TODO: implement release workflow" diff --git a/lib/ast.ml b/lib/ast.ml index 025f5f4..48a5f0e 100644 --- a/lib/ast.ml +++ b/lib/ast.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Abstract Syntax Tree for AffineScript *) (** Identifiers *) diff --git a/lib/borrow.ml b/lib/borrow.ml index 90bd26a..1aa68c4 100644 --- a/lib/borrow.ml +++ b/lib/borrow.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Borrow checker for ownership verification. diff --git a/lib/error.ml b/lib/error.ml index 663e09c..fb39911 100644 --- a/lib/error.ml +++ b/lib/error.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Error handling and diagnostics for AffineScript *) (** Error severity levels *) diff --git a/lib/lexer.ml b/lib/lexer.ml index 4c66a27..091e0e4 100644 --- a/lib/lexer.ml +++ b/lib/lexer.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Lexer for AffineScript using sedlex *) open Token diff --git a/lib/parse_driver.ml b/lib/parse_driver.ml index 0726447..7239de3 100644 --- a/lib/parse_driver.ml +++ b/lib/parse_driver.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Parser driver - bridges sedlex lexer with Menhir parser *) (** Exception for parse errors *) diff --git a/lib/quantity.ml b/lib/quantity.ml index d03e544..99628e4 100644 --- a/lib/quantity.ml +++ b/lib/quantity.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Quantity checking for QTT. diff --git a/lib/resolve.ml b/lib/resolve.ml index dc406ce..ab01a4d 100644 --- a/lib/resolve.ml +++ b/lib/resolve.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Name resolution pass. diff --git a/lib/span.ml b/lib/span.ml index 13797c2..692b77a 100644 --- a/lib/span.ml +++ b/lib/span.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Source location tracking for error messages *) type pos = { diff --git a/lib/symbol.ml b/lib/symbol.ml index 12d97b5..1d6a8f2 100644 --- a/lib/symbol.ml +++ b/lib/symbol.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Symbol table for name resolution. diff --git a/lib/token.ml b/lib/token.ml index 10c9d05..37a887f 100644 --- a/lib/token.ml +++ b/lib/token.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Token types for the AffineScript lexer *) type t = diff --git a/lib/typecheck.ml b/lib/typecheck.ml index ec85953..53c9f45 100644 --- a/lib/typecheck.ml +++ b/lib/typecheck.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Bidirectional type checker. diff --git a/lib/types.ml b/lib/types.ml index fdfac52..cc9c8ea 100644 --- a/lib/types.ml +++ b/lib/types.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Internal type representation for type checking. diff --git a/lib/unify.ml b/lib/unify.ml index b975e67..6461ac7 100644 --- a/lib/unify.ml +++ b/lib/unify.ml @@ -1,5 +1,5 @@ -(* SPDX-License-Identifier: Apache-2.0 OR MIT *) -(* Copyright 2024 AffineScript Contributors *) +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) (** Type unification. diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index bb7de0b..c971227 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -1,5 +1,6 @@ -# SPDX-License-Identifier: Apache-2.0 OR MIT -# Copyright 2024 AffineScript Contributors +# SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +# SPDX-FileCopyrightText: 2024-2025 hyperpolymath +# Copyright 2024-2025 AffineScript Contributors [package] name = "affinescript-runtime" @@ -7,7 +8,7 @@ version = "0.1.0" edition = "2021" authors = ["AffineScript Contributors"] description = "Runtime library for AffineScript, compiled to WebAssembly" -license = "Apache-2.0 OR MIT" +license = "MIT OR AGPL-3.0-or-later" repository = "https://github.com/hyperpolymath/affinescript" keywords = ["affinescript", "runtime", "wasm"] categories = ["wasm", "no-std"] diff --git a/runtime/src/alloc.rs b/runtime/src/alloc.rs index e1b4bd0..6bf9af1 100644 --- a/runtime/src/alloc.rs +++ b/runtime/src/alloc.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! Memory Allocator for AffineScript Runtime //! diff --git a/runtime/src/effects.rs b/runtime/src/effects.rs index 54edd99..0a7a9ba 100644 --- a/runtime/src/effects.rs +++ b/runtime/src/effects.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! Effect Handling Runtime for AffineScript //! diff --git a/runtime/src/ffi.rs b/runtime/src/ffi.rs index 85d618c..b8967a3 100644 --- a/runtime/src/ffi.rs +++ b/runtime/src/ffi.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! Foreign Function Interface for AffineScript Runtime //! diff --git a/runtime/src/gc.rs b/runtime/src/gc.rs index 63191a0..49d1f90 100644 --- a/runtime/src/gc.rs +++ b/runtime/src/gc.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! Optional Garbage Collector for AffineScript Runtime //! diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a60639c..d39373b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! AffineScript Runtime Library //! diff --git a/runtime/src/panic.rs b/runtime/src/panic.rs index efe6d97..8b9566d 100644 --- a/runtime/src/panic.rs +++ b/runtime/src/panic.rs @@ -1,5 +1,5 @@ -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright 2024 AffineScript Contributors +// SPDX-License-Identifier: MIT OR AGPL-3.0-or-later +// SPDX-FileCopyrightText: 2024-2025 hyperpolymath //! Panic Handling for AffineScript Runtime //! diff --git a/test/test_golden.ml b/test/test_golden.ml index cc9b069..03af3d1 100644 --- a/test/test_golden.ml +++ b/test/test_golden.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Golden tests for parser/AST output *) open Affinescript diff --git a/test/test_lexer.ml b/test/test_lexer.ml index 254f959..7b8a0a5 100644 --- a/test/test_lexer.ml +++ b/test/test_lexer.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Lexer tests *) open Affinescript diff --git a/test/test_parser.ml b/test/test_parser.ml index 362bc76..a9faf4d 100644 --- a/test/test_parser.ml +++ b/test/test_parser.ml @@ -1,3 +1,6 @@ +(* SPDX-License-Identifier: MIT OR AGPL-3.0-or-later *) +(* SPDX-FileCopyrightText: 2024-2025 hyperpolymath *) + (** Parser tests *) open Affinescript