changeset 29724:c19f8cbe0fd5

initial implementation of parsing for arguments validaton block (bug #59405) This change allows parsing of arguments validation blocks. Octave should now accept the arguments block syntax in a mostly Matlab compatible way. Multiple argument blocks are allowed. All arguments blocks must appear before any other exectuable statements in a function. Similar to "methods", "properties", etc., "arguments" is defined as a keyword in the octave.gperf file so that converting "arguments" to the ARGUMENTS token in the lexer and parser is simplified but it is not really treated as a reserved keyword in the language. One known problem with the current approach is that function [...] = f (...) arguments = 13; ... end will result in a parse error. A simple workaround is to place another statement (that is not an arguments block) ahead of the "arguments = ..." line in the function. Fixing this problem generally might require a different parsing method that allows a different type of lookahead than we currently use. NOTE: arguments blocks do not currently perform any actions. Since they may provide default values and/or transform arguments to different types or values, ignoring the arguments block can lead to incorrect results. Octave also currently allows arguments blocks in nested functions though they should be rejected. Special treatment of "arguments" and "endarguments" may be disabled by defining the macro DISABLE_ARGUMENTS_VALIDATION_BLOCK. With this macro defined, Octave's lexer will never return the ARGUMENTS token, so the parser will fail to parse these program elements and the behavior should be the same as prior to this change. * pt-args-block.h, pt-args-block.cc: New files. * libinterp/parse-tree/module.mk: Update. * lex.h, lex.ll (lexical_feedback::m_arguments_is_keyword): New member variable. (lexical_feedback::reset): Reset m_arguments_is_keyword to false. (iskeyword, Fiskeyword): Also handle "arguments" as a special case. (base_lexer::make_keyword_token): Handle arguments and endarguments. * parse.h, oct-parse.yy (base_parser::make_arguments_block, base_parser::make_args_attribute_list, base_parser::make_arg_validation, base_parser::make_args_validation_list, base_parser::append_args_validation_list, base_parser::make_arg_size_spec, base_parser::make_arg_validation_fcns): New functions. (function_body, at_first_executable_stmt, function_body1, arguments_block, arguments_beg, args_attr_list, args_validation_list, arg_validation, size_spec, class_name, validation_fcns, default_value): New non-terminals. (ARGUMENTS): New token. (function): Use new function_body and function_body1 non-terminals to accept arguments blocks, but only at the beginning of a function. (fcn_name): Set lexer.m_arguments_is_keyword to true after parsing function name. (param_list_beg): Set lexer.m_arguments_is_keyword to false while parsing function parameter list. (param_list_beg): Reset lexer.m_arguments_is_keyword to true after parsing function parameter list. (tree_arguments_block_type, tree_args_block_attribute_list_type, tree_args_block_validation_list_type, tree_arg_size_spec_type, tree_arg_validation_type, tree_arg_validation_fcns_type): New non-terminal types. Also declare %destructors for them. * octave.gperf (octave_kw_id): New IDs, arguments_kw and endarguments_kw. Provide entries for arguments and endarguments keywords. * pt-all.h: Include pt-args-block.h. * pt-bp.h, pt-bp.cc (tree_breakpoint::visit_arguments_block, tree_breakpoint::visit_args_block_attribute_list, tree_breakpoint::visit_args_block_validation_list, tree_breakpoint::visit_arg_validation, tree_breakpoint::visit_arg_size_spec, tree_breakpoint::visit_arg_validation_fcns): New virtual functions for arguments block elements. * pt-eval.h, pt-eval.cc (tree_evaluator::visit_arguments_block, tree_evaluator::visit_args_block_attribute_list, tree_evaluator::visit_args_block_validation_list, tree_evaluator::visit_arg_validation, tree_evaluator::visit_arg_size_spec, tree_evaluator::visit_arg_validation_fcns): New virtual functions for arguments block elements. * pt-pr-code.h, pt-pr-code.cc (tree_print_code::visit_arguments_block, tree_print_code::visit_args_block_attribute_list, tree_print_code::visit_args_block_validation_list, tree_print_code::visit_arg_validation, tree_print_code::visit_arg_size_spec, tree_print_code::visit_arg_validation_fcns): New virtual functions for arguments block elements. * pt-walk.h, pt-walk.cc (tree_walker::visit_arguments_block, tree_walker::visit_args_block_attribute_list, tree_walker::visit_args_block_validation_list, tree_walker::visit_arg_validation, tree_walker::visit_arg_size_spec, tree_walker::visit_arg_validation_fcns): New virtual functions for arguments block elements. * token.h (end_tok_type): New type, arguments_end.
author John W. Eaton <jwe@octave.org>
date Tue, 01 Jun 2021 13:34:57 -0400
parents 6858992dfadf
children 80f31f0a51fa
files libinterp/parse-tree/lex.h libinterp/parse-tree/lex.ll libinterp/parse-tree/module.mk libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/octave.gperf libinterp/parse-tree/parse.h libinterp/parse-tree/pt-all.h libinterp/parse-tree/pt-args-block.cc libinterp/parse-tree/pt-args-block.h libinterp/parse-tree/pt-bp.cc libinterp/parse-tree/pt-bp.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h libinterp/parse-tree/pt-pr-code.cc libinterp/parse-tree/pt-pr-code.h libinterp/parse-tree/pt-walk.cc libinterp/parse-tree/pt-walk.h libinterp/parse-tree/token.h
diffstat 18 files changed, 853 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/lex.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/lex.h	Tue Jun 01 13:34:57 2021 -0400
@@ -276,6 +276,7 @@
         m_looking_at_matrix_or_assign_lhs (false),
         m_looking_for_object_index (false),
         m_looking_at_indirect_ref (false),
+        m_arguments_is_keyword (false),
         m_parsing_anon_fcn_body (false),
         m_parsing_class_method (false),
         m_parsing_classdef (false),
@@ -391,6 +392,9 @@
     // structure element.
     bool m_looking_at_indirect_ref;
 
+    // true means arguments is handled as keyword.
+    bool m_arguments_is_keyword;
+
     // true means we are parsing the body of an anonymous function.
     bool m_parsing_anon_fcn_body;
 
--- a/libinterp/parse-tree/lex.ll	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/lex.ll	Tue Jun 01 13:34:57 2021 -0400
@@ -341,11 +341,12 @@
     // "set" and "get" portions of the names using the same mechanism
     // as is used for keywords.  However, they are not really keywords
     // in the language, so omit them from the list of possible
-    // keywords.  Likewise for "enumeration", "events", "methods", and
-    // "properties".
-
+    // keywords.  Likewise for "arguments", "enumeration", "events",
+    // "methods", and "properties".
+
+    // FIXME: The following check is duplicated in Fiskeyword.
     return (octave_kw_hash::in_word_set (s.c_str (), s.length ()) != nullptr
-            && ! (s == "set" || s == "get"
+            && ! (s == "set" || s == "get" || s == "arguments"
                   || s == "enumeration" || s == "events"
                   || s == "methods" || s == "properties"));
   }
@@ -2141,7 +2142,8 @@
         {
           std::string kword = wordlist[i].name;
 
-          if (! (kword == "set" || kword == "get"
+          // FIXME: The following check is duplicated in octave::iskeyword.
+          if (! (kword == "set" || kword == "get" || kword == "arguments"
                  || kword == "enumeration" || kword == "events"
                  || kword == "methods" || kword == "properties"))
             lst[j++] = kword;
@@ -2237,6 +2239,7 @@
     m_looking_at_matrix_or_assign_lhs = false;
     m_looking_for_object_index = false;
     m_looking_at_indirect_ref = false;
+    m_arguments_is_keyword = false;
     m_parsing_anon_fcn_body = false;
     m_parsing_class_method = false;
     m_parsing_classdef = false;
@@ -2628,7 +2631,7 @@
     m_filepos.increment_column (tok_len);
   }
 
-bool
+  bool
   base_lexer::looking_at_space (void)
   {
     int c = text_yyinput ();
@@ -2764,6 +2767,16 @@
         m_at_beginning_of_statement = true;
         break;
 
+      case endarguments_kw:
+#if defined (DISABLE_ARGUMENTS_VALIDATION_BLOCK)
+        return 0;
+#else
+        tok_val = new token (endarguments_kw, token::arguments_end, m_tok_beg,
+                             m_tok_end);
+        m_at_beginning_of_statement = true;
+        break;
+#endif
+
       case endclassdef_kw:
         tok_val = new token (endclassdef_kw, token::classdef_end, m_tok_beg,
                              m_tok_end);
@@ -2874,6 +2887,15 @@
           }
         break;
 
+      case arguments_kw:
+#if defined (DISABLE_ARGUMENTS_VALIDATION_BLOCK)
+        return 0;
+#else
+        if (! m_arguments_is_keyword)
+          return 0;
+        break;
+#endif
+
       case spmd_kw:
         m_at_beginning_of_statement = true;
         break;
--- a/libinterp/parse-tree/module.mk	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/module.mk	Tue Jun 01 13:34:57 2021 -0400
@@ -13,6 +13,7 @@
   %reldir%/pt-all.h \
   %reldir%/pt-anon-scopes.h \
   %reldir%/pt-arg-list.h \
+  %reldir%/pt-args-block.h \
   %reldir%/pt-array-list.h \
   %reldir%/pt-assign.h \
   %reldir%/pt-binop.h \
@@ -65,6 +66,7 @@
   %reldir%/profiler.cc \
   %reldir%/pt-anon-scopes.cc \
   %reldir%/pt-arg-list.cc \
+  %reldir%/pt-args-block.cc \
   %reldir%/pt-array-list.cc \
   %reldir%/pt-assign.cc \
   %reldir%/pt-binop.cc \
--- a/libinterp/parse-tree/oct-parse.yy	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/oct-parse.yy	Tue Jun 01 13:34:57 2021 -0400
@@ -174,6 +174,12 @@
   octave::tree_decl_command *tree_decl_command_type;
   octave::tree_statement *tree_statement_type;
   octave::tree_statement_list *tree_statement_list_type;
+  octave::tree_arguments_block *tree_arguments_block_type;
+  octave::tree_args_block_attribute_list *tree_args_block_attribute_list_type;
+  octave::tree_args_block_validation_list *tree_args_block_validation_list_type;
+  octave::tree_arg_size_spec *tree_arg_size_spec_type;
+  octave::tree_arg_validation *tree_arg_validation_type;
+  octave::tree_arg_validation_fcns *tree_arg_validation_fcns_type;
   octave_user_function *octave_user_function_type;
 
   octave::tree_classdef *tree_classdef_type;
@@ -228,6 +234,7 @@
 %token <tok_val> FQ_IDENT
 %token <tok_val> GET SET
 %token <tok_val> FCN
+%token <tok_val> ARGUMENTS
 %token <tok_val> LEXICAL_ERROR
 %token <tok_val> END_OF_INPUT
 
@@ -239,9 +246,9 @@
 %type <dummy_type> indirect_ref_op decl_param_init
 %type <dummy_type> push_fcn_symtab push_script_symtab begin_file
 %type <dummy_type> param_list_beg param_list_end stmt_begin anon_fcn_begin
-%type <dummy_type> parsing_local_fcns parse_error
+%type <dummy_type> parsing_local_fcns parse_error at_first_executable_stmt
 %type <comment_type> stash_comment
-%type <tok_val> function_beg classdef_beg
+%type <tok_val> function_beg classdef_beg arguments_beg
 %type <punct_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
 %type <tree_type> input
 %type <tree_constant_type> string constant magic_colon
@@ -277,7 +284,7 @@
 %type <tree_decl_command_type> declaration
 %type <tree_statement_type> statement function_end
 %type <tree_statement_list_type> simple_list simple_list1 list list1
-%type <tree_statement_list_type> opt_list
+%type <tree_statement_list_type> opt_list function_body function_body1
 %type <tree_statement_list_type> opt_fcn_list fcn_list fcn_list1
 %type <tree_classdef_attribute_type> attr
 %type <tree_classdef_attribute_list_type> attr_list attr_list1
@@ -296,6 +303,14 @@
 %type <tree_classdef_enum_list_type> enum_list enum_list1
 %type <tree_classdef_enum_block_type> enum_block
 %type <tree_function_def_type> method_decl method
+%type <tree_arguments_block_type> arguments_block
+%type <tree_args_block_attribute_list_type> args_attr_list
+%type <tree_args_block_validation_list_type> args_validation_list
+%type <tree_arg_validation_type> arg_validation
+%type <tree_arg_size_spec_type> size_spec
+%type <tree_identifier_type> class_name
+%type <tree_arg_validation_fcns_type> validation_fcns
+%type <tree_expression_type> default_value
 %type <octave_user_function_type> method_decl1
 
 // Precedence and associativity.
@@ -351,6 +366,12 @@
 %destructor { delete $$; } <tree_decl_command_type>
 %destructor { delete $$; } <tree_statement_type>
 %destructor { delete $$; } <tree_statement_list_type>
+%destructor { delete $$; } <tree_arguments_block_type>
+%destructor { delete $$; } <tree_args_block_attribute_list_type>
+%destructor { delete $$; } <tree_args_block_validation_list_type>
+%destructor { delete $$; } <tree_arg_validation_type>
+%destructor { delete $$; } <tree_arg_size_spec_type>
+%destructor { delete $$; } <tree_arg_validation_fcns_type>
 %destructor { delete $$; } <octave_user_function_type>
 
 %destructor { delete $$; } <tree_classdef_type>
@@ -1430,6 +1451,7 @@
 
                     $$ = 0;
                     lexer.m_looking_at_parameter_list = true;
+                    lexer.m_arguments_is_keyword = false;
 
                     if (lexer.m_looking_at_function_handle)
                       {
@@ -1447,6 +1469,7 @@
 
                     $$ = 0;
                     lexer.m_looking_at_parameter_list = false;
+                    lexer.m_arguments_is_keyword = true;
                     lexer.m_looking_for_object_index = false;
                   }
                 ;
@@ -1654,32 +1677,37 @@
 
 fcn_name        : identifier
                   {
-                    $$ = parser.make_fcn_name ($1);
-                    if (! $$)
+                    if (! ($$ = parser.make_fcn_name ($1)))
                       {
                         // make_fcn_name deleted $1.
                         YYABORT;
                       }
+
+                    lexer.m_arguments_is_keyword = true;
                   }
                 | GET '.' identifier
                   {
                     OCTAVE_YYUSE ($1);
                     OCTAVE_YYUSE ($2);
 
+                    $$ = $3;
+
                     lexer.m_parsed_function_name.top () = true;
                     lexer.m_maybe_classdef_get_set_method = false;
                     lexer.m_parsing_classdef_get_method = true;
-                    $$ = $3;
+                    lexer.m_arguments_is_keyword = true;
                   }
                 | SET '.' identifier
                   {
                     OCTAVE_YYUSE ($1);
                     OCTAVE_YYUSE ($2);
 
+                    $$ = $3;
+
                     lexer.m_parsed_function_name.top () = true;
                     lexer.m_maybe_classdef_get_set_method = false;
                     lexer.m_parsing_classdef_set_method = true;
-                    $$ = $3;
+                    lexer.m_arguments_is_keyword = true;
                   }
                 ;
 
@@ -1732,14 +1760,14 @@
                 ;
 
 function        : function_beg stash_comment fcn_name
-                  opt_param_list opt_sep opt_list function_end
+                  opt_param_list opt_sep function_body function_end
                   {
                     OCTAVE_YYUSE ($5);
 
                     $$ = parser.make_function ($1, nullptr, $3, $4, $6, $7, $2);
                   }
                 | function_beg stash_comment return_list '=' fcn_name
-                  opt_param_list opt_sep opt_list function_end
+                  opt_param_list opt_sep function_body function_end
                   {
                     OCTAVE_YYUSE ($4);
                     OCTAVE_YYUSE ($7);
@@ -1748,6 +1776,165 @@
                   }
                 ;
 
+function_body   : at_first_executable_stmt opt_list
+                  {
+                    OCTAVE_YYUSE ($1);
+
+                    $$ = $2;
+                  }
+                | function_body1 opt_sep at_first_executable_stmt opt_list
+                  {
+                    OCTAVE_YYUSE ($2);
+
+                    if ($4)
+                      {
+                        for (const auto& elt : *($4))
+                          $1->append (elt);
+                      }
+
+                    $4->clear ();
+                    delete ($4);
+
+                    $$ = $1;
+                  }
+                ;
+
+at_first_executable_stmt
+                : // empty
+                  {
+                    $$ = 0;
+                    lexer.m_arguments_is_keyword = false;
+                  }
+                ;
+
+function_body1  : arguments_block
+                  {
+                    octave::tree_statement *stmt = parser.make_statement ($1);
+
+                    $$ = parser.make_statement_list (stmt);
+                  }
+                | function_body1 opt_sep arguments_block
+                  {
+                    octave::tree_statement *stmt = parser.make_statement ($3);
+
+                    $$ = parser.append_statement_list ($1, $2, stmt, false);
+                  }
+                ;
+
+arguments_block : arguments_beg stash_comment opt_sep args_attr_list
+                  args_validation_list opt_sep END
+                  {
+                    OCTAVE_YYUSE ($3);
+                    OCTAVE_YYUSE ($6);
+
+                    octave::comment_list *lc = $2;
+                    octave::comment_list *tc = lexer.get_comment ();
+
+                    if (! ($$ = parser.make_arguments_block ($1, $4, $5, $7, lc, tc)))
+                      {
+                        // make_arguments_block deleted $4, $5, LC, and TC.
+                        YYABORT;
+                      }
+
+                    lexer.m_arguments_is_keyword = true;
+                  }
+                ;
+
+arguments_beg   : ARGUMENTS
+                  {
+                    $$ = $1;
+                    lexer.m_arguments_is_keyword = false;
+                  }
+                ;
+
+
+args_attr_list  : // empty
+                  { $$ = nullptr; }
+                | '(' identifier ')'
+                  {
+                    OCTAVE_YYUSE ($1);
+                    OCTAVE_YYUSE ($3);
+
+                    // Error if $$ is nullptr.
+                    if  (! ($$ = parser.make_args_attribute_list ($2)))
+                      {
+                        // make_args_attribute_list deleted $2.
+                        YYABORT;
+                      }
+                  }
+                ;
+
+args_validation_list
+                  : arg_validation
+                    { $$ = parser.make_args_validation_list ($1); }
+                  | args_validation_list sep arg_validation
+                    {
+                      OCTAVE_YYUSE ($2);
+
+                      $$ = parser.append_args_validation_list ($1, $3);
+                    }
+                  ;
+
+arg_validation    : identifier size_spec class_name validation_fcns default_value
+                  {
+                    // FIXME: Change grammar to allow IDENTIFIER to be
+                    // be either "NAME" or "NAME '.' NAME".
+
+                    if (! ($$ = parser.make_arg_validation ($1, $2, $3, $4, $5)))
+                      {
+                        // make_arg_validation deleted ...
+                        YYABORT;
+                      }
+                  }
+                ;
+
+size_spec       : // empty
+                  { $$ = nullptr; }
+                | '(' arg_list ')'
+                  {
+                    OCTAVE_YYUSE ($1);
+                    OCTAVE_YYUSE ($3);
+
+                    if (! ($$ = parser.make_arg_size_spec ($2)))
+                      {
+                        // make_arg_size_spec deleted $2.
+                        YYABORT;
+                      }
+                  }
+                ;
+
+class_name      : // empty
+                  { $$ = nullptr; }
+                | identifier
+                  { $$ = $1; }
+                ;
+
+// Use argument list so we can accept anonymous functions.
+validation_fcns : // empty
+                  { $$ = nullptr; }
+                | '{' arg_list '}'
+                  {
+                    OCTAVE_YYUSE ($1);
+                    OCTAVE_YYUSE ($3);
+
+                    if (! ($$ = parser.make_arg_validation_fcns ($2)))
+                      {
+                        // make_arg_validation_fcns deleted $2.
+                        YYABORT;
+                      }
+                  }
+                ;
+
+default_value   : // empty
+                  { $$ = nullptr; }
+                | '=' expression
+                  {
+                    OCTAVE_YYUSE ($1);
+
+                    $$ = $2;
+                  }
+                ;
+
 // ========
 // Classdef
 // ========
@@ -3888,6 +4075,90 @@
     return retval;
   }
 
+  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,
+                                     comment_list *lc, comment_list *tc)
+  {
+    tree_arguments_block *retval = nullptr;
+
+    if (end_token_ok (end_tok, token::arguments_end))
+      {
+        filepos beg_pos = arguments_tok->beg_pos ();
+
+        int l = beg_pos.line ();
+        int c = beg_pos.column ();
+
+        retval = new tree_arguments_block (attr_list, validation_list, l, c);
+      }
+    else
+      {
+        delete attr_list;
+        delete validation_list;
+
+        delete lc;
+        delete tc;
+      }
+
+    return retval;
+  }
+
+  tree_arg_validation *
+  base_parser::make_arg_validation (tree_expression *arg_name,
+                                    tree_arg_size_spec *size_spec,
+                                    tree_identifier *class_name,
+                                    tree_arg_validation_fcns *validation_fcns,
+                                    tree_expression *default_value)
+  {
+    // FIXME: Validate arguments and convert to more specific types
+    // (std::string for arg_name and class_name, etc).
+
+    return new tree_arg_validation (arg_name, size_spec, class_name,
+                                    validation_fcns, 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)
+  {
+    list->append (arg_validation);
+
+    return list;
+  }
+
+  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 (void)
   {
--- a/libinterp/parse-tree/octave.gperf	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/octave.gperf	Tue Jun 01 13:34:57 2021 -0400
@@ -31,6 +31,7 @@
 
 enum octave_kw_id
 {
+  arguments_kw,
   break_kw,
   case_kw,
   catch_kw,
@@ -42,6 +43,7 @@
   end_kw,
   end_try_catch_kw,
   end_unwind_protect_kw,
+  endarguments_kw,
   endclassdef_kw,
   endenumeration_kw,
   endevents_kw,
@@ -84,6 +86,7 @@
 struct octave_kw { const char *name; int tok; octave_kw_id kw_id; };
 
 %%
+arguments, ARGUMENTS, arguments_kw
 break, BREAK, break_kw
 case, CASE, case_kw
 catch, CATCH, catch_kw
@@ -95,6 +98,7 @@
 end, END, end_kw
 end_try_catch, END, end_try_catch_kw
 end_unwind_protect, END, end_unwind_protect_kw
+endarguments, END, endarguments_kw
 endclassdef, END, endclassdef_kw
 endenumeration, END, endenumeration_kw
 endevents, END, endevents_kw
--- a/libinterp/parse-tree/parse.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/parse.h	Tue Jun 01 13:34:57 2021 -0400
@@ -50,7 +50,13 @@
   class comment_list;
   class tree;
   class tree_anon_fcn_handle;
+  class tree_arg_size_spec;
+  class tree_arg_validation;
+  class tree_arg_validation_fcns;
+  class tree_args_block_attribute_list;
+  class tree_args_block_validation_list;
   class tree_argument_list;
+  class tree_arguments_block;
   class tree_array_list;
   class tree_cell;
   class tree_classdef;
@@ -377,6 +383,41 @@
                      octave_user_function *fcn, comment_list *lc,
                      int l, int c);
 
+    // Make an arguments validation block.
+    OCTINTERP_API tree_arguments_block *
+    make_arguments_block (token *arguments_tok,
+                          tree_args_block_attribute_list *attr_list,
+                          tree_args_block_validation_list *validation_list,
+                          token *end_tok, comment_list *lc, comment_list *tc);
+
+    OCTINTERP_API tree_args_block_attribute_list *
+    make_args_attribute_list (tree_identifier *attribute_name);
+
+    // Make an argument validation.
+    OCTINTERP_API tree_arg_validation *
+    make_arg_validation (tree_expression *arg_name,
+                         tree_arg_size_spec *size_spec,
+                         tree_identifier *class_name,
+                         tree_arg_validation_fcns *validation_fcns,
+                         tree_expression *default_value);
+
+    // Make an argument validation list.
+    OCTINTERP_API tree_args_block_validation_list *
+    make_args_validation_list (tree_arg_validation *arg_validation);
+
+    // Append an argument validation to an existing list.
+    OCTINTERP_API tree_args_block_validation_list *
+    append_args_validation_list (tree_args_block_validation_list *list,
+                                 tree_arg_validation *arg_validation);
+
+    // Make an argument size specification object.
+    OCTINTERP_API tree_arg_size_spec *
+    make_arg_size_spec (tree_argument_list *size_args);
+
+    // Make a list of argument validation functions.
+    OCTINTERP_API tree_arg_validation_fcns *
+    make_arg_validation_fcns (tree_argument_list *fcn_args);
+
     // Reset state after parsing function.
     OCTINTERP_API void
     recover_from_parsing_function (void);
--- a/libinterp/parse-tree/pt-all.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-all.h	Tue Jun 01 13:34:57 2021 -0400
@@ -29,6 +29,7 @@
 #include "octave-config.h"
 
 #include "pt.h"
+#include "pt-args-block.h"
 #include "pt-arg-list.h"
 #include "pt-assign.h"
 #include "pt-bp.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/pt-args-block.cc	Tue Jun 01 13:34:57 2021 -0400
@@ -0,0 +1,43 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "pt-args-block.h"
+
+namespace octave
+{
+  tree_args_block_validation_list::~tree_args_block_validation_list (void)
+  {
+    while (! empty ())
+      {
+        auto p = begin ();
+        delete *p;
+        erase (p);
+      }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/pt-args-block.h	Tue Jun 01 13:34:57 2021 -0400
@@ -0,0 +1,254 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2021 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_pt_args_block_h)
+#define octave_pt_args_block_h 1
+
+#include "octave-config.h"
+
+#include "pt-arg-list.h"
+#include "pt-cmd.h"
+#include "pt-exp.h"
+#include "pt-id.h"
+#include "pt-walk.h"
+
+#include "base-list.h"
+
+namespace octave
+{
+  class tree_arg_size_spec
+  {
+  public:
+
+    tree_arg_size_spec (tree_argument_list *size_args)
+      : m_size_args (size_args)
+    { }
+
+    // No copying!
+
+    tree_arg_size_spec (const tree_arg_size_spec&) = delete;
+
+    tree_arg_size_spec& operator = (const tree_arg_size_spec&) = delete;
+
+    ~tree_arg_size_spec (void)
+    {
+      delete m_size_args;
+    }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_arg_size_spec (*this);
+    }
+
+  private:
+
+    tree_argument_list *m_size_args;
+  };
+
+  class tree_arg_validation_fcns
+  {
+  public:
+
+    tree_arg_validation_fcns (tree_argument_list *fcn_args)
+      : m_fcn_args (fcn_args)
+    { }
+
+    // No copying!
+
+    tree_arg_validation_fcns (const tree_arg_validation_fcns&) = delete;
+
+    tree_arg_validation_fcns& operator = (const tree_arg_validation_fcns&) = delete;
+
+    ~tree_arg_validation_fcns (void)
+    {
+      delete m_fcn_args;
+    }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_arg_validation_fcns (*this);
+    }
+
+  private:
+
+    tree_argument_list *m_fcn_args;
+  };
+
+  class tree_arg_validation
+  {
+  public:
+
+    tree_arg_validation (tree_expression *arg_name,
+                         tree_arg_size_spec *size_spec,
+                         tree_identifier *class_name,
+                         tree_arg_validation_fcns *validation_fcns,
+                         tree_expression *default_value)
+      : m_arg_name (arg_name), m_size_spec (size_spec),
+        m_class_name (class_name), m_validation_fcns (validation_fcns),
+        m_default_value (default_value)
+    { }
+
+    // No copying!
+
+    tree_arg_validation (const tree_arg_validation&) = delete;
+
+    tree_arg_validation& operator = (const tree_arg_validation&) = delete;
+
+    ~tree_arg_validation (void)
+    {
+      delete m_arg_name;
+      delete m_size_spec;
+      delete m_class_name;
+      delete m_validation_fcns;
+      delete m_default_value;
+    }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_arg_validation (*this);
+    }
+
+  private:
+
+    // May be a simple identifier or an identifier followed by a single
+    // field name.
+    tree_expression *m_arg_name;
+    tree_arg_size_spec *m_size_spec;
+    tree_identifier *m_class_name;
+    tree_arg_validation_fcns *m_validation_fcns;
+    tree_expression *m_default_value;
+  };
+
+  class tree_args_block_validation_list : public base_list<tree_arg_validation *>
+  {
+  public:
+
+    tree_args_block_validation_list (void) { }
+
+    tree_args_block_validation_list (tree_arg_validation *a) { append (a); }
+
+    tree_args_block_validation_list (const base_list<tree_arg_validation *>& a)
+      : base_list<tree_arg_validation *> (a)
+    { }
+
+    // No copying!
+
+    tree_args_block_validation_list (const tree_args_block_validation_list&) = delete;
+
+    tree_args_block_validation_list& operator = (const tree_args_block_validation_list&) = delete;
+
+    ~tree_args_block_validation_list (void);
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_args_block_validation_list (*this);
+    }
+  };
+
+  // FIXME: Maybe make this object an actual list even though we don't
+  // currently need it?
+
+  class tree_args_block_attribute_list
+  {
+  public:
+
+    tree_args_block_attribute_list (tree_identifier *id = nullptr)
+      : m_id (id)
+    { }
+
+    // No copying!
+
+    tree_args_block_attribute_list (const tree_args_block_attribute_list&) = delete;
+
+    tree_args_block_attribute_list& operator = (const tree_args_block_attribute_list&) = delete;
+
+    ~tree_args_block_attribute_list (void)
+    {
+      delete m_id;
+    }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_args_block_attribute_list (*this);
+    }
+
+  private:
+
+    tree_identifier *m_id;
+  };
+
+  // Arguments block.
+
+  class tree_arguments_block : public tree_command
+  {
+  public:
+
+    tree_arguments_block (tree_args_block_attribute_list *attr_list,
+                          tree_args_block_validation_list *validation_list,
+                          int l = -1, int c = -1)
+      : tree_command (l, c), m_attr_list (attr_list),
+        m_validation_list (validation_list),
+        m_lead_comm (nullptr), m_trail_comm (nullptr)
+    { }
+
+    // No copying!
+
+    tree_arguments_block (const tree_arguments_block&) = delete;
+
+    tree_arguments_block& operator = (const tree_arguments_block&) = delete;
+
+    ~tree_arguments_block (void)
+    {
+      delete m_attr_list;
+      delete m_validation_list;
+
+      delete m_lead_comm;
+      delete m_trail_comm;
+    }
+
+    comment_list * leading_comment (void) { return m_lead_comm; }
+
+    comment_list * trailing_comment (void) { return m_trail_comm; }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_arguments_block (*this);
+    }
+
+  private:
+
+    tree_args_block_attribute_list *m_attr_list;
+
+    tree_args_block_validation_list *m_validation_list;
+
+    // Comment preceding ARGUMENTS token.
+    comment_list *m_lead_comm;
+
+    // Comment preceding ENDARGUMENTS token.
+    comment_list *m_trail_comm;
+  };
+}
+
+#endif
--- a/libinterp/parse-tree/pt-bp.cc	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-bp.cc	Tue Jun 01 13:34:57 2021 -0400
@@ -73,6 +73,42 @@
   }
 
   void
