Skip to content

Commit 9866727

Browse files
hyperpolymathclaude
andcommitted
feat(python-face): ADR-010 Priority 2 — Python-style surface syntax
Adds lib/python_face.ml: a source-level text transformer that maps Python-style AffineScript to canonical AffineScript before lex+parse. The compiler is entirely face-agnostic; only python_face.ml knows about the transformation. Supported surface mappings def → fn, True/False/None → true/false/() and/or/not → &&/||/! class → type, pass → () # comment → // comment import a.b → use a::b, from a import b → use a::b INDENT/DEDENT → {}/} (indentation-based blocks) colon-at-EOL → { (block-opening) else:/elif COND: → else {/else if COND { (merged with dedent }) statement lines get automatic ; CLI: `affinescript parse --face python FILE` `affinescript preview-python FILE` (debug: show canonical text) 5 new E2E tests (Python-Face suite). 73/73 total: 0 regressions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4322963 commit 9866727

6 files changed

Lines changed: 482 additions & 11 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ status = "active"
99

1010
[project-context]
1111
name = "affinescript"
12-
completion-percentage = 80
13-
phase = "adr-009-conformance-complete"
12+
completion-percentage = 82
13+
phase = "python-face-complete"
1414
tagline = "Rust-inspired language with affine types, dependent types, row polymorphism, and extensible effects"
15-
note = "ADR-009 conformance target reached 2026-04-11: 12/12 valid conformance tests parse and match expected output (was 0/12). Six parser/lexer issues fixed: float scientific notation, optional type-decl semicolons, enum leading-pipe syntax, effect op type params, row variable support (type + expression contexts), keyword field names. 68/68 E2E tests: 0 regressions. Next: Priority 2 Faces Architecture (ASS / parser layer faces)."
15+
note = "Python-face (ADR-010 Priority 2) shipped 2026-04-11. lib/python_face.ml: source-level transformer mapping Python surface syntax to canonical AffineScript before lex+parse. Mappings: def→fn, True/False/None→true/false/(), and/or/not→&&/||/!, class→type, pass→(), #→//, import→use, INDENT/DEDENT→{}/}, colon-at-EOL block openers, else/elif chains. `--face python` flag on `parse` subcommand. 5 new Python-face E2E tests. 73/73 E2E tests: 0 regressions. Previous: ADR-009 conformance complete 2026-04-11 (12/12 valid conformance tests). Next: Priority 3 — LSP Phase B (completion, hover) or borrow checker Phase 3."
1616

1717
[components]
1818
lexer = "complete-minus-affine-tokens (ZERO/ONE tokens for quantity literals NOT emitted; blocks QTT surface syntax). float_exponent now correctly named sub-regexp — scientific notation crash fixed 2026-04-11."
@@ -31,8 +31,8 @@ lsp-phase-d = "pending"
3131
stdlib = "95% (5 stubs remain as extern builtins)"
3232

3333
[stats]
34-
compiler-loc = 12424
35-
compiler-modules = 38
34+
compiler-loc = 12712
35+
compiler-modules = 39
3636
lsp-files = 5
3737
test-files = 54
3838

