def file parser
This commit is contained in:
1 parent
afb0a64953
commit
153442a10a
1 file changed
+285
@@ -0,0 +1,285 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from lark import Lark, Transformer, Tree
|
||||
|
||||
from kyupy import readtext
|
||||
|
||||
|
||||
class DefNet:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.pins = []
|
||||
|
||||
@property
|
||||
def wires(self):
|
||||
ww = defaultdict(list)
|
||||
[ww[dw.layer].append((int(dw.width), dw.wire_points)) for dw in self.routed if len(dw.wire_points) > 0]
|
||||
return ww
|
||||
|
||||
@property
|
||||
def vias(self):
|
||||
vv = defaultdict(list)
|
||||
[vv[vtype].extend(locs) for dw in self.routed for vtype, locs in dw.vias.items()]
|
||||
return vv
|
||||
|
||||
|
||||
class DefWire:
|
||||
def __init__(self):
|
||||
self.layer = None
|
||||
self.width = None
|
||||
self.points = []
|
||||
|
||||
@property
|
||||
def wire_points(self):
|
||||
start = [self.points[0]]
|
||||
rest = [p for p in self.points[1:] if not isinstance(p[0], str)] # skip over vias
|
||||
return start + rest if len(rest) > 0 else []
|
||||
|
||||
@property
|
||||
def vias(self):
|
||||
vv = defaultdict(list)
|
||||
loc = self.points[0]
|
||||
for p in self.points[1:]:
|
||||
if not isinstance(p[0], str): # new location
|
||||
loc = (loc[0] if p[0] is None else p[0], loc[1] if p[1] is None else p[1]) # if None, keep previous value
|
||||
continue
|
||||
vtype, param = p
|
||||
if isinstance(param, tuple): # expand "DO x BY y STEP xs ys"
|
||||
x_cnt, y_cnt, x_sp, y_sp = param
|
||||
[vv[vtype].append((loc[0] + x*x_sp, loc[1] + y*y_sp, 'N')) for x in range(x_cnt) for y in range(y_cnt)]
|
||||
else:
|
||||
vv[vtype].append((loc[0], loc[1], param or 'N'))
|
||||
return vv
|
||||
|
||||
def __repr__(self):
|
||||
return f'<DefWire {self.layer} {self.width} {self.points}>'
|
||||
|
||||
|
||||
class DefVia:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.rowcol = [1, 1]
|
||||
self.cutspacing = [0, 0]
|
||||
|
||||
|
||||
class DefPin:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.points = []
|
||||
|
||||
|
||||
class DefFile:
|
||||
def __init__(self):
|
||||
self.rows = []
|
||||
self.tracks = []
|
||||
self.units = []
|
||||
self.vias = {}
|
||||
self.components = {}
|
||||
self.pins = {}
|
||||
self.specialnets = {}
|
||||
self.nets = {}
|
||||
|
||||
|
||||
class DefTransformer(Transformer):
|
||||
def __init__(self): self.def_file = DefFile()
|
||||
def start(self, args): return self.def_file
|
||||
def design(self, args): self.def_file.design = args[0].value
|
||||
def point(self, args): return tuple(int(arg.value) if arg != '*' else None for arg in args)
|
||||
def do_step(self, args): return tuple(map(int, args))
|
||||
def spnet_wires(self, args): return args[0].lower(), args[1:]
|
||||
def net_wires(self, args): return args[0].lower(), args[1:]
|
||||
def sppoints(self, args): return args
|
||||
def points(self, args): return args
|
||||
def net_pin(self, args): return '__pin__', (args[0].value, args[1].value)
|
||||
def net_opt(self, args): return args[0].lower(), args[1].value
|
||||
|
||||
def file_stmt(self, args):
|
||||
value = args[1].value
|
||||
value = value[1:-1] if value[0] == '"' else value
|
||||
setattr(self.def_file, args[0].lower(), value)
|
||||
|
||||
def design_stmt(self, args):
|
||||
stmt = args[0].lower()
|
||||
if stmt == 'units': self.def_file.units.append((args[1].value, args[2].value, int(args[3])))
|
||||
elif stmt == 'diearea': self.def_file.diearea = args[1:]
|
||||
elif stmt == 'row':
|
||||
self.def_file.rows.append((args[1].value, # rowName
|
||||
args[2].value, # siteName
|
||||
(int(args[3]), int(args[4])), # origin x/y
|
||||
args[5].value, # orientation
|
||||
max(args[6][0], args[6][1]), # number of sites
|
||||
max(args[6][2], args[6][3]) # site width
|
||||
))
|
||||
elif stmt == 'tracks':
|
||||
self.def_file.tracks.append((args[1].value, # orientation
|
||||
int(args[2]), # start
|
||||
int(args[3]), # number of tracks
|
||||
int(args[4]), # spacing
|
||||
args[5].value # layer
|
||||
))
|
||||
|
||||
def vias_stmt(self, args):
|
||||
via = DefVia(args[0].value)
|
||||
[setattr(via, opt, val) for opt, val in args[1:]]
|
||||
self.def_file.vias[via.name] = via
|
||||
|
||||
def vias_opt(self, args):
|
||||
opt = args[0].lower()
|
||||
if opt in ['viarule', 'pattern']: val = args[1].value
|
||||
elif opt in ['layers']: val = [arg.value for arg in args[1:]]
|
||||
else: val = [int(arg) for arg in args[1:]]
|
||||
return opt, val
|
||||
|
||||
def comp_stmt(self, args):
|
||||
name = args[0].value
|
||||
kind = args[1].value
|
||||
point = args[2]
|
||||
orientation = args[3].value
|
||||
self.def_file.components[name] = (kind, point, orientation)
|
||||
|
||||
def pins_stmt(self, args):
|
||||
pin = DefPin(args[0].value)
|
||||
[pin.points.append(val) if opt == 'placed' else setattr(pin, opt, val) for opt, val in args[1:]]
|
||||
self.def_file.pins[pin.name] = pin
|
||||
|
||||
def pins_opt(self, args):
|
||||
opt = args[0].lower()
|
||||
if opt in ['net', 'direction', 'use']: val = args[1].value
|
||||
elif opt in ['layer']: val = [args[1].value] + args[2:]
|
||||
elif opt in ['placed']: val = (args[1][0], args[1][1], args[2].value)
|
||||
else: val = []
|
||||
return opt, val
|
||||
|
||||
def spnets_stmt(self, args):
|
||||
dnet = DefNet(args[0].value)
|
||||
for arg in args[1:]:
|
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1])
|
||||
else: setattr(dnet, arg[0], arg[1])
|
||||
self.def_file.specialnets[dnet.name] = dnet
|
||||
|
||||
def nets_stmt(self, args):
|
||||
dnet = DefNet(args[0].value)
|
||||
for arg in args[1:]:
|
||||
if arg[0] == '__pin__': dnet.pins.append(arg[1])
|
||||
else: setattr(dnet, arg[0], arg[1])
|
||||
self.def_file.nets[dnet.name] = dnet
|
||||
|
||||
def spwire(self, args):
|
||||
wire = DefWire()
|
||||
wire.layer = args[0].value
|
||||
wire.width = args[1].value
|
||||
wire.points = args[-1]
|
||||
return wire
|
||||
|
||||
def wire(self, args):
|
||||
wire = DefWire()
|
||||
wire.layer = args[0].value
|
||||
wire.points = args[-1]
|
||||
return wire
|
||||
|
||||
def sppoints_via(self, args):
|
||||
if len(args) == 1: return args[0].value, None
|
||||
else: return args[0].value, args[1]
|
||||
|
||||
def points_via(self, args):
|
||||
if len(args) == 1: return args[0].value, 'N'
|
||||
else: return args[0].value, args[1].value.strip()
|
||||
|
||||
|
||||
GRAMMAR = r"""
|
||||
start: /#[^\n]*/? file_stmt*
|
||||
|
||||
?file_stmt: /VERSION/ ID ";"
|
||||
| /DIVIDERCHAR/ STRING ";"
|
||||
| /BUSBITCHARS/ STRING ";"
|
||||
| design
|
||||
|
||||
design: "DESIGN" ID ";" design_stmt* "END" "DESIGN"
|
||||
|
||||
?design_stmt: /UNITS/ ID ID NUMBER ";"
|
||||
| /DIEAREA/ point+ ";"
|
||||
| /ROW/ ID ID NUMBER NUMBER ID do_step ";"
|
||||
| /TRACKS/ /[XY]/ NUMBER "DO" NUMBER "STEP" NUMBER "LAYER" ID ";"
|
||||
| propdef | vias | nondef | comp | pins | pinprop | spnets | nets
|
||||
|
||||
propdef: "PROPERTYDEFINITIONS" propdef_stmt* "END" "PROPERTYDEFINITIONS"
|
||||
propdef_stmt: /COMPONENTPIN/ ID ID ";"
|
||||
|
||||
vias: "VIAS" NUMBER ";" vias_stmt* "END" "VIAS"
|
||||
vias_stmt: "-" ID vias_opt* ";"
|
||||
vias_opt: "+" /VIARULE/ ID
|
||||
| "+" /CUTSIZE/ NUMBER NUMBER
|
||||
| "+" /LAYERS/ ID ID ID
|
||||
| "+" /CUTSPACING/ NUMBER NUMBER
|
||||
| "+" /ENCLOSURE/ NUMBER NUMBER NUMBER NUMBER
|
||||
| "+" /ROWCOL/ NUMBER NUMBER
|
||||
| "+" /PATTERN/ ID
|
||||
|
||||
nondef: "NONDEFAULTRULES" NUMBER ";" nondef_stmt+ "END" "NONDEFAULTRULES"
|
||||
nondef_stmt: "-" ID ( "+" /HARDSPACING/
|
||||
| "+" /LAYER/ ID "WIDTH" NUMBER "SPACING" NUMBER
|
||||
| "+" /VIA/ ID )* ";"
|
||||
|
||||
comp: "COMPONENTS" NUMBER ";" comp_stmt* "END" "COMPONENTS"
|
||||
comp_stmt: "-" ID ID "+" "PLACED" point ID ";"
|
||||
|
||||
pins: "PINS" NUMBER ";" pins_stmt* "END" "PINS"
|
||||
pins_stmt: "-" ID pins_opt* ";"
|
||||
pins_opt: "+" /NET/ ID
|
||||
| "+" /SPECIAL/
|
||||
| "+" /DIRECTION/ ID
|
||||
| "+" /USE/ ID
|
||||
| "+" /PORT/
|
||||
| "+" /LAYER/ ID point point
|
||||
| "+" /PLACED/ point ID
|
||||
|
||||
pinprop: "PINPROPERTIES" NUMBER ";" pinprop_stmt* "END" "PINPROPERTIES"
|
||||
pinprop_stmt: "-" "PIN" ID "+" "PROPERTY" ID STRING ";"
|
||||
|
||||
spnets: "SPECIALNETS" NUMBER ";" spnets_stmt* "END" "SPECIALNETS"
|
||||
spnets_stmt: "-" ID ( net_pin | net_opt | spnet_wires )* ";"
|
||||
|
||||
spnet_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ ) spwire ( "NEW" spwire )*
|
||||
|
||||
spwire: ID NUMBER spwire_opt* sppoints
|
||||
spwire_opt: "+" /SHAPE/ ID
|
||||
| "+" /STYLE/ ID
|
||||
|
||||
sppoints: point ( point | sppoints_via )+
|
||||
sppoints_via: ID do_step?
|
||||
|
||||
nets: "NETS" NUMBER ";" nets_stmt* "END" "NETS"
|
||||
nets_stmt: "-" ID ( net_pin | net_opt | net_wires )* ";"
|
||||
|
||||
net_pin: "(" ID ID ")"
|
||||
net_opt: "+" /USE/ ID
|
||||
| "+" /NONDEFAULTRULE/ ID
|
||||
net_wires: "+" ( /COVER/ | /FIXED/ | /ROUTED/ | /NOSHIELD/ ) wire ( "NEW" wire )*
|
||||
|
||||
wire: ID wire_opt points
|
||||
wire_opt: ( "TAPER" | "TAPERRULE" ID )? ("STYLE" ID)?
|
||||
|
||||
points: point ( point | points_via )+
|
||||
points_via: ID ORIENTATION?
|
||||
|
||||
point: "(" (NUMBER|/\*/) (NUMBER|/\*/) NUMBER? ")"
|
||||
|
||||
do_step: "DO" NUMBER "BY" NUMBER "STEP" (NUMBER|SIGNED_NUMBER) (NUMBER|SIGNED_NUMBER)
|
||||
|
||||
ORIENTATION.2: /F?[NWES]/ WS
|
||||
ID: /[^ \t\f\r\n+][^ \t\f\r\n]*/
|
||||
STRING : "\"" /.*?/s /(?<!\\)(\\\\)*?/ "\""
|
||||
WS: /[ \t\f\r\n]/
|
||||
|
||||
%import common.NUMBER
|
||||
%import common.SIGNED_NUMBER
|
||||
%ignore WS (/#[^\n]*/)?
|
||||
"""
|
||||
|
||||
|
||||
def parse(text):
|
||||
return Lark(GRAMMAR, parser="lalr", transformer=DefTransformer()).parse(text)
|
||||
|
||||
|
||||
def load(file):
|
||||
return parse(readtext(file))
|
||||
Reference in new issue
Block a user