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