ADD Add support for comments
5 files changed, 80 insertions(+), 2 deletions(-)

M README.org
M src/snabela/snabela.ml
M src/snabela/snabela_lexer.ml
M test_data/foo.tmpl
M tests/snabela/test.ml
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
     ]