Skip to content

Commit bd2476d

Browse files
committed
Initial commit
0 parents  commit bd2476d

File tree

11 files changed

+478
-0
lines changed

11 files changed

+478
-0
lines changed

.gitignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
*.annot
3+
*.cmo
4+
*.cma
5+
*.cmi
6+
*.a
7+
*.o
8+
*.cmx
9+
*.cmxs
10+
*.cmxa
11+
12+
# ocamlbuild working directory
13+
_build/
14+
15+
# ocamlbuild targets
16+
*.byte
17+
*.native
18+
19+
# oasis generated files
20+
setup.data
21+
setup.log
22+
23+
# Merlin configuring file for Vim and Emacs
24+
.merlin
25+
26+
# Dune generated files
27+
*.install
28+
29+
# Local OPAM switch
30+
_opam/
31+
notes
32+
*DS_Store

Makefile

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
all: build
2+
3+
build:
4+
@dune build @all
5+
@cp -f _build/default/bin/main.exe /usr/local/bin/test_chungus
6+
@echo "\n"
7+
@test_chungus
8+
9+
install:
10+
@dune install
11+
12+
test: build
13+
@dune runtest
14+
15+
doc: build
16+
@opam install odoc
17+
@dune build @doc
18+
19+
clean:
20+
@dune clean
21+
22+
# Create a release on Github, then run git pull
23+
publish:
24+
@git tag 1.0
25+
@git push origin 1.0
26+
@git pull
27+
@opam pin .
28+
@opam publish https://github.com/chrisnevers/chungus/archive/1.0.tar.gz

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Chungus Parser Combinator
2+
3+
A simple monadic parser combinator library with position information.
4+
5+
## Example
6+
7+
`bin/main.ml` demonstrates using the parser to parse a simple untyped lambda calculus.
8+
9+
```ocaml
10+
open Chungus.Parser
11+
open Chungus.Combinator
12+
open Chungus.Chars
13+
14+
type exp =
15+
| Var of position * string
16+
| Int of position * int
17+
| Lambda of position * exp * exp
18+
| Apply of position * exp * exp
19+
20+
let rec show_exp e =
21+
match e with
22+
| Var (_, id) -> id
23+
| Int (_, i) -> string_of_int i
24+
| Lambda (_, i, b) -> "λ " ^ show_exp i ^ " -> " ^ show_exp b
25+
| Apply (_, f, a) -> "(" ^ show_exp f ^ ") (" ^ show_exp a ^ ")"
26+
27+
let identifier () =
28+
let pos = get_position () in
29+
let* ident = lower <@> many alpha_digit in
30+
return @@ Var (pos, stringify ident)
31+
32+
let int () =
33+
let pos = get_position () in
34+
let* i = many1 digit in
35+
return @@ Int (pos, stringify i |> int_of_string)
36+
37+
let terminal () =
38+
return =<< (identifier <?> int)
39+
40+
let rec lambda () =
41+
ignore_spaces ();
42+
let pos = get_position () in
43+
let* id = str "λ" >> spaces >> identifier in
44+
let* ex = spaces >> str "->" >> expression in
45+
return @@ Lambda (pos, id, ex)
46+
47+
and non_app () =
48+
lambda <?> terminal <?> parens expression >>= return
49+
50+
and app l r =
51+
ignore_spaces ();
52+
Apply (get_position (), l, r)
53+
54+
and expression () = return =<< chainl (spaces >> non_app) (lift app)
55+
56+
let () =
57+
from_string "(λ chrisNevers -> chrisNevers (λ shamone -> hehe)) (λ whenTheImposterIsSus -> 54235)";
58+
match expression () with
59+
| Ok e -> print_endline (show_exp e)
60+
| _ -> print_endline "Failed to parse expression"
61+
62+
```

bin/dune

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
(executable
2+
(name main)
3+
(libraries chungus))

bin/main.ml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
open Chungus.Parser
2+
open Chungus.Combinator
3+
open Chungus.Chars
4+
5+
type exp =
6+
| Var of position * string
7+
| Int of position * int
8+
| Lambda of position * exp * exp
9+
| Apply of position * exp * exp
10+
11+
let rec show_exp e =
12+
match e with
13+
| Var (_, id) -> id
14+
| Int (_, i) -> string_of_int i
15+
| Lambda (_, i, b) -> "λ " ^ show_exp i ^ " -> " ^ show_exp b
16+
| Apply (_, f, a) -> "(" ^ show_exp f ^ ") (" ^ show_exp a ^ ")"
17+
18+
let identifier () =
19+
let pos = get_position () in
20+
let* ident = lower <@> many alpha_digit in
21+
return @@ Var (pos, stringify ident)
22+
23+
let int () =
24+
let pos = get_position () in
25+
let* i = many1 digit in
26+
return @@ Int (pos, stringify i |> int_of_string)
27+
28+
let terminal () =
29+
return =<< (identifier <?> int)
30+
31+
let rec lambda () =
32+
ignore_spaces ();
33+
let pos = get_position () in
34+
let* id = str "λ" >> spaces >> identifier in
35+
let* ex = spaces >> str "->" >> expression in
36+
return @@ Lambda (pos, id, ex)
37+
38+
and non_app () =
39+
lambda <?> terminal <?> parens expression >>= return
40+
41+
and app l r =
42+
ignore_spaces ();
43+
Apply (get_position (), l, r)
44+
45+
and expression () = return =<< chainl (spaces >> non_app) (lift app)
46+
47+
let () =
48+
from_string "(λ chrisNevers -> chrisNevers (λ shamone -> hehe)) (λ whenTheImposterIsSus -> 54235)";
49+
match expression () with
50+
| Ok e -> print_endline (show_exp e)
51+
| _ -> print_endline "Failed to parse expression"

