Add dict type, implement type checking in generate_expression()
authorNick Downing <nick@ndcode.org>
Mon, 28 Jan 2019 06:57:19 +0000 (17:57 +1100)
committerNick Downing <nick@ndcode.org>
Mon, 28 Jan 2019 10:32:29 +0000 (21:32 +1100)
.gitignore
n.sh
pitree.l
pitree.py
pitree.t
pitree.y

index b276a60..9985c3f 100644 (file)
@@ -1,6 +1,5 @@
 __pycache__
 /lex_yy.py
-/out
 /t_def.py
 /tests
 /y_tab.py
diff --git a/n.sh b/n.sh
index 2676045..e3d1c6b 100755 (executable)
--- a/n.sh
+++ b/n.sh
@@ -2,24 +2,20 @@
 if ! test -d tests
 then
   mkdir tests
-  ./ast_to_pitree.py <../c_to_python.git/ast.py >tests/ansi_c.t
-  ./ast_to_pitree.py <../pilex.git/ast.py >tests/pilex.t
-  ./ast_to_pitree.py <../piyacc.git/ast.py >tests/piyacc.t
-  ./ast_to_pitree.py <../piyacc.git/tests_ast/ast.py >tests/cal.t
+  cp ../c_to_python.git/ansi_c.t tests
+  cp ../c_to_python.git/t_def.py tests/ansi_c.py.ok
+  cp ../pilex.git/pilex.t tests
+  cp ../pilex.git/t_def.py tests/pilex.py.ok
+  cp ../piyacc.git/piyacc.t tests
+  cp ../piyacc.git/t_def.py tests/piyacc.py.ok
+  cp ../piyacc.git/tests_ast/cal_py.t tests
+  cp ../piyacc.git/tests_ast/t_def.py tests/cal_py.py.ok
 fi
