static REBOOL Same_Func(const RELVAL *val, const RELVAL *arg) { assert(IS_FUNCTION(val) && IS_FUNCTION(arg)); if (VAL_FUNC_PARAMLIST(val) == VAL_FUNC_PARAMLIST(arg)) { assert(VAL_FUNC_DISPATCHER(val) == VAL_FUNC_DISPATCHER(arg)); assert(VAL_FUNC_BODY(val) == VAL_FUNC_BODY(arg)); // All functions that have the same paramlist are not necessarily the // "same function". For instance, every RETURN shares a common // paramlist, but the binding is different in the REBVAL instances // in order to know where to "exit from". return LOGICAL(VAL_BINDING(val) == VAL_BINDING(arg)); } return FALSE; }
// // Do_String() // // This is a version of a routine that was offered by the RL_Api, which has // been expanded here in order to permit the necessary customizations for // interesting REPL behavior w.r.t. binding, error handling, and response // to throws. // // !!! Now that this code has been moved into the host, the convoluted // integer-return-scheme can be eliminated and the code integrated more // clearly into the surrounding calls. // int Do_String( int *exit_status, REBVAL *out, const REBYTE *text, REBOOL at_breakpoint ) { struct Reb_State state; REBCTX *error; // Breakpoint REPLs are nested, and we may wish to jump out of them to // the topmost level via a HALT. However, all other errors need to be // confined, so that if one is doing evaluations during the pause of // a breakpoint an error doesn't "accidentally resume" by virtue of // jumping the stack out of the REPL. // // The topmost layer REPL, however, needs to catch halts in order to // keep control and not crash out. // if (at_breakpoint) PUSH_TRAP(&error, &state); else PUSH_UNHALTABLE_TRAP(&error, &state); // The first time through the following code 'error' will be NULL, but... // `fail` can longjmp here, so 'error' won't be NULL *if* that happens! if (error) { // Save error for WHY? REBVAL *last = Get_System(SYS_STATE, STATE_LAST_ERROR); if (ERR_NUM(error) == RE_HALT) { assert(!at_breakpoint); return -1; // !!! Revisit hardcoded # } Val_Init_Error(out, error); *last = *out; return -cast(REBINT, ERR_NUM(error)); } REBARR *code = Scan_UTF8_Managed(text, LEN_BYTES(text)); // Where code ends up being bound when loaded at the REPL prompt should // be more generally configurable. (It may be, for instance, that one // wants to run something with it not bound at all.) Such choices // must come from this REPL host...not from the interpreter itself. { // First the scanned code is bound into the user context with a // fallback to the lib context. // // !!! This code is very old, and is how the REPL has bound since // R3-Alpha. It comes from RL_Do_String, but should receive a modern // review of why it's written exactly this way. // REBCTX *user_ctx = VAL_CONTEXT(Get_System(SYS_CONTEXTS, CTX_USER)); REBVAL vali; SET_INTEGER(&vali, CTX_LEN(user_ctx) + 1); Bind_Values_All_Deep(ARR_HEAD(code), user_ctx); Resolve_Context(user_ctx, Lib_Context, &vali, FALSE, FALSE); // If we're stopped at a breakpoint, the REPL should have a concept // of what stack level it is inspecting (conveyed by the |#|>> in the // prompt). This does a binding pass using the function for that // stack level, just the way a body is bound during Make_Function() // if (at_breakpoint) { REBVAL level; SET_INTEGER(&level, HG_Stack_Level); REBFRM *frame = Frame_For_Stack_Level(NULL, &level, FALSE); assert(frame); // Need to manage because it may be no words get bound into it, // and we're not putting it into a FRAME! value, so it might leak // otherwise if it's reified. // REBCTX *frame_ctx = Context_For_Frame_May_Reify_Managed(frame); Bind_Values_Deep(ARR_HEAD(code), frame_ctx); } // !!! This was unused code that used to be in Do_String from // RL_Api. It was an alternative path under `flags` which said // "Bind into lib or user spaces?" and then "Top words will be // added to lib". Is it relevant in any way? // /* Bind_Values_Set_Midstream_Shallow(ARR_HEAD(code), Lib_Context); Bind_Values_Deep(ARR_HEAD(code), Lib_Context); */ } if (Do_At_Throws(out, code, 0, SPECIFIED)) { // `code` will be GC protected if (at_breakpoint) { if ( IS_FUNCTION(out) && VAL_FUNC_DISPATCHER(out) == &N_resume ) { // // This means we're done with the embedded REPL. We want to // resume and may be returning a piece of code that will be // run by the finishing BREAKPOINT command in the target // environment. // // We'll never return a halt, so we reuse -1 (in this very // temporary scheme built on the very clunky historical REPL, // which will not last much longer...fingers crossed.) // DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state); CATCH_THROWN(out, out); *exit_status = -1; return -1; } if ( IS_FUNCTION(out) && VAL_FUNC_DISPATCHER(out) == &N_quit ) { // // It would be frustrating if the system did not respond to // a QUIT and forced you to do `resume/with [quit]`. So // this is *not* caught, rather passed back up with the // special -2 status code. // DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state); CATCH_THROWN(out, out); *exit_status = -2; return -2; } } else { // We are at the top level REPL, where we catch QUIT and for // now, also EXIT as meaning you want to leave. // if ( IS_FUNCTION(out) && ( VAL_FUNC_DISPATCHER(out) == &N_quit || VAL_FUNC_DISPATCHER(out) == &N_exit ) ) { DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state); CATCH_THROWN(out, out); *exit_status = Exit_Status_From_Value(out); return -2; // Revisit hardcoded # } } fail (Error_No_Catch_For_Throw(out)); } DROP_TRAP_SAME_STACKLEVEL_AS_PUSH(&state); return 0; }