expressions: Major work to improve error messages.
[pspp] / src / language / expressions / generate.py
index e530cd8208a11542c71e62f364e98890258aa974..ffe780a3c35ab9cbfdd07f016dcc857890605a06 100644 (file)
@@ -46,6 +46,8 @@ def init_all_types():
                      'string', 'ss', 'empty_string'),
         Type.new_any('boolean', 'double', 'number', 'n',
                      'boolean', 'ns', 'SYSMIS'),
+        Type.new_any('integer', 'int', 'number', 'n',
+                     'integer', 'ns', 'SYSMIS'),
 
         # Format types.
         Type.new_atom('format'),
@@ -55,8 +57,6 @@ def init_all_types():
                       'format', 'f', 'num_output_format'),
 
         # Integer types.
-        Type.new_leaf('integer', 'int', 'integer', 'n',
-                      'integer'),
         Type.new_leaf('pos_int', 'int', 'integer', 'n',
                       'positive_integer_constant'),
 
@@ -72,6 +72,12 @@ def init_all_types():
         # Vectors.
         Type.new_leaf('vector', 'const struct vector *',
                       'vector', 'v', 'vector'),
+        Type.new_any('num_vec_elem', 'double', 'number', 'n',
+                     'number', 'ns', 'SYSMIS'),
+
+        # Types as leaves or auxiliary data.
+        Type.new_leaf('expr_node', 'const struct expr_node *',
+                      'expr_node', 'e', 'expr_node'),
 
         # Types that appear only as auxiliary data.
         Type.new_auxonly('expression', 'struct expression *', 'e'),
@@ -118,14 +124,14 @@ class Type:
 
         'c_type' is the type used for C objects of this type.
 
+        'atom' should be the name of the member of "union
+        operation_data" that holds a value of this type.
+
         'mangle' should be a short string for name mangling purposes,
         to allow overloading functions with the same name but
         different argument types.  Use the same 'mangle' for two
         different types if those two types should not be overloaded.
 
-        'atom' should be the name of the member of "union
-        operation_data" that holds a value of this type.
-
         'human_name' should be a name to use when describing this type
         to the user (see Op.prototype()).
 
@@ -236,7 +242,7 @@ class Op:
             for arg in self.args:
                 arg_name = 'arg_%s' % arg.name
                 if arg.idx is None:
-                    if arg.type_.name in ['number', 'boolean']:
+                    if arg.type_.name in ['number', 'boolean', 'integer']:
                         sysmis_cond += ['!is_valid (%s)' % arg_name]
                 elif arg.type_.name == 'number':
                     a = arg_name
@@ -313,6 +319,10 @@ class Op:
             flags += ['OPF_PERM_ONLY']
         if self.no_abbrev:
             flags += ['OPF_NO_ABBREV']
+        for aux in self.aux:
+            if aux['TYPE'].name == 'expr_node':
+                flags += ['OPF_EXPR_NODE']
+                break
         return ' | '.join(flags) if flags else '0'
 
 
@@ -361,7 +371,7 @@ def parse_input():
         return_type = Type.parse()
         if return_type is None:
             return_type = types['number']
-        if return_type.name not in ['number', 'string', 'boolean']:
+        if return_type.name not in ['number', 'string', 'boolean', 'num_vec_elem']:
             die('%s is not a valid return type' % return_type.name)
 
         if token == 'operator':
@@ -665,17 +675,14 @@ class Arg:
 
 def print_header():
     """Prints the output file header."""
-    out_file.write("""\
-/* %s
-   Generated from %s by generate.py.
-   Do not modify! */
-
-""" % (out_file_name, in_file_name))
+    sys.stdout.write("""\
+/* Generated by generate.py. Do not modify! */
+""")
 
 
 def print_trailer():
     """Prints the output file trailer."""
-    out_file.write("""\
+    sys.stdout.write("""\
 
 /*
    Local Variables:
@@ -687,7 +694,7 @@ def print_trailer():
 
 
 def generate_evaluate_h():
-    out_file.write('#include "helpers.h"\n\n')
+    sys.stdout.write('#include "helpers.h"\n\n')
 
     for op in order:
         if op.unimplemented:
@@ -710,26 +717,34 @@ def generate_evaluate_h():
         else:
             statements = '  return %s;\n' % op.expression
 
-        out_file.write('static inline %s\n' % op.returns.c_type)
-        out_file.write('eval_%s (%s)\n' % (op.opname, ', '.join(args)))
-        out_file.write('{\n')
-        out_file.write(statements)
-        out_file.write('}\n\n')
+        sys.stdout.write('static inline %s\n' % op.returns.c_type)
+        sys.stdout.write('eval_%s (%s)\n' % (op.opname, ', '.join(args)))
+        sys.stdout.write('{\n')
+        sys.stdout.write(statements)
+        sys.stdout.write('}\n\n')
 
 
 def generate_evaluate_inc():
     for op in order:
         if op.unimplemented:
-            out_file.write('case %s:\n' % op.opname)
-            out_file.write('  NOT_REACHED ();\n\n')
+            sys.stdout.write('case %s:\n' % op.opname)
+            sys.stdout.write('  NOT_REACHED ();\n\n')
             continue
 
         decls = []
         args = []
         for arg in op.args:
             type_ = arg.type_
-            c_type = type_.c_type
-            args += ['arg_%s' % arg.name]
+            if type_.c_type == 'int ':
+                c_type = 'double '
+                if op.absorb_miss:
+                    args += ['arg_%s == SYSMIS ? INT_MIN : arg_%s'
+                             % (arg.name, arg.name)]
+                else:
+                    args += ['arg_%s' % arg.name]
+            else:
+                c_type = type_.c_type
+                args += ['arg_%s' % arg.name]
             if arg.idx is None:
                 decl = '%sarg_%s' % (c_type, arg.name)
                 if type_.role == 'any':
@@ -755,6 +770,10 @@ def generate_evaluate_inc():
                 decls += ['%saux_%s = op++->%s'
                           % (type_.c_type, name, type_.atom)]
                 args += ['aux_%s' % name]
+            elif type_.name == 'expr_node':
+                decls += ['%saux_%s = op++->node'
+                          % (type_.c_type, name)]
+                args += ['aux_%s' % name]
             elif type_.role == 'auxonly':
                 args += [type_.auxonly_value]
 
@@ -766,29 +785,29 @@ def generate_evaluate_inc():
 
         stack = op.returns.stack
 
-        out_file.write('case %s:\n' % op.opname)
+        sys.stdout.write('case %s:\n' % op.opname)
         if decls:
-            out_file.write('  {\n')
+            sys.stdout.write('  {\n')
             for decl in decls:
-                out_file.write('    %s;\n' % decl)
+                sys.stdout.write('    %s;\n' % decl)
             if sysmis_cond is not None:
                 miss_ret = op.returns.missing_value
-                out_file.write('    *%s++ = force_sysmis ? %s : %s;\n'
+                sys.stdout.write('    *%s++ = force_sysmis ? %s : %s;\n'
                                % (stack, miss_ret, result))
             else:
-                out_file.write('    *%s++ = %s;\n' % (stack, result))
-            out_file.write('  }\n')
+                sys.stdout.write('    *%s++ = %s;\n' % (stack, result))
+            sys.stdout.write('  }\n')
         else:
-            out_file.write('  *%s++ = %s;\n' % (stack, result))
-        out_file.write('  break;\n\n')
+            sys.stdout.write('  *%s++ = %s;\n' % (stack, result))
+        sys.stdout.write('  break;\n\n')
 
 
 def generate_operations_h():
-    out_file.write('#include <stdlib.h>\n')
-    out_file.write('#include <stdbool.h>\n\n')
+    sys.stdout.write('#include <stdlib.h>\n')
+    sys.stdout.write('#include <stdbool.h>\n\n')
 
-    out_file.write('typedef enum')
-    out_file.write('  {\n')
+    sys.stdout.write('typedef enum')
+    sys.stdout.write('  {\n')
     atoms = []
     for type_ in types.values():
         if type_.role != 'auxonly':
@@ -800,10 +819,10 @@ def generate_operations_h():
     print_operations('operator', 'OP_function_last + 1',
                      [o.opname for o in opers])
     print_range('OP_composite', 'OP_function_first', 'OP_operator_last')
-    out_file.write(',\n\n')
+    sys.stdout.write(',\n\n')
     print_range('OP', 'OP_atom_first', 'OP_composite_last')
-    out_file.write('\n  }\n')
-    out_file.write('operation_type, atom_type;\n')
+    sys.stdout.write('\n  }\n')
+    sys.stdout.write('operation_type, atom_type;\n')
 
     print_predicate('is_operation', 'OP')
     for key in ('atom', 'composite', 'function', 'operator'):
@@ -811,37 +830,37 @@ def generate_operations_h():
 
 
 def print_operations(type_, first, names):
-    out_file.write('    /* %s types. */\n' % type_.title())
-    out_file.write('    %s = %s,\n' % (names[0], first))
+    sys.stdout.write('    /* %s types. */\n' % type_.title())
+    sys.stdout.write('    %s = %s,\n' % (names[0], first))
     for name in names[1:]:
-        out_file.write('    %s,\n' % name)
+        sys.stdout.write('    %s,\n' % name)
     print_range('OP_%s' % type_, names[0], names[-1])
-    out_file.write(',\n\n')
+    sys.stdout.write(',\n\n')
 
 
 def print_range(prefix, first, last):
-    out_file.write('    %s_first = %s,\n' % (prefix, first))
-    out_file.write('    %s_last = %s,\n' % (prefix, last))
-    out_file.write('    n_%s = %s_last - %s_first + 1'
+    sys.stdout.write('    %s_first = %s,\n' % (prefix, first))
+    sys.stdout.write('    %s_last = %s,\n' % (prefix, last))
+    sys.stdout.write('    n_%s = %s_last - %s_first + 1'
                    % (prefix, prefix, prefix))
 
 
 def print_predicate(function, category):
-    out_file.write('\nstatic inline bool\n')
-    out_file.write('%s (operation_type op)\n' % function)
-    out_file.write('{\n')
+    sys.stdout.write('\nstatic inline bool\n')
+    sys.stdout.write('%s (operation_type op)\n' % function)
+    sys.stdout.write('{\n')
     if function != 'is_operation':
-        out_file.write('  assert (is_operation (op));\n')
-    out_file.write('  return op >= %s_first && op <= %s_last;\n'
+        sys.stdout.write('  assert (is_operation (op));\n')
+    sys.stdout.write('  return op >= %s_first && op <= %s_last;\n'
                    % (category, category))
-    out_file.write('}\n')
+    sys.stdout.write('}\n')
 
 
 def generate_optimize_inc():
     for op in order:
         if not op.optimizable or op.unimplemented:
-            out_file.write('case %s:\n' % op.opname)
-            out_file.write('  NOT_REACHED ();\n\n')
+            sys.stdout.write('case %s:\n' % op.opname)
+            sys.stdout.write('  NOT_REACHED ();\n\n')
             continue
 
         decls = []
@@ -881,9 +900,8 @@ def generate_optimize_inc():
         for aux in op.aux:
             type_ = aux['TYPE']
             if type_.role == 'leaf':
-                func = 'get_%s_arg' % type_.atom
-                args += '%s (node, %s)' % (func, arg_idx)
-                arg_idx += 1
+                assert type_.name == 'expr_node'
+                args += ['node']
             elif type_.role == 'auxonly':
                 args += [type_.auxonly_value]
             else:
@@ -896,28 +914,28 @@ def generate_optimize_inc():
                       % (op.returns.c_type, miss_ret, result)]
             result = 'result'
 
-        out_file.write('case %s:\n' % op.opname)
+        sys.stdout.write('case %s:\n' % op.opname)
         alloc_func = 'expr_allocate_%s' % op.returns.name
         if decls:
-            out_file.write('  {\n')
+            sys.stdout.write('  {\n')
             for decl in decls:
-                out_file.write('    %s;\n' % decl)
-            out_file.write('    return %s (e, %s);\n' % (alloc_func, result))
-            out_file.write('  }\n')
+                sys.stdout.write('    %s;\n' % decl)
+            sys.stdout.write('    return %s (e, %s);\n' % (alloc_func, result))
+            sys.stdout.write('  }\n')
         else:
-            out_file.write('  return %s (e, %s);\n' % (alloc_func, result))
-        out_file.write('\n')
+            sys.stdout.write('  return %s (e, %s);\n' % (alloc_func, result))
+        sys.stdout.write('\n')
 
 
 def generate_parse_inc():
     members = ['""', '""', '0', '0', '0', '{}', '0', '0']
-    out_file.write('{%s},\n' % ', '.join(members))
+    sys.stdout.write('{%s},\n' % ', '.join(members))
 
     for type_ in types.values():
         if type_.role != 'auxonly':
             members = ('"%s"' % type_.name, '"%s"' % type_.human_name,
                        '0', 'OP_%s' % type_.name, '0', '{}', '0', '0')
-            out_file.write('{%s},\n' % ', '.join(members))
+            sys.stdout.write('{%s},\n' % ', '.join(members))
 
     for op in order:
         members = []
@@ -939,15 +957,16 @@ def generate_parse_inc():
 
         members += ['%s' % (op.array_arg().times if op.array_arg() else 0)]
 
-        out_file.write('{%s},\n' % ', '.join(members))
+        sys.stdout.write('{%s},\n' % ', '.join(members))
 
 
 def usage():
     print("""\
 %s, for generating expression parsers and evaluators from definitions
-usage: generate.py -o OUTPUT [-i INPUT] [-h]
+usage: generate.py -o OUTPUT_TYPE [-i INPUT] [-h] > OUTPUT
   -i INPUT    input file containing definitions (default: operations.def)
-  -o OUTPUT   output file
+  -o OUTPUT   output file type, one of: evaluate.h, evaluate.inc,
+              operations.h, optimize.inc, parse.inc    
   -h          display this help message
 """ % argv0)
     sys.exit(0)
@@ -979,21 +998,20 @@ if __name__ == '__main__':
             '(use --help for help)' % argv0)
 
     in_file = open(in_file_name, 'r')
-    out_file = open(out_file_name, 'w')
 
     init_all_types()
     parse_input()
 
     print_header()
-    if out_file_name.endswith('evaluate.h'):
+    if out_file_name == 'evaluate.h':
         generate_evaluate_h()
-    elif out_file_name.endswith('evaluate.inc'):
+    elif out_file_name == 'evaluate.inc':
         generate_evaluate_inc()
-    elif out_file_name.endswith('operations.h'):
+    elif out_file_name == 'operations.h':
         generate_operations_h()
-    elif out_file_name.endswith('optimize.inc'):
+    elif out_file_name == 'optimize.inc':
         generate_optimize_inc()
-    elif out_file_name.endswith('parse.inc'):
+    elif out_file_name == 'parse.inc':
         generate_parse_inc()
     else:
         die('%s: unknown output type' % argv0)