Commit bc51c9f9 authored by Chris Jewell's avatar Chris Jewell
Browse files

Pylint tidying

parent b442d03f
Pipeline #273 passed with stage
in 4 minutes and 58 seconds
......@@ -16,6 +16,10 @@
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""The gemlang AST module"""
import inspect
import os
......@@ -25,9 +29,10 @@ from gem import gemlang
from .ast_base import ASTNode
def make_ast_parser():
"""Returns an AST parser object, which parses a serialized version of the AST."""
glpath = os.path.dirname(inspect.getfile(gemlang))
with open(os.path.join(glpath, 'ast/ast_grammar.cfgr'), 'r') as f:
grammar = f.read()
with open(os.path.join(glpath, 'ast/ast_grammar.cfgr'), 'r') as f_handle:
grammar = f_handle.read()
return Lark(grammar)
......
......@@ -34,7 +34,7 @@ class ASTNode:
self.__meta__ = meta
self.symbol = None
self.scope = None
self.evalType = None
self.eval_type = None
@property
def children(self):
......
......@@ -134,7 +134,7 @@ class DAG(ASTWalker):
raise StopWalk()
def onEnter_IdRef(self, node):
if isinstance(node.symbol, VariableSymbol) and node.symbol.type == SymbolTable._random_variable:
if isinstance(node.symbol, VariableSymbol) and node.symbol.type == SymbolTable.RANDOM_VARIABLE:
parent = self.__dag[node.value]
if len(self.__current_rv) > 0:
child = self.__current_rv[-1]
......
......@@ -159,7 +159,7 @@ class CodeGenerator(ASTWalker):
rhs = ast_node.children[1]
if lhs.symbol.attrib & SymbolTable.ATTRIB.EXTERN:
return NullNode('external reference removed')
if lhs.type == SymbolTable._state:
if lhs.type == SymbolTable.STATE:
initial = self._walk(rhs.children[1])
self.current_workspace['states'].append(lhs.value)
self.current_workspace['initial'].append(initial[0])
......
......@@ -17,3 +17,4 @@
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""gemlang semantic analysis module"""
......@@ -17,6 +17,8 @@
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
"""Scope table classes."""
from abc import ABC, abstractmethod
from collections import OrderedDict
......@@ -26,13 +28,10 @@ from gem.gemlang.ast import ASTNode
class ScopeBase(ABC):
"""Base class interface for a scope
"""
@property
@abstractmethod
def name(self):
pass
def __init__(self, enclosing_scope):
self.__enclosing_scope = enclosing_scope
@property
@abstractmethod
def parent_scope(self):
"""Where to look next for symbols.
:returns: an instance of a Scope
......@@ -40,7 +39,6 @@ class ScopeBase(ABC):
return self.enclosing_scope
@property
@abstractmethod
def enclosing_scope(self):
"""Returns the Scope in which this Scope is defined.
:returns: an instance of Scope, or None if global scope.
......@@ -55,12 +53,22 @@ class ScopeBase(ABC):
"""
@abstractmethod
def declare(self):
pass
def declare(self, symbol):
"""Symbol declaration
:param symbol: a :class:Symbol object
:returns: None
:raises: KeyError if symbol is already declared (duplicate declaration)
"""
@abstractmethod
def resolve(self):
pass
def resolve(self, symbol_name):
"""Resolves `symbol_name` in the scope.
:param symbol_name: the name of the symbol
:returns: an object of type Symbol if found
:raises: KeyError if symbol not found in the scope hierarchy.
"""
class Scope(ScopeBase):
......@@ -69,7 +77,7 @@ class Scope(ScopeBase):
:param enclosing_scope: an enclosing scope
"""
def __init__(self, enclosing_scope=None):
self.__enclosing_scope = enclosing_scope
super().__init__(enclosing_scope)
self.__symbols = OrderedDict()
self.__level = enclosing_scope.level + 1 if enclosing_scope is not None else 0
self.__ast_def = None
......@@ -78,20 +86,18 @@ class Scope(ScopeBase):
def level(self):
return self.__level
@property
def enclosing_scope(self):
return self.__enclosing_scope
@property
def parent_scope(self):
return self.enclosing_scope
@property
def ast_def(self):
"""Returns a reference to the AST where the scope was defined."""
return self.__ast_def
@ast_def.setter
def ast_def(self, ast_def):
"""Sets a reference to the ASTNode where the scope is defined."""
assert isinstance(ast_def, ASTNode)
self.__ast_def = ast_def
......@@ -138,8 +144,8 @@ class Scope(ScopeBase):
('%s%s: %r' % (" " * self.__level, key, value))
for key, value in self.__symbols.items()
)
s = '\n'.join(lines)
return s
string = '\n'.join(lines)
return string
class GlobalScope(Scope):
......@@ -150,6 +156,7 @@ class GlobalScope(Scope):
@property
def name(self):
"""Returns the name of the scope"""
return 'global'
......@@ -164,6 +171,5 @@ class LocalScope(Scope):
@property
def name(self):
"""Returns the name of the scope"""
return 'local'
......@@ -27,23 +27,32 @@
#
#
"""Base class for an AST walker that has scope management."""
from gem.gemlang import ASTWalker
from gem.gemlang.semantics.scope import ScopeBase
class ScopedWalker(ASTWalker):
"""Base class for AST Walkers needing scope management"""
# pylint: disable=too-few-public-methods
def __init__(self):
super().__init__(in_place=True)
self._scope_stack = []
@property
def current_scope(self):
"""Returns a reference to the current scope"""
return self._scope_stack[-1]
def _push_scope(self, scope):
"""Pushes a new scope onto the scope stack"""
assert isinstance(scope, ScopeBase)
self._scope_stack.append(scope)
return scope
def _pop_scope(self):
return self._scope_stack.pop()
\ No newline at end of file
"""Pops a scope from the scope stack."""
return self._scope_stack.pop()
......@@ -17,7 +17,7 @@
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Classes for symbol tables
"""gemlang Symbol classes"""
from abc import abstractmethod
from collections import OrderedDict
......@@ -29,14 +29,13 @@ __all__ = ['BuiltinTypeSymbol', 'VariableSymbol', 'MethodSymbol', 'STMSymbol']
class UnresolvedSymbolError(Exception):
def __init__(self, message):
super().__init__(message)
"""Thrown if a symbol is not found."""
class SYMBOL_ATTR(Flag):
class SYMBOL_ATTR(Flag): # pylint: disable=invalid-name
"""Symbol attribute enum"""
DEFAULT = auto()
EXTERN = auto()
RANDOM = auto()
class Symbol:
......@@ -46,39 +45,52 @@ class Symbol:
:param definition: a reference to the ASTNode where symbol was declared
:returns: an object of type :class:`Symbol`
"""
def __init__(self, name, type=None):
def __init__(self, name, symbol_type=None):
self.__name = str(name)
self.__type = type
self.__type = symbol_type
self.__scope = None
self.ast_def = None
self.__attrib = SYMBOL_ATTR.DEFAULT
@property
def name(self):
"""Returns the name of the symbol"""
return self.__name
@property
def type(self):
"""Returns the symbol type"""
return self.__type
@type.setter
def type(self, type):
self.__type = type
def type(self, symbol_type):
"""Sets the symbol type"""
self.__type = symbol_type
@property
def attrib(self):
"""Returns symbol attributes
:returns: instance of :class:SYMBOL_ATTR
"""
return self.__attrib
@attrib.setter
def attrib(self, attrib):
"""Sets symbol attributes
:param attrib: attributes
:type attrib: :class:SYMBOL_ATTR
"""
self.__attrib = attrib
@property
def scope(self):
"""Returns the scope the symbol was defined in"""
return self.__scope
@scope.setter
def scope(self, scope):
"""Sets the scope the symbol was defined in"""
self.__scope = scope
def __str__(self):
......@@ -94,7 +106,6 @@ class ExternalDataSymbol(Symbol):
:param name: the name of the symbol
:param type: the external data type
"""
pass
class Type(Symbol):
......@@ -110,7 +121,6 @@ class BuiltinTypeSymbol(Type):
"""A builtin symbol
:param name: the name of the symbol
"""
pass
class VariableSymbol(Symbol):
......@@ -118,17 +128,17 @@ class VariableSymbol(Symbol):
:param name: the name of the symbol
:param definition: a reference to the AST where the symbol is defined
:param type: the variable type
:param symbol_type: the variable type
"""
def __init__(self, name: str, type: Symbol = 'None') -> None:
super().__init__(name, type)
def __init__(self, name: str, symbol_type: Symbol = 'None') -> None:
super().__init__(name, symbol_type)
class ScopedSymbol(Symbol, ScopeBase):
"""Base class for a scoped symbol."""
def __init__(self, name, type, enclosing_scope):
super().__init__(name, type)
def __init__(self, name, symbol_type, enclosing_scope):
super().__init__(name, symbol_type)
self.__enclosing_scope = enclosing_scope
self.__level = enclosing_scope.level + 1
......@@ -162,7 +172,7 @@ class ScopedSymbol(Symbol, ScopeBase):
def get_members(self):
"""Returns member symbols.
"""
pass
class MethodSymbol(ScopedSymbol):
......@@ -177,10 +187,12 @@ class MethodSymbol(ScopedSymbol):
self.__ordered_args = OrderedDict()
def get_members(self):
"""Returns an OrderedDict of formal arguments"""
return self.__ordered_args
@property
def argcount(self):
"""Returns the number of formal arguments taken by the function."""
return len(self.__ordered_args)
......@@ -192,7 +204,7 @@ class STMSymbol(ScopedSymbol):
:param superclass: **Future implementation** model inheritance
"""
def __init__(self, name, enclosing_scope, superclass=None):
super().__init__(name=str(name), type=None, enclosing_scope=enclosing_scope)
super().__init__(name=str(name), symbol_type=None, enclosing_scope=enclosing_scope)
self.config = LocalScope(enclosing_scope=self)
self.config.declare(VariableSymbol('time_origin', 'Scalar'))
self.config.declare(VariableSymbol('time_step', 'Scalar'))
......@@ -202,8 +214,9 @@ class STMSymbol(ScopedSymbol):
self.type = self.resolve('RandomVariableType')
def __init_builtins(self):
self.__builtins['State'] = _make_method_symbol('State', self.enclosing_scope.resolve('StateType'), ['init'],
enclosing_scope=self) # State only allowable in an STM
self.__builtins['State'] = _make_method_symbol('State', self.enclosing_scope.resolve('StateType'),
['init'],
enclosing_scope=self) # State only allowable in an STM
def resolve(self, symbol_name):
sym = self.get_members().get(symbol_name)
......@@ -221,6 +234,7 @@ class STMSymbol(ScopedSymbol):
@property
def argcount(self):
"""Returns the number of arguments taken my the model constructor."""
return len(self.__ctor_formal_args)
......@@ -228,4 +242,4 @@ def _make_method_symbol(name, return_type, args, enclosing_scope):
symbol = MethodSymbol(name=name, return_type=return_type, enclosing_scope=enclosing_scope)
for arg in args:
symbol.declare(VariableSymbol(arg, None))
return symbol
\ No newline at end of file
return symbol
......@@ -27,14 +27,20 @@
#
#
from gem.gemlang.semantics.scoped_ast_walker import ScopedWalker
"""Symbol declaration step."""
from gem.gemlang.ast.ast_base import NullNode
from gem.gemlang.semantics.symbol import VariableSymbol, STMSymbol
from gem.gemlang.semantics.scope import LocalScope
from gem.gemlang.semantics.scoped_ast_walker import ScopedWalker
from gem.gemlang.semantics.symbol import VariableSymbol, STMSymbol
from gem.gemlang.semantics.symbol_table import SymbolTable
class SymbolDeclarer(ScopedWalker):
"""Class implementing symbol declaration on an AST"""
# pylint: disable=missing-function-docstring,no-self-use,unused-argument,invalid-name
def visit(self, ast_node, symbol_table):
"""Walks the AST and constructs a symbol table of declared symbols.
Each symbol is annotated with a pointer to its respective declaration
......@@ -44,8 +50,6 @@ class SymbolDeclarer(ScopedWalker):
:returns: a populated SymbolTable corresponding to ast_node
"""
# pylint: disable=missing-function-docstring,no-self-use,unused-argument
self._push_scope(symbol_table.globals)
self._walk(ast_node)
......@@ -66,7 +70,8 @@ class SymbolDeclarer(ScopedWalker):
return NullNode(f"Extern ref to {sym}")
else:
raise SyntaxError(
f"Duplicate declaration of '{varname}' at line {assign.meta.line} column {assign.meta.column}")
f"Duplicate declaration of '{varname}' at line {assign.meta.line} \
column {assign.meta.column}")
except NameError:
pass
......@@ -79,12 +84,13 @@ class SymbolDeclarer(ScopedWalker):
def onExit_StochasticAssignExpr(self, assign):
var = assign.children[0]
varname = str(var.value)
sym = VariableSymbol(varname, SymbolTable._random_variable)
sym = VariableSymbol(varname, SymbolTable.RANDOM_VARIABLE)
try:
self.current_scope.declare(sym)
except NameError as e:
raise SyntaxError(
f"Duplicate declaration of '{varname}' at line {assign.meta.line} column {assign.meta.column}")
f"Duplicate declaration of '{varname}' at \
line {assign.meta.line} column {assign.meta.column}")
sym.ast_def = var
var.symbol = sym
var.scope = self.current_scope
......
......@@ -28,6 +28,9 @@ import gem.gemlang.semantics.symbol
class SymbolResolver(ScopedWalker):
# pylint: disable=missing-function-docstring,no-self-use,unused-argument,invalid-name
def visit(self, ast_node, symbol_table):
"""Resolves symbols in ast_node against symbol_table.
ast_node is modified in-place, each reference to a symbol being
......@@ -38,8 +41,6 @@ class SymbolResolver(ScopedWalker):
:returns: Nothing. ast_node modified in place.
"""
# pylint: disable=missing-function-docstring,no-self-use,unused-argument,invalid-name
self._push_scope(symbol_table.globals)
self._walk(ast_node)
......@@ -71,7 +72,7 @@ class SymbolResolver(ScopedWalker):
raise NameError(
f"Undefined function '{f_idref.value}' at line {call.meta.line} column {call.meta.column}.")
f_idref.symbol = fsym
call.evalType = fsym.type
call.eval_type = fsym.type
if len(arglist.children) != fsym.argcount:
raise SyntaxError(
......@@ -92,18 +93,18 @@ class SymbolResolver(ScopedWalker):
lhs = assign.children[0]
rhs = assign.children[1]
if rhs.evalType == SymbolTable._random_variable:
if rhs.eval_type == SymbolTable.RANDOM_VARIABLE:
raise ValueError(
f"Probability distribution cannot be assigned in a '=' statement at line {assign.meta.line}"
)
lhs.type = rhs.evalType
lhs.type = rhs.eval_type
def onExit_StochasticAssignExpr(self, assign):
lhs = assign.children[0]
rhs = assign.children[1]
try:
sym = rhs.children[0].symbol
if sym.type is not SymbolTable._random_variable:
if sym.type is not SymbolTable.RANDOM_VARIABLE:
raise ValueError
lhs.symbol.type = sym.type
except:
......@@ -120,19 +121,19 @@ class SymbolResolver(ScopedWalker):
ast_node.meta.column))
ast_node.symbol = sym
ast_node.scope = self.current_scope
ast_node.evalType = sym.type
ast_node.eval_type = sym.type
def onExit_Integer(self, integer):
integer.evalType = self.current_scope.resolve('int')
integer.eval_type = self.current_scope.resolve('int')
def onExit_Float(self, node):
node.evalType = self.current_scope.resolve('float')
node.eval_type = self.current_scope.resolve('float')
def onExit_BinaryExpr(self, node):
node.type = node.children[0].evalType
node.type = node.children[0].eval_type
def onExit_UnaryExpr(self, node):
node.type = node.children[0].evalType
node.type = node.children[0].eval_type
RandomVariableList = namedtuple("RandomVariableList", ['free', 'observed'])
......@@ -150,7 +151,7 @@ def get_random_vars(symtab):
free_rvs = []
observed_rvs = []
symbols = [sym for sym in symtab.globals.get_by_type(gem.gemlang.semantics.symbol.VariableSymbol) if sym.type == SymbolTable._random_variable]
symbols = [sym for sym in symtab.globals.get_by_type(gem.gemlang.semantics.symbol.VariableSymbol) if sym.type == SymbolTable.RANDOM_VARIABLE]
for symbol in symbols:
ast = symbol.ast_def
assert ast is not None, f"symbol {symbol} is not annotated with an ASTNode"
......
......@@ -24,12 +24,12 @@ from gem.gemlang.semantics.scope import GlobalScope
class SymbolTable:
"""A GEM Symbol Table"""
_boolean = BuiltinTypeSymbol('boolean')
_char = BuiltinTypeSymbol('char')
_int = BuiltinTypeSymbol('int')
_float = BuiltinTypeSymbol('float')
_random_variable = BuiltinTypeSymbol('RandomVariableType')
_state = BuiltinTypeSymbol('StateType')
BOOLEAN = BuiltinTypeSymbol('boolean')
CHAR = BuiltinTypeSymbol('char')
INT = BuiltinTypeSymbol('int')
FLOAT = BuiltinTypeSymbol('float')
RANDOM_VARIABLE = BuiltinTypeSymbol('RandomVariableType')
STATE = BuiltinTypeSymbol('StateType')
ATTRIB = SYMBOL_ATTR
......@@ -43,17 +43,17 @@ class SymbolTable:
def __init_type_system(self):
global_scope = self.globals
global_scope.declare(SymbolTable._boolean)
global_scope.declare(SymbolTable._char)
global_scope.declare(SymbolTable._int)
global_scope.declare(SymbolTable._float)
global_scope.declare(SymbolTable.BOOLEAN)
global_scope.declare(SymbolTable.CHAR)
global_scope.declare(SymbolTable.INT)
global_scope.declare(SymbolTable.FLOAT)
global_scope.declare(SymbolTable._random_variable)
global_scope.declare(SymbolTable._state)
global_scope.declare(SymbolTable.RANDOM_VARIABLE)
global_scope.declare(SymbolTable.STATE)
global_scope.declare(BuiltinTypeSymbol('NDArrayType'))
global_scope.declare(_make_method_symbol('Gamma', SymbolTable._random_variable,
global_scope.declare(_make_method_symbol('Gamma', SymbolTable.RANDOM_VARIABLE,
['shape', 'scale'], enclosing_scope=global_scope))
global_scope.declare(_make_method_symbol('Normal', SymbolTable._random_variable,
global_scope.declare(_make_method_symbol('Normal', SymbolTable.RANDOM_VARIABLE,
['mean', 'stddev'], enclosing_scope=global_scope))
global_scope.declare(_make_method_symbol('Scalar', None, [], enclosing_scope=global_scope))
global_scope.declare(_make_method_symbol('Vector', None, [], enclosing_scope=global_scope))
......@@ -69,4 +69,4 @@ class SymbolTable:
sym.attrib |= SymbolTable.ATTRIB.EXTERN
def __str__(self):
return str(self.globals)
\ No newline at end of file
return str(self.globals)
......@@ -27,6 +27,8 @@ from lark import Transformer
@v_args(inline=True)
class Expr2Infix(Transformer):
# pylint: disable=missing-function-docstring,no-self-use,unused-argument,invalid-name
def idref(self, s):
return str(s)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment