parser: base parser on reader; reader: add a way to get the current progress

This commit is contained in:
Julian Daube 2019-07-01 13:08:10 +02:00
parent 25fce7a95a
commit 73af3adb34
2 changed files with 117 additions and 53 deletions

View File

@ -17,6 +17,7 @@ class errors(Enum):
EXPECTED_TEXTTYPE = "expected TEXTTYPE" EXPECTED_TEXTTYPE = "expected TEXTTYPE"
EXPECTED_STRING = "textelement is missing text" EXPECTED_STRING = "textelement is missing text"
EXPECTED_BOXTYPE = "expected BOXTYPE" EXPECTED_BOXTYPE = "expected BOXTYPE"
EXPECTED_SNAME = "expected SNAME"
INVALID_PATHTYPE = "pathtype must be in range [0,2]" INVALID_PATHTYPE = "pathtype must be in range [0,2]"
class Warnings(Enum): class Warnings(Enum):
@ -25,17 +26,19 @@ class Warnings(Enum):
class ParserError(Exception): class ParserError(Exception):
pass pass
class Parser(object): class Parser(Reader):
# parser stack # parser stack
_token = None _token = None
last_token = None progress_callback = None
# parser state # parser state
Library = Library() Library = Library()
current_structure = None current_structure = None
def __init__(self, reader): def __init__(self, file, progress_callback=None):
self.reader = reader super(Parser, self).__init__(file)
self.progress_callback = progress_callback
@property @property
def token(self): def token(self):
@ -43,11 +46,10 @@ class Parser(object):
@token.setter @token.setter
def token(self, t): def token(self, t):
self.last_token = self._token
self._token = t self._token = t
def next_token(self, throw=False): def next_token(self, throw=False):
self.token = self.reader.read_record() self.token = self.read_record()
if not self.token and throw: if not self.token and throw:
raise ParserError(errors.UNEXPECTED_EOF) raise ParserError(errors.UNEXPECTED_EOF)
@ -62,19 +64,19 @@ class Parser(object):
if not self.next_token() or self.token.ident != Records.HEADER: if not self.next_token() or self.token.ident != Records.HEADER:
raise ParserError(errors.EXPECTED_HEADER) raise ParserError(errors.EXPECTED_HEADER)
self.Library.version = self.reader.read_short() self.Library.version = self.read_short()
# must see BGNLIB # must see BGNLIB
if not self.next_token() or self.token.ident != Records.BGNLIB: if not self.next_token() or self.token.ident != Records.BGNLIB:
raise ParserError(errors.EXPECTED_BGNLIB) raise ParserError(errors.EXPECTED_BGNLIB)
self.Library.last_mod = self.reader.read_date() self.Library.last_mod = self.read_date()
self.Library.last_access = self.reader.read_date() self.Library.last_access = self.read_date()
if not self.next_token() or self.token.ident != Records.LIBNAME: if not self.next_token() or self.token.ident != Records.LIBNAME:
raise ParserError(errors.EXPECTED_LIBNAME) raise ParserError(errors.EXPECTED_LIBNAME)
self.Library.name = self.reader.read_ascii(self.token.len) self.Library.name = self.read_ascii(self.token.len)
# read optional records # read optional records
while self.next_token(): while self.next_token():
@ -83,14 +85,14 @@ class Parser(object):
self.token.ident == Records.ATTRTABLE or \ self.token.ident == Records.ATTRTABLE or \
self.token.ident == Records.GENERATIONS: self.token.ident == Records.GENERATIONS:
# skip these records # skip these records
self.reader.skip(self.token.len) self.skip(self.token.len)
elif self.token.ident == Records.FORMAT: elif self.token.ident == Records.FORMAT:
self.reader.skip(self.token.len) self.skip(self.token.len)
# look for optional mask # look for optional mask
while self.next_token().ident == Records.MASK: while self.next_token().ident == Records.MASK:
self.reader.skip(self.token.len) self.skip(self.token.len)
if self.token.ident != Records.ENDMASK: if self.token.ident != Records.ENDMASK:
raise ParserError(errors.EXPECTED_ENDMASK) raise ParserError(errors.EXPECTED_ENDMASK)
@ -103,8 +105,8 @@ class Parser(object):
raise ParserError(errors.EXPECTED_UNITS) raise ParserError(errors.EXPECTED_UNITS)
# read units # read units
self.Library.units_per_dbunit = self.reader.read_double() self.Library.units_per_dbunit = self.read_double()
self.Library.meters_per_unit = self.reader.read_double() self.Library.meters_per_unit = self.read_double()
while self.next_token() and self.parse_structure(): while self.next_token() and self.parse_structure():
pass pass
@ -112,20 +114,24 @@ class Parser(object):
if self.token.ident != Records.ENDLIB: if self.token.ident != Records.ENDLIB:
print(Warnings.EXPECTED_ENDLIB.value) print(Warnings.EXPECTED_ENDLIB.value)
# tell callback that the process completed
if self.progress_callback:
self.progress_callback(self)
def parse_structure(self): def parse_structure(self):
self.structure = Structure() self.structure = Structure()
if self.token.ident != Records.BGNSTR: if self.token.ident != Records.BGNSTR:
return False return False
self.structure.last_mod = self.reader.read_date() self.structure.last_mod = self.read_date()
self.structure.last_access = self.reader.read_date() self.structure.last_access = self.read_date()
# read sname # read sname
if not self.next_token() or self.token.ident != Records.STRNAME: if not self.next_token() or self.token.ident != Records.STRNAME:
raise ParserError(errors.EXPECTED_STRNAME) raise ParserError(errors.EXPECTED_STRNAME)
self.structure.name = self.reader.read_ascii(self.token.len) self.structure.name = self.read_ascii(self.token.len)
while self.next_token() and self.token.ident != Records.ENDSTR: while self.next_token() and self.token.ident != Records.ENDSTR:
if self.token.ident == Records.BOUNDARY: if self.token.ident == Records.BOUNDARY:
@ -137,7 +143,10 @@ class Parser(object):
elif self.token.ident == Records.SREF: elif self.token.ident == Records.SREF:
self.parse_sref() self.parse_sref()
else: else:
self.reader.skip(self.token.len) self.skip(self.token.len)
if self.progress_callback:
self.progress_callback(self)
self.Library.structures[self.structure.name] = self.structure self.Library.structures[self.structure.name] = self.structure
self.structure = None self.structure = None
@ -146,7 +155,7 @@ class Parser(object):
def parse_element(self, element): def parse_element(self, element):
if self.token.ident == Records.ELFLAGS: if self.token.ident == Records.ELFLAGS:
element.elflags = self.reader.read_short() element.elflags = self.read_short()
self.next_token(True) self.next_token(True)
if self.token.ident == Records.PLEX: if self.token.ident == Records.PLEX:
@ -162,17 +171,17 @@ class Parser(object):
self.parse_element(element) self.parse_element(element)
if self.token.ident == Records.LAYER: if self.token.ident == Records.LAYER:
element.layer = self.reader.read_short() element.layer = self.read_short()
if not self.next_token() or self.token.ident != Records.DATATYPE: if not self.next_token() or self.token.ident != Records.DATATYPE:
raise ParserError(errors.EXPECTED_DATATYPE) raise ParserError(errors.EXPECTED_DATATYPE)
element.datatype = self.reader.read_short() element.datatype = self.read_short()
if not self.next_token() or self.token.ident != Records.XY: if not self.next_token() or self.token.ident != Records.XY:
raise ParserError(errors.EXPECTED_POINTS) raise ParserError(errors.EXPECTED_POINTS)
element.points = self.reader.read_coords(self.token.len) element.points = self.read_coords(self.token.len)
if not self.next_token() or self.token.ident != Records.ENDEL: if not self.next_token() or self.token.ident != Records.ENDEL:
raise ParserError(errors.EXPECTED_ENDEL) raise ParserError(errors.EXPECTED_ENDEL)
@ -189,17 +198,17 @@ class Parser(object):
if self.token.ident != Records.LAYER: if self.token.ident != Records.LAYER:
raise ParserError(errors.EXPECTED_LAYER) raise ParserError(errors.EXPECTED_LAYER)
element.layer = self.reader.read_short() element.layer = self.read_short()
if not self.next_token() or self.token.ident != Records.DATATYPE: if not self.next_token() or self.token.ident != Records.DATATYPE:
raise ParserError(errors.EXPECTED_DATATYPE) raise ParserError(errors.EXPECTED_DATATYPE)
element.datatype = self.reader.read_short() element.datatype = self.read_short()
self.next_token(True) self.next_token(True)
if self.token.ident == Records.PATHTYPE: if self.token.ident == Records.PATHTYPE:
pathtype = self.reader.read_short() pathtype = self.read_short()
if pathtype < 0 or pathtype > 4 or pathtype == 3: if pathtype < 0 or pathtype > 4 or pathtype == 3:
raise ParserError(errors.INVALID_PATHTYPE) raise ParserError(errors.INVALID_PATHTYPE)
@ -207,17 +216,17 @@ class Parser(object):
self.next_token(True) self.next_token(True)
if self.token.ident == Records.WIDTH: if self.token.ident == Records.WIDTH:
element.width = self.reader.read_int() element.width = self.read_int()
self.next_token(True) self.next_token(True)
if self.token.ident == Records.BGNEXTN: if self.token.ident == Records.BGNEXTN:
element.extendEnd[0] = self.reader.read_int() element.extendEnd[0] = self.read_int()
self.next_token(True) self.next_token(True)
elif element.pathStyle == Path.Styles.OFFSET_ENDS: elif element.pathStyle == Path.Styles.OFFSET_ENDS:
element.extendEnd[0] = element.width/2 element.extendEnd[0] = element.width/2
if self.token.ident == Records.ENDEXTN: if self.token.ident == Records.ENDEXTN:
element.extendEnd[1] = self.reader.read_int() element.extendEnd[1] = self.read_int()
self.next_token(True) self.next_token(True)
elif element.pathStyle == Path.Styles.OFFSET_ENDS: elif element.pathStyle == Path.Styles.OFFSET_ENDS:
element.extendEnd[1] = element.width/2 element.extendEnd[1] = element.width/2
@ -225,7 +234,7 @@ class Parser(object):
if self.token.ident != Records.XY: if self.token.ident != Records.XY:
raise ParserError(errors.EXPECTED_POINTS) raise ParserError(errors.EXPECTED_POINTS)
element.points = self.reader.read_coords(self.token.len) element.points = self.read_coords(self.token.len)
if not self.next_token() or self.token.ident != Records.ENDEL: if not self.next_token() or self.token.ident != Records.ENDEL:
raise ParserError(errors.EXPECTED_ENDEL) raise ParserError(errors.EXPECTED_ENDEL)
@ -242,17 +251,17 @@ class Parser(object):
if not self.token.ident == Records.LAYER: if not self.token.ident == Records.LAYER:
raise ParserError(errors.EXPECTED_LAYER) raise ParserError(errors.EXPECTED_LAYER)
element.layer = self.reader.read_short() element.layer = self.read_short()
if not self.next_token() or self.token.ident != Records.TEXTTYPE: if not self.next_token() or self.token.ident != Records.TEXTTYPE:
raise ParserError(errors.EXPECTED_TEXTTYPE) raise ParserError(errors.EXPECTED_TEXTTYPE)
element.datatype = self.reader.read_short() element.datatype = self.read_short()
self.next_token(True) self.next_token(True)
if self.token.ident == Records.PRESENTATION: if self.token.ident == Records.PRESENTATION:
temp = self.reader.read_short() temp = self.read_short()
element.fontnumber = (temp>>10)&0x03 element.fontnumber = (temp>>10)&0x03
element.verticalJustification = Text.VJust((temp>>12)&0x03) element.verticalJustification = Text.VJust((temp>>12)&0x03)
element.horizontalJustification = Text.HJust((temp>>14)&0X03) element.horizontalJustification = Text.HJust((temp>>14)&0X03)
@ -260,11 +269,11 @@ class Parser(object):
self.next_token(True) self.next_token(True)
if self.token.ident == Records.PATHTYPE: if self.token.ident == Records.PATHTYPE:
element.pathStype = Path.Styles(self.reader.read_short()) element.pathStype = Path.Styles(self.read_short())
self.next_token(True) self.next_token(True)
if self.token.ident == Records.WIDTH: if self.token.ident == Records.WIDTH:
element.pathWidth = self.reader.read_short() element.pathWidth = self.read_short()
self.next_token(True) self.next_token(True)
self.parse_strans(element.transformation) self.parse_strans(element.transformation)
@ -272,15 +281,15 @@ class Parser(object):
if self.token.ident != Records.XY: if self.token.ident != Records.XY:
raise ParserError(errors.EXPECTED_POINT) raise ParserError(errors.EXPECTED_POINT)
element.point = self.reader.read_coord() element.point = self.read_coord()
# skip potential array (if given) # skip potential array (if given)
self.reader.skip(self.token.len - 8) self.skip(self.token.len - 8)
if not self.next_token() or self.token.ident != Records.STRING: if not self.next_token() or self.token.ident != Records.STRING:
raise ParserError(errors.EXPECTED_STRING) raise ParserError(errors.EXPECTED_STRING)
element.string = self.reader.read_ascii(self.token.len) element.string = self.read_ascii(self.token.len)
if not self.next_token() or self.token.ident != Records.ENDEL: if not self.next_token() or self.token.ident != Records.ENDEL:
raise ParserError(errors.EXPECTED_ENDEL) raise ParserError(errors.EXPECTED_ENDEL)
@ -291,7 +300,7 @@ class Parser(object):
if self.token.ident != Records.STRANS: if self.token.ident != Records.STRANS:
return return
flags = self.reader.read_short() flags = self.read_short()
if flags & 0x01: if flags & 0x01:
trans.mirror_x = True trans.mirror_x = True
@ -304,11 +313,11 @@ class Parser(object):
self.next_token(True) self.next_token(True)
if self.token.ident == Records.MAG: if self.token.ident == Records.MAG:
trans.zoom = self.reader.read_double() trans.zoom = self.read_double()
self.next_token(True) self.next_token(True)
if self.token.ident == Records.ANGLE: if self.token.ident == Records.ANGLE:
trans.rotation = self.reader.read_double() trans.rotation = self.read_double()
self.next_token(True) self.next_token(True)
def parse_box(self): def parse_box(self):
@ -321,17 +330,17 @@ class Parser(object):
if self.token.ident != Records.LAYER: if self.token.ident != Records.LAYER:
raise ParserError(errors.EXPECTED_LAYER) raise ParserError(errors.EXPECTED_LAYER)
element.layer = self.reader.read_short() element.layer = self.read_short()
if not self.next_token() or self.token.ident != Records.BOXTYPE: if not self.next_token() or self.token.ident != Records.BOXTYPE:
raise ParserError(errors.EXPECTED_BOXTYPE) raise ParserError(errors.EXPECTED_BOXTYPE)
element.datatype = self.reader.read_short() element.datatype = self.read_short()
if not self.next_token() or self.token.ident != Records.XY: if not self.next_token() or self.token.ident != Records.XY:
raise ParserError(errors.EXPECTED_POINTS) raise ParserError(errors.EXPECTED_POINTS)
element.points = self.reader.read_coords(self.token.len) element.points = self.read_coords(self.token.len)
if not self.next_token() or self.token.ident != Records.ENDEL: if not self.next_token() or self.token.ident != Records.ENDEL:
raise ParserError(errors.EXPECTED_ENDEL) raise ParserError(errors.EXPECTED_ENDEL)
@ -339,12 +348,35 @@ class Parser(object):
self.structure.elements.append(element) self.structure.elements.append(element)
def parse_sref(self): def parse_sref(self):
while self.next_token() and self.token.ident != Records.ENDEL: element = SRef()
self.reader.skip(self.token.len) element.parent = self.structure
def parse_file(file): if not self.next_token():
r = Reader(file) raise ParserError(errors.EXPECTED_SNAME)
parser = Parser(r)
self.parse_element(element)
if self.token.ident != Records.SNAME:
raise ParserError(errors.EXPECTED_SNAME)
element.structure = self.read_ascii(self.token.len)
self.next_token(True)
self.parse_strans(element.transformation)
if self.token.ident != Records.XY:
raise ParserError(errors.EXPECTED_POINT)
element.point = self.read_coord()
self.skip(self.token.len - 8)
if not self.next_token() or self.token.ident != Records.ENDEL:
raise ParserError(errors.EXPECTED_ENDEL)
self.structure.references.append(element)
def parse_file(file, progress_func=None):
parser = Parser(file, progress_callback=progress_func)
parser.parse_lib() parser.parse_lib()
return parser.Library return parser.Library

View File

@ -1,10 +1,42 @@
from datetime import datetime from datetime import datetime
from .record import * from .record import *
import abc
class ProgressGetter(object, metaclass=abc.ABCMeta):
@abc.abstractproperty
def current(self):
pass
@abc.abstractproperty
def total(self):
pass
@abc.abstractmethod
def progress(self):
raise NotImplementedError("getting the progess is not implemented")
class Reader(ProgressGetter):
# statistics
total = 0
@property
def current(self):
return float(self.stream.tell())
# returns the read progress in percent
def progress(self):
if self.total <= 0:
return 1.0
return self.current / self.total
class Reader(object):
def __init__(self, file): def __init__(self, file):
self.stream = file self.stream = file
# find file size
self.stream.seek(0, 2)
self.total = self.stream.tell()
self.stream.seek(0, 0)
def skip(self, n): def skip(self, n):
self.stream.read(n) self.stream.read(n)