From cd6a363b011d799823b6225cb99ec8cda37b7cc3 Mon Sep 17 00:00:00 2001
From: Xun Yang <x299yang@uwaterloo.ca>
Date: Sat, 22 Feb 2020 22:10:21 -0500
Subject: [PATCH] build Env

---
 AST.py         |  19 +++++++--
 AstBuilding.py |  27 +++++++------
 CompNode.py    |  21 +++++++++-
 Environment.py | 108 +++++++++++++++++++++++++++++--------------------
 LineNodes.py   |  16 ++++++++
 MemberNodes.py |  22 ++++++++--
 Test.py        |  12 ++++--
 TypeNodes.py   |  15 ++++++-
 8 files changed, 172 insertions(+), 68 deletions(-)

diff --git a/AST.py b/AST.py
index c7fe49a..88e71d0 100644
--- a/AST.py
+++ b/AST.py
@@ -26,13 +26,12 @@ class ASTNode():
         for c in self.children:
             c.recurseAction(actionName, result)
 
-    # use the parse tree to populate current AST node and construct child AST nodes
-    def buildASTNodeRecursive(self):
-        pass
+    def buildEnv(self, parentEnv):
+        self.env = parentEnv
+        return parentEnv
 
     def printNodePretty(self, prefix=0):
         pp = pprint.PrettyPrinter(indent=prefix)
-
         pp.pprint(self.__class__.__name__)
         pp.pprint(vars(self))
         pp.pprint("-----children-----")
@@ -42,6 +41,18 @@ class ASTNode():
     def printTree(self):
         self.recurseAction('printNodePretty')
 
+    def printEnv(self, prefix=0):
+        pp = pprint.PrettyPrinter(indent=prefix)
+        pp.pprint(self.__class__.__name__)
+        if self.env:
+            pp.pprint(vars(self.env))
+        pp.pprint("-----children-----")
+        prefix += 1
+        return prefix
+
+
+
+
 # Utils ######################################################
 
 #   given a parseTree and a list of names, traverse the tree
diff --git a/AstBuilding.py b/AstBuilding.py
index 190bd21..36238fb 100644
--- a/AstBuilding.py
+++ b/AstBuilding.py
@@ -1,6 +1,6 @@
 from CompNode import CompNode
-from pprpint import pprint
-from Env import GlobalEnv
+from pprint import pprint
+from Environment import GlobalEnv
 
 # tree: (filename, parseTree)
 def astBuild(trees):
@@ -13,20 +13,21 @@ def buildEnvAndLink(ASTs):
     # build env
     globalEnv = GlobalEnv()
     for t in ASTs:
-        globalEnv.addtoEnv(t)
-        # build Env inside each class / interface
-        t.typeDcl.recurseAction("buildEnv")
-
-    # type Linking
-    for t in ASTs:
-        t.buildEnvFromImports(globalEnv)
-        # t.typeDcl.recurseAction("linkType")
+        globalEnv.addtoEnv(t[1])
 
     print ("\n\n###################### global Env ####################")
-    pprint(GlobalEnv)
+    pprint(vars(globalEnv))
+
     for t in ASTs:
-        printt ("\n\n###################### Comp Unit Env ####################")
-        t.recurseAction("printEnv")
+        t[1].recurseAction("buildEnv", globalEnv)
+        print('\n\n\n', t[0])
+        print("###################### Comp Unit Env ####################")
+        t[1].recurseAction("printEnv")
+
+    # # type Linking
+    # for t in ASTs:
+    #     t.buildEnvFromImports(globalEnv)
+    #     # t.typeDcl.recurseAction("linkType")
 
 #######################################################
 
diff --git a/CompNode.py b/CompNode.py
index 2a74f59..d77cc1c 100644
--- a/CompNode.py
+++ b/CompNode.py
@@ -1,6 +1,6 @@
 from AST import ASTNode, getParseTreeNodes
 from TypeNodes import InterNode, ClassNode
-
+from Environment import Env
 
 # compilation unit
 class CompNode(ASTNode):
@@ -31,3 +31,22 @@ class CompNode(ASTNode):
                     self.typeDcl = ClassNode(typeNode[0])
         # always populate the children list
         self.children.append(self.typeDcl)
