diff options
| author | Graydon Hoare <[email protected]> | 2010-06-23 21:03:09 -0700 |
|---|---|---|
| committer | Graydon Hoare <[email protected]> | 2010-06-23 21:03:09 -0700 |
| commit | d6b7c96c3eb29b9244ece0c046d3f372ff432d04 (patch) | |
| tree | b425187e232966063ffc2f0d14c04a55d8f004ef /src/boot/fe/cexp.ml | |
| parent | Initial git commit. (diff) | |
| download | rust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.tar.xz rust-d6b7c96c3eb29b9244ece0c046d3f372ff432d04.zip | |
Populate tree.
Diffstat (limited to 'src/boot/fe/cexp.ml')
| -rw-r--r-- | src/boot/fe/cexp.ml | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/src/boot/fe/cexp.ml b/src/boot/fe/cexp.ml new file mode 100644 index 00000000..6dffdb96 --- /dev/null +++ b/src/boot/fe/cexp.ml @@ -0,0 +1,762 @@ + +open Common;; +open Token;; +open Parser;; + +(* NB: cexps (crate-expressions / constant-expressions) are only used + * transiently during compilation: they are the outermost expression-language + * describing crate configuration and constants. They are completely evaluated + * at compile-time, in a little micro-interpreter defined here, with the + * results of evaluation being the sequence of directives controlling the rest + * of the compiler. + * + * Cexps, like pexps, do not escape the language front-end. + * + * You can think of the AST as a statement-language called "item" sandwiched + * between two expression-languages, "cexp" on the outside and "pexp" on the + * inside. The front-end evaluates cexp on the outside in order to get one big + * directive-list, evaluating those parts of pexp that are directly used by + * cexp in passing, and desugaring those remaining parts of pexp that are + * embedded within the items of the directives. + * + * The rest of the compiler only deals with the directives, which are mostly + * just a set of containers for items. Items are what most of AST describes + * ("most" because the type-grammar spans both items and pexps). + * + *) + +type meta = (Ast.ident * Pexp.pexp) array;; + +type meta_pat = (Ast.ident * (Pexp.pexp option)) array;; + +type auth = (Ast.name * Ast.effect);; + +type cexp = + CEXP_alt of cexp_alt identified + | CEXP_let of cexp_let identified + | CEXP_src_mod of cexp_src identified + | CEXP_dir_mod of cexp_dir identified + | CEXP_use_mod of cexp_use identified + | CEXP_nat_mod of cexp_nat identified + | CEXP_meta of meta identified + | CEXP_auth of auth identified + +and cexp_alt = + { alt_val: Pexp.pexp; + alt_arms: (Pexp.pexp * cexp array) array; + alt_else: cexp array } + +and cexp_let = + { let_ident: Ast.ident; + let_value: Pexp.pexp; + let_body: cexp array; } + +and cexp_src = + { src_ident: Ast.ident; + src_path: Pexp.pexp option } + +and cexp_dir = + { dir_ident: Ast.ident; + dir_path: Pexp.pexp option; + dir_body: cexp array } + +and cexp_use = + { use_ident: Ast.ident; + use_meta: meta_pat; } + +and cexp_nat = + { nat_abi: string; + nat_ident: Ast.ident; + nat_path: Pexp.pexp option; + (* + * FIXME: possibly support embedding optional strings as + * symbol-names, to handle mangling schemes that aren't + * Token.IDENT values + *) + nat_items: Ast.mod_items; + } +;; + + +(* Cexp grammar. *) + +let parse_meta_input (ps:pstate) : (Ast.ident * Pexp.pexp option) = + let lab = (ctxt "meta input: label" Pexp.parse_ident ps) in + match peek ps with + EQ -> + bump ps; + let v = + match peek ps with + UNDERSCORE -> bump ps; None + | _ -> Some (Pexp.parse_pexp ps) + in + (lab, v) + | _ -> raise (unexpected ps) +;; + +let parse_meta_pat (ps:pstate) : meta_pat = + bracketed_zero_or_more LPAREN RPAREN + (Some COMMA) parse_meta_input ps +;; + +let parse_meta (ps:pstate) : meta = + Array.map + begin + fun (id,v) -> + match v with + None -> + raise (err ("wildcard found in meta pattern " + ^ "where value expected") ps) + | Some v -> (id,v) + end + (parse_meta_pat ps) +;; + +let parse_optional_meta_pat + (ps:pstate) + (ident:Ast.ident) + : meta_pat = + match peek ps with + LPAREN -> parse_meta_pat ps + | _ -> + let apos = lexpos ps in + [| ("name", Some (span ps apos apos (Pexp.PEXP_str ident))) |] +;; + +let rec parse_cexps (ps:pstate) (term:Token.token) : cexp array = + let cexps = Queue.create () in + while ((peek ps) <> term) + do + Queue.push (parse_cexp ps) cexps + done; + expect ps term; + queue_to_arr cexps + +and parse_cexp (ps:pstate) : cexp = + + let apos = lexpos ps in + match peek ps with + MOD -> + begin + bump ps; + let name = ctxt "mod: name" Pexp.parse_ident ps in + let path = ctxt "mod: path" parse_eq_pexp_opt ps + in + match peek ps with + SEMI -> + bump ps; + let bpos = lexpos ps in + CEXP_src_mod + (span ps apos bpos { src_ident = name; + src_path = path }) + | LBRACE -> + let body = + bracketed_zero_or_more LBRACE RBRACE + None parse_cexp ps + in + let bpos = lexpos ps in + CEXP_dir_mod + (span ps apos bpos { dir_ident = name; + dir_path = path; + dir_body = body }) + | _ -> raise (unexpected ps) + end + + | NATIVE -> + begin + bump ps; + let abi = + match peek ps with + MOD -> "cdecl" + | LIT_STR s -> bump ps; s + | _ -> raise (unexpected ps) + in + let _ = expect ps MOD in + let name = ctxt "native mod: name" Pexp.parse_ident ps in + let path = ctxt "native mod: path" parse_eq_pexp_opt ps in + let items = Hashtbl.create 0 in + let get_item ps = + let (ident, item) = Item.parse_mod_item_from_signature ps in + htab_put items ident item; + in + ignore (bracketed_zero_or_more + LBRACE RBRACE None get_item ps); + let bpos = lexpos ps in + CEXP_nat_mod + (span ps apos bpos { nat_abi = abi; + nat_ident = name; + nat_path = path; + nat_items = items }) + end + + | USE -> + begin + bump ps; + let ident = ctxt "use mod: name" Pexp.parse_ident ps in + let meta = + ctxt "use mod: meta" parse_optional_meta_pat ps ident + in + let bpos = lexpos ps in + expect ps SEMI; + CEXP_use_mod + (span ps apos bpos { use_ident = ident; + use_meta = meta }) + end + + | LET -> + begin + bump ps; + expect ps LPAREN; + let id = Pexp.parse_ident ps in + expect ps EQ; + let v = Pexp.parse_pexp ps in + expect ps RPAREN; + expect ps LBRACE; + let body = parse_cexps ps RBRACE in + let bpos = lexpos ps in + CEXP_let + (span ps apos bpos + { let_ident = id; + let_value = v; + let_body = body }) + end + + | ALT -> + begin + bump ps; + expect ps LPAREN; + let v = Pexp.parse_pexp ps in + expect ps RPAREN; + expect ps LBRACE; + let rec consume_arms arms = + match peek ps with + CASE -> + begin + bump ps; + expect ps LPAREN; + let cond = Pexp.parse_pexp ps in + expect ps RPAREN; + expect ps LBRACE; + let consequent = parse_cexps ps RBRACE in + let arm = (cond, consequent) in + consume_arms (arm::arms) + end + | ELSE -> + begin + bump ps; + expect ps LBRACE; + let consequent = parse_cexps ps RBRACE in + expect ps RBRACE; + let bpos = lexpos ps in + span ps apos bpos + { alt_val = v; + alt_arms = Array.of_list (List.rev arms); + alt_else = consequent } + end + + | _ -> raise (unexpected ps) + in + CEXP_alt (consume_arms []) + end + + | META -> + bump ps; + let meta = parse_meta ps in + expect ps SEMI; + let bpos = lexpos ps in + CEXP_meta (span ps apos bpos meta) + + | AUTH -> + bump ps; + let name = Pexp.parse_name ps in + expect ps EQ; + let effect = Pexp.parse_effect ps in + expect ps SEMI; + let bpos = lexpos ps in + CEXP_auth (span ps apos bpos (name, effect)) + + | _ -> raise (unexpected ps) + + +and parse_eq_pexp_opt (ps:pstate) : Pexp.pexp option = + match peek ps with + EQ -> + begin + bump ps; + Some (Pexp.parse_pexp ps) + end + | _ -> None +;; + + +(* + * Dynamic-typed micro-interpreter for the cexp language. + * + * The product of evaluating a pexp is a pval. + * + * The product of evlauating a cexp is a cdir array. + *) + +type pval = + PVAL_str of string + | PVAL_num of int64 + | PVAL_bool of bool +;; + +type cdir = + CDIR_meta of ((Ast.ident * string) array) + | CDIR_syntax of Ast.name + | CDIR_check of (Ast.name * pval array) + | CDIR_mod of (Ast.ident * Ast.mod_item) + | CDIR_auth of auth + +type env = { env_bindings: (Ast.ident * pval) list; + env_prefix: filename list; + env_items: (filename, Ast.mod_items) Hashtbl.t; + env_files: (node_id,filename) Hashtbl.t; + env_required: (node_id, (required_lib * nabi_conv)) Hashtbl.t; + env_required_syms: (node_id, string) Hashtbl.t; + env_ps: pstate; } + +let unexpected_val (expected:string) (v:pval) = + let got = + match v with + PVAL_str s -> "str \"" ^ (String.escaped s) ^ "\"" + | PVAL_num i -> "num " ^ (Int64.to_string i) + | PVAL_bool b -> if b then "bool true" else "bool false" + in + (* FIXME: proper error reporting, please. *) + bug () "expected %s, got %s" expected got +;; + +let rewrap_items id items = + let item = decl [||] (Ast.MOD_ITEM_mod items) in + { id = id; node = item } +;; + + +let rec eval_cexps (env:env) (exps:cexp array) : cdir array = + Parser.arj (Array.map (eval_cexp env) exps) + +and eval_cexp (env:env) (exp:cexp) : cdir array = + match exp with + CEXP_alt {node=ca} -> + let v = eval_pexp env ca.alt_val in + let rec try_arm i = + if i >= Array.length ca.alt_arms + then ca.alt_else + else + let (arm_head, arm_body) = ca.alt_arms.(i) in + let v' = eval_pexp env arm_head in + if v' = v + then arm_body + else try_arm (i+1) + in + eval_cexps env (try_arm 0) + + | CEXP_let {node=cl} -> + let ident = cl.let_ident in + let v = eval_pexp env cl.let_value in + let env = { env with + env_bindings = ((ident,v)::env.env_bindings ) } + in + eval_cexps env cl.let_body + + | CEXP_src_mod {node=s; id=id} -> + let name = s.src_ident in + let path = + match s.src_path with + None -> name ^ ".rs" + | Some p -> eval_pexp_to_str env p + in + let full_path = + List.fold_left Filename.concat "" + (List.rev (path :: env.env_prefix)) + in + let ps = env.env_ps in + let p = + make_parser + ps.pstate_temp_id + ps.pstate_node_id + ps.pstate_opaque_id + ps.pstate_sess + ps.pstate_get_mod + ps.pstate_infer_lib_name + env.env_required + env.env_required_syms + full_path + in + let items = Item.parse_mod_items p EOF in + htab_put env.env_files id full_path; + [| CDIR_mod (name, rewrap_items id items) |] + + | CEXP_dir_mod {node=d; id=id} -> + let items = Hashtbl.create 0 in + let name = d.dir_ident in + let path = + match d.dir_path with + None -> name + | Some p -> eval_pexp_to_str env p + in + let env = { env with + env_prefix = path :: env.env_prefix } in + let sub_directives = eval_cexps env d.dir_body in + let add d = + match d with + CDIR_mod (name, item) -> + htab_put items name item + | _ -> raise (err "non-'mod' directive found in 'dir' directive" + env.env_ps) + in + Array.iter add sub_directives; + [| CDIR_mod (name, rewrap_items id (Item.empty_view, items)) |] + + | CEXP_use_mod {node=u; id=id} -> + let ps = env.env_ps in + let name = u.use_ident in + let (path, items) = + let meta_pat = + Array.map + begin + fun (k,vo) -> + match vo with + None -> (k, None) + | Some p -> (k, Some (eval_pexp_to_str env p)) + end + u.use_meta + in + ps.pstate_get_mod meta_pat id ps.pstate_node_id ps.pstate_opaque_id + in + iflog ps + begin + fun _ -> + log ps "extracted mod signature from %s (binding to %s)" + path name; + log ps "%a" Ast.sprintf_mod_items items; + end; + let rlib = REQUIRED_LIB_rust { required_libname = path; + required_prefix = 1 } + in + let item = decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, items)) in + let item = { id = id; node = item } in + let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in + Item.note_required_mod env.env_ps span CONV_rust rlib item; + [| CDIR_mod (name, item) |] + + | CEXP_nat_mod {node=cn;id=id} -> + let conv = + let v = cn.nat_abi in + match string_to_conv v with + None -> unexpected_val "calling convention" (PVAL_str v) + | Some c -> c + in + let name = cn.nat_ident in + let filename = + match cn.nat_path with + None -> env.env_ps.pstate_infer_lib_name name + | Some p -> eval_pexp_to_str env p + in + let item = + decl [||] (Ast.MOD_ITEM_mod (Item.empty_view, cn.nat_items)) + in + let item = { id = id; node = item } in + let rlib = REQUIRED_LIB_c { required_libname = filename; + required_prefix = 1 } + in + let ps = env.env_ps in + let span = Hashtbl.find ps.pstate_sess.Session.sess_spans id in + Item.note_required_mod env.env_ps span conv rlib item; + [| CDIR_mod (name, item) |] + + | CEXP_meta m -> + [| CDIR_meta + begin + Array.map + begin + fun (id, p) -> (id, eval_pexp_to_str env p) + end + m.node + end |] + + | CEXP_auth a -> [| CDIR_auth a.node |] + + +and eval_pexp (env:env) (exp:Pexp.pexp) : pval = + match exp.node with + | Pexp.PEXP_binop (bop, a, b) -> + begin + let av = eval_pexp env a in + let bv = eval_pexp env b in + match (bop, av, bv) with + (Ast.BINOP_add, PVAL_str az, PVAL_str bz) -> + PVAL_str (az ^ bz) + | _ -> + let av = (need_num av) in + let bv = (need_num bv) in + PVAL_num + begin + match bop with + Ast.BINOP_add -> Int64.add av bv + | Ast.BINOP_sub -> Int64.sub av bv + | Ast.BINOP_mul -> Int64.mul av bv + | Ast.BINOP_div -> Int64.div av bv + | _ -> + bug () + "unhandled arithmetic op in Cexp.eval_pexp" + end + end + + | Pexp.PEXP_unop (uop, a) -> + begin + match uop with + Ast.UNOP_not -> + PVAL_bool (not (eval_pexp_to_bool env a)) + | Ast.UNOP_neg -> + PVAL_num (Int64.neg (eval_pexp_to_num env a)) + | _ -> bug () "Unexpected unop in Cexp.eval_pexp" + end + + | Pexp.PEXP_lval (Pexp.PLVAL_ident ident) -> + begin + match ltab_search env.env_bindings ident with + None -> raise (err (Printf.sprintf "no binding for '%s' found" + ident) env.env_ps) + | Some v -> v + end + + | Pexp.PEXP_lit (Ast.LIT_bool b) -> + PVAL_bool b + + | Pexp.PEXP_lit (Ast.LIT_int (i, _)) -> + PVAL_num i + + | Pexp.PEXP_str s -> + PVAL_str s + + | _ -> bug () "unexpected Pexp in Cexp.eval_pexp" + + +and eval_pexp_to_str (env:env) (exp:Pexp.pexp) : string = + match eval_pexp env exp with + PVAL_str s -> s + | v -> unexpected_val "str" v + +and need_num (cv:pval) : int64 = + match cv with + PVAL_num n -> n + | v -> unexpected_val "num" v + +and eval_pexp_to_num (env:env) (exp:Pexp.pexp) : int64 = + need_num (eval_pexp env exp) + +and eval_pexp_to_bool (env:env) (exp:Pexp.pexp) : bool = + match eval_pexp env exp with + PVAL_bool b -> b + | v -> unexpected_val "bool" v + +;; + + +let find_main_fn + (ps:pstate) + (crate_items:Ast.mod_items) + : Ast.name = + let fns = ref [] in + let extend prefix_name ident = + match prefix_name with + None -> Ast.NAME_base (Ast.BASE_ident ident) + | Some n -> Ast.NAME_ext (n, Ast.COMP_ident ident) + in + let rec dig prefix_name items = + Hashtbl.iter (extract_fn prefix_name) items + and extract_fn prefix_name ident item = + if not (Array.length item.node.Ast.decl_params = 0) || + Hashtbl.mem ps.pstate_required item.id + then () + else + match item.node.Ast.decl_item with + Ast.MOD_ITEM_mod (_, items) -> + dig (Some (extend prefix_name ident)) items + + | Ast.MOD_ITEM_fn _ -> + if ident = "main" + then fns := (extend prefix_name ident) :: (!fns) + else () + + | _ -> () + in + dig None crate_items; + match !fns with + [] -> raise (err "no 'main' function found" ps) + | [x] -> x + | _ -> raise (err "multiple 'main' functions found" ps) +;; + + +let with_err_handling sess thunk = + try + thunk () + with + Parse_err (ps, str) -> + Session.fail sess "Parse error: %s\n%!" str; + List.iter + (fun (cx,pos) -> + Session.fail sess "%s:E (parse context): %s\n%!" + (Session.string_of_pos pos) cx) + ps.pstate_ctxt; + let apos = lexpos ps in + span ps apos apos + { Ast.crate_items = (Item.empty_view, Hashtbl.create 0); + Ast.crate_meta = [||]; + Ast.crate_auth = Hashtbl.create 0; + Ast.crate_required = Hashtbl.create 0; + Ast.crate_required_syms = Hashtbl.create 0; + Ast.crate_main = None; + Ast.crate_files = Hashtbl.create 0 } +;; + + +let parse_crate_file + (sess:Session.sess) + (get_mod:get_mod_fn) + (infer_lib_name:(Ast.ident -> filename)) + : Ast.crate = + let fname = Session.filename_of sess.Session.sess_in in + let tref = ref (Temp 0) in + let nref = ref (Node 0) in + let oref = ref (Opaque 0) in + let required = Hashtbl.create 4 in + let required_syms = Hashtbl.create 4 in + let ps = + make_parser tref nref oref sess get_mod + infer_lib_name required required_syms fname + in + + let files = Hashtbl.create 0 in + let items = Hashtbl.create 4 in + let target_bindings = + let (os, arch, libc) = + match sess.Session.sess_targ with + Linux_x86_elf -> ("linux", "x86", "libc.so.6") + | Win32_x86_pe -> ("win32", "x86", "msvcrt.dll") + | MacOS_x86_macho -> ("macos", "x86", "libc.dylib") + in + [ + ("target_os", PVAL_str os); + ("target_arch", PVAL_str arch); + ("target_libc", PVAL_str libc) + ] + in + let build_bindings = + [ + ("build_compiler", PVAL_str Sys.executable_name); + ("build_input", PVAL_str fname); + ] + in + let initial_bindings = + target_bindings + @ build_bindings + in + let env = { env_bindings = initial_bindings; + env_prefix = [Filename.dirname fname]; + env_items = Hashtbl.create 0; + env_files = files; + env_required = required; + env_required_syms = required_syms; + env_ps = ps; } + in + let auth = Hashtbl.create 0 in + with_err_handling sess + begin + fun _ -> + let apos = lexpos ps in + let cexps = parse_cexps ps EOF in + let cdirs = eval_cexps env cexps in + let meta = Queue.create () in + let _ = + Array.iter + begin + fun d -> + match d with + CDIR_mod (name, item) -> htab_put items name item + | CDIR_meta metas -> + Array.iter (fun m -> Queue.add m meta) metas + | CDIR_auth (n,e) -> + if Hashtbl.mem auth n + then raise (err "duplicate 'auth' clause" ps) + else Hashtbl.add auth n e + | _ -> + raise + (err "unhandled directive at top level" ps) + end + cdirs + in + let bpos = lexpos ps in + let main = + if ps.pstate_sess.Session.sess_library_mode + then None + else Some (find_main_fn ps items) in + let crate = { Ast.crate_items = (Item.empty_view, items); + Ast.crate_meta = queue_to_arr meta; + Ast.crate_auth = auth; + Ast.crate_required = required; + Ast.crate_required_syms = required_syms; + Ast.crate_main = main; + Ast.crate_files = files } + in + let cratei = span ps apos bpos crate in + htab_put files cratei.id fname; + cratei + end +;; + +let parse_src_file + (sess:Session.sess) + (get_mod:get_mod_fn) + (infer_lib_name:(Ast.ident -> filename)) + : Ast.crate = + let fname = Session.filename_of sess.Session.sess_in in + let tref = ref (Temp 0) in + let nref = ref (Node 0) in + let oref = ref (Opaque 0) in + let required = Hashtbl.create 0 in + let required_syms = Hashtbl.create 0 in + let ps = + make_parser tref nref oref sess get_mod + infer_lib_name required required_syms fname + in + with_err_handling sess + begin + fun _ -> + let apos = lexpos ps in + let items = Item.parse_mod_items ps EOF in + let bpos = lexpos ps in + let files = Hashtbl.create 0 in + let main = + if ps.pstate_sess.Session.sess_library_mode + then None + else Some (find_main_fn ps (snd items)) + in + let crate = { Ast.crate_items = items; + Ast.crate_required = required; + Ast.crate_required_syms = required_syms; + Ast.crate_auth = Hashtbl.create 0; + Ast.crate_meta = [||]; + Ast.crate_main = main; + Ast.crate_files = files } + in + let cratei = span ps apos bpos crate in + htab_put files cratei.id fname; + cratei + end +;; + + +(* + * Local Variables: + * fill-column: 78; + * indent-tabs-mode: nil + * buffer-file-coding-system: utf-8-unix + * compile-command: "make -k -C ../.. 2>&1 | sed -e 's/\\/x\\//x:\\//g'"; + * End: + *) |