Skip to content

Commit 27571a3

Browse files
hyperpolymathclaude
andcommitted
compiler: begin ReScript->AffineScript migration — port Types.res
First module of the compiler's migration to AffineScript (the Hyperpolymath language policy bans ReScript). Adds compiler/src/Types.affine, a faithful port of compiler/src/Types.res, verified green with `affinescript check`: - all token / AST / error / stability types (structs + enums + match) - ReScript inline-record variants lowered to positional constructor args - token variants Float/String renamed FloatTok/StringTok (reserved type keywords in AffineScript) - Echo types (TyEcho / TyEchoResidue) shaped Trope-IR-ready per docs/Trope-Particularity-Integration.adoc (Phase 0) - make_default_state, stability_impact, calculate_stability, error_code_to_string Toolchain, so the .affine sources are reproducibly CI-verifiable: - scripts/install-affinescript-toolchain.sh — builds the AffineScript compiler from distro OCaml packages (independent of opam.ocaml.org) + installs the binary and stdlib under a discoverable share/ path - verification/check-affinescript.sh — typechecks all compiler/src/*.affine Types.res is retained until its dependents migrate; format_diagnostic is deferred pending the string / affine-borrow pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0195yA45jSSP7YDPwJSpw4bM
1 parent a3c0df8 commit 27571a3

3 files changed

Lines changed: 321 additions & 0 deletions

File tree

