Mercurial > octave
changeset 33322:ba0ba1b58e5e bytecode-interpreter
maint: merge default to bytecode-interpreter
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 04 Apr 2024 15:07:55 -0400 |
parents | 302c3a1d12d7 (current diff) 44c24e645677 (diff) |
children | 263ddd6b96f7 |
files | |
diffstat | 2 files changed, 3284 insertions(+), 3276 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/parse-tree/oct-parse.yy Thu Apr 04 14:23:30 2024 -0400 +++ b/libinterp/parse-tree/oct-parse.yy Thu Apr 04 15:07:55 2024 -0400 @@ -511,13 +511,11 @@ word_list_cmd : identifier word_list { - if (! ($$ = parser.make_index_expression ($1, nullptr, $2, nullptr, '('))) + if (! ($$ = parser.make_word_list_command ($1, $2))) { // make_index_expression deleted $1 and $2. YYABORT; } - - $$->mark_word_list_cmd (); } ; @@ -2214,3011 +2212,3018 @@ OCTAVE_BEGIN_NAMESPACE(octave) - class parse_exception : public std::runtime_error - { - public: - - parse_exception () = delete; - - parse_exception (const std::string& message, const std::string& fcn_name = "", const std::string& file_name = "", const filepos& pos = filepos ()) - : runtime_error (message), m_message (message), m_fcn_name (fcn_name), m_file_name (file_name), m_pos (pos) - { } - - OCTAVE_DEFAULT_COPY_MOVE_DELETE (parse_exception) - - std::string message () const { return m_message; } - - // Provided for std::exception interface. - const char * what () const noexcept { return m_message.c_str (); } - - std::string fcn_name () const { return m_fcn_name; } - std::string file_name () const { return m_file_name; } - - filepos pos () const { return m_pos; } - - // virtual void display (std::ostream& os) const; - - private: - - std::string m_message; - - std::string m_fcn_name; - std::string m_file_name; - filepos m_pos; - }; - - class parse_tree_validator : public tree_walker +class parse_exception : public std::runtime_error +{ +public: + + parse_exception () = delete; + + parse_exception (const std::string& message, const std::string& fcn_name = "", const std::string& file_name = "", const filepos& pos = filepos ()) + : runtime_error (message), m_message (message), m_fcn_name (fcn_name), m_file_name (file_name), m_pos (pos) + { } + + OCTAVE_DEFAULT_COPY_MOVE_DELETE (parse_exception) + + std::string message () const { return m_message; } + + // Provided for std::exception interface. + const char * what () const noexcept { return m_message.c_str (); } + + std::string fcn_name () const { return m_fcn_name; } + std::string file_name () const { return m_file_name; } + + filepos pos () const { return m_pos; } + + // virtual void display (std::ostream& os) const; + +private: + + std::string m_message; + + std::string m_fcn_name; + std::string m_file_name; + filepos m_pos; +}; + +class parse_tree_validator : public tree_walker +{ +public: + + parse_tree_validator () + : m_scope (symbol_scope::anonymous ()), m_error_list () + { } + + OCTAVE_DISABLE_COPY_MOVE (parse_tree_validator) + + ~parse_tree_validator () = default; + + symbol_scope get_scope () const { return m_scope; } + + bool ok () const { return m_error_list.empty (); } + + std::list<parse_exception> error_list () const { - public: - - parse_tree_validator () - : m_scope (symbol_scope::anonymous ()), m_error_list () - { } - - OCTAVE_DISABLE_COPY_MOVE (parse_tree_validator) - - ~parse_tree_validator () = default; - - symbol_scope get_scope () const { return m_scope; } - - bool ok () const { return m_error_list.empty (); } - - std::list<parse_exception> error_list () 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.beg_pos ()); - - m_error_list.push_back (pe); - } - } - } - - private: - - symbol_scope m_scope; - - std::list<parse_exception> m_error_list; - }; - - template <typename LIST_T, typename ELT_T> - static LIST_T * - list_append (LIST_T *list, ELT_T elt) + return m_error_list; + } + + void visit_octave_user_script (octave_user_script& script) { - list->push_back (elt); - return list; - } - - template <typename LIST_T, typename ELT_T> - static LIST_T * - list_append (LIST_T *list, const token& /*sep_tok*/, ELT_T elt) - { - // FIXME XXX! need to capture SEP_TOK here - list->push_back (elt); - return list; - } - - std::size_t - base_parser::parent_scope_info::size () const - { - return m_info.size (); - } - - void - base_parser::parent_scope_info::push (const value_type& elt) - { - m_info.push_back (elt); - } - - void - base_parser::parent_scope_info::push (const symbol_scope& scope) - { - push (value_type (scope, "")); + 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 - base_parser::parent_scope_info::pop () - { - m_info.pop_back (); - } - - bool - base_parser::parent_scope_info::name_ok (const std::string& name) + void visit_octave_user_function (octave_user_function& fcn) { - // Name can't be the same as any parent function or any other - // function we've already seen. We could maintain a complex - // tree structure of names, or we can just store the set of - // full names of all the functions, which must be unique. - - std::string full_name; - - for (std::size_t i = 0; i < size()-1; i++) - { - const value_type& elt = m_info[i]; - - if (name == elt.second) - return false; - - full_name += elt.second + ">"; - } - - full_name += name; - - if (m_all_names.find (full_name) != m_all_names.end ()) + 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 ()) { - // Return false (failure) if we are parsing a subfunction, local - // function, or nested function. Otherwise, it is OK to have a - // duplicate name. - - return ! (m_parser.parsing_subfunctions () || m_parser.parsing_local_functions () || m_parser.curr_fcn_depth () > 0); - } - - m_all_names.insert (full_name); - - return true; - } - - bool - base_parser::parent_scope_info::name_current_scope (const std::string& name) - { - if (! name_ok (name)) - return false; - - if (size () > 0) - m_info.back().second = name; - - return true; - } - - symbol_scope - base_parser::parent_scope_info::parent_scope () const - { - return size () > 1 ? m_info[size()-2].first : symbol_scope::invalid (); - } - - std::string - base_parser::parent_scope_info::parent_name () const - { - return m_info[size()-2].second; - } - - void base_parser::parent_scope_info::clear () - { - m_info.clear (); - m_all_names.clear (); - } - - base_parser::base_parser (base_lexer& lxr) - : m_endfunction_found (false), m_autoloading (false), - m_fcn_file_from_relative_lookup (false), - m_parsing_subfunctions (false), m_parsing_local_functions (false), - m_max_fcn_depth (-1), m_curr_fcn_depth (-1), - m_primary_fcn_scope (symbol_scope::invalid ()), - m_curr_class_name (), m_curr_package_name (), m_function_scopes (*this), - m_primary_fcn (), m_subfunction_names (), m_classdef_object (), - m_stmt_list (), m_lexer (lxr), m_parser_state (yypstate_new ()) - { } - - base_parser::~base_parser () - { - delete &m_lexer; - - // FIXME: Deleting the internal Bison parser state structure does - // not clean up any partial parse trees in the event of an interrupt or - // error. It's not clear how to safely do that with the C language - // parser that Bison generates. The C++ language parser that Bison - // generates would do it for us automatically whenever an exception - // is thrown while parsing input, but there is currently no C++ - // interface for a push parser. - - yypstate_delete (static_cast<yypstate *> (m_parser_state)); - } - - void - base_parser::reset () - { - m_endfunction_found = false; - m_autoloading = false; - m_fcn_file_from_relative_lookup = false; - m_parsing_subfunctions = false; - m_parsing_local_functions = false; - m_max_fcn_depth = -1; - m_curr_fcn_depth = -1; - m_primary_fcn_scope = symbol_scope::invalid (); - m_curr_class_name = ""; - m_curr_package_name = ""; - m_function_scopes.clear (); - m_primary_fcn = octave_value (); - m_subfunction_names.clear (); - m_classdef_object.reset (); - m_stmt_list.reset (); - - m_lexer.reset (); - - yypstate_delete (static_cast<yypstate *> (m_parser_state)); - m_parser_state = yypstate_new (); - } - - // Error messages for mismatched end tokens. - - static std::string - end_token_as_string (token::end_tok_type ettype) - { - std::string retval = "<unknown>"; - - switch (ettype) - { - case token::simple_end: - retval = "end"; - break; - - case token::classdef_end: - retval = "endclassdef"; - break; - - case token::enumeration_end: - retval = "endenumeration"; - break; - - case token::events_end: - retval = "endevents"; - break; - - case token::for_end: - retval = "endfor"; - break; - - case token::function_end: - retval = "endfunction"; - break; - - case token::if_end: - retval = "endif"; - break; - - case token::methods_end: - retval = "endmethods"; - break; - - case token::parfor_end: - retval = "endparfor"; - break; - - case token::properties_end: - retval = "endproperties"; - break; - - case token::spmd_end: - retval = "endspmd"; - break; - - case token::switch_end: - retval = "endswitch"; - break; - - case token::try_catch_end: - retval = "end_try_catch"; - break; - - case token::unwind_protect_end: - retval = "end_unwind_protect"; - break; - - case token::while_end: - retval = "endwhile"; - break; - - default: - panic_impossible (); - break; - } - - return retval; - } - - void - base_parser::statement_list (std::shared_ptr<tree_statement_list>& lst) - { - if (! lst) - return; - - if (m_stmt_list) - { - // Append additional code to existing statement list. - - while (! lst->empty ()) + for (auto& nm_val : subfcns) { - m_stmt_list->push_back (lst->front ()); - lst->pop_front (); + octave_user_function *subfcn = nm_val.second.user_function_value (); + + if (subfcn) + subfcn->accept (*this); } } - else - m_stmt_list = lst; } - void - base_parser::end_token_error (token *tok, token::end_tok_type expected) - { - std::string msg = ("'" + end_token_as_string (expected) + "' command matched by '" + end_token_as_string (tok->ettype ()) + "'"); - - bison_error (msg, tok->beg_pos ()); - } - - // Check to see that end tokens are properly matched. - - bool - base_parser::end_token_ok (token *tok, token::end_tok_type expected) - { - token::end_tok_type ettype = tok->ettype (); - - return ettype == expected || ettype == token::simple_end; - } - - bool - base_parser::push_fcn_symtab () - { - m_curr_fcn_depth++; - - if (m_max_fcn_depth < m_curr_fcn_depth) - m_max_fcn_depth = m_curr_fcn_depth; - - // Will get a real name later. - m_lexer.m_symtab_context.push (symbol_scope ("parser:push_fcn_symtab")); - m_function_scopes.push (m_lexer.m_symtab_context.curr_scope ()); - - if (! m_lexer.m_reading_script_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) - { - m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope (); - m_primary_fcn_scope.mark_primary_fcn_scope (); - } - - if (m_lexer.m_reading_script_file && m_curr_fcn_depth > 0) - { - bison_error ("nested functions not implemented in this context"); - - return false; - } - - return true; - } - - // Make a constant. - - tree_constant * - base_parser::make_constant (token *tok) - { - int op = tok->token_id (); - - tree_constant *retval = nullptr; - - switch (op) - { - case ':': - retval = new tree_constant (octave_value (octave_value::magic_colon_t), *tok); - break; - - case NUMBER: - retval = new tree_constant (tok->number (), tok->text_rep (), *tok); - break; - - case DQ_STRING: - case SQ_STRING: - { - std::string txt = tok->text (); - - char delim = op == DQ_STRING ? '"' : '\''; - octave_value tmp (txt, delim); - - if (txt.empty ()) - { - if (op == DQ_STRING) - tmp = octave_null_str::instance; - else - tmp = octave_null_sq_str::instance; - } - - if (op == DQ_STRING) - txt = undo_string_escapes (txt); - - // FIXME: maybe the addition of delims should be handled by - // tok->text_rep () for character strings? - - retval = new tree_constant (tmp, delim + txt + delim, *tok); - } - break; - - default: - panic_impossible (); - break; - } - - return retval; - } - - tree_black_hole * - base_parser::make_black_hole (token *tilde) - { - return new tree_black_hole (*tilde); - } - - // Make a function handle. - - tree_fcn_handle * - base_parser::make_fcn_handle (token *tok) - { - tree_fcn_handle *retval = new tree_fcn_handle (*tok); - - return retval; - } - - // Make an anonymous function handle. - - tree_anon_fcn_handle * - base_parser::make_anon_fcn_handle (token *at_tok, tree_parameter_list *param_list, tree_expression *expr) - { - // FIXME: We need to examine EXPR and issue an error if any - // sub-expression contains an assignment, compound assignment, - // increment, or decrement operator. - - anon_fcn_validator validator (param_list, expr); - - if (! validator.ok ()) - { - delete param_list; - delete expr; - - bison_error (validator.message (), validator.beg_pos ()); - - return nullptr; - } - - symbol_scope fcn_scope = m_lexer.m_symtab_context.curr_scope (); - symbol_scope parent_scope = m_lexer.m_symtab_context.parent_scope (); - - m_lexer.m_symtab_context.pop (); - - expr->set_print_flag (false); - - fcn_scope.mark_static (); - - tree_anon_fcn_handle *retval = new tree_anon_fcn_handle (*at_tok, param_list, expr, fcn_scope, parent_scope); - - std::ostringstream buf; - - tree_print_code tpc (buf); - - retval->accept (tpc); - - std::string file = m_lexer.m_fcn_file_full_name; - if (! file.empty ()) - buf << ": file: " << file; - else if (m_lexer.input_from_terminal ()) - buf << ": *terminal input*"; - else if (m_lexer.input_from_eval_string ()) - buf << ": *eval string*"; - - filepos at_pos = at_tok->beg_pos (); - buf << ": line: " << at_pos.line () << " column: " << at_pos.column (); - - std::string scope_name = buf.str (); - - fcn_scope.cache_name (scope_name); - - // FIXME: Stash the filename. This does not work and produces - // errors when executed. - //retval->stash_file_name (m_lexer.m_fcn_file_name); - - return retval; - } - - // Build a colon expression. - - tree_expression * - base_parser::make_colon_expression (tree_expression *base, token *colon_tok, tree_expression *limit) + void visit_index_expression (tree_index_expression& idx_expr) { - return make_colon_expression (base, colon_tok, nullptr, nullptr, limit); - } - - tree_expression * - base_parser::make_colon_expression (tree_expression *base, token *colon_1_tok, tree_expression *incr, token *colon_2_tok, tree_expression *limit) - { - tree_expression *retval = nullptr; - - if (! base || ! limit) - { - delete base; - delete limit; - delete incr; - - return retval; - } - - token tmp_colon_2_tok = colon_2_tok ? *colon_2_tok : token (); - - tree_colon_expression *expr = new tree_colon_expression (base, *colon_1_tok, incr, tmp_colon_2_tok, limit); - - retval = expr; - - if (base->is_constant () && limit->is_constant () && (! incr || incr->is_constant ())) - { - interpreter& interp = m_lexer.m_interpreter; - - try - { - // If the evaluation generates a warning message, restore - // the previous value of last_warning_message and skip the - // conversion to a constant value. - - error_system& es = interp.get_error_system (); - - unwind_action restore_last_warning_message (&error_system::set_last_warning_message, &es, es.last_warning_message ("")); - - unwind_action restore_discard_warning_messages (&error_system::set_discard_warning_messages, &es, es.discard_warning_messages (true)); - - tree_evaluator& tw = interp.get_evaluator (); - - octave_value tmp = expr->evaluate (tw); - - std::string msg = es.last_warning_message (); - - if (msg.empty ()) - { - std::ostringstream buf; - - tree_print_code tpc (buf); - - expr->accept (tpc); - - std::string orig_text = buf.str (); - - token tok (CONSTANT, tmp, orig_text, expr->beg_pos (), expr->end_pos ()); - - tree_constant *tc_retval = new tree_constant (tmp, orig_text, tok); - - delete expr; - - retval = tc_retval; - } - } - catch (const execution_exception&) - { - interp.recover_from_exception (); - } - } - - return retval; - } - - // Build a binary expression. - - tree_expression * - base_parser::make_binary_op (tree_expression *op1, token *op_tok, tree_expression *op2) - { - octave_value::binary_op t = octave_value::unknown_binary_op; - - switch (op_tok->token_id ()) + if (idx_expr.is_word_list_cmd ()) { - case POW: - t = octave_value::op_pow; - break; - - case EPOW: - t = octave_value::op_el_pow; - break; - - case '+': - t = octave_value::op_add; - break; - - case '-': - t = octave_value::op_sub; - break; - - case '*': - t = octave_value::op_mul; - break; - - case '/': - t = octave_value::op_div; - break; - - case EMUL: - t = octave_value::op_el_mul; - break; - - case EDIV: - t = octave_value::op_el_div; - break; - - case LEFTDIV: - t = octave_value::op_ldiv; - break; - - case ELEFTDIV: - t = octave_value::op_el_ldiv; - break; - - case EXPR_LT: - t = octave_value::op_lt; - break; - - case EXPR_LE: - t = octave_value::op_le; - break; - - case EXPR_EQ: - t = octave_value::op_eq; - break; - - case EXPR_GE: - t = octave_value::op_ge; - break; - - case EXPR_GT: - t = octave_value::op_gt; - break; - - case EXPR_NE: - t = octave_value::op_ne; - break; - - case EXPR_AND: - t = octave_value::op_el_and; - break; - - case EXPR_OR: - t = octave_value::op_el_or; - break; - - default: - panic_impossible (); - break; - } - - return maybe_compound_binary_expression (op1, *op_tok, op2, t); - } - - void - base_parser::maybe_convert_to_braindead_shortcircuit (tree_expression*& expr) - { - if (expr->is_binary_expression ()) - { - tree_binary_expression *binexp = dynamic_cast<tree_binary_expression *> (expr); - - token op_tok = binexp->operator_token (); - - tree_expression *lhs = binexp->lhs (); - tree_expression *rhs = binexp->rhs (); - - maybe_convert_to_braindead_shortcircuit (lhs); - maybe_convert_to_braindead_shortcircuit (rhs); - - // Operands may have changed. - binexp->lhs (lhs); - binexp->rhs (rhs); - - octave_value::binary_op op_type = binexp->op_type (); - if (op_type == octave_value::op_el_and || op_type == octave_value::op_el_or) + std::string sym_nm = idx_expr.name (); + + if (m_scope.is_variable (sym_nm)) { - binexp->preserve_operands (); - - delete expr; - - expr = new tree_braindead_shortcircuit_binary_expression (lhs, op_tok, rhs, op_type); + 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.beg_pos ()); + + m_error_list.push_back (pe); } } } - // Build a boolean expression. - - tree_expression * - base_parser::make_boolean_op (tree_expression *op1, token *op_tok, tree_expression *op2) - { - tree_boolean_expression::type t; - - switch (op_tok->token_id ()) - { - case EXPR_AND_AND: - t = tree_boolean_expression::bool_and; - break; - - case EXPR_OR_OR: - t = tree_boolean_expression::bool_or; - break; - - default: - panic_impossible (); - break; - } - - return new tree_boolean_expression (op1, *op_tok, op2, t); - } - - // Build a prefix expression. - - tree_expression * - base_parser::make_prefix_op (token *op_tok, tree_expression *op1) - { - octave_value::unary_op t = octave_value::unknown_unary_op; - - switch (op_tok->token_id ()) - { - case '~': - case '!': - t = octave_value::op_not; - break; - - case '+': - t = octave_value::op_uplus; - break; - - case '-': - t = octave_value::op_uminus; - break; - - case PLUS_PLUS: - t = octave_value::op_incr; - break; - - case MINUS_MINUS: - t = octave_value::op_decr; - break; - - default: - panic_impossible (); - break; - } - - return new tree_prefix_expression (*op_tok, op1, t); - } - - // Build a postfix expression. - - tree_expression * - base_parser::make_postfix_op (tree_expression *op1, token *op_tok) - { - octave_value::unary_op t = octave_value::unknown_unary_op; - - switch (op_tok->token_id ()) - { - case HERMITIAN: - t = octave_value::op_hermitian; - break; - - case TRANSPOSE: - t = octave_value::op_transpose; - break; - - case PLUS_PLUS: - t = octave_value::op_incr; - break; - - case MINUS_MINUS: - t = octave_value::op_decr; - break; - - default: - panic_impossible (); - break; - } - - return new tree_postfix_expression (op1, *op_tok, t); - } - - // Build an unwind-protect command. - - tree_command * - base_parser::make_unwind_command (token *unwind_tok, tree_statement_list *body, token *cleanup_tok, tree_statement_list *cleanup_stmts, token *end_tok) - { - tree_command *retval = nullptr; - - if (end_token_ok (end_tok, token::unwind_protect_end)) - { - retval = new tree_unwind_protect_command (*unwind_tok, body, *cleanup_tok, cleanup_stmts, *end_tok); - } - else - { - delete body; - delete cleanup_stmts; - - end_token_error (end_tok, token::unwind_protect_end); - } - - return retval; - } - - // Build a try-catch command. - - tree_command * - base_parser::make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, char catch_sep, tree_statement_list *cleanup_stmts, token *end_tok) - { - tree_command *retval = nullptr; - - if (end_token_ok (end_tok, token::try_catch_end)) - { - tree_identifier *id = nullptr; - - // Look for exception ID. Could this be done in the grammar or - // does that create another shift-reduce conflict? - - if (! catch_sep && cleanup_stmts && ! cleanup_stmts->empty ()) - { - tree_statement *stmt = cleanup_stmts->front (); - - if (stmt) - { - tree_expression *expr = stmt->expression (); - - if (expr && expr->is_identifier ()) - { - id = dynamic_cast<tree_identifier *> (expr); - - cleanup_stmts->pop_front (); - - stmt->set_expression (nullptr); - delete stmt; - } - } - } - - token tmp_catch_tok = catch_tok ? *catch_tok : token (); - - retval = new tree_try_catch_command (*try_tok, body, tmp_catch_tok, id, cleanup_stmts, *end_tok); - } - else - { - delete body; - delete cleanup_stmts; - - end_token_error (end_tok, token::try_catch_end); - } - - return retval; - } - - // Build a while command. - - tree_command * - base_parser::make_while_command (token *while_tok, tree_expression *expr, tree_statement_list *body, token *end_tok) - { - tree_command *retval = nullptr; - - maybe_warn_assign_as_truth_value (expr); - - if (end_token_ok (end_tok, token::while_end)) - { - m_lexer.m_looping--; - - retval = new tree_while_command (*while_tok, expr, body, *end_tok); - } - else - { - delete expr; - delete body; - - end_token_error (end_tok, token::while_end); - } - - return retval; - } - - // Build a do-until command. - - tree_command * - base_parser::make_do_until_command (token *do_tok, tree_statement_list *body, token *until_tok, tree_expression *expr) - { - maybe_warn_assign_as_truth_value (expr); - - m_lexer.m_looping--; - - return new tree_do_until_command (*do_tok, body, *until_tok, expr); - } - - // Build a for command. - - tree_command * - base_parser::make_for_command (token *for_tok, token *open_paren, tree_argument_list *lhs, token *eq_tok, tree_expression *expr, token *sep_tok, tree_expression *maxproc, token *close_paren, tree_statement_list *body, token *end_tok) - { - tree_command *retval = nullptr; - - bool parfor = for_tok->token_id () == PARFOR; - - token tmp_open_paren = open_paren ? *open_paren : token (); - token tmp_close_paren = close_paren ? *close_paren : token (); - token tmp_sep_tok = sep_tok ? *sep_tok : token (); - - if (end_token_ok (end_tok, parfor ? token::parfor_end : token::for_end)) - { - expr->mark_as_for_cmd_expr (); - - m_lexer.m_looping--; - - if (lhs->size () == 1) - { - tree_expression *tmp = lhs->remove_front (); - - m_lexer.mark_as_variable (tmp->name ()); - - retval = new tree_simple_for_command (parfor, *for_tok, tmp_open_paren, tmp, *eq_tok, expr, tmp_sep_tok, maxproc, tmp_close_paren, body, *end_tok); - - delete lhs; - } - else if (parfor) - { - delete lhs; - delete expr; - delete maxproc; - delete body; - - bison_error ("invalid syntax for parfor statement"); - } - else - { - m_lexer.mark_as_variables (lhs->variable_names ()); - - retval = new tree_complex_for_command (*for_tok, lhs, *eq_tok, expr, body, *end_tok); - } - } - else - { - delete lhs; - delete expr; - delete maxproc; - delete body; - - end_token_error (end_tok, parfor ? token::parfor_end : token::for_end); - } - - return retval; - } - - // Build a break command. - - tree_command * - base_parser::make_break_command (token *break_tok) - { - if (! m_lexer.m_looping) +private: + + symbol_scope m_scope; + + std::list<parse_exception> m_error_list; +}; + +template <typename LIST_T, typename ELT_T> +static LIST_T * +list_append (LIST_T *list, ELT_T elt) +{ + list->push_back (elt); + return list; +} + +template <typename LIST_T, typename ELT_T> +static LIST_T * +list_append (LIST_T *list, const token& /*sep_tok*/, ELT_T elt) +{ + // FIXME XXX! need to capture SEP_TOK here + list->push_back (elt); + return list; +} + +std::size_t +base_parser::parent_scope_info::size () const +{ + return m_info.size (); +} + +void +base_parser::parent_scope_info::push (const value_type& elt) +{ + m_info.push_back (elt); +} + +void +base_parser::parent_scope_info::push (const symbol_scope& scope) +{ + push (value_type (scope, "")); +} + +void +base_parser::parent_scope_info::pop () +{ + m_info.pop_back (); +} + +bool +base_parser::parent_scope_info::name_ok (const std::string& name) +{ + // Name can't be the same as any parent function or any other + // function we've already seen. We could maintain a complex + // tree structure of names, or we can just store the set of + // full names of all the functions, which must be unique. + + std::string full_name; + + for (std::size_t i = 0; i < size()-1; i++) + { + const value_type& elt = m_info[i]; + + if (name == elt.second) + return false; + + full_name += elt.second + ">"; + } + + full_name += name; + + if (m_all_names.find (full_name) != m_all_names.end ()) + { + // Return false (failure) if we are parsing a subfunction, local + // function, or nested function. Otherwise, it is OK to have a + // duplicate name. + + return ! (m_parser.parsing_subfunctions () || m_parser.parsing_local_functions () || m_parser.curr_fcn_depth () > 0); + } + + m_all_names.insert (full_name); + + return true; +} + +bool +base_parser::parent_scope_info::name_current_scope (const std::string& name) +{ + if (! name_ok (name)) + return false; + + if (size () > 0) + m_info.back().second = name; + + return true; +} + +symbol_scope +base_parser::parent_scope_info::parent_scope () const +{ + return size () > 1 ? m_info[size()-2].first : symbol_scope::invalid (); +} + +std::string +base_parser::parent_scope_info::parent_name () const +{ + return m_info[size()-2].second; +} + +void base_parser::parent_scope_info::clear () +{ + m_info.clear (); + m_all_names.clear (); +} + +base_parser::base_parser (base_lexer& lxr) + : m_endfunction_found (false), m_autoloading (false), + m_fcn_file_from_relative_lookup (false), + m_parsing_subfunctions (false), m_parsing_local_functions (false), + m_max_fcn_depth (-1), m_curr_fcn_depth (-1), + m_primary_fcn_scope (symbol_scope::invalid ()), + m_curr_class_name (), m_curr_package_name (), m_function_scopes (*this), + m_primary_fcn (), m_subfunction_names (), m_classdef_object (), + m_stmt_list (), m_lexer (lxr), m_parser_state (yypstate_new ()) +{ } + +base_parser::~base_parser () +{ + delete &m_lexer; + + // FIXME: Deleting the internal Bison parser state structure does + // not clean up any partial parse trees in the event of an interrupt or + // error. It's not clear how to safely do that with the C language + // parser that Bison generates. The C++ language parser that Bison + // generates would do it for us automatically whenever an exception + // is thrown while parsing input, but there is currently no C++ + // interface for a push parser. + + yypstate_delete (static_cast<yypstate *> (m_parser_state)); +} + +void +base_parser::reset () +{ + m_endfunction_found = false; + m_autoloading = false; + m_fcn_file_from_relative_lookup = false; + m_parsing_subfunctions = false; + m_parsing_local_functions = false; + m_max_fcn_depth = -1; + m_curr_fcn_depth = -1; + m_primary_fcn_scope = symbol_scope::invalid (); + m_curr_class_name = ""; + m_curr_package_name = ""; + m_function_scopes.clear (); + m_primary_fcn = octave_value (); + m_subfunction_names.clear (); + m_classdef_object.reset (); + m_stmt_list.reset (); + + m_lexer.reset (); + + yypstate_delete (static_cast<yypstate *> (m_parser_state)); + m_parser_state = yypstate_new (); +} + +// Error messages for mismatched end tokens. + +static std::string +end_token_as_string (token::end_tok_type ettype) +{ + std::string retval = "<unknown>"; + + switch (ettype) + { + case token::simple_end: + retval = "end"; + break; + + case token::classdef_end: + retval = "endclassdef"; + break; + + case token::enumeration_end: + retval = "endenumeration"; + break; + + case token::events_end: + retval = "endevents"; + break; + + case token::for_end: + retval = "endfor"; + break; + + case token::function_end: + retval = "endfunction"; + break; + + case token::if_end: + retval = "endif"; + break; + + case token::methods_end: + retval = "endmethods"; + break; + + case token::parfor_end: + retval = "endparfor"; + break; + + case token::properties_end: + retval = "endproperties"; + break; + + case token::spmd_end: + retval = "endspmd"; + break; + + case token::switch_end: + retval = "endswitch"; + break; + + case token::try_catch_end: + retval = "end_try_catch"; + break; + + case token::unwind_protect_end: + retval = "end_unwind_protect"; + break; + + case token::while_end: + retval = "endwhile"; + break; + + default: + panic_impossible (); + break; + } + + return retval; +} + +void +base_parser::statement_list (std::shared_ptr<tree_statement_list>& lst) +{ + if (! lst) + return; + + if (m_stmt_list) + { + // Append additional code to existing statement list. + + while (! lst->empty ()) + { + m_stmt_list->push_back (lst->front ()); + lst->pop_front (); + } + } + else + m_stmt_list = lst; +} + +void +base_parser::end_token_error (token *tok, token::end_tok_type expected) +{ + std::string msg = ("'" + end_token_as_string (expected) + "' command matched by '" + end_token_as_string (tok->ettype ()) + "'"); + + bison_error (msg, tok->beg_pos ()); +} + +// Check to see that end tokens are properly matched. + +bool +base_parser::end_token_ok (token *tok, token::end_tok_type expected) +{ + token::end_tok_type ettype = tok->ettype (); + + return ettype == expected || ettype == token::simple_end; +} + +bool +base_parser::push_fcn_symtab () +{ + m_curr_fcn_depth++; + + if (m_max_fcn_depth < m_curr_fcn_depth) + m_max_fcn_depth = m_curr_fcn_depth; + + // Will get a real name later. + m_lexer.m_symtab_context.push (symbol_scope ("parser:push_fcn_symtab")); + m_function_scopes.push (m_lexer.m_symtab_context.curr_scope ()); + + if (! m_lexer.m_reading_script_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) + { + m_primary_fcn_scope = m_lexer.m_symtab_context.curr_scope (); + m_primary_fcn_scope.mark_primary_fcn_scope (); + } + + if (m_lexer.m_reading_script_file && m_curr_fcn_depth > 0) + { + bison_error ("nested functions not implemented in this context"); + + return false; + } + + return true; +} + +// Make a constant. + +tree_constant * +base_parser::make_constant (token *tok) +{ + int op = tok->token_id (); + + tree_constant *retval = nullptr; + + switch (op) + { + case ':': + retval = new tree_constant (octave_value (octave_value::magic_colon_t), *tok); + break; + + case NUMBER: + retval = new tree_constant (tok->number (), tok->text_rep (), *tok); + break; + + case DQ_STRING: + case SQ_STRING: { - bison_error ("break must appear within a loop"); - return nullptr; - } - else - return new tree_break_command (*break_tok); - } - - // Build a continue command. - - tree_command * - base_parser::make_continue_command (token *continue_tok) - { - if (! m_lexer.m_looping) - { - bison_error ("continue must appear within a loop"); - return nullptr; - } - else - return new tree_continue_command (*continue_tok); - } - - // Build a return command. - - tree_command * - base_parser::make_return_command (token *return_tok) - { - return new tree_return_command (*return_tok); - } - - // Build an spmd command. - - tree_spmd_command * - base_parser::make_spmd_command (token *spmd_tok, tree_statement_list *body, token *end_tok) - { - tree_spmd_command *retval = nullptr; - - if (end_token_ok (end_tok, token::spmd_end)) - retval = new tree_spmd_command (*spmd_tok, body, *end_tok); - else - { - delete body; - - end_token_error (end_tok, token::spmd_end); - } - - return retval; - } - - // Start an if command. - - tree_if_command_list * - base_parser::start_if_command (tree_if_clause *clause) - { - return new tree_if_command_list (clause); - } - - // Finish an if command. - - tree_if_command * - base_parser::finish_if_command (tree_if_command_list *list, tree_if_clause *else_clause, token *end_tok) - { - tree_if_command *retval = nullptr; - - if (end_token_ok (end_tok, token::if_end)) - { - if (else_clause) - list_append (list, else_clause); - - token if_tok = list->if_token (); - - retval = new tree_if_command (if_tok, list, *end_tok); - } - else - { - delete list; - delete else_clause; - - end_token_error (end_tok, token::if_end); - } - - return retval; - } - - // Build an if, elseif, or else clause. - - tree_if_clause * - base_parser::make_if_clause (token *tok, tree_expression *expr, tree_statement_list *list) - { - if (expr) - { - maybe_warn_assign_as_truth_value (expr); - - maybe_convert_to_braindead_shortcircuit (expr); - } - - return new tree_if_clause (*tok, expr, list); - } - - tree_if_command_list * - base_parser::append_if_clause (tree_if_command_list *list, tree_if_clause *clause) - { - return list_append (list, clause); - } - - // Finish a switch command. - - tree_switch_command * - base_parser::finish_switch_command (token *switch_tok, tree_expression *expr, tree_switch_case_list *list, token *end_tok) - { - tree_switch_command *retval = nullptr; - - if (end_token_ok (end_tok, token::switch_end)) - retval = new tree_switch_command (*switch_tok, expr, list, *end_tok); - else - { - delete expr; - delete list; - - end_token_error (end_tok, token::switch_end); - } - - return retval; - } - - tree_switch_case_list * - base_parser::make_switch_case_list (tree_switch_case *switch_case) - { - return new tree_switch_case_list (switch_case); - } - - // Build a switch case. - - tree_switch_case * - base_parser::make_switch_case (token *case_tok, tree_expression *expr, tree_statement_list *list) - { - maybe_warn_variable_switch_label (expr); - - return new tree_switch_case (*case_tok, expr, list); - } - - tree_switch_case * - base_parser::make_default_switch_case (token *default_tok, tree_statement_list *list) - { - return new tree_switch_case (*default_tok, list); - } - - tree_switch_case_list * - base_parser::append_switch_case (tree_switch_case_list *list, tree_switch_case *elt) - { - return list_append (list, elt); - } - - // Build an assignment to a variable. - - tree_expression * - base_parser::make_assign_op (tree_argument_list *lhs, token *eq_tok, tree_expression *rhs) - { - octave_value::assign_op t = octave_value::unknown_assign_op; - - switch (eq_tok->token_id ()) - { - case '=': - t = octave_value::op_asn_eq; - break; - - case ADD_EQ: - t = octave_value::op_add_eq; - break; - - case SUB_EQ: - t = octave_value::op_sub_eq; - break; - - case MUL_EQ: - t = octave_value::op_mul_eq; - break; - - case DIV_EQ: - t = octave_value::op_div_eq; - break; - - case LEFTDIV_EQ: - t = octave_value::op_ldiv_eq; - break; - - case POW_EQ: - t = octave_value::op_pow_eq; - break; - - case EMUL_EQ: - t = octave_value::op_el_mul_eq; - break; - - case EDIV_EQ: - t = octave_value::op_el_div_eq; - break; - - case ELEFTDIV_EQ: - t = octave_value::op_el_ldiv_eq; - break; - - case EPOW_EQ: - t = octave_value::op_el_pow_eq; - break; - - case AND_EQ: - t = octave_value::op_el_and_eq; - break; - - case OR_EQ: - t = octave_value::op_el_or_eq; - break; - - default: - panic_impossible (); - break; - } - - if (! lhs->is_simple_assign_lhs () && t != octave_value::op_asn_eq) - { - // Multiple assignments like [x,y] OP= rhs are only valid for - // '=', not '+=', etc. - - delete lhs; - delete rhs; - - bison_error ("computed multiple assignment not allowed", eq_tok->beg_pos ()); - - return nullptr; - } - - if (lhs->is_simple_assign_lhs ()) - { - // We are looking at a simple assignment statement like x = rhs; - - tree_expression *tmp = lhs->remove_front (); - - if ((tmp->is_identifier () || tmp->is_index_expression ()) && iskeyword (tmp->name ())) + std::string txt = tok->text (); + + char delim = op == DQ_STRING ? '"' : '\''; + octave_value tmp (txt, delim); + + if (txt.empty ()) { - std::string kw = tmp->name (); - - delete tmp; - delete lhs; - delete rhs; - - bison_error ("invalid assignment to keyword \"" + kw + "\"", eq_tok->beg_pos ()); - - return nullptr; - } - - delete lhs; - - m_lexer.mark_as_variable (tmp->name ()); - - return new tree_simple_assignment (tmp, rhs, false, t); - } - else - { - std::list<std::string> names = lhs->variable_names (); - - for (const auto& kw : names) - { - if (iskeyword (kw)) - { - delete lhs; - delete rhs; - - bison_error ("invalid assignment to keyword \"" + kw + "\"", eq_tok->beg_pos ()); - - return nullptr; - } + if (op == DQ_STRING) + tmp = octave_null_str::instance; + else + tmp = octave_null_sq_str::instance; } - m_lexer.mark_as_variables (names); - - return new tree_multi_assignment (lhs, rhs, false); - } - } - - void - base_parser::make_script (tree_statement_list *cmds, tree_statement *end_script) - { - // Any comments at the beginning of a script file should be - // attached to the first statement in the file or the END_SCRIPT - // statement created by the parser. - - if (! cmds) - cmds = new tree_statement_list (); - - cmds->push_back (end_script); - - symbol_scope script_scope = m_lexer.m_symtab_context.curr_scope (); - - script_scope.cache_name (m_lexer.m_fcn_file_full_name); - script_scope.cache_fcn_file_name (m_lexer.m_fcn_file_full_name); - script_scope.cache_dir_name (m_lexer.m_dir_name); - - // First non-copyright comment in classdef body, before first - // properties, methods, etc. block. - - comment_list leading_comments = cmds->leading_comments (); - - std::string doc_string = leading_comments.find_doc_string (); - - octave_user_script *script = new octave_user_script (m_lexer.m_fcn_file_full_name, m_lexer.m_fcn_file_name, script_scope, cmds, doc_string); - - m_lexer.m_symtab_context.pop (); - - sys::time now; - - script->stash_fcn_file_time (now); - script->stash_dir_name (m_lexer.m_dir_name); - - m_primary_fcn = octave_value (script); - } - - tree_identifier * - base_parser::make_fcn_name (tree_identifier *id) - { - std::string id_name = id->name (); - - // Make classdef local functions unique from classdef methods. - - if (m_parsing_local_functions && m_curr_fcn_depth == 0) - id_name = m_lexer.m_fcn_file_name + ">" + id_name; - - if (! m_function_scopes.name_current_scope (id_name)) - { - // FIXME: is this correct? Before using position, the column - // was incremented. Hmm. - - filepos id_pos = id->beg_pos (); - id_pos.increment_column (); - - bison_error ("duplicate subfunction or nested function name", id_pos); - - delete id; - return nullptr; - } - - symbol_scope curr_scope = m_lexer.m_symtab_context.curr_scope (); - curr_scope.cache_name (id_name); - - m_lexer.m_parsed_function_name.top () = true; - m_lexer.m_maybe_classdef_get_set_method = false; - - return id; - } - - // Define a function. - - // FIXME: combining start_function, finish_function, and - // recover_from_parsing_function should be possible, but it makes - // for a large mess. Maybe this could be a bit better organized? - - tree_function_def * - base_parser::make_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt) - { - // First non-copyright comments found above and below function keyword. - comment_elt leading_doc_comment; - comment_elt body_doc_comment; - - comment_list lc = fcn_tok->leading_comments (); - - if (! lc.empty ()) - leading_doc_comment = lc.find_doc_comment (); - - if (body) - { - comment_list bc = body->leading_comments (); - - if (! bc.empty ()) - body_doc_comment = bc.find_doc_comment (); - } - else if (end_fcn_stmt) - { - comment_list ec = end_fcn_stmt->leading_comments (); - - if (! ec.empty ()) - body_doc_comment = ec.find_doc_comment (); - } - - // Choose which comment to use for doc string. - - // For ordinary functions, use the first comment that isn't empty. - - // If we are looking at a classdef method and there is a comment - // prior to the function keyword and another after, then - // - // * Choose the one outside the function definition if either of - // the comments use hash '#' characters. This is the preferred - // Octave style. - // - // * Choose the one inside the function definition if both - // comments use percent '%' characters. This is - // Matlab-compatible behavior. - - // FIXME: maybe choose which comment to used by checking whether - // any language extensions are noticed in the entire source file, - // not just in the comments that are candidates to become the - // function doc string. - - std::string doc_string; - - if (leading_doc_comment.empty () - || (m_lexer.m_parsing_classdef && ! body_doc_comment.empty () - && (! (leading_doc_comment.uses_hash_char () || body_doc_comment.uses_hash_char ())))) - doc_string = body_doc_comment.text (); - else - doc_string = leading_doc_comment.text (); - - octave_user_function *tmp_fcn = start_function (id, param_list, body, end_fcn_stmt, doc_string); - - tree_function_def *retval = finish_function (fcn_tok, ret_list, eq_tok, tmp_fcn); - - recover_from_parsing_function (); - - return retval; - } - - // Begin defining a function. - - octave_user_function * - base_parser::start_function (tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt, const std::string& doc_string) - { - // We'll fill in the return list later. - - std::string id_name = id->name (); - - if (m_lexer.m_parsing_classdef_get_method) - id_name.insert (0, "get."); - else if (m_lexer.m_parsing_classdef_set_method) - id_name.insert (0, "set."); - - m_lexer.m_parsing_classdef_get_method = false; - m_lexer.m_parsing_classdef_set_method = false; - - if (! body) - body = new tree_statement_list (); - - body->push_back (end_fcn_stmt); - - octave_user_function *fcn = new octave_user_function (m_lexer.m_symtab_context.curr_scope (), id, param_list, nullptr, body); - - // If input is coming from a file, issue a warning if the name of - // the file does not match the name of the function stated in the - // file. Matlab doesn't provide a diagnostic (it ignores the stated - // name). - if (! m_autoloading && m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) - { - // FIXME: should m_lexer.m_fcn_file_name already be - // preprocessed when we get here? It seems to only be a - // problem with relative filenames. - - std::string nm = m_lexer.m_fcn_file_name; - - std::size_t pos = nm.find_last_of (sys::file_ops::dir_sep_chars ()); - - if (pos != std::string::npos) - nm = m_lexer.m_fcn_file_name.substr (pos+1); - - if (nm != id_name) - { - warning_with_id ("Octave:function-name-clash", "function name '%s' does not agree with function filename '%s'", id_name.c_str (), m_lexer.m_fcn_file_full_name.c_str ()); - - id_name = nm; - } - } - - sys::time now; - - fcn->stash_fcn_file_name (m_lexer.m_fcn_file_full_name); - fcn->stash_fcn_file_time (now); - fcn->stash_dir_name (m_lexer.m_dir_name); - fcn->stash_package_name (m_lexer.m_package_name); - fcn->mark_as_system_fcn_file (); - fcn->stash_function_name (id_name); - - if (m_lexer.m_reading_fcn_file || m_lexer.m_reading_classdef_file || m_autoloading) - { - if (m_fcn_file_from_relative_lookup) - fcn->mark_relative (); - - if (m_lexer.m_parsing_class_method) - { - if (m_lexer.m_parsing_classdef) - { - if (m_curr_class_name == id_name) - fcn->mark_as_classdef_constructor (); - else - fcn->mark_as_classdef_method (); - } - else - { - if (m_curr_class_name == id_name) - fcn->mark_as_legacy_constructor (); - else - fcn->mark_as_legacy_method (); - } - - fcn->stash_dispatch_class (m_curr_class_name); - } - - std::string nm = fcn->fcn_file_name (); - - sys::file_stat fs (nm); - - if (fs && fs.is_newer (now)) - warning_with_id ("Octave:future-time-stamp", "time stamp for '%s' is in the future", nm.c_str ()); - } - else if (! m_lexer.input_from_tmp_history_file () && ! m_lexer.m_force_script && m_lexer.m_reading_script_file && m_lexer.m_fcn_file_name == id_name) - warning ("function '%s' defined within script file '%s'", id_name.c_str (), m_lexer.m_fcn_file_full_name.c_str ()); - - // Record doc string for functions other than nested functions. - // We cannot currently record help for nested functions (bug #46008) - // because the doc_string of the outermost function is read first, - // whereas this function is called for the innermost function first. - // We could have a stack of doc_string objects in lexer. - if (! doc_string.empty () && m_curr_fcn_depth == 0) - fcn->document (doc_string); - - - if (m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) - m_primary_fcn = octave_value (fcn); - - return fcn; - } - - tree_statement * - base_parser::make_end (const std::string& type, bool eof, token *end_tok) - { - return make_statement (new tree_no_op_command (type, eof, *end_tok)); - } - - tree_function_def * - base_parser::finish_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, octave_user_function *fcn) - { - tree_function_def *retval = nullptr; - - if (! ret_list) - ret_list = new tree_parameter_list (tree_parameter_list::out); - - ret_list->mark_as_formal_parameters (); - - if (fcn) - { - fcn->set_fcn_tok (*fcn_tok); - - if (eq_tok) - fcn->set_eq_tok (*eq_tok); - - std::string fcn_nm = fcn->name (); - std::string file = fcn->fcn_file_name (); - - std::string tmp = fcn_nm; - if (! file.empty ()) - tmp += ": " + file; - - symbol_scope fcn_scope = fcn->scope (); - fcn_scope.cache_name (tmp); - fcn_scope.cache_fcn_name (fcn_nm); - fcn_scope.cache_fcn_file_name (file); - fcn_scope.cache_dir_name (m_lexer.m_dir_name); - - fcn->define_ret_list (ret_list); - - if (m_curr_fcn_depth > 0 || m_parsing_subfunctions) - { - octave_value ov_fcn (fcn); - - if (m_endfunction_found && m_function_scopes.size () > 1) - { - fcn->mark_as_nested_function (); - fcn_scope.set_nesting_depth (m_curr_fcn_depth); - - symbol_scope pscope = m_function_scopes.parent_scope (); - fcn_scope.set_parent (pscope); - fcn_scope.set_primary_parent (m_primary_fcn_scope); - - pscope.install_nestfunction (fcn_nm, ov_fcn, fcn_scope); - - // For nested functions, the list of parent functions is - // set in symbol_scope::update_nest. - } - else - { - fcn->mark_as_subfunction (); - m_subfunction_names.push_back (fcn_nm); - - fcn_scope.set_parent (m_primary_fcn_scope); - if (m_parsing_subfunctions) - fcn_scope.set_primary_parent (m_primary_fcn_scope); - - m_primary_fcn_scope.install_subfunction (fcn_nm, ov_fcn); - } - } - - if (m_curr_fcn_depth == 0) - fcn_scope.update_nest (); - - if (! m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0) - { - // We are either reading a script file or defining a function - // at the command line, so this definition creates a - // tree_function object that is placed in the parse tree. - // Otherwise, it is just inserted in the symbol table, - // either as a subfunction or nested function (see above), - // or as the primary function for the file, via - // m_primary_fcn (see also load_fcn_from_file,, - // parse_fcn_file, and - // fcn_info::fcn_info_rep::find_user_function). - - if (m_lexer.m_buffer_function_text) - { - fcn->cache_function_text (m_lexer.m_function_text, - fcn->time_parsed ()); - m_lexer.m_buffer_function_text = false; - } - - retval = new tree_function_def (fcn); - } - } - - return retval; - } - - tree_statement_list * - base_parser::append_function_body (tree_statement_list *body, tree_statement_list *list) - { - if (list) - { - for (const auto& elt : *list) - list_append (body, elt); - - list->clear (); - delete (list); - } - - return body; - } - - tree_arguments_block * - base_parser::make_arguments_block (token *arguments_tok, tree_args_block_attribute_list *attr_list, tree_args_block_validation_list *validation_list, token *end_tok) - { - tree_arguments_block *retval = nullptr; - - if (end_token_ok (end_tok, token::arguments_end)) - retval = new tree_arguments_block (*arguments_tok, attr_list, validation_list, *end_tok); - else - { - delete attr_list; - delete validation_list; - } - - return retval; - } - - tree_arg_validation * - base_parser::make_arg_validation (tree_arg_size_spec *size_spec, tree_identifier *class_name, tree_arg_validation_fcns *validation_fcns, token *eq_tok, tree_expression *default_value) - { - // FIXME: Validate arguments and convert to more specific types - // (std::string for arg_name and class_name, etc). - - token tmp_eq_tok = eq_tok ? *eq_tok : token (); - - return new tree_arg_validation (size_spec, class_name, validation_fcns, tmp_eq_tok, default_value); - } - - tree_args_block_attribute_list * - base_parser::make_args_attribute_list (tree_identifier *attribute_name) - { - // FIXME: Validate argument and convert to more specific type - // (std::string for attribute_name). - - return new tree_args_block_attribute_list (attribute_name); - } - - tree_args_block_validation_list * - base_parser::make_args_validation_list (tree_arg_validation *arg_validation) - { - return new tree_args_block_validation_list (arg_validation); - } - - tree_args_block_validation_list * - base_parser::append_args_validation_list (tree_args_block_validation_list *list, tree_arg_validation *arg_validation) - { - return list_append (list, arg_validation); - } - - tree_arg_size_spec * - base_parser::make_arg_size_spec (tree_argument_list *size_args) - { - // FIXME: Validate argument. - - return new tree_arg_size_spec (size_args); - } - - tree_arg_validation_fcns * - base_parser::make_arg_validation_fcns (tree_argument_list *fcn_args) - { - // FIXME: Validate argument. - - return new tree_arg_validation_fcns (fcn_args); - } - - void - base_parser::recover_from_parsing_function () - { - m_lexer.m_symtab_context.pop (); - - if (m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) - m_parsing_subfunctions = true; - - m_curr_fcn_depth--; - m_function_scopes.pop (); - - m_lexer.m_defining_fcn--; - m_lexer.m_parsed_function_name.pop (); - m_lexer.m_looking_at_return_list = false; - m_lexer.m_looking_at_parameter_list = false; - } - - // A CLASSDEF block defines a class that has a constructor and other - // methods, but it is not an executable command. Parsing the block - // makes some changes in the symbol table (inserting the constructor - // and methods, and adding to the list of known objects) and creates - // a parse tree containing meta information about the class. - - tree_classdef * - base_parser::make_classdef (token *cdef_tok, tree_classdef_attribute_list *a, tree_identifier *id, tree_classdef_superclass_list *sc, tree_classdef_body *body, token *end_tok) - { - tree_classdef *retval = nullptr; - - m_lexer.m_symtab_context.pop (); - - std::string cls_name = id->name (); - - std::string full_name = m_lexer.m_fcn_file_full_name; - std::string short_name = m_lexer.m_fcn_file_name; - - std::size_t pos = short_name.find_last_of (sys::file_ops::dir_sep_chars ()); - - if (pos != std::string::npos) - short_name = short_name.substr (pos+1); - - if (short_name != cls_name) - { - delete a; - delete id; - delete sc; - delete body; - - bison_error ("invalid classdef definition, the class name must match the filename", id->beg_pos ()); - - } - else - { - if (end_token_ok (end_tok, token::classdef_end)) - { - if (! body) - body = new tree_classdef_body (); - - retval = new tree_classdef (m_lexer.m_symtab_context.curr_scope (), *cdef_tok, a, id, sc, body, *end_tok, m_curr_package_name, full_name); - } - else - { - delete a; - delete id; - delete sc; - delete body; - - end_token_error (end_tok, token::switch_end); - } - } - - return retval; - } - - tree_classdef_properties_block * - base_parser::make_classdef_properties_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_property_list *plist, token *end_tok) - { - tree_classdef_properties_block *retval = nullptr; - - if (end_token_ok (end_tok, token::properties_end)) - { - if (plist) - { - // If the element at the end of the list doesn't have a doc - // string, see whether the first element of the comments - // attached to the end token is an end-of-line comment for - // us to use. - - tree_classdef_property *last_elt = plist->back (); - - if (last_elt && ! last_elt->have_doc_string ()) - { - comment_list comments = end_tok->leading_comments (); - - if (! comments.empty ()) - { - comment_elt elt = comments.front (); - - if (elt.is_end_of_line ()) - last_elt->doc_string (elt.text ()); - } - } - } - else - plist = new tree_classdef_property_list (); - - retval = new tree_classdef_properties_block (*tok, a, plist, *end_tok); - } - else - { - delete a; - delete plist; - - end_token_error (end_tok, token::properties_end); - } - - return retval; - } - - tree_classdef_property_list * - base_parser::make_classdef_property_list (tree_classdef_property *prop) - { - return new tree_classdef_property_list (prop); - } - - tree_classdef_property * - base_parser::make_classdef_property (tree_identifier *id, tree_arg_validation *av) - { - av->arg_name (id); - - if (av->size_spec () || av->class_name () || av->validation_fcns ()) - warning ("size, class, and validation function specifications are not yet supported for classdef properties; INCORRECT RESULTS ARE POSSIBLE!"); - - return new tree_classdef_property (av); - } - - tree_classdef_methods_block * - base_parser::make_classdef_methods_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_method_list *mlist, token *end_tok) - { - tree_classdef_methods_block *retval = nullptr; - - if (end_token_ok (end_tok, token::methods_end)) - { - if (! mlist) - mlist = new tree_classdef_method_list (); - - retval = new tree_classdef_methods_block (*tok, a, mlist, *end_tok); - } - else - { - delete a; - delete mlist; - - end_token_error (end_tok, token::methods_end); + if (op == DQ_STRING) + txt = undo_string_escapes (txt); + + // FIXME: maybe the addition of delims should be handled by + // tok->text_rep () for character strings? + + retval = new tree_constant (tmp, delim + txt + delim, *tok); } - - return retval; - } - - tree_classdef_events_block * - base_parser::make_classdef_events_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_event_list *elist, token *end_tok) - { - tree_classdef_events_block *retval = nullptr; - - if (end_token_ok (end_tok, token::events_end)) - { - if (! elist) - elist = new tree_classdef_event_list (); - - retval = new tree_classdef_events_block (*tok, a, elist, *end_tok); - } - else - { - delete a; - delete elist; - - end_token_error (end_tok, token::events_end); - } - - return retval; - } - - tree_classdef_event_list * - base_parser::make_classdef_event_list (tree_classdef_event *e) - { - return new tree_classdef_event_list (e); - } - - tree_classdef_event * - base_parser::make_classdef_event (tree_identifier *id) - { - return new tree_classdef_event (id); - } - - tree_classdef_enum_block * - base_parser::make_classdef_enum_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_enum_list *elist, token *end_tok) - { - tree_classdef_enum_block *retval = nullptr; - - if (end_token_ok (end_tok, token::enumeration_end)) - { - if (! elist) - elist = new tree_classdef_enum_list (); - - retval = new tree_classdef_enum_block (*tok, a, elist, *end_tok); - } - else - { - delete a; - delete elist; - - end_token_error (end_tok, token::enumeration_end); - } - - return retval; - } - - tree_classdef_enum_list * - base_parser::make_classdef_enum_list (tree_classdef_enum *e) - { - return new tree_classdef_enum_list (e); - } - - tree_classdef_enum * - base_parser::make_classdef_enum (tree_identifier *id, token *open_paren, tree_expression *expr, token *close_paren) - { - return new tree_classdef_enum (id, *open_paren, expr, *close_paren); - } - - tree_classdef_property_list * - base_parser::append_classdef_property (tree_classdef_property_list *list, - tree_classdef_property *elt) - { - return list_append (list, elt); - } - - tree_classdef_event_list * - base_parser::append_classdef_event (tree_classdef_event_list *list, - tree_classdef_event *elt) - { - return list_append (list, elt); - } - - tree_classdef_enum_list * - base_parser::append_classdef_enum (tree_classdef_enum_list *list, - tree_classdef_enum *elt) - { - return list_append (list, elt); - } - - tree_classdef_superclass_list * - base_parser::make_classdef_superclass_list (token *lt_tok, tree_classdef_superclass *sc) - { - sc->set_separator (*lt_tok); - - return new tree_classdef_superclass_list (sc); - } - - tree_classdef_superclass * - base_parser::make_classdef_superclass (token *fqident) - { - return new tree_classdef_superclass (*fqident); - } - - tree_classdef_superclass_list * - base_parser::append_classdef_superclass (tree_classdef_superclass_list *list, token *and_tok, tree_classdef_superclass *elt) - { - elt->set_separator (*and_tok); - - return list_append (list, elt); - } - - tree_classdef_attribute_list * - base_parser::make_classdef_attribute_list (tree_classdef_attribute *attr) - { - return new tree_classdef_attribute_list (attr); - } - - tree_classdef_attribute * - base_parser::make_classdef_attribute (tree_identifier *id) - { - return make_classdef_attribute (id, nullptr, nullptr); - } - - tree_classdef_attribute * - base_parser::make_classdef_attribute (tree_identifier *id, token *eq_tok, tree_expression *expr) - { - return (expr ? new tree_classdef_attribute (id, *eq_tok, expr) : new tree_classdef_attribute (id)); - } - - tree_classdef_attribute * - base_parser::make_not_classdef_attribute (token *not_tok, tree_identifier *id) - { - return new tree_classdef_attribute (*not_tok, id, false); - } - - tree_classdef_attribute_list * - base_parser::append_classdef_attribute (tree_classdef_attribute_list *list, token *sep_tok, tree_classdef_attribute *elt) - { - return list_append (list, *sep_tok, elt); - } - - tree_classdef_body * - base_parser::make_classdef_body (tree_classdef_properties_block *pb) - { - return new tree_classdef_body (pb); - } - - tree_classdef_body * - base_parser::make_classdef_body (tree_classdef_methods_block *mb) - { - return new tree_classdef_body (mb); - } - - tree_classdef_body * - base_parser::make_classdef_body (tree_classdef_events_block *evb) - { - return new tree_classdef_body (evb); - } - - tree_classdef_body * - base_parser::make_classdef_body (tree_classdef_enum_block *enb) - { - return new tree_classdef_body (enb); - } - - tree_classdef_body * - base_parser::append_classdef_properties_block (tree_classdef_body *body, tree_classdef_properties_block *block) - { - return body->append (block); - } - - tree_classdef_body * - base_parser::append_classdef_methods_block (tree_classdef_body *body, tree_classdef_methods_block *block) - { - return body->append (block); - } - - tree_classdef_body * - base_parser::append_classdef_events_block (tree_classdef_body *body, tree_classdef_events_block *block) - { - return body->append (block); - } - - tree_classdef_body * - base_parser::append_classdef_enum_block (tree_classdef_body *body, tree_classdef_enum_block *block) - { - return body->append (block); - } - - octave_user_function* - base_parser::start_classdef_external_method (tree_identifier *id, tree_parameter_list *pl) - { - octave_user_function* retval = nullptr; - - // External methods are only allowed within @-folders. In this case, - // m_curr_class_name will be non-empty. - - if (! m_curr_class_name.empty ()) - { - std::string mname = id->name (); - - // Methods that cannot be declared outside the classdef file: - // - methods with '.' character (e.g. property accessors) - // - class constructor - // - 'delete' - - if (mname.find_first_of (".") == std::string::npos && mname != "delete" && mname != m_curr_class_name) - { - // Create a dummy function that is used until the real method - // is loaded. - - retval = new octave_user_function (symbol_scope::anonymous (), id, pl); - - retval->stash_function_name (mname); - } - else - bison_error ("invalid external method declaration, an external method cannot be the class constructor, 'delete' or have a dot (.) character in its name"); - } - else - bison_error ("external methods are only allowed in @-folders"); - - return retval; - } - - tree_function_def * - base_parser::finish_classdef_external_method (octave_user_function *fcn, tree_parameter_list *ret_list, token *eq_tok) - { - if (! ret_list) - ret_list = new tree_parameter_list (tree_parameter_list::out); - - fcn->define_ret_list (ret_list); - - if (eq_tok) - fcn->set_eq_tok (*eq_tok); - - return new tree_function_def (fcn); - } - - tree_classdef_method_list * - base_parser::make_classdef_method_list (tree_function_def *fcn_def) - { - octave_value fcn; - - if (fcn_def) + break; + + default: + panic_impossible (); + break; + } + + return retval; +} + +tree_black_hole * +base_parser::make_black_hole (token *tilde) +{ + return new tree_black_hole (*tilde); +} + +// Make a function handle. + +tree_fcn_handle * +base_parser::make_fcn_handle (token *tok) +{ + tree_fcn_handle *retval = new tree_fcn_handle (*tok); + + return retval; +} + +// Make an anonymous function handle. + +tree_anon_fcn_handle * +base_parser::make_anon_fcn_handle (token *at_tok, tree_parameter_list *param_list, tree_expression *expr) +{ + // FIXME: We need to examine EXPR and issue an error if any + // sub-expression contains an assignment, compound assignment, + // increment, or decrement operator. + + anon_fcn_validator validator (param_list, expr); + + if (! validator.ok ()) + { + delete param_list; + delete expr; + + bison_error (validator.message (), validator.beg_pos ()); + + return nullptr; + } + + symbol_scope fcn_scope = m_lexer.m_symtab_context.curr_scope (); + symbol_scope parent_scope = m_lexer.m_symtab_context.parent_scope (); + + m_lexer.m_symtab_context.pop (); + + expr->set_print_flag (false); + + fcn_scope.mark_static (); + + tree_anon_fcn_handle *retval = new tree_anon_fcn_handle (*at_tok, param_list, expr, fcn_scope, parent_scope); + + std::ostringstream buf; + + tree_print_code tpc (buf); + + retval->accept (tpc); + + std::string file = m_lexer.m_fcn_file_full_name; + if (! file.empty ()) + buf << ": file: " << file; + else if (m_lexer.input_from_terminal ()) + buf << ": *terminal input*"; + else if (m_lexer.input_from_eval_string ()) + buf << ": *eval string*"; + + filepos at_pos = at_tok->beg_pos (); + buf << ": line: " << at_pos.line () << " column: " << at_pos.column (); + + std::string scope_name = buf.str (); + + fcn_scope.cache_name (scope_name); + + // FIXME: Stash the filename. This does not work and produces + // errors when executed. + //retval->stash_file_name (m_lexer.m_fcn_file_name); + + return retval; +} + +// Build a colon expression. + +tree_expression * +base_parser::make_colon_expression (tree_expression *base, token *colon_tok, tree_expression *limit) +{ + return make_colon_expression (base, colon_tok, nullptr, nullptr, limit); +} + +tree_expression * +base_parser::make_colon_expression (tree_expression *base, token *colon_1_tok, tree_expression *incr, token *colon_2_tok, tree_expression *limit) +{ + tree_expression *retval = nullptr; + + if (! base || ! limit) + { + delete base; + delete limit; + delete incr; + + return retval; + } + + token tmp_colon_2_tok = colon_2_tok ? *colon_2_tok : token (); + + tree_colon_expression *expr = new tree_colon_expression (base, *colon_1_tok, incr, tmp_colon_2_tok, limit); + + retval = expr; + + if (base->is_constant () && limit->is_constant () && (! incr || incr->is_constant ())) + { + interpreter& interp = m_lexer.m_interpreter; + + try + { + // If the evaluation generates a warning message, restore + // the previous value of last_warning_message and skip the + // conversion to a constant value. + + error_system& es = interp.get_error_system (); + + unwind_action restore_last_warning_message (&error_system::set_last_warning_message, &es, es.last_warning_message ("")); + + unwind_action restore_discard_warning_messages (&error_system::set_discard_warning_messages, &es, es.discard_warning_messages (true)); + + tree_evaluator& tw = interp.get_evaluator (); + + octave_value tmp = expr->evaluate (tw); + + std::string msg = es.last_warning_message (); + + if (msg.empty ()) + { + std::ostringstream buf; + + tree_print_code tpc (buf); + + expr->accept (tpc); + + std::string orig_text = buf.str (); + + token tok (CONSTANT, tmp, orig_text, expr->beg_pos (), expr->end_pos ()); + + tree_constant *tc_retval = new tree_constant (tmp, orig_text, tok); + + delete expr; + + retval = tc_retval; + } + } + catch (const execution_exception&) + { + interp.recover_from_exception (); + } + } + + return retval; +} + +// Build a binary expression. + +tree_expression * +base_parser::make_binary_op (tree_expression *op1, token *op_tok, tree_expression *op2) +{ + octave_value::binary_op t = octave_value::unknown_binary_op; + + switch (op_tok->token_id ()) + { + case POW: + t = octave_value::op_pow; + break; + + case EPOW: + t = octave_value::op_el_pow; + break; + + case '+': + t = octave_value::op_add; + break; + + case '-': + t = octave_value::op_sub; + break; + + case '*': + t = octave_value::op_mul; + break; + + case '/': + t = octave_value::op_div; + break; + + case EMUL: + t = octave_value::op_el_mul; + break; + + case EDIV: + t = octave_value::op_el_div; + break; + + case LEFTDIV: + t = octave_value::op_ldiv; + break; + + case ELEFTDIV: + t = octave_value::op_el_ldiv; + break; + + case EXPR_LT: + t = octave_value::op_lt; + break; + + case EXPR_LE: + t = octave_value::op_le; + break; + + case EXPR_EQ: + t = octave_value::op_eq; + break; + + case EXPR_GE: + t = octave_value::op_ge; + break; + + case EXPR_GT: + t = octave_value::op_gt; + break; + + case EXPR_NE: + t = octave_value::op_ne; + break; + + case EXPR_AND: + t = octave_value::op_el_and; + break; + + case EXPR_OR: + t = octave_value::op_el_or; + break; + + default: + panic_impossible (); + break; + } + + return maybe_compound_binary_expression (op1, *op_tok, op2, t); +} + +void +base_parser::maybe_convert_to_braindead_shortcircuit (tree_expression*& expr) +{ + if (expr->is_binary_expression ()) + { + tree_binary_expression *binexp = dynamic_cast<tree_binary_expression *> (expr); + + token op_tok = binexp->operator_token (); + + tree_expression *lhs = binexp->lhs (); + tree_expression *rhs = binexp->rhs (); + + maybe_convert_to_braindead_shortcircuit (lhs); + maybe_convert_to_braindead_shortcircuit (rhs); + + // Operands may have changed. + binexp->lhs (lhs); + binexp->rhs (rhs); + + octave_value::binary_op op_type = binexp->op_type (); + if (op_type == octave_value::op_el_and || op_type == octave_value::op_el_or) + { + binexp->preserve_operands (); + + delete expr; + + expr = new tree_braindead_shortcircuit_binary_expression (lhs, op_tok, rhs, op_type); + } + } +} + +// Build a boolean expression. + +tree_expression * +base_parser::make_boolean_op (tree_expression *op1, token *op_tok, tree_expression *op2) +{ + tree_boolean_expression::type t; + + switch (op_tok->token_id ()) + { + case EXPR_AND_AND: + t = tree_boolean_expression::bool_and; + break; + + case EXPR_OR_OR: + t = tree_boolean_expression::bool_or; + break; + + default: + panic_impossible (); + break; + } + + return new tree_boolean_expression (op1, *op_tok, op2, t); +} + +// Build a prefix expression. + +tree_expression * +base_parser::make_prefix_op (token *op_tok, tree_expression *op1) +{ + octave_value::unary_op t = octave_value::unknown_unary_op; + + switch (op_tok->token_id ()) + { + case '~': + case '!': + t = octave_value::op_not; + break; + + case '+': + t = octave_value::op_uplus; + break; + + case '-': + t = octave_value::op_uminus; + break; + + case PLUS_PLUS: + t = octave_value::op_incr; + break; + + case MINUS_MINUS: + t = octave_value::op_decr; + break; + + default: + panic_impossible (); + break; + } + + return new tree_prefix_expression (*op_tok, op1, t); +} + +// Build a postfix expression. + +tree_expression * +base_parser::make_postfix_op (tree_expression *op1, token *op_tok) +{ + octave_value::unary_op t = octave_value::unknown_unary_op; + + switch (op_tok->token_id ()) + { + case HERMITIAN: + t = octave_value::op_hermitian; + break; + + case TRANSPOSE: + t = octave_value::op_transpose; + break; + + case PLUS_PLUS: + t = octave_value::op_incr; + break; + + case MINUS_MINUS: + t = octave_value::op_decr; + break; + + default: + panic_impossible (); + break; + } + + return new tree_postfix_expression (op1, *op_tok, t); +} + +// Build an unwind-protect command. + +tree_command * +base_parser::make_unwind_command (token *unwind_tok, tree_statement_list *body, token *cleanup_tok, tree_statement_list *cleanup_stmts, token *end_tok) +{ + tree_command *retval = nullptr; + + if (end_token_ok (end_tok, token::unwind_protect_end)) + { + retval = new tree_unwind_protect_command (*unwind_tok, body, *cleanup_tok, cleanup_stmts, *end_tok); + } + else + { + delete body; + delete cleanup_stmts; + + end_token_error (end_tok, token::unwind_protect_end); + } + + return retval; +} + +// Build a try-catch command. + +tree_command * +base_parser::make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, char catch_sep, tree_statement_list *cleanup_stmts, token *end_tok) +{ + tree_command *retval = nullptr; + + if (end_token_ok (end_tok, token::try_catch_end)) + { + tree_identifier *id = nullptr; + + // Look for exception ID. Could this be done in the grammar or + // does that create another shift-reduce conflict? + + if (! catch_sep && cleanup_stmts && ! cleanup_stmts->empty ()) + { + tree_statement *stmt = cleanup_stmts->front (); + + if (stmt) + { + tree_expression *expr = stmt->expression (); + + if (expr && expr->is_identifier ()) + { + id = dynamic_cast<tree_identifier *> (expr); + + cleanup_stmts->pop_front (); + + stmt->set_expression (nullptr); + delete stmt; + } + } + } + + token tmp_catch_tok = catch_tok ? *catch_tok : token (); + + retval = new tree_try_catch_command (*try_tok, body, tmp_catch_tok, id, cleanup_stmts, *end_tok); + } + else + { + delete body; + delete cleanup_stmts; + + end_token_error (end_tok, token::try_catch_end); + } + + return retval; +} + +// Build a while command. + +tree_command * +base_parser::make_while_command (token *while_tok, tree_expression *expr, tree_statement_list *body, token *end_tok) +{ + tree_command *retval = nullptr; + + maybe_warn_assign_as_truth_value (expr); + + if (end_token_ok (end_tok, token::while_end)) + { + m_lexer.m_looping--; + + retval = new tree_while_command (*while_tok, expr, body, *end_tok); + } + else + { + delete expr; + delete body; + + end_token_error (end_tok, token::while_end); + } + + return retval; +} + +// Build a do-until command. + +tree_command * +base_parser::make_do_until_command (token *do_tok, tree_statement_list *body, token *until_tok, tree_expression *expr) +{ + maybe_warn_assign_as_truth_value (expr); + + m_lexer.m_looping--; + + return new tree_do_until_command (*do_tok, body, *until_tok, expr); +} + +// Build a for command. + +tree_command * +base_parser::make_for_command (token *for_tok, token *open_paren, tree_argument_list *lhs, token *eq_tok, tree_expression *expr, token *sep_tok, tree_expression *maxproc, token *close_paren, tree_statement_list *body, token *end_tok) +{ + tree_command *retval = nullptr; + + bool parfor = for_tok->token_id () == PARFOR; + + token tmp_open_paren = open_paren ? *open_paren : token (); + token tmp_close_paren = close_paren ? *close_paren : token (); + token tmp_sep_tok = sep_tok ? *sep_tok : token (); + + if (end_token_ok (end_tok, parfor ? token::parfor_end : token::for_end)) + { + expr->mark_as_for_cmd_expr (); + + m_lexer.m_looping--; + + if (lhs->size () == 1) + { + tree_expression *tmp = lhs->remove_front (); + + m_lexer.mark_as_variable (tmp->name ()); + + retval = new tree_simple_for_command (parfor, *for_tok, tmp_open_paren, tmp, *eq_tok, expr, tmp_sep_tok, maxproc, tmp_close_paren, body, *end_tok); + + delete lhs; + } + else if (parfor) + { + delete lhs; + delete expr; + delete maxproc; + delete body; + + bison_error ("invalid syntax for parfor statement"); + } + else + { + m_lexer.mark_as_variables (lhs->variable_names ()); + + retval = new tree_complex_for_command (*for_tok, lhs, *eq_tok, expr, body, *end_tok); + } + } + else + { + delete lhs; + delete expr; + delete maxproc; + delete body; + + end_token_error (end_tok, parfor ? token::parfor_end : token::for_end); + } + + return retval; +} + +// Build a break command. + +tree_command * +base_parser::make_break_command (token *break_tok) +{ + if (! m_lexer.m_looping) + { + bison_error ("break must appear within a loop"); + return nullptr; + } + else + return new tree_break_command (*break_tok); +} + +// Build a continue command. + +tree_command * +base_parser::make_continue_command (token *continue_tok) +{ + if (! m_lexer.m_looping) + { + bison_error ("continue must appear within a loop"); + return nullptr; + } + else + return new tree_continue_command (*continue_tok); +} + +// Build a return command. + +tree_command * +base_parser::make_return_command (token *return_tok) +{ + return new tree_return_command (*return_tok); +} + +// Build an spmd command. + +tree_spmd_command * +base_parser::make_spmd_command (token *spmd_tok, tree_statement_list *body, token *end_tok) +{ + tree_spmd_command *retval = nullptr; + + if (end_token_ok (end_tok, token::spmd_end)) + retval = new tree_spmd_command (*spmd_tok, body, *end_tok); + else + { + delete body; + + end_token_error (end_tok, token::spmd_end); + } + + return retval; +} + +// Start an if command. + +tree_if_command_list * +base_parser::start_if_command (tree_if_clause *clause) +{ + return new tree_if_command_list (clause); +} + +// Finish an if command. + +tree_if_command * +base_parser::finish_if_command (tree_if_command_list *list, tree_if_clause *else_clause, token *end_tok) +{ + tree_if_command *retval = nullptr; + + if (end_token_ok (end_tok, token::if_end)) + { + if (else_clause) + list_append (list, else_clause); + + token if_tok = list->if_token (); + + retval = new tree_if_command (if_tok, list, *end_tok); + } + else + { + delete list; + delete else_clause; + + end_token_error (end_tok, token::if_end); + } + + return retval; +} + +// Build an if, elseif, or else clause. + +tree_if_clause * +base_parser::make_if_clause (token *tok, tree_expression *expr, tree_statement_list *list) +{ + if (expr) + { + maybe_warn_assign_as_truth_value (expr); + + maybe_convert_to_braindead_shortcircuit (expr); + } + + return new tree_if_clause (*tok, expr, list); +} + +tree_if_command_list * +base_parser::append_if_clause (tree_if_command_list *list, tree_if_clause *clause) +{ + return list_append (list, clause); +} + +// Finish a switch command. + +tree_switch_command * +base_parser::finish_switch_command (token *switch_tok, tree_expression *expr, tree_switch_case_list *list, token *end_tok) +{ + tree_switch_command *retval = nullptr; + + if (end_token_ok (end_tok, token::switch_end)) + retval = new tree_switch_command (*switch_tok, expr, list, *end_tok); + else + { + delete expr; + delete list; + + end_token_error (end_tok, token::switch_end); + } + + return retval; +} + +tree_switch_case_list * +base_parser::make_switch_case_list (tree_switch_case *switch_case) +{ + return new tree_switch_case_list (switch_case); +} + +// Build a switch case. + +tree_switch_case * +base_parser::make_switch_case (token *case_tok, tree_expression *expr, tree_statement_list *list) +{ + maybe_warn_variable_switch_label (expr); + + return new tree_switch_case (*case_tok, expr, list); +} + +tree_switch_case * +base_parser::make_default_switch_case (token *default_tok, tree_statement_list *list) +{ + return new tree_switch_case (*default_tok, list); +} + +tree_switch_case_list * +base_parser::append_switch_case (tree_switch_case_list *list, tree_switch_case *elt) +{ + return list_append (list, elt); +} + +// Build an assignment to a variable. + +tree_expression * +base_parser::make_assign_op (tree_argument_list *lhs, token *eq_tok, tree_expression *rhs) +{ + octave_value::assign_op t = octave_value::unknown_assign_op; + + switch (eq_tok->token_id ()) + { + case '=': + t = octave_value::op_asn_eq; + break; + + case ADD_EQ: + t = octave_value::op_add_eq; + break; + + case SUB_EQ: + t = octave_value::op_sub_eq; + break; + + case MUL_EQ: + t = octave_value::op_mul_eq; + break; + + case DIV_EQ: + t = octave_value::op_div_eq; + break; + + case LEFTDIV_EQ: + t = octave_value::op_ldiv_eq; + break; + + case POW_EQ: + t = octave_value::op_pow_eq; + break; + + case EMUL_EQ: + t = octave_value::op_el_mul_eq; + break; + + case EDIV_EQ: + t = octave_value::op_el_div_eq; + break; + + case ELEFTDIV_EQ: + t = octave_value::op_el_ldiv_eq; + break; + + case EPOW_EQ: + t = octave_value::op_el_pow_eq; + break; + + case AND_EQ: + t = octave_value::op_el_and_eq; + break; + + case OR_EQ: + t = octave_value::op_el_or_eq; + break; + + default: + panic_impossible (); + break; + } + + if (! lhs->is_simple_assign_lhs () && t != octave_value::op_asn_eq) + { + // Multiple assignments like [x,y] OP= rhs are only valid for + // '=', not '+=', etc. + + delete lhs; + delete rhs; + + bison_error ("computed multiple assignment not allowed", eq_tok->beg_pos ()); + + return nullptr; + } + + if (lhs->is_simple_assign_lhs ()) + { + // We are looking at a simple assignment statement like x = rhs; + + tree_expression *tmp = lhs->remove_front (); + + if ((tmp->is_identifier () || tmp->is_index_expression ()) && iskeyword (tmp->name ())) + { + std::string kw = tmp->name (); + + delete tmp; + delete lhs; + delete rhs; + + bison_error ("invalid assignment to keyword \"" + kw + "\"", eq_tok->beg_pos ()); + + return nullptr; + } + + delete lhs; + + m_lexer.mark_as_variable (tmp->name ()); + + return new tree_simple_assignment (tmp, rhs, false, t); + } + else + { + std::list<std::string> names = lhs->variable_names (); + + for (const auto& kw : names) + { + if (iskeyword (kw)) + { + delete lhs; + delete rhs; + + bison_error ("invalid assignment to keyword \"" + kw + "\"", eq_tok->beg_pos ()); + + return nullptr; + } + } + + m_lexer.mark_as_variables (names); + + return new tree_multi_assignment (lhs, rhs, false); + } +} + +void +base_parser::make_script (tree_statement_list *cmds, tree_statement *end_script) +{ + // Any comments at the beginning of a script file should be + // attached to the first statement in the file or the END_SCRIPT + // statement created by the parser. + + if (! cmds) + cmds = new tree_statement_list (); + + cmds->push_back (end_script); + + symbol_scope script_scope = m_lexer.m_symtab_context.curr_scope (); + + script_scope.cache_name (m_lexer.m_fcn_file_full_name); + script_scope.cache_fcn_file_name (m_lexer.m_fcn_file_full_name); + script_scope.cache_dir_name (m_lexer.m_dir_name); + + // First non-copyright comment in classdef body, before first + // properties, methods, etc. block. + + comment_list leading_comments = cmds->leading_comments (); + + std::string doc_string = leading_comments.find_doc_string (); + + octave_user_script *script = new octave_user_script (m_lexer.m_fcn_file_full_name, m_lexer.m_fcn_file_name, script_scope, cmds, doc_string); + + m_lexer.m_symtab_context.pop (); + + sys::time now; + + script->stash_fcn_file_time (now); + script->stash_dir_name (m_lexer.m_dir_name); + + m_primary_fcn = octave_value (script); +} + +tree_identifier * +base_parser::make_fcn_name (tree_identifier *id) +{ + std::string id_name = id->name (); + + // Make classdef local functions unique from classdef methods. + + if (m_parsing_local_functions && m_curr_fcn_depth == 0) + id_name = m_lexer.m_fcn_file_name + ">" + id_name; + + if (! m_function_scopes.name_current_scope (id_name)) + { + // FIXME: is this correct? Before using position, the column + // was incremented. Hmm. + + filepos id_pos = id->beg_pos (); + id_pos.increment_column (); + + bison_error ("duplicate subfunction or nested function name", id_pos); + + delete id; + return nullptr; + } + + symbol_scope curr_scope = m_lexer.m_symtab_context.curr_scope (); + curr_scope.cache_name (id_name); + + m_lexer.m_parsed_function_name.top () = true; + m_lexer.m_maybe_classdef_get_set_method = false; + + return id; +} + +// Define a function. + +// FIXME: combining start_function, finish_function, and +// recover_from_parsing_function should be possible, but it makes +// for a large mess. Maybe this could be a bit better organized? + +tree_function_def * +base_parser::make_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt) +{ + // First non-copyright comments found above and below function keyword. + comment_elt leading_doc_comment; + comment_elt body_doc_comment; + + comment_list lc = fcn_tok->leading_comments (); + + if (! lc.empty ()) + leading_doc_comment = lc.find_doc_comment (); + + if (body) + { + comment_list bc = body->leading_comments (); + + if (! bc.empty ()) + body_doc_comment = bc.find_doc_comment (); + } + else if (end_fcn_stmt) + { + comment_list ec = end_fcn_stmt->leading_comments (); + + if (! ec.empty ()) + body_doc_comment = ec.find_doc_comment (); + } + + // Choose which comment to use for doc string. + + // For ordinary functions, use the first comment that isn't empty. + + // If we are looking at a classdef method and there is a comment + // prior to the function keyword and another after, then + // + // * Choose the one outside the function definition if either of + // the comments use hash '#' characters. This is the preferred + // Octave style. + // + // * Choose the one inside the function definition if both + // comments use percent '%' characters. This is + // Matlab-compatible behavior. + + // FIXME: maybe choose which comment to used by checking whether + // any language extensions are noticed in the entire source file, + // not just in the comments that are candidates to become the + // function doc string. + + std::string doc_string; + + if (leading_doc_comment.empty () + || (m_lexer.m_parsing_classdef && ! body_doc_comment.empty () + && (! (leading_doc_comment.uses_hash_char () || body_doc_comment.uses_hash_char ())))) + doc_string = body_doc_comment.text (); + else + doc_string = leading_doc_comment.text (); + + octave_user_function *tmp_fcn = start_function (id, param_list, body, end_fcn_stmt, doc_string); + + tree_function_def *retval = finish_function (fcn_tok, ret_list, eq_tok, tmp_fcn); + + recover_from_parsing_function (); + + return retval; +} + +// Begin defining a function. + +octave_user_function * +base_parser::start_function (tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt, const std::string& doc_string) +{ + // We'll fill in the return list later. + + std::string id_name = id->name (); + + if (m_lexer.m_parsing_classdef_get_method) + id_name.insert (0, "get."); + else if (m_lexer.m_parsing_classdef_set_method) + id_name.insert (0, "set."); + + m_lexer.m_parsing_classdef_get_method = false; + m_lexer.m_parsing_classdef_set_method = false; + + if (! body) + body = new tree_statement_list (); + + body->push_back (end_fcn_stmt); + + octave_user_function *fcn = new octave_user_function (m_lexer.m_symtab_context.curr_scope (), id, param_list, nullptr, body); + + // If input is coming from a file, issue a warning if the name of + // the file does not match the name of the function stated in the + // file. Matlab doesn't provide a diagnostic (it ignores the stated + // name). + if (! m_autoloading && m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) + { + // FIXME: should m_lexer.m_fcn_file_name already be + // preprocessed when we get here? It seems to only be a + // problem with relative filenames. + + std::string nm = m_lexer.m_fcn_file_name; + + std::size_t pos = nm.find_last_of (sys::file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + nm = m_lexer.m_fcn_file_name.substr (pos+1); + + if (nm != id_name) + { + warning_with_id ("Octave:function-name-clash", "function name '%s' does not agree with function filename '%s'", id_name.c_str (), m_lexer.m_fcn_file_full_name.c_str ()); + + id_name = nm; + } + } + + sys::time now; + + fcn->stash_fcn_file_name (m_lexer.m_fcn_file_full_name); + fcn->stash_fcn_file_time (now); + fcn->stash_dir_name (m_lexer.m_dir_name); + fcn->stash_package_name (m_lexer.m_package_name); + fcn->mark_as_system_fcn_file (); + fcn->stash_function_name (id_name); + + if (m_lexer.m_reading_fcn_file || m_lexer.m_reading_classdef_file || m_autoloading) + { + if (m_fcn_file_from_relative_lookup) + fcn->mark_relative (); + + if (m_lexer.m_parsing_class_method) + { + if (m_lexer.m_parsing_classdef) + { + if (m_curr_class_name == id_name) + fcn->mark_as_classdef_constructor (); + else + fcn->mark_as_classdef_method (); + } + else + { + if (m_curr_class_name == id_name) + fcn->mark_as_legacy_constructor (); + else + fcn->mark_as_legacy_method (); + } + + fcn->stash_dispatch_class (m_curr_class_name); + } + + std::string nm = fcn->fcn_file_name (); + + sys::file_stat fs (nm); + + if (fs && fs.is_newer (now)) + warning_with_id ("Octave:future-time-stamp", "time stamp for '%s' is in the future", nm.c_str ()); + } + else if (! m_lexer.input_from_tmp_history_file () && ! m_lexer.m_force_script && m_lexer.m_reading_script_file && m_lexer.m_fcn_file_name == id_name) + warning ("function '%s' defined within script file '%s'", id_name.c_str (), m_lexer.m_fcn_file_full_name.c_str ()); + + // Record doc string for functions other than nested functions. + // We cannot currently record help for nested functions (bug #46008) + // because the doc_string of the outermost function is read first, + // whereas this function is called for the innermost function first. + // We could have a stack of doc_string objects in lexer. + if (! doc_string.empty () && m_curr_fcn_depth == 0) + fcn->document (doc_string); + + + if (m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) + m_primary_fcn = octave_value (fcn); + + return fcn; +} + +tree_statement * +base_parser::make_end (const std::string& type, bool eof, token *end_tok) +{ + return make_statement (new tree_no_op_command (type, eof, *end_tok)); +} + +tree_function_def * +base_parser::finish_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, octave_user_function *fcn) +{ + tree_function_def *retval = nullptr; + + if (! ret_list) + ret_list = new tree_parameter_list (tree_parameter_list::out); + + ret_list->mark_as_formal_parameters (); + + if (fcn) + { + fcn->set_fcn_tok (*fcn_tok); + + if (eq_tok) + fcn->set_eq_tok (*eq_tok); + + std::string fcn_nm = fcn->name (); + std::string file = fcn->fcn_file_name (); + + std::string tmp = fcn_nm; + if (! file.empty ()) + tmp += ": " + file; + + symbol_scope fcn_scope = fcn->scope (); + fcn_scope.cache_name (tmp); + fcn_scope.cache_fcn_name (fcn_nm); + fcn_scope.cache_fcn_file_name (file); + fcn_scope.cache_dir_name (m_lexer.m_dir_name); + + fcn->define_ret_list (ret_list); + + if (m_curr_fcn_depth > 0 || m_parsing_subfunctions) + { + octave_value ov_fcn (fcn); + + if (m_endfunction_found && m_function_scopes.size () > 1) + { + fcn->mark_as_nested_function (); + fcn_scope.set_nesting_depth (m_curr_fcn_depth); + + symbol_scope pscope = m_function_scopes.parent_scope (); + fcn_scope.set_parent (pscope); + fcn_scope.set_primary_parent (m_primary_fcn_scope); + + pscope.install_nestfunction (fcn_nm, ov_fcn, fcn_scope); + + // For nested functions, the list of parent functions is + // set in symbol_scope::update_nest. + } + else + { + fcn->mark_as_subfunction (); + m_subfunction_names.push_back (fcn_nm); + + fcn_scope.set_parent (m_primary_fcn_scope); + if (m_parsing_subfunctions) + fcn_scope.set_primary_parent (m_primary_fcn_scope); + + m_primary_fcn_scope.install_subfunction (fcn_nm, ov_fcn); + } + } + + if (m_curr_fcn_depth == 0) + fcn_scope.update_nest (); + + if (! m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0) + { + // We are either reading a script file or defining a function + // at the command line, so this definition creates a + // tree_function object that is placed in the parse tree. + // Otherwise, it is just inserted in the symbol table, + // either as a subfunction or nested function (see above), + // or as the primary function for the file, via + // m_primary_fcn (see also load_fcn_from_file,, + // parse_fcn_file, and + // fcn_info::fcn_info_rep::find_user_function). + + if (m_lexer.m_buffer_function_text) + { + fcn->cache_function_text (m_lexer.m_function_text, fcn->time_parsed ()); + m_lexer.m_buffer_function_text = false; + } + + retval = new tree_function_def (fcn); + } + } + + return retval; +} + +tree_statement_list * +base_parser::append_function_body (tree_statement_list *body, tree_statement_list *list) +{ + if (list) + { + for (const auto& elt : *list) + list_append (body, elt); + + list->clear (); + delete (list); + } + + return body; +} + +tree_arguments_block * +base_parser::make_arguments_block (token *arguments_tok, tree_args_block_attribute_list *attr_list, tree_args_block_validation_list *validation_list, token *end_tok) +{ + tree_arguments_block *retval = nullptr; + + if (end_token_ok (end_tok, token::arguments_end)) + retval = new tree_arguments_block (*arguments_tok, attr_list, validation_list, *end_tok); + else + { + delete attr_list; + delete validation_list; + } + + return retval; +} + +tree_arg_validation * +base_parser::make_arg_validation (tree_arg_size_spec *size_spec, tree_identifier *class_name, tree_arg_validation_fcns *validation_fcns, token *eq_tok, tree_expression *default_value) +{ + // FIXME: Validate arguments and convert to more specific types + // (std::string for arg_name and class_name, etc). + + token tmp_eq_tok = eq_tok ? *eq_tok : token (); + + return new tree_arg_validation (size_spec, class_name, validation_fcns, tmp_eq_tok, default_value); +} + +tree_args_block_attribute_list * +base_parser::make_args_attribute_list (tree_identifier *attribute_name) +{ + // FIXME: Validate argument and convert to more specific type + // (std::string for attribute_name). + + return new tree_args_block_attribute_list (attribute_name); +} + +tree_args_block_validation_list * +base_parser::make_args_validation_list (tree_arg_validation *arg_validation) +{ + return new tree_args_block_validation_list (arg_validation); +} + +tree_args_block_validation_list * +base_parser::append_args_validation_list (tree_args_block_validation_list *list, tree_arg_validation *arg_validation) +{ + return list_append (list, arg_validation); +} + +tree_arg_size_spec * +base_parser::make_arg_size_spec (tree_argument_list *size_args) +{ + // FIXME: Validate argument. + + return new tree_arg_size_spec (size_args); +} + +tree_arg_validation_fcns * +base_parser::make_arg_validation_fcns (tree_argument_list *fcn_args) +{ + // FIXME: Validate argument. + + return new tree_arg_validation_fcns (fcn_args); +} + +void +base_parser::recover_from_parsing_function () +{ + m_lexer.m_symtab_context.pop (); + + if (m_lexer.m_reading_fcn_file && m_curr_fcn_depth == 0 && ! m_parsing_subfunctions) + m_parsing_subfunctions = true; + + m_curr_fcn_depth--; + m_function_scopes.pop (); + + m_lexer.m_defining_fcn--; + m_lexer.m_parsed_function_name.pop (); + m_lexer.m_looking_at_return_list = false; + m_lexer.m_looking_at_parameter_list = false; +} + +// A CLASSDEF block defines a class that has a constructor and other +// methods, but it is not an executable command. Parsing the block +// makes some changes in the symbol table (inserting the constructor +// and methods, and adding to the list of known objects) and creates +// a parse tree containing meta information about the class. + +tree_classdef * +base_parser::make_classdef (token *cdef_tok, tree_classdef_attribute_list *a, tree_identifier *id, tree_classdef_superclass_list *sc, tree_classdef_body *body, token *end_tok) +{ + tree_classdef *retval = nullptr; + + m_lexer.m_symtab_context.pop (); + + std::string cls_name = id->name (); + + std::string full_name = m_lexer.m_fcn_file_full_name; + std::string short_name = m_lexer.m_fcn_file_name; + + std::size_t pos = short_name.find_last_of (sys::file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + short_name = short_name.substr (pos+1); + + if (short_name != cls_name) + { + delete a; + delete id; + delete sc; + delete body; + + bison_error ("invalid classdef definition, the class name must match the filename", id->beg_pos ()); + + } + else + { + if (end_token_ok (end_tok, token::classdef_end)) + { + if (! body) + body = new tree_classdef_body (); + + retval = new tree_classdef (m_lexer.m_symtab_context.curr_scope (), *cdef_tok, a, id, sc, body, *end_tok, m_curr_package_name, full_name); + } + else + { + delete a; + delete id; + delete sc; + delete body; + + end_token_error (end_tok, token::switch_end); + } + } + + return retval; +} + +tree_classdef_properties_block * +base_parser::make_classdef_properties_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_property_list *plist, token *end_tok) +{ + tree_classdef_properties_block *retval = nullptr; + + if (end_token_ok (end_tok, token::properties_end)) + { + if (plist) + { + // If the element at the end of the list doesn't have a doc + // string, see whether the first element of the comments + // attached to the end token is an end-of-line comment for + // us to use. + + tree_classdef_property *last_elt = plist->back (); + + if (last_elt && ! last_elt->have_doc_string ()) + { + comment_list comments = end_tok->leading_comments (); + + if (! comments.empty ()) + { + comment_elt elt = comments.front (); + + if (elt.is_end_of_line ()) + last_elt->doc_string (elt.text ()); + } + } + } + else + plist = new tree_classdef_property_list (); + + retval = new tree_classdef_properties_block (*tok, a, plist, *end_tok); + } + else + { + delete a; + delete plist; + + end_token_error (end_tok, token::properties_end); + } + + return retval; +} + +tree_classdef_property_list * +base_parser::make_classdef_property_list (tree_classdef_property *prop) +{ + return new tree_classdef_property_list (prop); +} + +tree_classdef_property * +base_parser::make_classdef_property (tree_identifier *id, tree_arg_validation *av) +{ + av->arg_name (id); + + if (av->size_spec () || av->class_name () || av->validation_fcns ()) + warning ("size, class, and validation function specifications are not yet supported for classdef properties; INCORRECT RESULTS ARE POSSIBLE!"); + + return new tree_classdef_property (av); +} + +tree_classdef_methods_block * +base_parser::make_classdef_methods_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_method_list *mlist, token *end_tok) +{ + tree_classdef_methods_block *retval = nullptr; + + if (end_token_ok (end_tok, token::methods_end)) + { + if (! mlist) + mlist = new tree_classdef_method_list (); + + retval = new tree_classdef_methods_block (*tok, a, mlist, *end_tok); + } + else + { + delete a; + delete mlist; + + end_token_error (end_tok, token::methods_end); + } + + return retval; +} + +tree_classdef_events_block * +base_parser::make_classdef_events_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_event_list *elist, token *end_tok) +{ + tree_classdef_events_block *retval = nullptr; + + if (end_token_ok (end_tok, token::events_end)) + { + if (! elist) + elist = new tree_classdef_event_list (); + + retval = new tree_classdef_events_block (*tok, a, elist, *end_tok); + } + else + { + delete a; + delete elist; + + end_token_error (end_tok, token::events_end); + } + + return retval; +} + +tree_classdef_event_list * +base_parser::make_classdef_event_list (tree_classdef_event *e) +{ + return new tree_classdef_event_list (e); +} + +tree_classdef_event * +base_parser::make_classdef_event (tree_identifier *id) +{ + return new tree_classdef_event (id); +} + +tree_classdef_enum_block * +base_parser::make_classdef_enum_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_enum_list *elist, token *end_tok) +{ + tree_classdef_enum_block *retval = nullptr; + + if (end_token_ok (end_tok, token::enumeration_end)) + { + if (! elist) + elist = new tree_classdef_enum_list (); + + retval = new tree_classdef_enum_block (*tok, a, elist, *end_tok); + } + else + { + delete a; + delete elist; + + end_token_error (end_tok, token::enumeration_end); + } + + return retval; +} + +tree_classdef_enum_list * +base_parser::make_classdef_enum_list (tree_classdef_enum *e) +{ + return new tree_classdef_enum_list (e); +} + +tree_classdef_enum * +base_parser::make_classdef_enum (tree_identifier *id, token *open_paren, tree_expression *expr, token *close_paren) +{ + return new tree_classdef_enum (id, *open_paren, expr, *close_paren); +} + +tree_classdef_property_list * +base_parser::append_classdef_property (tree_classdef_property_list *list, tree_classdef_property *elt) +{ + return list_append (list, elt); +} + +tree_classdef_event_list * +base_parser::append_classdef_event (tree_classdef_event_list *list, tree_classdef_event *elt) +{ + return list_append (list, elt); +} + +tree_classdef_enum_list * +base_parser::append_classdef_enum (tree_classdef_enum_list *list, tree_classdef_enum *elt) +{ + return list_append (list, elt); +} + +tree_classdef_superclass_list * +base_parser::make_classdef_superclass_list (token *lt_tok, tree_classdef_superclass *sc) +{ + sc->set_separator (*lt_tok); + + return new tree_classdef_superclass_list (sc); +} + +tree_classdef_superclass * +base_parser::make_classdef_superclass (token *fqident) +{ + return new tree_classdef_superclass (*fqident); +} + +tree_classdef_superclass_list * +base_parser::append_classdef_superclass (tree_classdef_superclass_list *list, token *and_tok, tree_classdef_superclass *elt) +{ + elt->set_separator (*and_tok); + + return list_append (list, elt); +} + +tree_classdef_attribute_list * +base_parser::make_classdef_attribute_list (tree_classdef_attribute *attr) +{ + return new tree_classdef_attribute_list (attr); +} + +tree_classdef_attribute * +base_parser::make_classdef_attribute (tree_identifier *id) +{ + return make_classdef_attribute (id, nullptr, nullptr); +} + +tree_classdef_attribute * +base_parser::make_classdef_attribute (tree_identifier *id, token *eq_tok, tree_expression *expr) +{ + return (expr ? new tree_classdef_attribute (id, *eq_tok, expr) : new tree_classdef_attribute (id)); +} + +tree_classdef_attribute * +base_parser::make_not_classdef_attribute (token *not_tok, tree_identifier *id) +{ + return new tree_classdef_attribute (*not_tok, id, false); +} + +tree_classdef_attribute_list * +base_parser::append_classdef_attribute (tree_classdef_attribute_list *list, token *sep_tok, tree_classdef_attribute *elt) +{ + return list_append (list, *sep_tok, elt); +} + +tree_classdef_body * +base_parser::make_classdef_body (tree_classdef_properties_block *pb) +{ + return new tree_classdef_body (pb); +} + +tree_classdef_body * +base_parser::make_classdef_body (tree_classdef_methods_block *mb) +{ + return new tree_classdef_body (mb); +} + +tree_classdef_body * +base_parser::make_classdef_body (tree_classdef_events_block *evb) +{ + return new tree_classdef_body (evb); +} + +tree_classdef_body * +base_parser::make_classdef_body (tree_classdef_enum_block *enb) +{ + return new tree_classdef_body (enb); +} + +tree_classdef_body * +base_parser::append_classdef_properties_block (tree_classdef_body *body, tree_classdef_properties_block *block) +{ + return body->append (block); +} + +tree_classdef_body * +base_parser::append_classdef_methods_block (tree_classdef_body *body, tree_classdef_methods_block *block) +{ + return body->append (block); +} + +tree_classdef_body * +base_parser::append_classdef_events_block (tree_classdef_body *body, tree_classdef_events_block *block) +{ + return body->append (block); +} + +tree_classdef_body * +base_parser::append_classdef_enum_block (tree_classdef_body *body, tree_classdef_enum_block *block) +{ + return body->append (block); +} + +octave_user_function* +base_parser::start_classdef_external_method (tree_identifier *id, tree_parameter_list *pl) +{ + octave_user_function* retval = nullptr; + + // External methods are only allowed within @-folders. In this case, + // m_curr_class_name will be non-empty. + + if (! m_curr_class_name.empty ()) + { + std::string mname = id->name (); + + // Methods that cannot be declared outside the classdef file: + // - methods with '.' character (e.g. property accessors) + // - class constructor + // - 'delete' + + if (mname.find_first_of (".") == std::string::npos && mname != "delete" && mname != m_curr_class_name) + { + // Create a dummy function that is used until the real method + // is loaded. + + retval = new octave_user_function (symbol_scope::anonymous (), id, pl); + + retval->stash_function_name (mname); + } + else + bison_error ("invalid external method declaration, an external method cannot be the class constructor, 'delete' or have a dot (.) character in its name"); + } + else + bison_error ("external methods are only allowed in @-folders"); + + return retval; +} + +tree_function_def * +base_parser::finish_classdef_external_method (octave_user_function *fcn, tree_parameter_list *ret_list, token *eq_tok) +{ + if (! ret_list) + ret_list = new tree_parameter_list (tree_parameter_list::out); + + fcn->define_ret_list (ret_list); + + if (eq_tok) + fcn->set_eq_tok (*eq_tok); + + return new tree_function_def (fcn); +} + +tree_classdef_method_list * +base_parser::make_classdef_method_list (tree_function_def *fcn_def) +{ + octave_value fcn; + + if (fcn_def) + fcn = fcn_def->function (); + + delete fcn_def; + + return new tree_classdef_method_list (fcn); +} + +tree_classdef_method_list * +base_parser::append_classdef_method (tree_classdef_method_list *list, tree_function_def *fcn_def) +{ + octave_value fcn; + + if (fcn_def) + { fcn = fcn_def->function (); - delete fcn_def; - - return new tree_classdef_method_list (fcn); - } - - tree_classdef_method_list * - base_parser::append_classdef_method (tree_classdef_method_list *list, tree_function_def *fcn_def) - { - octave_value fcn; - - if (fcn_def) - { - fcn = fcn_def->function (); - - delete fcn_def; - } - - return list_append (list, fcn); - } - - bool - base_parser::finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns, token *eof_tok) - { - 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) - { - interpreter& interp = m_lexer.m_interpreter; - - symbol_table& symtab = interp.get_symbol_table (); - - for (tree_statement *elt : *local_fcns) - { - tree_command *cmd = elt->command (); - - tree_function_def *fcn_def = dynamic_cast<tree_function_def *> (cmd); - - octave_value ov_fcn = fcn_def->function (); - octave_user_function *fcn = ov_fcn.user_function_value (); - - std::string nm = fcn->name (); - std::string file = fcn->fcn_file_name (); - - fcn->attach_trailing_comments (eof_tok->leading_comments ()); - - symtab.install_local_function (nm, ov_fcn, file); - } - - 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. - - tree_index_expression * - base_parser::make_index_expression (tree_expression *expr, token *open_delim, tree_argument_list *args, token *close_delim, char type) - { - tree_index_expression *retval = nullptr; - - if (! args) - args = new tree_argument_list (); - - if (args->has_magic_tilde ()) - { - delete expr; - delete args; - - bison_error ("invalid use of empty argument (~) in index expression"); - } - else - { - if (! expr->is_postfix_indexed ()) - expr->set_postfix_index (type); - - token tmp_open_delim = open_delim ? *open_delim : token (); - token tmp_close_delim = close_delim ? *close_delim : token (); - - if (expr->is_index_expression ()) - { - retval = dynamic_cast<tree_index_expression *> (expr); - - retval->append (tmp_open_delim, args, tmp_close_delim, type); - } - else - retval = new tree_index_expression (expr, tmp_open_delim, args, tmp_close_delim, type); - } - - return retval; - } - - // Make an indirect reference expression. - - tree_index_expression * - base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *struct_elt_tok) - { - tree_index_expression *retval = nullptr; - - if (! expr->is_postfix_indexed ()) - expr->set_postfix_index ('.'); - - if (expr->is_index_expression ()) - { - retval = dynamic_cast<tree_index_expression *> (expr); - - retval->append (*dot_tok, *struct_elt_tok); - } - else - retval = new tree_index_expression (expr, *dot_tok, *struct_elt_tok); - - m_lexer.m_looking_at_indirect_ref = false; - - return retval; - } - - // Make an indirect reference expression with dynamic field name. - - tree_index_expression * - base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *open_paren, tree_expression *elt, token *close_paren) - { - tree_index_expression *retval = nullptr; - - if (! expr->is_postfix_indexed ()) - expr->set_postfix_index ('.'); - - if (expr->is_index_expression ()) - { - retval = dynamic_cast<tree_index_expression *> (expr); - - retval->append (*dot_tok, *open_paren, elt, *close_paren); - } - else - retval = new tree_index_expression (expr, *dot_tok, *open_paren, elt, *close_paren); - - m_lexer.m_looking_at_indirect_ref = false; - - return retval; - } - - // Make a declaration command. - - tree_decl_command * - base_parser::make_decl_command (token *tok, tree_decl_init_list *lst) - { - tree_decl_command *retval = nullptr; - - if (lst) - m_lexer.mark_as_variables (lst->variable_names ()); - - switch (tok->token_id ()) - { - case GLOBAL: + delete fcn_def; + } + + return list_append (list, fcn); +} + +bool +base_parser::finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns, token *eof_tok) +{ + 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) + { + interpreter& interp = m_lexer.m_interpreter; + + symbol_table& symtab = interp.get_symbol_table (); + + for (tree_statement *elt : *local_fcns) { - retval = new tree_decl_command ("global", *tok, lst); - retval->mark_global (); + tree_command *cmd = elt->command (); + + tree_function_def *fcn_def = dynamic_cast<tree_function_def *> (cmd); + + octave_value ov_fcn = fcn_def->function (); + octave_user_function *fcn = ov_fcn.user_function_value (); + + std::string nm = fcn->name (); + std::string file = fcn->fcn_file_name (); + + fcn->attach_trailing_comments (eof_tok->leading_comments ()); + + symtab.install_local_function (nm, ov_fcn, file); + } + + 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 a word list command. +tree_index_expression * +base_parser::make_word_list_command (tree_expression *expr, tree_argument_list *args) +{ + tree_index_expression *retval = make_index_expression (expr, nullptr, args, nullptr, '('); + + if (retval) + retval->mark_word_list_cmd (); + + return retval; +} + +// Make an index expression. + +tree_index_expression * +base_parser::make_index_expression (tree_expression *expr, token *open_delim, tree_argument_list *args, token *close_delim, char type) +{ + tree_index_expression *retval = nullptr; + + if (! args) + args = new tree_argument_list (); + + if (args->has_magic_tilde ()) + { + delete expr; + delete args; + + bison_error ("invalid use of empty argument (~) in index expression"); + } + else + { + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index (type); + + token tmp_open_delim = open_delim ? *open_delim : token (); + token tmp_close_delim = close_delim ? *close_delim : token (); + + if (expr->is_index_expression ()) + { + retval = dynamic_cast<tree_index_expression *> (expr); + + retval->append (tmp_open_delim, args, tmp_close_delim, type); } - break; - - case PERSISTENT: - if (m_curr_fcn_depth >= 0) - { - retval = new tree_decl_command ("persistent", *tok, lst); - retval->mark_persistent (); - } - else - { - filepos pos = tok->beg_pos (); - int line = pos.line (); - - if (m_lexer.m_reading_script_file) - warning ("ignoring persistent declaration near line %d of file '%s'", - line, m_lexer.m_fcn_file_full_name.c_str ()); - else - warning ("ignoring persistent declaration near line %d", line); - } - break; - - default: - panic_impossible (); - break; - } - - return retval; - } - - tree_decl_init_list * - base_parser::make_decl_init_list (tree_decl_elt *elt) - { - return new tree_decl_init_list (elt); - } - - tree_decl_init_list * - base_parser::append_decl_init_list (tree_decl_init_list *list, tree_decl_elt *elt) - { - return list_append (list, elt); - } - - tree_decl_elt * - base_parser::make_decl_elt (tree_identifier *id, token */*eq_op*/, tree_expression *expr) - { - // FIXME XXX! need to capture EQ_OP here. - return expr ? new tree_decl_elt (id, expr) : new tree_decl_elt (id); - } - - bool - base_parser::validate_param_list (tree_parameter_list *lst, tree_parameter_list::in_or_out type) - { - std::set<std::string> dict; - - for (tree_decl_elt *elt : *lst) - { - tree_identifier *id = elt->ident (); - - if (id) - { - std::string name = id->name (); - - if (id->is_black_hole ()) - { - if (type != tree_parameter_list::in) - { - bison_error ("invalid use of ~ in output list"); - return false; - } - } - else if (iskeyword (name)) - { - bison_error ("invalid use of keyword '" + name + "' in parameter list"); - return false; - } - else if (dict.find (name) != dict.end ()) - { - bison_error ("'" + name + "' appears more than once in parameter list"); - return false; - } - else - dict.insert (name); - } - } - - std::string va_type = (type == tree_parameter_list::in ? "varargin" : "varargout"); - - std::size_t len = lst->size (); - - if (len > 0) - { - tree_decl_elt *elt = lst->back (); - - tree_identifier *id = elt->ident (); - - if (id && id->name () == va_type) - { - if (len == 1) - lst->mark_varargs_only (); - else - lst->mark_varargs (); - - tree_parameter_list::iterator p = lst->end (); - --p; - delete *p; - lst->erase (p); - } - } - - return true; - } - - bool - base_parser::validate_array_list (tree_expression *e) - { - bool retval = true; - - tree_array_list *al = dynamic_cast<tree_array_list *> (e); - - for (tree_argument_list* row : *al) + else + retval = new tree_index_expression (expr, tmp_open_delim, args, tmp_close_delim, type); + } + + return retval; +} + +// Make an indirect reference expression. + +tree_index_expression * +base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *struct_elt_tok) +{ + tree_index_expression *retval = nullptr; + + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index ('.'); + + if (expr->is_index_expression ()) + { + retval = dynamic_cast<tree_index_expression *> (expr); + + retval->append (*dot_tok, *struct_elt_tok); + } + else + retval = new tree_index_expression (expr, *dot_tok, *struct_elt_tok); + + m_lexer.m_looking_at_indirect_ref = false; + + return retval; +} + +// Make an indirect reference expression with dynamic field name. + +tree_index_expression * +base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *open_paren, tree_expression *elt, token *close_paren) +{ + tree_index_expression *retval = nullptr; + + if (! expr->is_postfix_indexed ()) + expr->set_postfix_index ('.'); + + if (expr->is_index_expression ()) + { + retval = dynamic_cast<tree_index_expression *> (expr); + + retval->append (*dot_tok, *open_paren, elt, *close_paren); + } + else + retval = new tree_index_expression (expr, *dot_tok, *open_paren, elt, *close_paren); + + m_lexer.m_looking_at_indirect_ref = false; + + return retval; +} + +// Make a declaration command. + +tree_decl_command * +base_parser::make_decl_command (token *tok, tree_decl_init_list *lst) +{ + tree_decl_command *retval = nullptr; + + if (lst) + m_lexer.mark_as_variables (lst->variable_names ()); + + switch (tok->token_id ()) + { + case GLOBAL: { - if (row && row->has_magic_tilde ()) - { - retval = false; - - if (e->is_matrix ()) - bison_error ("invalid use of tilde (~) in matrix expression"); - else - bison_error ("invalid use of tilde (~) in cell expression"); - - break; - } - } - - return retval; - } - - tree_argument_list * - base_parser::validate_matrix_for_assignment (tree_expression *e) - { - tree_argument_list *retval = nullptr; - - if (e->is_constant ()) - { - interpreter& interp = m_lexer.m_interpreter; - - tree_evaluator& tw = interp.get_evaluator (); - - octave_value ov = e->evaluate (tw); - - delete e; - - if (ov.isempty ()) - bison_error ("invalid empty left hand side of assignment"); - else - bison_error ("invalid constant left hand side of assignment"); - } - else - { - bool is_simple_assign = true; - - tree_argument_list *tmp = nullptr; - - if (e->is_matrix ()) - { - tree_matrix *mat = dynamic_cast<tree_matrix *> (e); - - if (mat && mat->size () == 1) - { - tmp = mat->front (); - mat->pop_front (); - delete e; - is_simple_assign = false; - } - } - else - tmp = new tree_argument_list (e); - - if (tmp && tmp->is_valid_lvalue_list ()) - { - m_lexer.mark_as_variables (tmp->variable_names ()); - retval = tmp; - } - else - { - delete tmp; - - bison_error ("invalid left hand side of assignment"); - } - - if (retval && is_simple_assign) - retval->mark_as_simple_assign_lhs (); - } - - return retval; - } - - // Finish building an array_list. - - tree_expression * - base_parser::finish_array_list (token *open_delim, tree_array_list *array_list, token *close_delim) - { - tree_expression *retval = array_list; - - array_list->mark_in_delims (*open_delim, *close_delim); - - if (array_list->all_elements_are_constant ()) - { - interpreter& interp = m_lexer.m_interpreter; - - try - { - // If the evaluation generates a warning message, restore - // the previous value of last_warning_message and skip the - // conversion to a constant value. - - error_system& es = interp.get_error_system (); - - unwind_action restore_last_warning_message (&error_system::set_last_warning_message, &es, es.last_warning_message ("")); - - unwind_action restore_discard_warning_messages (&error_system::set_discard_warning_messages, &es, es.discard_warning_messages (true)); - - tree_evaluator& tw = interp.get_evaluator (); - - octave_value tmp = array_list->evaluate (tw); - - std::string msg = es.last_warning_message (); - - if (msg.empty ()) - { - std::ostringstream buf; - - tree_print_code tpc (buf); - - array_list->accept (tpc); - - std::string orig_text = buf.str (); - - token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); - - tree_constant *tc_retval = new tree_constant (tmp, orig_text, tok); - - delete array_list; - - retval = tc_retval; - } - } - catch (const execution_exception&) - { - interp.recover_from_exception (); - } - } - - return retval; - } - - // Finish building a matrix list. - - tree_expression * - base_parser::finish_matrix (token *open_delim, tree_matrix *m, token *close_delim) - { - if (m) - return finish_array_list (open_delim, m, close_delim); - - octave_value tmp {octave_null_matrix::instance}; - std::string orig_text {"{}"}; - - token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); - - return new tree_constant (tmp, orig_text, tok); - } - - tree_matrix * - base_parser::make_matrix (tree_argument_list *row) - { - return row ? new tree_matrix (row) : nullptr; - } - - tree_matrix * - base_parser::append_matrix_row (tree_matrix *matrix, token *sep_tok, tree_argument_list *row) - { - if (! matrix) - return make_matrix (row); - - return row ? list_append (matrix, *sep_tok, row) : matrix; - } - - // Finish building a cell list. - - tree_expression * - base_parser::finish_cell (token *open_delim, tree_cell *c, token *close_delim) - { - if (c) - return finish_array_list (open_delim, c, close_delim); - - octave_value tmp {Cell ()}; - std::string orig_text {"{}"}; - - token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); - - return new tree_constant (tmp, orig_text, tok); - } - - tree_cell * - base_parser::make_cell (tree_argument_list *row) - { - return row ? new tree_cell (row) : nullptr; - } - - tree_cell * - base_parser::append_cell_row (tree_cell *cell, token *sep_tok, tree_argument_list *row) - { - if (! cell) - return make_cell (row); - - return row ? list_append (cell, *sep_tok, row) : cell; - } - - tree_identifier * - base_parser::make_identifier (token *ident) - { - symbol_scope scope = m_lexer.m_symtab_context.curr_scope (); - - return new tree_identifier (scope, *ident); - } - - tree_superclass_ref * - base_parser::make_superclass_ref (token *superclassref) - { - std::string meth = superclassref->superclass_method_name (); - std::string cls = superclassref->superclass_class_name (); - - return new tree_superclass_ref (meth, cls, *superclassref); - } - - tree_metaclass_query * - base_parser::make_metaclass_query (token *metaquery) - { - std::string cls = metaquery->text (); - - return new tree_metaclass_query (cls, *metaquery); - } - - tree_statement_list * - base_parser::set_stmt_print_flag (tree_statement_list *list, char sep, bool warn_missing_semi) - { - tree_statement *tmp = list->back (); - - switch (sep) - { - case ';': - tmp->set_print_flag (false); - break; - - case 0: - case ',': - case '\n': - tmp->set_print_flag (true); - if (warn_missing_semi) - maybe_warn_missing_semi (list); - break; - - default: - warning ("unrecognized separator type!"); - break; - } - - // Even if a statement is null, we add it to the list then remove it - // here so that the print flag is applied to the correct statement. - - if (tmp->is_null_statement ()) - { - list->pop_back (); - delete tmp; + retval = new tree_decl_command ("global", *tok, lst); + retval->mark_global (); } - - return list; - } - - // Finish building a statement. - template <typename T> - tree_statement * - base_parser::make_statement (T *arg) - { - return new tree_statement (arg); - } - - tree_statement_list * - base_parser::make_statement_list (tree_statement *stmt) - { - return new tree_statement_list (stmt); - } - - tree_statement_list * - base_parser::append_statement_list (tree_statement_list *list, char sep, tree_statement *stmt, bool warn_missing_semi) - { - set_stmt_print_flag (list, sep, warn_missing_semi); - - return list_append (list, stmt); - } - - tree_statement_list * - base_parser::make_function_def_list (tree_function_def *fcn_def) - { - tree_statement *stmt = make_statement (fcn_def); - - return new tree_statement_list (stmt); - } - - tree_statement_list * - base_parser::append_function_def_list (tree_statement_list *list, char, tree_function_def *fcn_def) - { - tree_statement *stmt = make_statement (fcn_def); - - return list_append (list, stmt); - } - - tree_argument_list * - base_parser::make_argument_list (tree_expression *expr) - { - return new tree_argument_list (expr); - } - - tree_argument_list * - base_parser::append_argument_list (tree_argument_list *list, token *sep_tok, tree_expression *expr) - { - return list_append (list, *sep_tok, expr); - } - - tree_parameter_list * - base_parser::make_parameter_list (tree_parameter_list::in_or_out io) - { - return new tree_parameter_list (io); - } - - tree_parameter_list * - base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_decl_elt *t) - { - return new tree_parameter_list (io, t); - } - - tree_parameter_list * - base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_identifier *id) - { - return new tree_parameter_list (io, id); - } - - tree_parameter_list * - base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_decl_elt *t) - { - return list_append (list, *sep_tok, t); - } - - tree_parameter_list * - base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_identifier *id) - { - return list_append (list, *sep_tok, new tree_decl_elt (id)); - } - - void - base_parser::disallow_command_syntax () - { - m_lexer.m_allow_command_syntax = false; - } - - void - base_parser::bison_error (const std::string& str) - { - bison_error (str, m_lexer.m_filepos); - } - - void - base_parser::bison_error (const std::string& str, const filepos& pos) - { - std::ostringstream output_buf; - - int err_line = pos.line (); - int err_col = pos.column (); - - bool in_file = (m_lexer.m_reading_fcn_file || m_lexer.m_reading_script_file || m_lexer.m_reading_classdef_file); - - // Adjust the error column for display because it is 1-based in the - // lexer for easier reporting. - err_col--; - - if (in_file) - output_buf << str << " near line " << err_line << ", column " << err_col << " in file " << m_lexer.m_fcn_file_full_name << "\n"; - else - { - // On command line, point directly to error - output_buf << str << "\n\n"; - std::string curr_line = m_lexer.m_current_input_line; - - if (! curr_line.empty ()) - { - // FIXME: we could do better if we just cached lines from the - // input file in a list. See also functions for managing input - // buffers in lex.ll. - std::size_t len = curr_line.length (); - - if (curr_line[len-1] == '\n') - curr_line.resize (len-1); - - // Print the line, maybe with a pointer near the error token. - output_buf << ">>> " << curr_line << "\n"; - - if (err_col == 0) - err_col = len; - - for (int i = 0; i < err_col + 3; i++) - output_buf << " "; - - output_buf << "^" << "\n"; - } - - } - - m_parse_error_msg = output_buf.str (); - } - - void - base_parser::bison_error (const parse_exception& pe) - { - bison_error (pe.message (), pe.pos ()); - } - - 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.pos ()); - } - - int - parser::run () - { - int status = -1; - - yypstate *pstate = static_cast<yypstate *> (m_parser_state); - - try - { - status = octave_pull_parse (pstate, *this); - } - catch (const execution_exception&) - { - // FIXME: In previous versions, we emitted a parse error here - // but that is not always correct because the error could have - // happened inside a GUI callback functions executing in the - // readline event_hook loop. Maybe we need a separate exception - // class for parse errors? - - throw; - } - catch (const exit_exception&) - { - throw; - } - catch (const interrupt_exception&) - { - throw; - } - catch (...) - { - std::string file = m_lexer.m_fcn_file_full_name; - - if (file.empty ()) - error ("unexpected exception while parsing input"); - else - error ("unexpected exception while parsing %s", file.c_str ()); - } - - if (status != 0) - parse_error_with_id ("Octave:parse-error", "%s", m_parse_error_msg.c_str ()); - - return status; - } - - // Parse input from INPUT. Pass TRUE for EOF if the end of INPUT should - // finish the parse. - - int - push_parser::run (const std::string& input, bool eof) - { - int status = -1; - - dynamic_cast<push_lexer&> (m_lexer).append_input (input, eof); - - do - { - YYSTYPE lval; - - int tok_id = octave_lex (&lval, m_lexer.m_scanner); - - if (tok_id < 0) - { - // TOKEN == -2 means that the lexer recognized a comment - // and we should be at the end of the buffer but not the - // end of the file so we should return 0 to indicate - // "complete input" instead of -1 to request more input. - - status = (tok_id == -2 ? 0 : -1); - - if (! eof && m_lexer.at_end_of_buffer ()) - return status; - - break; - } - - yypstate *pstate = static_cast<yypstate *> (m_parser_state); - - try - { - status = octave_push_parse (pstate, tok_id, &lval, *this); - } - catch (execution_exception& e) - { - std::string file = m_lexer.m_fcn_file_full_name; - - if (file.empty ()) - error (e, "parse error"); - else - error (e, "parse error in %s", file.c_str ()); - } - catch (const exit_exception&) - { - throw; - } - catch (interrupt_exception &) - { - throw; - } - catch (...) - { - std::string file = m_lexer.m_fcn_file_full_name; - - if (file.empty ()) - error ("unexpected exception while parsing input"); - else - error ("unexpected exception while parsing %s", file.c_str ()); - } - } - while (status == YYPUSH_MORE || ! m_lexer.at_end_of_buffer ()); - - if (status != 0) - parse_error_with_id ("Octave:parse-error", "%s", m_parse_error_msg.c_str ()); - - return status; - } - - int - push_parser::run () - { - if (! m_reader) - error ("push_parser::run requires valid input_reader"); - - int exit_status = 0; - - std::string prompt = command_editor::decode_prompt_string (m_interpreter.PS1 ()); - - do - { - // Reset status each time through the read loop so that - // it won't be set to -1 and cause us to exit the outer - // loop early if there is an exception while reading - // input or parsing. - - exit_status = 0; - - bool eof = false; - std::string input_line = m_reader->get_input (prompt, eof); - - if (eof) - { - exit_status = EOF; - break; - } - - exit_status = run (input_line, false); - - prompt = command_editor::decode_prompt_string (m_interpreter.PS2 ()); - } - while (exit_status < 0); - - return exit_status; - } - - octave_value - parse_fcn_file (interpreter& interp, const std::string& full_file, const std::string& file, const std::string& dir_name, const std::string& dispatch_type, const std::string& package_name, bool require_file, bool force_script, bool autoload, bool relative_lookup) - { - octave_value retval; - - FILE *ffile = nullptr; - - if (! full_file.empty ()) + break; + + case PERSISTENT: + if (m_curr_fcn_depth >= 0) + { + retval = new tree_decl_command ("persistent", *tok, lst); + retval->mark_persistent (); + } + else + { + filepos pos = tok->beg_pos (); + int line = pos.line (); + + if (m_lexer.m_reading_script_file) + warning ("ignoring persistent declaration near line %d of file '%s'", line, m_lexer.m_fcn_file_full_name.c_str ()); + else + warning ("ignoring persistent declaration near line %d", line); + } + break; + + default: + panic_impossible (); + break; + } + + return retval; +} + +tree_decl_init_list * +base_parser::make_decl_init_list (tree_decl_elt *elt) +{ + return new tree_decl_init_list (elt); +} + +tree_decl_init_list * +base_parser::append_decl_init_list (tree_decl_init_list *list, tree_decl_elt *elt) +{ + return list_append (list, elt); +} + +tree_decl_elt * +base_parser::make_decl_elt (tree_identifier *id, token */*eq_op*/, tree_expression *expr) +{ + // FIXME XXX! need to capture EQ_OP here. + return expr ? new tree_decl_elt (id, expr) : new tree_decl_elt (id); +} + +bool +base_parser::validate_param_list (tree_parameter_list *lst, tree_parameter_list::in_or_out type) +{ + std::set<std::string> dict; + + for (tree_decl_elt *elt : *lst) + { + tree_identifier *id = elt->ident (); + + if (id) + { + std::string name = id->name (); + + if (id->is_black_hole ()) + { + if (type != tree_parameter_list::in) + { + bison_error ("invalid use of ~ in output list"); + return false; + } + } + else if (iskeyword (name)) + { + bison_error ("invalid use of keyword '" + name + "' in parameter list"); + return false; + } + else if (dict.find (name) != dict.end ()) + { + bison_error ("'" + name + "' appears more than once in parameter list"); + return false; + } + else + dict.insert (name); + } + } + + std::string va_type = (type == tree_parameter_list::in ? "varargin" : "varargout"); + + std::size_t len = lst->size (); + + if (len > 0) + { + tree_decl_elt *elt = lst->back (); + + tree_identifier *id = elt->ident (); + + if (id && id->name () == va_type) + { + if (len == 1) + lst->mark_varargs_only (); + else + lst->mark_varargs (); + + tree_parameter_list::iterator p = lst->end (); + --p; + delete *p; + lst->erase (p); + } + } + + return true; +} + +bool +base_parser::validate_array_list (tree_expression *e) +{ + bool retval = true; + + tree_array_list *al = dynamic_cast<tree_array_list *> (e); + + for (tree_argument_list* row : *al) + { + if (row && row->has_magic_tilde ()) + { + retval = false; + + if (e->is_matrix ()) + bison_error ("invalid use of tilde (~) in matrix expression"); + else + bison_error ("invalid use of tilde (~) in cell expression"); + + break; + } + } + + return retval; +} + +tree_argument_list * +base_parser::validate_matrix_for_assignment (tree_expression *e) +{ + tree_argument_list *retval = nullptr; + + if (e->is_constant ()) + { + interpreter& interp = m_lexer.m_interpreter; + + tree_evaluator& tw = interp.get_evaluator (); + + octave_value ov = e->evaluate (tw); + + delete e; + + if (ov.isempty ()) + bison_error ("invalid empty left hand side of assignment"); + else + bison_error ("invalid constant left hand side of assignment"); + } + else + { + bool is_simple_assign = true; + + tree_argument_list *tmp = nullptr; + + if (e->is_matrix ()) + { + tree_matrix *mat = dynamic_cast<tree_matrix *> (e); + + if (mat && mat->size () == 1) + { + tmp = mat->front (); + mat->pop_front (); + delete e; + is_simple_assign = false; + } + } + else + tmp = new tree_argument_list (e); + + if (tmp && tmp->is_valid_lvalue_list ()) + { + m_lexer.mark_as_variables (tmp->variable_names ()); + retval = tmp; + } + else + { + delete tmp; + + bison_error ("invalid left hand side of assignment"); + } + + if (retval && is_simple_assign) + retval->mark_as_simple_assign_lhs (); + } + + return retval; +} + +// Finish building an array_list. + +tree_expression * +base_parser::finish_array_list (token *open_delim, tree_array_list *array_list, token *close_delim) +{ + tree_expression *retval = array_list; + + array_list->mark_in_delims (*open_delim, *close_delim); + + if (array_list->all_elements_are_constant ()) + { + interpreter& interp = m_lexer.m_interpreter; + + try + { + // If the evaluation generates a warning message, restore + // the previous value of last_warning_message and skip the + // conversion to a constant value. + + error_system& es = interp.get_error_system (); + + unwind_action restore_last_warning_message (&error_system::set_last_warning_message, &es, es.last_warning_message ("")); + + unwind_action restore_discard_warning_messages (&error_system::set_discard_warning_messages, &es, es.discard_warning_messages (true)); + + tree_evaluator& tw = interp.get_evaluator (); + + octave_value tmp = array_list->evaluate (tw); + + std::string msg = es.last_warning_message (); + + if (msg.empty ()) + { + std::ostringstream buf; + + tree_print_code tpc (buf); + + array_list->accept (tpc); + + std::string orig_text = buf.str (); + + token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); + + tree_constant *tc_retval = new tree_constant (tmp, orig_text, tok); + + delete array_list; + + retval = tc_retval; + } + } + catch (const execution_exception&) + { + interp.recover_from_exception (); + } + } + + return retval; +} + +// Finish building a matrix list. + +tree_expression * +base_parser::finish_matrix (token *open_delim, tree_matrix *m, token *close_delim) +{ + if (m) + return finish_array_list (open_delim, m, close_delim); + + octave_value tmp {octave_null_matrix::instance}; + std::string orig_text {"{}"}; + + token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); + + return new tree_constant (tmp, orig_text, tok); +} + +tree_matrix * +base_parser::make_matrix (tree_argument_list *row) +{ + return row ? new tree_matrix (row) : nullptr; +} + +tree_matrix * +base_parser::append_matrix_row (tree_matrix *matrix, token *sep_tok, tree_argument_list *row) +{ + if (! matrix) + return make_matrix (row); + + return row ? list_append (matrix, *sep_tok, row) : matrix; +} + +// Finish building a cell list. + +tree_expression * +base_parser::finish_cell (token *open_delim, tree_cell *c, token *close_delim) +{ + if (c) + return finish_array_list (open_delim, c, close_delim); + + octave_value tmp {Cell ()}; + std::string orig_text {"{}"}; + + token tok (CONSTANT, tmp, orig_text, open_delim->beg_pos (), close_delim->end_pos ()); + + return new tree_constant (tmp, orig_text, tok); +} + +tree_cell * +base_parser::make_cell (tree_argument_list *row) +{ + return row ? new tree_cell (row) : nullptr; +} + +tree_cell * +base_parser::append_cell_row (tree_cell *cell, token *sep_tok, tree_argument_list *row) +{ + if (! cell) + return make_cell (row); + + return row ? list_append (cell, *sep_tok, row) : cell; +} + +tree_identifier * +base_parser::make_identifier (token *ident) +{ + symbol_scope scope = m_lexer.m_symtab_context.curr_scope (); + + return new tree_identifier (scope, *ident); +} + +tree_superclass_ref * +base_parser::make_superclass_ref (token *superclassref) +{ + std::string meth = superclassref->superclass_method_name (); + std::string cls = superclassref->superclass_class_name (); + + return new tree_superclass_ref (meth, cls, *superclassref); +} + +tree_metaclass_query * +base_parser::make_metaclass_query (token *metaquery) +{ + std::string cls = metaquery->text (); + + return new tree_metaclass_query (cls, *metaquery); +} + +tree_statement_list * +base_parser::set_stmt_print_flag (tree_statement_list *list, char sep, bool warn_missing_semi) +{ + tree_statement *tmp = list->back (); + + switch (sep) + { + case ';': + tmp->set_print_flag (false); + break; + + case 0: + case ',': + case '\n': + tmp->set_print_flag (true); + if (warn_missing_semi) + maybe_warn_missing_semi (list); + break; + + default: + warning ("unrecognized separator type!"); + break; + } + + // Even if a statement is null, we add it to the list then remove it + // here so that the print flag is applied to the correct statement. + + if (tmp->is_null_statement ()) + { + list->pop_back (); + delete tmp; + } + + return list; +} + +// Finish building a statement. +template <typename T> +tree_statement * +base_parser::make_statement (T *arg) +{ + return new tree_statement (arg); +} + +tree_statement_list * +base_parser::make_statement_list (tree_statement *stmt) +{ + return new tree_statement_list (stmt); +} + +tree_statement_list * +base_parser::append_statement_list (tree_statement_list *list, char sep, tree_statement *stmt, bool warn_missing_semi) +{ + set_stmt_print_flag (list, sep, warn_missing_semi); + + return list_append (list, stmt); +} + +tree_statement_list * +base_parser::make_function_def_list (tree_function_def *fcn_def) +{ + tree_statement *stmt = make_statement (fcn_def); + + return new tree_statement_list (stmt); +} + +tree_statement_list * +base_parser::append_function_def_list (tree_statement_list *list, char, tree_function_def *fcn_def) +{ + tree_statement *stmt = make_statement (fcn_def); + + return list_append (list, stmt); +} + +tree_argument_list * +base_parser::make_argument_list (tree_expression *expr) +{ + return new tree_argument_list (expr); +} + +tree_argument_list * +base_parser::append_argument_list (tree_argument_list *list, token *sep_tok, tree_expression *expr) +{ + return list_append (list, *sep_tok, expr); +} + +tree_parameter_list * +base_parser::make_parameter_list (tree_parameter_list::in_or_out io) +{ + return new tree_parameter_list (io); +} + +tree_parameter_list * +base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_decl_elt *t) +{ + return new tree_parameter_list (io, t); +} + +tree_parameter_list * +base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_identifier *id) +{ + return new tree_parameter_list (io, id); +} + +tree_parameter_list * +base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_decl_elt *t) +{ + return list_append (list, *sep_tok, t); +} + +tree_parameter_list * +base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_identifier *id) +{ + return list_append (list, *sep_tok, new tree_decl_elt (id)); +} + +void +base_parser::disallow_command_syntax () +{ + m_lexer.m_allow_command_syntax = false; +} + +void +base_parser::bison_error (const std::string& str) +{ + bison_error (str, m_lexer.m_filepos); +} + +void +base_parser::bison_error (const std::string& str, const filepos& pos) +{ + std::ostringstream output_buf; + + int err_line = pos.line (); + int err_col = pos.column (); + + bool in_file = (m_lexer.m_reading_fcn_file || m_lexer.m_reading_script_file || m_lexer.m_reading_classdef_file); + + // Adjust the error column for display because it is 1-based in the + // lexer for easier reporting. + err_col--; + + if (in_file) + output_buf << str << " near line " << err_line << ", column " << err_col << " in file " << m_lexer.m_fcn_file_full_name << "\n"; + else + { + // On command line, point directly to error + output_buf << str << "\n\n"; + std::string curr_line = m_lexer.m_current_input_line; + + if (! curr_line.empty ()) + { + // FIXME: we could do better if we just cached lines from the + // input file in a list. See also functions for managing input + // buffers in lex.ll. + std::size_t len = curr_line.length (); + + if (curr_line[len-1] == '\n') + curr_line.resize (len-1); + + // Print the line, maybe with a pointer near the error token. + output_buf << ">>> " << curr_line << "\n"; + + if (err_col == 0) + err_col = len; + + for (int i = 0; i < err_col + 3; i++) + output_buf << " "; + + output_buf << "^" << "\n"; + } + + } + + m_parse_error_msg = output_buf.str (); +} + +void +base_parser::bison_error (const parse_exception& pe) +{ + bison_error (pe.message (), pe.pos ()); +} + +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.pos ()); +} + +int +parser::run () +{ + int status = -1; + + yypstate *pstate = static_cast<yypstate *> (m_parser_state); + + try + { + status = octave_pull_parse (pstate, *this); + } + catch (const execution_exception&) + { + // FIXME: In previous versions, we emitted a parse error here + // but that is not always correct because the error could have + // happened inside a GUI callback functions executing in the + // readline event_hook loop. Maybe we need a separate exception + // class for parse errors? + + throw; + } + catch (const exit_exception&) + { + throw; + } + catch (const interrupt_exception&) + { + throw; + } + catch (...) + { + std::string file = m_lexer.m_fcn_file_full_name; + + if (file.empty ()) + error ("unexpected exception while parsing input"); + else + error ("unexpected exception while parsing %s", file.c_str ()); + } + + if (status != 0) + parse_error_with_id ("Octave:parse-error", "%s", m_parse_error_msg.c_str ()); + + return status; +} + +// Parse input from INPUT. Pass TRUE for EOF if the end of INPUT should +// finish the parse. + +int +push_parser::run (const std::string& input, bool eof) +{ + int status = -1; + + dynamic_cast<push_lexer&> (m_lexer).append_input (input, eof); + + do + { + YYSTYPE lval; + + int tok_id = octave_lex (&lval, m_lexer.m_scanner); + + if (tok_id < 0) + { + // TOKEN == -2 means that the lexer recognized a comment + // and we should be at the end of the buffer but not the + // end of the file so we should return 0 to indicate + // "complete input" instead of -1 to request more input. + + status = (tok_id == -2 ? 0 : -1); + + if (! eof && m_lexer.at_end_of_buffer ()) + return status; + + break; + } + + yypstate *pstate = static_cast<yypstate *> (m_parser_state); + + try + { + status = octave_push_parse (pstate, tok_id, &lval, *this); + } + catch (execution_exception& e) + { + std::string file = m_lexer.m_fcn_file_full_name; + + if (file.empty ()) + error (e, "parse error"); + else + error (e, "parse error in %s", file.c_str ()); + } + catch (const exit_exception&) + { + throw; + } + catch (interrupt_exception &) + { + throw; + } + catch (...) + { + std::string file = m_lexer.m_fcn_file_full_name; + + if (file.empty ()) + error ("unexpected exception while parsing input"); + else + error ("unexpected exception while parsing %s", file.c_str ()); + } + } + while (status == YYPUSH_MORE || ! m_lexer.at_end_of_buffer ()); + + if (status != 0) + parse_error_with_id ("Octave:parse-error", "%s", m_parse_error_msg.c_str ()); + + return status; +} + +int +push_parser::run () +{ + if (! m_reader) + error ("push_parser::run requires valid input_reader"); + + int exit_status = 0; + + std::string prompt = command_editor::decode_prompt_string (m_interpreter.PS1 ()); + + do + { + // Reset status each time through the read loop so that + // it won't be set to -1 and cause us to exit the outer + // loop early if there is an exception while reading + // input or parsing. + + exit_status = 0; + + bool eof = false; + std::string input_line = m_reader->get_input (prompt, eof); + + if (eof) + { + exit_status = EOF; + break; + } + + exit_status = run (input_line, false); + + prompt = command_editor::decode_prompt_string (m_interpreter.PS2 ()); + } + while (exit_status < 0); + + return exit_status; +} + +octave_value +parse_fcn_file (interpreter& interp, const std::string& full_file, const std::string& file, const std::string& dir_name, const std::string& dispatch_type, const std::string& package_name, bool require_file, bool force_script, bool autoload, bool relative_lookup) +{ + octave_value retval; + + FILE *ffile = nullptr; + + if (! full_file.empty ()) { // Check that m-file is not overly large which can segfault interpreter. const int max_file_size = 512 * 1024 * 1024; // 512 MB @@ -5234,303 +5239,302 @@ ffile = sys::fopen (full_file, "rb"); } - if (! ffile) - { - if (require_file) - error ("no such file, '%s'", full_file.c_str ()); - - return octave_value (); - } - - unwind_action act ([ffile] () { ::fclose (ffile); }); - - // get the encoding for this folder - input_system& input_sys = interp.get_input_system (); - parser parser (ffile, interp, input_sys.dir_encoding (dir_name)); - - parser.m_curr_class_name = dispatch_type; - parser.m_curr_package_name = package_name; - parser.m_autoloading = autoload; - parser.m_fcn_file_from_relative_lookup = relative_lookup; - - parser.m_lexer.m_force_script = force_script; - parser.m_lexer.prep_for_file (); - parser.m_lexer.m_parsing_class_method = ! dispatch_type.empty (); - - parser.m_lexer.m_fcn_file_name = file; - parser.m_lexer.m_fcn_file_full_name = full_file; - parser.m_lexer.m_dir_name = dir_name; - parser.m_lexer.m_package_name = package_name; - - int err = parser.run (); - - if (err) - error ("parse error while reading file %s", full_file.c_str ()); - - octave_value ov_fcn = parser.m_primary_fcn; - - if (parser.m_lexer.m_reading_classdef_file && parser.classdef_object ()) - { - // Convert parse tree for classdef object to - // meta.class info (and stash it in the symbol - // table?). Return pointer to constructor? - - if (ov_fcn.is_defined ()) - panic_impossible (); - - bool is_at_folder = ! dispatch_type.empty (); - - std::shared_ptr<tree_classdef> cdef_obj = parser.classdef_object(); - - return cdef_obj->make_meta_class (interp, is_at_folder); - } - else if (ov_fcn.is_defined ()) - { - octave_function *fcn = ov_fcn.function_value (); - - fcn->maybe_relocate_end (); - - if (parser.m_parsing_subfunctions) - { - if (! parser.m_endfunction_found) - parser.m_subfunction_names.reverse (); - - fcn->stash_subfunction_names (parser.m_subfunction_names); - } - - return ov_fcn; - } - - 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<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 () - { - 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. - - void - base_parser::maybe_warn_assign_as_truth_value (tree_expression *expr) - { - if (expr->is_assignment_expression () && expr->delim_count () < 2) - { - if (m_lexer.m_fcn_file_full_name.empty ()) - warning_with_id ("Octave:assign-as-truth-value", "suggest parenthesis around assignment used as truth value"); - else - warning_with_id ("Octave:assign-as-truth-value", "suggest parenthesis around assignment used as truth value near line %d, column %d in file '%s'", expr->line (), expr->column (), m_lexer.m_fcn_file_full_name.c_str ()); - } - } - - // Maybe print a warning about switch labels that aren't constants. - - void - base_parser::maybe_warn_variable_switch_label (tree_expression *expr) - { - if (! expr->is_constant ()) - { - if (m_lexer.m_fcn_file_full_name.empty ()) - warning_with_id ("Octave:variable-switch-label", "variable switch label"); - else - warning_with_id ("Octave:variable-switch-label", "variable switch label near line %d, column %d in file '%s'", expr->line (), expr->column (), m_lexer.m_fcn_file_full_name.c_str ()); - } - } - - void - base_parser::maybe_warn_missing_semi (tree_statement_list *t) - { - if (m_curr_fcn_depth >= 0) - { - tree_statement *tmp = t->back (); - - if (tmp->is_expression ()) - warning_with_id ("Octave:missing-semicolon", "missing semicolon near line %d, column %d in file '%s'", tmp->line (), tmp->column (), m_lexer.m_fcn_file_full_name.c_str ()); - } - } - - std::string - get_help_from_file (const std::string& nm, bool& symbol_found, - std::string& full_file) - { - std::string retval; - - full_file = fcn_file_in_path (nm); - - std::string file = full_file; - - std::size_t file_len = file.length (); - - if ((file_len > 4 && file.substr (file_len-4) == ".oct") - || (file_len > 4 && file.substr (file_len-4) == ".mex") - || (file_len > 2 && file.substr (file_len-2) == ".m")) - { - file = sys::env::base_pathname (file); - file = file.substr (0, file.find_last_of ('.')); - - std::size_t pos = file.find_last_of (sys::file_ops::dir_sep_str ()); - if (pos != std::string::npos) - file = file.substr (pos+1); - } - - if (! file.empty ()) - { - interpreter& interp = __get_interpreter__ (); - - symbol_found = true; - - octave_value ov_fcn = parse_fcn_file (interp, full_file, file, "", "", "", true, false, false, false); - - if (ov_fcn.is_defined ()) - { - octave_function *fcn = ov_fcn.function_value (); - - if (fcn) - retval = fcn->doc_string (); - } - } - - return retval; - } - - std::string - get_help_from_file (const std::string& nm, bool& symbol_found) - { - std::string file; - return get_help_from_file (nm, symbol_found, file); - } - - octave_value - load_fcn_from_file (const std::string& file_name, const std::string& dir_name, const std::string& dispatch_type, const std::string& package_name, const std::string& fcn_name, bool autoload) - { - octave_value retval; - - unwind_protect frame; - - std::string nm = file_name; - - std::size_t nm_len = nm.length (); - - std::string file; - - bool relative_lookup = false; - - file = nm; - - if ((nm_len > 4 && nm.substr (nm_len-4) == ".oct") - || (nm_len > 4 && nm.substr (nm_len-4) == ".mex") - || (nm_len > 2 && nm.substr (nm_len-2) == ".m")) - { - nm = sys::env::base_pathname (file); - nm = nm.substr (0, nm.find_last_of ('.')); - - std::size_t pos = nm.find_last_of (sys::file_ops::dir_sep_str ()); - if (pos != std::string::npos) - nm = nm.substr (pos+1); - } - - relative_lookup = ! sys::env::absolute_pathname (file); - - file = sys::env::make_absolute (file); - - int len = file.length (); - - interpreter& interp = __get_interpreter__ (); - - dynamic_loader& dyn_loader = interp.get_dynamic_loader (); - - if (len > 4 && file.substr (len-4, len-1) == ".oct") - { - if (autoload && ! fcn_name.empty ()) - nm = fcn_name; - - octave_function *tmpfcn = dyn_loader.load_oct (nm, file, relative_lookup); - - if (tmpfcn) - { - tmpfcn->stash_package_name (package_name); - retval = octave_value (tmpfcn); - } - } - else if (len > 4 && file.substr (len-4, len-1) == ".mex") - { - // Temporarily load m-file version of mex-file, if it exists, - // to get the help-string to use. - - std::string doc_string; - - octave_value ov_fcn = parse_fcn_file (interp, file.substr (0, len - 2), nm, dir_name, dispatch_type, package_name, false, autoload, autoload, relative_lookup); - - if (ov_fcn.is_defined ()) - { - octave_function *tmpfcn = ov_fcn.function_value (); - - if (tmpfcn) - doc_string = tmpfcn->doc_string (); - } - - octave_function *tmpfcn = dyn_loader.load_mex (nm, file, relative_lookup); - - if (tmpfcn) - { - tmpfcn->document (doc_string); - tmpfcn->stash_package_name (package_name); - - retval = octave_value (tmpfcn); - } - } - else if (len > 2) - retval = parse_fcn_file (interp, file, nm, dir_name, dispatch_type, package_name, true, autoload, autoload, relative_lookup); - - return retval; - } + if (! ffile) + { + if (require_file) + error ("no such file, '%s'", full_file.c_str ()); + + return octave_value (); + } + + unwind_action act ([ffile] () { ::fclose (ffile); }); + + // get the encoding for this folder + input_system& input_sys = interp.get_input_system (); + parser parser (ffile, interp, input_sys.dir_encoding (dir_name)); + + parser.m_curr_class_name = dispatch_type; + parser.m_curr_package_name = package_name; + parser.m_autoloading = autoload; + parser.m_fcn_file_from_relative_lookup = relative_lookup; + + parser.m_lexer.m_force_script = force_script; + parser.m_lexer.prep_for_file (); + parser.m_lexer.m_parsing_class_method = ! dispatch_type.empty (); + + parser.m_lexer.m_fcn_file_name = file; + parser.m_lexer.m_fcn_file_full_name = full_file; + parser.m_lexer.m_dir_name = dir_name; + parser.m_lexer.m_package_name = package_name; + + int err = parser.run (); + + if (err) + error ("parse error while reading file %s", full_file.c_str ()); + + octave_value ov_fcn = parser.m_primary_fcn; + + if (parser.m_lexer.m_reading_classdef_file && parser.classdef_object ()) + { + // Convert parse tree for classdef object to + // meta.class info (and stash it in the symbol + // table?). Return pointer to constructor? + + if (ov_fcn.is_defined ()) + panic_impossible (); + + bool is_at_folder = ! dispatch_type.empty (); + + std::shared_ptr<tree_classdef> cdef_obj = parser.classdef_object(); + + return cdef_obj->make_meta_class (interp, is_at_folder); + } + else if (ov_fcn.is_defined ()) + { + octave_function *fcn = ov_fcn.function_value (); + + fcn->maybe_relocate_end (); + + if (parser.m_parsing_subfunctions) + { + if (! parser.m_endfunction_found) + parser.m_subfunction_names.reverse (); + + fcn->stash_subfunction_names (parser.m_subfunction_names); + } + + return ov_fcn; + } + + 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<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 () +{ + 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. + +void +base_parser::maybe_warn_assign_as_truth_value (tree_expression *expr) +{ + if (expr->is_assignment_expression () && expr->delim_count () < 2) + { + if (m_lexer.m_fcn_file_full_name.empty ()) + warning_with_id ("Octave:assign-as-truth-value", "suggest parenthesis around assignment used as truth value"); + else + warning_with_id ("Octave:assign-as-truth-value", "suggest parenthesis around assignment used as truth value near line %d, column %d in file '%s'", expr->line (), expr->column (), m_lexer.m_fcn_file_full_name.c_str ()); + } +} + +// Maybe print a warning about switch labels that aren't constants. + +void +base_parser::maybe_warn_variable_switch_label (tree_expression *expr) +{ + if (! expr->is_constant ()) + { + if (m_lexer.m_fcn_file_full_name.empty ()) + warning_with_id ("Octave:variable-switch-label", "variable switch label"); + else + warning_with_id ("Octave:variable-switch-label", "variable switch label near line %d, column %d in file '%s'", expr->line (), expr->column (), m_lexer.m_fcn_file_full_name.c_str ()); + } +} + +void +base_parser::maybe_warn_missing_semi (tree_statement_list *t) +{ + if (m_curr_fcn_depth >= 0) + { + tree_statement *tmp = t->back (); + + if (tmp->is_expression ()) + warning_with_id ("Octave:missing-semicolon", "missing semicolon near line %d, column %d in file '%s'", tmp->line (), tmp->column (), m_lexer.m_fcn_file_full_name.c_str ()); + } +} + +std::string +get_help_from_file (const std::string& nm, bool& symbol_found, std::string& full_file) +{ + std::string retval; + + full_file = fcn_file_in_path (nm); + + std::string file = full_file; + + std::size_t file_len = file.length (); + + if ((file_len > 4 && file.substr (file_len-4) == ".oct") + || (file_len > 4 && file.substr (file_len-4) == ".mex") + || (file_len > 2 && file.substr (file_len-2) == ".m")) + { + file = sys::env::base_pathname (file); + file = file.substr (0, file.find_last_of ('.')); + + std::size_t pos = file.find_last_of (sys::file_ops::dir_sep_str ()); + if (pos != std::string::npos) + file = file.substr (pos+1); + } + + if (! file.empty ()) + { + interpreter& interp = __get_interpreter__ (); + + symbol_found = true; + + octave_value ov_fcn = parse_fcn_file (interp, full_file, file, "", "", "", true, false, false, false); + + if (ov_fcn.is_defined ()) + { + octave_function *fcn = ov_fcn.function_value (); + + if (fcn) + retval = fcn->doc_string (); + } + } + + return retval; +} + +std::string +get_help_from_file (const std::string& nm, bool& symbol_found) +{ + std::string file; + return get_help_from_file (nm, symbol_found, file); +} + +octave_value +load_fcn_from_file (const std::string& file_name, const std::string& dir_name, const std::string& dispatch_type, const std::string& package_name, const std::string& fcn_name, bool autoload) +{ + octave_value retval; + + unwind_protect frame; + + std::string nm = file_name; + + std::size_t nm_len = nm.length (); + + std::string file; + + bool relative_lookup = false; + + file = nm; + + if ((nm_len > 4 && nm.substr (nm_len-4) == ".oct") + || (nm_len > 4 && nm.substr (nm_len-4) == ".mex") + || (nm_len > 2 && nm.substr (nm_len-2) == ".m")) + { + nm = sys::env::base_pathname (file); + nm = nm.substr (0, nm.find_last_of ('.')); + + std::size_t pos = nm.find_last_of (sys::file_ops::dir_sep_str ()); + if (pos != std::string::npos) + nm = nm.substr (pos+1); + } + + relative_lookup = ! sys::env::absolute_pathname (file); + + file = sys::env::make_absolute (file); + + int len = file.length (); + + interpreter& interp = __get_interpreter__ (); + + dynamic_loader& dyn_loader = interp.get_dynamic_loader (); + + if (len > 4 && file.substr (len-4, len-1) == ".oct") + { + if (autoload && ! fcn_name.empty ()) + nm = fcn_name; + + octave_function *tmpfcn = dyn_loader.load_oct (nm, file, relative_lookup); + + if (tmpfcn) + { + tmpfcn->stash_package_name (package_name); + retval = octave_value (tmpfcn); + } + } + else if (len > 4 && file.substr (len-4, len-1) == ".mex") + { + // Temporarily load m-file version of mex-file, if it exists, + // to get the help-string to use. + + std::string doc_string; + + octave_value ov_fcn = parse_fcn_file (interp, file.substr (0, len - 2), nm, dir_name, dispatch_type, package_name, false, autoload, autoload, relative_lookup); + + if (ov_fcn.is_defined ()) + { + octave_function *tmpfcn = ov_fcn.function_value (); + + if (tmpfcn) + doc_string = tmpfcn->doc_string (); + } + + octave_function *tmpfcn = dyn_loader.load_mex (nm, file, relative_lookup); + + if (tmpfcn) + { + tmpfcn->document (doc_string); + tmpfcn->stash_package_name (package_name); + + retval = octave_value (tmpfcn); + } + } + else if (len > 2) + retval = parse_fcn_file (interp, file, nm, dir_name, dispatch_type, package_name, true, autoload, autoload, relative_lookup); + + return retval; +} DEFMETHOD (autoload, interp, args, , doc: /* -*- texinfo -*-
--- a/libinterp/parse-tree/parse.h Thu Apr 04 14:23:30 2024 -0400 +++ b/libinterp/parse-tree/parse.h Thu Apr 04 15:07:55 2024 -0400 @@ -519,6 +519,10 @@ OCTINTERP_API bool finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns, token *eof_tok); + // Make a word list command. + OCTINTERP_API tree_index_expression * + make_word_list_command (tree_expression *expr, tree_argument_list *args); + // Make an index expression. OCTINTERP_API tree_index_expression * make_index_expression (tree_expression *expr, token *open_paren, tree_argument_list *args, token *close_paren, char type);