M README.org +4 -0
@@ 241,6 241,10 @@ End of the world party party has a minim
No guests have signed up.
#+END_EXAMPLE
+*** Comments
+A template can have a comments. Comments start with ~@%~ or ~@%-~ and can
+contain any character other than ~@~. A command ends with ~@~ or ~-@~.
+
*** Transformers
Any template replacement may include one or more transformers. A transformer is
a function which takes the value of a template and converts can perform any
M src/snabela/snabela.ml +16 -1
@@ 75,6 75,9 @@ module Template = struct
| String s::At ln::Left_trim::xs ->
(* @- ... *)
at (At ln::String (trim_trailing_ws s)::acc) xs
+ | String s::At ln::Comment::Left_trim::xs ->
+ (* @%- ... *)
+ at (Comment::At ln::String (trim_trailing_ws s)::acc) xs
| Left_trim::xs ->
(* ... - ... *)
at acc xs
@@ 91,13 94,25 @@ module Template = struct
in
List.rev (at [] tokens)
+ let remove_comments tokens =
+ let open Snabela_lexer.Token in
+ let rec rc acc = function
+ | At _::Comment::At _::xs ->
+ rc acc xs
+ | x::xs ->
+ rc (x::acc) xs
+ | [] ->
+ acc
+ in
+ List.rev (rc [] tokens)
+
let of_utf8_string s =
let open CCResult.Infix in
try
let lexbuf = Sedlexing.Utf8.from_string s in
Snabela_lexer.tokenize lexbuf
>>= fun tokens ->
- Ok (apply_trims tokens)
+ Ok (remove_comments (apply_trims tokens))
with
| exn ->
Error (`Exn exn)
M src/snabela/snabela_lexer.ml +17 -0
@@ 11,6 11,7 @@ module Token = struct
| Transformer of string
| String of string
| End_section
+ | Comment
[@@deriving show,eq]
type t = token list [@@deriving show,eq]
@@ 47,6 48,10 @@ let rec token ln bldr buf =
match%sedlex buf with
| "@@" ->
token ln (Tb.add Escaped_at bldr) buf
+ | "@%-" ->
+ comment ln (Tb.add_l [At ln; Comment; Left_trim] bldr) buf
+ | "@%" ->
+ comment ln (Tb.add_l [At ln; Comment] bldr) buf
| "@#?-" ->
replacement ln (Tb.add_l [At ln; List; Test; Left_trim] bldr) buf
| "@#-" ->
@@ 125,6 130,18 @@ and replacement_close ln bldr buf =
token ln (Tb.add (At ln) bldr) buf
| _ ->
raise (Tokenize_error (`Invalid_replacement ln))
+and comment ln bldr buf =
+ match%sedlex buf with
+ | "-@" ->
+ token ln (Tb.add_l [Right_trim; At ln] bldr) buf
+ | "@" ->
+ token ln (Tb.add (At ln) bldr) buf
+ | '\n' ->
+ comment (ln + 1) bldr buf
+ | any ->
+ comment ln bldr buf
+ | _ ->
+ assert false
let tokenize s =
try
M test_data/foo.tmpl +7 -1
@@ 1,5 1,11 @@
@#parties-@
-@name@ has a minimum age of @min_age@ and has a $@cost | money@ cover charge.
+@name@ has a minimum age of @min_age-@
+@% This comment is between two lines that will be turned
+ into one line because of the whitespace trimming with -
+ Notice the indent in the line after after this, otherwise
+ the min_age and the word "and" would be right next to each
+ other -@
+ and has a $ @-cost | money@ cover charge.
@#?guest_list-@
Guest list:
@#-guest_list-@
M tests/snabela/test.ml +36 -0
@@ 308,6 308,17 @@ let test_apply11 =
let applied = CCResult.get_exn (Snabela.apply compile kv) in
assert ("Hello, Joe" = applied))
+let test_apply12 =
+ Oth.test
+ ~name:"Apply: Comment"
+ (fun _ ->
+ let template = "@%This is a template-@\nHello, @name@" in
+ let kv = Snabela.Kv.(Map.of_list [("name", string "foo")]) in
+ let t = CCResult.get_exn (Snabela.Template.of_utf8_string template) in
+ let compile = Snabela.of_template t [] in
+ let applied = CCResult.get_exn (Snabela.apply compile kv) in
+ assert ("Hello, foo" = applied))
+
let test_apply_fail1 =
Oth.test
~name:"Apply Fail: Missing key"
@@ 396,6 407,28 @@ let test_apply_fail8 =
let ret = Snabela.apply compile kv in
assert (ret = Error (`Missing_key ("name1", 7))))
+let test_apply_fail9 =
+ Oth.test
+ ~name:"Apply Fail: Comment"
+ (fun _ ->
+ let template = "@%This is a template-@\nHello, @name@" in
+ let kv = Snabela.Kv.(Map.of_list []) in
+ let t = CCResult.get_exn (Snabela.Template.of_utf8_string template) in
+ let compile = Snabela.of_template t [] in
+ let ret = Snabela.apply compile kv in
+ assert (ret = Error (`Missing_key ("name", 2))))
+
+let test_apply_fail10 =
+ Oth.test
+ ~name:"Apply Fail: More Comment"
+ (fun _ ->
+ let template = "@%This is\na template-@\nHello, @name@" in
+ let kv = Snabela.Kv.(Map.of_list []) in
+ let t = CCResult.get_exn (Snabela.Template.of_utf8_string template) in
+ let compile = Snabela.of_template t [] in
+ let ret = Snabela.apply compile kv in
+ assert (ret = Error (`Missing_key ("name", 3))))
+
let test_transformer1 =
Oth.test
~name:"Transformer: Capitalize"
@@ 453,6 486,7 @@ let test =
; test_apply9
; test_apply10
; test_apply11
+ ; test_apply12
; test_apply_fail1
; test_apply_fail2
; test_apply_fail3
@@ 461,6 495,8 @@ let test =
; test_apply_fail6
; test_apply_fail7
; test_apply_fail8
+ ; test_apply_fail9
+ ; test_apply_fail10
; test_transformer1
; test_transformer2
]