initial commit (work in progress)
python version of parser works, BUT does not support AREF and SREF at the moment
This commit is contained in:
		
						commit
						5d939157ed
					
				
							
								
								
									
										1
									
								
								gds/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gds/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					__pycache__
 | 
				
			||||||
							
								
								
									
										7
									
								
								gds/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								gds/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					from .library import Library
 | 
				
			||||||
 | 
					from .structure import Structure
 | 
				
			||||||
 | 
					from .elements import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .record import *
 | 
				
			||||||
 | 
					from .reader import Reader
 | 
				
			||||||
 | 
					from .parser import *
 | 
				
			||||||
							
								
								
									
										76
									
								
								gds/elements.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								gds/elements.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Transformation(object):
 | 
				
			||||||
 | 
					    mirror_x = 0
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    absolute_rotation = 0
 | 
				
			||||||
 | 
					    absolute_magnification = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    zoom = 1
 | 
				
			||||||
 | 
					    rotation = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Element(object):
 | 
				
			||||||
 | 
					    elflags = 0
 | 
				
			||||||
 | 
					    plex    = 0
 | 
				
			||||||
 | 
					    datatype = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Drawable(object):
 | 
				
			||||||
 | 
					    layer   = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Boundary(Element, Drawable):
 | 
				
			||||||
 | 
					    points = []
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					class Path(Element, Drawable):
 | 
				
			||||||
 | 
					    class Styles(Enum):
 | 
				
			||||||
 | 
					        SQUARE_ENDS  = 0
 | 
				
			||||||
 | 
					        ROUNDED_ENDS = 1
 | 
				
			||||||
 | 
					        OFFSET_ENDS  = 2
 | 
				
			||||||
 | 
					        CUSTOM_END   = 4
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    extendEnd = [0,0] # extend past start and end    
 | 
				
			||||||
 | 
					    width     = 0
 | 
				
			||||||
 | 
					    pathStyle = Styles.SQUARE_ENDS
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    points = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Text(Element, Drawable):
 | 
				
			||||||
 | 
					    class VJust(Enum):
 | 
				
			||||||
 | 
					        Top    = 0
 | 
				
			||||||
 | 
					        Middle = 1
 | 
				
			||||||
 | 
					        Bottom = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class HJust(Enum):
 | 
				
			||||||
 | 
					        Left   = 0
 | 
				
			||||||
 | 
					        Center = 1
 | 
				
			||||||
 | 
					        Right  = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # text info
 | 
				
			||||||
 | 
					    string = ""
 | 
				
			||||||
 | 
					    position = (0,0)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # presentation
 | 
				
			||||||
 | 
					    fontnumber               = 0
 | 
				
			||||||
 | 
					    verticalJustification    = VJust.Top
 | 
				
			||||||
 | 
					    horizontalJustification = HJust.Left
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # optional path info
 | 
				
			||||||
 | 
					    pathStype = Path.Styles.SQUARE_ENDS
 | 
				
			||||||
 | 
					    pathWidth = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    transformation = Transformation()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					class Box(Element, Drawable):
 | 
				
			||||||
 | 
					    points = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SRef(Element):
 | 
				
			||||||
 | 
					    Position = None
 | 
				
			||||||
 | 
					    Structure = ""
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    transformation  = Transformation()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # tree
 | 
				
			||||||
 | 
					    Parent   = None
 | 
				
			||||||
 | 
					    Children = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								gds/library.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								gds/library.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Library(object):
 | 
				
			||||||
 | 
					    version = 0
 | 
				
			||||||
 | 
					    name = "NONAME"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    last_access = datetime.now()
 | 
				
			||||||
 | 
					    last_mod    = datetime.now()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # unit setup
 | 
				
			||||||
 | 
					    units_per_dbunit = 1
 | 
				
			||||||
 | 
					    meters_per_unit  = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    structures = {}
 | 
				
			||||||
							
								
								
									
										352
									
								
								gds/parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										352
									
								
								gds/parser.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,352 @@
 | 
				
			|||||||
 | 
					from . import *
 | 
				
			||||||
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class errors(Enum):
 | 
				
			||||||
 | 
					    UNEXPECTED_EOF   = "unexpected EOF"
 | 
				
			||||||
 | 
					    EXPECTED_HEADER  = "expected file header"    
 | 
				
			||||||
 | 
					    EXPECTED_BGNLIB  = "expected beginning of library"
 | 
				
			||||||
 | 
					    EXPECTED_LIBNAME = "Library name is missing"
 | 
				
			||||||
 | 
					    EXPECTED_ENDMASK = "MASK records was not terminated by ENDMASK"
 | 
				
			||||||
 | 
					    EXPECTED_STRNAME = "encountered nameless structure"
 | 
				
			||||||
 | 
					    EXPECTED_UNITS   = "no unit record in file"
 | 
				
			||||||
 | 
					    EXPECTED_LAYER   = "expected a layer record in element"
 | 
				
			||||||
 | 
					    EXPECTED_POINTS  = "expected coordinates for element"
 | 
				
			||||||
 | 
					    EXPECTED_POINT   = "expected coordinate for element"
 | 
				
			||||||
 | 
					    EXPECTED_DATATYPE = "expected datatype"
 | 
				
			||||||
 | 
					    EXPECTED_ENDEL   = "end of element is missing"
 | 
				
			||||||
 | 
					    EXPECTED_TEXTTYPE = "expected TEXTTYPE"
 | 
				
			||||||
 | 
					    EXPECTED_STRING   = "textelement is missing text"
 | 
				
			||||||
 | 
					    EXPECTED_BOXTYPE  = "expected BOXTYPE"
 | 
				
			||||||
 | 
					    INVALID_PATHTYPE = "pathtype must be in range [0,2]"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Warnings(Enum):
 | 
				
			||||||
 | 
					    EXPECTED_ENDLIB  = "missing end of library"
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					class ParserError(Exception):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Parser(object):
 | 
				
			||||||
 | 
					    # parser stack
 | 
				
			||||||
 | 
					    _token = None
 | 
				
			||||||
 | 
					    last_token = None
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # parser state
 | 
				
			||||||
 | 
					    Library = Library()
 | 
				
			||||||
 | 
					    current_structure = None
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def __init__(self, reader):
 | 
				
			||||||
 | 
					        self.reader = reader
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def token(self):
 | 
				
			||||||
 | 
					        return self._token
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @token.setter
 | 
				
			||||||
 | 
					    def token(self, t):
 | 
				
			||||||
 | 
					        self.last_token = self._token
 | 
				
			||||||
 | 
					        self._token = t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def next_token(self, throw=False):
 | 
				
			||||||
 | 
					        self.token = self.reader.read_record()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.token and throw:
 | 
				
			||||||
 | 
					            raise ParserError(errors.UNEXPECTED_EOF)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # if self.token:
 | 
				
			||||||
 | 
					        #     print(self.token.ident.name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return self.token
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_lib(self):
 | 
				
			||||||
 | 
					        # read header  
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.HEADER:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_HEADER)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.Library.version = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # must see BGNLIB
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.BGNLIB:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_BGNLIB)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.Library.last_mod    = self.reader.read_date()
 | 
				
			||||||
 | 
					        self.Library.last_access = self.reader.read_date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.LIBNAME:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LIBNAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.Library.name = self.reader.read_ascii(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # read optional records
 | 
				
			||||||
 | 
					        while self.next_token():
 | 
				
			||||||
 | 
					            if self.token.ident  == Records.REFLIBS or    \
 | 
				
			||||||
 | 
					                self.token.ident == Records.FONTS or     \
 | 
				
			||||||
 | 
					                self.token.ident == Records.ATTRTABLE or \
 | 
				
			||||||
 | 
					                self.token.ident == Records.GENERATIONS:
 | 
				
			||||||
 | 
					                # skip these records
 | 
				
			||||||
 | 
					                self.reader.skip(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            elif self.token.ident == Records.FORMAT:
 | 
				
			||||||
 | 
					                self.reader.skip(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # look for optional mask
 | 
				
			||||||
 | 
					                while self.next_token().ident == Records.MASK:
 | 
				
			||||||
 | 
					                    self.reader.skip(self.token.len)
 | 
				
			||||||
 | 
					                                   
 | 
				
			||||||
 | 
					                if self.token.ident != Records.ENDMASK:
 | 
				
			||||||
 | 
					                    raise ParserError(errors.EXPECTED_ENDMASK)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # continue
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident != Records.UNITS:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_UNITS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # read units
 | 
				
			||||||
 | 
					        self.Library.units_per_dbunit = self.reader.read_double()
 | 
				
			||||||
 | 
					        self.Library.meters_per_unit  = self.reader.read_double()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        while self.next_token() and self.parse_structure():
 | 
				
			||||||
 | 
					            pass
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					        if self.token.ident != Records.ENDLIB:
 | 
				
			||||||
 | 
					            print(Warnings.EXPECTED_ENDLIB.value)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					    def parse_structure(self):
 | 
				
			||||||
 | 
					        self.structure = Structure()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident != Records.BGNSTR:
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.structure.last_mod = self.reader.read_date()
 | 
				
			||||||
 | 
					        self.structure.last_access = self.reader.read_date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # read sname
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.STRNAME:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_STRNAME)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.structure.name = self.reader.read_ascii(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while self.next_token() and self.token.ident != Records.ENDSTR:
 | 
				
			||||||
 | 
					            if self.token.ident == Records.BOUNDARY:
 | 
				
			||||||
 | 
					                self.parse_boundary()
 | 
				
			||||||
 | 
					            elif self.token.ident == Records.PATH:
 | 
				
			||||||
 | 
					                self.parse_path()
 | 
				
			||||||
 | 
					            elif self.token.ident == Records.TEXT:
 | 
				
			||||||
 | 
					                self.parse_text()
 | 
				
			||||||
 | 
					            elif self.token.ident == Records.SREF:
 | 
				
			||||||
 | 
					                self.parse_sref()
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self.reader.skip(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.Library.structures[self.structure.name] = self.structure
 | 
				
			||||||
 | 
					        self.structure = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_element(self, element):
 | 
				
			||||||
 | 
					        if self.token.ident == Records.ELFLAGS:
 | 
				
			||||||
 | 
					            element.elflags = self.reader.read_short()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if self.token.ident == Records.PLEX:
 | 
				
			||||||
 | 
					            element.plex = self.read_int()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_boundary(self):
 | 
				
			||||||
 | 
					        element = Boundary()
 | 
				
			||||||
 | 
					        if not self.next_token():
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.parse_element(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.LAYER:
 | 
				
			||||||
 | 
					            element.layer = self.reader.read_short()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.DATATYPE:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_DATATYPE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.datatype = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.XY:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_POINTS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.points = self.reader.read_coords(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.ENDEL:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_ENDEL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.structure.elements.append(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_path(self):
 | 
				
			||||||
 | 
					        element = Path()
 | 
				
			||||||
 | 
					        if not self.next_token():
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.parse_element(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident != Records.LAYER:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.layer = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.DATATYPE:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_DATATYPE)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        element.datatype = self.reader.read_short()
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        self.next_token(True)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if self.token.ident == Records.PATHTYPE:
 | 
				
			||||||
 | 
					            pathtype = self.reader.read_short()
 | 
				
			||||||
 | 
					            if pathtype < 0 or pathtype > 4 or pathtype == 3:  
 | 
				
			||||||
 | 
					                raise ParserError(errors.INVALID_PATHTYPE)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            element.pathStyle = Path.Styles(pathtype)
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if self.token.ident == Records.WIDTH:
 | 
				
			||||||
 | 
					            element.width = self.reader.read_int()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.BGNEXTN:
 | 
				
			||||||
 | 
					            element.extendEnd[0] = self.reader.read_int()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					        elif element.pathStyle == Path.Styles.OFFSET_ENDS:
 | 
				
			||||||
 | 
					            element.extendEnd[0] = element.width/2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.ENDEXTN:
 | 
				
			||||||
 | 
					            element.extendEnd[1] = self.reader.read_int()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					        elif element.pathStyle == Path.Styles.OFFSET_ENDS:
 | 
				
			||||||
 | 
					            element.extendEnd[1] = element.width/2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident != Records.XY:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_POINTS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.points = self.reader.read_coords(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.ENDEL:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_ENDEL)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        self.structure.elements.append(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_text(self):
 | 
				
			||||||
 | 
					        element = Text()
 | 
				
			||||||
 | 
					        if not self.next_token():
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.parse_element(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.token.ident == Records.LAYER:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        element.layer = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.TEXTTYPE:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_TEXTTYPE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.datatype = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.PRESENTATION:
 | 
				
			||||||
 | 
					            temp = self.reader.read_short()
 | 
				
			||||||
 | 
					            element.fontnumber = (temp>>10)&0x03
 | 
				
			||||||
 | 
					            element.verticalJustification   = Text.VJust((temp>>12)&0x03)
 | 
				
			||||||
 | 
					            element.horizontalJustification = Text.HJust((temp>>14)&0X03)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.PATHTYPE:
 | 
				
			||||||
 | 
					            element.pathStype = Path.Styles(self.reader.read_short())
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if self.token.ident == Records.WIDTH:
 | 
				
			||||||
 | 
					            element.pathWidth = self.reader.read_short()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.parse_strans(element.transformation)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if self.token.ident != Records.XY:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_POINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.point = self.reader.read_coord()
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # skip potential array (if given)
 | 
				
			||||||
 | 
					        self.reader.skip(self.token.len - 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.STRING:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_STRING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.string = self.reader.read_ascii(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.ENDEL:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_ENDEL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.structure.elements.append(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_strans(self, trans:Transformation):
 | 
				
			||||||
 | 
					        if self.token.ident != Records.STRANS:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flags = self.reader.read_short()
 | 
				
			||||||
 | 
					        if flags & 0x01:
 | 
				
			||||||
 | 
					            trans.mirror_x = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if flags & 0x2000:
 | 
				
			||||||
 | 
					            trans.absolute_magnification = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if flags & 0x4000:
 | 
				
			||||||
 | 
					            trans.absolute_rotation = True
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        self.next_token(True) 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.MAG:
 | 
				
			||||||
 | 
					            trans.zoom = self.reader.read_double()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident == Records.ANGLE:
 | 
				
			||||||
 | 
					            trans.rotation = self.reader.read_double()
 | 
				
			||||||
 | 
					            self.next_token(True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def parse_box(self):
 | 
				
			||||||
 | 
					        element = Box()
 | 
				
			||||||
 | 
					        if not self.next_token():
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.parse_element(element)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.token.ident != Records.LAYER:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_LAYER)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        element.layer = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.BOXTYPE:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_BOXTYPE)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        element.datatype = self.reader.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.XY:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_POINTS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        element.points = self.reader.read_coords(self.token.len)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not self.next_token() or self.token.ident != Records.ENDEL:
 | 
				
			||||||
 | 
					            raise ParserError(errors.EXPECTED_ENDEL)
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        self.structure.elements.append(element)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def parse_sref(self):
 | 
				
			||||||
 | 
					        while self.next_token() and self.token.ident != Records.ENDEL:
 | 
				
			||||||
 | 
					            self.reader.skip(self.token.len)
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					def parse_file(file):
 | 
				
			||||||
 | 
					    r = Reader(file) 
 | 
				
			||||||
 | 
					    parser = Parser(r)
 | 
				
			||||||
 | 
					    parser.parse_lib()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return parser.Library
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										97
									
								
								gds/reader.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								gds/reader.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					from .record import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Reader(object):
 | 
				
			||||||
 | 
					    def __init__(self, file):
 | 
				
			||||||
 | 
					        self.stream = file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def skip(self, n):
 | 
				
			||||||
 | 
					        self.stream.read(n)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_int(self):
 | 
				
			||||||
 | 
					        temp = self.stream.read(4)
 | 
				
			||||||
 | 
					        if len(temp) != 4:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return int(temp[3]) | int(temp[2]) << 8 | int(temp[1]) << 16 | int(temp[0]) << 24
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_short(self):
 | 
				
			||||||
 | 
					        temp = self.stream.read(2)
 | 
				
			||||||
 | 
					        if len(temp) != 2:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return int(temp[1]) | int(temp[0]) << 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_double(self):
 | 
				
			||||||
 | 
					        temp = self.stream.read(8)
 | 
				
			||||||
 | 
					        if len(temp) != 8:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        result = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in temp:
 | 
				
			||||||
 | 
					            if int(i) != 0: 
 | 
				
			||||||
 | 
					                # read double
 | 
				
			||||||
 | 
					                result = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for j in range(1,8):
 | 
				
			||||||
 | 
					                    result += float(temp[8-j])/(2**(j*8))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                exp = int(temp[0]) & 0x7F
 | 
				
			||||||
 | 
					                exp -= 64
 | 
				
			||||||
 | 
					                result *= 16**exp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if int(temp[0]) & 0x80:
 | 
				
			||||||
 | 
					                    result += -1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return result
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        # double is Zero
 | 
				
			||||||
 | 
					        return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_ascii(self, len):
 | 
				
			||||||
 | 
					        return self.stream.read(len).decode("ASCII").strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_record(self):
 | 
				
			||||||
 | 
					        result = Record()
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            result.len = self.read_short() 
 | 
				
			||||||
 | 
					            result.ident = Records(self.read_short())
 | 
				
			||||||
 | 
					        except ValueError:
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result.len -= 4 # remove record header len 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_date(self):
 | 
				
			||||||
 | 
					        # date
 | 
				
			||||||
 | 
					        year  = self.read_short()
 | 
				
			||||||
 | 
					        month = self.read_short()
 | 
				
			||||||
 | 
					        day   = self.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # time
 | 
				
			||||||
 | 
					        hour   = self.read_short()
 | 
				
			||||||
 | 
					        minute = self.read_short()
 | 
				
			||||||
 | 
					        second = self.read_short()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return datetime(year=year, month=month, day=day, hour=hour, minute=minute, second=second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def read_coord(self):
 | 
				
			||||||
 | 
					        X = self.read_int()
 | 
				
			||||||
 | 
					        Y = self.read_int()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (X,Y)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    def read_coords(self, len):
 | 
				
			||||||
 | 
					        len /= 8
 | 
				
			||||||
 | 
					        result = []
 | 
				
			||||||
 | 
					        while len > 0:
 | 
				
			||||||
 | 
					            point = self.read_coord()
 | 
				
			||||||
 | 
					            if not point:
 | 
				
			||||||
 | 
					                return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            result.append(point)
 | 
				
			||||||
 | 
					            len -= 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
							
								
								
									
										52
									
								
								gds/record.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								gds/record.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					from enum import Enum
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Records(Enum):
 | 
				
			||||||
 | 
					    UNKNOWN      = 0x0000
 | 
				
			||||||
 | 
					    HEADER       = 0x0002
 | 
				
			||||||
 | 
					    BGNLIB       = 0x0102
 | 
				
			||||||
 | 
					    LIBNAME      = 0x0206
 | 
				
			||||||
 | 
					    REFLIBS      = 0x1F06
 | 
				
			||||||
 | 
					    FONTS        = 0x2006
 | 
				
			||||||
 | 
					    ATTRTABLE    = 0x2306
 | 
				
			||||||
 | 
					    GENERATIONS  = 0x2202
 | 
				
			||||||
 | 
					    FORMAT       = 0x3602
 | 
				
			||||||
 | 
					    MASK         = 0x3706
 | 
				
			||||||
 | 
					    ENDMASKS     = 0x3800
 | 
				
			||||||
 | 
					    UNITS        = 0x0305
 | 
				
			||||||
 | 
					    ENDLIB       = 0x0400
 | 
				
			||||||
 | 
					    BGNSTR       = 0x0502
 | 
				
			||||||
 | 
					    STRNAME      = 0x0606
 | 
				
			||||||
 | 
					    ENDEL        = 0x1100
 | 
				
			||||||
 | 
					    ENDSTR       = 0x0700
 | 
				
			||||||
 | 
					    BOUNDARY     = 0x0800
 | 
				
			||||||
 | 
					    PATH         = 0x0900
 | 
				
			||||||
 | 
					    SREF         = 0x0A00
 | 
				
			||||||
 | 
					    AREF         = 0x0B00
 | 
				
			||||||
 | 
					    TEXT         = 0x0C00
 | 
				
			||||||
 | 
					    NODE         = 0x1500
 | 
				
			||||||
 | 
					    BOX          = 0x2D00
 | 
				
			||||||
 | 
					    ELFLAGS      = 0x2601
 | 
				
			||||||
 | 
					    PLEX         = 0x2F03
 | 
				
			||||||
 | 
					    LAYER        = 0x0D02
 | 
				
			||||||
 | 
					    DATATYPE     = 0x0E02
 | 
				
			||||||
 | 
					    XY	         = 0x1003
 | 
				
			||||||
 | 
					    PATHTYPE     = 0x2102
 | 
				
			||||||
 | 
					    WIDTH        = 0x0F03
 | 
				
			||||||
 | 
					    BGNEXTN      = 0x3003
 | 
				
			||||||
 | 
					    ENDEXTN      = 0x3103
 | 
				
			||||||
 | 
					    SNAME        = 0x1206
 | 
				
			||||||
 | 
					    STRANS       = 0x1A01
 | 
				
			||||||
 | 
					    MAG          = 0x1B05
 | 
				
			||||||
 | 
					    ANGLE        = 0x1C05
 | 
				
			||||||
 | 
					    COLROW       = 0x1302
 | 
				
			||||||
 | 
					    TEXTTYPE     = 0x1602
 | 
				
			||||||
 | 
					    PRESENTATION = 0x1701
 | 
				
			||||||
 | 
					    NODETYPE     = 0x2A02
 | 
				
			||||||
 | 
					    BOXTYPE      = 0x2E02
 | 
				
			||||||
 | 
					    STRING       = 0x1906
 | 
				
			||||||
 | 
					    PROPATTR     = 0x2B02
 | 
				
			||||||
 | 
					    PROPVALUE    = 0x2C06
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Record(object):
 | 
				
			||||||
 | 
					    ident = Records.UNKNOWN
 | 
				
			||||||
 | 
					    len   = 0
 | 
				
			||||||
							
								
								
									
										10
									
								
								gds/structure.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								gds/structure.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					from datetime import datetime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Structure(object):
 | 
				
			||||||
 | 
					    # metainfo
 | 
				
			||||||
 | 
					    creation_date = datetime.now()
 | 
				
			||||||
 | 
					    last_mod      = datetime.now()
 | 
				
			||||||
 | 
					    name          = "NONAME"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # contains all the elements
 | 
				
			||||||
 | 
					    elements = []
 | 
				
			||||||
							
								
								
									
										20
									
								
								test.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								test.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					import gds
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for arg in sys.argv[1:]:
 | 
				
			||||||
 | 
					    f = open(arg, "rb")
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        lib = gds.parse_file(f)
 | 
				
			||||||
 | 
					        print("file version: {}".format(lib.version))
 | 
				
			||||||
 | 
					        print("last access: {}".format(lib.last_access.isoformat()))
 | 
				
			||||||
 | 
					        print("last modification: {}".format(lib.last_mod.isoformat()))
 | 
				
			||||||
 | 
					        print("m/unit : {}".format(lib.meters_per_unit))
 | 
				
			||||||
 | 
					        print("unit/dbunit : {}".format(lib.units_per_dbunit))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print("library name : {}".format(lib.name))
 | 
				
			||||||
 | 
					        print("contains a total of {} structure(s)".format(len(lib.structures)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    except gds.ParserError as e:
 | 
				
			||||||
 | 
					        print("parser error: {}".format(e))
 | 
				
			||||||
 | 
					    finally:
 | 
				
			||||||
 | 
					        f.close()
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user