Skip to content

Commit 3f14e78

Browse files
committed
WIP: Type Tetris 2, Electric Boogaloo part 3
1 parent 422d2b4 commit 3f14e78

8 files changed

Lines changed: 225 additions & 241 deletions

File tree

lib/Ast.ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module type EXPR = sig
99
val is_functor : t -> bool
1010
end
1111

12-
type head = { name : string; arity : int }
12+
type head = { name : string; arity : int } [@@deriving show, ord]
1313

1414
module ClauseF (Expr : EXPR) = struct
1515
type multi_declaration = head * decl * decl Location.with_location FT.t
@@ -48,6 +48,9 @@ module ClauseF (Expr : EXPR) = struct
4848
}
4949

5050
and ('directives, 'mods) t = ('directives, 'mods) base Location.with_location
51+
52+
let signature_populated (s : ('directives, 'mods) signature_body) : bool =
53+
not (FT.is_empty s.directives && FT.is_empty s.declarations)
5154
end
5255

5356
module Expr = struct

lib/FT.ml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,6 @@ let group (cmp : 'a -> 'a -> int) (ft : 'a t) : 'a t t =
5959
let rec find_opt (f : 'a -> bool) (ft : 'a t) : 'a option =
6060
Option.bind (front ft) (fun (remaining, first) ->
6161
if f first then Some first else find_opt f remaining)
62+
63+
let for_all (f : 'a -> bool) : 'a t -> bool =
64+
Fun.compose Option.is_none (find_opt (Fun.compose not f))

lib/Preprocessor.ml

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,22 @@ let rename_arg ({ loc; _ } as expr : Ast.Expr.t) (counter : int) :
1313
loc;
1414
}
1515

16+
let all_atoms (args : Ast.Expr.t FT.t) : unit =
17+
match
18+
args
19+
|> FT.find_opt
20+
@@ Fun.compose
21+
(function
22+
| Ast.Expr.Functor { elements; _ } when FT.is_empty elements ->
23+
false
24+
| _ -> true)
25+
Location.strip_loc
26+
with
27+
| None -> ()
28+
| Some { loc; _ } ->
29+
Logger.error loc "Expected atom.";
30+
exit 1
31+
1632
let compare_clauses (c1 : Ast.ParserClause.t) (c2 : Ast.ParserClause.t) : int =
1733
match (c1, c2) with
1834
| ( { content = Declaration { head = h1; _ }; _ },
@@ -57,12 +73,8 @@ let rec remove_comments (clause : Ast.ParserClause.t) :
5773
| _ -> true
5874
in
5975
match clause with
60-
| {
61-
content =
62-
ParserClause.Directive
63-
({ content = { name = [], { content = "comment"; _ }; _ }; _ }, _);
64-
_;
65-
} ->
76+
| { content = ParserClause.Directive ({ content = header; _ }, _); _ }
77+
when Ast.Expr.match_func header [ "comment" ] ->
6678
None
6779
| { content = ParserClause.Directive (head, body); loc } ->
6880
Some
@@ -72,11 +84,8 @@ let rec remove_comments (clause : Ast.ParserClause.t) :
7284
(head, FT.map (FT.filter_map remove_comments) body);
7385
loc;
7486
}
75-
| {
76-
content =
77-
Declaration { head = { name = [], { content = "comment"; _ }; _ }; _ };
78-
_;
79-
} ->
87+
| { content = Declaration { head; _ }; _ }
88+
when Ast.Expr.match_func head [ "comment" ] ->
8089
None
8190
| { content = Declaration { head; body }; _ } as decl ->
8291
Some
@@ -93,22 +102,26 @@ let rec remove_comments (clause : Ast.ParserClause.t) :
93102
(Location.fmap (fun v -> Ast.Expr.Functor v));
94103
};
95104
}
96-
| { content = QueryConjunction [ _ ]; _ } as query -> Some query
97-
| { content = QueryConjunction []; _ } -> None
105+
| { content = QueryConjunction q; _ } as query when FT.size q = 1 ->
106+
Some query
107+
| { content = QueryConjunction q; _ } when FT.is_empty q -> None
98108
| { content = QueryConjunction queries; loc } ->
99109
let filtered_queries =
100110
FT.map
101111
(fun query ->
102112
remove_comments
103113
@@ {
104-
content = Ast.ParserClause.QueryConjunction [ query ];
114+
content =
115+
Ast.ParserClause.QueryConjunction (FT.singleton query);
105116
loc = query.loc;
106117
})
107118
queries
108119
in
109120
filtered_queries |> FT.concat_map FT.of_option
110121
|> FT.map (function
111-
| { content = Ast.ParserClause.QueryConjunction [ func ]; _ } -> func
122+
| { content = Ast.ParserClause.QueryConjunction q; _ }
123+
when FT.size q = 1 ->
124+
FT.head_exn q
112125
| _ ->
113126
Logger.simply_unreachable
114127
"The conjunctions we constructed are guaranteed not to have this \
@@ -201,7 +214,6 @@ module DependencyGraph = struct
201214
graph
202215

203216
let expand graph : t Error.attempt =
204-
let module FT = BatFingerTree in
205217
let module Set = BatSet.String in
206218
let module Map = BatMap.String in
207219
let atom_to_filepath =
@@ -343,28 +355,26 @@ module Make (Target : TARGET) :
343355
(dependencies, FT.empty) grouped_body
344356
in
345357
let dependencies =
346-
match
347-
(fst head.content.name, Ast.Expr.func_label head.content, body)
348-
with
349-
| [], "import", [] -> (
350-
match head.content.elements with
351-
| [ singleton ] ->
358+
if Ast.Expr.match_func head.content [ "import" ] then
359+
if FT.is_empty body then (
360+
match FT.front head.content.elements with
361+
| Some (rest, singleton) when FT.is_empty rest ->
352362
let external_dep =
353363
Ast.Expr.extract_unqualified_atom singleton
354364
in
355365
DependencyGraph.add filename external_dep dependencies
356-
| [] ->
366+
| None ->
357367
Logger.error head.loc
358368
"Directive 'import' cannot be an empty functor";
359369
exit 1
360370
| _ ->
361371
Logger.error head.loc
362372
"Directive 'import' cannot have multiple expressions within";
363373
exit 1)
364-
| [], "import", _ ->
374+
else (
365375
Logger.error head.loc "Directive 'import' cannot have a body";
366-
exit 1
367-
| _ -> dependencies
376+
exit 1)
377+
else dependencies
368378
in
369379
{
370380
dependencies;

lib/compiler/Declaration.ml

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ let rec compile_expr (expr : Ast.Expr.t) : Beam.Builder.Expr.t =
1212
| Variable var -> Builder.var var
1313
| Nil -> Builder.nil
1414
| Cons (h, t) -> Builder.cons (compile_expr h) (compile_expr t)
15-
| Functor ({ arity; _ } as f) when arity = 0 ->
15+
| Functor ({ elements; _ } as f) when FT.size elements = 0 ->
1616
Builder.atom @@ Ast.Expr.extract_func_label f
1717
| Functor ({ elements; _ } as f) ->
1818
let name = Builder.atom @@ Ast.Expr.extract_func_label f in
19-
Builder.tuple (name :: List.map compile_expr elements)
19+
(* TODO: use finger trees in Builder *)
20+
Builder.tuple @@ FT.to_list (FT.cons (FT.map compile_expr elements) name)
2021
| Integer number -> Builder.int number
2122

2223
let call_with_fresh (name : string) expr =
2324
let open Beam in
2425
Ukanren.call_with_fresh @@ Builder.lambda name expr
2526

2627
let compile_declaration_bodies { module_name; imports; _ }
27-
(clauses : Ast.Clause.decl Location.with_location list) =
28-
if List.is_empty clauses then (
28+
(clauses : Ast.Clause.decl Location.with_location FT.t) =
29+
if FT.is_empty clauses then (
2930
Logger.simply_unreachable "Predicates must have at least one body";
3031
exit 1)
3132
else
@@ -36,8 +37,8 @@ let compile_declaration_bodies { module_name; imports; _ }
3637
let find_variables call = Preprocessor.find_variables (Functor call) in
3738
let vars =
3839
content.body
39-
|> List.map (Fun.compose find_variables Location.strip_loc)
40-
|> List.fold_left Set.union Set.empty
40+
|> FT.map (Fun.compose find_variables Location.strip_loc)
41+
|> FT.fold_left Set.union Set.empty
4142
|> Set.filter (fun name ->
4243
Str.string_match (Str.regexp "^[A-Z]") name 0)
4344
in
@@ -46,50 +47,54 @@ let compile_declaration_bodies { module_name; imports; _ }
4647
(* TODO: We should use locations when calling Beam helpers. They don't use
4748
locations yet, hence they are not being sent as arguments *)
4849
let make_function { content = call; loc } =
49-
let { Ast.Expr.name; elements; arity } = call in
50-
let args = List.map compile_expr elements in
51-
match (name, arity) with
52-
| ([ { content = "karuta"; _ } ], { content = "eq"; _ }), 2 -> (
53-
match args with
54-
| expr1 :: expr2 :: _ -> Ukanren.eq expr1 expr2
55-
| _ ->
56-
Logger.unreachable loc
57-
"Mismatch between arity and length of elements in builtin \
58-
'eq'";
59-
exit 1)
60-
| ([ { content = "karuta"; _ } ], { content = "nat"; _ }), 1 -> (
61-
match args with
62-
| expr1 :: _ -> Ukanren.nat expr1
63-
| _ ->
64-
Logger.unreachable loc
65-
"Mismatch between arity and length of elements in builtin \
66-
'nat'";
67-
exit 1)
68-
| ([], _), _ ->
69-
Builder.call
70-
(Builder.atom @@ Ast.Expr.extract_func_label call)
71-
args
72-
| ((head :: _ as path), { content = fun_name; _ }), _ ->
73-
Builder.call_with_module
74-
(Builder.atom
75-
@@ flat_module_name
76-
(let suffix = List.map Location.strip_loc path in
77-
if BatSet.String.mem head.content imports then suffix
78-
else module_name :: suffix))
79-
(Builder.atom fun_name) args
50+
let { Ast.Expr.name; elements } = call in
51+
let arity = FT.size elements in
52+
let args = FT.map compile_expr elements in
53+
if Ast.Expr.match_func call [ "karuta"; "eq" ] && arity = 2 then (
54+
match FT.to_list args with
55+
| [ expr1; expr2 ] -> Ukanren.eq expr1 expr2
56+
| _ ->
57+
Logger.unreachable loc
58+
"Mismatch between arity and length of elements in builtin \
59+
'eq'";
60+
exit 1)
61+
else if Ast.Expr.match_func call [ "karuta"; "nat" ] && arity = 1 then (
62+
match FT.to_list args with
63+
| [ expr1 ] -> Ukanren.nat expr1
64+
| _ ->
65+
Logger.unreachable loc
66+
"Mismatch between arity and length of elements in builtin \
67+
'nat'";
68+
exit 1)
69+
else
70+
let path, { content = fun_name; _ } = name in
71+
match FT.head path with
72+
| None ->
73+
Builder.call (Builder.atom @@ Ast.Expr.extract_func_label call)
74+
@@ FT.to_list args
75+
| Some head ->
76+
Builder.call_with_module
77+
(Builder.atom
78+
@@ flat_module_name
79+
(let suffix =
80+
FT.to_list @@ FT.map Location.strip_loc path
81+
in
82+
if BatSet.String.mem head.content imports then suffix
83+
else module_name :: suffix))
84+
(Builder.atom fun_name) (FT.to_list args)
8085
in
8186

82-
content.body |> List.map make_function |> Ukanren.conj
87+
content.body |> FT.map make_function |> FT.to_list |> Ukanren.conj
8388
in
8489
Set.fold call_with_fresh vars body
8590
in
86-
clauses |> List.map compile_single_body |> Ukanren.disj
91+
clauses |> FT.map compile_single_body |> FT.to_list |> Ukanren.disj
8792

8893
let compile_multi
8994
(({ name; arity }, first_clause, remaining_clauses) :
9095
Ast.head
9196
* Ast.Clause.decl Location.with_location
92-
* Ast.Clause.decl Location.with_location list)
97+
* Ast.Clause.decl Location.with_location FT.t)
9398
({ env; _ } as compiler : unit t) : unit t =
9499
let declaration =
95100
let args =
@@ -98,7 +103,8 @@ let compile_multi
98103
in
99104
Beam.Builder.single_function_declaration name
100105
(List.map (fun v -> Beam.Builder.Pattern.Variable v) args)
101-
@@ compile_declaration_bodies compiler (first_clause :: remaining_clauses)
106+
@@ compile_declaration_bodies compiler
107+
(FT.cons remaining_clauses first_clause)
102108
in
103109
let export = Beam.Builder.Attribute.export [ (name, arity) ] in
104110
{

lib/compiler/Declaration.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ val call_with_fresh : string -> Builder.Expr.t -> Builder.Expr.t
55
val compile_multi :
66
Ast.head
77
* Ast.Clause.decl Location.with_location
8-
* Ast.Clause.decl Location.with_location list ->
8+
* Ast.Clause.decl Location.with_location FT.t ->
99
unit Types.t ->
1010
unit Types.t

lib/compiler/Lookup.ml

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ let (signature_select : signature selector) = function
2020
`UnexpectedSignature sig_loc
2121

2222
let rec lookup_mod_sig (envs : 'a env BatLazyList.t)
23-
(names : string Location.with_location list) (select : 'a selector) =
24-
let rec lookup_mod_sig_qualified (rest : string Location.with_location list)
23+
(names : string Location.with_location FT.t) (select : 'a selector) =
24+
let rec lookup_mod_sig_qualified (rest : string Location.with_location FT.t)
2525
(value : 'a Location.with_location) =
26-
match rest with
27-
| [] -> `Ok value
28-
| qualifier :: more -> (
26+
match FT.front rest with
27+
| None -> `Ok value
28+
| Some (more, qualifier) -> (
2929
match select value with
3030
| `NestedLookup modules -> (
3131
match BatMap.String.find_opt qualifier.content modules with
@@ -39,11 +39,11 @@ let rec lookup_mod_sig (envs : 'a env BatLazyList.t)
3939
Logger.error sig_loc "Reference is here";
4040
unexpected)
4141
in
42-
match names with
43-
| [] ->
42+
match FT.front names with
43+
| None ->
4444
Logger.simply_unreachable "There should be names in lookup_mod_sig";
4545
exit 1
46-
| first :: rest -> (
46+
| Some (rest, first) -> (
4747
match Lazy.force envs with
4848
| BatLazyList.Cons (env, parent) -> (
4949
match BatMap.String.find_opt first.content env with
@@ -87,9 +87,7 @@ let ancestors_of_compiler (compiler : t) : scope =
8787
let signature (scope : scope)
8888
((qualifiers, unqualified_name) : Ast.Expr.func_label) =
8989
match
90-
lookup_mod_sig scope
91-
(List.append qualifiers [ unqualified_name ])
92-
comptime_select
90+
lookup_mod_sig scope (FT.snoc qualifiers unqualified_name) comptime_select
9391
with
9492
| `Ok { content = Module m; loc } ->
9593
Logger.error unqualified_name.loc "Found module instead of signature";
@@ -101,9 +99,7 @@ let signature (scope : scope)
10199
let m0dule (scope : scope)
102100
((qualifiers, unqualified_name) : Ast.Expr.func_label) =
103101
match
104-
lookup_mod_sig scope
105-
(List.append qualifiers [ unqualified_name ])
106-
comptime_select
102+
lookup_mod_sig scope (FT.snoc qualifiers unqualified_name) comptime_select
107103
with
108104
| `Ok { content = Module module'; loc } -> `Ok (Location.add_loc module' loc)
109105
| `Ok { content = Signature _; loc } ->
@@ -117,7 +113,7 @@ let nested_signature (compiled_signatures : sig_scope) (scope : scope)
117113
((qualifiers, unqualified_name) as names : Ast.Expr.func_label) =
118114
match
119115
lookup_mod_sig compiled_signatures
120-
(List.append qualifiers [ unqualified_name ])
116+
(FT.snoc qualifiers unqualified_name)
121117
signature_select
122118
with
123119
| `Ok _ as ok -> ok

lib/compiler/Types.ml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
module Form = Beam.Core.Form (Beam.Core.Erlang)
2-
module FT = BatFingerTree
32
module Set = BatSet
43

54
type predicate_name = Ast.head [@@deriving show, ord]

0 commit comments

Comments
 (0)