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

Add CSV support to symtab parser

Signed-off-by: Michael Büsch's avatarMichael Buesch <[email protected]>
parent e860dfa4
......@@ -27,6 +27,17 @@ from awlsim.core.cpuspecs import *
from awlsim.core.optrans import *
from awlsim.core.util import *
import csv
class AwlsimSymTabCSVDialect(csv.Dialect):
delimiter = str(';')
quotechar = str('"')
doublequote = True
skipinitialspace = True
lineterminator = str('\r\n')
quoting = csv.QUOTE_MINIMAL
csv.register_dialect("awlsim_symtab", AwlsimSymTabCSVDialect)
class Symbol(object):
"""One symbol."""
......@@ -40,10 +51,70 @@ class Symbol(object):
def nameIsEqual(self, otherName):
return self.name.upper() == otherName.upper()
def __csvRecord(self, value):
value = str(value).encode(SymTabParser_CSV.ENCODING)
value = value.replace(b'"', b'""')
if b';' in value or\
b'"' in value or\
b'\r' in value or\
b'\n' in value:
value = b'"' + value + b'"'
return value
def toCSV(self):
# Returns compact CSV of this symbol.
# Return type is bytes.
try:
name = self.__csvRecord(self.name)
operator = self.__csvRecord(self.operator)
type = self.__csvRecord(self.type)
comment = self.__csvRecord(self.comment)
return b''.join((name, b';', operator, b';',
type, b';', comment, b'\r\n'))
except UnicodeError as e:
raise AwlSimError("Unicode error while trying to generate "
"symbol CSV dump.")
def toReadableCSV(self):
# Returns human readable, but also machine processable
# CSV of this symbol.
# Return type is bytes.
try:
name = self.__csvRecord(self.name)
operator = self.__csvRecord(self.operator)
type = self.__csvRecord(self.type)
comment = self.__csvRecord(self.comment)
namePadding = b" " * (24 - len(name))
operatorPadding = b" " * (11 - len(operator))
typePadding = b" " * (9 - len(type))
return b''.join((name, b';', namePadding, b' ',
operator, b';', operatorPadding, b' ',
type, b';', typePadding, b' ',
comment, b'\r\n'))
except UnicodeError as e:
raise AwlSimError("Unicode error while trying to generate "
"symbol CSV dump.")
def toASC(self):
# Returns ASC format of this symbol.
# Return type is bytes.
try:
name = str(self.name).encode(SymTabParser_ASC.ENCODING)
operator = str(self.operator).encode(SymTabParser_ASC.ENCODING)
type = str(self.type).encode(SymTabParser_ASC.ENCODING)
comment = str(self.comment).encode(SymTabParser_ASC.ENCODING)
name += b" " * (24 - len(name))
operator += b" " * (11 - len(operator))
type += b" " * (9 - len(type))
comment += b" " * (80 - len(comment))
return b''.join((b'126,', name, operator,
b' ', type, b' ', comment, b'\r\n'))
except UnicodeError as e:
raise AwlSimError("Unicode error while trying to generate "
"symbol ASC dump.")
def __repr__(self):
return '"%s", "%s", "%s", "%s"' %\
(str(self.name), str(self.operator),
str(self.type), str(self.comment))
return self.toReadableCSV().decode(SymTabParser_CSV.ENCODING)
class SymbolTable(object):
"""Parsed symbol table."""
......@@ -71,56 +142,80 @@ class SymbolTable(object):
for symbol in other.symbols:
self.add(symbol)
def toCSV(self):
return b"".join(s.toCSV() for s in self.symbols)
def toReadableCSV(self):
return b"".join(s.toReadableCSV() for s in self.symbols)
def toASC(self):
return b"".join(s.toASC() for s in self.symbols)
def __repr__(self):
return "\n".join(str(s) for s in self.symbols)
return self.toReadableCSV()
class SymTabParser(object):
"""Abstract symbol table parser."""
ENCODING = "latin_1"
implementations = []
@classmethod
def parseFile(cls, filename,
autodetectFormat=True,
mnemonics=S7CPUSpecs.MNEMONICS_AUTO):
data = awlFileRead(filename)
return cls.parseData(data, autodetectFormat, mnemonics)
dataBytes = awlFileRead(filename, encoding="binary")
return cls.parseData(dataBytes, autodetectFormat, mnemonics)
@classmethod
def parseData(cls, data,
def parseData(cls, dataBytes,
autodetectFormat=True,
mnemonics=S7CPUSpecs.MNEMONICS_AUTO):
if autodetectFormat:
for implCls in cls.implementations:
if implCls._probe(data):
parserClass = implCls
break
try:
if autodetectFormat:
for implCls in cls.implementations:
if implCls._probe(dataBytes):
parserClass = implCls
break
else:
raise AwlSimError("Failed to find a suitable "\
"symbol table parser")
else:
parserClass = cls
if mnemonics == S7CPUSpecs.MNEMONICS_AUTO:
instance = parserClass(S7CPUSpecs.MNEMONICS_EN)
try:
symTab = instance._parse(dataBytes)
except AwlSimError as e:
instance = parserClass(S7CPUSpecs.MNEMONICS_DE)
symTab = instance._parse(dataBytes)
else:
raise AwlSimError("Failed to find a suitable "\
"symbol table parser")
else:
parserClass = cls
if mnemonics == S7CPUSpecs.MNEMONICS_AUTO:
instance = parserClass(S7CPUSpecs.MNEMONICS_EN)
try:
symTab = instance._parse(data)
except AwlSimError as e:
instance = parserClass(S7CPUSpecs.MNEMONICS_DE)
symTab = instance._parse(data)
else:
instance = parserClass(mnemonics)
symTab = instance._parse(data)
return symTab
instance = parserClass(mnemonics)
symTab = instance._parse(dataBytes)
return symTab
except UnicodeError as e:
raise AwlSimError("Encoding error while trying to decode "
"symbol table.")
@classmethod
def _probe(cls, data):
raise NotImplementedError
def _probe(cls, dataBytes):
try:
if not dataBytes.decode(cls.ENCODING).strip():
return False
p = cls(None)
p._parse(dataBytes, probeOnly=True)
except AwlSimError as e:
return False
except UnicodeError as e:
return False
return True
def __init__(self, mnemonics):
self.mnemonics = mnemonics
def _parse(self, data):
pass
def _parse(self, data, probeOnly=False):
raise NotImplementedError
def _parseSym(self, symName, symAddr, symType, symComment,
lineNr):
......@@ -134,6 +229,18 @@ class SymTabParser(object):
if not symAddr:
raise AwlSimError("Symbol table parser: Symbol '%s' lacks "
"an address (line %d)" % (symName, lineNr))
if len(symName) > 24:
raise AwlSimError("Symbol table parser: Symbol name '%s' is "
"too long. Maximum is 24 characters." % symName)
if len(symAddr) > 11:
raise AwlSimError("Symbol table parser: Symbol address string of symbol '%s' is "
"too long. Maximum is 11 characters." % symName)
if len(symType) > 9:
raise AwlSimError("Symbol table parser: Symbol type string of symbol '%s' is "
"too long. Maximum is 9 characters." % symName)
if len(symComment) > 80:
raise AwlSimError("Symbol table parser: Symbol comment string of symbol '%s' is "
"too long. Maximum is 80 characters." % symName)
if symAddr.startswith("VAT") and not symType:
symType = symAddr
if not symType:
......@@ -158,14 +265,8 @@ class SymTabParser(object):
comment = symComment)
class SymTabParser_ASC(SymTabParser):
@classmethod
def _probe(cls, data):
lines = data.splitlines()
return lines and\
len(lines[0]) == 130 and\
lines[0].startswith("126,")
def _parse(self, data):
def _parse(self, dataBytes, probeOnly=False):
data = dataBytes.decode(self.ENCODING)
table = SymbolTable()
lines = data.splitlines()
for i, line in enumerate(lines):
......@@ -184,19 +285,34 @@ class SymTabParser_ASC(SymTabParser):
symAddr = line[28:40]
symType = line[40:50]
symComment = line[50:]
table.add(self._parseSym(symName = symName,
symAddr = symAddr,
symType = symType,
symComment = symComment,
lineNr = lineNr))
if not probeOnly:
table.add(self._parseSym(symName = symName,
symAddr = symAddr,
symType = symType,
symComment = symComment,
lineNr = lineNr))
return table
SymTabParser.implementations.append(SymTabParser_ASC)
class SymTabParser_CSV(SymTabParser):
def _parse(self, dataBytes, probeOnly=False):
data = dataBytes.decode(self.ENCODING)
table = SymbolTable()
csvReader = csv.reader(data.splitlines(),
dialect="awlsim_symtab")
for i, row in enumerate(csvReader):
lineNr = i + 1
if len(row) != 4:
raise AwlSimError("Wrong record count in "
"line %d. Expected 4, but got %d records." %\
(lineNr, len(row)))
if not probeOnly:
table.add(self._parseSym(symName = row[0],
symAddr = row[1],
symType = row[2],
symComment = row[3],
lineNr = lineNr))
return table
if __name__ == "__main__":
import sys
try:
print(SymTabParser.parseFile(sys.argv[1]))
except AwlSimError as e:
print(str(e))
SymTabParser.implementations.append(SymTabParser_CSV)
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