Skip to content

Commit d523bed

Browse files
committed
refactor(cram): lex locations in cram tests
We add support for lexing locations in cram tests. Signed-off-by: Ali Caglayan <[email protected]>
1 parent 21c3e94 commit d523bed

File tree

5 files changed

+495
-94
lines changed

5 files changed

+495
-94
lines changed

src/dune_rules/cram/cram_exec.ml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,16 @@ let cram_stanzas =
6363
let rec loop acc conflict_state =
6464
match Cram_lexer.block lexbuf with
6565
| None -> List.rev acc
66-
| Some s ->
66+
| Some (loc, block) ->
6767
let conflict_state =
68-
match s with
68+
match block with
6969
| Command _ -> conflict_state
7070
| Comment lines ->
7171
(match conflict_markers with
7272
| Ignore -> conflict_state
7373
| Error -> List.fold_left lines ~init:conflict_state ~f:find_conflict)
7474
in
75-
loop (s :: acc) conflict_state
75+
loop ((loc, block) :: acc) conflict_state
7676
in
7777
loop [] `No_conflict
7878
;;
@@ -485,7 +485,7 @@ let run_cram_test env ~src ~script ~cram_stanzas ~temp_dir ~cwd ~timeout =
485485

486486
let run_produce_correction ~conflict_markers ~src ~env ~script ~timeout lexbuf =
487487
let temp_dir = make_temp_dir ~script in
488-
let cram_stanzas = cram_stanzas lexbuf ~conflict_markers in
488+
let cram_stanzas = cram_stanzas lexbuf ~conflict_markers |> List.map ~f:snd in
489489
let cwd = Path.parent_exn script in
490490
let env = make_run_env env ~temp_dir ~cwd in
491491
let open Fiber.O in
@@ -506,7 +506,7 @@ let run_and_produce_output ~conflict_markers ~src ~env ~dir:cwd ~script ~dst ~ti
506506
let script_contents = Io.read_file ~binary:false script in
507507
let lexbuf = Lexbuf.from_string script_contents ~fname:(Path.to_string script) in
508508
let temp_dir = make_temp_dir ~script in
509-
let cram_stanzas = cram_stanzas lexbuf ~conflict_markers in
509+
let cram_stanzas = cram_stanzas lexbuf ~conflict_markers |> List.map ~f:snd in
510510
(* We don't want the ".cram.run.t" dir around when executing the script. *)
511511
Path.rm_rf (Path.parent_exn script);
512512
let env = make_run_env env ~temp_dir ~cwd in
@@ -599,6 +599,7 @@ module Make_script = struct
599599
Io.read_file ~binary:false src
600600
|> Lexbuf.from_string ~fname:(Path.to_string src)
601601
|> cram_stanzas ~conflict_markers
602+
|> List.map ~f:snd
602603
|> List.filter_map ~f:(function
603604
| Cram_lexer.Comment _ -> None
604605
| Command s -> Some s)
@@ -642,6 +643,7 @@ module Diff = struct
642643
let current_stanzas =
643644
Lexbuf.from_string ~fname:(Path.to_string script) current
644645
|> cram_stanzas ~conflict_markers:Ignore
646+
|> List.map ~f:snd
645647
in
646648
let rec loop acc current expected =
647649
match current with

src/dune_rules/cram/cram_exec.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ val diff : src:Path.t -> output:Path.t -> Action.t
2323
val action : Path.t -> Action.t
2424

2525
module For_tests : sig
26-
val cram_stanzas : Lexing.lexbuf -> string list Cram_lexer.block list
26+
val cram_stanzas : Lexing.lexbuf -> (Loc.t * string list Cram_lexer.block) list
2727
val dyn_of_block : string list Cram_lexer.block -> Dyn.t
2828
end

src/dune_rules/cram/cram_lexer.mli

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
open Import
2+
13
(** .t file parser *)
24

35
(** A command or comment. Output blocks are skipped *)
46
type 'command block =
57
| Command of 'command
68
| Comment of string list
79

8-
val block : Lexing.lexbuf -> string list block option
10+
val block : Lexing.lexbuf -> (Loc.t * string list block) option

src/dune_rules/cram/cram_lexer.mll

Lines changed: 127 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,142 @@
11
{
2+
open Import
3+
24
type 'command block =
35
| Command of 'command
46
| Comment of string list
7+
8+
(* Gets position before consuming newline *)
9+
let pos_before_newline lexbuf =
10+
let pos = Lexing.lexeme_end_p lexbuf in
11+
{ pos with pos_cnum = pos.pos_cnum - 1 }
12+
13+
(* Creates comment from string list accumulator *)
14+
let create_comment_from_acc ~start ~stop acc =
15+
match acc with
16+
| [] -> None
17+
| _ -> Some (Loc.create ~start ~stop, Comment (List.rev acc))
18+
19+
(* Helper for extracting start/stop positions *)
20+
let with_span lexbuf f =
21+
let start = Lexing.lexeme_start_p lexbuf in
22+
let stop = Lexing.lexeme_end_p lexbuf in
23+
f ~start ~stop
24+
25+
(* Forward declarations for lexer rules *)
26+
let eol_fdecl = Fdecl.create Dyn.opaque
27+
let command_cont_fdecl = Fdecl.create Dyn.opaque
28+
29+
(* Helper for position extraction + eol + recursive call *)
30+
let with_span_eol lexbuf content next_rule =
31+
with_span lexbuf (fun ~start ~stop ->
32+
ignore (Fdecl.get eol_fdecl lexbuf);
33+
next_rule start stop content lexbuf)
34+
35+
(* Handles command continuation with eol checking *)
36+
let handle_command_continuation ~start ~content ~acc lexbuf =
37+
let stop = Lexing.lexeme_end_p lexbuf in
38+
match Fdecl.get eol_fdecl lexbuf with
39+
| true -> Fdecl.get command_cont_fdecl start stop (content :: acc) lexbuf
40+
| false -> (Loc.create ~start ~stop, Command (List.rev (content :: acc)))
41+
42+
(* Processes " $ " command start pattern *)
43+
let process_command_start str lexbuf =
44+
let start =
45+
let pos = Lexing.lexeme_start_p lexbuf in
46+
{ pos with pos_cnum = pos.pos_cnum + 2 }
47+
in
48+
let stop = Lexing.lexeme_end_p lexbuf in
49+
match Fdecl.get eol_fdecl lexbuf with
50+
| true -> Some (Fdecl.get command_cont_fdecl start stop [ str ] lexbuf)
51+
| false -> Some (Loc.create ~start ~stop, Command [ str ])
552
}
653

7-
let eol = '\n' | eof
54+
let nonspace = [^' ' '\n']
55+
let not_nl = [^'\n']
856

9-
let blank = [' ' '\t' '\r' '\012']
57+
rule eol = parse
58+
| '\n' { Lexing.new_line lexbuf; true }
59+
| eof { false }
1060

11-
rule block = parse
61+
and block = parse
1262
| eof { None }
13-
| " $ " ([^'\n']* as str) eol
14-
{ Some (command_cont [str] lexbuf) }
15-
| " " [^'\n']* eol
16-
{ output [] lexbuf }
17-
| ' '? as str eol
18-
{ comment [str] lexbuf }
19-
| ' '? [^' ' '\n'] [^'\n']* as str eol
20-
{ comment [str] lexbuf }
21-
22-
and comment acc = parse
63+
| " $ " ([^'\n']* as str)
64+
{ process_command_start str lexbuf }
65+
| " > " ([^'\n']* as str)
66+
{ with_span_eol lexbuf [ " > " ^ str ] comment }
67+
| " >"
68+
{ with_span_eol lexbuf [ " >" ] comment }
69+
| " " [^'\n']*
70+
{ with_span_eol lexbuf [] (fun start _ content lexbuf -> output start content lexbuf) }
71+
| ' ' ((nonspace not_nl*) as rest)
72+
{ with_span_eol lexbuf [ " " ^ rest ] comment }
73+
| ' ' '\n'
74+
{ let start = Lexing.lexeme_start_p lexbuf in
75+
let stop = pos_before_newline lexbuf in
76+
Lexing.new_line lexbuf;
77+
comment start stop [ " " ] lexbuf }
78+
| ' '
79+
{ with_span lexbuf (fun ~start ~stop -> comment start stop [ " " ] lexbuf) }
80+
| '\n'
81+
{ let start = Lexing.lexeme_start_p lexbuf in
82+
let stop = Lexing.lexeme_start_p lexbuf in
83+
Lexing.new_line lexbuf;
84+
comment start stop [ "" ] lexbuf }
85+
| nonspace not_nl* as str
86+
{ with_span_eol lexbuf [str] comment }
87+
88+
and comment start last_content_stop acc = parse
2389
| eof
24-
{ match acc with
25-
| [] -> None
26-
| _ -> Some (Comment (List.rev acc))
27-
}
28-
| ' '? as str eol
29-
{ comment (str :: acc) lexbuf }
30-
| ' '? [^' ' '\n'] [^'\n']* as str eol
31-
{ comment (str :: acc) lexbuf }
32-
| ""
33-
{ Some (Comment (List.rev acc)) }
90+
{ create_comment_from_acc ~start ~stop:last_content_stop acc }
91+
| ' ' ((nonspace not_nl*) as rest)
92+
{ with_span_eol lexbuf [(" " ^ rest)] (fun _start stop content lexbuf ->
93+
comment start stop (content @ acc) lexbuf) }
94+
| ' ' '\n'
95+
{ let content_stop = pos_before_newline lexbuf in
96+
Lexing.new_line lexbuf;
97+
comment start content_stop (" " :: acc) lexbuf }
98+
| '\n'
99+
{ let content_stop = Lexing.lexeme_start_p lexbuf in
100+
Lexing.new_line lexbuf;
101+
comment start content_stop ("" :: acc) lexbuf }
102+
| nonspace not_nl* as str
103+
{ with_span_eol lexbuf [str] (fun _start stop content lexbuf ->
104+
comment start stop (content @ acc) lexbuf) }
105+
| "" { create_comment_from_acc ~start ~stop:last_content_stop acc }
34106

35-
and output maybe_comment = parse
107+
108+
and output block_start maybe_comment = parse
36109
| eof
37-
{ match maybe_comment with
38-
| [] -> None
39-
| l -> Some (Comment (List.rev l))
40-
}
41-
| ' ' eof
42-
{ Some (Comment (List.rev (" " :: maybe_comment))) }
43-
| " "? eof
44-
{ None }
45-
| " " eol
46-
{ output [] lexbuf }
47-
| ' '? as s eol
48-
{ output (s :: maybe_comment) lexbuf }
49-
| " $" eol
50-
{ output [] lexbuf }
51-
| " " '$' [^' ' '\n'] [^'\n']* eol
52-
{ output [] lexbuf }
53-
| " " [^'$' '\n'] [^'\n']* eol
54-
{ output [] lexbuf }
110+
{ create_comment_from_acc ~start:block_start ~stop:(Lexing.lexeme_start_p lexbuf) maybe_comment }
111+
| " $ " ([^'\n']* as str)
112+
{ process_command_start str lexbuf }
113+
| ' ' ((nonspace not_nl*) as rest)
114+
{ match eol lexbuf with
115+
| true -> output block_start ((" " ^ rest) :: maybe_comment) lexbuf
116+
| false ->
117+
Some (Loc.create ~start:block_start ~stop:(Lexing.lexeme_start_p lexbuf)
118+
, Comment (List.rev ((" " ^ rest) :: maybe_comment))) }
119+
| ' ' '\n'
120+
{ Lexing.new_line lexbuf;
121+
output block_start (" " :: maybe_comment) lexbuf }
122+
| " " [^'\n']*
123+
{ with_span_eol lexbuf [] (fun _start _stop _content lexbuf ->
124+
output block_start maybe_comment lexbuf) }
55125
| ""
56126
{ match maybe_comment with
57-
| [] -> block lexbuf
58-
| l -> comment l lexbuf
59-
}
60-
61-
and command_cont acc = parse
62-
| " > " ([^'\n']* as str) eol
63-
{ command_cont (str :: acc) lexbuf }
64-
| " >" eol
65-
{ command_cont ("" :: acc) lexbuf }
127+
| [] -> block lexbuf
128+
| l -> comment block_start (Lexing.lexeme_start_p lexbuf) l lexbuf }
129+
130+
and command_cont start last_stop acc = parse
131+
| " > " ([^'\n']* as str)
132+
{ handle_command_continuation ~start ~content:str ~acc lexbuf }
133+
| " >"
134+
{ handle_command_continuation ~start ~content:"" ~acc lexbuf }
66135
| ""
67-
{ Command (List.rev acc) }
136+
{ (Loc.create ~start ~stop:last_stop, Command (List.rev acc)) }
137+
138+
{
139+
let () =
140+
Fdecl.set eol_fdecl eol;
141+
Fdecl.set command_cont_fdecl command_cont
142+
}

0 commit comments

Comments
 (0)