Commit 9d72cb27 authored by Michael Büsch's avatar Michael Büsch

fupcompiler: Move boolean and operand compiler to their own files

Signed-off-by: Michael Büsch's avatarMichael Buesch <m@bues.ch>
parent ded2776d
......@@ -82,27 +82,11 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
TYPE_OPERAND = EnumGen.item
EnumGen.end
EnumGen.start
SUBTYPE_AND = EnumGen.item
SUBTYPE_OR = EnumGen.item
SUBTYPE_XOR = EnumGen.item
SUBTYPE_LOAD = EnumGen.item
SUBTYPE_ASSIGN = EnumGen.item
EnumGen.end
str2type = {
"boolean" : TYPE_BOOLEAN,
"operand" : TYPE_OPERAND,
}
str2subtype = {
"and" : SUBTYPE_AND,
"or" : SUBTYPE_OR,
"xor" : SUBTYPE_XOR,
"load" : SUBTYPE_LOAD,
"assign" : SUBTYPE_ASSIGN,
}
@classmethod
def sorted(cls, elems):
"""Sort all elements from 'elems' sequence in ascending order by Y position.
......@@ -120,15 +104,23 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
@classmethod
def parse(cls, grid, x, y, elemType, subType, content):
from awlsim.fupcompiler.fupcompiler_elembool import FupCompiler_ElemBool
from awlsim.fupcompiler.fupcompiler_elemoper import FupCompiler_ElemOper
try:
elemType = cls.str2type[elemType]
if subType:
subType = cls.str2subtype[subType]
else:
subType = None
if elemType == cls.TYPE_BOOLEAN:
return FupCompiler_ElemBool.parse(grid=grid,
x=x, y=y,
subType=subType,
content=content)
elif elemType == cls.TYPE_OPERAND:
return FupCompiler_ElemOper.parse(grid=grid,
x=x, y=y,
subType=subType,
content=content)
except KeyError:
return None
return cls(grid, x, y, elemType, subType, content)
pass
return None
def __init__(self, grid, x, y, elemType, subType, content):
FupCompiler_BaseObj.__init__(self)
......@@ -140,17 +132,13 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
self.content = content or "" # content string
self.connections = set() # FupCompiler_Conn
# Constructor class used for LOAD operand.
self.__insnClass = None
# If this field has already been compiled and the result is
# stored in a temp-field, this is the name of that field.
self.__resultVkeVarName = None
if elemType == self.TYPE_OPERAND and\
subType == self.SUBTYPE_LOAD:
# Allow multiple compilations of LOAD operand.
self.allowTrans_done2Running = True
@property
def opTrans(self):
return self.grid.compiler.opTrans
def addConn(self, conn):
self.connections.add(conn)
......@@ -172,8 +160,7 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
if conn.dirOut:
yield conn
def __mayStoreToTemp(self):
opTrans = self.grid.compiler.opTrans
def _mayStoreToTemp(self):
insns = []
# Check if we need to save the result
......@@ -183,15 +170,14 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
if any(len(tuple(c.getConnected(getInputs=True))) > 1
for c in self.outConnections):
varName = self.grid.compiler.interf.allocTEMP("BOOL")
opDesc = opTrans.translateFromString("#" + varName)
opDesc = self.opTrans.translateFromString("#" + varName)
insns.append(AwlInsn_ASSIGN(cpu=None,
ops=[opDesc.operator]))
self.__resultVkeVarName = varName
return insns
def __loadFromTemp(self, insnClass):
opTrans = self.grid.compiler.opTrans
def _loadFromTemp(self, insnClass):
insns = []
# otherElem has already been compiled and the result has
......@@ -202,166 +188,17 @@ class FupCompiler_Elem(FupCompiler_BaseObj):
raise AwlSimError("FUP compiler: Result of a "
"compiled element has not been stored "
"to a TEMP variable.")
opDesc = opTrans.translateFromString("#" + varName)
opDesc = self.opTrans.translateFromString("#" + varName)
insns.append(insnClass(cpu=None, ops=[opDesc.operator]))
return insns
def __compile_OPERAND_LOAD(self):
opTrans = self.grid.compiler.opTrans
insns = []
insnClass = self.__insnClass
if not insnClass:
# This shall never happen.
raise AwlSimError("FUP LOAD: Load without a "
"known instruction class.")
# Translate the LOAD operand and create the
# corresponding instruction.
opDesc = opTrans.translateFromString(self.content)
insns.append(insnClass(cpu=None, ops=[opDesc.operator]))
return insns
def __compile_OPERAND_ASSIGN(self):
opTrans = self.grid.compiler.opTrans
insns = []
# Only one connection allowed per ASSIGN.
if len(self.connections) != 1:
raise AwlSimError("FUP ASSIGN: Invalid number of "
"connections in '%s'." % (
str(self)))
# The connection must be input.
conn = getany(self.connections)
if not conn.dirIn or conn.dirOut or conn.pos != 0:
raise AwlSimError("FUP ASSIGN: Invalid connection "
"properties in '%s'." % (
str(self)))
# Compile the element connected to the input.
connsOut = tuple(conn.getConnected(getOutputs=True))
if len(connsOut) != 1:
raise AwlSimError("FUP ASSIGN: Multiple outbound signals "
"connected to '%s'." % (
str(self)))
otherElem = connsOut[0].elem
if otherElem.compileState == self.NOT_COMPILED:
insns.extend(otherElem.compile())
else:
insns.extend(otherElem.__loadFromTemp(AwlInsn_U))
# Create the ASSIGN instruction.
opDesc = opTrans.translateFromString(self.content)
insns.append(AwlInsn_ASSIGN(cpu=None, ops=[opDesc.operator]))
insns.extend(otherElem.__mayStoreToTemp())
# Compile additional assign operators.
# This is an optimization to avoid additional compilations
# of the whole tree. We just assign the VKE once again.
#FIXME this might lead to problems in evaluation order, if we have a branch with interleaved assigns and other elems.
for otherElem in self.sorted(conn.getConnectedElems(viaIn=True)):
if otherElem.elemType == self.TYPE_OPERAND and\
otherElem.subType == self.SUBTYPE_ASSIGN:
otherElem.compileState = self.COMPILE_RUNNING
opDesc = opTrans.translateFromString(otherElem.content)
insns.append(AwlInsn_ASSIGN(cpu=None, ops=[opDesc.operator]))
otherElem.compileState = self.COMPILE_DONE
return insns
__operandTable = {
SUBTYPE_LOAD : __compile_OPERAND_LOAD,
SUBTYPE_ASSIGN : __compile_OPERAND_ASSIGN,
}
def __compile_OPERAND(self):
try:
handler = self.__operandTable[self.subType]
except KeyError:
raise AwlSimError("FUP compiler: Unknown element "
"subtype OPERAND/%d" % self.subType)
return handler(self)
def __compile_BOOLEAN_generic(self, insnClass, insnBranchClass):
opTrans = self.grid.compiler.opTrans
insns = []
# Walk down each input connection of this element.
for conn in sorted(self.connections, key=lambda c: c.pos):
if not conn.dirIn:
continue
# For each element that is connected to this element's
# input connection via its output connection.
for otherElem in conn.getConnectedElems(viaOut=True):
if otherElem.elemType == self.TYPE_OPERAND and\
otherElem.subType == self.SUBTYPE_LOAD:
# The other element is a LOAD operand.
# Compile the boolean (load) instruction.
try:
otherElem.__insnClass = insnClass
insns.extend(otherElem.compile())
finally:
otherElem.__insnClass = None
elif otherElem.elemType == self.TYPE_BOOLEAN:
# The other element we get the signal from
# is a boolean element. Compile this to get its
# resulting VKE.
if otherElem.compileState == self.NOT_COMPILED:
insns.append(insnBranchClass(cpu=None))
insns.extend(otherElem.compile())
# Store result to a TEMP variable, if required.
insns.extend(otherElem.__mayStoreToTemp())
insns.append(AwlInsn_BEND(cpu=None))
else:
# Get the stored result from TEMP.
insns.extend(otherElem.__loadFromTemp(insnClass))
else:
raise AwlSimError("FUP compiler: Invalid "
"element '%s' connected to '%s'." % (
str(otherElem), str(self)))
return insns
def __compile_BOOLEAN_AND(self):
return self.__compile_BOOLEAN_generic(AwlInsn_U, AwlInsn_UB)
def __compile_BOOLEAN_OR(self):
return self.__compile_BOOLEAN_generic(AwlInsn_O, AwlInsn_OB)
def __compile_BOOLEAN_XOR(self):
return self.__compile_BOOLEAN_generic(AwlInsn_X, AwlInsn_XB)
__booleanTable = {
SUBTYPE_AND : __compile_BOOLEAN_AND,
SUBTYPE_OR : __compile_BOOLEAN_OR,
SUBTYPE_XOR : __compile_BOOLEAN_XOR,
}
def __compile_BOOLEAN(self):
try:
handler = self.__booleanTable[self.subType]
except KeyError:
raise AwlSimError("FUP compiler: Unknown element "
"subtype BOOLEAN/%d" % self.subType)
return handler(self)
__typeTable = {
TYPE_OPERAND : __compile_OPERAND,
TYPE_BOOLEAN : __compile_BOOLEAN,
}
def _doCompile(self):
raise NotImplementedError
def compile(self):
self.compileState = self.COMPILE_RUNNING
try:
handler = self.__typeTable[self.elemType]
except KeyError:
raise AwlSimError("FUP compiler: Unknown element "
"type %d" % self.elemType)
result = handler(self)
result = self._doCompile()
self.compileState = self.COMPILE_DONE
return result
......
# -*- coding: utf-8 -*-
#
# AWL simulator - FUP compiler - Boolean element
#
# Copyright 2016-2017 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
from awlsim.fupcompiler.fupcompiler_elem import *
from awlsim.fupcompiler.fupcompiler_elemoper import *
#from awlsim.core.instructions.all_insns cimport * #@cy
from awlsim.core.instructions.all_insns import * #@nocy
class FupCompiler_ElemBool(FupCompiler_Elem):
"""FUP compiler - Boolean element.
"""
EnumGen.start
SUBTYPE_AND = EnumGen.item
SUBTYPE_OR = EnumGen.item
SUBTYPE_XOR = EnumGen.item
EnumGen.end
str2subtype = {
"and" : SUBTYPE_AND,
"or" : SUBTYPE_OR,
"xor" : SUBTYPE_XOR,
}
@classmethod
def parse(cls, grid, x, y, subType, content):
try:
subType = cls.str2subtype[subType]
if subType == cls.SUBTYPE_AND:
return FupCompiler_ElemBoolAnd(grid=grid,
x=x, y=y,
content=content)
elif subType == cls.SUBTYPE_OR:
return FupCompiler_ElemBoolOr(grid=grid,
x=x, y=y,
content=content)
elif subType == cls.SUBTYPE_XOR:
return FupCompiler_ElemBoolXor(grid=grid,
x=x, y=y,
content=content)
except KeyError:
pass
return None
def __init__(self, grid, x, y, subType, content):
FupCompiler_Elem.__init__(self, grid=grid, x=x, y=y,
elemType=FupCompiler_Elem.TYPE_BOOLEAN,
subType=subType, content=content)
def _doCompileBool(self, insnClass, insnBranchClass):
insns = []
# Walk down each input connection of this element.
for conn in sorted(self.connections, key=lambda c: c.pos):
if not conn.dirIn:
continue
# For each element that is connected to this element's
# input connection via its output connection.
for otherElem in conn.getConnectedElems(viaOut=True):
if otherElem.elemType == self.TYPE_OPERAND and\
otherElem.subType == FupCompiler_ElemOper.SUBTYPE_LOAD:
# The other element is a LOAD operand.
# Compile the boolean (load) instruction.
try:
otherElem.setInsnClass(insnClass)
insns.extend(otherElem.compile())
finally:
otherElem.setInsnClass(None)
elif otherElem.elemType == self.TYPE_BOOLEAN:
# The other element we get the signal from
# is a boolean element. Compile this to get its
# resulting VKE.
if otherElem.compileState == self.NOT_COMPILED:
insns.append(insnBranchClass(cpu=None))
insns.extend(otherElem.compile())
# Store result to a TEMP variable, if required.
insns.extend(otherElem._mayStoreToTemp())
insns.append(AwlInsn_BEND(cpu=None))
else:
# Get the stored result from TEMP.
insns.extend(otherElem._loadFromTemp(insnClass))
else:
raise AwlSimError("FUP compiler: Invalid "
"element '%s' connected to '%s'." % (
str(otherElem), str(self)))
return insns
class FupCompiler_ElemBoolAnd(FupCompiler_ElemBool):
"""FUP compiler - Boolean AND element.
"""
def __init__(self, grid, x, y, content):
FupCompiler_ElemBool.__init__(self, grid=grid, x=x, y=y,
subType=FupCompiler_ElemBool.SUBTYPE_AND,
content=content)
def _doCompile(self):
return self._doCompileBool(AwlInsn_U, AwlInsn_UB)
class FupCompiler_ElemBoolOr(FupCompiler_ElemBool):
"""FUP compiler - Boolean OR element.
"""
def __init__(self, grid, x, y, content):
FupCompiler_ElemBool.__init__(self, grid=grid, x=x, y=y,
subType=FupCompiler_ElemBool.SUBTYPE_OR,
content=content)
def _doCompile(self):
return self._doCompileBool(AwlInsn_O, AwlInsn_OB)
class FupCompiler_ElemBoolXor(FupCompiler_ElemBool):
"""FUP compiler - Boolean XOR element.
"""
def __init__(self, grid, x, y, content):
FupCompiler_ElemBool.__init__(self, grid=grid, x=x, y=y,
subType=FupCompiler_ElemBool.SUBTYPE_XOR,
content=content)
def _doCompile(self):
return self._doCompileBool(AwlInsn_X, AwlInsn_XB)
# -*- coding: utf-8 -*-
#
# AWL simulator - FUP compiler - Operand element
#
# Copyright 2016-2017 Michael Buesch <m@bues.ch>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
from awlsim.fupcompiler.fupcompiler_elem import *
#from awlsim.core.instructions.all_insns cimport * #@cy
from awlsim.core.instructions.all_insns import * #@nocy
class FupCompiler_ElemOper(FupCompiler_Elem):
"""FUP compiler - Operand element.
"""
EnumGen.start
SUBTYPE_LOAD = EnumGen.item
SUBTYPE_ASSIGN = EnumGen.item
EnumGen.end
str2subtype = {
"load" : SUBTYPE_LOAD,
"assign" : SUBTYPE_ASSIGN,
}
@classmethod
def parse(cls, grid, x, y, subType, content):
try:
subType = cls.str2subtype[subType]
if subType == cls.SUBTYPE_LOAD:
return FupCompiler_ElemOperLoad(grid=grid,
x=x, y=y,
content=content)
elif subType == cls.SUBTYPE_ASSIGN:
return FupCompiler_ElemOperAssign(grid=grid,
x=x, y=y,
content=content)
except KeyError:
pass
return None
def __init__(self, grid, x, y, subType, content):
FupCompiler_Elem.__init__(self, grid=grid, x=x, y=y,
elemType=FupCompiler_Elem.TYPE_OPERAND,
subType=subType, content=content)
class FupCompiler_ElemOperLoad(FupCompiler_ElemOper):
"""FUP compiler - Operand LOAD element.
"""
# Allow multiple compilations of LOAD operand.
allowTrans_done2Running = True
def __init__(self, grid, x, y, content):
FupCompiler_ElemOper.__init__(self, grid=grid, x=x, y=y,
subType=FupCompiler_ElemOper.SUBTYPE_LOAD,
content=content)
# Constructor class used for LOAD operand.
self.__insnClass = None
def setInsnClass(self, insnClass):
self.__insnClass = insnClass
def _doCompile(self):
insns = []
insnClass = self.__insnClass
if not insnClass:
# This shall never happen.
raise AwlSimError("FUP LOAD: Load without a "
"known instruction class.")
# Translate the LOAD operand and create the
# corresponding instruction.
opDesc = self.opTrans.translateFromString(self.content)
insns.append(insnClass(cpu=None, ops=[opDesc.operator]))
return insns
class FupCompiler_ElemOperAssign(FupCompiler_ElemOper):
"""FUP compiler - Operand ASSIGN element.
"""
def __init__(self, grid, x, y, content):
FupCompiler_ElemOper.__init__(self, grid=grid, x=x, y=y,
subType=FupCompiler_ElemOper.SUBTYPE_ASSIGN,
content=content)
def _doCompile(self):
insns = []
# Only one connection allowed per ASSIGN.
if len(self.connections) != 1:
raise AwlSimError("FUP ASSIGN: Invalid number of "
"connections in '%s'." % (
str(self)))
# The connection must be input.
conn = getany(self.connections)
if not conn.dirIn or conn.dirOut or conn.pos != 0:
raise AwlSimError("FUP ASSIGN: Invalid connection "
"properties in '%s'." % (
str(self)))
# Compile the element connected to the input.
connsOut = tuple(conn.getConnected(getOutputs=True))
if len(connsOut) != 1:
raise AwlSimError("FUP ASSIGN: Multiple outbound signals "
"connected to '%s'." % (
str(self)))
otherElem = connsOut[0].elem
if otherElem.compileState == self.NOT_COMPILED:
insns.extend(otherElem.compile())
else:
insns.extend(otherElem._loadFromTemp(AwlInsn_U))
# Create the ASSIGN instruction.
opDesc = self.opTrans.translateFromString(self.content)
insns.append(AwlInsn_ASSIGN(cpu=None, ops=[opDesc.operator]))
insns.extend(otherElem._mayStoreToTemp())
# Compile additional assign operators.
# This is an optimization to avoid additional compilations
# of the whole tree. We just assign the VKE once again.
#FIXME this might lead to problems in evaluation order, if we have a branch with interleaved assigns and other elems.
for otherElem in self.sorted(conn.getConnectedElems(viaIn=True)):
if otherElem.elemType == self.TYPE_OPERAND and\
otherElem.subType == self.SUBTYPE_ASSIGN:
otherElem.compileState = self.COMPILE_RUNNING
opDesc = self.opTrans.translateFromString(otherElem.content)
insns.append(AwlInsn_ASSIGN(cpu=None, ops=[opDesc.operator]))
otherElem.compileState = self.COMPILE_DONE
return insns
Markdown is supported
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