-if ! test -d out
-then
-  mkdir out
-  ./expected.sh ../c_to_python.git/ast.py tests/ansi_c.t >out/ast_ansi_c.py.ok
-  ./expected.sh ../pilex.git/ast.py tests/pilex.t >out/ast_pilex.py.ok
-  ./expected.sh ../piyacc.git/ast.py tests/piyacc.t >out/ast_piyacc.py.ok
-  ./expected.sh ../piyacc.git/tests_ast/ast.py tests/cal.t >out/ast_cal.py.ok
-fi
-./pitree.py --python -o out/ast_ansi_c.py tests/ansi_c.t
-diff -q out/ast_ansi_c.py.ok out/ast_ansi_c.py
-./pitree.py --python -o out/ast_pilex.py tests/pilex.t
-diff -q out/ast_pilex.py.ok out/ast_pilex.py
-./pitree.py --python -o out/ast_piyacc.py tests/piyacc.t
-diff -q out/ast_piyacc.py.ok out/ast_piyacc.py
-./pitree.py --python -o out/ast_cal.py tests/cal.t
-diff -q out/ast_cal.py.ok out/ast_cal.py
+./pitree.py --python -o tests/ansi_c.py tests/ansi_c.t
+diff -q tests/ansi_c.py.ok tests/ansi_c.py
+./pitree.py --python -o tests/pilex.py tests/pilex.t
+diff -q tests/pilex.py.ok tests/pilex.py
+./pitree.py --python -o tests/piyacc.py tests/piyacc.t
+diff -q tests/piyacc.py.ok tests/piyacc.py
+./pitree.py --python -o tests/cal_py.py tests/cal_py.t
+diff -q tests/cal_py.py.ok tests/cal_py.py
index 319c5c8..f8c544f 100644 (file)
--- a/pitree.l
+++ b/pitree.l
@@ -50,6 +50,7 @@
 
 <SECTION2>{
   "."                          return ord('.')
+  ","                          return ord(',')
   "="                          return ord('=')
   ":"                          return ord(':')
   ";"                          return ord(';')
@@ -61,6 +62,7 @@
   "}"                          return ord('}')
   "bool"                       return y_tab.KEYWORD_BOOL
   "class"                      return y_tab.KEYWORD_CLASS
+  "dict"                       return y_tab.KEYWORD_DICT
   "int"                                return y_tab.KEYWORD_INT
   "list"                       return y_tab.KEYWORD_LIST
   "ref"                                return y_tab.KEYWORD_REF
index 750af66..f77a553 100755 (executable)
--- a/pitree.py
+++ b/pitree.py
@@ -60,19 +60,19 @@ in_file = args[0]
 
 with open(in_file) as fin:
   if in_file[-4:] == '.xml':
-    _ast = element.deserialize(fin, t_def.factory)
+    ast = element.deserialize(fin, t_def.factory)
   else:
     import lex_yy
     import y_tab
     lex_yy.yyin = fin
-    _ast = y_tab.yyparse(t_def.AST)
-#element.serialize(_ast, 'a.xml', 'utf-8')
-#_ast = element.deserialize('a.xml', t_def.factory, 'utf-8')
-#_t_def.post_process()
-#element.serialize(_ast, 'b.xml', 'utf-8')
-#_ast = element.deserialize('b.xml', t_def.factory, 'utf-8')
+    ast = y_tab.yyparse(t_def.AST)
+#element.serialize(ast, 'a.xml', 'utf-8')
+#ast = element.deserialize('a.xml', t_def.factory, 'utf-8')
+ast.post_process()
+#element.serialize(ast, 'b.xml', 'utf-8')
+#ast = element.deserialize('b.xml', t_def.factory, 'utf-8')
 (generate_py.generate_py if python else generate_c.generate_c)(
-  _ast,
+  ast,
   home_dir,
   skel_file,
   out_file
index 01574b6..ce7fbbb 100644 (file)
--- a/pitree.t
+++ b/pitree.t
 
 class AST {
   /* internal classes: */
-  class ClassOrFieldDef;
   class Expression;
-  class Type;
+  class Type {
+    bool is_json = False;
+  };
+
   /* syntax classes: */
   class BaseClass;
-  class ClassBody;
   class ClassName;
   class DefaultValue;
+  class DictInitializer;
   class Identifier;
   class LiteralBool: Expression {
     bool value = False;
   };
+  class LiteralDict: Expression;
   class LiteralInt: Expression {
     str sign = '';
     int base = -1;
@@ -46,24 +49,30 @@ class AST {
   class LiteralRef: Expression;
   class LiteralSet: Expression;
   class LiteralStr: Expression;
+  class Section1 {
+    class CodeBlock;
+  };
+  class Section2 {
+    /* internal classes: */
+    class ClassOrFieldDef;
+
+    /* syntax classes: */
+    class ClassBody;
+    class ClassDef: ClassOrFieldDef;
+    class FieldDef: ClassOrFieldDef;
+  };
   class Text {
     class Escape {
       int value = -1;
     };
   };
   class TypeBool: Type;
+  class TypeDict: Type;
   class TypeInt: Type;
   class TypeList: Type;
   class TypeRef: Type;
   class TypeSet: Type;
   class TypeStr: Type;
-  class Section1 {
-    class CodeBlock;
-  };
-  class Section2 {
-    class ClassDef;
-    class FieldDef;
-  };
 };
 
 %%
@@ -90,7 +99,7 @@ class Context:
 def factory(tag, attrib = {}, *args, **kwargs):
   return tag_to_class.get(tag, element.Element)(tag, attrib, *args, **kwargs)
 
-@method(AST.ClassOrFieldDef)
+@method(AST.Section2.ClassOrFieldDef)
 def generate_class_or_field_def(self, context):
   raise NotImplementedError
 @method(AST.Section2.ClassDef)
@@ -154,7 +163,7 @@ def generate_class_or_field_def(self, context):
             context.indent,
             i[1].get_text(),
             (
-              ' = {0:s}'.format(i[2][0].generate_expression(context))
+              ' = {0:s}'.format(i[2][0].generate_expression(i[0]))
             if len(i[2]) else
               ''
             )
@@ -303,34 +312,53 @@ def generate_class_or_field_def(self, context):
 del generate_class_or_field_def
 
 @method(AST.Expression)
-def generate_expression(self, context):
-  raiseNotImplementedError
+def generate_expression(self, _type):
+  raise NotImplementedError
 @method(AST.LiteralBool)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeBool)
   return "True" if self.value else "False"
+@method(AST.LiteralDict)
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeDict)
+  return '{{{0:s}}}'.format(
+    ', '.join(
+      [
+        '{0:s}: {1:s}'.format(
+          i[0].generate_expression(_type[0]),
+          i[1].generate_expression(_type[1])
+        )
+        for i in self
+      ]
+    )
+  )
 @method(AST.LiteralInt)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeInt)
   return str(self.get_value())
 @method(AST.LiteralList)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeList)
   return '[{0:s}]'.format(
     ', '.join(
       [
-        i.generate_expression(context)
+        i.generate_expression(_type[0])
         for i in self
       ]
     )
   )
 @method(AST.LiteralRef)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeRef)
   return 'None'
 @method(AST.LiteralSet)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeSet)
   return (
     'set([{0:s}])'.format(
       ', '.join(
         [
-          i.generate_expression(context)
+          i.generate_expression(_type[0])
           for i in self
         ]
       )
@@ -339,7 +367,8 @@ def generate_expression(self, context):
     'set()'
   )
 @method(AST.LiteralStr)
-def generate_expression(self, context):
+def generate_expression(self, _type):
+  assert isinstance(_type, AST.TypeStr)
   return "'{0:s}'".format(
     self[0].get_text().replace('\\', '\\\\').replace('\'', '\\\'')
   )
@@ -351,6 +380,14 @@ def generate_expression_serialized(self, context):
 @method(AST.LiteralBool)
 def generate_expression_serialized(self, context):
   return "true" if self.value else "false"
+@method(AST.LiteralDict)
+def generate_expression_serialized(self, context):
+  return ' '.join(
+    [
+      i.generate_expression_serialized(context)
+      for i in self
+    ]
+  )
 @method(AST.LiteralInt)
 def generate_expression_serialized(self, context):
   return str(self.get_value())
@@ -495,3 +532,53 @@ def get_value(self):
   value = int(self.digits, self.base)
   return -value if len(self.sign) else value
 del get_value
+
+@method(AST)
+def post_process(self):
+  self[1].post_process()
+@method(AST.Section2)
+def post_process(self):
+  for i in self:
+    i.post_process()
+@method(AST.Section2.ClassOrFieldDef)
+def post_process(self):
+  raise NotImplementedError
+@method(AST.Section2.ClassBody)
+def post_process(self):
+  for i in self:
+    i.post_process()
+@method(AST.Section2.ClassDef)
+def post_process(self):
+  self[2].post_process()
+@method(AST.Section2.FieldDef)
+def post_process(self):
+  self[0].post_process()
+@method(AST.Type)
+def post_process(self):
+  raise NotImplementedError
+@method(AST.TypeBool)
+def post_process(self):
+  self.is_json = True
+@method(AST.TypeDict)
+def post_process(self):
+  self[0].post_process()
+  self[1].post_process()
+  self.is_json = isinstance(self[0], AST.TypeString) and self[1].is_json
+@method(AST.TypeInt)
+def post_process(self):
+  self.is_json = True
+@method(AST.TypeList)
+def post_process(self):
+  self[0].post_process()
+  self.is_json = self[0].is_json
+@method(AST.TypeRef)
+def post_process(self):
+  self.is_json = False
+@method(AST.TypeSet)
+def post_process(self):
+  self[0].post_process()
+  self.is_json = False
+@method(AST.TypeStr)
+def post_process(self):
+  self.is_json = True
+del post_process
index 52255c1..9878e22 100644 (file)
--- a/pitree.y
+++ b/pitree.y
@@ -21,8 +21,9 @@
 %}
 
 %token CODEBLOCK_START CODEBLOCK_END SECTION2_START SECTION3_START
