Mercurial > octave
changeset 29857:0b01806bb663
fix command syntax parsing error (bug #60882)
* lex.h, lex.ll (lexical_feedback::maybe_mark_previous_token_as_variable):
Delete function and all uses.
(lexical_feedback::m_pending_local_variables): Now a list of sets.
Change all uses.
(lexical_feedback::init): Insert empty set as first element of
m_pending_local_variables.
(lexical_feedback::reset): Preserve first element of
m_pending_local_variables.
(lexical_feedback::mark_as_variable): New function.
(lexical_feedback::is_variable): New function.
(base_lexer::is_variable): Delete.
* oct-parse.yy: Anywhere a symbol scope is pushed to or popped from
the symbol table context, also push or pop a set of pending local
variables. Mark symbols that appear on the LHS of '=' operators as
pending variables for the current scope.
* test/bug-60882/bug-60882.tst, test/bug-60882/bug_60882.m,
test/bug-60882/module.mk: New files.
* test/module.mk: Update.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Tue, 06 Jul 2021 23:16:14 -0400 |
parents | 56b3e2af0298 |
children | 6dc298d3261c |
files | libinterp/parse-tree/lex.h libinterp/parse-tree/lex.ll libinterp/parse-tree/oct-parse.yy test/bug-60882/bug-60882.tst test/bug-60882/bug_60882.m test/bug-60882/module.mk test/module.mk |
diffstat | 7 files changed, 106 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/parse-tree/lex.h Tue Jul 06 11:12:20 2021 -0700 +++ b/libinterp/parse-tree/lex.h Tue Jul 06 23:16:14 2021 -0400 @@ -347,11 +347,11 @@ bool previous_token_may_be_command (void) const; - void maybe_mark_previous_token_as_variable (void); - void mark_as_variable (const std::string& nm); void mark_as_variables (const std::list<std::string>& lst); + bool is_variable (const std::string& nm) const; + interpreter& m_interpreter; // true means that we have encountered eof on the input stream. @@ -515,8 +515,10 @@ // current_function_level > 0 std::stack<bool> m_parsed_function_name; - // set of identifiers that might be local variable names. - std::set<std::string> m_pending_local_variables; + // A list of sets of identifiers that might be local variable names. + // The front of the list corresponds to the current scope. The next + // element is for the parent scope, etc. + std::list<std::set<std::string>> m_pending_local_variables; // Track current symbol table scope and context. symbol_table_context m_symtab_context; @@ -657,8 +659,6 @@ bool inside_any_object_index (void); - bool is_variable (const std::string& name); - int make_keyword_token (const std::string& s); bool fq_identifier_contains_keyword (const std::string& s);
--- a/libinterp/parse-tree/lex.ll Tue Jul 06 11:12:20 2021 -0700 +++ b/libinterp/parse-tree/lex.ll Tue Jul 06 23:16:14 2021 -0400 @@ -1817,8 +1817,6 @@ "=" { curr_lexer->lexer_debug ("="); - curr_lexer->maybe_mark_previous_token_as_variable (); - return curr_lexer->handle_op ('='); } @@ -2223,6 +2221,12 @@ // The closest paren, brace, or bracket nesting is not an object // index. m_looking_at_object_index.push_front (false); + + // Provide an initial set to store variables at the top-level. + // Don't clear this one when resetting lexical_feedback state. + // It should persist since the top-level scope does. Hmm maybe + // we should just use the symbol scope object for this job? + m_pending_local_variables.push_front (std::set<std::string> ()); } void @@ -2280,7 +2284,9 @@ while (! m_parsed_function_name.empty ()) m_parsed_function_name.pop (); - m_pending_local_variables.clear (); + while (m_pending_local_variables.size () > 1) + m_pending_local_variables.pop_front (); + m_symtab_context.clear (); m_nesting_level.reset (); m_tokens.clear (); @@ -2353,19 +2359,34 @@ } void - lexical_feedback::maybe_mark_previous_token_as_variable (void) + lexical_feedback::mark_as_variable (const std::string& nm) { - token *tok = m_tokens.front (); - - if (tok && tok->isstring ()) - m_pending_local_variables.insert (tok->text ()); + auto& vars = m_pending_local_variables.front (); + vars.insert (nm); } void lexical_feedback::mark_as_variables (const std::list<std::string>& lst) { - for (const auto& var : lst) - m_pending_local_variables.insert (var); + auto& vars = m_pending_local_variables.front (); + for (const auto& nm : lst) + vars.insert (nm); + } + + bool + lexical_feedback::is_variable (const std::string& nm) const + { + if (m_interpreter.at_top_level () && m_interpreter.is_variable (nm)) + return true; + + // Search current scope, then parents. + for (const auto& vars : m_pending_local_variables) + { + if (vars.find (nm) != vars.end ()) + return true; + } + + return false; } } @@ -2656,15 +2677,6 @@ return retval; } - bool - base_lexer::is_variable (const std::string& name) - { - return ((m_interpreter.at_top_level () - && m_interpreter.is_variable (name)) - || (m_pending_local_variables.find (name) - != m_pending_local_variables.end ())); - } - int base_lexer::make_keyword_token (const std::string& s) {
--- a/libinterp/parse-tree/oct-parse.yy Tue Jul 06 11:12:20 2021 -0700 +++ b/libinterp/parse-tree/oct-parse.yy Tue Jul 06 23:16:14 2021 -0400 @@ -1451,7 +1451,8 @@ if (lexer.m_looking_at_function_handle) { // Will get a real name later. - lexer.m_symtab_context.push (octave::symbol_scope ("parser:param_lsit_beg")); + lexer.m_symtab_context.push (octave::symbol_scope ("parser:param_list_beg")); + lexer.m_pending_local_variables.push_front (std::set<std::string> ()); lexer.m_looking_at_function_handle--; lexer.m_looking_at_anon_fcn_args = true; } @@ -1607,6 +1608,7 @@ // This scope may serve as the parent scope for local // functions in classdef files.. lexer.m_symtab_context.push (octave::symbol_scope ("parser:push_script_symtab")); + lexer.m_pending_local_variables.push_front (std::set<std::string> ()); } ; @@ -1628,6 +1630,7 @@ // Unused symbol table context. lexer.m_symtab_context.pop (); + lexer.m_pending_local_variables.pop_front (); delete $3; } @@ -1650,6 +1653,7 @@ // Unused symbol table context. lexer.m_symtab_context.pop (); + lexer.m_pending_local_variables.pop_front (); parser.finish_classdef_file ($3, $6); @@ -1952,6 +1956,7 @@ // Create invalid parent scope. lexer.m_symtab_context.push (octave::symbol_scope ()); + lexer.m_pending_local_variables.push_front (std::set<std::string> ()); lexer.m_parsing_classdef = true; lexer.m_parsing_classdef_decl = true; lexer.m_classdef_element_names_are_keywords = true; @@ -2824,6 +2829,7 @@ // Will get a real name later. m_lexer.m_symtab_context.push (symbol_scope ("parser:push_fcn_symtab")); + m_lexer.m_pending_local_variables.push_front (std::set<std::string> ()); m_function_scopes.push (m_lexer.m_symtab_context.curr_scope ()); if (! m_lexer.m_reading_script_file && m_curr_fcn_depth == 0 @@ -2938,6 +2944,7 @@ symbol_scope parent_scope = m_lexer.m_symtab_context.parent_scope (); m_lexer.m_symtab_context.pop (); + m_lexer.m_pending_local_variables.pop_front (); expr->set_print_flag (false); @@ -3428,6 +3435,8 @@ { tree_expression *tmp = lhs->remove_front (); + m_lexer.mark_as_variable (tmp->name ()); + retval = new tree_simple_for_command (parfor, tmp, expr, maxproc, body, lc, tc, l, c); @@ -3444,9 +3453,11 @@ bison_error ("invalid syntax for parfor statement"); } - else - retval = new tree_complex_for_command (lhs, expr, body, - lc, tc, l, c); + + m_lexer.mark_as_variables (lhs->variable_names ()); + + retval = new tree_complex_for_command (lhs, expr, body, + lc, tc, l, c); } } else @@ -3769,6 +3780,8 @@ delete lhs; + m_lexer.mark_as_variable (tmp->name ()); + return new tree_simple_assignment (tmp, rhs, false, l, c, t); } else @@ -3789,6 +3802,8 @@ } } + m_lexer.mark_as_variables (names); + return new tree_multi_assignment (lhs, rhs, false, l, c); } } @@ -3816,6 +3831,7 @@ cmds, m_lexer.m_help_text); m_lexer.m_symtab_context.pop (); + m_lexer.m_pending_local_variables.pop_front (); m_lexer.m_help_text = ""; sys::time now; @@ -4216,6 +4232,7 @@ base_parser::recover_from_parsing_function (void) { m_lexer.m_symtab_context.pop (); + m_lexer.m_pending_local_variables.pop_front (); if (m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) @@ -4251,6 +4268,7 @@ tree_classdef *retval = nullptr; m_lexer.m_symtab_context.pop (); + m_lexer.m_pending_local_variables.pop_front (); std::string cls_name = id->name ();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60882/bug-60882.tst Tue Jul 06 23:16:14 2021 -0400 @@ -0,0 +1,28 @@ +######################################################################## +## +## Copyright (C) 2021 The Octave Project Developers +## +## See the file COPYRIGHT.md in the top-level directory of this +## distribution or <https://octave.org/copyright/>. +## +## This file is part of Octave. +## +## Octave is free software: you can redistribute it and/or modify it +## under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## Octave is distributed in the hope that it will be useful, but +## WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## <https://www.gnu.org/licenses/>. +## +######################################################################## + +## bug #60882: error parsing command syntax + +%!assert (bug_60882 (), 42)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60882/bug_60882.m Tue Jul 06 23:16:14 2021 -0400 @@ -0,0 +1,13 @@ +function retval = bug_60882 () + + job.foobar = {}; + + foobar off + + retval = 42; + +endfunction + +function foobar (opt) + assert (opt, 'off'); +endfunction
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/bug-60882/module.mk Tue Jul 06 23:16:14 2021 -0400 @@ -0,0 +1,5 @@ +bug_60882_TEST_FILES = \ + %reldir%/bug-60882.tst \ + %reldir%/bug_60882.m + +TEST_FILES += $(bug_60882_TEST_FILES)
--- a/test/module.mk Tue Jul 06 11:12:20 2021 -0700 +++ b/test/module.mk Tue Jul 06 23:16:14 2021 -0400 @@ -95,6 +95,7 @@ include %reldir%/bug-59704/module.mk include %reldir%/bug-59937/module.mk include %reldir%/bug-60237/module.mk +include %reldir%/bug-60882/module.mk include %reldir%/class-concat/module.mk include %reldir%/classdef/module.mk include %reldir%/classdef-multiple-inheritance/module.mk