+
+    def buildEnv(self, parentEnv):
+        env = Env(None)  # global environment is not the parent Env, each file don't have access to all the stuff in global env
+        env.addtoEnv(self.typeDcl)
+        # add imports
+        for i in self.importNames:
+            pName = i.split('.*') # check if it's import all
+            if len(pName) == 2:
+                nodes = parentEnv.getNodesByPackage(pName[0])
+                for k, v in nodes:
+                    if k in env.map:
+                        raise Exception('ERROR: Declaration of {} is already in current Environment, cannot import it again'.format(pName + '.'  + typeDcl.name))
+                    env.map[k] = v
+            else:
+                node = parentEnv.getNode(pName[0])
+                env.addtoEnv(node)
+
+        self.env = env
+        return env
diff --git a/Environment.py b/Environment.py
index 8404087..b9fad9a 100644
--- a/Environment.py
+++ b/Environment.py
@@ -1,36 +1,51 @@
+import string
+
 class Env:
     nodeToNamespace = dict({
-        'FieldNode': 'field',
-        'InterNode': 'interface',
-        'ClassNode': 'class',
+        'FieldNode': 'expr',
+        'InterNode': 'type',
+        'ClassNode': 'type',
         'MethodNode': 'method'
     })
 
     def __init__(self, parentEnv):
         self.parentEnv = parentEnv
-        self.map = {} # {(name, namespace) -> node}
+        self.map = {}
+                #  entries in the map:
+                # type: (typeName, 'type') -> node
+                # method: (methodName. 'method') -> {paramTypes -> node} for method overloding
+                # expr:
+                #   param: (name.'expr') -> (isParam:True, paramPosition:Int)
+                #   nonParam: (name.'expr') -> (isParam:False, node)
 
-    def addtoEnv(node):
+    def addtoEnv(self, node):
         # use node.__class__.__name__ to get namespace
         nodeType = node.__class__.__name__
-        if not self.nodeToNamespace.contains(nodeType):
-            namespace = 'local'
+        if nodeType not in self.nodeToNamespace:
+            namespace = 'expr'
         namespace = self.nodeToNamespace.get(nodeType)
+
         # Add to Env
         key = (node.name, namespace)
-        if self.map.contains(key):
-            raise Exception('ERROR: Declaration of {} is already in current Environment'.format(node.name))
-        self.map[key] = node
+        # add entry in nested map for method overloding
+        if namespace == 'method':
+            if key not in self.map:
+                self.map[key] = {}
+            self.map[key][node.paramTypes] = node
+        else:
+            if key in self.map:
+                raise Exception('ERROR: Declaration of {} is already in current Environment'.format(node.name))
+            if namespace == 'expr':
+                self.map[key] = (False, node)
+            else:
+                self.map[key] = node
 
-    def getNode(name, namespace):
+
+    def getNode(self, name, namespace):
         key = (name, namespace)
 
-        if self.map.contains(key):
+        if key in self.map:
             return self.map.get(key)
-        elif namespace == 'local':
-            key2 = (name, 'field')
-            if self.map.contains(key2):
-                return self.map.get(key2)
         elif parentEnv:
             return parentEnv.getNode(name, namespace)
         raise Exception("ERROR: Can't find definition of {} in the Environment".format(name))
@@ -40,39 +55,46 @@ class Env:
 class GlobalEnv(Env):
     def __init__(self):
         super().__init__(None)
-        #self.map = {} # {(Canonical name, namespace) -> node}
+        #self.map = {} # {Canonical name -> node}
         self.pacakgeMap = {} # {packageName -> {(simpleName, namespace) -> node}}
                             #   A map of maps
                             #   contains duplicate info as self.map for easy searching
 
-        # input: Compiliation Unit Node (CompNode)
-        def addtoEnv(node):
-            pName = node.packageName
-            typeDcl = node.typeDcl
-            typeDclType = typeDcl.__class__.__name__
-            # at global level there are only class and interface
-            namespace = self.nodeToNamespace.get(typeDclType)
+    # input: Compiliation Unit Node (CompNode)
+    def addtoEnv(self, node):
+        pName = node.packageName
+        typeDcl = node.typeDcl
+        typeDclType = typeDcl.__class__.__name__
+        # at global level there are only class and interface
+        namespace = self.nodeToNamespace.get(typeDclType)
 
