Skip to content

Commit 6e197f2

Browse files
committed
Differentiate between Gospel and OCaml namespaces.
1 parent 2e0f425 commit 6e197f2

9 files changed

Lines changed: 261 additions & 102 deletions

File tree

bin/bin_utils.ml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let path2file p = Filename.basename p |> Filename.chop_extension
1818
(** [check_file file] parses and type checks [file] and creates a corresponding
1919
[.gospel] file in the directory [comp_dir].
2020
@raise Warnings.Error if there is a parsing or typing error in [file]. *)
21-
let check_file ?(comp_dir = "") ?(env = Namespace.empty_env) file =
21+
let check_file ?(comp_dir = "") ?(env = Environment.empty_global_env) file =
2222
(* If the compilation directory is non empty and does not end with "/", add
2323
it. *)
2424
let comp_dir =
@@ -29,7 +29,7 @@ let check_file ?(comp_dir = "") ?(env = Namespace.empty_env) file =
2929
let ocaml = parse_ocaml file in
3030
let module_nm = path2file file in
3131
let sigs = parse_gospel ~filename:file ocaml in
32-
let _, mods = signatures env sigs in
32+
let _, file = signatures env sigs in
3333
(* Stores the Gospel definitions in [file] in a [.gospel] file in the
3434
[_gospel] directory.
3535
@@ -38,6 +38,6 @@ let check_file ?(comp_dir = "") ?(env = Namespace.empty_env) file =
3838
let comp =
3939
open_out_bin (Format.sprintf "%s%s%s" comp_dir module_nm gospel_ext)
4040
in
41-
Marshal.to_channel comp mods [];
41+
Marshal.to_channel comp file [];
4242
close_out comp;
43-
mods
43+
file

bin/bin_utils.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ val gospel_ext : string
1616
(** The extension that compiled gospel files have. *)
1717

1818
val check_file :
19-
?comp_dir:string -> ?env:Namespace.env -> string -> Namespace.mod_defs
19+
?comp_dir:string -> ?env:Environment.global_env -> string -> Typing.file_defs
2020
(** [check_file comp_dir env file] receives an [.mli] file name and type checks
2121
it under [env]. If the file is correctly type checked, we create a file in
2222
directory [comp_dir] with the name [file] where [.mli] has been replaced

bin/check.ml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ let read_gospel_file f =
5050
(* Note: although this explicit type annotation is not strictly necessary for
5151
the code to compile, it ensures that [read_gospel_file] can only be used to
5252
un-marshal values of type [Namespace.mod_defs]. *)
53-
let defs : Namespace.mod_defs =
53+
let defs : Typing.file_defs =
5454
try Marshal.from_channel ic
5555
with Failure _ ->
5656
error
@@ -87,16 +87,21 @@ let run files =
8787
in
8888
let module_nm = path2module file in
8989
let id = Ident.mk_id module_nm Location.none in
90-
Namespace.add_mod env id mods
90+
Typing.add_file env id mods
9191
in
9292
(* Un-marshal the gospel standard library that is created in compile time. *)
93-
let stdlib : Namespace.mod_defs =
93+
let stdlib : Typing.file_defs =
9494
(* At compile time the [%blob] annotation is replaced with the raw string
9595
of the marshalled gospel standard library. *)
9696
Marshal.from_string [%blob "gospelstdlib.gospel"] 0
9797
in
9898

99-
let env = Namespace.init_env stdlib in
99+
let gospel = Namespace.init_gospel_env stdlib.gospel_defs in
100+
let ocaml =
101+
(* TODO: OCaml standard library annotated with Gospel. *)
102+
Namespace.init_ocaml_env Namespace.empty_ocaml_env.defs
103+
in
104+
let env = { Environment.gospel; ocaml } in
100105
let _ =
101106
try List.fold_left check env files
102107
with Warnings.Error e ->

src/environment.ml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
(**************************************************************************)
2+
(* *)
3+
(* GOSPEL -- A Specification Language for OCaml *)
4+
(* *)
5+
(* Copyright (c) 2018- The VOCaL Project *)
6+
(* *)
7+
(* This software is free software, distributed under the MIT license *)
8+
(* (as described in file LICENSE enclosed). *)
9+
(**************************************************************************)
10+
11+
module Env = Map.Make (String)
12+
open Namespace
13+
14+
type local_env = {
15+
term_var : Ident.t Env.t;
16+
(* Local term variables e.g. names bound with a [let] or a quantifier *)
17+
type_vars : (string, Ident.t) Hashtbl.t;
18+
(* Type variables. *)
19+
type_nms : Ident.t Env.t;
20+
(* When processing a recursive type definition, this environment keeps
21+
track of all the type names that are not aliases that the definition
22+
introduces. *)
23+
}
24+
(** Keeps track of the variables within the scope of a term. We use an immutable
25+
environment for term variables since it makes it easier to manage scopes. We
26+
use a mutable table for type variables since there is no notion of scopes
27+
for type variables: any type variable that appears in a Gospel term is
28+
assumed to be defined at the top of the Gospel structure in a universal
29+
quantifier. *)
30+
31+
type global_env = { ocaml : Namespace.env; gospel : Namespace.env }
32+
33+
let empty_global_env =
34+
{ ocaml = Namespace.empty_ocaml_env; gospel = Namespace.empty_gospel_env }
35+
36+
let submodule m = { ocaml = submodule m.ocaml; gospel = submodule m.gospel }
37+
38+
let add_mod m id mod_defs =
39+
{
40+
ocaml = add_mod m.ocaml id mod_defs.ocaml.defs;
41+
gospel = add_mod m.gospel id mod_defs.gospel.defs;
42+
}
43+
44+
let empty_local_env () =
45+
{ term_var = Env.empty; type_vars = Hashtbl.create 100; type_nms = Env.empty }
46+
47+
let add_term_var env id =
48+
{ env with term_var = Env.add id.Ident.id_str id env.term_var }
49+
50+
let mem_term_var env id = Env.mem id env.term_var
51+
let get_term_var env id = Env.find id env.term_var
52+
let add_type_var env id = Hashtbl.add env.type_vars id.Ident.id_str id
53+
let mem_type_var env v = Hashtbl.mem env.type_vars v
54+
let get_type_var env v = Hashtbl.find env.type_vars v
55+
let get_type_vars env = Hashtbl.to_seq_values env.type_vars |> List.of_seq
56+
let refresh_type_vars lenv = { lenv with type_vars = Hashtbl.create 100 }
57+
58+
let add_type_nm env id =
59+
{ env with type_nms = Env.add id.Ident.id_str id env.type_nms }
60+
61+
let mem_type_nm env id = Env.mem id env.type_nms
62+
let get_type_nm env id = Env.find id env.type_nms

src/environment.mli

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
(**************************************************************************)
2+
(* *)
3+
(* GOSPEL -- A Specification Language for OCaml *)
4+
(* *)
5+
(* Copyright (c) 2018- The VOCaL Project *)
6+
(* *)
7+
(* This software is free software, distributed under the MIT license *)
8+
(* (as described in file LICENSE enclosed). *)
9+
(**************************************************************************)
10+
11+
(** [Environment] contains the utilities necessary to keep track of all the
12+
names contained in a Gospel annotated OCaml file. More specifically,
13+
[Environment] defines an environment to keep track of local variables as
14+
well as another environment to keep track of all Gospel and OCaml top level
15+
declaration. *)
16+
17+
type local_env
18+
(** Keeps track of the names that do not exist outside the scope of the Gospel
19+
structure we are currently processing. This includes local term variables,
20+
type variables and recursive top level definitions.
21+
22+
All operations on this type produce no observable changes to it with the
23+
exception of adding type variables. *)
24+
25+
type global_env = { ocaml : Namespace.env; gospel : Namespace.env }
26+
(** Keeps track of the names within the top level. The [ocaml] namespace
27+
represents the set of names defined in the OCaml code and the [gospel]
28+
namespace the set of names defined within Gospel annotations.
29+
30+
Exception: The [gospel] namespace also includes module names that are
31+
defined in the OCaml code. However, the only names that are accessible from
32+
those modules are their [gospel] definitions. *)
33+
34+
val empty_global_env : global_env
35+
(** [empty_global_env] contains only primitive Gospel types. *)
36+
37+
val submodule : global_env -> global_env
38+
(** [submodule env] creates an environment where there are *)
39+
40+
val add_mod : global_env -> Ident.t -> global_env -> global_env
41+
(** [add_mod m id env] updates the environment [env] with the definitions in
42+
[m].*)
43+
44+
(* The following functions are used to keep track of names that are defined
45+
within the scope of a single structure. *)
46+
47+
(* When a function receives some [local_env] and returns an updated [local_env],
48+
any type variables added to either environment will be accessible from
49+
both. *)
50+
51+
val empty_local_env : unit -> local_env
52+
(** [empty_local_env ()] Creates an environment with no variables in scope. *)
53+
54+
val add_term_var : local_env -> Ident.t -> local_env
55+
(** [add_term_var lenv v] Adds a the local term variable [id] to [lenv]. *)
56+
57+
val mem_term_var : local_env -> string -> bool
58+
(** [mem_term_var lenv v] Checks if [v] is a local term variable in [lenv]. *)
59+
60+
val get_term_var : local_env -> string -> Ident.t
61+
(** [mem_term_var lenv v] Gets the variable [v] from [lenv]. *)
62+
63+
val add_type_var : local_env -> Ident.t -> unit
64+
(** [add_type_var lenv v] adds the type variable [v] to [lenv]. *)
65+
66+
val mem_type_var : local_env -> string -> bool
67+
(** [lem_type_var lenv v] checks if the variable [v] has been defined in [lenv].
68+
*)
69+
70+
val get_type_var : local_env -> string -> Ident.t
71+
(** [get_type_var lenv v] returns the identifier associated with variable [v].
72+
*)
73+
74+
val get_type_vars : local_env -> Ident.t list
75+
(** [get_type_vars lenv] returns the list of type variables found in the current
76+
term. *)
77+
78+
val refresh_type_vars : local_env -> local_env
79+
(** [refresh_type_vars lenv] returns a new [local_env] with the same definitions
80+
as [lenv] except the set of type variables is empty. Additionally, any
81+
changes to the set of type variables in either environment are not reflected
82+
in both objects. *)
83+
84+
val add_type_nm : local_env -> Ident.t -> local_env
85+
(** [add_type_nm lenv nm] adds the type name [id] to [lenv]. *)
86+
87+
val mem_type_nm : local_env -> string -> bool
88+
(** [lem_type_nm lenv v] checks if the type name [v] has been defined in [lenv].
89+
*)
90+
91+
val get_type_nm : local_env -> string -> Ident.t
92+
(** [get_type_nm lenv v] returns the identifier associated with variable [v]. *)

src/namespace.ml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -451,15 +451,17 @@ let fun_env =
451451
let disj_id = Ident.mk_id disj Location.none in
452452
Env.empty |> Env.add conj (op_info conj_id) |> Env.add disj (op_info disj_id)
453453

454-
(** The empty environment. The only names that it contains with are primitive
455-
Gospel type definitions. *)
456-
let empty_env =
454+
(** The empty Gospel environment. The only names that it contains with are
455+
primitive Gospel type definitions. *)
456+
let empty_gospel_env =
457457
{ defs = empty_defs; scope = { empty_defs with type_env; fun_env } }
458458

459+
let empty_ocaml_env = { defs = empty_defs; scope = empty_defs }
460+
459461
(** The initial environment for every Gospel file. The only names that it
460462
contains with are primitive Gospel type definitions and the definitions
461463
within the Gospel standard library. *)
462-
let init_env stdlib =
464+
let init_gospel_env stdlib =
463465
(* Combines two environments whose domains are disjoint. *)
464466
let union e1 e2 = Env.union (fun _ _ _ -> assert false) e1 e2 in
465467

@@ -474,8 +476,10 @@ let init_env stdlib =
474476
let mod_env = Env.add Ident.stdlib_id.id_str stdlib_info stdlib.mod_env in
475477
{ defs = empty_defs; scope = { stdlib with type_env; fun_env; mod_env } }
476478

477-
let scope e = e.scope
478-
let defs e = e.defs
479+
let init_ocaml_env stdlib =
480+
let stdlib_info = { mid = Ident.stdlib_id; mdefs = stdlib } in
481+
let mod_env = Env.add Ident.stdlib_id.id_str stdlib_info stdlib.mod_env in
482+
{ defs = empty_defs; scope = { stdlib with mod_env } }
479483

480484
(** [add_def f env] adds a new definition to the list of definitions and the
481485
scope. *)

src/namespace.mli

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,19 @@
1313

1414
open Uast
1515

16-
type env
17-
(** The (immutable) environment that keeps track of the definitions in the top
18-
level. *)
19-
2016
type mod_defs
2117
(** A set of top level definitions *)
2218

23-
val scope : env -> mod_defs
24-
(** Gets the set of definitions in scope *)
25-
26-
val defs : env -> mod_defs
27-
(** Gets the set of definitions contained within the submodule we are currently
28-
processing. *)
19+
type env = {
20+
defs : mod_defs;
21+
(* Contains the top level definitions in the current module *)
22+
scope : mod_defs;
23+
(* Contains the top level definitions currently in scope. A definition is in
24+
scope if it has been previously defined or exposed through an [open]. Not
25+
necessarily a subset of [defs_mod], since opening modules can shadow
26+
previous definitions. *)
27+
}
28+
(** The environment that keeps track of the definitions in the top level. *)
2929

3030
(* Records containing the information necessary regarding each top level
3131
definitions *)
@@ -99,17 +99,29 @@ val local_open : mod_defs -> ParseUast.qualid -> IdUast.qualid * mod_defs
9999
scope within a term and returns a new local scope with all the definitions
100100
in module [id]. *)
101101

102-
val empty_env : env
103-
(** The empty environment. The only names in scope are primitive Gospel types.
104-
This should be the initial environment when processing the Gospel standard
105-
library. *)
106-
107-
val init_env : mod_defs -> env
108-
(** [init_env stdlib] receives the definitions in the Gospel standard library
109-
[stdlib] and creates an environment where every name in [stdlib] is in scope
110-
as well as every name in [empty_env]. Additionally creates a module named
111-
[Gospelstdlib] with every definition in [stdlib]. This should be the initial
112-
environment for any Gospel project. *)
102+
val empty_gospel_env : env
103+
(** The empty Gospel environment. The only names in scope are primitive Gospel
104+
types. This should be the initial Gospel namespace when processing the
105+
Gospel standard library. *)
106+
107+
val empty_ocaml_env : env
108+
(** The empty OCaml environment. Does not contain any names in scope. This
109+
should be the initial OCaml namespace when processing the Gospel standard
110+
library.*)
111+
112+
val init_gospel_env : mod_defs -> env
113+
(** [init_gospel_env stdlib] receives the definitions in the Gospel standard
114+
library [stdlib] and creates an environment where every name in [stdlib] is
115+
in scope as well as every name in [empty_env]. Additionally creates a module
116+
named [Gospelstdlib] with every definition in [stdlib]. This should be the
117+
initial Gospel namespace for any Gospel project. *)
118+
119+
val init_ocaml_env : mod_defs -> env
120+
(** [init_ocaml_env stdlib] receives the definitions in the Gospel standard
121+
library [stdlib] and creates an environment where every name in [stdlib] is
122+
in scope as well as every name in [empty_env]. Additionally creates a module
123+
named [Stdlib] with every definition in [stdlib]. This should be the initial
124+
OCaml namespace for any Gospel project. *)
113125

114126
val submodule : env -> env
115127
(** Returns a new environment for processing a submodule. The variables in scope

0 commit comments

Comments
 (0)