about summary refs log tree commit diff stats
path: root/modal/ocaml/_build/default/src/parse.ml
diff options
context:
space:
mode:
Diffstat (limited to 'modal/ocaml/_build/default/src/parse.ml')
-rw-r--r--modal/ocaml/_build/default/src/parse.ml50
1 files changed, 50 insertions, 0 deletions
diff --git a/modal/ocaml/_build/default/src/parse.ml b/modal/ocaml/_build/default/src/parse.ml
new file mode 100644
index 0000000..a415a80
--- /dev/null
+++ b/modal/ocaml/_build/default/src/parse.ml
@@ -0,0 +1,50 @@
+(* Minimal s-expression parser for Modal syntax *)
+
+open Ast
+
+exception Parse_error of string
+
+let is_space = function ' ' | '\n' | '\t' | '\r' -> true | _ -> false
+
+let tokenize s =
+  let buf = Buffer.create 16 in
+  let tokens = ref [] in
+  let flush () =
+    if Buffer.length buf > 0 then (
+      tokens := Buffer.contents buf :: !tokens;
+      Buffer.clear buf)
+  in
+  String.iter
+    (fun c ->
+      match c with
+      | '(' | ')' -> flush (); tokens := (String.make 1 c) :: !tokens
+      | _ when is_space c -> flush ()
+      | _ -> Buffer.add_char buf c)
+    s;
+  flush (); List.rev !tokens
+
+let parse_tokens tokens =
+  let rec parse_list i =
+    match List.nth_opt tokens i with
+    | Some ")" -> ([], i + 1)
+    | None -> raise (Parse_error "Unclosed list")
+    | _ ->
+        let (node, i') = parse_node i in
+        let (rest, j) = parse_list i' in
+        (node :: rest, j)
+  and parse_node i =
+    match List.nth_opt tokens i with
+    | Some "(" ->
+        let (nodes, j) = parse_list (i + 1) in
+        (List nodes, j)
+    | Some ")" -> raise (Parse_error "Unexpected )")
+    | Some tok -> (Atom tok, i + 1)
+    | None -> raise (Parse_error "Unexpected end")
+  in
+  let (node, i) = parse_node 0 in
+  if i <> List.length tokens then raise (Parse_error "Trailing tokens");
+  node
+
+let parse s = parse_tokens (tokenize s)
+
+