-            # Add to map
-            mapKey = (pName + '.'  + typeDclType.name, namespace)
-            if self.map.contains(mapKey):
-                raise Exception('ERROR: Declaration of {} is already in current Environment'.format(pName + '.'  + typeDclType.name))
-            self.map[mapKey] = typeDcl
+        # Add to map
+        mapKey = pName + '.'  + typeDcl.name
+        if mapKey in self.map:
+            raise Exception('ERROR: Declaration of {} is already in current Environment'.format(pName + '.'  + typeDcl.name))
+        self.map[mapKey] = typeDcl
 
-            # Add to packageMap
-            pMapKey = (typeDclType.name, namespace)
-            if not self.pacakgeMap.contains(pName):
-                self.pacakgeMap[pName] = {}
+        # Add to packageMap
+        pMapKey = (typeDcl.name, namespace)
+        if not pName in self.pacakgeMap:
+            self.pacakgeMap[pName] = {}
 
-            if not self.pacakgeMap[pName].contains(pMapKey):
-                raise Exception('ERROR: Declaration of {} is already in current Environment'.format(pName + '.'  + typeDclType.name))
-            self.pacakgeMap[pName][pMapKey] = typeDcl
+        if pMapKey in self.pacakgeMap[pName]:
+            raise Exception('ERROR: Declaration of {} is already in current Environment'.format(pName + '.'  + typeDcl.name))
+        self.pacakgeMap[pName][pMapKey] = typeDcl
 
-        # Use getNode() from base class to get node using Canonical Name (full name)
+    # Use getNode() from base class to get node using Canonical Name (full name)
+    def getNode(self, name):    # since all names in global namespace is in namespace type
+        key = name
+        if key in self.map:
+            return self.map.get(key)
+        elif parentEnv:
+            return parentEnv.getNode(name, namespace)
+        raise Exception("ERROR: Can't find definition of {} in the Environment".format(name))
 
-        # method for getting all the nodes under a package (import All)
-        # returns a dict of types under that package name
-        def getNodesByPackage(pName):
-            if not self.pacakgeMap.contains(pName):
-                raise Exception("ERROR: Can't find definition of package {} in the Environment".format(pName))
-            return self.pacakgeMap.get(pName)
+    # method for getting all the nodes under a package (import All)
+    # returns a dict of types under that package name
+    def getNodesByPackage(self, pName):
+        if not pName in self.pacakgeMap:
+            raise Exception("ERROR: Can't find definition of package {} in the Environment".format(pName))
+        return self.pacakgeMap.get(pName)
diff --git a/LineNodes.py b/LineNodes.py
index 20dce65..eb8e271 100644
--- a/LineNodes.py
+++ b/LineNodes.py
@@ -1,4 +1,5 @@
 from AST import ASTNode, getParseTreeNodes, getTypeName
+from Environment import Env
 
 # expr
 class ExprNode(ASTNode):
@@ -11,6 +12,14 @@ class ExprNode(ASTNode):
         self.env = None
         self.children = []
 
+    # def buildEnv(self, parentEnv):
+    #     env = Env(parentEnv)
+    #     # env.addtoEnv()
+    #
+    #
+    #     self.env = env
+    #     return env
+
 # block
 class BlockNode(ASTNode):
     # always list all fields in the init method to show the class structure
@@ -19,3 +28,10 @@ class BlockNode(ASTNode):
         self.statements = []  # list of statements in a block
         self.env = None
         self.children = []
+
+    # def buildEnv(self, parentEnv):
+    #     env = Env(parentEnv)
+    #     # env.addtoEnv()
+    #
+    #     self.env = env
+    #     return env
diff --git a/MemberNodes.py b/MemberNodes.py
index cb4e295..0785aad 100644
--- a/MemberNodes.py
+++ b/MemberNodes.py
@@ -1,5 +1,7 @@
 from AST import ASTNode, getParseTreeNodes, getTypeName
 from LineNodes import ExprNode, BlockNode
+from Environment import Env
+from collections import OrderedDict
 
 # field
 class FieldNode(ASTNode):
@@ -34,8 +36,6 @@ class FieldNode(ASTNode):
 
         if self.fieldInit: self.children.append(self.fieldInit)
 