‎compiler/src/Types.affine‎

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// Types.affine — core type definitions for Error-Lang
3+
// Ported from compiler/src/Types.res. Port conventions:
4+
// * ReScript inline-record variants -> positional constructor args.
5+
// * array<T> -> [T]; option<T> -> Option<T>; dict<k,v> -> Dict<k,v>; tuples kept.
6+
// * Token variants `Float`/`String` renamed `FloatTok`/`StringTok`
7+
// (`Float`/`String` are reserved type keywords in AffineScript).
8+
9+
use prelude::*;
10+
11+
struct Position {
12+
line: Int,
13+
column: Int,
14+
offset: Int
15+
}
16+
17+
struct Location {
18+
start: Position,
19+
end_: Position,
20+
file: String
21+
}
22+
23+
enum TokenType {
24+
// Keywords
25+
Main, End, Let, Mutable, Function, Struct, If, Elseif, Else, While, For, In,
26+
Break, Continue, Return, And, Or, Not, True, False, Nil, Gutter, Fn,
27+
// Types
28+
TInt, TFloat, TString, TBool, TArray, TEcho, TEchoR,
29+
// Literals
30+
Integer(Int),
31+
FloatTok(Float), // ReScript Float(float)
32+
StringTok(String), // ReScript String(string)
33+
Identifier(String),
34+
// Operators
35+
Plus, Minus, Star, Slash, Percent, EqualEqual, BangEqual, Less, Greater,
36+
LessEqual, GreaterEqual, Ampersand, Pipe, Caret, Tilde, LessLess, GreaterGreater,
37+
Equal, Arrow, Question, Colon,
38+
// Delimiters
39+
LParen, RParen, LBracket, RBracket, LBrace, RBrace, Comma, Dot,
40+
// Special
41+
Newline, EOF, Error(String)
42+
}
43+
44+
struct Token {
45+
type_: TokenType,
46+
lexeme: String,
47+
loc: Location
48+
}
49+
50+
// ============================================
51+
// AST
52+
// ============================================
53+
54+
enum Expr {
55+
IntLit(Int, Location),
56+
FloatLit(Float, Location),
57+
StringLit(String, Location),
58+
BoolLit(Bool, Location),
59+
NilLit(Location),
60+
Ident(String, Location),
61+
Array([Expr], Location),
62+
Binary(Expr, BinaryOp, Expr, Location),
63+
Unary(UnaryOp, Expr, Location),
64+
Call(Expr, [Expr], Location),
65+
Index(Expr, Expr, Location),
66+
Member(Expr, String, Location),
67+
Ternary(Expr, Expr, Expr, Location),
68+
Lambda([Param], Option<TypeExpr>, LambdaBody, Location)
69+
}
70+
71+
enum BinaryOp {
72+
Add, Sub, Mul, Div, Mod,
73+
Eq, Neq, Lt, Gt, Lte, Gte,
74+
BAnd, BOr, BXor, Shl, Shr,
75+
LAnd, LOr
76+
}
77+
78+
enum UnaryOp { Neg, LNot, BNot }
79+
80+
struct Param {
81+
name: String,
82+
type_: Option<TypeExpr>,
83+
loc: Location
84+
}
85+
86+
enum TypeExpr {
87+
TyInt,
88+
TyFloat,
89+
TyString,
90+
TyBool,
91+
TyArray(TypeExpr),
92+
// Echo types (Trope-IR-ready, see docs/Trope-Particularity-Integration.adoc):
93+
// TyEcho ~ Trope[Phi] (retained witness)
94+
// TyEchoResidue ~ FloatingQuality (witness severed)
95+
TyEcho(Option<TypeExpr>, Option<TypeExpr>),
96+
TyEchoResidue(Option<TypeExpr>, Option<TypeExpr>),
97+
TyIdent(String)
98+
}
99+
100+
enum LambdaBody {
101+
LambdaExpr(Expr),
102+
LambdaBlock([Stmt])
103+
}
104+
105+
enum Stmt {
106+
// inline records -> positional: (mutable_, name, type_, value, loc)
107+
LetStmt(Bool, String, Option<TypeExpr>, Expr, Location),
108+
// (target, value, loc)
109+
AssignStmt(Expr, Expr, Location),
110+
// (cond, then_, elseifs, else_, loc)
111+
IfStmt(Expr, [Stmt], [(Expr, [Stmt])], Option<[Stmt]>, Location),
112+
// (cond, body, loc)
113+
WhileStmt(Expr, [Stmt], Location),
114+
// (var, iter, body, loc)
115+
ForStmt(String, Expr, [Stmt], Location),
116+
// (value, loc)
117+
ReturnStmt(Option<Expr>, Location),
118+
BreakStmt(Location),
119+
ContinueStmt(Location),
120+
// (println, args, loc)
121+
PrintStmt(Bool, [Expr], Location),
122+
// (tokens, recovered, loc)
123+
GutterBlock([Token], Bool, Location),
124+
ExprStmt(Expr)
125+
}
126+
127+
enum Decl {
128+
// (name, params, returnType, body, loc)
129+
FunctionDecl(String, [Param], Option<TypeExpr>, [Stmt], Location),
130+
// (name, fields, loc)
131+
StructDecl(String, [(String, TypeExpr)], Location),
132+
// (body, loc)
133+
MainBlock([Stmt], Location),
134+
StmtDecl(Stmt)
135+
}
136+
137+
struct Program {
138+
declarations: [Decl],
139+
loc: Location
140+
}
141+
142+
// ============================================
143+
// Errors
144+
// ============================================
145+
146+
enum ErrorCode {
147+
E0001, E0002, E0003, E0004, E0005, E0006, E0007, E0008, E0009, E0010
148+
}
149+
150+
struct Diagnostic {
151+
code: ErrorCode,
152+
message: String,
153+
loc: Location,
154+
runNumber: Int,
155+
hint: Option<String>
156+
}
157+
158+
// ============================================
159+
// Runtime state & stability
160+
// ============================================
161+
162+
enum StabilityFactor {
163+
MutableState(Int, Int), // mutations, readers
164+
TypeInstability(Int), // reassignments
165+
NullPropagation(Int), // depth
166+
GlobalState(Int, Int), // mutations, dependencies
167+
UnhandledError(Int), // paths
168+
AlgorithmComplexity(Float), // time_ms
169+
MemoryLeak(Int), // bytes
170+
RaceCondition(Int) // conflicts
171+
}
172+
173+
struct StabilityReport {
174+
score: Int,
175+
factors: [StabilityFactor],
176+
breakdown: Dict<String, Int>,
177+
recommendations: [String]
178+
}
179+
180+
struct RuntimeState {
181+
runCounter: Int,
182+
stabilityScore: Int,
183+
lastError: Option<ErrorCode>,
184+
seed: Int,
185+
stabilityFactors: [StabilityFactor],
186+
discoveredRules: [String],
187+
historicalRuns: [Int]
188+
}
189+
190+
fn make_default_state() -> RuntimeState {
191+
#{
192+
runCounter: 0,
193+
stabilityScore: 100,
194+
lastError: None,
195+
seed: 0,
196+
stabilityFactors: [],
197+
discoveredRules: [],
198+
historicalRuns: []
199+
}
200+
}
201+
202+
// Stability impact (non-positive), mirrors Types.res `stabilityImpact`.
203+
fn stability_impact(factor: StabilityFactor) -> Int {
204+
match factor {
205+
MutableState(mutations, readers) => -(10 * mutations + 5 * readers),
206+
TypeInstability(reassignments) => -(15 * reassignments),
207+
NullPropagation(depth) => -(20 * depth),
208+
GlobalState(mutations, dependencies) => -(30 * mutations + 5 * dependencies),
209+
UnhandledError(paths) => -(25 * paths),
210+
AlgorithmComplexity(time_ms) => -trunc(time_ms / 10.0),
211+
MemoryLeak(bytes) => -(10 * (bytes / 1024)),
212+
RaceCondition(conflicts) => -(40 * conflicts)
213+
}
214+
}
215+
216+
// mirrors Types.res `calculateStability`: max(0, 100 + sum(impacts))
217+
fn calculate_stability(factors: [StabilityFactor]) -> Int {
218+
let penalties = fold(factors, 0, |acc, x| acc + stability_impact(x));
219+
max(0, 100 + penalties)
220+
}
221+
222+
fn error_code_to_string(code: ErrorCode) -> String {
223+
match code {
224+
E0001 => "E0001",
225+
E0002 => "E0002",
226+
E0003 => "E0003",
227+
E0004 => "E0004",
228+
E0005 => "E0005",
229+
E0006 => "E0006",
230+
E0007 => "E0007",
231+
E0008 => "E0008",
232+
E0009 => "E0009",
233+
E0010 => "E0010"
234+
}
235+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# install-affinescript-toolchain.sh — build & install the AffineScript compiler
6+
# (hyperpolymath/affinescript, the OCaml/dune compiler) so error-lang's `.affine`
7+
# sources — which replace the legacy ReScript per the Hyperpolymath language
8+
# policy — can be typechecked and compiled.
9+
#
10+
# Why build from distro OCaml packages instead of opam: some CI/network policies
11+
# block opam.ocaml.org. Every dependency below is available from the Debian/Ubuntu
12+
# archive at a version satisfying affinescript's dune-project constraints
13+
# (notably ocaml-dune 3.14 == `(lang dune 3.14)`).
14+
#
15+
# Network note: this clones github.com/hyperpolymath/affinescript directly; run it
16+
# where GitHub is reachable (a normal CI runner or dev box).
17+
set -euo pipefail
18+
19+
AFFINE_REPO="${AFFINE_REPO:-https://github.com/hyperpolymath/affinescript}"
20+
AFFINE_SRC="${AFFINE_SRC:-${TMPDIR:-/tmp}/affinescript}"
21+
PREFIX="${PREFIX:-/usr/local}"
22+
SUDO="$(command -v sudo || true)"
23+
24+
# 1. OCaml toolchain + AffineScript build dependencies.
25+
$SUDO apt-get update
26+
$SUDO apt-get install -y \
27+
ocaml-dune menhir libmenhir-ocaml-dev libsedlex-ocaml-dev \
28+
libppx-deriving-ocaml-dev libppx-sexp-conv-ocaml-dev libsexplib0-ocaml-dev \
29+
libfmt-ocaml-dev libcmdliner-ocaml-dev libyojson-ocaml-dev \
30+
libppxlib-ocaml-dev libjs-of-ocaml-dev
31+
32+
# 2. Fetch + build the compiler binary.
33+
[ -d "$AFFINE_SRC/.git" ] || git clone --depth 1 "$AFFINE_REPO" "$AFFINE_SRC"
34+
( cd "$AFFINE_SRC" && dune build bin/main.exe )
35+
36+
# 3. Install the binary + stdlib. The module loader discovers the stdlib at
37+
# <binary_dir>/../share/affinescript/stdlib, so this needs no env var.
38+
$SUDO install -m755 "$AFFINE_SRC/_build/default/bin/main.exe" "$PREFIX/bin/affinescript"
39+
$SUDO mkdir -p "$PREFIX/share/affinescript"
40+
$SUDO rm -rf "$PREFIX/share/affinescript/stdlib"
41+
$SUDO cp -r "$AFFINE_SRC/stdlib" "$PREFIX/share/affinescript/stdlib"
42+
43+
echo "Installed: $(command -v affinescript)"
44+
affinescript check "$AFFINE_SRC/examples/hello.affine" || true
45+
echo "AffineScript toolchain installed under $PREFIX."

‎verification/check-affinescript.sh‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: MPL-2.0
3+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# check-affinescript.sh — typecheck every ported `.affine` compiler source with
6+
# the AffineScript compiler. Companion to verification/check-proofs.sh.
7+
# Requires `affinescript` on PATH (see scripts/install-affinescript-toolchain.sh).
8+
set -euo pipefail
9+
10+
cd "$(dirname "$0")/.."
11+
12+
if ! command -v affinescript >/dev/null 2>&1; then
13+
echo "affinescript not found — run scripts/install-affinescript-toolchain.sh" >&2
14+
exit 127
15+
fi
16+
17+
shopt -s nullglob
18+
sources=(compiler/src/*.affine)
19+
if [ ${#sources[@]} -eq 0 ]; then
20+
echo "no .affine sources yet (ReScript->AffineScript migration in progress)."
21+
exit 0
22+
fi
23+
24+
fail=0
25+
for f in "${sources[@]}"; do
26+
printf 'checking %-28s ... ' "$(basename "$f")"
27+
if affinescript check "$f" >/tmp/as_check.out 2>&1; then
28+
echo ok
29+
else
30+
echo FAIL
31+
cat /tmp/as_check.out
32+
fail=1
33+
fi
34+
done
35+
36+
if [ "$fail" -eq 0 ]; then
37+
echo "all .affine sources check."
38+
else
39+
echo "affinescript check failures." >&2
40+
exit 1
41+
fi

0 commit comments

Comments
 (0)