+  tree_breakpoint::visit_arguments_block (tree_arguments_block&)
+  {
+    // FIXME
+  }
+
+  void
+  tree_breakpoint::visit_args_block_attribute_list (tree_args_block_attribute_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_breakpoint::visit_args_block_validation_list (tree_args_block_validation_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_breakpoint::visit_arg_validation (tree_arg_validation&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_breakpoint::visit_arg_size_spec (tree_arg_size_spec&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_breakpoint::visit_arg_validation_fcns (tree_arg_validation_fcns&)
+  {
+    panic_impossible ();
+  }
+
+  void
   tree_breakpoint::visit_binary_expression (tree_binary_expression&)
   {
     panic_impossible ();
--- a/libinterp/parse-tree/pt-bp.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-bp.h	Tue Jun 01 13:34:57 2021 -0400
@@ -62,6 +62,18 @@
 
     void visit_argument_list (tree_argument_list&);
 
+    void visit_arguments_block (tree_arguments_block&);
+
+    void visit_args_block_attribute_list (tree_args_block_attribute_list&);
+
+    void visit_args_block_validation_list (tree_args_block_validation_list&);
+
+    void visit_arg_validation (tree_arg_validation&);
+
+    void visit_arg_size_spec (tree_arg_size_spec&);
+
+    void visit_arg_validation_fcns (tree_arg_validation_fcns&);
+
     void visit_binary_expression (tree_binary_expression&);
 
     void visit_break_command (tree_break_command&);
--- a/libinterp/parse-tree/pt-eval.cc	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Tue Jun 01 13:34:57 2021 -0400
@@ -1245,6 +1245,42 @@
   }
 
   void
+  tree_evaluator::visit_arguments_block (tree_arguments_block&)
+  {
+    warning ("function arguments validation blocks are not supported");
+  }
+
+  void
+  tree_evaluator::visit_args_block_attribute_list (tree_args_block_attribute_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_evaluator::visit_args_block_validation_list (tree_args_block_validation_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_evaluator::visit_arg_validation (tree_arg_validation&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_evaluator::visit_arg_size_spec (tree_arg_size_spec&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_evaluator::visit_arg_validation_fcns (tree_arg_validation_fcns&)
+  {
+    panic_impossible ();
+  }
+
+  void
   tree_evaluator::visit_binary_expression (tree_binary_expression&)
   {
     panic_impossible ();
--- a/libinterp/parse-tree/pt-eval.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Tue Jun 01 13:34:57 2021 -0400
@@ -212,6 +212,18 @@
 
     void visit_argument_list (tree_argument_list&);
 
+    void visit_arguments_block (tree_arguments_block&);
+
+    void visit_args_block_attribute_list (tree_args_block_attribute_list&);
+
+    void visit_args_block_validation_list (tree_args_block_validation_list&);
+
+    void visit_arg_validation (tree_arg_validation&);
+
+    void visit_arg_size_spec (tree_arg_size_spec&);
+
+    void visit_arg_validation_fcns (tree_arg_validation_fcns&);
+
     void visit_binary_expression (tree_binary_expression&);
 
     void visit_boolean_expression (tree_boolean_expression&);
--- a/libinterp/parse-tree/pt-pr-code.cc	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-pr-code.cc	Tue Jun 01 13:34:57 2021 -0400
@@ -76,6 +76,45 @@
   }
 
   void
+  tree_print_code::visit_arguments_block (tree_arguments_block&)
+  {
+    indent ();
+
+    // FIXME
+    m_os << "arguments ... endarguments";
+  }
+
+  void
+  tree_print_code::visit_args_block_attribute_list (tree_args_block_attribute_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_print_code::visit_args_block_validation_list (tree_args_block_validation_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_print_code::visit_arg_validation (tree_arg_validation&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_print_code::visit_arg_size_spec (tree_arg_size_spec&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_print_code::visit_arg_validation_fcns (tree_arg_validation_fcns&)
+  {
+    panic_impossible ();
+  }
+
+  void
   tree_print_code::visit_binary_expression (tree_binary_expression& expr)
   {
     indent ();
--- a/libinterp/parse-tree/pt-pr-code.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-pr-code.h	Tue Jun 01 13:34:57 2021 -0400
@@ -70,6 +70,18 @@
 
     void visit_argument_list (tree_argument_list&);
 
+    void visit_arguments_block (tree_arguments_block&);
+
+    void visit_args_block_attribute_list (tree_args_block_attribute_list&);
+
+    void visit_args_block_validation_list (tree_args_block_validation_list&);
+
+    void visit_arg_validation (tree_arg_validation&);
+
+    void visit_arg_size_spec (tree_arg_size_spec&);
+
+    void visit_arg_validation_fcns (tree_arg_validation_fcns&);
+
     void visit_binary_expression (tree_binary_expression&);
 
     void visit_break_command (tree_break_command&);
--- a/libinterp/parse-tree/pt-walk.cc	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-walk.cc	Tue Jun 01 13:34:57 2021 -0400
@@ -49,6 +49,36 @@
       }
   }
 
+  void tree_walker::visit_arguments_block (tree_arguments_block&)
+  {
+    // FIXME?
+  }
+
+  void tree_walker::visit_args_block_attribute_list (tree_args_block_attribute_list&)
+  {
+    // FIXME?
+  }
+
+  void tree_walker::visit_args_block_validation_list (tree_args_block_validation_list&)
+  {
+    // FIXME?
+  }
+
+  void tree_walker::visit_arg_validation (tree_arg_validation&)
+  {
+    // FIXME?
+  }
+
+  void tree_walker::visit_arg_size_spec (tree_arg_size_spec&)
+  {
+    // FIXME?
+  }
+
+  void tree_walker::visit_arg_validation_fcns (tree_arg_validation_fcns&)
+  {
+    // FIXME?
+  }
+
   void tree_walker::visit_binary_expression (tree_binary_expression& expr)
   {
     tree_expression *op1 = expr.lhs ();
--- a/libinterp/parse-tree/pt-walk.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/pt-walk.h	Tue Jun 01 13:34:57 2021 -0400
@@ -41,7 +41,13 @@
   //  class tree_black_hole
 
   class tree_anon_fcn_handle;
+  class tree_arg_size_spec;
+  class tree_arg_validation;
+  class tree_arg_validation_fcns;
+  class tree_args_block_attribute_list;
+  class tree_args_block_validation_list;
   class tree_argument_list;
+  class tree_arguments_block;
   class tree_binary_expression;
   class tree_boolean_expression;
   class tree_compound_binary_expression;
@@ -121,6 +127,18 @@
 
     virtual void visit_argument_list (tree_argument_list&);
 
+    virtual void visit_arguments_block (tree_arguments_block&);
+
+    virtual void visit_args_block_attribute_list (tree_args_block_attribute_list&);
+
+    virtual void visit_args_block_validation_list (tree_args_block_validation_list&);
+
+    virtual void visit_arg_validation (tree_arg_validation&);
+
+    virtual void visit_arg_size_spec (tree_arg_size_spec&);
+
+    virtual void visit_arg_validation_fcns (tree_arg_validation_fcns&);
+
     virtual void visit_binary_expression (tree_binary_expression&);
 
     virtual void visit_boolean_expression (tree_boolean_expression&);
--- a/libinterp/parse-tree/token.h	Tue Jun 01 16:57:40 2021 -0400
+++ b/libinterp/parse-tree/token.h	Tue Jun 01 13:34:57 2021 -0400
@@ -52,6 +52,7 @@
     enum end_tok_type
     {
       simple_end,
+      arguments_end,
       classdef_end,
       enumeration_end,
       events_end,