chungus.opam

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
opam-version: "2.0"
3+
version: "1.0"
4+
authors: "Chris Nevers <[email protected]>"
5+
maintainer: "Chris Nevers <[email protected]>"
6+
homepage: "https://github.com/chrisnevers/chungus"
7+
bug-reports: "https://github.com/chrisnevers/chungus/issues"
8+
dev-repo: "git://github.com/chrisnevers/chungus.git"
9+
synopsis: ""
10+
build: [
11+
["dune" "subst"] {pinned}
12+
["dune" "build" "-p" name "-j" jobs]
13+
]
14+
15+
depends: [
16+
"ocaml"
17+
"dune" {>= "2.7.1"}
18+
]

dune-project

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(lang dune 2.7)
2+
(name chungus)

lib/Chars.ml

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
open Parser
2+
open Combinator
3+
open Stream
4+
5+
let is_lower c =
6+
let code = Char.code c in
7+
code >= 97 && code <= 122
8+
9+
let is_upper c =
10+
let code = Char.code c in
11+
code >= 65 && code <= 90
12+
13+
let is_alpha c =
14+
is_lower c || is_upper c
15+
16+
let is_digit c =
17+
let code = Char.code c in
18+
code >= 48 && code <= 57
19+
20+
let is_whitespace c =
21+
let code = Char.code c in
22+
List.mem code [32; 9; 10; 11; 12; 13]
23+
24+
let newline = "\n"
25+
26+
let crlf = "\r\n"
27+
28+
let eof = "\000"
29+
30+
let char f =
31+
let stream = get_stream () in
32+
match peek stream with
33+
| Some a when f a ->
34+
let _ = process_char stream in
35+
Ok a
36+
| _ -> Fail
37+
38+
let str s () =
39+
let s_len = String.length s in
40+
let stream = get_stream () in
41+
let chars = Stream.npeek s_len stream in
42+
match s_len == List.length chars with
43+
| true ->
44+
if String.compare (stringify chars) s == 0 then
45+
let _ = process_chars stream s_len in
46+
Ok chars
47+
else
48+
Fail
49+
| false -> Fail
50+
51+
let any_char () =
52+
char (fun _ -> true)
53+
54+
let one_of cs =
55+
char (fun c -> List.mem c cs)
56+
57+
let none_of cs =
58+
char (fun c -> not (List.mem c cs))
59+
60+
let digit () =
61+
char (fun c -> is_digit c)
62+
63+
let alpha () =
64+
char (fun c -> is_alpha c)
65+
66+
let alpha_digit () =
67+
char (fun c -> is_alpha c || is_digit c)
68+
69+
let whitespace () =
70+
char (fun c -> is_whitespace c)
71+
72+
let lower () =
73+
char (fun c -> is_lower c)
74+
75+
let upper () =
76+
char (fun c -> is_upper c)
77+
78+
let spaces = many whitespace
79+
80+
let ignore_spaces () = spaces () |> ignore
81+
82+
let match_char uc () =
83+
char (fun c -> c == uc)
84+
85+
let tab = match_char '\t'
86+
87+
let comma = match_char ','
88+
89+
let l_paren = match_char '('
90+
91+
let r_paren = match_char ')'
92+
93+
let end_of_line () =
94+
str newline <?> str crlf
95+
96+
let parens e () = between l_paren r_paren e

lib/Combinator.ml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
open Parser
2+
3+
let many p () =
4+
let rec aux () =
5+
match p () with
6+
| Ok e ->
7+
let* rst = aux in
8+
return (e :: rst)
9+
| _ -> return []
10+
in
11+
aux ()
12+
13+
let many1 p () =
14+
let rec aux () =
15+
match p () with
16+
| Ok e ->
17+
let* rst = aux in
18+
return (e :: rst)
19+
| _ -> return []
20+
in
21+
let* fst = p in
22+
let* rst = aux in
23+
return (fst :: rst)
24+
25+
let rec choice ps =
26+
match ps with
27+
| [] -> Fail
28+
| h :: t ->
29+
match h () with
30+
| Ok a -> Ok a
31+
| _ -> choice t
32+
33+
let count i p =
34+
let rec aux i () =
35+
match i with
36+
| 0 -> return []
37+
| n ->
38+
let* a = p in
39+
let* rst = aux (n - 1) in
40+
Ok (a :: rst)
41+
in
42+
aux i ()
43+
44+
let sepBy p sep =
45+
let rec aux () =
46+
match p () with
47+
| Ok e ->
48+
begin match sep () with
49+
| Ok _ ->
50+
let* rst = aux in
51+
return (e :: rst)
52+
| _ -> return [e]
53+
end
54+
| _ -> return []
55+
in
56+
aux ()
57+
58+
let between s e p =
59+
let* _ = s in
60+
let* p' = p in
61+
let* _ = e in
62+
return p'
63+
64+
let chainl p op () =
65+
let rec aux x : 'a result =
66+
let work () =
67+
let* f = op in
68+
let* y = p in
69+
aux (f x y)
70+
in
71+
let* res = work <?> const x in
72+
return res
73+
in
74+
let* fst = p in
75+
aux fst

0 commit comments

Comments
 (0)