Skip to content

Commit

Permalink
use node.py helpers when possible
Browse files Browse the repository at this point in the history
* remove reference to unused IDENTIFIER_NODE
* changed to sets for membership tests in node_info
* added Node __repr__/__str__'s for easier debugging/printing
  • Loading branch information
mgeplf committed Feb 28, 2019
1 parent a41d669 commit fbd1201
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 73 deletions.
11 changes: 8 additions & 3 deletions src/language/argument.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,28 @@ class Argument:
"""Utility class for holding all arguments for node classes"""

def __init__(self):
self.base_class = ""
# BaseNode
self.class_name = ""
self.description = ""
self.nmodl_name = ""
self.prefix = ""
self.suffix = ""
self.force_prefix = ""
self.force_suffix = ""
self.separator = ""
self.description = ""

# ChildNode
self.typename = ""
self.varname = ""
self.is_public = False
self.is_vector = False
self.is_optional = False
self.add_method = False
self.get_node_name = False
self.has_token = False
self.getter_method = False
self.getter_override = False

# Node
self.base_class = ""
self.has_token = False
self.url = None
51 changes: 32 additions & 19 deletions src/language/node_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"""

# yapf: disable
BASE_TYPES = ["short",
BASE_TYPES = {"short",
"int",
"float",
"double",
Expand All @@ -29,16 +29,18 @@
"FirstLastType",
"QueueType",
"BAType",
"UnitStateType"]
"UnitStateType",
}

# base types which are enums
ENUM_BASE_TYPES = ["BinaryOp",
ENUM_BASE_TYPES = {"BinaryOp",
"UnaryOp",
"ReactionOp",
"FirstLastType",
"QueueType",
"BAType",
"UnitStateType"]
"UnitStateType",
}

# data types and then their return types
DATA_TYPES = {"Boolean": "bool",
Expand All @@ -52,10 +54,11 @@
"UnitState": "UnitStateType",
"BABlockType": "BAType",
"QueueExpressionType": "QueueType",
"FirstLastTypeIndex": "FirstLastType"}
"FirstLastTypeIndex": "FirstLastType",
}

# nodes which will go into symbol table
SYMBOL_VAR_TYPES = ["LocalVar",
SYMBOL_VAR_TYPES = {"LocalVar",
"ParamAssign",
"Argument",
"DependentDef",
Expand All @@ -73,67 +76,77 @@
"BbcorePointerVar",
"ExternVar",
"PrimeName",
"ConstantVar"]
"ConstantVar",
}

# block nodes which will go into symbol table
# these blocks doesn't define global variables but they
# use variables from other global variables
SYMBOL_BLOCK_TYPES = ["FunctionBlock",
SYMBOL_BLOCK_TYPES = {"FunctionBlock",
"ProcedureBlock",
"DerivativeBlock",
"LinearBlock",
"NonLinearBlock",
"DiscreteBlock",
"PartialBlock",
"KineticBlock",
"FunctionTableBlock"]
"FunctionTableBlock"
}

# nodes which need extra handling to augument symbol table
SYMBOL_TABLE_HELPER_NODES = ["TableStatement"]
SYMBOL_TABLE_HELPER_NODES = {"TableStatement",
}

# blocks defining global variables
GLOBAL_BLOCKS = ["NeuronBlock",
GLOBAL_BLOCKS = {"NeuronBlock",
"ParamBlock",
"UnitBlock",
"StepBlock",
"IndependentBlock",
"DependentBlock",
"StateBlock",
"ConstantBlock"]
"ConstantBlock",
}

# when translating back to nmodl, we need print each statement
# to new line. Those nodes are are used from this list.
STATEMENT_TYPES = ["Statement",
STATEMENT_TYPES = {"Statement",
"IndependentDef",
"DependentDef",
"ParamAssign",
"ConstantStatement",
"Stepped"]
"Stepped",
}

# data types which have token as an argument to the constructor
LEXER_DATA_TYPES = ["Name",
LEXER_DATA_TYPES = {"Name",
"PrimeName",
"Integer",
"Double",
"String",
"FactorDef"]
"FactorDef",
}

# while printing symbol table we needed setToken() method for StatementBlock and
# hence need to add this
ADDITIONAL_TOKEN_BLOCKS = ["StatementBlock"]
ADDITIONAL_TOKEN_BLOCKS = {"StatementBlock",
}

# for printing NMODL, we need to know which nodes are block types.
# TODO: NEURON block is removed because it has internal statement block
# and we don't want to print extra brace block for NMODL
# We are removing NeuronBlock because it has statement block which
# prints braces already.
BLOCK_TYPES = (GLOBAL_BLOCKS + ADDITIONAL_TOKEN_BLOCKS)
BLOCK_TYPES = GLOBAL_BLOCKS | ADDITIONAL_TOKEN_BLOCKS
BLOCK_TYPES.remove("NeuronBlock")

# Note that these are nodes which are not of type pointer in AST.
# This also means that they can't be optional because they will appear
# as value type in AST.
PTR_EXCLUDE_TYPES = ["BinaryOperator", "UnaryOperator", "ReactionOperator"]
PTR_EXCLUDE_TYPES = {"BinaryOperator",
"UnaryOperator",
"ReactionOperator",
}

# these node names are explicitly added because they are used in ast/visitor
# printer classes. In order to avoid hardcoding in the printer functions, they
Expand Down
38 changes: 19 additions & 19 deletions src/language/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,6 @@ def is_number_node(self):
def is_boolean_node(self):
return self.class_name == node_info.BOOLEAN_NODE

@property
def is_identifier_node(self):
return self.class_name == node_info.IDENTIFIER_NODE

@property
def is_name_node(self):
return self.class_name == node_info.NAME_NODE
Expand Down Expand Up @@ -225,6 +221,12 @@ def get_setter_method(self):
reference = "" if self.is_base_type_node else "&&"
return f"void {setter_method}({setter_type}{reference} {self.varname}) {{ this->{self.varname} = {self.varname}; }}"

def __repr__(self):
return "ChildNode(class_name='{}', nmodl_name='{}')".format(
self.class_name, self.nmodl_name)

__str__ = __repr__


class Node(BaseNode):
"""represent a class for every rule in language specification"""
Expand Down Expand Up @@ -298,21 +300,13 @@ def is_symtab_method_required(self):
:return: True if need to print visit method for node in symtabjsonvisitor
otherwise False
"""
method_required = False