-%token KEYWORD_BOOL KEYWORD_CLASS KEYWORD_INT KEYWORD_LIST KEYWORD_REF
-%token KEYWORD_SET KEYWORD_STR IDENTIFIER LITERAL_BOOL LITERAL_INT LITERAL_REF
+%token KEYWORD_BOOL KEYWORD_CLASS KEYWORD_DICT KEYWORD_INT KEYWORD_LIST
+%token KEYWORD_REF KEYWORD_SET KEYWORD_STR IDENTIFIER LITERAL_BOOL LITERAL_DICT
+%token LITERAL_INT LITERAL_REF
 
 %start pitree
 
@@ -53,17 +54,18 @@ class_name
   ;
 
 class_body_opt
-  : %space (?E{t_def.AST.ClassBody})
-  | %space (?E{t_def.AST.ClassBody}'{' section2 '}')
+  : %space (?E{t_def.AST.Section2.ClassBody})
+  | %space (?E{t_def.AST.Section2.ClassBody}'{' section2 '}')
   ;
 
 type
   : %space (?E{t_def.AST.TypeBool}KEYWORD_BOOL)
+  | %space (?E{t_def.AST.TypeDict}KEYWORD_DICT '(' type ',' type ')')
   | %space (?E{t_def.AST.TypeInt}KEYWORD_INT)
   | %space (?E{t_def.AST.TypeList}KEYWORD_LIST '(' type ')')
   | %space (?E{t_def.AST.TypeRef}KEYWORD_REF)
   | %space (?E{t_def.AST.TypeSet}KEYWORD_SET '(' type ')')
-  | %space (?E{t_def.AST.TypeInt}KEYWORD_STR)
+  | %space (?E{t_def.AST.TypeStr}KEYWORD_STR)
   ;
  
 default_value_opt
@@ -73,6 +75,7 @@ default_value_opt
 
 expression
   : LITERAL_BOOL
+  | %space (?E{t_def.AST.LiteralDict}'{' dict_initializer_list_opt '}')
   | LITERAL_INT
   | %space (?E{t_def.AST.LiteralList}'[' expression_list_opt ']')
   | LITERAL_REF
@@ -91,6 +94,21 @@ expression_list
   | expression_list ',' expression
   ;
 
+dict_initializer_list_opt
+  :
+  | dict_initializer_list
+  | dict_initializer_list ','
+  ;
+
+dict_initializer_list
+  : dict_initializer
+  | dict_initializer_list ',' dict_initializer
+  ;
+
+dict_initializer
+  : (?E{t_def.AST.DictInitializer}expression ':' expression)
+  ;
+
 set_initializer_opt
   :
   | '[' expression_list_opt ']'