-
-
 ###########################################################
 
 # method
@@ -45,10 +45,11 @@ class MethodNode(ASTNode):
         self.parseTree = parseTree
         self.name = ''
         self.methodType = ''
-        self.params = {} # a dictionary {paramName -> (isPrimType, typeName)}, after type linking: {paramName -> typeNode}
+        self.params = OrderedDict() # a dictionary {paramName -> (isPrimType, typeName)}, after type linking: {paramName -> typeNode}
         self.mods = []
         self.body = None
         self.isPrimType = False # easy boolean flag, can be optimize later
+        self.paramTypes = '' # a string of param types for easy type checking against arguments
         self.env = None
         self.children = []
 
@@ -57,6 +58,7 @@ class MethodNode(ASTNode):
         for n in nameNodes:
             self.name = n.lex
 
+        # params
         nameNodes = getParseTreeNodes(['param'], parseTree, ['methodBody'])
         for n in nameNodes:
             paramName = ''
@@ -67,6 +69,7 @@ class MethodNode(ASTNode):
                 elif c.name == 'ID':
                     paramName = c.lex
             self.params[paramName] = paramType
+            self.paramTypes += paramType[1] + ","
 
         nameNodes = getParseTreeNodes(['type', 'VOID'], parseTree, ['methodBody', 'params'])
         for n in nameNodes:
@@ -87,3 +90,16 @@ class MethodNode(ASTNode):
                     self.body = BlockNode(n)
 
         if self.body: self.children.append(self.body)
+
+
+    def buildEnv(self, parentEnv):
+        env = Env(parentEnv)
+        i = 0
+        for k, v in self.params:
+            key = (k, 'expr')
+            if key in env.map:
+                raise Exception('ERROR: Declaration of {} is already in current Environment'.format(node.name))
+            env.map[key] = (True, i)
+            i += 1
+        self.env = env
+        return env
diff --git a/Test.py b/Test.py
index ba6c77e..2307aaa 100644
--- a/Test.py
+++ b/Test.py
@@ -1,13 +1,16 @@
 import sys
-from os import listdir, scandir
+from os import listdir, scandir, walk
 from os.path import isfile, join
 
 from Scanning import scan
 from Parsing import parse
-from AstBuilding import astBuild
+from AstBuilding import astBuild, buildEnvAndLink
 import Weeding
 
 
+# run test with python Test.py Tests/A2/J1_3_ImportOnDemand_ProgramDefinedPackage/
+
+
 def main():
     a2Multiple()
 
@@ -39,7 +42,8 @@ def a2Multiple():
     for c in testCases:
         print(c)
         print("**********************************************************")
-        testFiles = [c + '/'+  f for f in listdir(c) if isfile(join(c, f))]
+        # get all files in the folder recursively
+        testFiles = [join(dp, f) for dp, dn, filenames in walk(c) for f in filenames]
         run(testFiles)
         print("\n \n\n ")
 
@@ -106,6 +110,8 @@ def run(testFiles):
         t.printTree()
         print("\n \n\n \n")
 
+    buildEnvAndLink(ASTs)
+
 
     print("Succeeded")
     print("**********************************************************")
diff --git a/TypeNodes.py b/TypeNodes.py
index 4a9a79f..2d82d99 100644
--- a/TypeNodes.py
+++ b/TypeNodes.py
@@ -1,5 +1,6 @@
 from AST import ASTNode, getParseTreeNodes
 from MemberNodes import FieldNode, MethodNode
+from Environment import Env
 
 # types: class , interface
 
@@ -49,7 +50,12 @@ class ClassNode(ASTNode):
 
         self.children += self.fields + self.methods + self.constructors
 
-
+    def buildEnv(self, parentEnv):
+        env = Env(parentEnv)
+        for c in self.children:
+            env.addtoEnv(c)
+        self.env = env
+        return env
 
 #####################################################################
 # interface
@@ -78,3 +84,10 @@ class InterNode(ASTNode):
                     self.methods.append(MethodNode(n))
 
         self.children = self.methods
+
+    def buildEnv(self, parentEnv):
+        env = Env(parentEnv)
+        for c in self.children:
+            env.addtoEnv(c)
+        self.env = env
+        return env
-- 
GitLab