aboutsummaryrefslogtreecommitdiffstats
path: root/test/let5.ok
Commit message (Collapse)AuthorAgeFilesLines
* Feature: @let statement provides block-scoped localsKaz Kylheku2022-04-091-0/+2
This is a feature which builds on the @local work, providing a statement which looks like this: @let (var1 [= init1], var2 [= init2], ...) statement The variables are bound, given their optional values or else left undefined, and the statement is executed in the scope in which they exist. These variables may have the same name as existing block-scoped locals or function-wide @local locals, or function parameters, in which case shadowing takes place. * awk.h (NODETYPE): New enum symbol Node_alias. This marks entries in the new symbol table alias_table which is needed for compiling @let statements that occur outside of functions. (NODE): New union member sub.nodep.n. Member sub.nodep.rn is moved into this union becoming sub.nodep.n.rn. New member sub.nodep.n.rpn, of type NODE **. This is now the the implementation of fparms. (nxparam): New macro, denoting sub.nodep.x.extra. This is used as the link pointer for maintaining a stack of lexicals while compiling a function. (let_alias): New macro, denoting sub.nodep.l.lptr. This is the new Node_alias type's semantic payload: the node being aliased. (frame_cnt): New macro for sub.nodep.reserved. This is used to keep track of a function's frame size (number of local variables). If there are no local variables other than function parameters, then then f->frame_cnt == f->param_cnt. Otherwise f->frame_cnt > f->param_cnt. Note that traditional Awk local variables, which are indistinguishable from parameters, are part of param_cnt. We are talking about the new style local variables here. (fparms): Now sub.nodep.n.rpn. (for_array, xarray): Macros follow the move of sub.nodep.rn to sub.nodep.n.rn. (OPCODE): New opcode, Op_clear_val for clearing a variable to the null value. (make_params): Return NODE **, rather than NODE *. (extend_locals, install_let, install_global_let, remove_let): Functions declared. * awkgram.y (in_function): Global variable changes type from bool to INSTRUCTION *. It still functions as a Boolean, indicating that the parser is processing a function, but it also gives the instruction node. (in_loop): New static variable: loop nesting count. If this is positive, we are parsing a construct located inside a loop. (let_free): New static variable. This is the free list of lexical variables. When, during the compilation of a function, a let block is done, the lexical variable nodes are returned to the free list. The next time a let block opens, it can allocate nodes from that list. Thanks to this recycling list, lexical variables with non-overlapping scopes will map to the same underlying locations in the function's param/local frame. (let_stack): New static variable. When a new let variable is introduced into the scope, it is pushed onto this stack. The @let construct remembers the original stack top. When it's done compiling, it pops the stack back down to the original top, and returns all the entries thus removed into the let_free list. (LEX_LOCAL, LEX_LET): New terminal symbols for the param keyword in the @ param notation. (function_prologue): Reset let_stack and let_free to NULL when starting to compile function. Store the function node $1 into in_function. (statement): For all the looping constructs, decrement the in_loop nesting count that is incremented by the lexical analyzer. New @ 'LEX_LET' '(' ... phrase structure here. Add new production for '@' LEX_LOCAL. (local_var_list_opt, local_var_list): New nonterminal symbols. (let_var_list_opt, let_var_list): New nonterminal symbols. (simple_variable): New production for @ LEX_LOCAL ':' NAME. This is a copy of the NAME production, with the additional logic that when in_function is true, the symbol is added as a local to the current function via add_local before being processed as a variable reference. So it is as if it had been a parameter all along. (tokentab): Register "let" with the LEX_LET token number, attributing it as a special symbol that is a Gawk extension, much like "include" and other "@" items. Same for "local" and LET_LOCAL. (yylex): Recognize "let" and "local" only after "@". Increment in_loop nesting counter for for, while and do. (parms_shadow): Check the entire frame, not just the parameters, for shadowing. Update to NODE ** fparms representation. (mk_function): Updated due to rename of remove_params to remove_locals. (install_function): Pass the new third parameter to make_params in order to receive the allocation size of the parameter vector. This is installed into f->param_alloc. The new add_local function makes use of this to diagnose it if there is no room to add parameters. Update to NODE ** fparams, and initialize frame_cnt equal to param_cnt. (add_local): New static function for adding a parameter to the function currently being compiled. This has to check perform diagnostics on the local variable, similar to the checks done on a parameter. The extend_locals function in symbol.c is relied upon to do the reallocation to add the parameter and also register it in the symbol table. We strdup parm->lextok parameter name, because that will later be freed during parsing. (add_let): New function, closely based on add_local, but with these differences: no duplicate check since shadowing is allowed. And extend_locals is only used if the let_free list is empty. (check_params): A few parameter checks are moved into the new check_param function. This was because they were shared with an earlier implementation of add_local. The function then got cloned into check_locals in order to have different wording. (check_param): New static function, split off from check_params. (check_local): New static function, closely based on check_param, but using different wording to distinguish locals from params. Uses NULL value of fname to disable checks; this is for when we are are compiling a let that isn't in a function. (genym): New static function. Used for generating anonymous globals. Let variables outside of functions are implemented as aliases for anonymous globals. The globals are allocated/freed in a stack-like manner, exactly like frame locations are allocated for let variables inside a function. * command.y (variable_generator): Update to NODE ** fparms. * debug.c (do_info): Report the locals as if they were parameters, so that they are visible under debugging. Update parameter access to reflect NODE ** representation. (find_param): Find a local variable too. Update to NODE ** fparms. (print_function, print_memory, print_instruction): Adjust to NODE ** fparms. In print_instruction, handle new Op_clear_var. (do_eval): Follow rename of remove_params to remove_locals. Append all the locals to the frame stack. Update to NODE ** fparms. (parse_condition): Follow rename of remove_params to remove_locals. * eval.c (nodetypes): Entry for Node_alias. (optypes): Entry for Op_clear_var. (setup_frame): Allocate the full locals frame (f->frame_cnt), but check the arguments against only the param count (f->param_cnt). Update to NODE ** fparms. (restore_frame): Destroy all the locals, not just parameters. * interpret.h (r_interpret): Implement Op_clear_var opcode. * profile.c (func_params): Change to NODE ** type. (pprint, pp_func): Update to NODE ** fparms. In pprint Handle Op_clear_var. * symbol.c (alias_table): New static variable. (TAB_PARAM, TAB_ALIAS, TAB_GLOBAL, TAB_FUNC, TAB_SYM, TAB_COUNT): New enum symbols. These identify indices into the tables array. (tables): New static array variable: replaces the automatic array used in the lookup function which is initialized on every lookup call. This array is no longer terminated by a null pointer. (init_symbol_table): Initialize alias_table. Initialize entries of tables. (lookup): Local array tables and its initialization removed. Iterate over table up to below TAB_COUNT rather than by looking for a null pointer. Check for the global_table by comparing the index to TAB_GLOBAL rather than the table pointer to global_table. Interestingly, gawk already has support for placing multiple NODE objects in the param_table under the same name with LIFO discipline, i.e. shadowing. Each node in the param_table is understood to have a stack of duplicates using the dup_ent link pointer. Only thing is, the lookup() function ignores this. We fix it so that if there is a stack of duplicates, it substitutes the top indicated by dup_ent. We implement the alias table: if lookup finds an entry in this table, we don't return that entry, but what it aliases for, indicated by n->let_alias. The alias table thus implements symbol macros; Node_alias nodes are never seen at run-time because aliases resolve during compiling, and that's why we don't check for them in any of the run-time code, like eval.c, debug.c, profile.c or interpret.h. (make_params): Returns NODE ** now and allocates array of pointers, using individual getnode calls to allocate the NODE objects, rather than allocating params as a contiguous block of NODE objects. (extend_locals): New function. This reallocates the param array to hold one more parameter, on the assumption that the caller's lcount parameter is one greater than the current size. The parameter is initialized and registered in the symbol table. (install_params): Update to NODE ** fparms. (remove_params): Renamed to remove_locals. Refers to frame_cnt rather than param_cnt. Symbol logic moved to remove_common. (remove_common): New static function, factored out from remove_locals. (install_let): New public wrapper around install: lets us install a node under a specific alias name. (install_global_let): New function: this is the public interface for installing an entry in the alias table. (remove_let): New function: essentially a wrapper around remove_common. Previously, this functionality is only exposed to other modules via the install_params and remove_locals, which take a function object from which they retrieve an array of locals. For the let construct, we need more flexibility to bind and unbind individual symbols, not everything at once. (destroy_symbol): Refer to frame_cnt rather than param_cnt. (install): Handle nodes of NODETYPE Node_alias, by selecting the alias_table. The alias_table supports entries via the dup_ent mechanism just like param_table. (check_param_names): Refer to frame_cnt rather than param_cnt and update to NODE ** representation of fparms. * test/let[1-6].awk, test/let[1-6].ok: New files * test/Makefile.am, test/Makefile.in, test/Maketests: rules for new tests. * doc/gawktexi.in, doc/gawk.texi: Documented.