spefparse 0.2.0

SPEF parasitics parser
Documentation
// implementing ieee 1481-2009 SPEF.
// hchar, special_chars refer to the ieee documentation.

WHITESPACE = _{ " " | "\t" | NEWLINE | ("//" ~ (!NEWLINE ~ ANY)* ~ (NEWLINE | &EOI)) }
// COMMENT_STR = @{"//" ~ (!NEWLINE ~ ANY)* ~ (NEWLINE | &EOI)}
// COMMENT = _{ COMMENT_STR }

main = {
    SOI ~
    header ~  // pushes DIVIDER, DELIMITER, BUS_DELIMITER*2 onto the stack.
    name_map? ~ top_ports? ~ parasitic_net* ~
    DROP ~ DROP ~ DROP ~ DROP ~ &EOI
}

str = @{ "\"" ~ ((!"\"" ~ !"\\" ~ ANY) | ("\\" ~ ANY))* ~ "\"" }
float = @{ "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT*)? ~
    ("e" ~ ("+" | "-") ~ ASCII_DIGIT+)?}
int = @{ ASCII_DIGIT+ }
par_value = ${ float ~ (":" ~ float ~ ":" ~ float)? }

hchar = { "." | "/" | ":" | "|" }
prefix_bus_delim_char = { "[" | "{" | "(" | "<" }  //  ":" | "."  omitted.
suffix_bus_delim_char = { ">" | ")" | "}" | "]" }
special_char = {
    "!" | "#" | "$"  | "%" | "&" | "'" | "(" | ")" | "*" | "+"
  | "," | "-" | "."  | "/" | ":" | ";" | "<" | "=" | ">" | "?"
  | "@" | "[" | "\\" | "]" | "^" | "`" | "{" | "|" | "}" | "~" }

header = {
    "*SPEF" ~ str ~
    "*DESIGN" ~ str ~
    "*DATE" ~ str ~
    "*VENDOR" ~ str ~
    "*PROGRAM" ~ str ~
    "*VERSION" ~ str ~
    "*DESIGN_FLOW" ~ header_design_flow ~
    "*DIVIDER" ~ PUSH(hchar) ~
    "*DELIMITER" ~ PUSH(hchar) ~
    "*BUS_DELIMITER" ~ PUSH(prefix_bus_delim_char) ~ PUSH(suffix_bus_delim_char) ~
    "*T_UNIT" ~ float ~ unit_type ~
    "*C_UNIT" ~ float ~ unit_type ~
    "*R_UNIT" ~ float ~ unit_type ~
    "*L_UNIT" ~ float ~ unit_type
}

header_design_flow = { str+ }

hier_delim = _{ &hchar ~ PEEK[0..1] }
pin_delim = _{ &hchar ~ PEEK[1..2] }
prefix_bus_delim = _{ &prefix_bus_delim_char ~ PEEK[2..3] }
suffix_bus_delim = _{ &suffix_bus_delim_char ~ PEEK[3..4] }

ident = @{ (ASCII_ALPHANUMERIC | "_" | ("\\" ~ ANY))+ }
bit_id = _{ prefix_bus_delim ~ int ~ suffix_bus_delim }  // flattened to int
ident_path = {
    hier_delim? ~ ident ~ (hier_delim ~ ident)* ~
    bit_id?
}  // contains hierarchy and bit. e.g., [/]top/hier1/u3[0]. NO macro pin `:a`.

unit_type = {
    ^"NS" | ^"PS"
  | ^"PF" | ^"FF"
  | ^"OHM" | ^"KOHM"
  | ^"HENRY" | ^"MH" | ^"UH"
}

name_map = { "*NAME_MAP" ~ name_map_entry* }
name_map_entry = {
    ("*" ~ int) ~ ident_path
}
// with bit_id: net or port names. w/o bit_id: can be pin prefix.
// `physical_name` omitted.

ident_path_m = ${ ("*" ~ int) | ident_path }

direction = { "O" | "I" | "B" }

top_ports = { "*PORTS" ~ top_ports_entry* }
top_ports_entry = { ident_path_m ~ direction ~ conn_attr }

conn_attr = { (ca_coords | ca_cap_load | ca_slew | ca_driving_cell)* }
ca_coords = ${ "*C" ~ WHITESPACE+ ~ float ~ WHITESPACE+ ~ float }
ca_cap_load = ${ "*L" ~ WHITESPACE+ ~ par_value }
ca_slew = ${ "*S" ~ WHITESPACE+ ~ par_value ~ WHITESPACE+ ~ par_value }
ca_driving_cell = ${ "*D" ~ WHITESPACE+ ~ ident }

parasitic_net = {
    "*D_NET" ~ ident_path_m ~ float ~
    "*CONN" ~ net_conn* ~
    "*CAP" ~ net_cap* ~
    "*RES" ~ net_res* ~
    "*END"
}

net_conn = {
    (net_conn_port | net_conn_intr) ~ direction ~ conn_attr
}
net_conn_port = { "*P" ~ ident_path_m }
net_conn_intr = { "*I" ~ ident_path_m ~ pin_delim ~ ident ~ bit_id? }
net_name_ref = { ident_path_m ~ (pin_delim ~ ident ~ bit_id?)? }

net_cap = { int ~ net_name_ref ~ (par_value | net_name_ref ~ par_value) }
// we must try matching a value first, as some values can be parsed
// as idents as well... e.g., 20-aes256.spef:L393759:
// ```
// 2 *83544:DIODE 0
// 3 *375415:A 5.75436e-05
// ```
// sensitivity omitted

net_res = { int ~ net_name_ref ~ net_name_ref ~ par_value }
// sensitivity omitted