From 72454d3538f7aaff40c39182a0b171d916103554 Mon Sep 17 00:00:00 2001 From: DrMaxNix Date: Sun, 2 Oct 2022 15:46:04 +0200 Subject: [PATCH] :sparkles: code to enter and return from scope --- src/runner.js | 202 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 169 insertions(+), 33 deletions(-) diff --git a/src/runner.js b/src/runner.js index f5d96ab..4672413 100644 --- a/src/runner.js +++ b/src/runner.js @@ -16,12 +16,10 @@ class Juicescript_runner { */ run(){ // RESET // - // command counter - this.counter = 0; - - // scope and variable stack + // stack for command counter, scope and variable list this.stack = [{ scope: null, + counter: 0, variable: {} }]; @@ -70,8 +68,7 @@ class Juicescript_runner { // TRY AS USER-DEFINED COMMAND // if(Object.keys(this.tree.scope).includes(this.command.name)){ - //*/ TODO: enter scope - /**/this.error("user-defined: " + this.command.name); + this.scope_enter(); return; } @@ -95,9 +92,10 @@ class Juicescript_runner { // IN USER-DEFINED SCOPE // if(this.scope !== null){ // return from scope - //*/ TODO: return from scope - /**/this.error("return from scope"); - return false; + this.scope_return(); + + // check again for new scope + return this.handle_command_list_end(); } @@ -112,12 +110,122 @@ class Juicescript_runner { this.command = this.scope_tree.command[this.counter]; } + /* + HELPER: Handle entering another scope by user command + */ + scope_enter(){ + // VALIDATE ARGUMENTS // + // argument count + this.argument_validate_count(this.tree.scope[this.command.name].parameter_count); + + // they must all be of type 'value' + for(var q = 1; q <= this.command.argument.length; q++){ + this.argument_validate_type(q, Juicescript.argument_type.VALUE); + } + + + // RESOLVE ARGUMENTS // + // resolve to value + let argument_value_list = []; + for(var q = 1; q <= this.command.argument.length; q++){ + argument_value_list.push(this.argument_value(q)); + } + + // resolve to absolute variable + let argument_variable_list = []; + for(var q = 0; q < this.command.argument.length; q++){ + if(this.command.argument[q].type === Juicescript.argument_type.VARIABLE){ + // variable, add absolute variable + argument_variable_list.push(this.argument_variable(q + 1)); + + } else { + // literal, add null + argument_variable_list.push(null); + } + } + + + // PUSH NEW LAYER TO STACK // + this.stack.push({ + scope: this.command.name, + counter: 0, + variable: {}, + argument_variable: argument_variable_list + }); + + + // FILL IN PARAMETER VARIABLES // + for(var q = 0; q < this.scope_tree.parameter.length; q++){ + // get parameter name + let parameter_name = this.scope_tree.parameter[q].name; + + // get argument value + let argument_value = argument_value_list[q]; + + // store as variable in current scope + this.variable_set({ + name: parameter_name, + index: [] + }, argument_value); + } + } /* - GETTER: Return name of current scope (top of stack) + HELPER: Handle returning back to previous scope + */ + scope_return(){ + // REMEMBER VALUES TO TAKE OVER // + let take_over_list = []; + for(var q = 0; q < this.scope_tree.parameter.length; q++){ + // make sure argument was set when called + if(q >= this.stack_top.argument_variable.length) continue; + + // make sure parameter is read-write + if(!this.scope_tree.parameter[q].reference) continue; + + // make sure the argument was a variable + if(this.stack_top.argument_variable[q] === null) continue; + + // get parameter name + let parameter_name = this.scope_tree.parameter[q].name; + + // get variable's final value + let variable_value = this.variable_get({ + name: parameter_name, + index: [] + }); + + // add to list + take_over_list.push({variable: this.stack_top.argument_variable[q], value: variable_value}); + } + + + // POP LAYER FROM STACK // + this.stack.pop(); + + + // TAKE OVER READ-WRITE PARAMETERS // + for(var one_reference of take_over_list){ + // set variable's new value + this.variable_set(one_reference.variable, one_reference.value); + } + } + + /* + GETTER: Return top/bottom of stack + */ + get stack_top(){ + return this.stack[this.stack.length - 1]; + } + get stack_bottom(){ + return this.stack[0]; + } + + /* + GETTER: Return name of current scope */ get scope(){ - return this.stack[this.stack.length - 1].scope; + return this.stack_top.scope; } /* @@ -139,6 +247,16 @@ class Juicescript_runner { return this.tree.scope[this.scope]; } + /* + GETTER / SETTER: Return current command counter + */ + get counter(){ + return this.stack_top.counter; + } + set counter(value){ + this.stack_top.counter = value; + } + /* COMMAND HELPER: Validate number of command arguments */ @@ -219,9 +337,9 @@ class Juicescript_runner { */ argument(number){ // GET WANTED ARGUMENT // - // make sure this argument number exists + // fallback for argument not existing if(this.command.argument.length < number){ - throw "unable to extract value of argument " + number + ", command " + this.command.name + " (not defined)"; + return {type: Juicescript.argument_type.LITERAL, value: null}; } // get argument object @@ -318,17 +436,15 @@ class Juicescript_runner { */ variable_get(variable){ // FIND VARIABLE IN STACK // - // check if global - /**/let is_global = false; + // get its scope's full variable list + let variable_list; + if(this.variable_is_global(variable)){ + variable_list = this.stack_bottom.variable; + } else { + variable_list = this.stack_top.variable; + } - // get index on stack - let stack_index = this.stack.length - 1; - if(is_global) stack_index = 0; - - // get index' full variable list - let variable_list = this.stack[stack_index].variable; - - // try to load value from stack + // try to load value from list let value = null; if(Object.keys(variable_list).includes(variable.name)){ value = variable_list[variable.name]; @@ -336,7 +452,9 @@ class Juicescript_runner { // APPLY INDEXES // + //*/ TODO: apply indexes /**/if(variable.index.length > 0) this.warning("yet to be implemented"); + /**/if(value === undefined) value = null; // RETURN VALUE // @@ -384,20 +502,38 @@ class Juicescript_runner { */ variable_set(variable, value){ // FIND VARIABLE IN STACK // - // check if global - /**/let is_global = false; + // get its scope's full variable list + let variable_list; + if(this.variable_is_global(variable)){ + variable_list = this.stack_bottom.variable; + } else { + variable_list = this.stack_top.variable; + } - // get index on stack - let stack_index = this.stack.length - 1; - if(is_global) stack_index = 0; - - // get index' full variable list - let variable_list = this.stack[stack_index].variable; - - // set value on stack + // set value on list + //*/ TODO: apply indexes variable_list[variable.name] = value; } + /* + VARIABLE HELPER: Check if variable is global + */ + variable_is_global(variable){ + // CHECK IF IN ROOT SCOPE // + if(this.scope === null){ + // can't be global in here + return false; + } + + + // CHECK IF IN LIST OF GLOBAL VARIABLE NAMES // + // get list + let global_list = this.scope_tree.global; + + // check + return global_list.includes(variable.name); + } + /* GETTER: Check if there was a warning/error so far */