| @@ -0,0 +1,62 @@ | ||
| open Grain_parsing | ||
| open Grain_typed | ||
| open Anftree | ||
| type loc = Location.t | ||
| type env = Env.t | ||
| type ident = Ident.t | ||
| let default_loc = Location.dummy_loc | ||
| let default_env = Env.empty | ||
| let or_default_loc = Option.default default_loc | ||
| let or_default_env = Option.default default_env | ||
| module Imm = struct | ||
| let mk ?loc ?env d = | ||
| {imm_desc=d; | ||
| imm_loc=or_default_loc loc; | ||
| imm_env=or_default_env env} | ||
| let id ?loc ?env id = mk ?loc ?env (ImmId id) | ||
| let const ?loc ?env const = mk ?loc ?env (ImmConst const) | ||
| end | ||
| module Comp = struct | ||
| let mk ?loc ?env d = | ||
| {comp_desc=d; | ||
| comp_loc=or_default_loc loc; | ||
| comp_env=or_default_env env} | ||
| let imm ?loc ?env imm = mk ?loc ?env (CImmExpr imm) | ||
| let prim1 ?loc ?env p1 a = mk ?loc ?env (CPrim1(p1, a)) | ||
| let prim2 ?loc ?env p2 a1 a2 = mk ?loc ?env (CPrim2(p2, a1, a2)) | ||
| let tuple ?loc ?env elts = mk ?loc ?env (CTuple elts) | ||
| let tuple_get ?loc ?env tup idx = mk ?loc ?env (CGetTupleItem(tup, idx)) | ||
| let tuple_set ?loc ?env tup idx value = mk ?loc ?env (CSetTupleItem(tup, idx, value)) | ||
| let if_ ?loc ?env cond tru fals = mk ?loc ?env (CIf(cond, tru, fals)) | ||
| let switch ?loc ?env arg branches = mk ?loc ?env (CSwitch(arg, branches)) | ||
| let app ?loc ?env func args = mk ?loc ?env (CApp(func, args)) | ||
| let app_builtin ?loc ?env modname name args = mk ?loc ?env (CAppBuiltin(modname, name, args)) | ||
| let lambda ?loc ?env args body = mk ?loc ?env (CLambda(args, body)) | ||
| let string ?loc ?env s = mk ?loc ?env (CString s) | ||
| end | ||
| module AExp = struct | ||
| let mk ?loc ?env d = | ||
| {anf_desc=d; | ||
| anf_loc=or_default_loc loc; | ||
| anf_env=or_default_env env} | ||
| let let_ ?loc ?env ?glob:(glob=Nonglobal) rec_flag binds body = mk ?loc ?env (AELet(glob, rec_flag, binds, body)) | ||
| let seq ?loc ?env hd tl = mk ?loc ?env (AESeq(hd, tl)) | ||
| let comp ?loc ?env e = mk ?loc ?env (AEComp(e)) | ||
| end | ||
| module Imp = struct | ||
| let mk use_id d s= | ||
| {imp_use_id=use_id; | ||
| imp_desc=d; | ||
| imp_shape=s;} | ||
| let grain_value a md name s = mk a (GrainValue(md, name)) s | ||
| let wasm_func a md name s = mk a (WasmFunction(md, name)) s | ||
| let js_func a md name s = mk a (JSFunction(md, name)) s | ||
| end | ||
| @@ -0,0 +1,44 @@ | ||
| open Grain_parsing | ||
| open Grain_typed | ||
| open Types | ||
| open Anftree | ||
| type loc = Location.t | ||
| type env = Env.t | ||
| type ident = Ident.t | ||
| module Imm : sig | ||
| val mk : ?loc:loc -> ?env:env -> imm_expression_desc -> imm_expression | ||
| val id : ?loc:loc -> ?env:env -> ident -> imm_expression | ||
| val const : ?loc:loc -> ?env:env -> constant -> imm_expression | ||
| end | ||
| module Comp : sig | ||
| val mk : ?loc:loc -> ?env:env -> comp_expression_desc -> comp_expression | ||
| val imm : ?loc:loc -> ?env:env -> imm_expression -> comp_expression | ||
| val prim1 : ?loc:loc -> ?env:env -> prim1 -> imm_expression -> comp_expression | ||
| val prim2 : ?loc:loc -> ?env:env -> prim2 -> imm_expression -> imm_expression -> comp_expression | ||
| val tuple : ?loc:loc -> ?env:env -> imm_expression list -> comp_expression | ||
| val tuple_get : ?loc:loc -> ?env:env -> int32 -> imm_expression -> comp_expression | ||
| val tuple_set : ?loc:loc -> ?env:env -> int32 -> imm_expression -> imm_expression -> comp_expression | ||
| val if_ : ?loc:loc -> ?env:env -> imm_expression -> anf_expression -> anf_expression -> comp_expression | ||
| val switch : ?loc:loc -> ?env:env -> imm_expression -> (int * anf_expression) list -> comp_expression | ||
| val app : ?loc:loc -> ?env:env -> imm_expression -> imm_expression list -> comp_expression | ||
| val app_builtin : ?loc:loc -> ?env:env -> string -> string -> imm_expression list -> comp_expression | ||
| val lambda : ?loc:loc -> ?env:env -> ident list -> anf_expression -> comp_expression | ||
| val string : ?loc:loc -> ?env:env -> string -> comp_expression | ||
| end | ||
| module AExp : sig | ||
| val mk : ?loc:loc -> ?env:env -> anf_expression_desc -> anf_expression | ||
| val let_ : ?loc:loc -> ?env:env -> ?glob:global_flag -> rec_flag -> (ident * comp_expression) list -> anf_expression -> anf_expression | ||
| val seq : ?loc:loc -> ?env:env -> comp_expression -> anf_expression -> anf_expression | ||
| val comp : ?loc:loc -> ?env:env -> comp_expression -> anf_expression | ||
| end | ||
| module Imp : sig | ||
| val mk : ident -> import_desc -> import_shape -> import_spec | ||
| val grain_value : ident -> string -> string -> import_shape -> import_spec | ||
| val wasm_func : ident -> string -> string -> import_shape -> import_spec | ||
| val js_func : ident -> string -> string -> import_shape -> import_spec | ||
| end |
| @@ -0,0 +1,90 @@ | ||
| open Grain_typed | ||
| open Anftree | ||
| let rec anf_free_vars_help env (a : anf_expression) = | ||
| match a.anf_desc with | ||
| | AESeq(fst, rest) -> | ||
| Ident.Set.union | ||
| (comp_free_vars_help env fst) | ||
| (anf_free_vars_help env rest) | ||
| | AEComp(c) -> comp_free_vars_help env c | ||
| | AELet(_, recflag, binds, body) -> | ||
| let with_names = List.fold_left (fun acc (id, _) -> Ident.Set.add id acc) env binds in | ||
| let free_binds = | ||
| match recflag with | ||
| | Recursive -> List.fold_left | ||
| (fun acc (_, body) -> Ident.Set.union acc (comp_free_vars_help with_names body)) | ||
| Ident.Set.empty | ||
| binds | ||
| | Nonrecursive -> List.fold_left | ||
| (fun acc (_, body) -> Ident.Set.union acc (comp_free_vars_help env body)) | ||
| Ident.Set.empty | ||
| binds in | ||
| Ident.Set.union free_binds (anf_free_vars_help with_names body) | ||
| and comp_free_vars_help env (c : comp_expression) = | ||
| match c.comp_desc with | ||
| | CLambda(args, body) -> | ||
| anf_free_vars_help (Ident.Set.union env (Ident.Set.of_list args)) body | ||
| | CIf(cond, thn, els) -> | ||
| Ident.Set.union (imm_free_vars_help env cond) @@ | ||
| Ident.Set.union (anf_free_vars_help env thn) (anf_free_vars_help env els) | ||
| | CSwitch(arg, branches) -> | ||
| List.fold_left (fun acc (_, b) -> Ident.Set.union (anf_free_vars_help env b) acc) | ||
| (imm_free_vars_help env arg) | ||
| branches | ||
| | CPrim1(_, arg) -> imm_free_vars_help env arg | ||
| | CPrim2(_, arg1, arg2) -> | ||
| Ident.Set.union | ||
| (imm_free_vars_help env arg1) | ||
| (imm_free_vars_help env arg2) | ||
| | CApp(fn, args) -> | ||
| List.fold_left (fun acc a -> Ident.Set.union (imm_free_vars_help env a) acc) | ||
| (imm_free_vars_help env fn) | ||
| args | ||
| | CAppBuiltin(_, _, args) -> | ||
| List.fold_left (fun acc a -> Ident.Set.union (imm_free_vars_help env a) acc) | ||
| Ident.Set.empty | ||
| args | ||
| | CTuple(args) -> | ||
| List.fold_left (fun acc a -> Ident.Set.union (imm_free_vars_help env a) acc) | ||
| Ident.Set.empty | ||
| args | ||
| | CGetTupleItem(_, arg) -> | ||
| imm_free_vars_help env arg | ||
| | CSetTupleItem(_, arg1, arg2) -> | ||
| Ident.Set.union | ||
| (imm_free_vars_help env arg1) | ||
| (imm_free_vars_help env arg2) | ||
| | CString(s) -> Ident.Set.empty | ||
| | CImmExpr(i) -> imm_free_vars_help env i | ||
| and imm_free_vars_help env (i : imm_expression) = | ||
| match i.imm_desc with | ||
| | ImmId(x) when not(Ident.Set.mem x env) -> Ident.Set.singleton x | ||
| | _ -> Ident.Set.empty | ||
| let anf_free_vars = anf_free_vars_help Ident.Set.empty | ||
| let comp_free_vars = comp_free_vars_help Ident.Set.empty | ||
| let imm_free_vars = imm_free_vars_help Ident.Set.empty | ||
| let rec anf_count_vars a = | ||
| match a.anf_desc with | ||
| | AELet(_, recflag, binds, body) -> | ||
| let max_binds = List.fold_left max 0 @@ List.map (fun (_, c) -> comp_count_vars c) binds in | ||
| begin match recflag with | ||
| | Recursive -> (List.length binds) + (max max_binds (anf_count_vars body)) | ||
| | Nonrecursive -> max max_binds ((List.length binds) + (anf_count_vars body)) | ||
| end | ||
| | AESeq(hd, tl) -> max (comp_count_vars hd) (anf_count_vars tl) | ||
| | AEComp(c) -> comp_count_vars c | ||
| and comp_count_vars c = | ||
| match c.comp_desc with | ||
| | CIf(_, t, f) -> max (anf_count_vars t) (anf_count_vars f) | ||
| | CSwitch(_, bs) -> List.fold_left max 0 @@ List.map (fun (_, b) -> anf_count_vars b) bs | ||
| | CApp(_, args) -> List.length args | ||
| | CAppBuiltin(_, _, args) -> List.length args | ||
| | _ -> 0 |
| @@ -0,0 +1,9 @@ | ||
| open Grain_typed | ||
| open Anftree | ||
| val anf_free_vars : anf_expression -> Ident.Set.t | ||
| val comp_free_vars : comp_expression -> Ident.Set.t | ||
| val imm_free_vars : imm_expression -> Ident.Set.t | ||
| val anf_count_vars : anf_expression -> int | ||
| val comp_count_vars : comp_expression -> int |
| @@ -0,0 +1,112 @@ | ||
| (** Linearized (ANF) AST. *) | ||
| open Sexplib.Conv | ||
| open Grain_parsing | ||
| open Grain_typed | ||
| open Types | ||
| type rec_flag = Asttypes.rec_flag = Nonrecursive | Recursive | ||
| type global_flag = Global | Nonglobal [@@deriving sexp] | ||
| type 'a loc = 'a Location.loc | ||
| type prim1 = Parsetree.prim1 = | ||
| | Add1 | ||
| | Sub1 | ||
| | Not | ||
| | IsNum | ||
| | IsBool | ||
| | IsTuple | ||
| type prim2 = Parsetree.prim2 = | ||
| | Plus | ||
| | Minus | ||
| | Times | ||
| | Less | ||
| | Greater | ||
| | LessEq | ||
| | GreaterEq | ||
| | Eq | ||
| | And | ||
| | Or | ||
| let prim1_of_sexp, sexp_of_prim1 = Parsetree.prim1_of_sexp, Parsetree.sexp_of_prim1 | ||
| let prim2_of_sexp, sexp_of_prim2 = Parsetree.prim2_of_sexp, Parsetree.sexp_of_prim2 | ||
| let locs_disabled _ = not !Grain_utils.Config.sexp_locs_enabled | ||
| (** Immediate expressions (requiring no computation) *) | ||
| type imm_expression = { | ||
| imm_desc: imm_expression_desc; | ||
| imm_loc: Location.t [@sexp_drop_if locs_disabled]; | ||
| imm_env: Env.t sexp_opaque; | ||
| } [@@deriving sexp] | ||
| and imm_expression_desc = | ||
| | ImmId of Ident.t | ||
| | ImmConst of constant | ||
| [@@deriving sexp] | ||
| (** Compound expressions (non-let-bound) *) | ||
| type comp_expression = { | ||
| comp_desc: comp_expression_desc; | ||
| comp_loc: Location.t [@sexp_drop_if locs_disabled]; | ||
| comp_env: Env.t sexp_opaque; | ||
| } | ||
| [@@deriving sexp] | ||
| and comp_expression_desc = | ||
| | CImmExpr of imm_expression | ||
| | CPrim1 of prim1 * imm_expression | ||
| | CPrim2 of prim2 * imm_expression * imm_expression | ||
| | CTuple of imm_expression list | ||
| | CGetTupleItem of int32 * imm_expression | ||
| | CSetTupleItem of int32 * imm_expression * imm_expression | ||
| | CIf of imm_expression * anf_expression * anf_expression | ||
| | CSwitch of imm_expression * (int * anf_expression) list | ||
| | CApp of imm_expression * imm_expression list | ||
| | CAppBuiltin of string * string * imm_expression list | ||
| | CLambda of Ident.t list * anf_expression | ||
| | CString of string | ||
| [@@deriving sexp] | ||
| (** Compound expressions (possibly let-bound) | ||
| TODO: better name *) | ||
| and anf_expression = { | ||
| anf_desc: anf_expression_desc; | ||
| anf_loc: Location.t [@sexp_drop_if locs_disabled]; | ||
| anf_env: Env.t sexp_opaque; | ||
| } | ||
| [@@deriving sexp] | ||
| and anf_expression_desc = | ||
| | AELet of global_flag * rec_flag * (Ident.t * comp_expression) list * anf_expression | ||
| | AESeq of comp_expression * anf_expression | ||
| | AEComp of comp_expression | ||
| [@@deriving sexp] | ||
| type import_shape = | ||
| | FunctionShape of int * int | ||
| | GlobalShape | ||
| [@@deriving sexp] | ||
| type import_desc = | ||
| | GrainValue of string * string | ||
| | WasmFunction of string * string | ||
| | JSFunction of string * string | ||
| [@@deriving sexp] | ||
| type import_spec = { | ||
| imp_use_id: Ident.t; (* <- internal references to the name will use this *) | ||
| imp_desc: import_desc; | ||
| imp_shape: import_shape; | ||
| } | ||
| [@@deriving sexp] | ||
| type anf_program = { | ||
| body: anf_expression; | ||
| env: Env.t sexp_opaque; | ||
| imports: import_spec list; | ||
| signature: Cmi_format.cmi_infos; | ||
| } [@@deriving sexp] |
| @@ -0,0 +1,107 @@ | ||
| (** Linearized (ANF) AST. *) | ||
| open Sexplib.Conv | ||
| open Grain_parsing | ||
| open Grain_typed | ||
| open Types | ||
| type rec_flag = Asttypes.rec_flag = Nonrecursive | Recursive | ||
| type global_flag = Global | Nonglobal [@@deriving sexp] | ||
| type 'a loc = 'a Location.loc | ||
| type prim1 = Parsetree.prim1 = | ||
| | Add1 | ||
| | Sub1 | ||
| | Not | ||
| | IsNum | ||
| | IsBool | ||
| | IsTuple | ||
| type prim2 = Parsetree.prim2 = | ||
| | Plus | ||
| | Minus | ||
| | Times | ||
| | Less | ||
| | Greater | ||
| | LessEq | ||
| | GreaterEq | ||
| | Eq | ||
| | And | ||
| | Or | ||
| (** Immediate expressions (requiring no computation) *) | ||
| type imm_expression = { | ||
| imm_desc: imm_expression_desc; | ||
| imm_loc: Location.t; | ||
| imm_env: Env.t; | ||
| } [@@deriving sexp] | ||
| and imm_expression_desc = | ||
| | ImmId of Ident.t | ||
| | ImmConst of constant | ||
| [@@deriving sexp] | ||
| (** Compound expressions (non-let-bound) *) | ||
| type comp_expression = { | ||
| comp_desc: comp_expression_desc; | ||
| comp_loc: Location.t; | ||
| comp_env: Env.t; | ||
| } | ||
| [@@deriving sexp] | ||
| and comp_expression_desc = | ||
| | CImmExpr of imm_expression | ||
| | CPrim1 of prim1 * imm_expression | ||
| | CPrim2 of prim2 * imm_expression * imm_expression | ||
| | CTuple of imm_expression list | ||
| | CGetTupleItem of int32 * imm_expression | ||
| | CSetTupleItem of int32 * imm_expression * imm_expression | ||
| | CIf of imm_expression * anf_expression * anf_expression | ||
| | CSwitch of imm_expression * (int * anf_expression) list | ||
| | CApp of imm_expression * imm_expression list | ||
| | CAppBuiltin of string * string * imm_expression list (* Unwrapped function call (to WASM functions) *) | ||
| | CLambda of Ident.t list * anf_expression | ||
| | CString of string | ||
| [@@deriving sexp] | ||
| (** Compound expressions (possibly let-bound) | ||
| TODO: better name *) | ||
| and anf_expression = { | ||
| anf_desc: anf_expression_desc; | ||
| anf_loc: Location.t; | ||
| anf_env: Env.t; | ||
| } | ||
| [@@deriving sexp] | ||
| and anf_expression_desc = | ||
| | AELet of global_flag * rec_flag * (Ident.t * comp_expression) list * anf_expression | ||
| | AESeq of comp_expression * anf_expression | ||
| | AEComp of comp_expression | ||
| [@@deriving sexp] | ||
| type import_shape = | ||
| | FunctionShape of int * int | ||
| | GlobalShape | ||
| [@@deriving sexp] | ||
| type import_desc = | ||
| | GrainValue of string * string | ||
| | WasmFunction of string * string | ||
| | JSFunction of string * string | ||
| [@@deriving sexp] | ||
| type import_spec = { | ||
| imp_use_id: Ident.t; (* <- internal references to the name will use this *) | ||
| imp_desc: import_desc; | ||
| imp_shape: import_shape; | ||
| } | ||
| [@@deriving sexp] | ||
| type anf_program = { | ||
| body: anf_expression; | ||
| env: Env.t; | ||
| imports: import_spec list; | ||
| signature: Cmi_format.cmi_infos; | ||
| } [@@deriving sexp] |
| @@ -0,0 +1,9 @@ | ||
| (jbuild_version 1) | ||
| (library | ||
| ((name grain_middle_end) | ||
| (public_name grain_middle_end) | ||
| (synopsis "Grain compiler middle end") | ||
| (libraries (grain_parsing grain_typed grain_utils ppx_deriving ppx_sexp_conv sexplib)) | ||
| (preprocess (pps (ppx_sexp_conv ppx_deriving))))) | ||
| @@ -0,0 +1,5 @@ | ||
| let optimize_program (prog : Anftree.anf_program) : Anftree.anf_program = | ||
| (* TODO: Port optimization to new tree *) | ||
| prog |
| @@ -0,0 +1,6 @@ | ||
| # Parsing | ||
| This submodule contains code used for the Grain frontend. | ||
| While this will certainly evolve over time, the initial version of this | ||
| library is heavily inspired by the `parsing` directory of the OCaml | ||
| compiler; we try to use their idioms, since they know better than anyone | ||
| the best way to write a compiler in OCaml. |
| @@ -0,0 +1,159 @@ | ||
| (* This file is mostly copied from OCaml's parsing/ast_helper.ml. | ||
| The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) | ||
| (* *) | ||
| (* Copyright 1996 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Parsetree | ||
| type id = Identifier.t loc | ||
| type str = string loc | ||
| type loc = Location.t | ||
| let default_loc_src = ref (fun () -> Location.dummy_loc) | ||
| let with_default_loc_src ls f = | ||
| let old = !default_loc_src in | ||
| default_loc_src := ls; | ||
| try let r = f () in default_loc_src := old; r | ||
| with exn -> default_loc_src := old; raise exn | ||
| let with_default_loc l = | ||
| with_default_loc_src (fun() -> l) | ||
| module Const = struct | ||
| let string s = PConstString s | ||
| let int i = PConstNumber i | ||
| let bool b = PConstBool b | ||
| end | ||
| module Typ = struct | ||
| let mk ?loc d = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {ptyp_desc=d; ptyp_loc=loc} | ||
| let any ?loc () = mk ?loc PTyAny | ||
| let var ?loc a = mk ?loc (PTyVar a) | ||
| let arrow ?loc a b = mk ?loc (PTyArrow(a, b)) | ||
| let tuple ?loc a = mk ?loc (PTyTuple a) | ||
| let constr ?loc a b = mk ?loc (PTyConstr(a, b)) | ||
| let poly ?loc a b = mk ?loc (PTyPoly(a, b)) | ||
| end | ||
| module CDecl = struct | ||
| let mk ?loc n a = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pcd_name=n; pcd_args=a; pcd_loc=loc} | ||
| let singleton ?loc n = mk ?loc n PConstrSingleton | ||
| let tuple ?loc n a = mk ?loc n (PConstrTuple a) | ||
| end | ||
| module Dat = struct | ||
| let mk ?loc n t k = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pdata_name=n; pdata_params=t; pdata_kind=k; pdata_loc=loc} | ||
| let variant ?loc n t cdl = mk ?loc n t (PDataVariant cdl) | ||
| end | ||
| module Pat = struct | ||
| let mk ?loc d = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {ppat_desc=d; ppat_loc=loc} | ||
| let any ?loc () = mk ?loc PPatAny | ||
| let var ?loc a = mk ?loc (PPatVar a) | ||
| let tuple ?loc a = mk ?loc (PPatTuple a) | ||
| let constant ?loc a = mk ?loc (PPatConstant a) | ||
| let constraint_ ?loc a b = mk ?loc (PPatConstraint(a, b)) | ||
| let construct ?loc a b = mk ?loc (PPatConstruct(a, b)) | ||
| let or_ ?loc a b = mk ?loc (PPatOr(a, b)) | ||
| let alias ?loc a b = mk ?loc (PPatAlias(a, b)) | ||
| end | ||
| module Exp = struct | ||
| let mk ?loc d = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pexp_desc=d; pexp_loc=loc} | ||
| let ident ?loc a = mk ?loc (PExpId a) | ||
| let constant ?loc a = mk ?loc (PExpConstant a) | ||
| let tuple ?loc a = mk ?loc (PExpTuple a) | ||
| let let_ ?loc a b c = mk ?loc (PExpLet(a, b, c)) | ||
| let match_ ?loc a b = mk ?loc (PExpMatch(a, b)) | ||
| let prim1 ?loc a b = mk ?loc (PExpPrim1(a, b)) | ||
| let prim2 ?loc a b c = mk ?loc (PExpPrim2(a, b, c)) | ||
| let if_ ?loc a b c = mk ?loc (PExpIf(a, b, c)) | ||
| let lambda ?loc a b = mk ?loc (PExpLambda(a, b)) | ||
| let apply ?loc a b = mk ?loc (PExpApp(a, b)) | ||
| let block ?loc a = mk ?loc (PExpBlock a) | ||
| let null ?loc () = mk ?loc PExpNull | ||
| end | ||
| module Top = struct | ||
| let mk ?loc d = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {ptop_desc=d; ptop_loc=loc} | ||
| let foreign ?loc d = mk ?loc (PTopForeign d) | ||
| let import ?loc i = mk ?loc (PTopImport i) | ||
| let data ?loc d = mk ?loc (PTopData d) | ||
| let let_ ?loc r vb = mk ?loc (PTopLet(r, vb)) | ||
| end | ||
| module Val = struct | ||
| let mk ?loc ~mod_ ~name ~typ ~prim = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| { | ||
| pval_mod=mod_; | ||
| pval_name=name; | ||
| pval_type=typ; | ||
| pval_prim=prim; | ||
| pval_loc=loc; | ||
| } | ||
| end | ||
| module Vb = struct | ||
| let mk ?loc p e = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pvb_pat=p; pvb_expr=e; pvb_loc=loc} | ||
| end | ||
| module Mb = struct | ||
| let mk ?loc p e = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pmb_pat=p; pmb_body=e; pmb_loc=loc} | ||
| end | ||
| module Imp = struct | ||
| let mk ?loc m = | ||
| let loc = match loc with | ||
| | None -> (!default_loc_src)() | ||
| | Some l -> l in | ||
| {pimp_mod=m; pimp_loc=loc} | ||
| end | ||
| @@ -0,0 +1,112 @@ | ||
| (* This file is largely copied from OCaml's parsing/ast_helper.mli. | ||
| The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) | ||
| (* *) | ||
| (* Copyright 1996 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Parsetree | ||
| type id = Identifier.t loc | ||
| type str = string loc | ||
| type loc = Location.t | ||
| val default_loc_src: (unit -> loc) ref | ||
| (** Default value for all optional location arguments. *) | ||
| val with_default_loc: loc -> (unit -> 'a) -> 'a | ||
| (** Set the [default_loc] within the scope of the execution | ||
| of the provided function. *) | ||
| val with_default_loc_src: (unit -> loc) -> (unit -> 'a) -> 'a | ||
| module Const : sig | ||
| val string : string -> constant | ||
| val int : int -> constant | ||
| val bool : bool -> constant | ||
| end | ||
| module Typ : sig | ||
| val mk: ?loc:loc -> parsed_type_desc -> parsed_type | ||
| val any: ?loc:loc -> unit -> parsed_type | ||
| val var: ?loc:loc -> string -> parsed_type | ||
| val arrow: ?loc:loc -> parsed_type list -> parsed_type -> parsed_type | ||
| val tuple: ?loc:loc -> parsed_type list -> parsed_type | ||
| val constr: ?loc:loc -> id -> parsed_type list -> parsed_type | ||
| val poly: ?loc:loc -> str list -> parsed_type -> parsed_type | ||
| end | ||
| module CDecl : sig | ||
| val mk: ?loc:loc -> str -> constructor_arguments -> constructor_declaration | ||
| val singleton: ?loc:loc -> str -> constructor_declaration | ||
| val tuple: ?loc:loc -> str -> parsed_type list -> constructor_declaration | ||
| end | ||
| module Dat : sig | ||
| val mk: ?loc:loc -> str -> parsed_type list -> data_kind -> data_declaration | ||
| val variant: ?loc:loc -> str -> parsed_type list -> constructor_declaration list -> data_declaration | ||
| end | ||
| module Pat : sig | ||
| val mk: ?loc:loc -> pattern_desc -> pattern | ||
| val any: ?loc:loc -> unit -> pattern | ||
| val var: ?loc:loc -> str -> pattern | ||
| val tuple: ?loc:loc -> pattern list -> pattern | ||
| val constant: ?loc:loc -> constant -> pattern | ||
| val constraint_: ?loc:loc -> pattern -> parsed_type -> pattern | ||
| val construct: ?loc:loc -> id -> pattern list -> pattern | ||
| val or_: ?loc:loc -> pattern -> pattern -> pattern | ||
| val alias: ?loc:loc -> pattern -> str -> pattern | ||
| end | ||
| module Exp: sig | ||
| val mk: ?loc:loc -> expression_desc -> expression | ||
| val ident: ?loc:loc -> id -> expression | ||
| val constant: ?loc:loc -> constant -> expression | ||
| val tuple: ?loc:loc -> expression list -> expression | ||
| val let_: ?loc:loc -> rec_flag -> value_binding list -> expression -> expression | ||
| val match_: ?loc:loc -> expression -> match_branch list -> expression | ||
| val prim1: ?loc:loc -> prim1 -> expression -> expression | ||
| val prim2: ?loc:loc -> prim2 -> expression -> expression -> expression | ||
| val if_: ?loc:loc -> expression -> expression -> expression -> expression | ||
| val lambda: ?loc:loc -> pattern list -> expression -> expression | ||
| val apply: ?loc:loc -> expression -> expression list -> expression | ||
| val block: ?loc:loc -> expression list -> expression | ||
| val null: ?loc:loc -> unit -> expression | ||
| end | ||
| module Top: sig | ||
| val mk: ?loc:loc -> toplevel_stmt_desc -> toplevel_stmt | ||
| val foreign: ?loc:loc -> value_description -> toplevel_stmt | ||
| val import: ?loc:loc -> import_declaration -> toplevel_stmt | ||
| val data: ?loc:loc -> data_declaration -> toplevel_stmt | ||
| val let_: ?loc:loc -> rec_flag -> value_binding list -> toplevel_stmt | ||
| end | ||
| module Val: sig | ||
| val mk: ?loc:loc -> mod_:str -> name:str -> typ:parsed_type -> prim:string list -> value_description | ||
| end | ||
| module Vb: sig | ||
| val mk: ?loc:loc -> pattern -> expression -> value_binding | ||
| end | ||
| module Mb: sig | ||
| val mk: ?loc:loc -> pattern -> expression -> match_branch | ||
| end | ||
| module Imp: sig | ||
| val mk: ?loc:loc -> id -> import_declaration | ||
| end | ||
| @@ -0,0 +1,137 @@ | ||
| (* See copyright in ast_iterator.mli *) | ||
| open Parsetree | ||
| type iterator = { | ||
| constant: iterator -> constant -> unit; | ||
| expr: iterator -> expression -> unit; | ||
| pat: iterator -> pattern -> unit; | ||
| typ: iterator -> parsed_type -> unit; | ||
| data: iterator -> data_declaration -> unit; | ||
| constructor: iterator -> constructor_declaration -> unit; | ||
| location: iterator -> Location.t -> unit; | ||
| import: iterator -> import_declaration -> unit; | ||
| value_binding: iterator -> value_binding -> unit; | ||
| match_branch: iterator -> match_branch -> unit; | ||
| value_description: iterator -> value_description -> unit; | ||
| toplevel: iterator -> toplevel_stmt -> unit; | ||
| } | ||
| let iter_loc sub {loc; txt} = sub.location sub loc | ||
| module Cnst = struct | ||
| let iter sub c = () | ||
| end | ||
| module E = struct | ||
| let iter sub {pexp_desc = desc; pexp_loc = loc} = | ||
| sub.location sub loc; | ||
| match desc with | ||
| | PExpId(i) -> iter_loc sub i | ||
| | PExpConstant(c) -> sub.constant sub c | ||
| | PExpTuple(es) -> List.iter (sub.expr sub) es | ||
| | PExpLet(r, vbs, e) -> List.iter (sub.value_binding sub) vbs; sub.expr sub e | ||
| | PExpMatch(e, mbs) -> sub.expr sub e; List.iter (sub.match_branch sub) mbs | ||
| | PExpPrim1(p1, e) -> sub.expr sub e | ||
| | PExpPrim2(p2, e1, e2) -> sub.expr sub e1; sub.expr sub e2 | ||
| | PExpIf(c, t, f) -> sub.expr sub c; sub.expr sub t; sub.expr sub f | ||
| | PExpLambda(pl, e) -> List.iter (sub.pat sub) pl; sub.expr sub e | ||
| | PExpApp(e, el) -> sub.expr sub e; List.iter (sub.expr sub) el | ||
| | PExpBlock(el) -> List.iter (sub.expr sub) el | ||
| | PExpNull -> () | ||
| end | ||
| module P = struct | ||
| let iter sub {ppat_desc = desc; ppat_loc = loc} = | ||
| sub.location sub loc; | ||
| match desc with | ||
| | PPatAny -> () | ||
| | PPatVar sl -> iter_loc sub sl | ||
| | PPatTuple pl -> List.iter (sub.pat sub) pl | ||
| | PPatConstant c -> sub.constant sub c | ||
| | PPatConstraint(p, pt) -> sub.pat sub p; sub.typ sub pt | ||
| | PPatConstruct(id, pl) -> iter_loc sub id; List.iter (sub.pat sub) pl | ||
| | PPatOr(p1, p2) -> sub.pat sub p1; sub.pat sub p2 | ||
| | PPatAlias(p, id) -> sub.pat sub p; iter_loc sub id | ||
| end | ||
| module C = struct | ||
| let iter sub {pcd_name = name; pcd_args = args; pcd_loc = loc} = | ||
| sub.location sub loc; | ||
| iter_loc sub name; | ||
| match args with | ||
| | PConstrTuple(ptl) -> List.iter (sub.typ sub) ptl | ||
| | PConstrSingleton -> () | ||
| end | ||
| module D = struct | ||
| let iter sub{pdata_name = name; pdata_params = args; pdata_kind = kind; pdata_loc = loc} = | ||
| sub.location sub loc; | ||
| iter_loc sub name; | ||
| List.iter (sub.typ sub) args; | ||
| match kind with | ||
| | PDataVariant cdl -> List.iter (sub.constructor sub) cdl | ||
| end | ||
| module T = struct | ||
| let iter sub {ptyp_desc = desc; ptyp_loc = loc} = | ||
| sub.location sub loc; | ||
| match desc with | ||
| | PTyAny -> () | ||
| | PTyVar v -> () | ||
| | PTyArrow(args, ret) -> List.iter (sub.typ sub) args; sub.typ sub ret | ||
| | PTyTuple ts -> List.iter (sub.typ sub) ts | ||
| | PTyConstr(name, ts) -> iter_loc sub name; List.iter (sub.typ sub) ts | ||
| | PTyPoly(args, t) -> List.iter (iter_loc sub) args; sub.typ sub t | ||
| end | ||
| module V = struct | ||
| let iter sub {pvb_pat = pat; pvb_expr = expr; pvb_loc = loc} = | ||
| sub.pat sub pat; | ||
| sub.expr sub expr; | ||
| sub.location sub loc; | ||
| end | ||
| module MB = struct | ||
| let iter sub {pmb_pat = pat; pmb_body = expr; pmb_loc = loc} = | ||
| sub.pat sub pat; | ||
| sub.expr sub expr; | ||
| sub.location sub loc; | ||
| end | ||
| module I = struct | ||
| let iter sub {pimp_mod = imod; pimp_loc = loc} = | ||
| sub.location sub loc; | ||
| iter_loc sub imod | ||
| end | ||
| module VD = struct | ||
| let iter sub {pval_mod = vmod; pval_name = vname; pval_loc = loc} = | ||
| sub.location sub loc; | ||
| iter_loc sub vmod; | ||
| iter_loc sub vname | ||
| end | ||
| module TL = struct | ||
| let iter sub {ptop_desc = desc; ptop_loc = loc} = | ||
| sub.location sub loc; | ||
| match desc with | ||
| | PTopForeign vd -> sub.value_description sub vd | ||
| | PTopImport id -> sub.import sub id | ||
| | PTopData dd -> sub.data sub dd | ||
| | PTopLet(r, vb) -> List.iter (sub.value_binding sub) vb | ||
| end | ||
| let default_iterator = { | ||
| constant = Cnst.iter; | ||
| expr = E.iter; | ||
| pat = P.iter; | ||
| typ = T.iter; | ||
| data = D.iter; | ||
| constructor = C.iter; | ||
| location = (fun _ x -> ()); | ||
| import = I.iter; | ||
| value_binding = V.iter; | ||
| match_branch = MB.iter; | ||
| value_description = VD.iter; | ||
| toplevel = TL.iter; | ||
| } |
| @@ -0,0 +1,40 @@ | ||
| (* This file is largely copied from OCaml's parsing/ast_iterator.mli. | ||
| The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Nicolas Ojeda Bar, LexiFi *) | ||
| (* *) | ||
| (* Copyright 2012 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Parsetree | ||
| type iterator = { | ||
| constant: iterator -> constant -> unit; | ||
| expr: iterator -> expression -> unit; | ||
| pat: iterator -> pattern -> unit; | ||
| typ: iterator -> parsed_type -> unit; | ||
| data: iterator -> data_declaration -> unit; | ||
| constructor: iterator -> constructor_declaration -> unit; | ||
| location: iterator -> Location.t -> unit; | ||
| import: iterator -> import_declaration -> unit; | ||
| value_binding: iterator -> value_binding -> unit; | ||
| match_branch: iterator -> match_branch -> unit; | ||
| value_description: iterator -> value_description -> unit; | ||
| toplevel: iterator -> toplevel_stmt -> unit; | ||
| } | ||
| (** A [iterator] record implements one "method" per syntactic category, | ||
| using an open recursion style: each method takes as its first | ||
| argument the iterator to be applied to children in the syntax | ||
| tree. *) | ||
| val default_iterator: iterator | ||
| (** A default iterator, which implements a "do not do anything" mapping. *) |
| @@ -0,0 +1,151 @@ | ||
| (* See copyright in ast_mapper.mli *) | ||
| open Parsetree | ||
| open Ast_helper | ||
| type mapper = { | ||
| constant: mapper -> constant -> constant; | ||
| expr: mapper -> expression -> expression; | ||
| pat: mapper -> pattern -> pattern; | ||
| typ: mapper -> parsed_type -> parsed_type; | ||
| data: mapper -> data_declaration -> data_declaration; | ||
| constructor: mapper -> constructor_declaration -> constructor_declaration; | ||
| location: mapper -> Location.t -> Location.t; | ||
| import: mapper -> import_declaration -> import_declaration; | ||
| value_binding: mapper -> value_binding -> value_binding; | ||
| match_branch: mapper -> match_branch -> match_branch; | ||
| value_description: mapper -> value_description -> value_description; | ||
| toplevel: mapper -> toplevel_stmt -> toplevel_stmt; | ||
| } | ||
| let map_loc sub {loc; txt} = {loc = sub.location sub loc; txt} | ||
| module Cnst = struct | ||
| let map sub c = c | ||
| end | ||
| module E = struct | ||
| let map sub {pexp_desc = desc; pexp_loc = loc} = | ||
| let open Exp in | ||
| let loc = sub.location sub loc in | ||
| match desc with | ||
| | PExpId(i) -> ident ~loc (map_loc sub i) | ||
| | PExpConstant(c) -> constant ~loc (sub.constant sub c) | ||
| | PExpTuple(es) -> tuple ~loc (List.map (sub.expr sub) es) | ||
| | PExpLet(r, vbs, e) -> let_ ~loc r (List.map (sub.value_binding sub) vbs) (sub.expr sub e) | ||
| | PExpMatch(e, mbs) -> match_ ~loc (sub.expr sub e) (List.map (sub.match_branch sub) mbs) | ||
| | PExpPrim1(p1, e) -> prim1 ~loc p1 (sub.expr sub e) | ||
| | PExpPrim2(p2, e1, e2) -> prim2 ~loc p2 (sub.expr sub e1) (sub.expr sub e2) | ||
| | PExpIf(c, t, f) -> if_ ~loc (sub.expr sub c) (sub.expr sub t) (sub.expr sub f) | ||
| | PExpLambda(pl, e) -> lambda ~loc (List.map (sub.pat sub) pl) (sub.expr sub e) | ||
| | PExpApp(e, el) -> apply ~loc (sub.expr sub e) (List.map (sub.expr sub) el) | ||
| | PExpBlock(el) -> block ~loc (List.map (sub.expr sub) el) | ||
| | PExpNull -> null ~loc () | ||
| end | ||
| module P = struct | ||
| let map sub {ppat_desc = desc; ppat_loc = loc} = | ||
| let open Pat in | ||
| let loc = sub.location sub loc in | ||
| match desc with | ||
| | PPatAny -> any ~loc () | ||
| | PPatVar sl -> var ~loc (map_loc sub sl) | ||
| | PPatTuple pl -> tuple ~loc (List.map (sub.pat sub) pl) | ||
| | PPatConstant c -> constant ~loc (sub.constant sub c) | ||
| | PPatConstraint(p, pt) -> constraint_ ~loc (sub.pat sub p) (sub.typ sub pt) | ||
| | PPatConstruct(id, pl) -> construct ~loc (map_loc sub id) (List.map (sub.pat sub) pl) | ||
| | PPatOr(p1, p2) -> or_ ~loc (sub.pat sub p1) (sub.pat sub p2) | ||
| | PPatAlias(p, id) -> alias ~loc (sub.pat sub p) (map_loc sub id) | ||
| end | ||
| module C = struct | ||
| let map sub {pcd_name = name; pcd_args = args; pcd_loc = loc} = | ||
| let open CDecl in | ||
| let loc = sub.location sub loc in | ||
| let sname = map_loc sub name in | ||
| match args with | ||
| | PConstrTuple(ptl) -> tuple ~loc sname (List.map (sub.typ sub) ptl) | ||
| | PConstrSingleton -> singleton ~loc sname | ||
| end | ||
| module D = struct | ||
| let map sub{pdata_name = name; pdata_params = args; pdata_kind = kind; pdata_loc = loc} = | ||
| let open Dat in | ||
| let loc = sub.location sub loc in | ||
| let sname = map_loc sub name in | ||
| let sargs = List.map (sub.typ sub) args in | ||
| match kind with | ||
| | PDataVariant cdl -> variant ~loc sname sargs (List.map (sub.constructor sub) cdl) | ||
| end | ||
| module T = struct | ||
| let map sub {ptyp_desc = desc; ptyp_loc = loc} = | ||
| let open Typ in | ||
| let loc = sub.location sub loc in | ||
| match desc with | ||
| | PTyAny -> any ~loc () | ||
| | PTyVar v -> var ~loc v | ||
| | PTyArrow(args, ret) -> arrow ~loc (List.map (sub.typ sub) args) (sub.typ sub ret) | ||
| | PTyTuple ts -> tuple ~loc (List.map (sub.typ sub) ts) | ||
| | PTyConstr(name, ts) -> constr ~loc (map_loc sub name) (List.map (sub.typ sub) ts) | ||
| | PTyPoly(vars, t) -> poly ~loc (List.map (map_loc sub) vars) (sub.typ sub t) | ||
| end | ||
| module V = struct | ||
| let map sub {pvb_pat = pat; pvb_expr = expr; pvb_loc = loc} = | ||
| { | ||
| pvb_pat = sub.pat sub pat; | ||
| pvb_expr = sub.expr sub expr; | ||
| pvb_loc = sub.location sub loc; | ||
| } | ||
| end | ||
| module MB = struct | ||
| let map sub {pmb_pat = pat; pmb_body = expr; pmb_loc = loc} = | ||
| { | ||
| pmb_pat = sub.pat sub pat; | ||
| pmb_body = sub.expr sub expr; | ||
| pmb_loc = sub.location sub loc; | ||
| } | ||
| end | ||
| module I = struct | ||
| let map sub {pimp_mod = imod; pimp_loc = loc} = | ||
| let open Imp in | ||
| let loc = sub.location sub loc in | ||
| mk ~loc (map_loc sub imod) | ||
| end | ||
| module VD = struct | ||
| let map sub ({pval_mod = vmod; pval_name = vname; pval_loc = loc} as d) = | ||
| let pval_loc = sub.location sub loc in | ||
| let pval_mod = map_loc sub vmod in | ||
| let pval_name = map_loc sub vname in | ||
| {d with pval_name; pval_mod; pval_loc} | ||
| end | ||
| module TL = struct | ||
| let map sub {ptop_desc = desc; ptop_loc = loc} = | ||
| let open Top in | ||
| let loc = sub.location sub loc in | ||
| match desc with | ||
| | PTopForeign d -> Top.foreign ~loc (sub.value_description sub d) | ||
| | PTopImport id -> Top.import ~loc (sub.import sub id) | ||
| | PTopData dd -> Top.data ~loc (sub.data sub dd) | ||
| | PTopLet(r, vb) -> Top.let_ ~loc r (List.map (sub.value_binding sub) vb) | ||
| end | ||
| let default_mapper = { | ||
| constant = Cnst.map; | ||
| expr = E.map; | ||
| pat = P.map; | ||
| typ = T.map; | ||
| data = D.map; | ||
| constructor = C.map; | ||
| location = (fun _ x -> x); | ||
| import = I.map; | ||
| value_binding = V.map; | ||
| match_branch = MB.map; | ||
| value_description = VD.map; | ||
| toplevel = TL.map; | ||
| } | ||
| @@ -0,0 +1,40 @@ | ||
| (* This file is largely copied from OCaml's parsing/ast_mapper.mli. | ||
| The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Alain Frisch, LexiFi *) | ||
| (* *) | ||
| (* Copyright 2012 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Parsetree | ||
| type mapper = { | ||
| constant: mapper -> constant -> constant; | ||
| expr: mapper -> expression -> expression; | ||
| pat: mapper -> pattern -> pattern; | ||
| typ: mapper -> parsed_type -> parsed_type; | ||
| data: mapper -> data_declaration -> data_declaration; | ||
| constructor: mapper -> constructor_declaration -> constructor_declaration; | ||
| location: mapper -> Location.t -> Location.t; | ||
| import: mapper -> import_declaration -> import_declaration; | ||
| value_binding: mapper -> value_binding -> value_binding; | ||
| match_branch: mapper -> match_branch -> match_branch; | ||
| value_description: mapper -> value_description -> value_description; | ||
| toplevel: mapper -> toplevel_stmt -> toplevel_stmt; | ||
| } | ||
| (** A mapper record implements one "method" per syntactic category, | ||
| using an open recursion style: each method takes as its first | ||
| argument the mapper to be applied to children in the syntax | ||
| tree. *) | ||
| val default_mapper: mapper | ||
| (** A default mapper, which implements a "deep identity" mapping. *) |
| @@ -0,0 +1,38 @@ | ||
| (* Modified from OCaml. The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) | ||
| (* *) | ||
| (* Copyright 1996 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Sexplib.Conv | ||
| (** Auxiliary AST types used by parsetree and typedtree. *) | ||
| (* These are taken from OCaml. While not all fully supported, | ||
| we will likely want to support them. *) | ||
| type constant = | ||
| | Const_int of int | ||
| | Const_string of string | ||
| | Const_float of string | ||
| | Const_int32 of int32 | ||
| | Const_int64 of int64 | ||
| | Const_bool of bool | ||
| [@@deriving sexp] | ||
| (** Marker for recursive/nonrecursive let bindings *) | ||
| type rec_flag = Nonrecursive | Recursive [@@deriving sexp] | ||
| (** A location-tagged value. *) | ||
| type 'a loc = 'a Location.loc = { | ||
| txt : 'a; | ||
| loc : Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] |
| @@ -0,0 +1,41 @@ | ||
| (** External frontend for running the parser. *) | ||
| open Lexing | ||
| open Location | ||
| type error = | ||
| | AmbiguousParse of Parsetree.parsed_program list | ||
| | NoValidParse | ||
| let report_error ppf = function | ||
| | AmbiguousParse parses -> | ||
| if !Grain_utils.Config.verbose || (!Grain_utils.Config.parser_debug_level > 0) then begin | ||
| Format.fprintf ppf "The Grain compiler had trouble parsing your program. Here were the potential parses:@\n@[<v>%a@]" | ||
| (fun ppf -> List.iter (fun x -> | ||
| Format.fprintf ppf "@[%s@]%," (Sexplib.Sexp.to_string_hum @@ Parsetree.sexp_of_parsed_program x) | ||
| )) parses | ||
| end else | ||
| Format.fprintf ppf "The Grain compiler had trouble parsing your program." | ||
| | NoValidParse -> | ||
| Format.fprintf ppf "The Grain compiler was unable to parse your program. \ | ||
| If you see this message, please file an issue at https://github.com/grain-lang/grain" | ||
| exception Error of Location.t * error | ||
| let () = | ||
| Location.register_error_of_exn | ||
| (function | ||
| | Error(loc, err) -> Some(Location.error_of_printer loc report_error err) | ||
| | _ -> None) | ||
| let parse ?name lexbuf : Parsetree.parsed_program = | ||
| Option.may (fun n -> | ||
| lexbuf.lex_curr_p <- { lexbuf.lex_curr_p with pos_fname = n }; | ||
| Location.input_name := n; | ||
| ) name; | ||
| let loc_start = Option.map_default (fun n -> {lexbuf.lex_start_p with pos_fname = n}) lexbuf.lex_start_p name in | ||
| let loc_end = lexbuf.lex_curr_p in | ||
| let startpos = {loc_start; loc_end; loc_ghost=true} in | ||
| match Parser.parse_program Lexer.token lexbuf with | ||
| | [] -> raise (Error(startpos, NoValidParse)) | ||
| | [(x, _)] -> x | ||
| | parses -> raise (Error(startpos, AmbiguousParse (List.map fst parses))) |
| @@ -0,0 +1,2 @@ | ||
| (** Wrapper for the parser, including error handling and ambiguous parses. *) | ||
| val parse : ?name:string -> Lexing.lexbuf -> Parsetree.parsed_program |
| @@ -0,0 +1,75 @@ | ||
| open Sexplib.Conv | ||
| let sep = "::" | ||
| type t = | ||
| | IdentName of string | ||
| | IdentExternal of t * string | ||
| [@@deriving sexp] | ||
| let rec equal i1 i2 = | ||
| match i1, i2 with | ||
| | (IdentName n1), (IdentName n2) -> String.equal n1 n2 | ||
| | (IdentExternal(mod1, n1)), (IdentExternal(mod2, n2)) -> | ||
| (equal mod1 mod2) && (String.equal n1 n2) | ||
| | _ -> false | ||
| open Format | ||
| let rec print_ident ppf = function | ||
| | IdentName n -> fprintf ppf "%s" n | ||
| | IdentExternal(m, n) -> fprintf ppf "%a%s%s" print_ident m sep n | ||
| let default_printer ppf i = fprintf ppf "@{<id>%a@}@," print_ident i | ||
| let printer = ref default_printer | ||
| let print ppf = !printer ppf | ||
| let rec string_of_ident i = | ||
| print_ident str_formatter i; | ||
| flush_str_formatter() | ||
| let rec compare i1 i2 = | ||
| match i1, i2 with | ||
| | (IdentName n1), (IdentName n2) -> String.compare n1 n2 | ||
| | (IdentExternal(mod1, n1)), (IdentExternal(mod2, n2)) -> | ||
| let n_comp = String.compare n1 n2 in | ||
| if n_comp <> 0 then | ||
| n_comp | ||
| else | ||
| compare mod1 mod2 | ||
| | (IdentName _), (IdentExternal _) | ||
| | (IdentExternal _), (IdentName _) -> | ||
| String.compare (string_of_ident i1) (string_of_ident i2) | ||
| let last = function | ||
| | IdentName s -> s | ||
| | IdentExternal (_, s) -> s | ||
| let rec split_at_dots s pos = | ||
| try | ||
| let dot = String.index_from s pos '.' in | ||
| String.sub s pos (dot - pos) :: split_at_dots s (dot + 1) | ||
| with Not_found -> | ||
| [String.sub s pos (String.length s - pos)] | ||
| let flatten n = | ||
| let rec help acc = function | ||
| | IdentName(n) -> List.rev (n::acc) | ||
| | IdentExternal(p, n) -> help (n::acc) p | ||
| in | ||
| help [] n | ||
| let unflatten = function | ||
| | [] -> None | ||
| | hd::tl -> Some (List.fold_left (fun p s -> IdentExternal(p, s)) (IdentName hd) tl) | ||
| let parse s = | ||
| match unflatten (split_at_dots s 0) with | ||
| | None -> IdentName "" | ||
| | Some v -> v | ||
| let hash name = Hashtbl.hash (flatten name) | ||
| let output oc name = output_string oc (string_of_ident name) | ||
| @@ -0,0 +1,32 @@ | ||
| (** Types for identifiers *) | ||
| open Format | ||
| (** The type of identifiers. *) | ||
| type t = | ||
| | IdentName of string | ||
| (** A simple name. *) | ||
| | IdentExternal of t * string | ||
| (** (module, ident) An external name. It is currently a well-formedness error | ||
| to have a non-name on the LHS. *) | ||
| [@@deriving sexp] | ||
| val equal : t -> t -> bool | ||
| val compare : t -> t -> int | ||
| val print : formatter -> t -> unit | ||
| val default_printer : formatter -> t -> unit | ||
| (** The default {!formatter} implementation for identifiers. | ||
| When formatting {!type:t} instances, one should use {!print} instead. *) | ||
| val printer : (formatter -> t -> unit) ref | ||
| (** The active {!formatter} implementation for identifiers *) | ||
| val string_of_ident : t -> string | ||
| val last : t -> string | ||
| val unflatten: string list -> t option | ||
| val parse: string -> t | ||
| val hash: t -> int | ||
| val output : out_channel -> t -> unit | ||
| val flatten : t -> string list |
| @@ -0,0 +1,16 @@ | ||
| (jbuild_version 1) | ||
| (ocamllex (lexer)) | ||
| (library | ||
| ((name grain_parsing) | ||
| (public_name grain_parsing) | ||
| (synopsis "Frontend modules for Grain compiler") | ||
| (libraries (dyp batteries ppx_deriving ppx_sexp_conv sexplib extlib grain_utils)) | ||
| (preprocess (pps (ppx_sexp_conv ppx_deriving))))) | ||
| (rule | ||
| ((targets (parser.ml)) | ||
| (deps (parser.dyp)) | ||
| (action (run dypgen --no-mli --Werror ${<})))) | ||
| @@ -0,0 +1,392 @@ | ||
| (* This file is mostly copied from OCaml's parsing/location.ml. | ||
| The original copyright notice is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) | ||
| (* *) | ||
| (* Copyright 1996 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| open Sexplib.Conv | ||
| open Lexing | ||
| open Grain_utils | ||
| (* NOTE: A lot of this file is taken from OCaml's parsing/location.ml. | ||
| Perhaps we should just go ahead and copy the whole thing. *) | ||
| let absname = ref false | ||
| let sexp_of_position (p : position) = | ||
| Sexplib.Sexp.List [ | ||
| Sexplib.Sexp.Atom "position"; | ||
| Sexplib.Sexp.List [ | ||
| Sexplib.Sexp.Atom "file"; | ||
| Sexplib.Conv.sexp_of_string p.pos_fname; | ||
| ]; | ||
| Sexplib.Sexp.List [ | ||
| Sexplib.Sexp.Atom "line"; | ||
| Sexplib.Conv.sexp_of_int p.pos_lnum; | ||
| ]; | ||
| Sexplib.Sexp.List [ | ||
| Sexplib.Sexp.Atom "col"; | ||
| Sexplib.Conv.sexp_of_int p.pos_cnum; | ||
| ]; | ||
| Sexplib.Sexp.List [ | ||
| Sexplib.Sexp.Atom "bol"; | ||
| Sexplib.Conv.sexp_of_int p.pos_bol; | ||
| ]; | ||
| ] | ||
| let position_of_sexp (sexp : Sexplib.Sexp.t) = | ||
| let open Sexplib.Conv in | ||
| let open Sexplib.Sexp in | ||
| match sexp with | ||
| | Atom str -> of_sexp_error "position_of_sexp: list needed" sexp | ||
| | List [Atom "position"; List sexp_fields] when List.length sexp_fields = 4 -> | ||
| let fields = List.map (function | ||
| | List [Atom str; ((Atom _) as value)] -> (str, value) | ||
| | sexp -> of_sexp_error "position_of_sexp: invalid field" sexp) sexp_fields in | ||
| let pos_fname, pos_lnum, pos_cnum, pos_bol = begin | ||
| try | ||
| string_of_sexp (List.assoc "file" fields), | ||
| int_of_sexp (List.assoc "line" fields), | ||
| int_of_sexp (List.assoc "col" fields), | ||
| int_of_sexp (List.assoc "bol" fields) | ||
| with | ||
| | Not_found -> of_sexp_error "position_of_sexp: invalid fields" sexp | ||
| end in | ||
| { pos_fname; pos_lnum; pos_cnum; pos_bol } | ||
| | List ((Atom "position") :: _) -> of_sexp_error "position_of_sexp: invalid fields" sexp | ||
| | List _ -> of_sexp_error "position_of_sexp: invalid s-expression" sexp | ||
| type t = Warnings.loc = { | ||
| loc_start: position; | ||
| loc_end: position; | ||
| loc_ghost: bool; | ||
| } [@@deriving sexp] | ||
| let in_file name = | ||
| let loc = { | ||
| pos_fname = name; | ||
| pos_lnum = 1; | ||
| pos_bol = 0; | ||
| pos_cnum = -1; | ||
| } in | ||
| { loc_start = loc; loc_end = loc; loc_ghost = true } | ||
| let dummy_loc = { | ||
| loc_start=dummy_pos; | ||
| loc_end=dummy_pos; | ||
| loc_ghost=true | ||
| } | ||
| let sexp_of_t loc = | ||
| if loc = dummy_loc then | ||
| Sexplib.Sexp.Atom "dummy_loc" | ||
| else | ||
| sexp_of_t loc | ||
| let t_of_sexp sexp = | ||
| match sexp with | ||
| | Sexplib.Sexp.Atom "dummy_loc" -> dummy_loc | ||
| | _ -> t_of_sexp sexp | ||
| let curr lexbuf = { | ||
| loc_start = lexbuf.lex_start_p; | ||
| loc_end = lexbuf.lex_curr_p; | ||
| loc_ghost = false | ||
| } | ||
| let init lexbuf fname = | ||
| lexbuf.lex_curr_p <- { | ||
| pos_fname = fname; | ||
| pos_lnum = 1; | ||
| pos_bol = 0; | ||
| pos_cnum = 0; | ||
| } | ||
| let symbol_rloc () = { | ||
| loc_start = Parsing.symbol_start_pos (); | ||
| loc_end = Parsing.symbol_end_pos (); | ||
| loc_ghost = false; | ||
| } | ||
| let symbol_gloc () = { | ||
| loc_start = Parsing.symbol_start_pos (); | ||
| loc_end = Parsing.symbol_end_pos (); | ||
| loc_ghost = true; | ||
| } | ||
| let rhs_loc n = { | ||
| loc_start = Parsing.rhs_start_pos n; | ||
| loc_end = Parsing.rhs_end_pos n; | ||
| loc_ghost = false; | ||
| } | ||
| let input_name = ref "_none_" | ||
| let input_lexbuf = ref (None : lexbuf option) | ||
| (* Terminal info *) | ||
| let status = ref Terminfo.Uninitialised | ||
| let num_loc_lines = ref 0 (* number of lines already printed after input *) | ||
| let print_updating_num_loc_lines ppf f arg = | ||
| let open Format in | ||
| let out_functions = pp_get_formatter_out_functions ppf () in | ||
| let out_string str start len = | ||
| let rec count i c = | ||
| if i = start + len then c | ||
| else if String.get str i = '\n' then count (succ i) (succ c) | ||
| else count (succ i) c in | ||
| num_loc_lines := !num_loc_lines + count start 0 ; | ||
| out_functions.out_string str start len in | ||
| pp_set_formatter_out_functions ppf | ||
| { out_functions with out_string } ; | ||
| f ppf arg ; | ||
| pp_print_flush ppf (); | ||
| pp_set_formatter_out_functions ppf out_functions | ||
| let reset () = | ||
| num_loc_lines := 0 | ||
| open Format | ||
| let (msg_file, msg_line, msg_chars, msg_char, msg_to, msg_colon) = | ||
| ("File \"", "\", line ", ", characters ", ", character ", "-", ":") | ||
| (** Returns (file, line, char) *) | ||
| let get_pos_info pos = | ||
| (pos.pos_fname, pos.pos_lnum, pos.pos_cnum - pos.pos_bol) | ||
| let setup_colors() = | ||
| Misc.Color.setup @@ Some(if !Grain_utils.Config.color_enabled then Misc.Color.Auto else Misc.Color.Never) | ||
| let absolute_path s = (* This function could go into Filename *) | ||
| let open Filename in | ||
| let s = if is_relative s then concat (Sys.getcwd ()) s else s in | ||
| (* Now simplify . and .. components *) | ||
| let rec aux s = | ||
| let base = basename s in | ||
| let dir = dirname s in | ||
| if dir = s then dir | ||
| else if base = current_dir_name then aux dir | ||
| else if base = parent_dir_name then dirname (aux dir) | ||
| else concat (aux dir) base | ||
| in | ||
| aux s | ||
| let show_filename file = if !absname then absolute_path file else file | ||
| let print_filename ppf file = fprintf ppf "%s" (show_filename file) | ||
| let print_loc ppf loc = | ||
| setup_colors(); | ||
| let (file, line, startchar) = get_pos_info loc.loc_start in | ||
| let (_, endline, endchar) = get_pos_info loc.loc_end in | ||
| (*let endchar = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum + startchar in*) | ||
| fprintf ppf "%s@{<loc>%a%s%i" msg_file print_filename file msg_line line; | ||
| if startchar >= 0 then begin | ||
| if line = endline then | ||
| fprintf ppf "%s%i%s%i" msg_chars startchar msg_to endchar | ||
| else | ||
| fprintf ppf "%s%i%sline %i%s%i" msg_char startchar msg_to endline msg_char endchar | ||
| end; | ||
| fprintf ppf "@}" | ||
| let default_printer ppf loc = | ||
| setup_colors(); | ||
| fprintf ppf "@{<loc>%a@}%s@," print_loc loc msg_colon | ||
| let printer = ref default_printer | ||
| let print ppf loc = !printer ppf loc | ||
| let error_prefix = "Error" | ||
| let warning_prefix = "Warning" | ||
| let print_error_prefix ppf = | ||
| setup_colors (); | ||
| fprintf ppf "@{<error>%s@}" error_prefix | ||
| let print_compact ppf loc = | ||
| let (file, line, startchar) = get_pos_info loc.loc_start in | ||
| let (_, endline, endchar) = get_pos_info loc.loc_end in | ||
| fprintf ppf "%a:%i" print_filename file line; | ||
| if startchar >= 0 then begin | ||
| if line = endline then | ||
| fprintf ppf ",%i--%i" startchar endchar | ||
| else | ||
| fprintf ppf ",%i--%i,%i" startchar endline endchar | ||
| end | ||
| let print_error ppf loc = | ||
| fprintf ppf "%a%t:" print loc print_error_prefix | ||
| let print_error_cur_file ppf () = print_error ppf (in_file !input_name) | ||
| let default_warning_printer loc ppf w = | ||
| match Warnings.report w with | ||
| | `Inactive -> () | ||
| | `Active { Warnings. number; message; is_error; sub_locs } -> | ||
| setup_colors (); | ||
| fprintf ppf "@[<v>"; | ||
| print ppf loc; | ||
| if is_error | ||
| then | ||
| fprintf ppf "%t (%s %d): %s@," print_error_prefix | ||
| (String.uncapitalize_ascii warning_prefix) number message | ||
| else fprintf ppf "@{<warning>%s@} %d: %s@," warning_prefix number message; | ||
| List.iter | ||
| (fun (loc, msg) -> | ||
| if loc <> dummy_loc then fprintf ppf " %a %s@," print loc msg | ||
| ) | ||
| sub_locs; | ||
| fprintf ppf "@]" | ||
| let warning_printer = ref default_warning_printer ;; | ||
| let print_warning loc ppf w = | ||
| print_updating_num_loc_lines ppf (!warning_printer loc) w | ||
| ;; | ||
| let formatter_for_warnings = ref err_formatter;; | ||
| let prerr_warning loc w = print_warning loc !formatter_for_warnings w;; | ||
| let echo_eof () = | ||
| print_newline (); | ||
| incr num_loc_lines | ||
| type 'a loc = { | ||
| txt : 'a; | ||
| loc : t; | ||
| } | ||
| let mkloc txt loc = { txt ; loc } | ||
| let mknoloc txt = mkloc txt dummy_loc | ||
| type error = | ||
| { | ||
| loc: t; | ||
| msg: string; | ||
| sub: error list; | ||
| if_highlight: string; (* alternative message if locations are highlighted *) | ||
| } | ||
| let pp_ksprintf ?before k fmt = | ||
| let buf = Buffer.create 64 in | ||
| let ppf = Format.formatter_of_buffer buf in | ||
| Misc.Color.set_color_tag_handling ppf; | ||
| begin match before with | ||
| | None -> () | ||
| | Some f -> f ppf | ||
| end; | ||
| kfprintf | ||
| (fun _ -> | ||
| pp_print_flush ppf (); | ||
| let msg = Buffer.contents buf in | ||
| k msg) | ||
| ppf fmt | ||
| (* Shift the formatter's offset by the length of the error prefix, which | ||
| is always added by the compiler after the message has been formatted *) | ||
| let print_phanton_error_prefix ppf = | ||
| Format.pp_print_as ppf (String.length error_prefix + 2 (* ": " *)) "" | ||
| let errorf ?(loc = dummy_loc) ?(sub = []) ?(if_highlight = "") fmt = | ||
| pp_ksprintf | ||
| ~before:print_phanton_error_prefix | ||
| (fun msg -> {loc; msg; sub; if_highlight}) | ||
| fmt | ||
| let error ?(loc = dummy_loc) ?(sub = []) ?(if_highlight = "") msg = | ||
| {loc; msg; sub; if_highlight} | ||
| let error_of_exn : (exn -> error option) list ref = ref [] | ||
| let register_error_of_exn f = error_of_exn := f :: !error_of_exn | ||
| exception Already_displayed_error = Warnings.Errors | ||
| let error_of_exn exn = | ||
| match exn with | ||
| | Already_displayed_error -> Some `Already_displayed | ||
| | _ -> | ||
| let rec loop = function | ||
| | [] -> None | ||
| | f :: rest -> | ||
| match f exn with | ||
| | Some error -> Some (`Ok error) | ||
| | None -> loop rest | ||
| in | ||
| loop !error_of_exn | ||
| let rec default_error_reporter ppf {loc; msg; sub; if_highlight} = | ||
| fprintf ppf "@[<v>%a %s" print_error loc msg; | ||
| List.iter (Format.fprintf ppf "@,@[<2>%a@]" default_error_reporter) sub; | ||
| fprintf ppf "@]" | ||
| let error_reporter = ref default_error_reporter | ||
| let report_error ppf err = | ||
| print_updating_num_loc_lines ppf !error_reporter err | ||
| ;; | ||
| let error_of_printer loc print x = | ||
| errorf ~loc "%a@?" print x | ||
| let error_of_printer_file print x = | ||
| error_of_printer (in_file !input_name) print x | ||
| let () = | ||
| register_error_of_exn | ||
| (function | ||
| | Sys_error msg -> | ||
| Some (errorf ~loc:(in_file !input_name) | ||
| "I/O error: %s" msg) | ||
| | Misc.HookExnWrapper {error = e; hook_name; | ||
| hook_info={Misc.sourcefile}} -> | ||
| let sub = match error_of_exn e with | ||
| | None | Some `Already_displayed -> error (Printexc.to_string e) | ||
| | Some (`Ok err) -> err | ||
| in | ||
| Some | ||
| (errorf ~loc:(in_file sourcefile) | ||
| "In hook %S:" hook_name | ||
| ~sub:[sub]) | ||
| | _ -> None | ||
| ) | ||
| external reraise : exn -> 'a = "%reraise" | ||
| let rec report_exception_rec n ppf exn = | ||
| try | ||
| match error_of_exn exn with | ||
| | None -> reraise exn | ||
| | Some `Already_displayed -> () | ||
| | Some (`Ok err) -> fprintf ppf "@[%a@]@." report_error err | ||
| with exn when n > 0 -> report_exception_rec (n-1) ppf exn | ||
| let report_exception ppf exn = report_exception_rec 5 ppf exn | ||
| exception Error of error | ||
| let () = | ||
| register_error_of_exn | ||
| (function | ||
| | Error e -> Some e | ||
| | _ -> None | ||
| ) | ||
| let raise_errorf ?(loc = dummy_loc) ?(sub = []) ?(if_highlight = "") = | ||
| pp_ksprintf | ||
| ~before:print_phanton_error_prefix | ||
| (fun msg -> raise (Error ({loc; msg; sub; if_highlight}))) |
| @@ -0,0 +1,116 @@ | ||
| (** Source location data definitions *) | ||
| open Format | ||
| (** The type for source locations. This definition | ||
| is inspired by the OCaml compiler's source. *) | ||
| type t = Grain_utils.Warnings.loc = { | ||
| loc_start: Lexing.position; (** The starting position *) | ||
| loc_end: Lexing.position; (** The ending position *) | ||
| loc_ghost: bool; (** Whether this location was auto-generated *) | ||
| } [@@deriving sexp] | ||
| val dummy_loc : t | ||
| (** A placeholder dummy location *) | ||
| val in_file : string -> t | ||
| (** Return an empty ghost range located in a given file. *) | ||
| val init : Lexing.lexbuf -> string -> unit | ||
| (** Set the file name and line number of the [lexbuf] to be the start | ||
| of the named file. *) | ||
| val curr : Lexing.lexbuf -> t | ||
| (** Get the location of the current token from the [lexbuf]. *) | ||
| val symbol_rloc: unit -> t | ||
| val symbol_gloc: unit -> t | ||
| (** [rhs_loc n] returns the location of the symbol at position [n], starting | ||
| at 1, in the current parser rule. *) | ||
| val rhs_loc: int -> t | ||
| val input_name: string ref | ||
| val input_lexbuf: Lexing.lexbuf option ref | ||
| val print_error: formatter -> t -> unit | ||
| val print_error_cur_file: formatter -> unit -> unit | ||
| val print_warning: t -> formatter -> Grain_utils.Warnings.t -> unit | ||
| val formatter_for_warnings : formatter ref | ||
| val prerr_warning: t -> Grain_utils.Warnings.t -> unit | ||
| val reset: unit -> unit | ||
| val echo_eof: unit -> unit | ||
| val get_pos_info: Lexing.position -> string * int * int (* file, line, char *) | ||
| val print_loc: formatter -> t -> unit | ||
| val print : formatter -> t -> unit | ||
| val print_compact: formatter -> t -> unit | ||
| val print_filename: formatter -> string -> unit | ||
| val absolute_path: string -> string | ||
| val show_filename: string -> string | ||
| (** In -absname mode, return the absolute path for this filename. | ||
| Otherwise, returns the filename unchanged. *) | ||
| val default_printer : formatter -> t -> unit | ||
| (** The default {!formatter} implementation for source locations. | ||
| When formatting {!type:t} instances, one should use {!print} instead. *) | ||
| val printer : (formatter -> t -> unit) ref | ||
| (** The active {!formatter} implementation for source locations *) | ||
| (** The type for location-tagged values. *) | ||
| type 'a loc = { | ||
| txt : 'a; (** The tagged value*) | ||
| loc : t; (** The location of the value *) | ||
| } | ||
| val mknoloc : 'a -> 'a loc | ||
| (** Makes a location-tagged value with the dummy location. *) | ||
| val mkloc : 'a -> t -> 'a loc | ||
| (** Makes a location-tagged value with the given location. *) | ||
| (** Support for located errors *) | ||
| type error = { | ||
| loc: t; | ||
| msg: string; | ||
| sub: error list; | ||
| if_highlight: string; (* alternative message if locations are highlighted *) | ||
| } | ||
| exception Already_displayed_error | ||
| exception Error of error | ||
| val error: ?loc:t -> ?sub:error list -> ?if_highlight:string -> string -> error | ||
| val errorf: ?loc:t -> ?sub:error list -> ?if_highlight:string | ||
| -> ('a, Format.formatter, unit, error) format4 -> 'a | ||
| val raise_errorf: ?loc:t -> ?sub:error list -> ?if_highlight:string | ||
| -> ('a, Format.formatter, unit, 'b) format4 -> 'a | ||
| val error_of_printer: t -> (formatter -> 'a -> unit) -> 'a -> error | ||
| val error_of_printer_file: (formatter -> 'a -> unit) -> 'a -> error | ||
| val error_of_exn: exn -> [ `Ok of error | `Already_displayed ] option | ||
| val register_error_of_exn: (exn -> error option) -> unit | ||
| (** Each compiler module which defines a custom type of exception | ||
| which can surface as a user-visible error should register | ||
| a "printer" for this exception using [register_error_of_exn]. | ||
| The result of the printer is an [error] value containing | ||
| a location, a message, and optionally sub-messages (each of them | ||
| being located as well). *) | ||
| val report_error: formatter -> error -> unit | ||
| val error_reporter : (formatter -> error -> unit) ref | ||
| (** Hook for intercepting error reports. *) | ||
| val default_error_reporter : formatter -> error -> unit | ||
| (** Original error reporter for use in hooks. *) | ||
| val report_exception: formatter -> exn -> unit | ||
| (** Reraise the exception if it is unknown. *) |
| @@ -0,0 +1,367 @@ | ||
| { | ||
| open Location | ||
| open Identifier | ||
| open Dyp | ||
| open Parsetree | ||
| open Ast_helper | ||
| (* Used for error messages and as a default, in case anything slips through | ||
| without an explicit loc. *) | ||
| let first_loc = ref Location.dummy_loc | ||
| let last_loc = ref Location.dummy_loc | ||
| let dyp_merge = keep_all | ||
| let last_state_printer = ref (fun () -> ()) | ||
| let when_debug ?n thunk = | ||
| match n with | ||
| | Some(n) -> | ||
| if !Grain_utils.Config.parser_debug_level >= n then | ||
| thunk() | ||
| | None -> () | ||
| let prerr_string s = when_debug ~n:1 (fun () -> Pervasives.prerr_string s) | ||
| let debug_print_state () = | ||
| when_debug !last_state_printer | ||
| let symbol_rloc dyp = | ||
| let ret = { | ||
| loc_start = dyp.symbol_start_pos (); | ||
| loc_end = dyp.symbol_end_pos (); | ||
| loc_ghost = false; | ||
| } in | ||
| last_state_printer := (fun () -> dyp.print_state stderr); | ||
| when_debug ~n:1 !last_state_printer; | ||
| last_loc := ret; | ||
| ret | ||
| let symbol_gloc dyp = | ||
| let ret = { | ||
| loc_start = dyp.symbol_start_pos (); | ||
| loc_end = dyp.symbol_end_pos (); | ||
| loc_ghost = true; | ||
| } in | ||
| last_state_printer := (fun () -> dyp.print_state stderr); | ||
| when_debug ~n:1 !last_state_printer; | ||
| last_loc := ret; | ||
| ret | ||
| let rhs_loc dyp n = | ||
| let ret = { | ||
| loc_start = dyp.rhs_start_pos n; | ||
| loc_end = dyp.rhs_end_pos n; | ||
| loc_ghost = false; | ||
| } in | ||
| last_state_printer := (fun () -> dyp.print_state stderr); | ||
| when_debug ~n:1 !last_state_printer; | ||
| last_loc := ret; | ||
| ret | ||
| let fix_block_mapper super = | ||
| let open Ast_mapper in | ||
| let expr mapper ({pexp_desc; pexp_loc} as e) = | ||
| match pexp_desc with | ||
| | PExpBlock([]) -> super.expr mapper e | ||
| | PExpBlock(elts) -> | ||
| let elts = List.map (mapper.expr mapper) elts in | ||
| let pre, last = List.fold_left (fun (acc_l, acc_last) cur -> | ||
| match acc_last with | ||
| | None -> (acc_l, Some cur) | ||
| | Some e -> (e::acc_l, Some cur)) ([], None) elts in | ||
| let pre = List.rev pre in | ||
| let last = match last with | ||
| | None -> raise (Syntaxerr.Error(LetWithoutBody(pexp_loc))) | ||
| | Some(l) -> l in | ||
| let fixed = List.fold_right (fun ({pexp_desc; pexp_loc; _} as e) acc -> | ||
| match pexp_desc with | ||
| | PExpLet(r, vbs, {pexp_desc=PExpBlock(b)}) -> | ||
| [{e with pexp_desc=PExpLet(r, vbs, Ast_helper.Exp.block ~loc:pexp_loc (b @ acc))}] | ||
| | _ -> e::acc) pre [last] in | ||
| (match fixed with | ||
| | [x] -> x | ||
| | _ -> {e with pexp_desc=PExpBlock(fixed)}) | ||
| | _ -> super.expr mapper e in | ||
| {super with expr} | ||
| let fix_tyvar_mapper super = | ||
| let open Ast_mapper in | ||
| let open Ast_helper in | ||
| let typ mapper ({ptyp_desc; ptyp_loc} as t) = | ||
| match ptyp_desc with | ||
| | PTyVar v when (v <> "") && (match v.[0] with 'A'..'Z' -> true | _ -> false) -> | ||
| let id = mkloc (IdentName v) ptyp_loc in | ||
| {t with ptyp_desc=PTyConstr(id, [])} | ||
| | _ -> super.typ mapper t in | ||
| {super with typ} | ||
| let fix_blocks ({statements; body} as prog) = | ||
| let open Ast_mapper in | ||
| let mapper = default_mapper | ||
| |> fix_block_mapper | ||
| |> fix_tyvar_mapper in | ||
| {prog with | ||
| statements=List.map (mapper.toplevel mapper) statements; | ||
| body=mapper.expr mapper body} | ||
| let mkid ns = | ||
| let help ns = | ||
| let rec help ns (acc_ident, acc_str) = | ||
| let ident = Option.map_default (fun i -> IdentExternal(i, acc_str)) (IdentName acc_str) acc_ident in | ||
| match ns with | ||
| | [] -> ident | ||
| | n::tl -> help tl (Some ident, n) in | ||
| match ns with | ||
| | [] -> failwith "Should be impossible" | ||
| | n::tl -> help tl (None, n) in | ||
| mkloc @@ help ns | ||
| let mkstr dyp s = mkloc s (symbol_rloc dyp) | ||
| let make_program statements body = | ||
| let prog_loc = { | ||
| loc_start=(!first_loc).loc_end; | ||
| loc_end=(!last_loc).loc_end; | ||
| loc_ghost=false; | ||
| } in | ||
| fix_blocks {statements; body; prog_loc} | ||
| } | ||
| %relation pe<pt<pp<pb<pc | ||
| %token <int> NUM | ||
| %token <string> ID | ||
| %token <string> TYPEID | ||
| %token <string> STRING | ||
| %token LBRACK RBRACK LPAREN LPARENNOSPACE RPAREN LBRACE RBRACE LCARET RCARET | ||
| %token COMMA SEMI | ||
| %token THICKARROW ARROW PIPE | ||
| %token EQEQ LESSEQ GREATEREQ | ||
| %token EQUAL GETS | ||
| %token UNDERSCORE | ||
| %token COLON COLONCOLON | ||
| %token ADD1 SUB1 ISBOOL ISNUM ISTUPLE | ||
| %token PLUS MINUS TIMES | ||
| %token TRUE FALSE | ||
| %token LET REC IF ELSE MATCH | ||
| %token AND OR NOT | ||
| %token DATA IMPORT FOREIGN WASM | ||
| %token EOF | ||
| %start <Parsetree.parsed_program> program | ||
| %parser | ||
| const : | ||
| | NUM { Const.int $1 } | ||
| | TRUE { Const.bool true } | ||
| | FALSE { Const.bool false } | ||
| | STRING { Const.string $1 } | ||
| prim1 : | ||
| | ADD1 { Add1 } | ||
| | SUB1 { Sub1 } | ||
| | NOT { Not } | ||
| | ISBOOL { IsBool } | ||
| | ISNUM { IsNum } | ||
| | ISTUPLE { IsTuple } | ||
| binop_expr: | ||
| | binop_expr(<=pp) PLUS binop_expr(<pp) { prerr_string "\nbinop_expr_plus\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); Exp.prim2 ~loc:(symbol_rloc dyp) Plus $1 $3 } pp | ||
| | binop_expr(<=pp) MINUS binop_expr(<pp) { Exp.prim2 ~loc:(symbol_rloc dyp) Minus $1 $3 } pp | ||
| | binop_expr(<=pt) TIMES binop_expr(<pt) { Exp.prim2 ~loc:(symbol_rloc dyp) Times $1 $3 } pt | ||
| | binop_expr(<=pc) EQEQ binop_expr(<pc) { Exp.prim2 ~loc:(symbol_rloc dyp) Eq $1 $3 } pc | ||
| | binop_expr(<=pc) LCARET binop_expr(<pc) { Exp.prim2 ~loc:(symbol_rloc dyp) Less $1 $3 } pc | ||
| | binop_expr(<=pc) RCARET binop_expr(<pc) { Exp.prim2 ~loc:(symbol_rloc dyp) Greater $1 $3 } pc | ||
| | binop_expr(<=pc) LESSEQ binop_expr(<pc) { Exp.prim2 ~loc:(symbol_rloc dyp) LessEq $1 $3 } pc | ||
| | binop_expr(<=pc) GREATEREQ binop_expr(<pc) { Exp.prim2 ~loc:(symbol_rloc dyp) GreaterEq $1 $3 } pc | ||
| | binop_expr(<=pb) AND binop_expr(<pb) { Exp.prim2 ~loc:(symbol_rloc dyp) And $1 $3 } pb | ||
| | binop_expr(<=pb) OR binop_expr(<pb) { Exp.prim2 ~loc:(symbol_rloc dyp) Or $1 $3 } pb | ||
| | expr { prerr_string "\nbinop_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); $1 } pe | ||
| pattern : | ||
| | pattern COLON typ { Pat.constraint_ ~loc:(symbol_rloc dyp) $1 $3 } | ||
| | UNDERSCORE { Pat.any ~loc:(symbol_rloc dyp) () } | ||
| /* If the pattern uses an external ID, we know it's a constructor, not a variable */ | ||
| | ext_id { Pat.construct ~loc:(symbol_rloc dyp) $1 [] } | ||
| | ID { Pat.var ~loc:(symbol_rloc dyp) (mkstr dyp $1) } | ||
| | LPAREN tuple_patterns RPAREN { Pat.tuple ~loc:(symbol_rloc dyp) $2 } | ||
| | LPAREN pattern RPAREN { $2 } | ||
| | type_id LPAREN tuple_patterns RPAREN { Pat.construct ~loc:(symbol_rloc dyp) $1 $3 } | ||
| | type_id { Pat.construct ~loc:(symbol_rloc dyp) $1 [] } | ||
| patterns : | ||
| | pattern [COMMA pattern {$2}]* { $1::$2 } | ||
| tuple_patterns : | ||
| | pattern COMMA { [$1] } | ||
| | pattern [COMMA pattern {$2}]+ { $1::$2 } | ||
| data_typ : | ||
| | type_id LCARET typ [COMMA typ {$2}]* RCARET { Typ.constr $1 ($3::$4) } | ||
| | type_id { Typ.constr $1 [] } | ||
| typ : | ||
| /* Convenience: Parens optional for single-argument functions */ | ||
| | data_typ ARROW typ { Typ.arrow ~loc:(symbol_rloc dyp) [$1] $3 } | ||
| | ID ARROW typ { Typ.arrow ~loc:(symbol_rloc dyp) [(Typ.var $1)] $3 } | ||
| | LPAREN typs RPAREN ARROW typ { Typ.arrow ~loc:(symbol_rloc dyp) $2 $5 } | ||
| | LPAREN tuple_typs RPAREN { Typ.tuple ~loc:(symbol_rloc dyp) $2 } | ||
| | LPAREN typ RPAREN { $2 } | ||
| | ID { Typ.var $1 } | ||
| | data_typ | ||
| typs : | ||
| | [typ [COMMA typ {$2}]* {$1::$2}]? { Option.default [] $1 } | ||
| tuple_typs : | ||
| | typ COMMA { [$1] } | ||
| | typ [COMMA typ {$2}]+ { $1::$2 } | ||
| value_bind : | ||
| | pattern EQUAL binop_expr { Vb.mk ~loc:(symbol_rloc dyp) $1 $3 } | ||
| value_binds : | ||
| | value_bind [COMMA value_bind {$2}]* { $1::$2 } | ||
| import_stmt : | ||
| | IMPORT id { Imp.mk $2 } | ||
| data_constructor : | ||
| | TYPEID { CDecl.singleton ~loc:(symbol_rloc dyp) (mkstr dyp $1) } | ||
| | TYPEID LPAREN typs RPAREN { CDecl.tuple ~loc:(symbol_rloc dyp) (mkstr dyp $1) $3 } | ||
| data_constructors : | ||
| | data_constructor [PIPE data_constructor {$2}]* { $1::$2 } | ||
| | [PIPE data_constructor {$2}]+ { $1 } | ||
| data_declaration : | ||
| | DATA TYPEID EQUAL data_constructors { Dat.mk ~loc:(symbol_rloc dyp) (mkstr dyp $2) [] (PDataVariant $4) } | ||
| | DATA TYPEID LCARET ID [COMMA ID {$2}]* RCARET EQUAL data_constructors { Dat.mk ~loc:(symbol_rloc dyp) (mkstr dyp $2) (List.map Typ.var ($4::$5)) (PDataVariant $8) } | ||
| prim1_expr : | ||
| | prim1 LPAREN binop_expr RPAREN { Exp.prim1 ~loc:(symbol_rloc dyp) $1 $3 } | ||
| paren_expr : | ||
| | LPAREN binop_expr RPAREN { $2 } | ||
| app_arg_exprs : | ||
| | [binop_expr [COMMA binop_expr {$2}]* { $1::$2 }]? { Option.default [] $1 } | ||
| app_expr : | ||
| | expr LPAREN app_arg_exprs RPAREN { prerr_string "\napp_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); Exp.apply ~loc:(symbol_rloc dyp) $1 $3 } | ||
| ext_id : | ||
| | ID [COLONCOLON TYPEID {$2}]+ { prerr_string "\nid\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); (mkid ($1::$2)) (symbol_rloc dyp) } | ||
| id : | ||
| | ID [COLONCOLON ID {$2}]* { prerr_string "\nid\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); (mkid ($1::$2)) (symbol_rloc dyp) } | ||
| type_id : | ||
| | TYPEID { prerr_string "\nid\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); (mkid [$1]) (symbol_rloc dyp) } | ||
| simple_expr : | ||
| | const { Exp.constant ~loc:(symbol_rloc dyp) $1 } | ||
| | id { prerr_string "\nsimple_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); Exp.ident ~loc:(symbol_rloc dyp) $1 } | ||
| | type_id { prerr_string "\nsimple_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); Exp.ident ~loc:(symbol_rloc dyp) $1 } | ||
| | LPAREN tuple_exprs RPAREN { Exp.tuple ~loc:(symbol_rloc dyp) $2 } | ||
| block_expr : | ||
| | LBRACE block RBRACE { Exp.block ~loc:(symbol_rloc dyp) $2 } | ||
| lam_args : | ||
| | patterns? { Option.default [] $1 } | ||
| lam_expr : | ||
| | LPAREN lam_args RPAREN THICKARROW block_expr { Exp.lambda ~loc:(symbol_rloc dyp) $2 $5 } | ||
| let_expr : | ||
| | LET REC value_binds { Exp.let_ ~loc:(symbol_rloc dyp) Recursive $3 (Exp.block []) } | ||
| | LET value_binds { Exp.let_ ~loc:(symbol_rloc dyp) Nonrecursive $2 (Exp.block []) } | ||
| if_expr : | ||
| | IF binop_expr block_expr { Exp.if_ ~loc:(symbol_rloc dyp) $2 $3 (Exp.block []) } | ||
| | IF binop_expr block_expr ELSE block_expr { Exp.if_ ~loc:(symbol_rloc dyp) $2 $3 $5 } | ||
| match_branch : | ||
| | pattern THICKARROW binop_expr { Mb.mk ~loc:(symbol_rloc dyp) $1 $3 } | ||
| match_branches : | ||
| | [PIPE match_branch {$2}]+ { $1 } | ||
| match_expr : | ||
| | MATCH LPAREN expr RPAREN LBRACE match_branches RBRACE { Exp.match_ ~loc:(symbol_rloc dyp) $3 $6 } | ||
| expr : | ||
| | app_expr { prerr_string "\nexpr_app_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); $1 } | ||
| | simple_expr { prerr_string "\nexpr_simple_expr\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); $1 } | ||
| | paren_expr { $1 } | ||
| | prim1_expr { $1 } | ||
| | block_expr { $1 } | ||
| | lam_expr { $1 } | ||
| | if_expr { $1 } | ||
| | match_expr { $1 } | ||
| block_body_expr : | ||
| | let_expr { $1 } | ||
| | binop_expr { $1 } | ||
| tuple_exprs : | ||
| | binop_expr COMMA { [$1] } | ||
| | binop_expr [COMMA binop_expr {$2}]+ { $1::$2 } | ||
| block : | ||
| | block_body_expr [SEMI block_body_expr {$2}]* { $1::$2 } | ||
| id_str : | ||
| | ID { Location.mkloc $1 (symbol_rloc dyp) } | ||
| foreign_stmt : | ||
| | FOREIGN WASM id_str id_str COLON typ { Val.mk ~loc:(symbol_rloc dyp) ~mod_:$3 ~name:$4 ~typ:$6 ~prim:[$4.txt] } | ||
| toplevel_stmt : | ||
| | LET REC value_binds { Top.let_ Recursive $3 } | ||
| | LET value_binds { Top.let_ Nonrecursive $2 } | ||
| | foreign_stmt { Top.foreign ~loc:(symbol_rloc dyp) $1 } | ||
| | import_stmt { Top.import $1 } | ||
| | data_declaration { Top.data $1 } | ||
| toplevel_stmts : | ||
| | toplevel_stmt [SEMI toplevel_stmt {$2}]* { $1::$2 } | ||
| program : | ||
| | toplevel_stmts SEMI EOF { make_program $1 (Exp.null ~loc:dummy_loc ()) } | ||
| | toplevel_stmts SEMI binop_expr EOF { make_program $1 $3 } | ||
| | binop_expr EOF { prerr_string "\nprogram\n"; when_debug ~n:1 (fun () -> dyp.print_state stderr); make_program [] $1 } | ||
| %% | ||
| { | ||
| let parse_program t lexbuf = | ||
| Dyp.dypgen_verbose := !Grain_utils.Config.parser_debug_level; | ||
| first_loc := Location.curr lexbuf; | ||
| with_default_loc_src (fun() -> !last_loc) (fun() -> program t lexbuf) | ||
| let print_syntax_error = | ||
| let open Printf in | ||
| let open Location in | ||
| function | ||
| | Syntax_error -> begin | ||
| debug_print_state(); | ||
| Some(errorf ~loc:(!last_loc) "Syntax error") | ||
| end | ||
| | _ -> None | ||
| let () = | ||
| Dyp.dypgen_verbose := !Grain_utils.Config.parser_debug_level; | ||
| Location.register_error_of_exn print_syntax_error | ||
| } | ||
| %mli { | ||
| val parse_program : Lexing.lexbuf -> ((Parsetree.parsed_program * 'a) list) | ||
| } |
| @@ -0,0 +1,171 @@ | ||
| (** Parse tree type definitions. This is a reformulation | ||
| of our original parse tree which better aligns with the design | ||
| of the OCaml parse tree. Credit for the module's architecture goes to | ||
| the OCaml team. *) | ||
| open Sexplib.Conv | ||
| open Asttypes | ||
| type 'a loc = 'a Asttypes.loc = { | ||
| txt: 'a; | ||
| loc: Location.t; | ||
| } | ||
| type rec_flag = Asttypes.rec_flag = Nonrecursive | Recursive | ||
| (** Type for syntax-level types *) | ||
| type parsed_type_desc = | ||
| | PTyAny | ||
| | PTyVar of string | ||
| | PTyArrow of parsed_type list * parsed_type | ||
| | PTyTuple of parsed_type list | ||
| | PTyConstr of (Identifier.t loc) * parsed_type list | ||
| | PTyPoly of string loc list * parsed_type | ||
| [@@deriving sexp] | ||
| and parsed_type = { | ||
| ptyp_desc: parsed_type_desc; | ||
| ptyp_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } | ||
| (** Type for arguments to a constructor *) | ||
| type constructor_arguments = | ||
| | PConstrTuple of parsed_type list | ||
| | PConstrSingleton | ||
| [@@deriving sexp] | ||
| (** Type for branches within data declarations *) | ||
| type constructor_declaration = { | ||
| pcd_name: string loc; | ||
| pcd_args: constructor_arguments; | ||
| pcd_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** Different types of data which can be declared. Currently only one. *) | ||
| type data_kind = | ||
| | PDataVariant of constructor_declaration list | ||
| [@@deriving sexp] | ||
| (** Type for data declarations. *) | ||
| type data_declaration = { | ||
| pdata_name: string loc; | ||
| pdata_params: parsed_type list; | ||
| pdata_kind: data_kind; | ||
| pdata_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** Constants supported by Grain *) | ||
| type constant = | ||
| | PConstNumber of int | ||
| | PConstBool of bool | ||
| | PConstString of string | ||
| [@@deriving sexp] | ||
| (** Various binding forms *) | ||
| type pattern_desc = | ||
| | PPatAny | ||
| | PPatVar of string loc | ||
| | PPatTuple of pattern list | ||
| | PPatConstant of constant | ||
| | PPatConstraint of pattern * parsed_type | ||
| | PPatConstruct of Identifier.t loc * pattern list | ||
| | PPatOr of pattern * pattern | ||
| | PPatAlias of pattern * string loc | ||
| [@@deriving sexp] | ||
| and pattern = { | ||
| ppat_desc: pattern_desc; | ||
| ppat_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** Single-argument operators *) | ||
| type prim1 = | ||
| | Add1 | ||
| | Sub1 | ||
| | Not | ||
| | IsNum | ||
| | IsBool | ||
| | IsTuple | ||
| [@@deriving sexp] | ||
| (** Two-argument operators *) | ||
| type prim2 = | ||
| | Plus | ||
| | Minus | ||
| | Times | ||
| | Less | ||
| | Greater | ||
| | LessEq | ||
| | GreaterEq | ||
| | Eq | ||
| | And | ||
| | Or | ||
| [@@deriving sexp] | ||
| (** Type for expressions (i.e. things which evaluate to something) *) | ||
| type expression = { | ||
| pexp_desc: expression_desc; | ||
| pexp_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| and expression_desc = | ||
| | PExpId of Identifier.t loc | ||
| | PExpConstant of constant | ||
| | PExpTuple of expression list | ||
| | PExpLet of rec_flag * value_binding list * expression | ||
| | PExpMatch of expression * match_branch list | ||
| | PExpPrim1 of prim1 * expression | ||
| | PExpPrim2 of prim2 * expression * expression | ||
| | PExpIf of expression * expression * expression | ||
| | PExpLambda of pattern list * expression | ||
| | PExpApp of expression * expression list | ||
| | PExpBlock of expression list | ||
| | PExpNull (** Used for modules without body expressions *) | ||
| [@@deriving sexp] | ||
| (** let-binding form *) | ||
| and value_binding = { | ||
| pvb_pat: pattern; | ||
| pvb_expr: expression; | ||
| pvb_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| and match_branch = { | ||
| pmb_pat: pattern; | ||
| pmb_body: expression; | ||
| pmb_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** Type for import statements *) | ||
| type import_declaration = { | ||
| pimp_mod: Identifier.t loc; | ||
| pimp_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| type value_description = { | ||
| pval_mod: string loc; | ||
| pval_name: string loc; | ||
| pval_type: parsed_type; | ||
| pval_prim: string list; | ||
| pval_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** Statements which can exist at the top level *) | ||
| type toplevel_stmt_desc = | ||
| | PTopForeign of value_description | ||
| | PTopImport of import_declaration | ||
| | PTopData of data_declaration | ||
| | PTopLet of rec_flag * value_binding list | ||
| [@@deriving sexp] | ||
| type toplevel_stmt = { | ||
| ptop_desc: toplevel_stmt_desc; | ||
| ptop_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| (** The type for parsed programs *) | ||
| type parsed_program = { | ||
| statements: toplevel_stmt list; | ||
| body: expression; | ||
| prog_loc: Location.t [@sexp_drop_if fun _ -> not !Grain_utils.Config.sexp_locs_enabled]; | ||
| } [@@deriving sexp] | ||
| @@ -0,0 +1,26 @@ | ||
| (* See copyright information in syntaxerr.mli *) | ||
| type stxerr = | ||
| | LetWithoutBody of Location.t | ||
| | Other of Location.t | ||
| exception Error of stxerr | ||
| let prepare_error = function | ||
| | LetWithoutBody loc -> | ||
| Location.errorf ~loc "Missing expression after let binding" | ||
| | Other loc -> | ||
| Location.errorf ~loc "Syntax error" | ||
| let () = | ||
| Location.register_error_of_exn | ||
| (function | ||
| | Error err -> Some (prepare_error err) | ||
| | _ -> None | ||
| ) | ||
| let location_of_error = function | ||
| | LetWithoutBody l | ||
| | Other l -> l | ||
| @@ -0,0 +1,23 @@ | ||
| (* This file is largely copied from OCaml's parsing/syntaxerr.mli file. The original copyright is reproduced below. *) | ||
| (**************************************************************************) | ||
| (* *) | ||
| (* OCaml *) | ||
| (* *) | ||
| (* Xavier Leroy, projet Cristal, INRIA Rocquencourt *) | ||
| (* *) | ||
| (* Copyright 1997 Institut National de Recherche en Informatique et *) | ||
| (* en Automatique. *) | ||
| (* *) | ||
| (* All rights reserved. This file is distributed under the terms of *) | ||
| (* the GNU Lesser General Public License version 2.1, with the *) | ||
| (* special exception on linking described in the file LICENSE. *) | ||
| (* *) | ||
| (**************************************************************************) | ||
| type stxerr = | ||
| | LetWithoutBody of Location.t | ||
| | Other of Location.t | ||
| exception Error of stxerr | ||
| val location_of_error: stxerr -> Location.t |
| @@ -0,0 +1,103 @@ | ||
| open Parsetree | ||
| open Ast_iterator | ||
| open Grain_utils | ||
| type wferr = | ||
| | MalformedString of Location.t | ||
| | MultipleModuleName of Location.t | ||
| | TypeNameShouldBeUppercase of string * Location.t | ||
| | TyvarNameShouldBeLowercase of string * Location.t | ||
| exception Error of wferr | ||
| let prepare_error = | ||
| let open Printf in | ||
| let open Location in | ||
| function | ||
| | MalformedString loc -> | ||
| errorf ~loc "Malformed string literal" | ||
| | MultipleModuleName loc -> | ||
| errorf ~loc "Multiple modules in identifier" | ||
| | TypeNameShouldBeUppercase(name, loc) -> | ||
| errorf ~loc "Type '%s' should have an uppercase name." name | ||
| | TyvarNameShouldBeLowercase(var, loc) -> | ||
| errorf ~loc "Type variable '%s' should be lowercase." var | ||
| let () = | ||
| Location.register_error_of_exn | ||
| (function | ||
| | Error err -> Some (prepare_error err) | ||
| | _ -> None | ||
| ) | ||
| type well_formedness_checker = { | ||
| errs : wferr list ref; | ||
| iterator : iterator; | ||
| } | ||
| let malformed_strings errs super = | ||
| let iter_expr self ({pexp_desc=desc; pexp_loc=loc} as e) = | ||
| begin | ||
| match desc with | ||
| | PExpConstant(PConstString s) -> | ||
| begin | ||
| try | ||
| BatUTF8.validate s | ||
| with | ||
| | BatUTF8.Malformed_code -> | ||
| errs := (MalformedString loc)::!errs | ||
| end | ||
| | _ -> () | ||
| end; | ||
| super.expr self e in | ||
| let iterator = { super with | ||
| expr = iter_expr } in | ||
| { errs; iterator } | ||
| let malformed_identifiers errs super = | ||
| let open Identifier in | ||
| let iter_expr self ({pexp_desc=desc; pexp_loc=loc} as e) = | ||
| begin | ||
| match desc with | ||
| | PExpId {txt=(IdentExternal(IdentExternal _, _))} -> | ||
| errs := (MultipleModuleName loc)::!errs | ||
| | _ -> () | ||
| end; | ||
| super.expr self e in | ||
| let iterator = { super with expr = iter_expr } in | ||
| { errs; iterator } | ||
| let types_have_correct_case errs super = | ||
| let check_uppercase loc s = | ||
| let first_char = String.get s 0 in | ||
| if first_char <> BatChar.uppercase first_char then | ||
| errs := (TypeNameShouldBeUppercase(s, loc))::!errs in | ||
| let iter_data self ({pdata_name={loc=name_loc; txt=name}; pdata_loc=loc; _} as d) = | ||
| check_uppercase name_loc name; | ||
| super.data self d in | ||
| (* FIXME: The parser should read in uppercase types as PTyConstr instances *) | ||
| let iterator = { super with data = iter_data } in | ||
| { errs; iterator } | ||
| let compose_well_formedness { errs; iterator } cur = | ||
| cur errs iterator | ||
| let well_formedness_checks = [ | ||
| malformed_strings; | ||
| malformed_identifiers; | ||
| types_have_correct_case; | ||
| ] | ||
| let well_formedness_checker() = | ||
| List.fold_left | ||
| compose_well_formedness | ||
| {errs=ref []; iterator=default_iterator} | ||
| well_formedness_checks | ||
| let check_well_formedness {statements; body} = | ||
| let checker = well_formedness_checker() in | ||
| List.iter (checker.iterator.toplevel checker.iterator) statements; | ||
| checker.iterator.expr checker.iterator body; | ||
| (* FIXME: We should be able to raise _all_ errors at once *) | ||
| List.iter (fun e -> raise (Error e)) !(checker.errs) |