Compare commits
2 Commits
bf5daf1b54
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 961b3f00fb | |||
| bc6aee7bb7 |
+4
-1
@@ -1,3 +1,6 @@
|
|||||||
*.cmo
|
*.cmo
|
||||||
*.cmi
|
*.cmi
|
||||||
a.out
|
*.cmx
|
||||||
|
*.o
|
||||||
|
*.out
|
||||||
|
*.cma
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
#load "parser.cmo";;
|
||||||
|
#load "periodic.cmo";;
|
||||||
|
#load "main.cmo";;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
common_files = parser.ml
|
||||||
|
extensions = *.cmo *.cmi *.cma *.o *.out *.cmx *.a
|
||||||
|
|
||||||
|
lib:
|
||||||
|
ocamlc -c $(common_files)
|
||||||
|
ocamlc -a parser.cmo -o parser.cma
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(extensions)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# usagi - simple ocaml parser combinator library
|
||||||
|
|
||||||
|
## compilation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ make lib
|
||||||
|
```
|
||||||
|
|
||||||
|
generate `.cma` that can be linked with `ocamlc -I . parser.cma`
|
||||||
|
|
||||||
|
## todo
|
||||||
|
|
||||||
|
- [ ] error recovery
|
||||||
|
|||||||
+23
-73
@@ -1,13 +1,16 @@
|
|||||||
type input =
|
type input =
|
||||||
{ content : string
|
{ content : string
|
||||||
; column : int
|
; line : int
|
||||||
|
; offset : int
|
||||||
}
|
}
|
||||||
|
|
||||||
let make_input s = { content = s; column = 0 }
|
let make_input s = { content = s; line = 0; offset = 0 }
|
||||||
|
|
||||||
type error =
|
type error =
|
||||||
{ content : string
|
{ content : string
|
||||||
; column : int }
|
; line : int
|
||||||
|
; offset : int
|
||||||
|
}
|
||||||
|
|
||||||
type 'a parser_result = (('a * input) list, error list) result
|
type 'a parser_result = (('a * input) list, error list) result
|
||||||
|
|
||||||
@@ -15,8 +18,6 @@ type 'a parser =
|
|||||||
{ run : input -> 'a parser_result
|
{ run : input -> 'a parser_result
|
||||||
}
|
}
|
||||||
|
|
||||||
let runParser i p = p.run i
|
|
||||||
|
|
||||||
let fold_left1 (f: 'a -> 'a -> 'a) (xs: 'a list) : 'a = match xs with
|
let fold_left1 (f: 'a -> 'a -> 'a) (xs: 'a list) : 'a = match xs with
|
||||||
| [] -> failwith "TODO: make method total; empty list"
|
| [] -> failwith "TODO: make method total; empty list"
|
||||||
| x :: xs -> List.fold_left f x xs
|
| x :: xs -> List.fold_left f x xs
|
||||||
@@ -28,16 +29,18 @@ let append (a: 'a parser_result) (b: 'a parser_result) : 'a parser_result =
|
|||||||
| (v, _) -> v
|
| (v, _) -> v
|
||||||
|
|
||||||
let fail (s: string) : 'a parser =
|
let fail (s: string) : 'a parser =
|
||||||
{ run = fun x -> match x with
|
{ run = fun x -> Error [{content = s; line = x.line; offset = x.offset}]
|
||||||
| {content = _; column = c} -> Error [{content = s; column = c}]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let get : char parser =
|
let get : char parser =
|
||||||
{ run = function
|
{ run = function
|
||||||
| { content = ""; column = c } ->
|
| { content = ""; line = l; offset = o } ->
|
||||||
Error [{ content = "unexpected EOF"; column = c }]
|
Error [{ content = "unexpected EOF"; line = l; offset = o }]
|
||||||
| { content = xs; column = c } ->
|
| { content = xs; line = l; offset = o } ->
|
||||||
Ok [(String.get xs 0, { content = String.sub xs 1 (String.length xs - 1); column = c+1})]
|
let x = String.get xs 0 in
|
||||||
|
let xs' = String.sub xs 1 (String.length xs - 1) in
|
||||||
|
let (line', offset') = if x == '\n' then (l+1,0) else (l,o+1) in
|
||||||
|
Ok [(x, {content = xs'; line = line'; offset = offset'})]
|
||||||
}
|
}
|
||||||
|
|
||||||
let return (x: 'a) : 'a parser =
|
let return (x: 'a) : 'a parser =
|
||||||
@@ -93,69 +96,16 @@ let many1 (p: 'a parser): 'a list parser =
|
|||||||
let* xs = many p in
|
let* xs = many p in
|
||||||
x::xs |> return
|
x::xs |> return
|
||||||
|
|
||||||
type element =
|
let rec manyTill (p: 'a parser) (q: 'b parser): 'a list parser =
|
||||||
{ symbol : string
|
(
|
||||||
; count : int
|
let* x = p in
|
||||||
}
|
let* xs = manyTill p q in
|
||||||
|
x::xs |> return
|
||||||
type molecule =
|
) <++ (q >>= fun _ -> return [])
|
||||||
{ elements : element list
|
|
||||||
}
|
|
||||||
|
|
||||||
let unary_symbol_p : string parser =
|
|
||||||
let* x = upper in
|
|
||||||
String.make 1 x |> return
|
|
||||||
|
|
||||||
let binary_symbol_p : string parser =
|
|
||||||
let* x = upper in
|
|
||||||
let* x' = lower in
|
|
||||||
String.make 1 x ^ String.make 1 x' |> return
|
|
||||||
|
|
||||||
let symbol_p : string parser = binary_symbol_p <++ unary_symbol_p
|
|
||||||
|
|
||||||
let element_count_p : int parser =
|
|
||||||
let* x = many1 digit in
|
|
||||||
List.to_seq x
|
|
||||||
|> String.of_seq
|
|
||||||
|> int_of_string
|
|
||||||
|> return
|
|
||||||
|
|
||||||
let element_p : element parser =
|
|
||||||
let* s = symbol_p in
|
|
||||||
let* n = element_count_p <++ return 1 in
|
|
||||||
{ symbol = s
|
|
||||||
; count = n
|
|
||||||
} |> return
|
|
||||||
|
|
||||||
let manyTill1 (p: 'a parser) (q: 'b parser): 'a list parser =
|
|
||||||
let* xs = many1 p in
|
|
||||||
let* _ = q in
|
|
||||||
return xs
|
|
||||||
|
|
||||||
let eof : unit parser =
|
let eof : unit parser =
|
||||||
{ run = fun inp ->
|
{ run = fun inp ->
|
||||||
if inp.content = ""
|
if inp.content = ""
|
||||||
then Ok [(), {content = ""; column = inp.column}]
|
then Ok [(), inp]
|
||||||
else Error [{content = "expected EOF"; column = inp.column+1}]
|
else Error[{content = "expected EOF"; line = inp.line; offset = inp.offset+1}]
|
||||||
}
|
}
|
||||||
|
|
||||||
let molecule_p : molecule parser =
|
|
||||||
let* xs = manyTill1 element_p eof in
|
|
||||||
return { elements = xs }
|
|
||||||
|
|
||||||
let parse_molecule s : (molecule, error list) result =
|
|
||||||
match make_input s |> molecule_p.run with
|
|
||||||
| Ok [] -> failwith "TODO: make method total; empty list"
|
|
||||||
| Ok ((x,_)::_) -> Ok x
|
|
||||||
| Error xs -> Error xs
|
|
||||||
|
|
||||||
let print_molecule (s: molecule) : unit =
|
|
||||||
List.iter (fun x -> Printf.printf "%s, %i\n" x.symbol x.count) s.elements
|
|
||||||
|
|
||||||
let print_error (xs: error list) (s: string) : unit =
|
|
||||||
List.iter (fun x -> Printf.printf "%s\n%*s\n%s\n" s x.column "^" x.content) xs
|
|
||||||
|
|
||||||
let molecule s : unit =
|
|
||||||
match parse_molecule s with
|
|
||||||
| Ok x -> print_molecule x
|
|
||||||
| Error xs -> print_error xs s
|
|
||||||
Reference in New Issue
Block a user