Mercurial > octave
diff libinterp/parse-tree/oct-parse.yy @ 29864:e2e493712818
improve previous change for command syntax parsing (bug #60882)
* symrec.h (symbol_record::variable): New static constant.
(symbol_record_rep::is_variable, symbol_record_rep::mark_as_variable,
symbol_record_rep::unmark_as_variable): New functions.
(symbol_record_rep::mark_formal): Also mark as variable.
(symbol_record::is_variable, symbol_record::mark_as_variable,
symbol_record::unmark_as_variable): New functions.
* symscope.h, symscope.cc (symbol_scope_rep::mark_as_variable,
symbol_scope_rep::mark_as_variables, symbol_scope_rep::is_variable,
symbol_scope::mark_as_variable, symbol_scope::mark_as_variables,
symbol_scope::is_variable): New functions.
* lex.h, lex.ll (lexical_feedback::m_pending_local_variables): Delete
member variable and all uses.
(CMD_OR_COMPUTED_ASSIGN_OP): Don't attempt Matlab compatibility for
computed assignment operators.
(lexical_feedback::mark_as_variable,
lexical_feedback::mark_as_variables, lexical_feedback::is_variable):
Store status of symbols as variables in scope objects instead of
m_pending_local_variables lists.
(base_lexer::handle_identifier): Don't check for variables when
deciding whether an identifier can be recognized as a command.
* parse.h, oct-parse.yy (parse_exception, parse_tree_validator):
New classes.
(base_parser::bison_error): New overloads that accept parse_exception
and list of parse_exception objects.
(base_parser::finish_classdef_file): Validate classdef object and
local functions and return status.
(base_parser::validate_primary_fcn, base_parser::finish_input):
New functions.
(file): Validate primary function after parser is finished reading and
parsing file.
(input): Call base_parser::finish_input to validate and accept
statement list.
* try.tst: Fix syntax in test.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Fri, 09 Jul 2021 04:05:58 -0400 |
parents | 0b01806bb663 |
children | 6549fa7558ba |
line wrap: on
line diff
--- a/libinterp/parse-tree/oct-parse.yy Fri Jul 09 04:03:36 2021 -0400 +++ b/libinterp/parse-tree/oct-parse.yy Fri Jul 09 04:05:58 2021 -0400 @@ -417,19 +417,22 @@ OCTAVE_YYUSE ($2); $$ = nullptr; - std::shared_ptr<octave::tree_statement_list> tmp_lst ($1); - parser.statement_list (tmp_lst); - YYACCEPT; + + if (! parser.finish_input ($1)) + YYABORT; + else + YYACCEPT; } | simple_list END_OF_INPUT { OCTAVE_YYUSE ($2); $$ = nullptr; - lexer.m_end_of_input = true; - std::shared_ptr<octave::tree_statement_list> tmp_lst ($1); - parser.statement_list (tmp_lst); - YYACCEPT; + + if (! parser.finish_input ($1, true)) + YYABORT; + else + YYACCEPT; } | parse_error { @@ -1452,7 +1455,6 @@ { // Will get a real name later. 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; } @@ -1608,7 +1610,6 @@ // 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> ()); } ; @@ -1630,9 +1631,11 @@ // Unused symbol table context. lexer.m_symtab_context.pop (); - lexer.m_pending_local_variables.pop_front (); delete $3; + + if (! parser.validate_primary_fcn ()) + YYABORT; } else { @@ -1641,6 +1644,9 @@ $4->beg_pos (), $4->end_pos ()); parser.make_script ($3, end_of_script); + + if (! parser.validate_primary_fcn ()) + YYABORT; } $$ = nullptr; @@ -1653,9 +1659,9 @@ // Unused symbol table context. lexer.m_symtab_context.pop (); - lexer.m_pending_local_variables.pop_front (); - - parser.finish_classdef_file ($3, $6); + + if (! parser.finish_classdef_file ($3, $6)) + YYABORT; $$ = nullptr; } @@ -1956,7 +1962,6 @@ // 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; @@ -2560,6 +2565,131 @@ namespace octave { + class parse_exception : public std::runtime_error + { + public: + + parse_exception (const std::string& message, + const std::string& fcn_name = "", + const std::string& file_name = "", + int line = -1, int column = -1) + : runtime_error (message), m_message (message), + m_fcn_name (fcn_name), m_file_name (file_name), + m_line (line), m_column (column) + { } + + parse_exception (const parse_exception&) = default; + + parse_exception& operator = (const parse_exception&) = default; + + ~parse_exception (void) = default; + + std::string message (void) const { return m_message; } + + // Provided for std::exception interface. + const char * what (void) const noexcept { return m_message.c_str (); } + + std::string fcn_name (void) const { return m_fcn_name; } + std::string file_name (void) const { return m_file_name; } + + int line (void) const { return m_line; } + int column (void) const { return m_column; } + + // virtual void display (std::ostream& os) const; + + private: + + std::string m_message; + + std::string m_fcn_name; + std::string m_file_name; + int m_line; + int m_column; + }; + + class parse_tree_validator : public tree_walker + { + public: + + parse_tree_validator (void) + : m_scope (), m_error_list () + { } + + parse_tree_validator (const parse_tree_validator&) = delete; + + parse_tree_validator& operator = (const parse_tree_validator&) = delete; + + ~parse_tree_validator (void) = default; + + symbol_scope get_scope (void) const { return m_scope; } + + bool ok (void) const { return m_error_list.empty (); } + + std::list<parse_exception> error_list (void) const + { + return m_error_list; + } + + void visit_octave_user_script (octave_user_script& script) + { + unwind_protect_var<symbol_scope> restore_var (m_scope, script.scope ()); + + tree_statement_list *stmt_list = script.body (); + + if (stmt_list) + stmt_list->accept (*this); + } + + void visit_octave_user_function (octave_user_function& fcn) + { + unwind_protect_var<symbol_scope> restore_var (m_scope, fcn.scope ()); + + tree_statement_list *stmt_list = fcn.body (); + + if (stmt_list) + stmt_list->accept (*this); + + std::map<std::string, octave_value> subfcns = fcn.subfunctions (); + + if (! subfcns.empty ()) + { + for (auto& nm_val : subfcns) + { + octave_user_function *subfcn + = nm_val.second.user_function_value (); + + if (subfcn) + subfcn->accept (*this); + } + } + } + + void visit_index_expression (tree_index_expression& idx_expr) + { + if (idx_expr.is_word_list_cmd ()) + { + std::string sym_nm = idx_expr.name (); + + if (m_scope.is_variable (sym_nm)) + { + std::string message + = sym_nm + ": invalid use of symbol as both variable and command"; + parse_exception pe (message, m_scope.fcn_name (), + m_scope.fcn_file_name (), + idx_expr.line (), idx_expr.column ()); + + m_error_list.push_back (pe); + } + } + } + + private: + + symbol_scope m_scope; + + std::list<parse_exception> m_error_list; + }; + std::size_t base_parser::parent_scope_info::size (void) const { @@ -2829,7 +2959,6 @@ // 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 @@ -2944,7 +3073,6 @@ 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); @@ -3808,8 +3936,6 @@ } } - // Define a script. - void base_parser::make_script (tree_statement_list *cmds, tree_statement *end_script) @@ -3831,7 +3957,6 @@ 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; @@ -4232,7 +4357,6 @@ 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) @@ -4268,7 +4392,6 @@ tree_classdef *retval = nullptr; m_lexer.m_symtab_context.pop (); - m_lexer.m_pending_local_variables.pop_front (); std::string cls_name = id->name (); @@ -4579,12 +4702,39 @@ return new tree_function_def (fcn, l, c); } - void + bool base_parser::finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns) { - if (m_lexer.m_reading_classdef_file) - m_classdef_object = std::shared_ptr<tree_classdef> (cls); + parse_tree_validator validator; + + cls->accept (validator); + + if (local_fcns) + { + for (tree_statement *elt : *local_fcns) + { + tree_command *cmd = elt->command (); + + tree_function_def *fcn_def + = dynamic_cast<tree_function_def *> (cmd); + + fcn_def->accept (validator); + } + } + + if (! validator.ok ()) + { + delete cls; + delete local_fcns; + + bison_error (validator.error_list ()); + + return false; + } + + // Require all validations to succeed before installing any local + // functions or defining the classdef object for later use. if (local_fcns) { @@ -4599,7 +4749,8 @@ = dynamic_cast<tree_function_def *> (cmd); octave_value ov_fcn = fcn_def->function (); - octave_function *fcn = ov_fcn.function_value (); + octave_user_function *fcn = ov_fcn.user_function_value (); + std::string nm = fcn->name (); std::string file = fcn->fcn_file_name (); @@ -4608,6 +4759,12 @@ delete local_fcns; } + + // FIXME: Is it possible for the following condition to be false? + if (m_lexer.m_reading_classdef_file) + m_classdef_object = std::shared_ptr<tree_classdef> (cls); + + return true; } // Make an index expression. @@ -5167,6 +5324,23 @@ m_parse_error_msg = output_buf.str (); } + void + base_parser::bison_error (const parse_exception& pe) + { + bison_error (pe.message (), pe.line (), pe.column ()); + } + + void + base_parser::bison_error (const std::list<parse_exception>& pe_list) + { + // For now, we just report the first error found. Reporting all + // errors will require a bit more refactoring. + + parse_exception pe = pe_list.front (); + + bison_error (pe.message (), pe.line (), pe.column ()); + } + int parser::run (void) { @@ -5423,6 +5597,57 @@ return octave_value (); } + bool + base_parser::finish_input (tree_statement_list *lst, bool at_eof) + { + m_lexer.m_end_of_input = at_eof; + + if (lst) + { + parse_tree_validator validator; + + lst->accept (validator); + + if (! validator.ok ()) + { + delete lst; + + bison_error (validator.error_list ()); + + return false; + } + } + + std::shared_ptr<octave::tree_statement_list> tmp_lst (lst); + + statement_list (tmp_lst); + + return true; + } + + // Check script or function for semantic errors. + bool + base_parser::validate_primary_fcn (void) + { + octave_user_code *code = m_primary_fcn.user_code_value (); + + if (code) + { + parse_tree_validator validator; + + code->accept (validator); + + if (! validator.ok ()) + { + bison_error (validator.error_list ()); + + return false; + } + } + + return true; + } + // Maybe print a warning if an assignment expression is used as the // test in a logical expression.