Commit fceea92f authored by Michael Büsch's avatar Michael Büsch

Add support for ARRAYs in DBs

Signed-off-by: Michael Büsch's avatarMichael Buesch <[email protected]>
parent c0c02569
......@@ -177,7 +177,8 @@ class S7CPU(object):
def __translateInterfaceField(self, rawVar):
dtype = AwlDataType.makeByName(rawVar.typeTokens, rawVar.dimensions)
field = BlockInterfaceField(name = rawVar.name,
assert(len(rawVar.idents) == 1) #TODO no structs, yet
field = BlockInterfaceField(name = rawVar.idents[0].name,
dataType = dtype)
return field
......@@ -189,9 +190,10 @@ class S7CPU(object):
for rawVar in rawBlock.vars_out:
block.interface.addField_OUT(self.__translateInterfaceField(rawVar))
if rawBlock.retTypeTokens:
#FIXME ARRAY?
# ARRAY is not supported for RET_VAL. So make non-array dtype.
dtype = AwlDataType.makeByName(rawBlock.retTypeTokens)
if dtype.type != AwlDataType.TYPE_VOID:
# Ok, we have a RET_VAL.
field = BlockInterfaceField(name = "RET_VAL",
dataType = dtype)
block.interface.addField_OUT(field)
......@@ -204,35 +206,44 @@ class S7CPU(object):
block.interface.buildDataStructure()
return block
# Initialize a DB (global or instance) data field from a raw data-init.
def __initDBField(self, db, dataType, rawDataInit):
if dataType.type == AwlDataType.TYPE_ARRAY:
index = dataType.arrayIndicesCollapse(*rawDataInit.idents[-1].indices)
value = dataType.parseMatchingImmediate(rawDataInit.valueTokens)
name = AwlStruct.makeArrayChildName(rawDataInit.idents[-1].name, index)
db.structInstance.setFieldDataByName(name, value)
else:
value = dataType.parseMatchingImmediate(rawDataInit.valueTokens)
db.structInstance.setFieldDataByName(rawDataInit.idents[-1].name, value)
def __translateGlobalDB(self, rawDB):
db = DB(rawDB.index, None)
# Create the data structure fields
for f in rawDB.fields:
if not f.typeTokens:
raise AwlSimError(
"DB %d assigns field '%s', "
"but does not declare it." %\
(rawDB.index, f.name))
if f.valueTokens is None:
for field in rawDB.fields:
assert(len(field.idents) == 1) #TODO no structs, yet
if not rawDB.getFieldInit(field):
raise AwlSimError(
"DB %d declares field '%s', "
"but does not initialize." %\
(rawDB.index, f.name))
dtype = AwlDataType.makeByName(f.typeTokens, f.dimensions)
db.struct.addFieldNaturallyAligned(f.name, dtype)
(rawDB.index, field.idents[0].name))
dtype = AwlDataType.makeByName(field.typeTokens,
field.dimensions)
db.struct.addFieldNaturallyAligned(field.idents[0].name,
dtype)
# Allocate the data structure fields
db.allocate()
# Initialize the data structure fields
for f in rawDB.fields:
dtype = AwlDataType.makeByName(f.typeTokens, f.dimensions)
if dtype.type == AwlDataType.TYPE_ARRAY:
for i, valueTokens in enumerate(f.valueTokens):
value = dtype.parseMatchingImmediate(valueTokens)
name = AwlStruct.makeArrayChildName(f.name, i)
db.structInstance.setFieldDataByName(name, value)
else:
value = dtype.parseMatchingImmediate(f.valueTokens)
db.structInstance.setFieldDataByName(f.name, value)
for field, init in rawDB.allFieldInits():
if not field:
raise AwlSimError(
"DB %d assigns field '%s', "
"but does not declare it." %\
(rawDB.index, init.getIdentString()))
assert(len(field.idents) == 1 and len(init.idents) == 1) #TODO no structs, yet
dtype = AwlDataType.makeByName(field.typeTokens,
field.dimensions)
self.__initDBField(db, dtype, init)
return db
def __translateInstanceDB(self, rawDB):
......@@ -251,20 +262,18 @@ class S7CPU(object):
db = DB(rawDB.index, fb)
interface = fb.interface
# Sanity checks
for f in rawDB.fields:
if f.typeTokens:
raise AwlSimError("DB %d is an "
"instance DB, but it also "
"declares a data structure." %\
rawDB.index)
if rawDB.fields:
raise AwlSimError("DB %d is an "
"instance DB, but it also "
"declares a data structure." %\
rawDB.index)
# Allocate the data structure fields
db.allocate()
# Initialize the data structure fields
for f in rawDB.fields:
#TODO ARRAY
dtype = interface.getFieldByName(f.name).dataType
value = dtype.parseMatchingImmediate(f.valueTokens)
db.structInstance.setFieldDataByName(f.name, value)
for init in rawDB.fieldInits:
assert(len(init.idents) == 1) #TODO no structs, yet
dtype = interface.getFieldByName(init.idents[-1].name).dataType
self.__initDBField(db, dtype, init)
return db
def __translateDB(self, rawDB):
......
......@@ -240,9 +240,11 @@ class AwlDataType(object):
assert(self.type == self.TYPE_ARRAY)
signif = self.arrayIndexSignificances()
assert(len(indices) == len(signif))
assert(len(indices) == len(self.arrayDimensions))
resIndex = 0
for i, idx in enumerate(indices):
resIndex += idx * signif[i]
startIdx = self.arrayDimensions[i][0]
resIndex += (idx - startIdx) * signif[i]
return resIndex
# Get the array dimension sizes. Returns a tuple of integers.
......@@ -262,41 +264,45 @@ class AwlDataType(object):
# Parse an immediate, constrained by our datatype.
def parseMatchingImmediate(self, tokens):
typeId = self.type
if typeId == self.TYPE_ARRAY:
typeId = self.children[0].type
value = None
if tokens is None:
value = 0
elif len(tokens) == 9:
if self.type == self.TYPE_DWORD:
if typeId == self.TYPE_DWORD:
value, fields = self.tryParseImmediate_ByteArray(
tokens)
elif len(tokens) == 5:
if self.type == self.TYPE_WORD:
if typeId == self.TYPE_WORD:
value, fields = self.tryParseImmediate_ByteArray(
tokens)
elif len(tokens) == 2:
if self.type == self.TYPE_TIMER:
if typeId == self.TYPE_TIMER:
if tokens[0].upper() == "T":
value = self.tryParseImmediate_INT(tokens[1])
elif self.type == self.TYPE_COUNTER:
elif typeId == self.TYPE_COUNTER:
if tokens[0].upper() in ("C", "Z"):
value = self.tryParseImmediate_INT(tokens[1])
elif self.type == self.TYPE_BLOCK_DB:
elif typeId == self.TYPE_BLOCK_DB:
if tokens[0].upper() == "DB":
value = self.tryParseImmediate_INT(tokens[1])
elif self.type == self.TYPE_BLOCK_FB:
elif typeId == self.TYPE_BLOCK_FB:
if tokens[0].upper() == "FB":
value = self.tryParseImmediate_INT(tokens[1])
elif self.type == self.TYPE_BLOCK_FC:
elif typeId == self.TYPE_BLOCK_FC:
if tokens[0].upper() == "FC":
value = self.tryParseImmediate_INT(tokens[1])
elif len(tokens) == 1:
if self.type == self.TYPE_BOOL:
if typeId == self.TYPE_BOOL:
value = self.tryParseImmediate_BOOL(
tokens[0])
elif self.type == self.TYPE_BYTE:
elif typeId == self.TYPE_BYTE:
value = self.tryParseImmediate_HexByte(
tokens[0])
elif self.type == self.TYPE_WORD:
elif typeId == self.TYPE_WORD:
value = self.tryParseImmediate_Bin(
tokens[0])
if value is None:
......@@ -305,34 +311,34 @@ class AwlDataType(object):
if value is None:
value = self.tryParseImmediate_BCD(
tokens[0])
elif self.type == self.TYPE_DWORD:
elif typeId == self.TYPE_DWORD:
value = self.tryParseImmediate_Bin(
tokens[0])
if value is None:
value = self.tryParseImmediate_HexDWord(
tokens[0])
elif self.type == self.TYPE_INT:
elif typeId == self.TYPE_INT:
value = self.tryParseImmediate_INT(
tokens[0])
elif self.type == self.TYPE_DINT:
elif typeId == self.TYPE_DINT:
value = self.tryParseImmediate_DINT(
tokens[0])
elif self.type == self.TYPE_REAL:
elif typeId == self.TYPE_REAL:
value = self.tryParseImmediate_REAL(
tokens[0])
elif self.type == self.TYPE_S5T:
elif typeId == self.TYPE_S5T:
value = self.tryParseImmediate_S5T(
tokens[0])
elif self.type == self.TYPE_TIME:
elif typeId == self.TYPE_TIME:
value = self.tryParseImmediate_TIME(
tokens[0])
elif self.type == self.TYPE_DATE:
elif typeId == self.TYPE_DATE:
pass#TODO
elif self.type == self.TYPE_DT:
elif typeId == self.TYPE_DT:
pass#TODO
elif self.type == self.TYPE_TOD:
elif typeId == self.TYPE_TOD:
pass#TODO
elif self.type == self.TYPE_CHAR:
elif typeId == self.TYPE_CHAR:
value = self.tryParseImmediate_CHAR(
tokens[0])
if value is None:
......
......@@ -120,12 +120,13 @@ class RawAwlBlock(object):
class RawAwlCodeBlock(RawAwlBlock):
def __init__(self, tree, index):
RawAwlBlock.__init__(self, tree, index)
self.insns = []
self.vars_in = []
self.vars_out = []
self.vars_inout = []
self.vars_static = []
self.vars_temp = []
self.insns = [] # List of RawAwlInsn()s
# The block interface
self.vars_in = [] # List of RawAwlDataField()s for IN vars
self.vars_out = [] # List of RawAwlDataField()s for OUT vars
self.vars_inout = [] # List of RawAwlDataField()s for IN_OUT vars
self.vars_static = [] # List of RawAwlDataField()s for STATIC vars
self.vars_temp = [] # List of RawAwlDataField()s for TEMP vars
self.retTypeTokens = None
def hasLabel(self, string):
......@@ -135,20 +136,59 @@ class RawAwlCodeBlock(RawAwlBlock):
return True
return False
class RawAwlDataIdent(object):
def __init__(self, name, indices=None):
self.name = name # Name string of the variable
self.indices = indices # Possible array indices (or None)
def __eq__(self, other):
if self.name != other.name:
return False
if self.indices and other.indices:
if self.indices != other.indices:
return False
return True
def __ne__(self, other):
return not self.__eq__(other)
def __repr__(self):
if self.indices:
return "%s[%s]" % (self.name,
",".join(str(i) for i in self.indices))
return self.name
class RawAwlDataInit(object):
def __init__(self, idents, valueTokens):
"""idents -> The identifications for the data field.
valueTokens -> List of tokens for the value.
"""
self.idents = toList(idents)
self.valueTokens = valueTokens
def getIdentString(self):
return ".".join(str(ident) for ident in self.idents)
def __repr__(self):
return self.getIdentString() + " := " + str(self.valueTokens)
class RawAwlDataField(object):
def __init__(self, name, valueTokens, typeTokens,
dimensions=None):
"""name -> The data field name string.
valueTokens -> List of tokens for the value. Or None, if no value.
If this is an array (that is dimensions is not None),
valueTokens is a list of value token strings.
def __init__(self, idents, typeTokens, dimensions=None, inits=None):
"""idents -> The identifications for the data field.
typeTokens -> List of tokens for the data type.
dimensions -> List of array dimensions, where each dimension is a
tuple of (start, end). Or None, if this is not an array."""
self.name = name
self.valueTokens = valueTokens
tuple of (start, end). Or None, if this is not an array.
inits -> List of RawAwlDataInit()s"""
self.idents = toList(idents)
self.typeTokens = typeTokens
self.dimensions = dimensions
self.inits = inits if inits else [] # List of RawAwlDataInit()s
def getIdentString(self):
return ".".join(str(ident) for ident in self.idents)
def __repr__(self):
return self.getIdentString()
class RawAwlDB(RawAwlBlock):
class FBRef(object):
......@@ -159,18 +199,50 @@ class RawAwlDB(RawAwlBlock):
def __init__(self, tree, index):
RawAwlBlock.__init__(self, tree, index)
self.fields = []
self.fb = None
def isInstanceDB(self):
return bool(self.fb)
# Data fields and initializations.
# fieldInits are the inits from the DB init section.
# The inits from the DB declaration section are in fields[x].inits.
self.fields = []
self.fieldInits = [] # List of RawAwlDataInit()s
def getByName(self, name):
for field in self.fields:
if field.name == name:
return field
def getField(self, idents):
try:
return [f for f in self.fields if f.idents == idents][0]
except IndexError as e:
return None
def addFieldInit(self, fieldInit):
self.fieldInits.append(fieldInit)
def getFieldInit(self, field):
"""Returns the RawAwlDataInit() for the specified RawAwlDataField()"""
for otherField, init in self.allFieldInits():
if field.idents == init.idents:
return init
return None
def allFieldInits(self):
"""Returns a list (generator) of all RawAwlDataInits()"""
# First all explicit field initializations
for init in self.fieldInits:
yield self.getField(init.idents), init
# Then all field initializations from the declaration,
# that we did not initialize, yet.
for field in self.fields:
for init in field.inits:
for otherInit in self.fieldInits:
if init.idents == otherInit.idents:
# We already had an init for this field.
break
else:
# We did not have this init, yet.
yield field, init
def isInstanceDB(self):
return bool(self.fb)
class RawAwlOB(RawAwlCodeBlock):
def __init__(self, tree, index):
RawAwlCodeBlock.__init__(self, tree, index)
......@@ -569,12 +641,13 @@ class AwlParser(object):
else:
raise AwlParserError("In DB header: Unknown token: %s" % name)
def __parseArrayInitializer(self, valueList, tokens):
def __parseArrayInitializer(self, name, initsList, tokens):
"""Parse an ARRAY initializer. That is either of:
1, 2, 3, 4
4 (1, 2, 3, 4)
or similar.
valueList -> The result list. Each element is a string.
name -> The name string of the variable.
initsList -> The result list. Each element is a RawAwlDataInit().
tokens -> The tokens to parse."""
pass#TODO
......@@ -585,6 +658,7 @@ class AwlParser(object):
return False
colonIdx = listIndex(t.tokens, ":")
assignIdx = listIndex(t.tokens, ":=")
initsList = []
if len(t.tokens) >= 10 and\
colonIdx == 1 and t.tokens[colonIdx+1].upper() == "ARRAY":
# This is an array variable.
......@@ -620,7 +694,7 @@ class AwlParser(object):
dimensions.append( (int(dimTokens[0]),
int(dimTokens[2])) )
except ValueError as e:
raise AwlSimError("In variable section: "
raise AwlParserError("In variable section: "
"Invalid ARRAY dimensions")
if dimensions[-1][0] > dimensions[-1][1]:
raise AwlParserError("In variable section: "
......@@ -638,11 +712,11 @@ class AwlParser(object):
else:
type = t.tokens[ofIdx+1:]
nrElems = AwlDataType.arrayDimensionsToNrElements(dimensions)
value = [ None, ] * nrElems
if assignIdx >= 0:
# Parse the ARRAY initializer (:= ...)
initTokens = t.tokens[assignIdx+1:]
self.__parseArrayInitializer(value,
self.__parseArrayInitializer(name,
initsList,
initTokens)
else:
# This is a normal non-array variable.
......@@ -650,14 +724,18 @@ class AwlParser(object):
if mayHaveInitval and colonIdx == 1 and assignIdx > colonIdx + 1:
name = t.tokens[0]
type = t.tokens[colonIdx+1:assignIdx]
value = t.tokens[assignIdx+1:]
initTokens = t.tokens[assignIdx+1:]
initsList.append(RawAwlDataInit(RawAwlDataIdent(name),
initTokens))
elif colonIdx == 1:
name = t.tokens[0]
type = t.tokens[colonIdx+1:]
value = None
else:
raise AwlParserError("In variable section: Unknown tokens")
field = RawAwlDataField(name, value, type, dimensions)
field = RawAwlDataField(idents = RawAwlDataIdent(name),
typeTokens = type,
dimensions = dimensions,
inits = initsList)
varList.append(field)
return True
......@@ -676,23 +754,45 @@ class AwlParser(object):
# Array subscript assignment
name = t.tokens[0]
db = self.tree.curBlock
field = db.getByName(name)
if field:
pass#TODO
else:
#FIXME this must be done later
raise AwlParserError("Initialization of incomplete array type")
closeIdx = listIndex(t.tokens, "]", 2)
assignIdx = listIndex(t.tokens, ":=", closeIdx + 1)
if closeIdx < 0:
raise AwlParserError("Array assignment: "
"Missing closing braces")
if assignIdx < 0 or\
assignIdx != closeIdx + 1:
raise AwlParserError("Array assignment: "
"Invalid value assignment")
indexTokens = t.tokens[2:closeIdx]
valueTokens = t.tokens[assignIdx+1:]
# Parse the array indices
indices = []
while indexTokens:
try:
indices.append(int(indexTokens[0]))
except ValueError as e:
raise AwlParserError("Array assignment: "
"Invalid index value")
indexTokens = indexTokens[1:]
if indexTokens:
if indexTokens[0] != ",":
raise AwlParserError("Array assignment: "
"Expected comma")
indexTokens = indexTokens[1:]
if not indices:
raise AwlParserError("Array assignment: "
"Invalid indices")
if len(indices) > 6:
raise AwlParserError("Array assignment: "
"More than 6 indices specified")
db.addFieldInit(RawAwlDataInit(RawAwlDataIdent(name, indices),
valueTokens))
elif len(t.tokens) >= 3 and t.tokens[1] == ":=":
# Variable assignment
name, valueTokens = t.tokens[0], t.tokens[2:]
db = self.tree.curBlock
field = db.getByName(name)
if field:
field.valueTokens = valueTokens
else:
field = RawAwlDataField(name, valueTokens,
None)
db.fields.append(field)
db.addFieldInit(RawAwlDataInit(RawAwlDataIdent(name),
valueTokens))
else:
raise AwlParserError("In DB: Unknown tokens")
......
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