if self.has_children():

if(self.class_name in node_info.SYMBOL_VAR_TYPES or
self.class_name in node_info.SYMBOL_BLOCK_TYPES):
method_required = True

if self.is_program_node or self.has_parent_block_node():
method_required = True

if self.class_name in node_info.SYMBOL_TABLE_HELPER_NODES:
method_required = True

return method_required
return (self.has_children() and
(self.is_symbol_var_node or
self.is_symbol_block_node or
self.is_symbol_helper_node or
self.is_program_node or
self.has_parent_block_node()
))

@property
def is_base_class_number_node(self):
Expand Down Expand Up @@ -383,3 +377,9 @@ def private_members(self):
@property
def non_base_members(self):
return [child for child in self.children if not child.is_base_type_node]

def __repr__(self):
return "Node(class_name='{}', base_class='{}', nmodl_name='{}')".format(
self.class_name, self.base_class, self.nmodl_name)

__str__ = __repr__
53 changes: 21 additions & 32 deletions src/language/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,17 @@ def parse_child_rule(self, child):
Child specification has additional option like list, optional,
getName method etc.
"""

# there is only one key and it has one value
varname = next(iter(list(child.keys())))
properties = next(iter(list(child.values())))
varname, properties = next(iter(child.items()))

# arguments holder for creating tree node
args = Argument()

# node of the variable
args.varname = varname

# type i.e. class of the variable
args.class_name = properties['type']

if self.debug:
print(('Child {}, {}'.format(args.varname, args.class_name)))
print('Child {}, {}'.format(args.varname, args.class_name))

# if there is add method for member in the class
if 'add' in properties:
Expand Down Expand Up @@ -130,9 +125,12 @@ def parse_yaml_rules(self, nodelist, base_class=None):

for node in nodelist:
# name of the ast class and it's properties as dictionary
class_name = next(iter(list(node.keys())))
properties = next(iter(list(node.values())))
url = properties['url'] if 'url' in properties else None
class_name, properties = next(iter(node.items()))

args = Argument()
args.url = properties['url'] if 'url' in properties else None
args.class_name = class_name
args.description = properties['description']

# yaml file has abstract classes and their subclasses with children as a property
if 'children' in properties:
Expand All @@ -145,45 +143,35 @@ def parse_yaml_rules(self, nodelist, base_class=None):

# classes like AST which don't have base class
# are not added (we print AST class separately)
if base_class:
args = Argument()
if base_class is not None:
args.base_class = base_class
args.class_name = class_name
args.description = properties['description']
args.url = url
node = Node(args)
abstract_nodes.append(node)
nodes.insert(0, node)
if self.debug:
print(('Abstract {}, {}'.format(base_class, class_name)))
print('Abstract {}'.format(node))
else:
# name of the node while printing back to NMODL
nmodl_name = properties['nmodl'] if 'nmodl' in properties else None
args.base_class = base_class if base_class else 'AST'

# check if we need token for the node
# todo : we will have token for every node
has_token = LanguageParser.is_token(class_name)
args.has_token = LanguageParser.is_token(class_name)

args = Argument()
args.base_class = base_class if base_class else 'AST'
args.class_name = class_name
args.description = properties['description']
args.nmodl_name = nmodl_name
args.has_token = has_token
args.url = url
# name of the node while printing back to NMODL
args.nmodl_name = properties['nmodl'] if 'nmodl' in properties else None

# create tree node and add to the list
node = Node(args)
nodes.append(node)

if self.debug:
print(('Class {}, {}, {}'.format(base_class, class_name, nmodl_name)))
print('Class {}'.format(node))

# now process all children specification
childs = properties['members'] if 'members' in properties else []
for child in childs:
args = self.parse_child_rule(child)
node.add_child(args)
if 'members' in properties:
for child in properties['members']:
args = self.parse_child_rule(child)
node.add_child(args)

# update the abstract nodes
for absnode in abstract_nodes:
Expand All @@ -202,7 +190,8 @@ def parse_file(self):
rules = yaml.load(stream)
_, nodes = self.parse_yaml_rules(rules)
except yaml.YAMLError as e:
print(("Error while parsing YAML definition file {0} : {1}".format(self.filename, e.strerror)))
print("Error while parsing YAML definition file {0} : {1}".format(
self.filename, e.strerror))
sys.exit(1)

return nodes

0 comments on commit fbd1201

Please sign in to comment.