bin/main.ml

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,21 @@ let lex_file path =
5959
Format.eprintf "@[<v>%s:%d:%d: error: %s@]@." path pos.line pos.col msg;
6060
`Error (false, "Lexer error")
6161

62+
(** Preview the Python-face text transform (debug tool). *)
63+
let preview_python_transform path =
64+
let ch = open_in_bin path in
65+
let s = really_input_string ch (in_channel_length ch) in
66+
close_in ch;
67+
print_string (Affinescript.Python_face.preview_transform s);
68+
`Ok ()
69+
6270
(** Parse a file and print AST (no --json support). *)
63-
let parse_file path =
71+
let parse_file face path =
6472
try
65-
let prog = Affinescript.Parse_driver.parse_file path in
73+
let prog = match face with
74+
| `Canonical -> Affinescript.Parse_driver.parse_file path
75+
| `Python -> Affinescript.Python_face.parse_file_python path
76+
in
6677
Format.printf "%s@." (Affinescript.Ast.show_program prog);
6778
`Ok ()
6879
with
@@ -433,6 +444,16 @@ let wasm_gc_arg =
433444
Requires a runtime that supports the GC proposal: V8/Chrome ≥ 119, \
434445
SpiderMonkey/Firefox ≥ 120, or Wasmtime with --wasm-features gc.")
435446

447+
(** Shared --face flag: select the parser surface-syntax face. *)
448+
let face_arg =
449+
let faces = Arg.enum [("canonical", `Canonical); ("python", `Python)] in
450+
Arg.(value & opt faces `Canonical & info ["face"]
451+
~docv:"FACE"
452+
~doc:"Parser face (surface-syntax variant). $(docv) must be $(b,canonical) \
453+
(default, standard AffineScript) or $(b,python) (Python-style syntax: \
454+
indentation-based blocks, $(b,def)/$(b,True)/$(b,False)/$(b,None)/\
455+
$(b,and)/$(b,or)/$(b,not) etc. — compiled to the same canonical AST).")
456+
436457
let lex_cmd =
437458
let doc = "Lex a file and print tokens" in
438459
let info = Cmd.info "lex" ~doc in
@@ -441,7 +462,7 @@ let lex_cmd =
441462
let parse_cmd =
442463
let doc = "Parse a file and print AST" in
443464
let info = Cmd.info "parse" ~doc in
444-
Cmd.v info Term.(ret (const parse_file $ path_arg))
465+
Cmd.v info Term.(ret (const parse_file $ face_arg $ path_arg))
445466

446467
let check_cmd =
447468
let doc = "Type check a file" in
@@ -473,10 +494,15 @@ let lint_cmd =
473494
let info = Cmd.info "lint" ~doc in
474495
Cmd.v info Term.(ret (const lint_file $ json_arg $ path_arg))
475496

497+
let preview_python_cmd =
498+
let doc = "Preview the Python-face text transform (debug)" in
499+
let info = Cmd.info "preview-python" ~doc in
500+
Cmd.v info Term.(ret (const preview_python_transform $ path_arg))
501+
476502
let default_cmd =
477503
let doc = "The AffineScript compiler" in
478504
let info = Cmd.info "affinescript" ~version ~doc in
479505
let default = Term.(ret (const (`Help (`Pager, None)))) in
480-
Cmd.group info ~default [lex_cmd; parse_cmd; check_cmd; eval_cmd; repl_cmd; compile_cmd; fmt_cmd; lint_cmd]
506+
Cmd.group info ~default [lex_cmd; parse_cmd; check_cmd; eval_cmd; repl_cmd; compile_cmd; fmt_cmd; lint_cmd; preview_python_cmd]
481507

482508
let () = exit (Cmd.eval default_cmd)

lib/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
(name affinescript)
33
(public_name affinescript)
44
(modes byte native)
5-
(modules ast codegen codegen_gc desugar_traits effect error error_collector error_formatter formatter interp julia_codegen json_output lexer linter module_loader opt parse_driver parse parser parser_errors quantity resolve span symbol token trait typecheck types unify value wasm wasm_encode wasm_gc wasm_gc_encode wasi_runtime)
5+
(modules ast codegen codegen_gc desugar_traits effect error error_collector error_formatter formatter interp julia_codegen json_output lexer linter module_loader opt parse_driver parse parser parser_errors python_face quantity resolve span symbol token trait typecheck types unify value wasm wasm_encode wasm_gc wasm_gc_encode wasi_runtime)
66
(libraries str unix sedlex fmt menhirLib yojson)
77
(preprocess
88
(pps ppx_deriving.show ppx_deriving.eq ppx_deriving.ord sedlex.ppx)))

0 commit comments

Comments
 (0)