Step 14: source locations — pos-to-loc, error-loc, sx-parse-loc — 15 tests

Pure SX layer: pos-to-loc (offset→line/col), error-loc (parse result→loc),
format-parse-error (human-readable error with source context line).
OCaml platform: cst_to_ast_loc (CST spans→loc dicts), sx-parse-loc
primitive (parse with locations), source-loc accessor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-11 08:03:45 +00:00
parent 36ae0384ae
commit 99c5c44cc1
4 changed files with 224 additions and 0 deletions

View File

@@ -164,6 +164,24 @@ let make_test_env () =
| [String s] -> List (parse_all s)
| _ -> raise (Eval_error "sx-parse: expected string"));
bind "sx-parse-loc" (fun args ->
match args with
| [String s] ->
let cst = Sx_parser.parse_all_cst s in
List (Sx_cst.cst_to_ast_loc s cst.nodes)
| _ -> raise (Eval_error "sx-parse-loc: expected string"));
bind "source-loc" (fun args ->
match args with
| [Dict d] ->
let line = try Hashtbl.find d "line" with Not_found -> Nil in
let col = try Hashtbl.find d "col" with Not_found -> Nil in
let ld = Sx_types.make_dict () in
Sx_types.dict_set ld "line" line;
Sx_types.dict_set ld "col" col;
Dict ld
| _ -> Nil);
bind "sx-parse-one" (fun args ->
match args with
| [String s] ->

View File

@@ -104,6 +104,33 @@ let rec cst_to_ast = function
Dict d
(** Convert character offset to line/col (1-based lines, 0-based cols) *)
let offset_to_loc src offset =
let line = ref 1 and col = ref 0 in
for i = 0 to min (offset - 1) (String.length src - 1) do
if src.[i] = '\n' then (incr line; col := 0)
else col := !col + 1
done;
(!line, !col)
(** CST → AST with source location dicts ({:form value :line N :col N}) *)
let cst_to_ast_loc src nodes =
List.map (fun node ->
let span = match node with
| CstAtom { span; _ } -> span
| CstList { span; _ } -> span
| CstDict { span; _ } -> span
in
let value = cst_to_ast node in
let (line, col) = offset_to_loc src span.start_offset in
let d = make_dict () in
dict_set d "form" value;
dict_set d "line" (Number (float_of_int line));
dict_set d "col" (Number (float_of_int col));
Dict d
) nodes
(** {1 CST editing — apply AST-level edits back to the CST} *)
(** Replace the CST node at [path] with [new_source], preserving the