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