parser: base parser on reader; reader: add a way to get the current progress
This commit is contained in:
parent
25fce7a95a
commit
73af3adb34
130
gds/parser.py
130
gds/parser.py
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user