Skip to content

Commit d2811a9

Browse files
fix(faces): strip trailing ; from module name in JaffaScript imports (#120)
`transform_import` extracted the module name by stripping surrounding quote characters with `String.sub mod_part 1 (len - 2)`. When the import ends with a semicolon (the normal JS style: `import { x } from \"m\";`), `mod_part` is `\"m\";` — the last character is `;`, not `\"`, so the slice kept the trailing `\"` in the module name and produced `use m\"::{x};` instead of `use m::{x};`. Fix: in all three import branches (`import { x }`, `import *`, and `import name`), strip a trailing `;` from `mod_part` / `quoted` before the quote-stripping slice. Updated `examples/faces/hello-jaffa.affine` to include `import { println } from \"io\";` so the import path is exercised by the regression test. Updated snapshot.
1 parent 872d13c commit d2811a9

3 files changed

Lines changed: 24 additions & 7 deletions

File tree

examples/faces/hello-jaffa.affine

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
//
44
// JaffaScript face. Distinctive features exercised: `function` keyword,
55
// `const`/`let` bindings (let lowers to let mut), brace-delimited
6-
// blocks, `===` equality.
6+
// blocks, `===` equality, `import { x } from "m";` imports.
77
// face: jaffascript
88

9+
import { println } from "io";
10+
911
effect IO {
1012
fn println(s: String) -> ();
1113
}

lib/js_face.ml

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
[{ expr }]. Full multi-line arrow bodies are a follow-up task.
4040
*)
4141

42-
(* ─── Character helpers ────────────────────────────────────────────────── *)
42+
(* ─── Character helpers ────────────────────────────────────────────── *)
4343

4444
let is_id_char c =
4545
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
@@ -113,7 +113,7 @@ let ends_with s suffix =
113113
let sl = String.length s and tl = String.length suffix in
114114
sl >= tl && String.sub s (sl - tl) tl = suffix
115115

116-
(* ─── Import / export handling ─────────────────────────────────────────── *)
116+
(* ─── Import / export handling ────────────────────────────────────────────── *)
117117

118118
(** Transform JavaScript import declarations to AffineScript use declarations.
119119
Examples:
@@ -136,6 +136,11 @@ let transform_import line =
136136
let rest = String.trim rest in
137137
if starts_with rest "from " then begin
138138
let mod_part = String.trim (String.sub rest 5 (String.length rest - 5)) in
139+
(* Strip a trailing `;` before stripping the surrounding quotes. *)
140+
let mod_part =
141+
if ends_with mod_part ";" then String.sub mod_part 0 (String.length mod_part - 1)
142+
else mod_part
143+
in
139144
let mod_name = String.sub mod_part 1 (String.length mod_part - 2) in (* strip quotes *)
140145
let mod_name = String.map (fun c -> if c = '/' || c = '-' then '_' else c) mod_name in
141146
let names = String.trim names_raw in
@@ -151,13 +156,21 @@ let transform_import line =
151156
if starts_with rest "from " then
152157
(* import * from "module" — just bring in module *)
153158
let mod_part = String.trim (String.sub rest 5 (String.length rest - 5)) in
159+
let mod_part =
160+
if ends_with mod_part ";" then String.sub mod_part 0 (String.length mod_part - 1)
161+
else mod_part
162+
in
154163
let mod_name = String.sub mod_part 1 (String.length mod_part - 2) in
155164
let mod_name = String.map (fun c -> if c = '/' || c = '-' then '_' else c) mod_name in
156165
Printf.sprintf "use %s;" mod_name
157166
else begin
158167
(* import name from "module" *)
159168
match String.split_on_char ' ' rest with
160169
| name :: "from" :: quoted :: _ ->
170+
let quoted =
171+
if ends_with quoted ";" then String.sub quoted 0 (String.length quoted - 1)
172+
else quoted
173+
in
161174
let mod_name = String.sub quoted 1 (String.length quoted - 2) in
162175
let mod_name = String.map (fun c -> if c = '/' || c = '-' then '_' else c) mod_name in
163176
Printf.sprintf "use %s::%s;" mod_name name
@@ -176,7 +189,7 @@ let strip_export line =
176189
String.sub line 7 (String.length line - 7)
177190
else line
178191

179-
(* ─── Function / variable declarations ────────────────────────────────── *)
192+
(* ─── Function / variable declarations ──────────────────────────────────────────── *)
180193

181194
(** Transform a [const]/[let]/[var] binding line. *)
182195
let transform_var_decl line =
@@ -251,7 +264,7 @@ let transform_arrow_body line =
251264
before ^ "{ " ^ after ^ " }"
252265
end else line
253266

254-
(* ─── Line-by-line transform ────────────────────────────────────────────── *)
267+
(* ─── Line-by-line transform ────────────────────────────────────────────────── *)
255268

256269
(** Transform a single source line from JS-face to canonical AffineScript.
257270
Order matters: export stripping before function detection, variable
@@ -287,7 +300,7 @@ let transform_line line =
287300
end
288301
end
289302

290-
(* ─── File-level entry points ─────────────────────────────────────────── *)
303+
(* ─── File-level entry points ────────────────────────────────────────────── *)
291304

292305
(** Transform a full JS-face source string to canonical AffineScript. *)
293306
let transform_source source =

tests/faces/hello-jaffa.expected.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
//
44
// JaffaScript face. Distinctive features exercised: `function` keyword,
55
// `const`/`let` bindings (let lowers to let mut), brace-delimited
6-
// blocks, `==` equality.
6+
// blocks, `==` equality, `import { x } from "m";` imports.
77
// face: jaffascript
88

9+
use io::{println};
10+
911
effect IO {
1012
fn println(s: String) -> ();
1113
}

0 commit comments

Comments
 (0)