(* Predefined set of functions. *) include Omake_pos.Make (struct let name = "Omake_eval" end);; let debug_eval = Lm_debug.create_debug (**) { debug_name = "debug-eval"; debug_description = "Debug the evaluator"; debug_value = false } let print_ast = Lm_debug.create_debug (**) { debug_name = "print-ast"; debug_description = "Print the AST after parsing"; debug_value = false } let print_ir = Lm_debug.create_debug (**) { debug_name = "print-ir"; debug_description = "Print the IR after parsing"; debug_value = false } let print_rules = Lm_debug.create_debug (**) { debug_name = "print-rules"; debug_description = "Print the rules after evaluation"; debug_value = false } let print_files = Lm_debug.create_debug (**) { debug_name = "print-files"; debug_description = "Print the files as they are read"; debug_value = false } let bool_of_string s = match String.lowercase s with | "" | "0" | "no" | "nil" | "false" | "undefined" -> false | _ -> true (* * For now, use a bogu location for parameters. *) (* let param_loc = Lm_location.bogus_loc "Omake_eval.param" *) (* * Including files. *) (************************************************************************ * Utilities. *) let raise_uncaught_exception pos = function | Sys.Break | Omake_value_type.OmakeException _ | Omake_value_type.OmakeFatal _ | Omake_value_type.OmakeFatalErr _ | Omake_value_type.UncaughtException _ as exn -> raise exn | exn -> raise (Omake_value_type.UncaughtException (pos, exn)) (* * Add an optional quote. *) let buffer_add_quote buf = function Some c -> Buffer.add_char buf c | None -> () (* * The various forms of empty values. *) let rec is_empty_value ( v : Omake_value_type.t) = match v with | ValNone | ValWhite _ | ValString "" | ValData "" | ValQuote [] | ValArray [] | ValRules [] -> true | ValSequence vl -> List.for_all is_empty_value vl | ValObject obj -> (try is_empty_value (Omake_env.venv_find_field_internal_exn obj Omake_symbol.builtin_sym) with Not_found -> false) | ValInt _ | ValFloat _ | ValData _ | ValQuote _ | ValQuoteString _ | ValString _ | ValArray _ | ValMaybeApply _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValNode _ | ValDir _ | ValStringExp _ | ValBody _ | ValMap _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValDelayed _ | ValVar _ -> false (* * Check whether a value has an embedded array. *) let rec is_array_value (v : Omake_value_type.t) = match v with | ValArray _ -> true | ValSequence [v] | ValQuote [v] -> is_array_value v | ValObject obj -> (try match Omake_env.venv_find_field_internal_exn obj Omake_symbol.builtin_sym with ValArray _ -> true | _ -> false with Not_found -> false) | ValNone | ValInt _ | ValFloat _ | ValData _ | ValQuote _ | ValQuoteString _ | ValWhite _ | ValString _ | ValMaybeApply _ | ValSequence _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValNode _ | ValDir _ | ValStringExp _ | ValBody _ | ValMap _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValVar _ | ValDelayed _ -> false (* * Determine when an application is ready from its arity. *) type partial_arity = | FullArity of Omake_value_type.t list * Omake_value_type.t list | PartialArity of Omake_ir.arity * Omake_value_type.t list let rec concat_n_args args1 args2 n = if n = 0 then FullArity (List.rev args1, args2) else match args2 with arg :: args2 -> concat_n_args (arg :: args1) args2 (n - 1) | [] -> raise (Invalid_argument "concat_n_args") let arity_apply_args ( arity : Omake_ir.arity ) args1 args2 = let len = List.length args2 in match arity with | ArityRange (min, max) -> if len < min then let args = List.rev_append args2 args1 in PartialArity (ArityRange (min - len, max - len), args) else if len < max then let args = List.rev_append args1 args2 in FullArity (args, []) else concat_n_args args1 args2 max | ArityExact i -> if len < i then let args = List.rev_append args2 args1 in PartialArity ( ArityExact (i - len), args) else concat_n_args args1 args2 i | ArityNone -> FullArity ([], List.rev_append args1 args2) | ArityAny -> FullArity (List.rev_append args1 args2, []) (************************************************************************ * Compiling utilities. *) let postprocess_ir venv ( ir : Omake_ir.t) = let () = if Lm_debug.debug print_ir then Format.eprintf "@[<v 3>IR1:@ %a@]@." Omake_ir_print.pp_print_exp ir.ir_exp in let ir = { ir with ir_exp = Omake_ir_semant.build_prog venv ir.ir_exp } in let () = if Lm_debug.debug print_ir then Format.eprintf "@[<v 3>IR2:@ %a@]@." Omake_ir_print.pp_print_exp ir.ir_exp in ir (** Parse and evaluate a file. *) let rec parse_ir (venv : Omake_env.t) (scope : Omake_env.include_scope) (node : Omake_node.Node.t) : Omake_ir.t = let filename = Omake_node.Node.fullname node in let ast = Omake_ast_lex.parse_ast filename in let () = if Lm_debug.debug print_ast then Format.eprintf "@[<v 3>AST (initial):@ %a@]@." Omake_ast_print.pp_print_prog ast in let ast = Omake_exp_lex.compile_prog ast in let () = if Lm_debug.debug print_ast then Format.eprintf "@[<v 3>AST %a:@ %a@]@." Omake_node.pp_print_node node Omake_ast_print.pp_print_prog ast in let vars = Omake_env.venv_include_scope venv scope in let _senv, ir = Omake_ir_ast.compile_prog (Omake_ir_ast.penv_of_vars (open_ir venv) venv node vars) ast in postprocess_ir venv ir (* * When constructing a path, the relative filenames * should be auto-rehash. * * values : the path * dirname : the subdirectory to search (often ".") *) and path_of_values_select venv pos (values : Omake_value_type.t list) dirname = let rec collect groups auto_rehash items (values : Omake_value_type.t list) = match values with | v :: values -> let rehash_flag, dir = match v with | ValDir dir -> false, dir | ValNode _ -> let dir = Omake_env.venv_intern_dir venv (string_of_value venv pos v) in false, dir | _ -> let s = string_of_value venv pos v in let rehash_flag = not (Lm_filename_util.is_absolute s) in let dir = Omake_env.venv_intern_dir venv s in rehash_flag, dir in let dir = Omake_node.Dir.chdir dir dirname in let groups, items = if rehash_flag <> auto_rehash && items <> [] then (auto_rehash, List.rev items) :: groups, [dir] else groups, dir :: items in collect groups rehash_flag items values | [] -> if items <> [] then (auto_rehash, List.rev items) :: groups else groups in List.rev (collect [] false [] values) and path_of_values_rehash venv pos values dirname = let dir_of_value (v : Omake_value_type.t) = let dir = match v with | ValDir dir -> dir | _ -> Omake_env.venv_intern_dir venv (string_of_value venv pos v) in Omake_node.Dir.chdir dir dirname in [true, List.map dir_of_value values] and path_of_values venv pos values dirname = let auto_rehash = try bool_of_value venv pos (Omake_env.venv_find_var_exn venv Omake_var.auto_rehash_var) with Not_found -> false in let f = if auto_rehash then path_of_values_rehash else path_of_values_select in f venv pos values dirname (* * Open the file. * Get the IR and return the vars. *) and find_include_file venv pos loc filename = let pos = string_pos "find_include_file" pos in let cache = Omake_env.venv_cache venv in if not (Filename.is_relative filename) || not (Filename.is_implicit filename) then let fullname = filename ^ Omake_state.omake_file_suffix in let node1 = Omake_env.venv_intern venv PhonyProhibited fullname in if Omake_cache.exists cache node1 then node1 else let node2 = Omake_env.venv_intern venv PhonyProhibited filename in if Omake_cache.exists cache node2 then node2 else let print_error buf = Format.fprintf buf "@[<hv 3>include file not found, neither file exists:@ %a@ %a@]" (**) Omake_node.pp_print_node node1 Omake_node.pp_print_node node2 in raise (Omake_value_type.OmakeException (loc_pos loc pos, LazyError print_error)) else let dirname = Filename.dirname filename in let basename = Filename.basename filename in let fullname = basename ^ Omake_state.omake_file_suffix in let path = Omake_env.venv_find_var venv pos loc Omake_var.omakepath_var in let full_path = values_of_value venv pos path in let path = path_of_values venv pos full_path dirname in let cache = Omake_env.venv_cache venv in let listing = Omake_cache.ls_path cache path in try match Omake_cache.listing_find cache listing fullname with DirEntry dir -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringDirError ("is a directory", dir))) | NodeEntry node -> node with Not_found -> try match Omake_cache.listing_find cache listing basename with DirEntry dir -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringDirError ("is a directory", dir))) | NodeEntry node -> node with Not_found -> let print_error buf = Format.fprintf buf "@[<hv 3>include file %s not found in OMAKEPATH@ (@[<hv3>OMAKEPATH[] =%a@])@]" (**) filename Omake_value_print.pp_print_value_list full_path in raise (Omake_value_type.OmakeException (loc_pos loc pos, LazyError print_error)) and open_ir venv filename pos loc = let pos = string_pos "open_ir" pos in let source = find_include_file venv pos loc filename in let ir : Omake_ir.t = compile_ir venv Omake_env.IncludePervasives pos loc source in if !print_ir then begin Format.eprintf "@[<v 3>Vars: %a" Omake_node.pp_print_node source; Lm_symbol.SymbolTable.iter (fun v info -> Format.eprintf "@ %a = %a" Lm_symbol.pp_print_symbol v Omake_ir_print.pp_print_var_info info) ir.ir_vars; Format.eprintf "@]@." end; source, ir.ir_vars (* * The include file contains the IR for the file. * Try to load the old entry. * If it fails, compile the file and save the new entry. *) and compile_add_ir_info venv scope pos _ source info = let _pos = string_pos "compile_add_ir_info" pos in try Omake_env.Static.get_ir info with Not_found -> let ir = parse_ir venv scope source in Omake_env.Static.add_ir info ir; ir and compile_ir_info venv scope pos loc source info = let _pos = string_pos "compile_ir_info" pos in try Omake_env.Static.find_ir info with Not_found -> Omake_env.Static.rewrite info (compile_add_ir_info venv scope pos loc source) and compile_ir venv scope pos loc source = let pos = string_pos "compile_ir" pos in (* * Try to get a cached copy. *) try Omake_env.venv_find_ir_file_exn venv source with Not_found -> let ir = (* * Open the database. *) try Omake_env.Static.read venv source (compile_ir_info venv scope pos loc source) with Not_found -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringNodeError ("can't open IR", source))) in Omake_env.venv_add_ir_file venv source ir; ir (* * The object file contains the evaluated file. *) and compile_add_object_info compile _ pos source info = let _pos = string_pos "compile_add_object_info_info" pos in try Omake_env.Static.get_object info with Not_found -> let obj = compile info source in Omake_env.Static.add_object info obj; obj (* * Try to load the old entry. * If it fails, compile the file and save the new entry. *) and compile_object_info compile venv pos source info = let _pos = string_pos "compile_object_info" pos in try Omake_env.Static.find_object info with Not_found -> Omake_env.Static.rewrite info (compile_add_object_info compile venv pos source) and compile_object compile venv pos loc source = let pos = string_pos "compile_ast" pos in (* * Try to get a cached copy. *) try Omake_env.venv_find_object_file_exn venv source with Not_found -> let obj = (* * Open the database. *) try Omake_env.Static.read venv source (compile_object_info compile venv pos source) with Not_found -> raise (Omake_value_type.OmakeException (loc_pos loc pos, StringNodeError ("can't open object", source))) in Omake_env.venv_add_object_file venv source obj; obj (************************************************************************ * Value operations. *) (* * Get the string representation of a value. * It not legal to convert an array to a string. *) and string_of_value venv pos (v : Omake_value_type.t) = let pos = string_pos "string_of_value" pos in let scratch_buf = Buffer.create 32 in let rec collect (v : Omake_value_type.t) = match eval_prim_value venv pos v with (* Values that expand to nothing *) | ValNone | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValArray [] | ValVar _ -> () | ValSequence vl -> List.iter collect vl | ValQuote vl -> string_of_quote_buf scratch_buf venv pos vl | ValQuoteString (c, vl) -> Buffer.add_char scratch_buf c; string_of_quote_buf scratch_buf venv pos vl; Buffer.add_char scratch_buf c | ValArray [v] -> collect v | ValArray vl -> let print_error buf = Format.fprintf buf "@[<v 3>Array value where string expected:"; Format.fprintf buf "@ Use the $(string ...) function if you really want to do this"; Format.fprintf buf "@ @[<v 3>The array has length %d:" (List.length vl); ignore (List.fold_left (fun index v -> Format.fprintf buf "@ @[<hv 3>[%d] =@ %a@]" index Omake_value_print.pp_print_value v; succ index) 0 vl); Format.fprintf buf "@]@]@." in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) | ValInt i -> Buffer.add_string scratch_buf (string_of_int i) | ValFloat x -> Buffer.add_string scratch_buf (string_of_float x) | ValData s | ValWhite s | ValString s -> Buffer.add_string scratch_buf s | ValDir dir2 -> Buffer.add_string scratch_buf (Omake_env.venv_dirname venv dir2) | ValNode node -> Buffer.add_string scratch_buf (Omake_env.venv_nodename venv node) | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Invalid_argument "string_of_value") in collect v; Buffer.contents scratch_buf (* * Collect the values in a quotation into a string. * Even array values are flattened without warning. *) and string_of_quote venv pos c vl = let pos = string_pos "string_of_quote" pos in let scratch_buf = Buffer.create 32 in buffer_add_quote scratch_buf c; string_of_quote_buf scratch_buf venv pos vl; buffer_add_quote scratch_buf c; Buffer.contents scratch_buf and string_of_quote_buf scratch_buf venv pos vl = let pos = string_pos "string_of_quote_buf" pos in let rec collect v = match (eval_value venv pos v : Omake_value_type.t) with (* Values that expand to nothing *) | ValNone | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValArray [] | ValVar _ -> () | ValSequence vl | ValQuote vl -> List.iter collect vl | ValQuoteString (c, vl) -> Buffer.add_char scratch_buf c; List.iter collect vl; Buffer.add_char scratch_buf c | ValArray [v] -> collect v | ValArray vl -> collect_array vl | ValInt i -> Buffer.add_string scratch_buf (string_of_int i) | ValFloat x -> Buffer.add_string scratch_buf (string_of_float x) | ValData s | ValWhite s | ValString s -> Buffer.add_string scratch_buf s | ValDir dir2 -> Buffer.add_string scratch_buf (Omake_env.venv_dirname venv dir2) | ValNode node -> Buffer.add_string scratch_buf (Omake_env.venv_nodename venv node) | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Invalid_argument "string_of_value") and collect_array vl = match vl with [v] -> collect v | v :: vl -> collect v; Buffer.add_char scratch_buf ' '; collect_array vl | [] -> () in List.iter collect vl (* * Get a list of values from the value. * Array elements are always special, and returned as an element. * We divide values into two classes: * The "catenable" values are the values that can be concatenated to * form a string. These include: string, node, dir, int, float. * * Nothing else can be concatenated with a string, and is always preserved * in the value list. *) and values_of_value venv pos v = let pos = string_pos "values_of_value" pos in (* * Convert a catenable value to a string *) let group tokens : Omake_value_type.t = ValSequence tokens in let wrap_string s : Omake_value_type.t = ValString s in let wrap_data s : Omake_value_type.t = ValData s in let wrap_token s : Omake_value_type.t = ValData s in let lexer _ _ _ = None in let tokens = Lm_string_util.tokens_create_lexer ~lexer ~wrap_string ~wrap_data ~wrap_token ~group in (* * Array elements are always separate values. * The arrays are flattened. *) let rec collect_array tokens (vl : Omake_value_type.t list) vll = match vl, vll with | v :: vl, _ -> begin match eval_value venv pos v with ValArray el -> collect_array tokens el (vl :: vll) | ValSequence [v] -> collect_array tokens (v :: vl) vll | v -> collect_array (Lm_string_util.tokens_atomic tokens v) vl vll end | [], vl :: vll -> collect_array tokens vl vll | [], [] -> tokens in (* * Collect_string is used when we have seen whitespace * in a sequence. Collect the values into the string buffer, * then parse the string into separate tokens. *) let rec collect tokens vl vll = match vl, vll with | v :: vl, _ -> let v : Omake_value_type.t = eval_catenable_value venv pos v in begin match v with | ValNone -> collect tokens vl vll (* Strings *) | ValWhite s | ValString s -> collect (Lm_string_util.tokens_string tokens s) vl vll | ValSequence el -> collect tokens el (vl :: vll) (* Other catenable values *) | ValData _ | ValInt _ | ValFloat _ | ValDir _ | ValNode _ | ValQuote _ | ValQuoteString _ -> collect (Lm_string_util.tokens_add tokens v) vl vll (* Atomic values *) | ValArray el -> collect (collect_array (Lm_string_util.tokens_break tokens) el []) vl vll | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValVar _ -> collect (Lm_string_util.tokens_atomic tokens v) vl vll | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Omake_value_type.OmakeException (pos, StringValueError ("illegal application", v))) end | [], vl :: vll -> collect tokens vl vll | [], [] -> Lm_string_util.tokens_flush tokens in collect tokens [v] [] (* * Get a string list from the value. * This is always legal because arrays have been flattened. *) and strings_of_value venv pos v = let values = values_of_value venv pos v in List.map (string_of_value venv pos) values (* * Get a list of tokens from the value. * This is a lot like the previous function, but we use a lexer * for parsing special character sequences. *) and tokens_of_value venv pos lexer v = let pos = string_pos "tokens_of_value" pos in (* * Convert a catenable value to a string *) let group tokens = Omake_env.TokGroup tokens in let wrap_string s = Omake_env.TokString (ValString s) in let wrap_data s = Omake_env.TokString (ValData s) in let wrap_token s = Omake_env.TokToken s in let tokens = Lm_string_util.tokens_create_lexer ~lexer ~wrap_string ~wrap_data ~wrap_token ~group in (* * Array elements are always separate values. * The arrays are flattened. *) let rec collect_array (tokens : Omake_env.tok Lm_string_util.tokens) vl vll = match vl, vll with v :: vl, _ -> (match eval_value venv pos v with ValArray el -> collect_array tokens el (vl :: vll) | ValSequence [v] -> collect_array tokens (v :: vl) vll | v -> collect_array (Lm_string_util.tokens_atomic tokens (TokString v)) vl vll) | [], vl :: vll -> collect_array tokens vl vll | [], [] -> tokens in (* * Collect_string is used when we have seen whitespace * in a sequence. Collect the values into the string buffer, * then parse the string into separate tokens. *) let rec collect (tokens : Omake_env.tok Lm_string_util.tokens) vl vll = match vl, vll with v :: vl, _ -> let v = eval_catenable_value venv pos v in (match v with ValNone -> collect tokens vl vll (* Strings *) | ValWhite s | ValString s -> collect (Lm_string_util.tokens_lex tokens s) vl vll | ValSequence el -> collect tokens el (vl :: vll) (* Other catenable values *) | ValData _ | ValInt _ | ValFloat _ | ValDir _ | ValNode _ | ValQuote _ -> collect (Lm_string_util.tokens_add tokens (TokString v)) vl vll | ValQuoteString (_, v) -> collect (Lm_string_util.tokens_add tokens (TokString (ValQuote v))) vl vll (* Atomic values *) | ValArray el -> collect (collect_array (Lm_string_util.tokens_break tokens) el []) vl vll | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValVar _ -> collect (Lm_string_util.tokens_atomic tokens (TokString v)) vl vll | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Omake_value_type.OmakeException (pos, StringValueError ("illegal application", v)))) | [], vl :: vll -> collect tokens vl vll | [], [] -> Lm_string_util.tokens_flush tokens in collect tokens [v] [] (* * Flatten the value list into a arg_string list. * Basically just concatenate all the values, being * careful to preserve quoting. In addition, we want to * concatenate adjacent strings of the same type. *) and arg_of_values venv pos vl = let pos = string_pos "arg_of_values" pos in (* * Flatten all sequences. *) let rec collect is_quoted tokens vl vll = match vl, vll with v :: vl, _ -> let v = eval_value venv pos v in (match v with ValNone -> collect is_quoted tokens vl vll (* Strings *) | ValWhite s | ValString s -> let tokens = if is_quoted then Omake_command.arg_buffer_add_data tokens s else Omake_command.arg_buffer_add_string tokens s in collect is_quoted tokens vl vll | ValData s -> collect is_quoted (Omake_command.arg_buffer_add_data tokens s) vl vll | ValSequence el -> collect is_quoted tokens el (vl :: vll) | ValArray el -> collect true tokens el (vl :: vll) (* Other quoted values *) | ValInt _ | ValFloat _ | ValDir _ | ValNode _ | ValQuote _ | ValQuoteString _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValOther _ | ValVar _ -> let tokens = Omake_command.arg_buffer_add_data tokens (string_of_value venv pos v) in collect is_quoted tokens vl vll (* Illegal values *) | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Omake_value_type.OmakeException (pos, StringValueError ("illegal application", v)))) | [], vl :: vll -> collect is_quoted tokens vl vll | [], [] -> Omake_command.arg_buffer_contents tokens in collect false Omake_command.arg_buffer_empty vl [] and argv_of_values venv pos vll = List.map (arg_of_values venv pos) vll (* * Boolean test. * Arrays are always true. *) and bool_of_value venv pos v = let values = values_of_value venv pos v in match values with [] | [ValNone] | [ValWhite _] -> false | [ValInt i] -> i <> 0 | [ValFloat x] -> x <> 0.0 | [ValData s] | [ValString s] -> bool_of_string s | [ValQuote vl] -> bool_of_string (string_of_quote venv pos None vl) | _ -> true (* * The value should be a directory. *) and file_of_value venv pos file = let pos = string_pos "file_of_value" pos in let file = eval_prim_value venv pos file in match file with ValNode node -> node | ValDir dir -> Omake_node.Node.node_of_dir dir | ValData _ | ValString _ | ValSequence _ | ValQuote _ | ValQuoteString _ | ValInt _ | ValFloat _ -> Omake_env.venv_intern venv PhonyExplicit (string_of_value venv pos file) | ValArray _ | ValNone | ValWhite _ | ValMaybeApply _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValStringExp _ | ValBody _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValVar _ | ValDelayed _ | ValOther _ -> raise (Omake_value_type.OmakeException (pos, StringError "illegal value")) (* * Be lazy about concatenating arrays, to * avoid quadratic behavior. *) and append_arrays venv pos a1 a2 : Omake_value_type.t = if is_array_value a1 then if is_array_value a2 then ValArray [a1; a2] else let al = values_of_value venv pos a2 in ValArray (a1 :: al) else if is_array_value a2 then let al = values_of_value venv pos a1 in ValArray [ValArray al; a2] else if is_empty_value a1 then a2 else if is_empty_value a2 then a1 else ValSequence [a1; ValWhite " "; a2] (************************************************************************ * Evaluation. *) (* * Eval a static value. *) and eval_value_static venv pos key v = let pos = string_pos "eval_value_static" pos in let obj = match Omake_env.venv_find_static_info venv pos key with StaticValue obj -> obj | StaticRule { srule_env = venv; srule_deps = deps; srule_vals = values; srule_exp = e; srule_static ; _ } -> let values = List.flatten (List.map (values_of_value venv pos) values) in let values = List.map (eval_prim_value venv pos) values in let digest = Omake_command_digest.digest_of_exp pos values e in let cache = Omake_env.venv_cache venv in let obj = (* Try to fetch the value from the memo *) try Omake_cache.find_value cache key srule_static deps digest with Not_found -> (* Finally, if we don't have a value, evaluate the rule. * Prevent recursive calls *) let () = Omake_env.venv_set_static_info venv key (StaticValue Omake_value_util.empty_obj) in let venv, v = eval_exp venv Omake_value_type.ValNone e in let obj = eval_object venv pos v in Omake_cache.add_value cache key srule_static deps digest (MemoSuccess obj); obj in Omake_env.venv_set_static_info venv key (StaticValue obj); obj in Omake_env.venv_find_field_internal obj pos v and eval_value_delayed venv pos (p : Omake_value_type.value_delayed ref) = match !p with | ValValue v -> eval_value_core venv pos v | ValStaticApply (key, v) -> let v = eval_value_static venv pos key v in p := ValValue v; eval_value_core venv pos v (* * Unfold the outermost application to get a real value. *) and eval_value_core venv pos v : Omake_value_type.t = match v with | ValMaybeApply (loc, v) -> let v = try Some (Omake_env.venv_find_var_exn venv v) with Not_found -> None in begin match v with | Some v -> ValArray [eval_value_core venv pos (eval_var venv pos loc v)] | None -> ValNone end | ValDelayed p -> eval_value_delayed venv pos p | ValSequence [v] -> eval_value_core venv pos v | ValStringExp (env, e) -> let v = eval_string_exp (Omake_env.venv_with_env venv env) pos e in eval_value_core venv pos v | _ -> v and eval_value venv pos v = let pos = string_pos "eval_value" pos in eval_value_core venv pos v and eval_single_value venv pos v = let pos = string_pos "eval_single_value" pos in match eval_value venv pos v with ValArray [v] -> eval_single_value venv pos v | _ -> v and eval_prim_value venv pos v : Omake_value_type.t = let pos = string_pos "eval_prim_value" pos in let v = eval_value venv pos v in match v with ValArray [v] -> eval_prim_value venv pos v | ValObject obj -> (try Omake_env.venv_find_field_internal_exn obj Omake_symbol.builtin_sym with Not_found -> v) | _ -> v (* * The values are being flattened, so expand all sequences. *) and eval_catenable_value venv pos v = let pos = string_pos "eval_catenable_value" pos in let v = eval_value venv pos v in match v with ValObject obj -> (try match Omake_env.venv_find_field_internal_exn obj Omake_symbol.builtin_sym with ValNone | ValWhite _ | ValString _ | ValSequence _ | ValData _ | ValInt _ | ValFloat _ | ValDir _ | ValNode _ | ValArray _ | ValRules _ as v -> v | _ -> v with Not_found -> v) | _ -> v (* * Evaluate the value in a function body. * Expand all applications. *) and eval_body_value venv pos v : Omake_value_type.t = match (eval_value venv pos v : Omake_value_type.t) with | ValSequence sl -> ValSequence (List.map (eval_body_value venv pos) sl) | ValArray sl -> ValArray (List.map (eval_body_value venv pos) sl) | ValBody (_, [], [], body, _) -> snd (eval_sequence_exp venv pos body) | ValNone | ValInt _ | ValFloat _ | ValData _ | ValWhite _ | ValString _ | ValQuote _ | ValQuoteString _ | ValDir _ | ValNode _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValVar _ | ValOther _ as result -> result | ValBody _ (* it is an error when keyword/params <> [] *) | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Invalid_argument "eval_body_value") and eval_body_exp venv pos x v : (Omake_env.t * Omake_value_type.t) = match (eval_value venv pos v : Omake_value_type.t) with | ValSequence sl -> venv, ValSequence (List.map (eval_body_value venv pos) sl) | ValArray sl -> venv, ValArray (List.map (eval_body_value venv pos) sl) | ValBody (_, [], [], body, export) -> eval_sequence_export venv pos x body export | ValNone | ValInt _ | ValFloat _ | ValData _ | ValQuote _ | ValQuoteString _ | ValWhite _ | ValString _ | ValDir _ | ValNode _ | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ | ValRules _ | ValMap _ | ValObject _ | ValChannel _ | ValClass _ | ValCases _ | ValVar _ | ValOther _ as result -> venv, result | ValBody _ (* it is an error when keyword/params <> [] *) | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Invalid_argument "eval_body_exp") (* * Evaluate a variable. * It is fine for the variable to evaluate to a function. * But if the function has arity 0, then evaluate it. *) and eval_var venv pos loc v = match v with | ValFun (env, _, [], body, _) -> let venv = Omake_env.venv_with_env venv env in let _, result = eval_sequence venv pos Omake_value_type.ValNone body in result | ValFunCurry (env, args, _, [], body, _, []) -> let venv = Omake_env.venv_with_partial_args venv env args in let _, result = eval_sequence venv pos ValNone body in result | ValFunCurry (env, args, _, [], body, export, kargs) -> (* XXX: verify that we should pass forward the exports *) let venv_new = Omake_env.venv_with_partial_args venv env args in let venv_new, v = eval_sequence venv_new pos ValNone body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply venv pos loc v [] kargs | ValPrim (_, _, ApplyEmpty, f) -> snd (Omake_env.venv_apply_prim_fun f venv pos loc [] []) | _ -> v (* * Evaluate a key. *) and eval_key venv pos loc v = try let map = eval_map venv pos (Omake_env.venv_find_var_exn venv Omake_var.map_field_var) in Omake_env.venv_map_find map pos (ValData v) with Not_found -> raise (Omake_value_type.OmakeException (loc_pos loc pos, UnboundKey v)) (* * Evaluate an application. *) and eval_apply venv pos loc v args kargs = let pos = string_pos "eval_apply" pos in match eval_value venv pos v with ValFun (env, keywords, params, body, _) -> let venv = Omake_env.venv_add_args venv pos loc env params args keywords kargs in let _, result = eval_sequence_exp venv pos body in result | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> let venv_new, args, kargs = Omake_env.venv_add_curry_args venv pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply venv pos loc v args kargs | ValPrim (_, _, _, f) -> snd (Omake_env.venv_apply_prim_fun f venv pos loc args kargs) | ValPrimCurry (_, _, f, args1, kargs1) -> snd (Omake_env.venv_apply_prim_fun f venv pos loc (List.rev_append args1 args) (List.rev_append kargs1 kargs)) | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_apply venv pos loc v args kargs | v -> if args = [] && kargs = [] then v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_value_print.pp_print_value arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_value_print.pp_print_value arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) (* * Evaluate an application with string arguments. *) and eval_apply_string_exp venv venv_obj pos loc v args kargs = let pos = string_pos "eval_apply_string_exp" pos in match eval_value venv pos v with ValFun (env, keywords, params, body, _) -> let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new = Omake_env.venv_add_args venv_obj pos loc env params args keywords kargs in let _, result = eval_sequence_exp venv_new pos body in result | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new, args, kargs = Omake_env.venv_add_curry_args venv_obj pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply venv pos loc v args kargs | ValPrim (_, be_eager, _, f) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp true venv pos s) kargs in snd (Omake_env.venv_apply_prim_fun f venv_obj pos loc args kargs) | ValPrimCurry (_, be_eager, f, args1, kargs1) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp true venv pos s) kargs in snd (Omake_env.venv_apply_prim_fun f venv_obj pos loc (List.rev_append args1 args) (List.rev_append kargs1 kargs)) | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_apply_string_exp venv venv_obj pos loc v args kargs | v -> if args = [] && kargs = [] then v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_ir_print.pp_print_string_exp arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_ir_print.pp_print_string_exp arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) (* * Get a function from a value. *) and eval_fun ?(caller_env=false) venv pos v = match eval_value venv pos v with ValFun (env, keywords, params, body, export) -> let f venv pos loc args kargs = let venv_new = Omake_env.venv_add_args venv pos loc env params args keywords kargs in let venv_new, result = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result in true, f | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> let f venv pos loc args kargs = let venv_new, args, kargs = Omake_env.venv_add_curry_args venv pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply_export venv pos loc v args kargs in true, f | ValPrim (_, be_eager, _, f) -> be_eager, Omake_env.venv_apply_prim_fun f | ValPrimCurry (_, be_eager, f, args1, kargs1) -> let f venv pos loc args2 kargs2 = Omake_env.venv_apply_prim_fun f venv pos loc (List.rev_append args1 args2) (List.rev_append kargs1 kargs2) in be_eager, f | ValBody (defenv, keywords, params, body, export) -> let f venv pos loc args kargs = let env = (* diff to ValFun! *) if caller_env then Omake_env.venv_get_env venv else defenv in let venv_new = Omake_env.venv_add_args venv pos loc env params args keywords kargs in let venv_new, result = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result in true, f | _ -> raise (Omake_value_type.OmakeException (pos, StringError "not a function")) and definition_env_of_fun venv pos v = match eval_value venv pos v with | ValFun (env, _, _, _, _) -> env | ValFunCurry (env, _, _, _, _, _, _) -> env | ValBody (env, _, _, _, _) -> env | ValPrim _ | ValPrimCurry _ -> Omake_env.venv_get_env venv | _ -> raise (Omake_value_type.OmakeException (pos, StringError "not a function")) (* * Get an object from a variable. *) and eval_map venv pos x = match eval_value venv pos x with ValMap map -> map | _ -> raise (Omake_value_type.OmakeException (pos, StringError "not a map")) and eval_object venv pos x = try eval_object_exn venv pos x with Not_found -> raise (Omake_value_type.OmakeException (pos, StringError "not an object")) and eval_object_exn venv pos x = let x = eval_value venv pos x in match x with ValObject env -> env | ValInt _ | ValOther (ValExitCode _) -> create_object venv x Omake_var.int_object_var | ValFloat _ -> create_object venv x Omake_var.float_object_var | ValData _ | ValQuote _ | ValQuoteString _ -> create_object venv x Omake_var.string_object_var | ValSequence _ | ValWhite _ | ValString _ | ValNone -> create_object venv x Omake_var.sequence_object_var | ValArray _ -> create_object venv x Omake_var.array_object_var | ValFun _ | ValFunCurry _ | ValPrim _ | ValPrimCurry _ -> create_object venv x Omake_var.fun_object_var | ValRules _ -> create_object venv x Omake_var.rule_object_var | ValNode _ -> create_object venv x Omake_var.file_object_var | ValDir _ -> create_object venv x Omake_var.dir_object_var | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let x = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_object_exn venv pos x | ValBody _ -> create_object venv x Omake_var.body_object_var | ValChannel (InChannel, _) -> create_object venv x Omake_var.in_channel_object_var | ValChannel (OutChannel, _) -> create_object venv x Omake_var.out_channel_object_var | ValChannel (InOutChannel, _) -> create_object venv x Omake_var.in_out_channel_object_var | ValOther (ValLexer _) -> create_object venv x Omake_var.lexer_object_var | ValOther (ValParser _) -> create_object venv x Omake_var.parser_object_var | ValOther (ValLocation _) -> create_object venv x Omake_var.location_object_var | ValOther (ValEnv _) -> raise (Omake_value_type.OmakeException (pos, StringError "dereferenced <env>")) | ValClass _ -> raise (Invalid_argument "internal error: dereferenced $class") | ValCases _ -> raise (Invalid_argument "internal error: dereferenced cases") | ValMap _ -> create_map venv x Omake_var.map_object_var | ValVar _ -> create_object venv x Omake_var.var_object_var | ValStringExp _ | ValMaybeApply _ | ValDelayed _ -> raise (Invalid_argument "find_object") and create_object venv x v = let obj = Omake_env.venv_find_var_exn venv v in match obj with ValObject env -> Omake_env.venv_add_field_internal env Omake_symbol.builtin_sym x | _ -> raise Not_found and create_map venv x v = let obj = Omake_env.venv_find_var_exn venv v in match obj with ValObject env -> Omake_env.venv_add_field_internal env Omake_symbol.map_sym x | _ -> raise Not_found (* * Field operations. *) and eval_find_field_exn venv path obj pos vl = match vl with [v] -> path, obj, v | v :: vl -> let path, v = Omake_env.venv_find_field_path_exn venv path obj pos v in let obj = eval_object_exn venv pos v in eval_find_field_exn venv path obj pos vl | [] -> raise (Omake_value_type.OmakeException (pos, StringError "empty method name")) and eval_find_field_aux venv envl pos v vl = match envl with | [env] -> let env = eval_object_exn venv pos env in let path : Omake_value_type.path = PathVar v in eval_find_field_exn venv path env pos vl | env :: envl -> let env = eval_object_exn venv pos env in (try eval_find_field_exn venv (PathVar v) env pos vl with Not_found -> eval_find_field_aux venv envl pos v vl) | [] -> raise Not_found and eval_find_field venv pos _ v vl = let envl = Omake_env.venv_current_objects venv pos v in try eval_find_field_aux venv envl pos v vl with Not_found -> let pos = string_pos "eval_find_field" pos in raise (Omake_value_type.OmakeException (pos, UnboundMethod vl)) (* * Method paths. *) and eval_with_method_exn venv path obj pos vl = match vl with [v] -> let v = Omake_env.venv_find_field_exn venv obj pos v in let venv = Omake_env.venv_with_object venv obj in venv, path, v | v :: vl -> let path, v = Omake_env.venv_find_field_path_exn venv path obj pos v in let obj = eval_object_exn venv pos v in eval_with_method_exn venv path obj pos vl | [] -> raise (Omake_value_type.OmakeException (pos, StringError "empty method name")) and eval_with_method_aux venv envl pos v vl = match envl with | [env] -> let env = eval_object_exn venv pos env in eval_with_method_exn venv (PathVar v) env pos vl | env :: envl -> let env = eval_object_exn venv pos env in (try eval_with_method_exn venv (PathVar v) env pos vl with Not_found -> eval_with_method_aux venv envl pos v vl) | [] -> raise Not_found and eval_with_method venv pos loc v vl = let envl = Omake_env.venv_current_objects venv pos v in try eval_with_method_aux venv envl pos v vl with Not_found -> let pos = string_pos "eval_with_method" (loc_pos loc pos) in raise (Omake_value_type.OmakeException (pos, UnboundMethod vl)) (* * Method paths. *) and eval_find_method_exn venv obj pos vl = match vl with [v] -> let v = Omake_env.venv_find_field_exn venv obj pos v in let venv = Omake_env.venv_with_object venv obj in venv, v | v :: vl -> let v = Omake_env.venv_find_field_exn venv obj pos v in let obj = eval_object_exn venv pos v in eval_find_method_exn venv obj pos vl | [] -> raise (Omake_value_type.OmakeException (pos, StringError "empty method name")) and eval_find_method_aux venv envl pos vl = match envl with [env] -> let env = eval_object_exn venv pos env in eval_find_method_exn venv env pos vl | env :: envl -> let env = eval_object_exn venv pos env in (try eval_find_method_exn venv env pos vl with Not_found -> eval_find_method_aux venv envl pos vl) | [] -> raise Not_found and eval_find_method venv pos loc v vl = let envl = Omake_env.venv_current_objects venv pos v in try eval_find_method_aux venv envl pos vl with Not_found -> let pos = string_pos "eval_find_method" (loc_pos loc pos) in raise (Omake_value_type.OmakeException (pos, UnboundMethod vl)) (* * Check whether a field is defined. *) and eval_defined_field_exn venv env pos vl = match vl with [v] -> Omake_env.venv_defined_field venv env v | v :: vl -> let v = Omake_env.venv_find_field_exn venv env pos v in let obj = eval_object_exn venv pos v in eval_defined_field_exn venv obj pos vl | [] -> raise (Omake_value_type.OmakeException (pos, StringError "empty method name")) and eval_defined_field_aux venv envl pos vl = match envl with [env] -> let env = eval_object_exn venv pos env in eval_defined_field_exn venv env pos vl | env :: envl -> let env = eval_object_exn venv pos env in (try eval_defined_field_exn venv env pos vl with Not_found -> eval_defined_field_aux venv envl pos vl) | [] -> raise Not_found and eval_defined_field venv pos _ v vl = let envl = Omake_env.venv_current_objects venv pos v in try eval_defined_field_aux venv envl pos vl with Not_found -> false (* * Simplify a quoted value if possible. * Strings are concatenated. *) and simplify_quote_val venv pos c (el : Omake_value_type.t list) : Omake_value_type.t = let buf = Buffer.create 32 in let flush vl : Omake_value_type.t list = if Buffer.length buf = 0 then vl else let s = Buffer.contents buf in Buffer.clear buf; ValData s :: vl in let rec collect vl el = match el with | e :: el -> ( match eval_value venv pos e with | ValWhite s | ValString s | ValData s -> Buffer.add_string buf s; collect vl el | v -> collect (v :: flush vl) el ) | [] -> List.rev (flush vl) in let el = collect [] el in match c with | None -> (* GS: ValQuote just concatenates the inner elements without caring about sequences. Think about renaming to ValConcat. *) ( match el with | [ValData _ as e] -> e | _ -> ValQuote el ) | Some c -> ValQuoteString (c, el) (* * Evaluate a string expression. *) and eval_string_exp venv pos s = let pos = string_pos "eval_string_exp" pos in match s with NoneString _ -> ValNone | IntString (_, i) -> ValInt i | FloatString (_, x) -> ValFloat x | WhiteString (_, s) -> ValWhite s | ConstString (_, s) -> ValString s | KeyApplyString (loc, v) -> eval_key venv pos loc v | FunString (_, opt_params, params, body, export) -> let opt_params = eval_keyword_param_value_list_exp venv pos opt_params in let env = Omake_env.venv_get_env venv in (* We use now ValBody with parameters instead of ValFun for translating "=>..." blocks. ValFun has the disadvantage that it resets the static (private) variables every time the function is invoked. This doesn't play nice with foreach and potentially other imperative loop constructs. *) ValBody (env, opt_params, params, body, export) | ApplyString (loc, v, [], []) -> eval_var venv pos loc (Omake_env.venv_find_var venv pos loc v) | ApplyString (loc, v, args, kargs) -> eval_apply_string_exp venv venv pos loc (Omake_env.venv_find_var venv pos loc v) args kargs | SuperApplyString (loc, super, v, args, kargs) -> let v = Omake_env.venv_find_super_field venv pos loc super v in eval_apply_string_exp venv venv pos loc v args kargs | MethodApplyString (loc, v, vl, args, kargs) -> let venv_obj, v = eval_find_method venv pos loc v vl in eval_apply_string_exp venv venv_obj pos loc v args kargs | SequenceString (_, sl) -> ValSequence (List.map (eval_string_exp venv pos) sl) | ObjectString (_, e, export) | BodyString (_, e, export) -> let env = Omake_env.venv_get_env venv in ValBody (env, [], [], e, export) | ArrayString (_, el) -> ValArray (List.map (eval_string_exp venv pos) el) | ArrayOfString (_, e) -> let v = eval_string_exp venv pos e in ValArray (values_of_value venv pos v) | ExpString (_, e, _) -> let _, result = eval_sequence_exp venv pos e in result | CasesString (_, cases) -> let cases = List.map (fun (v, e1, e2, export) -> v, eval_string_exp venv pos e1, e2, export) cases in ValCases cases | QuoteString (_, el) -> simplify_quote_val venv pos None (List.map (eval_string_exp venv pos) el) | QuoteStringString (_, c, el) -> simplify_quote_val venv pos (Some c) (List.map (eval_string_exp venv pos) el) | VarString (loc, v) -> ValVar (loc, v) | ThisString _ -> ValObject (Omake_env.venv_this venv) | LazyString (_, s) -> ValStringExp (Omake_env.venv_get_env venv, s) | LetVarString (_, v, s1, s2) -> let x = eval_string_exp venv pos s1 in let venv = Omake_env.venv_add_var venv v x in eval_string_exp venv pos s2 (* and eval_keyword_string_exp venv pos (v, s) = *) (* v, eval_string_exp venv pos s *) and eval_keyword_param_value_list_exp venv pos opt_params = List.map (eval_keyword_param_value_exp venv pos) opt_params and eval_keyword_param_value_exp venv pos = function v, v_info, Some s -> v, v_info, Some (eval_string_exp venv pos s) | _, _, None as param -> param and eval_prim_arg_exp be_eager venv pos s = if be_eager then eval_string_exp venv pos s else ValStringExp (Omake_env.venv_get_env venv, s) (************************************************************************ * Export versions. * * These functions with the _export suffix also allow modifications * to the environment. *) and eval_var_export venv pos loc (v : Omake_value_type.t) = let pos = string_pos "eval_var_export" pos in (* Do not use eval_value; we don't want to force evaluation *) match v with | ValFun (env, _, [], body, export) -> let venv_new = Omake_env.venv_with_env venv env in let venv_new, result = eval_sequence venv_new pos ValNone body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result | ValFunCurry (env, pargs, _, [], body, export, []) -> let venv_new = Omake_env.venv_with_partial_args venv env pargs in let venv_new, result = eval_sequence venv_new pos ValNone body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result | ValFunCurry (env, pargs, _, [], body, export, kargs) -> let venv_new = Omake_env.venv_with_partial_args venv env pargs in let venv_new, v = eval_sequence venv_new pos ValNone body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply_export venv pos loc v [] kargs | ValPrim (_, _, ApplyEmpty, f) -> Omake_env.venv_apply_prim_fun f venv pos loc [] [] | _ -> venv, v (* * Evaluate an application. *) and eval_apply_export venv pos loc v args kargs = let pos = string_pos "eval_apply_export" pos in match (eval_value venv pos v : Omake_value_type.t) with | ValFun (env, keywords, params, body, export) -> let venv_new = Omake_env.venv_add_args venv pos loc env params args keywords kargs in let venv_new, result = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> let venv_new, args, kargs = Omake_env.venv_add_curry_args venv pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply_export venv pos loc v args kargs | ValPrim (_, _, _, f) -> Omake_env.venv_apply_prim_fun f venv pos loc args kargs | ValPrimCurry (_, _, f, args1, kargs1) -> Omake_env.venv_apply_prim_fun f venv pos loc (List.rev_append args1 args) (List.rev_append kargs1 kargs) | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_apply_export venv pos loc v args kargs | v -> if args = [] && kargs = [] then venv, v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_value_print.pp_print_value arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_value_print.pp_print_value arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) and eval_partial_apply venv pos loc v args kargs : (Omake_env.t * Omake_value_type.t )= match eval_value venv pos v with | ValFun (env, keywords, params, body, export) -> begin match (Omake_env.venv_add_partial_args venv pos loc env [] params args keywords [] kargs ) with | PartialApply (env, pargs, keywords, params, kargs) -> venv, ValFunCurry (env, pargs, keywords, params, body, export, kargs) | FullApply (venv, args, kargs) -> let venv_new, v = eval_sequence_exp venv pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_partial_apply venv pos loc v args kargs end | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> (match Omake_env.venv_add_partial_args venv pos loc env pargs params args keywords kargs1 kargs with PartialApply (env, pargs, keywords, params, kargs) -> venv, ValFunCurry (env, pargs, keywords, params, body, export, kargs) | FullApply (venv, args, kargs) -> let venv_new, v = eval_sequence_exp venv pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_partial_apply venv pos loc v args kargs) | ValPrim (arity, eager, _, f) -> (match arity_apply_args arity [] args with FullArity (current_args, rest_args) -> (* We assume the primitive takes all the keyword args *) let venv, v = Omake_env.venv_apply_prim_fun f venv pos loc current_args kargs in eval_partial_apply venv pos loc v rest_args [] | PartialArity (arity, args) -> venv, ValPrimCurry (arity, eager, f, args, List.rev kargs)) | ValPrimCurry (arity, eager, f, args1, kargs1) -> (match arity_apply_args arity args1 args with FullArity (current_args, rest_args) -> (* We assume the primitive takes all the keyword args *) let venv, v = Omake_env.venv_apply_prim_fun f venv pos loc current_args kargs in eval_partial_apply venv pos loc v rest_args [] | PartialArity (arity, args) -> venv, ValPrimCurry (arity, eager, f, args, List.rev_append kargs kargs1)) | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_partial_apply venv pos loc v args kargs | v -> if args = [] && kargs = [] then venv, v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_value_print.pp_print_value arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_value_print.pp_print_value arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) and eval_apply_string_export_exp venv venv_new pos loc v args kargs = let pos = string_pos "eval_apply_string_export_exp" pos in match eval_value venv pos v with ValFun (env, keywords, params, body, export) -> let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new = Omake_env.venv_add_args venv_new pos loc env params args keywords kargs in let venv_new, result = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in venv, result | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new, args, kargs = Omake_env.venv_add_curry_args venv_new pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_exports venv venv_new pos export in eval_apply_export venv pos loc v args kargs | ValPrim (_, be_eager, _, f) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp be_eager venv pos s) kargs in Omake_env.venv_apply_prim_fun f venv_new pos loc args kargs | ValPrimCurry (_, be_eager, f, args1, kargs1) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp be_eager venv pos s) kargs in Omake_env.venv_apply_prim_fun f venv_new pos loc (List.rev_append args1 args) (List.rev_append kargs1 kargs) | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_apply_string_export_exp venv venv_new pos loc v args kargs | v -> if args = [] && kargs = [] then venv, v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_ir_print.pp_print_string_exp arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_ir_print.pp_print_string_exp arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) and eval_apply_method_export_exp venv venv_obj pos loc path v args kargs = let pos = string_pos "eval_apply_method_export_exp" pos in match eval_value venv pos v with ValFun (env, keywords, params, body, export) -> let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new = Omake_env.venv_add_args venv_obj pos loc env params args keywords kargs in let venv_new, result = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_path_exports venv venv_obj venv_new pos path export in venv, result | ValFunCurry (env, pargs, keywords, params, body, export, kargs1) -> (* XXX: JYH: this, need to think about *) let args = List.map (eval_string_exp venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_string_exp venv pos s) kargs in let venv_new, args, kargs = Omake_env.venv_add_curry_args venv_obj pos loc env pargs params args keywords kargs1 kargs in let venv_new, v = eval_sequence_exp venv_new pos body in let venv = Omake_env.add_path_exports venv venv_obj venv_new pos path export in eval_apply_export venv pos loc v args kargs | ValPrim (_, be_eager, _, f) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp be_eager venv pos s) kargs in let venv_new, result = Omake_env.venv_apply_prim_fun f venv_obj pos loc args kargs in let venv = Omake_env.hoist_this venv venv_new path in venv, result | ValPrimCurry (_, be_eager, f, args1, kargs1) -> let args = List.map (eval_prim_arg_exp be_eager venv pos) args in let kargs = List.map (fun (v, s) -> v, eval_prim_arg_exp be_eager venv pos s) kargs in let venv_new, result = Omake_env.venv_apply_prim_fun f venv_obj pos loc (List.rev_append args1 args) (List.rev_append kargs1 kargs) in let venv = Omake_env.hoist_this venv venv_new path in venv, result | ValBody (env, keywords, params, body, exports) when keywords <> [] || params <> [] -> let v = Omake_value_type.ValFun(env, keywords, params, body, exports) in eval_apply_method_export_exp venv venv_obj pos loc path v args kargs | v -> if args = [] && kargs = [] then venv, v else let print_error buf = Format.fprintf buf "@[<v 3>illegal function application:@ @[<hv 3>function:@ %a@]" Omake_value_print.pp_print_value v; List.iter (fun arg -> Format.fprintf buf "@ @[<hv 3>arg = %a@]" Omake_ir_print.pp_print_string_exp arg) args; List.iter (fun (v, arg) -> Format.fprintf buf "@ @[<hv 3>%a = %a@]" Lm_symbol.pp_print_symbol v Omake_ir_print.pp_print_string_exp arg) kargs; Format.fprintf buf "@]" in raise (Omake_value_type.OmakeException (pos, LazyError print_error)) (* * Evaluate a string expression, and allow exports. *) and eval_string_export_exp venv pos ( s : Omake_ir.string_exp) : (Omake_env.t * Omake_value_type.t)= let pos = string_pos "eval_string_export_exp" pos in match s with | NoneString _ -> venv, ValNone | IntString (_, i) -> venv, ValInt i | FloatString (_, x) -> venv, ValFloat x | WhiteString (_, s) -> venv, ValWhite s | ConstString (_, s) -> venv, ValString s | KeyApplyString (loc, v) -> venv, eval_key venv pos loc v | FunString (_, opt_params, params, body, export) -> let opt_params = eval_keyword_param_value_list_exp venv pos opt_params in let env = Omake_env.venv_get_env venv in venv, ValFun (env, opt_params, params, body, export) | ApplyString (loc, v, [], []) -> eval_var_export venv pos loc (Omake_env.venv_find_var venv pos loc v) | ApplyString (loc, v, args, kargs) -> eval_apply_string_export_exp venv venv pos loc (Omake_env.venv_find_var venv pos loc v) args kargs | SuperApplyString (loc, super, v, args, kargs) -> let v = Omake_env.venv_find_super_field venv pos loc super v in eval_apply_string_export_exp venv venv pos loc v args kargs | MethodApplyString (loc, v, vl, args, kargs) -> let venv_obj, path, v = eval_with_method venv pos loc v vl in eval_apply_method_export_exp venv venv_obj pos loc path v args kargs | SequenceString (_, sl) -> venv, ValSequence (List.map (eval_string_exp venv pos) sl) | ObjectString (_, e, export) | BodyString (_, e, export) -> let env = Omake_env.venv_get_env venv in venv, ValBody (env, [], [], e, export) | ArrayString (_, el) -> venv, ValArray (List.map (eval_string_exp venv pos) el) | ArrayOfString (_, e) -> let v = eval_string_exp venv pos e in venv, ValArray (values_of_value venv pos v) | ExpString (_, e, export) -> eval_sequence_export_exp venv pos e export | CasesString (_, cases) -> let cases = List.map (fun (v, e1, e2, export) -> v, eval_string_exp venv pos e1, e2, export) cases in venv, ValCases cases | QuoteString (_, el) -> venv, simplify_quote_val venv pos None (List.map (eval_string_exp venv pos) el) | QuoteStringString (_, c, el) -> venv, simplify_quote_val venv pos (Some c) (List.map (eval_string_exp venv pos) el) | VarString (loc, v) -> venv, ValVar (loc, v) | ThisString _ -> venv, ValObject (Omake_env.venv_this venv) | LazyString (_, s) -> venv, ValStringExp (Omake_env.venv_get_env venv, s) | LetVarString (_, v, s1, s2) -> let venv, x = eval_string_export_exp venv pos s1 in let venv = Omake_env.venv_add_var venv v x in eval_string_export_exp venv pos s2 (************************************************************************ * Evaluate an expression. *) and eval_exp venv _ e = let pos = string_pos "eval_exp" (ir_exp_pos e) in match e with LetVarExp (_, v, [], flag, s) -> eval_let_var_exp venv pos v flag s | LetVarExp (loc, v, vl, flag, s) -> eval_let_var_field_exp venv pos loc v vl flag s | LetKeyExp (_, v, flag, s) -> eval_let_key_exp venv pos v flag s | LetFunExp (loc, v, [], curry, opt_params, params, body, export) -> eval_let_fun_exp venv pos loc v curry opt_params params body export | LetFunExp (loc, v, vl, curry, opt_params, params, body, export) -> eval_let_fun_field_exp venv pos loc v vl curry opt_params params body export | LetObjectExp (_, v, [], s, e, export) -> eval_let_object_exp venv pos v s e export | LetObjectExp (loc, v, vl, s, e, export) -> eval_let_object_field_exp venv pos loc v vl s e export | LetThisExp (_, e) -> eval_let_this_exp venv pos e | ShellExp (loc, e) -> eval_shell_exp venv pos loc e | IfExp (_, cases) -> eval_if_exp venv pos cases | SequenceExp (_, e) -> eval_sequence_exp venv pos e | SectionExp (_, _, e, export) -> eval_section_exp venv pos e export | OpenExp (loc, s) -> eval_open_exp venv pos loc s | IncludeExp (loc, s, e) -> eval_include_exp venv pos loc s e | ApplyExp (loc, f, args, kargs) -> eval_apply_exp venv pos loc f args kargs | SuperApplyExp (loc, super, v, args, kargs) -> eval_super_apply_exp venv pos loc super v args kargs | MethodApplyExp (loc, v, vl, args, kargs) -> eval_method_apply_exp venv pos loc v vl args kargs | ReturnBodyExp (_, e, id) -> eval_return_body_exp venv pos e id | StringExp (_, s) -> eval_string_value_exp venv pos s | ReturnExp (loc, s, id) -> eval_return_exp venv pos loc s id | ReturnSaveExp _ -> eval_return_save_exp venv pos | ReturnObjectExp (_, names) -> eval_return_object_exp venv pos names | KeyExp (loc, v) -> eval_key_exp venv pos loc v | StaticExp (_, node, key, e) -> eval_static_exp venv pos node key e (* * Variable definitions. *) and eval_let_var_exp venv pos v flag s = let pos = string_pos "eval_var_exp" pos in let venv, s = eval_string_export_exp venv pos s in let s = match flag with VarDefNormal -> s | VarDefAppend -> append_arrays venv pos (Omake_env.venv_get_var venv pos v) s in let venv = Omake_env.venv_add_var venv v s in venv, s and eval_let_var_field_exp venv pos loc v vl flag s = let pos = string_pos "eval_var_field_exp" pos in let venv, e = eval_string_export_exp venv pos s in let path, obj, v = eval_find_field venv pos loc v vl in let e = match flag with VarDefNormal -> e | VarDefAppend -> append_arrays venv pos (Omake_env.venv_find_field venv obj pos v) e in let venv, obj = Omake_env.venv_add_field venv obj pos v e in let venv = Omake_env.hoist_path venv path obj in venv, e (* * Key (property) definitions. *) and eval_let_key_exp venv pos v flag s = let pos = string_pos "eval_let_key_exp" pos in let venv, s = eval_string_export_exp venv pos s in (* Get the current property list *) let map = try Omake_env.venv_find_var_exn venv Omake_var.map_field_var with Not_found -> raise (Omake_value_type.OmakeException (pos, StringError "current object is not a Map")) in let map = eval_map venv pos map in let v : Omake_value_type.t = ValData v in (* Add the new definition *) let s = match flag with | VarDefNormal -> s | VarDefAppend -> append_arrays venv pos (Omake_env.venv_map_find map pos v) s in let map = Omake_env.venv_map_add map pos v s in let venv = Omake_env.venv_add_var venv Omake_var.map_field_var (ValMap map) in venv, s (* * Function definitions. *) and eval_let_fun_exp venv pos _ v curry opt_params params body export = let opt_params = eval_keyword_param_value_list_exp venv pos opt_params in let env = Omake_env.venv_get_env venv in let e : Omake_value_type.t = if curry then ValFunCurry (env, [], opt_params, params, body, export, []) else ValFun (env, opt_params, params, body, export) in let venv = Omake_env.venv_add_var venv v e in venv, e and eval_let_fun_field_exp venv pos loc v vl curry opt_params params body export = let opt_params = eval_keyword_param_value_list_exp venv pos opt_params in let env = Omake_env.venv_get_env venv in let e : Omake_value_type.t = if curry then ValFunCurry (env, [], opt_params, params, body, export, []) else ValFun (env, opt_params, params, body, export) in let path, obj, v = eval_find_field venv pos loc v vl in let venv, obj = Omake_env.venv_add_field venv obj pos v e in let venv = Omake_env.hoist_path venv path obj in venv, e (* * Shell expression. *) and eval_shell_exp venv pos loc e = let pos = string_pos "eval_shell_exp" pos in let () = if !Omake_shell_type.debug_shell then Format.eprintf "@[<v 3>eval_shell_exp (pid = %i):@ %a@]@." (**) (Unix.getpid()) Omake_ir_print.pp_print_string_exp e in let v = Omake_env.venv_find_var venv pos loc Omake_var.system_var in let venv, s = eval_string_export_exp venv pos e in eval_apply_export venv pos loc v [s] [] (* * Conditionals. * The test should expand to a Boolean of some form. *) and eval_if_cases venv pos cases = match cases with (s, el, export) :: cases -> let s = eval_string_exp venv pos s in let b = bool_of_value venv pos s in if b then eval_sequence_export_exp venv pos el export else eval_if_cases venv pos cases | [] -> venv, ValNone and eval_if_exp venv pos cases = let pos = string_pos "eval_if_exp" pos in eval_if_cases venv pos cases (* * Sequence. *) and eval_sequence venv pos result el = match el with e :: el -> let venv, result = eval_exp venv result e in eval_sequence venv pos result el | [] -> venv, result and eval_sequence_export venv pos result el export = let venv_new, result = eval_sequence venv pos result el in let venv = Omake_env.add_exports venv venv_new pos export in venv, result and eval_sequence_exp venv pos el = let pos = string_pos "eval_sequence_exp" pos in eval_sequence venv pos ValNone el and eval_sequence_export_exp venv pos el export = let pos = string_pos "eval_sequence_export_exp" pos in eval_sequence_export venv pos ValNone el export and eval_section_exp venv pos el export = let pos = string_pos "eval_section_exp" pos in eval_sequence_export venv pos ValNone el export (* * Look for a cached object. If it does not exist, * then evaluate the body to create the object. * Inline all the fields. *) and eval_static_exp venv pos node key el = let pos = string_pos "eval_static_exp" pos in let obj = try Omake_env.venv_find_static_object venv node key with Not_found -> (* Evaluate the object, and save it *) let _, result = eval_sequence (Omake_env.venv_define_object venv) pos ValNone el in let obj = eval_object venv pos result in Omake_env.venv_add_static_object venv node key obj; obj in let venv = Omake_env.venv_include_static_object venv obj in venv, ValNone (* * Object. * The argument string is ignored. * Push a new object. *) and eval_let_object_exp venv pos v s el export = let pos = string_pos "eval_let_object_exp" pos in let parent = eval_string_exp venv pos s in let obj = eval_object venv pos parent in let venv_obj = Omake_env.venv_define_object venv in let venv_obj = Omake_env.venv_include_object venv_obj obj in let venv_obj, result = eval_sequence venv_obj pos ValNone el in let venv = Omake_env.venv_add_var venv v result in let venv = Omake_env.add_exports venv venv_obj pos export in venv, result and eval_let_object_field_exp venv pos loc v vl s el export = let pos = string_pos "eval_let_object_field_exp" pos in let parent = eval_string_exp venv pos s in let obj = eval_object venv pos parent in let venv_obj = Omake_env.venv_define_object venv in let venv_obj = Omake_env.venv_include_object venv_obj obj in let venv_obj, e = eval_sequence venv_obj pos ValNone el in let path, obj, v = eval_find_field venv pos loc v vl in let venv, obj = Omake_env.venv_add_field venv obj pos v e in let venv = Omake_env.hoist_path venv path obj in let venv = Omake_env.add_exports venv venv_obj pos export in venv, e (* * This. * Set the current object to the given object. *) and eval_let_this_exp venv pos s = let pos = string_pos "eval_this_exp" pos in let venv, obj = eval_string_export_exp venv pos s in let obj = eval_object venv pos obj in let venv = Omake_env.venv_with_object venv obj in venv, ValObject obj (* * Include a file. * The environment after the file is evaluated is used in the rest * of this file. *) and eval_include_exp venv pos loc s _ = let pos = string_pos "eval_include" pos in let name = match eval_string_exp venv pos s with ValNode node -> (* Use an absolute name, preventing path lookup *) Omake_node.Node.absname node | name -> string_of_value venv pos name in let node = find_include_file venv pos loc name in let venv = Omake_env.venv_add_file venv node in let venv = include_file venv Omake_env.IncludePervasives pos loc node in venv, ValNone (* * Open a file. * Include it if it is not already included. *) and eval_open_exp venv pos loc nodes = let pos = string_pos "eval_open" pos in let venv = List.fold_left (fun venv node -> if Omake_env.venv_is_included_file venv node then venv else let venv = Omake_env.venv_add_file venv node in include_file venv Omake_env.IncludePervasives pos loc node) venv nodes in venv, ValNone (* * Key lookup. *) and eval_key_exp venv pos loc v = let pos = string_pos "eval_key_exp" pos in let result = eval_key venv pos loc v in venv, result (* * Function application. *) and eval_apply_exp venv pos loc f args kargs = let pos = string_pos "eval_apply_exp" pos in eval_apply_string_export_exp venv venv pos loc (Omake_env.venv_find_var venv pos loc f) args kargs and eval_super_apply_exp venv pos loc super v args kargs = let pos = string_pos "eval_super_apply_exp" pos in let v = Omake_env.venv_find_super_field venv pos loc super v in eval_apply_string_export_exp venv venv pos loc v args kargs and eval_method_apply_exp venv pos loc v vl args kargs = let pos = string_pos "eval_method_apply_exp" pos in let venv_obj, path, v = eval_with_method venv pos loc v vl in eval_apply_method_export_exp venv venv_obj pos loc path v args kargs (* * Return a value. This is just the identity. *) and eval_return_body_exp venv pos e id = let _pos = string_pos "eval_return_body_exp" pos in try eval_sequence_exp venv pos e with Omake_value_type.Return (_, v, id') when id' == id -> venv, v and eval_return_exp venv pos loc s id = let pos = string_pos "eval_return_exp" pos in let result = eval_string_exp venv pos s in raise (Omake_value_type.Return (loc, result, id)) and eval_string_value_exp venv pos s = let pos = string_pos "eval_string_value_exp" pos in let result = eval_string_exp venv pos s in venv, result and eval_return_save_exp venv pos = let _pos = string_pos "eval_return_save_exp" pos in venv, ValNone and eval_return_object_exp venv _ names = let result = Omake_env.venv_current_object venv names in venv, ValObject result (* * Include a file. *) and eval_include_file venv scope pos loc node = let ir = compile_ir venv scope pos loc node in let venv_new = Omake_env.venv_add_var venv Omake_var.file_var (ValNode node) in let venv_new, result = eval_exp venv_new ValNone ir.ir_exp in let venv = Omake_env.add_exports venv venv_new pos ExportAll in venv, result and include_file venv scope pos loc target = let pos = string_pos "include_file" pos in let venv = Omake_env.venv_add_included_file venv target in let venv, _ = eval_include_file venv scope pos loc target in venv (* * Parse and evaluate a file as if it were an object. *) and eval_object_file venv pos loc node = let parse_obj info node = let ir = compile_add_ir_info venv IncludePervasives pos loc node info in match ir with { ir_classnames = names; ir_exp = e; _} -> let venv = Omake_env.venv_get_pervasives venv node in let venv = Omake_env.venv_define_object venv in let venv, _ = eval_exp venv ValNone e in Omake_env.venv_current_object venv names in compile_object parse_obj venv pos loc node (************************************************************************ * Evaluator. *) and eval venv e = let _, result = eval_exp venv ValNone e in result let eval_open_file = open_ir let eval_apply = eval_apply_export (************************************************************************ * Project compiler. *) let compile venv = let rootname = if Sys.file_exists Omake_state.makeroot_name then Omake_state.makeroot_name else Omake_state.makeroot_short_name in let node = Omake_env.venv_intern venv PhonyProhibited rootname in let venv = Omake_env.venv_add_file venv node in let loc = Lm_location.bogus_loc (Omake_node.Node.fullname node) in let pos = string_pos "compile" (loc_exp_pos loc) in let _ = eval_include_file venv IncludePervasives pos loc node in if Lm_debug.debug print_rules then Format.eprintf "@[<hv 3>Rules:%a@]@." Omake_env.pp_print_explicit_rules venv (************************************************************************ * Dependencies. *) let compile_deps venv node buf = let deps = Omake_ast_lex.parse_deps buf in let vars = Omake_env.venv_include_scope venv IncludePervasives in let senv_empty = Omake_ir_ast.penv_of_vars (open_ir venv) venv node vars in List.map (fun (target, source, loc) -> let pos = string_pos "compile_deps" (loc_exp_pos loc) in let _, target = Omake_ir_ast.build_string senv_empty target pos in let _, source = Omake_ir_ast.build_string senv_empty source pos in let target = eval_string_exp venv pos target in let source = eval_string_exp venv pos source in let targets = strings_of_value venv pos target in let sources = strings_of_value venv pos source in targets, sources) deps