Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Grammar railroad diagram #2

Open
mingodad opened this issue Apr 13, 2022 · 1 comment
Open

Grammar railroad diagram #2

mingodad opened this issue Apr 13, 2022 · 1 comment

Comments

@mingodad
Copy link

Using this online tool https://www.bottlecaps.de/convert/ to convert the cleaned up Parser.y in an EBNF understood by https://www.bottlecaps.de/rr/ui and adding the tokens manually we can have a nice railroad diagram (https://en.wikipedia.org/wiki/Syntax_diagram).

Copy and paste the EBNF shown bellow at https://www.bottlecaps.de/rr/ui on the tab Edit Grammar then click on the tab View Diagram.

/* converted on Wed Apr 13, 2022, 13:18 (UTC+02) by bison-to-w3c v0.57 which is Copyright (c) 2011-2022 by Gunther Rademacher <[email protected]> */

defs     ::= ( procDef | includeProc | importExtern | structDef | bdDef | typeDef | constDef | includeDef | areaDef | areaImportDef )*
areaDef  ::= type const? allocRef
areaImportDef
         ::= import header type const? allocIdent
header   ::= ident ( '/' ident )* '.' ident
includeDef
         ::= include ident
constDef ::= type? ident '=' exp ';'
procDef  ::= type ident '(' args ')' '{' stmts '}' prePostBlk
includeProc
         ::= import '(' header ',' ident ')' type ident '(' args ')'
importExtern
         ::= extern header type ident
tyArg    ::= type ident
args     ::= tyArg? ( ',' tyArg? )*
prePostBlk
         ::= ( '{' ( prePost ';' )+ '}' )?
prePost  ::= ( pre | post ) '(' exp ')'
ptrIdent ::= '*' ident
arrIdent ::= ident '[' ']'
structIdent
         ::= ident '{' '}'
allocIdent
         ::= ptrIdent
           | arrIdent
           | structIdent
allocRef ::= alloc ( ptrIdent ( '=' exp )? | arrIdent ( '=' '{' exps '}' )? | structIdent ( '=' structInit )? ) ';'
simpleStmt
         ::= ( ( assert | assume | store exp as | assign type? ident '=' ) exp | refCopy ident ident | ivoryMacro | break | return exp? | ident ( expArgs | '<-' ivoryMacro ) ) ';'
           | allocRef
ivoryMacro
         ::= iMacro ident expArgs?
blkStmt  ::= ( ( mapArr | ( upTo | downFrom ) exp | ( upFromTo | downFromTo ) '(' exp ',' exp ')' ) ident | forever | if exp '{' stmts '}' else ) '{' stmts '}'
stmts    ::= ( simpleStmt | blkStmt )*
expArgs  ::= '(' exps ')'
exps     ::= exp? ( ',' exp? )*
structInit
         ::= ivoryMacro
           | '{' fieldAssign? ( ',' fieldAssign? )* '}'
fieldAssign
         ::= ident '=' exp
exp      ::= integer
           | str
           | floatlit
           | ident expArgs?
           | return
           | '(' exp ')'
           | ( '*' | '!' | '-' | '~' ) exp
           | exp ( ( '@' | '.' | '->' | '||' | '&&' | '|' | '^' | '&' | '<<' | '>>' | '==' | '!=' | '<' | '<=' | '>' | '>=' | '+' | '-' | '*' | '/' | '%' | '?' exp ':' ) exp | '[' exp ']' )
           | '&' ident
           | libFuncExp
           | ivoryMacro
libFuncExp
         ::= ( abs | signum | expOp | sqrt | log | pow | sin | cos | tan | asin | acos | atan | atan2 | sinh | cosh | tanh | asinh | acosh | atanh | isnan | isinf | round | ceil | floor | const | div | castWith | safeCast | bitCast | twosCompCast | twosCompRep | toIx | fromIx | ixSize | arrayLen | sizeOf | nullPtr | refToPtr | toCArray ) expArgs
typeDef  ::= ty tyident '=' type ';'
type     ::= baseType
           | cType
           | '(' type ')'
baseType ::= simpleCType
           | tyident
simpleCType
         ::= bool
           | char
           | float
           | double
           | string
           | void
           | int8_t
           | int16_t
           | int32_t
           | int64_t
           | uint8_t
           | uint16_t
           | uint32_t
           | uint64_t
           | ix_t integer
szType   ::= iMacro tyidentifier
           | integer
cType    ::= ( const? scopeC '*' | '&' ) type
           | baseType ( '[' szType ']' )+
           | struct structName
scopeC   ::= ( S | G | ident )?
typeHS   ::= simpleHSType
           | hsType
           | tyident
           | '(' typeHS ')'
simpleHSType
         ::= IBool
           | IChar
           | IFloat
           | IDouble
           | IString
           | '(' ')'
           | Sint8
           | Sint16
           | Sint32
           | Sint64
           | Uint8
           | Uint16
           | Uint32
           | Uint64
           | Ix integer
hsType   ::= ( ( Ref | ConstRef ) scopeHS | Array szType | Stored ) typeHS
           | Struct structName
scopeHS  ::= Stack tyident
           | Global
bitType  ::= Bit
           | Bits integer
           | BitArray integer bitType
           | '(' bitType ')'
           | tyident
structDef
         ::= struct structName '{' field ( ';' field? )* '}'
           | abstract struct structName str
           | string struct structName integer
structName
         ::= tyident
           | ident
field    ::= ident '::' typeHS
           | type ident
bdDef    ::= bitdata tyident '::' bitType '=' bdConstr ( '|' bdConstr )*
bdConstr ::= ident bdRecord bdLayout
bdRecord ::= ( '{' bdField ( ',' bdField )* '}' )?
bdField  ::= ( ident | '_' ) '::' bitType
bdLayout ::= ( as bdItem ( '#' bdItem )* )?
bdItem   ::= ident
           | integer
           | bitLiteral
bitLiteral
         ::= bitlit
ident    ::= ( tyidentifier '.' )? identifier
tyident  ::= tyidentifier ( '.' tyidentifier )?

//Tokens
//\(\w\S+\)\s+{ Located $$ (\S+ \("[^"]+"\).+

//-- Statements
if ::= "if"
else ::= "else"
assert ::= "assert"
assume ::= "assume"
pre ::= "pre"
post ::= "post"
assign ::= "let"
return ::= "return"
alloc ::= "alloc"
store ::= "store"
refCopy ::= "memcpy"
mapArr ::= "map"
upTo ::= "upTo"
upFromTo ::= "upFromTo"
downFrom ::= "downFrom"
downFromTo ::= "downFromTo"
forever ::= "forever"
break ::= "break"

//-- Start of Ivory macros
iMacro ::= "$"

//-- Expressions
abs ::= "abs"
signum ::= "signum"
expOp ::= "exp"
sqrt ::= "sqrt"
log ::= "log"
pow ::= "pow"
div ::= "div"

sin ::= "sin"
cos ::= "cos"
tan ::= "tan"

asin ::= "asin"
acos ::= "acos"
atan ::= "atan"
atan2 ::= "atan2"

sinh ::= "sinh"
cosh ::= "cosh"
tanh ::= "tanh"

asinh ::= "asinh"
acosh ::= "acosh"
atanh ::= "atanh"

isnan ::= "isnan"
isinf ::= "isinf"
round ::= "round"
ceil ::= "ceil"
floor ::= "floor"
const ::= "const"

//-- Casting
safeCast ::= "safeCast"
bitCast ::= "bitCast"
castWith ::= "castWith"
twosCompCast ::= "twosCompCast"
twosCompRep ::= "twosCompRep"

//-- Other internals

fromIx ::= "fromIx"
ixSize ::= "ixSize"
toIx ::= "toIx"
toCArray ::= "toCArray"
arrayLen ::= "arrayLen"

sizeOf ::= "sizeOf"
nullPtr ::= "nullPtr"
refToPtr ::= "refToPtr"
/*
//-- Type
'::'      { Located $$ (TokSym "::") }

'?'       { Located $$ (TokSym "?") }
':'       { Located $$ (TokSym ":") }

//-- Struct field dereference
'.'       { Located $$ (TokSym ".") }
'->'      { Located $$ (TokSym "->") }

'=='      { Located $$ (TokSym "==") }
'!='      { Located $$ (TokSym "!=") }

//-- Used for deref and mult
'*'       { Located $$ (TokSym "*") }

'/'       { Located $$ (TokSym "/") }

'+'       { Located $$ (TokSym "+") }
'-'       { Located $$ (TokSym "-") }
'%'       { Located $$ (TokSym "%") }

'='       { Located $$ (TokSym "=") }

'<'       { Located $$ (TokSym "<") }
'<='      { Located $$ (TokSym "<=") }
'>='      { Located $$ (TokSym ">=") }
'>'       { Located $$ (TokSym ">") }

'|'       { Located $$ (TokSym "|") }
'&'       { Located $$ (TokSym "&") }
'^'       { Located $$ (TokSym "^") }
'~'       { Located $$ (TokSym "~") }

'!'       { Located $$ (TokSym "!") }
'&&'      { Located $$ (TokSym "&&") }
'||'      { Located $$ (TokSym "||") }
'<<'      { Located $$ (TokSym "<<") }
'>>'      { Located $$ (TokSym ">>") }

//-- Other symbols
'('       { Located $$ (TokBrack "(") }
')'       { Located $$ (TokBrack ")") }
'{'       { Located $$ (TokBrack "{") }
'}'       { Located $$ (TokBrack "}") }
'['       { Located $$ (TokBrack "[") }
']'       { Located $$ (TokBrack "]") }

';'       { Located $$ (TokSep ";") }
','       { Located $$ (TokSep ",") }

'@'       { Located $$ (TokSym "@") }
'<-'      { Located $$ (TokSym "<-") }
*/
//-- Types
bool ::= "bool"
char ::= "char"
float ::= "float"
double ::= "double"
void ::= "void"

int8_t ::= "int8_t"
int16_t ::= "int16_t"
int32_t ::= "int32_t"
int64_t ::= "int64_t"

uint8_t ::= "uint8_t"
uint16_t ::= "uint16_t"
uint32_t ::= "uint32_t"
uint64_t ::= "uint64_t"

S ::= "S"
G ::= "G"

IBool ::= "IBool"
IChar ::= "IChar"
IFloat ::= "IFloat"
IDouble ::= "IDouble"
IString ::= "IString"

Sint8 ::= "Sint8"
Sint16 ::= "Sint16"
Sint32 ::= "Sint32"
Sint64 ::= "Sint64"

Uint8 ::= "Uint8"
Uint16 ::= "Uint16"
Uint32 ::= "Uint32"
Uint64 ::= "Uint64"

Ix ::= "Ix"
ix_t ::= "ix_t"

Ref ::= "Ref"
ConstRef ::= "ConstRef"
Array ::= "Array"
Struct ::= "Struct"
Stored ::= "Stored"

Stack ::= "Stack"
Global ::= "Global"

//-- Keywords
struct ::= "struct"
abstract ::= "abstract"
string ::= "string"

ty ::= "type"
include ::= "include"
import ::= "import"
extern ::= "extern"

//-- Bit data
bitdata ::= "bitdata"
Bit ::= "Bit"
Bits ::= "Bits"
BitArray ::= "BitArray"
as ::= "as"
/*
'_'      { Located $$ (TokSym "_") }
'#'      { Located $$ (TokSym "#") }
*/

Cleaned up Parser.y:

/*
  -- Statements
  if         { Located $$ (TokReserved "if") }
  else       { Located $$ (TokReserved "else") }
  assert     { Located $$ (TokReserved "assert") }
  assume     { Located $$ (TokReserved "assume") }
  pre        { Located $$ (TokReserved "pre") }
  post       { Located $$ (TokReserved "post") }
  assign     { Located $$ (TokReserved "let") }
  return     { Located $$ (TokReserved "return") }
  alloc      { Located $$ (TokReserved "alloc") }
  store      { Located $$ (TokReserved "store") }
  refCopy    { Located $$ (TokReserved "memcpy") }
  mapArr     { Located $$ (TokReserved "map") }
  upTo       { Located $$ (TokReserved "upTo") }
  upFromTo   { Located $$ (TokReserved "upFromTo") }
  downFrom   { Located $$ (TokReserved "downFrom") }
  downFromTo { Located $$ (TokReserved "downFromTo") }
  forever    { Located $$ (TokReserved "forever") }
  break      { Located $$ (TokReserved "break") }

  -- Start of Ivory macros
  iMacro   { Located $$ (TokSym "$") }

  -- Expressions
  abs          { Located $$ (TokReserved "abs") }
  signum       { Located $$ (TokReserved "signum") }
  expOp        { Located $$ (TokReserved "exp") }
  sqrt         { Located $$ (TokReserved "sqrt") }
  log          { Located $$ (TokReserved "log") }
  pow          { Located $$ (TokReserved "pow") }
  div          { Located $$ (TokReserved "div") }

  sin          { Located $$ (TokReserved "sin") }
  cos          { Located $$ (TokReserved "cos") }
  tan          { Located $$ (TokReserved "tan") }

  asin         { Located $$ (TokReserved "asin") }
  acos         { Located $$ (TokReserved "acos") }
  atan         { Located $$ (TokReserved "atan") }
  atan2        { Located $$ (TokReserved "atan2") }

  sinh         { Located $$ (TokReserved "sinh") }
  cosh         { Located $$ (TokReserved "cosh") }
  tanh         { Located $$ (TokReserved "tanh") }

  asinh        { Located $$ (TokReserved "asinh") }
  acosh        { Located $$ (TokReserved "acosh") }
  atanh        { Located $$ (TokReserved "atanh") }

  isnan        { Located $$ (TokReserved "isnan") }
  isinf        { Located $$ (TokReserved "isinf") }
  round        { Located $$ (TokReserved "round") }
  ceil         { Located $$ (TokReserved "ceil") }
  floor        { Located $$ (TokReserved "floor") }
  const        { Located $$ (TokReserved "const") }

  -- Casting
  safeCast         { Located $$ (TokReserved "safeCast") }
  bitCast          { Located $$ (TokReserved "bitCast") }
  castWith         { Located $$ (TokReserved "castWith") }
  twosCompCast     { Located $$ (TokReserved "twosCompCast") }
  twosCompRep      { Located $$ (TokReserved "twosCompRep") }

  -- Other internals

  fromIx           { Located $$ (TokReserved "fromIx") }
  ixSize           { Located $$ (TokReserved "ixSize") }
  toIx             { Located $$ (TokReserved "toIx") }
  toCArray         { Located $$ (TokReserved "toCArray") }
  arrayLen         { Located $$ (TokReserved "arrayLen") }

  sizeOf           { Located $$ (TokReserved "sizeOf") }
  nullPtr          { Located $$ (TokReserved "nullPtr") }
  refToPtr         { Located $$ (TokReserved "refToPtr") }

  -- Type
  '::'      { Located $$ (TokSym "::") }

  '?'       { Located $$ (TokSym "?") }
  ':'       { Located $$ (TokSym ":") }

  -- Struct field dereference
  '.'       { Located $$ (TokSym ".") }
  '->'      { Located $$ (TokSym "->") }

  '=='      { Located $$ (TokSym "==") }
  '!='      { Located $$ (TokSym "!=") }

  -- Used for deref and mult
  '*'       { Located $$ (TokSym "*") }

  '/'       { Located $$ (TokSym "/") }

  '+'       { Located $$ (TokSym "+") }
  '-'       { Located $$ (TokSym "-") }
  '%'       { Located $$ (TokSym "%") }

  '='       { Located $$ (TokSym "=") }

  '<'       { Located $$ (TokSym "<") }
  '<='      { Located $$ (TokSym "<=") }
  '>='      { Located $$ (TokSym ">=") }
  '>'       { Located $$ (TokSym ">") }

  '|'       { Located $$ (TokSym "|") }
  '&'       { Located $$ (TokSym "&") }
  '^'       { Located $$ (TokSym "^") }
  '~'       { Located $$ (TokSym "~") }

  '!'       { Located $$ (TokSym "!") }
  '&&'      { Located $$ (TokSym "&&") }
  '||'      { Located $$ (TokSym "||") }
  '<<'      { Located $$ (TokSym "<<") }
  '>>'      { Located $$ (TokSym ">>") }

-- Other symbols
  '('       { Located $$ (TokBrack "(") }
  ')'       { Located $$ (TokBrack ")") }
  '{'       { Located $$ (TokBrack "{") }
  '}'       { Located $$ (TokBrack "}") }
  '['       { Located $$ (TokBrack "[") }
  ']'       { Located $$ (TokBrack "]") }

  ';'       { Located $$ (TokSep ";") }
  ','       { Located $$ (TokSep ",") }

  '@'       { Located $$ (TokSym "@") }
  '<-'      { Located $$ (TokSym "<-") }

  -- Types
  bool     { Located $$ (TokReserved "bool") }
  char     { Located $$ (TokReserved "char") }
  float    { Located $$ (TokReserved "float") }
  double   { Located $$ (TokReserved "double") }
  void     { Located $$ (TokReserved "void") }

  int8_t   { Located $$ (TokReserved "int8_t") }
  int16_t  { Located $$ (TokReserved "int16_t") }
  int32_t  { Located $$ (TokReserved "int32_t") }
  int64_t  { Located $$ (TokReserved "int64_t") }

  uint8_t  { Located $$ (TokReserved "uint8_t") }
  uint16_t { Located $$ (TokReserved "uint16_t") }
  uint32_t { Located $$ (TokReserved "uint32_t") }
  uint64_t { Located $$ (TokReserved "uint64_t") }

  S        { Located $$ (TokReserved "S") }
  G        { Located $$ (TokReserved "G") }

  IBool     { Located $$ (TokReserved "IBool") }
  IChar     { Located $$ (TokReserved "IChar") }
  IFloat    { Located $$ (TokReserved "IFloat") }
  IDouble   { Located $$ (TokReserved "IDouble") }
  IString   { Located $$ (TokReserved "IString") }

  Sint8     { Located $$ (TokReserved "Sint8") }
  Sint16    { Located $$ (TokReserved "Sint16") }
  Sint32    { Located $$ (TokReserved "Sint32") }
  Sint64    { Located $$ (TokReserved "Sint64") }

  Uint8    { Located $$ (TokReserved "Uint8") }
  Uint16   { Located $$ (TokReserved "Uint16") }
  Uint32   { Located $$ (TokReserved "Uint32") }
  Uint64   { Located $$ (TokReserved "Uint64") }

  Ix       { Located $$ (TokReserved "Ix") }
  ix_t     { Located $$ (TokReserved "ix_t") }

  Ref      { Located $$ (TokReserved "Ref") }
  ConstRef { Located $$ (TokReserved "ConstRef") }
  Array    { Located $$ (TokReserved "Array") }
  Struct   { Located $$ (TokReserved "Struct") }
  Stored   { Located $$ (TokReserved "Stored") }

  Stack    { Located $$ (TokReserved "Stack") }
  Global   { Located $$ (TokReserved "Global") }

  -- Keywords
  struct   { Located $$ (TokReserved "struct") }
  abstract { Located $$ (TokReserved "abstract") }
  string   { Located $$ (TokReserved "string") }

  ty       { Located $$ (TokReserved "type") }
  include  { Located $$ (TokReserved "include") }
  import   { Located $$ (TokReserved "import") }
  extern   { Located $$ (TokReserved "extern") }

  -- Bit data
  bitdata  { Located $$ (TokReserved "bitdata") }
  Bit      { Located $$ (TokReserved "Bit") }
  Bits     { Located $$ (TokReserved "Bits") }
  BitArray { Located $$ (TokReserved "BitArray") }
  as       { Located $$ (TokReserved "as") }
  '_'      { Located $$ (TokSym "_") }
  '#'      { Located $$ (TokSym "#") }
*/
//--------------------------------------------------------------------------------
//-- Precedence

%right '#'
%right '?' ':'
%left '||'
%left '&&'
%left '|' '&'
%left '^'
%nonassoc '==' '!='
%nonassoc '<' '<=' '>' '>='
%left '<<' '>>'
%left '+' '-'
%left '*' '/' '%'
%right '*' '~' '!' '-'
//-- '[' assumed to be followed by ']'
%left '.' '@' '->' '[' ']'
%right ADDR
//-- Tighter than normal binding
%right
  abs
  signum
  expOp
  sqrt
  log
  pow
  sin
  cos
  tan
  asin
  acos
  atan
  sinh
  cosh
  tanh
  asinh
  acosh
  atanh
  isnan
  isinf
  round
  ceil
  floor
  const
  div
%%

//----------------------------------------
//-- Top-level definitions

//defs :: { [GlobalSym] }
defs : defs procDef       { GlobalProc       $2 : $1 }
     | defs includeProc   { GlobalInclProc   $2 : $1 }
     | defs importExtern  { GlobalExtern     $2 : $1 }
     | defs structDef     { GlobalStruct     $2 : $1 }
     | defs bdDef         { GlobalBitData    $2 : $1 }
     | defs typeDef       { GlobalTypeDef    $2 : $1 }
     | defs constDef      { GlobalConstDef   $2 : $1 }
     | defs includeDef    { GlobalInclude    $2 : $1 }
     | defs areaDef       { GlobalArea       $2 : $1 }
     | defs areaImportDef { GlobalAreaImport $2 : $1 }
     | {- empty -}        { [] }

//----------------------------------------
//-- Include areas

//areaDef :: { AreaDef }
areaDef :
    type allocRef
      { AreaDef False $1 (unLoc $2) (getLoc $2)
      }
  | type const allocRef
      { AreaDef True $1 (unLoc $3) (getLoc $3)
      }

//areaImportDef :: { AreaImportDef }
areaImportDef :
    import header type allocIdent
      { AreaImportDef (unLoc $4) False $3 $2
          (getLoc $1 <> getLoc $4)
      }
  | import header type const allocIdent
      { AreaImportDef (unLoc $5) True $3 $2
          (getLoc $1 <> getLoc $5)
      }

//----------------------------------------
//-- header paths

//filepath :: { [String] }
filepath :
    filepath ident '/' { (unLoc $2 ++ "/") : $1 }
  | {- empty -}        { [] }

//header :: { String }
header :
  filepath ident '.' ident
    { concat (reverse $1) ++ unLoc $2 ++ ('.' : unLoc $4) }

//----------------------------------------
//-- Include other modules (Ivory's "depend")

//includeDef :: { IncludeDef }
includeDef : include ident { IncludeDef (unLoc $2) ($1 <> getLoc $2) }

//----------------------------------------
//-- Constant definitions

//constDef :: { ConstDef }
constDef :
         ident '=' exp ';' { ConstDef (unLoc $1) $3 Nothing   (getLoc $1 <> getLoc $3) }
  | type ident '=' exp ';' { ConstDef (unLoc $2) $4 (Just $1) (mconcat [ getLoc $1
                                                                       , getLoc $2
                                                                       , getLoc $4]) }

//----------------------------------------
//-- Procs

//-- Defined procedure
//procDef :: { ProcDef }
procDef :
  type ident '(' args ')' '{' stmts '}' prePostBlk
    { ProcDef $1 (unLoc $2) (reverse $4) (reverse $7) $9 (mconcat [ getLoc $1
                                                                  , getLoc $2
                                                                  , getLoc $7
                                                                  , getLoc $9
                                                                  ]) }

//-- Externally-defined procedure
//includeProc :: { IncludeProc }
includeProc :
  import '(' header ',' ident ')' type ident '(' args ')'
    { IncludeProc $7 (unLoc $8) (reverse $10) ($3, unLoc $5)
        (mconcat [ getLoc $1
                 , getLoc $5
                 , getLoc $8
                 ]) }

//-- Externally-defined symbols
//importExtern :: { Extern }
importExtern :
  extern header type ident
    { Extern (unLoc $4) $2 $3
             (mconcat [ getLoc $1, getLoc $4])
    }

//tyArg :: { (Type, Var) }
tyArg : type ident { ($1, unLoc $2) }

//-- Zero or more typed arguments, separated by arbitrary many ','s.
//args :: { [(Type, Var)] }
args :  args ',' tyArg         { $3 : $1 }
      | args ','               { $1 }
      | tyArg                  { [$1] }
      | {- empty -}            { [] }

//-- pre/post conditions
//prePostBlk :: { [PrePost] }
prePostBlk :
    '{' prePosts '}'       { reverse $2 }
  | {- empty -}            { [] }

//prePosts :: { [PrePost] }
prePosts :
    prePosts prePost ';' { $2 : $1 }
  | prePost ';'          { [$1] }

//prePost :: { PrePost }
prePost :
    pre  '(' exp ')' { PreCond  $3 }
  | post '(' exp ')' { PostCond $3 }


//----------------------------------------
//-- Allocations

//ptrIdent :: { Located RefVar }
ptrIdent : '*' ident { $2 }

//arrIdent :: { Located RefVar }
arrIdent : ident '[' ']' { $1 }

//structIdent :: { Located RefVar }
structIdent : ident '{' '}' { $1 }

//allocIdent :: { Located RefVar }
allocIdent :
    ptrIdent    { $1 }
  | arrIdent    { $1 }
  | structIdent {$1 }
//allocRef :: { Located AllocRef }
allocRef :
    alloc ptrIdent ';'               { atBin (AllocBase (unLoc $2) Nothing) $1 $3 }
  | alloc ptrIdent '=' exp ';'       { atList (AllocBase (unLoc $2) (Just $4))
                                              [$1, getLoc $2, getLoc $4]
                                     }
  | alloc arrIdent ';'               { atBin (AllocArr (unLoc $2) []) $1 $2 }
  | alloc arrIdent '='
      '{' exps '}' ';'               { atList (AllocArr (unLoc $2) (reverse $5))
                                              [ $1, getLoc $2, getLoc $5]
                                     }

  | alloc structIdent ';'            { atBin (AllocStruct (unLoc $2) Empty) $1 $2 }
  | alloc structIdent '='
      structInit ';'                 { atBin (AllocStruct (unLoc $2) $4) $1 $2 }

//----------------------------------------
//-- Statements

//simpleStmt :: { Stmt }
simpleStmt :
    assert exp  ';'                   { LocStmt (atBin (Assert $2) $1 $2) }
  | assume exp  ';'                   { LocStmt (atBin (Assume $2) $1 $2) }
  | assign      ident '=' exp  ';'     { LocStmt (atList (Assign (unLoc $2) $4 Nothing)
                                      [ $1, getLoc $2, getLoc $4 ]) }
  | assign type ident '=' exp  ';'    { LocStmt (atList (Assign (unLoc $3) $5 (Just $2))
                                                    [ $1, getLoc $2, getLoc $3, getLoc $5]) }

  | return  ';'                       { LocStmt (ReturnVoid `at` $1) }
  | return exp  ';'                   { LocStmt (atBin (Return $2) $1 $2) }

  | refCopy ident ident  ';'          { LocStmt (atList (RefCopy (ExpVar (unLoc $2)) (ExpVar (unLoc $3)))
                                               [$1, getLoc $2, getLoc $3]) }

  | allocRef                      { LocStmt (AllocRef (unLoc $1) `at` (getLoc $1)) }

  //-- Storing
  | store exp as exp  ';'              { LocStmt (atList (Store $2 $4) [$1, getLoc $2, getLoc $4]) }

  //-- Function calls
  | ident expArgs  ';'                 { LocStmt (atBin (NoBindCall (unLoc $1) $2) $1 $2) }

  | ivoryMacro  ';'                    { LocStmt ((IvoryMacroStmt Nothing (unLoc $1)) `at` getLoc $1) }
  | ident '<-' ivoryMacro  ';'         { LocStmt (atBin (IvoryMacroStmt (Just (unLoc $1)) (unLoc $3))
                                               $1 $3) }
  | break  ';'                         { LocStmt (Break `at` $1) }

//ivoryMacro :: { Located (String, [Exp]) }
ivoryMacro : iMacro ident          { atBin (unLoc $2, []) $1 (getLoc $2) }
           | iMacro ident expArgs  { atList (unLoc $2, $3) [$1, getLoc $2, getLoc $3] }

//blkStmt :: { Stmt }
blkStmt :
    mapArr ident '{' stmts '}'             { LocStmt (atList (MapArr (unLoc $2) (reverse $4))
                                                        [$1, getLoc $2, getLoc $4]) }
  | upTo exp ident '{' stmts '}'           { LocStmt (atList (UpTo $2 (unLoc $3) (reverse $5))
                                                        [$1, getLoc $2, getLoc $3, getLoc $5]) }
  | downFrom exp ident '{' stmts '}'       { LocStmt (atList (DownFrom $2 (unLoc $3) (reverse $5))
                                                        [$1, getLoc $2, getLoc $3, getLoc $5]) }

  | upFromTo '(' exp ',' exp ')' ident '{' stmts '}'
      { LocStmt (atList (UpFromTo $3 $5 (unLoc $7) (reverse $9))
          [$1, getLoc $3, getLoc $5, getLoc $7, getLoc $9])
      }

  | downFromTo '(' exp ',' exp ')' ident '{' stmts '}'
      { LocStmt (atList (DownFromTo $3 $5 (unLoc $7) (reverse $9))
          [$1, getLoc $3, getLoc $5, getLoc $7, getLoc $9])
      }

  | forever '{' stmts '}'                  { LocStmt (atBin (Forever (reverse $3)) $1 $2) }

  | if exp '{' stmts '}'
      else '{' stmts '}'                   { LocStmt (atList (IfTE $2 (reverse $4) (reverse $8))
                                                        [ getLoc $2, getLoc $4, getLoc $8 ]) }

//-- Zero or more statements.
//stmts :: { [Stmt] }
stmts : stmts simpleStmt   { $2 : $1 }
      | stmts blkStmt          { $2 : $1 }
      | {- empty -}            { [] }

//expArgs :: { [Exp] }
expArgs : '(' exps ')' { reverse $2 }

//-- Zero or more expressions, separated by arbitrary many ','s.
//exps :: { [Exp] }
exps : exps ',' exp           { $3 : $1 }
     | exps ','               { $1 }
     | exp                    { [$1] }
     | {- empty -}            { [] }

//structInit :: { StructInit }
structInit :
    ivoryMacro           { MacroInit (unLoc $1) }
  | '{' fieldAssigns '}' { FieldInits $2 }

//fieldAssigns :: { [(FieldNm, Exp)] }
fieldAssigns :
    fieldAssigns ',' fieldAssign { $3 : $1 }
  | fieldAssigns ','             { $1 }
  | fieldAssign                  { [$1] }
  | {- empty -}                  { [] }

//fieldAssign :: { (FieldNm, Exp) }
fieldAssign : ident '=' exp { (unLoc $1, $3) }

//----------------------------------------
//-- Expressions
//exp :: { Exp }
exp : integer            { let TokInteger i = unLoc $1 in
                           LocExp (ExpLit (LitInteger i) `at` $1) }

    | str                { let TokString s = unLoc $1 in
                           LocExp (ExpLit (LitString s) `at` $1) }

    | floatlit           { let TokFloat f = unLoc $1 in
                           LocExp (ExpLit (LitFloat f) `at` $1) }

    //-- Works for Haskell values, too!
    | ident              { LocExp ((ExpVar (unLoc $1)) `at` $1) }

    //-- Used only in post-conditions (otherwise, it's a statement).
    | return             { LocExp (ExpRet `at` $1) }

    | '(' exp ')'        { $2 }

    //-- Areas
    | '*' exp              { LocExp (atBin (ExpDeref $2) $1 $2) }
    | exp '@' exp          { LocExp (atBin (ExpArray $1 $3) $1 $3) }
    | exp '[' exp ']'      { LocExp (atBin (ExpDeref (ExpArray $1 $3)) $1 $3) }
    | exp '.' exp          { LocExp (atBin (ExpStruct $1 $3) $1 $3) }
    | exp '->' exp         { LocExp (atBin (ExpDeref (ExpStruct $1 $3)) $1 $3) }
    | '&' ident            { LocExp (atBin (ExpAddrOf (unLoc $2)) $1 $2) }

    | libFuncExp           { $1 }

    //-- Ivory expression macros
    | ivoryMacro           { LocExp (IvoryMacroExp `fmap` $1) }

    //-- Function calls
    | ident expArgs        { LocExp (atBin (ExpCall (unLoc $1) $2) $1 $2) }

    //-- Unary operators
    | '!'       exp      { LocExp (atBin (ExpOp NotOp [$2]) $1 $2) }
    | '-'       exp      { LocExp (atBin (mkNegate $2) $1 $2) }
    | '~'       exp      { LocExp (atBin (ExpOp BitComplementOp [$2]) $1 $2) }

    //-- Binary operators
    | exp '||'  exp      { LocExp (atBin (ExpOp OrOp [$1, $3]) $1 $3) }
    | exp '&&'  exp      { LocExp (atBin (ExpOp AndOp [$1, $3]) $1 $3) }
    | exp '|'   exp      { LocExp (atBin (ExpOp BitOrOp [$1, $3]) $1 $3) }
    | exp '^'   exp      { LocExp (atBin (ExpOp BitXorOp [$1, $3]) $1 $3) }
    | exp '&'   exp      { LocExp (atBin (ExpOp BitAndOp [$1, $3]) $1 $3) }
    | exp '<<'  exp      { LocExp (atBin (ExpOp BitShiftLOp [$1, $3]) $1 $3) }
    | exp '>>'  exp      { LocExp (atBin (ExpOp BitShiftROp [$1, $3]) $1 $3) }

    | exp '=='  exp      { LocExp (atBin (ExpOp EqOp [$1, $3]) $1 $3) }
    | exp '!='  exp      { LocExp (atBin (ExpOp NeqOp [$1, $3]) $1 $3) }

    | exp '<'   exp      { LocExp (atBin (ExpOp (LtOp False) [$1, $3]) $1 $3) }
    | exp '<='  exp      { LocExp (atBin (ExpOp (LtOp True) [$1, $3] ) $1 $3)}
    | exp '>'   exp      { LocExp (atBin (ExpOp (GtOp False) [$1, $3]) $1 $3) }
    | exp '>='  exp      { LocExp (atBin (ExpOp (GtOp True) [$1, $3] ) $1 $3)}

    | exp '+'   exp      { LocExp (atBin (ExpOp AddOp [$1, $3]) $1 $3) }
    | exp '-'   exp      { LocExp (atBin (ExpOp SubOp [$1, $3]) $1 $3) }

    | exp '*'   exp      { LocExp (atBin (ExpOp MulOp [$1, $3]) $1 $3) }
    | exp '/'   exp      { LocExp (atBin (ExpOp DivOp [$1, $3]) $1 $3) }
    | exp '%'   exp      { LocExp (atBin (ExpOp ModOp [$1, $3]) $1 $3) }

    //-- Tertiary operators
    | exp '?' exp ':' exp { LocExp ((ExpOp CondOp [$1, $3, $5]) `at` (getLoc [$1, $3, $5]))  }

//libFuncExp :: { Exp }
libFuncExp :
      abs          expArgs { LocExp (atBin (ExpOp AbsOp $2) $1 $2) }
    | signum       expArgs { LocExp (atBin (ExpOp SignumOp $2) $1 $2) }
    | expOp        expArgs { LocExp (atBin (ExpOp FExpOp $2) $1 $2) }
    | sqrt         expArgs { LocExp (atBin (ExpOp FSqrtOp $2) $1 $2) }
    | log          expArgs { LocExp (atBin (ExpOp FLogOp $2) $1 $2) }
    | pow          expArgs { LocExp (atBin (ExpOp FPowOp $2) $1 $2) }
    | sin          expArgs { LocExp (atBin (ExpOp FSinOp $2) $1 $2) }
    | cos          expArgs { LocExp (atBin (ExpOp FCosOp $2) $1 $2) }
    | tan          expArgs { LocExp (atBin (ExpOp FTanOp $2) $1 $2) }
    | asin         expArgs { LocExp (atBin (ExpOp FAsinOp $2) $1 $2) }
    | acos         expArgs { LocExp (atBin (ExpOp FAcosOp $2) $1 $2) }
    | atan         expArgs { LocExp (atBin (ExpOp FAtanOp $2) $1 $2) }
    | atan2        expArgs { LocExp (atBin (ExpOp FAtan2Op $2) $1 $2) }
    | sinh         expArgs { LocExp (atBin (ExpOp FSinhOp $2) $1 $2) }
    | cosh         expArgs { LocExp (atBin (ExpOp FCoshOp $2) $1 $2) }
    | tanh         expArgs { LocExp (atBin (ExpOp FTanhOp $2) $1 $2) }
    | asinh        expArgs { LocExp (atBin (ExpOp FAsinhOp $2) $1 $2) }
    | acosh        expArgs { LocExp (atBin (ExpOp FAcoshOp $2) $1 $2) }
    | atanh        expArgs { LocExp (atBin (ExpOp FAtanhOp $2) $1 $2) }
    | isnan        expArgs { LocExp (atBin (ExpOp IsNanOp $2) $1 $2) }
    | isinf        expArgs { LocExp (atBin (ExpOp IsInfOp $2) $1 $2) }
    | round        expArgs { LocExp (atBin (ExpOp RoundFOp $2) $1 $2) }
    | ceil         expArgs { LocExp (atBin (ExpOp CeilFOp $2) $1 $2) }
    | floor        expArgs { LocExp (atBin (ExpOp FloorFOp $2) $1 $2) }
    | const        expArgs { LocExp (atBin (ExpOp ConstRefOp $2) $1 $2) }
    | div          expArgs { LocExp (atBin (ExpOp EucDivOp $2) $1 $2) }

    | castWith     expArgs { LocExp (atBin (ExpOp CastWith $2) $1 $2) }
    | safeCast     expArgs { LocExp (atBin (ExpOp SafeCast $2) $1 $2) }
    | bitCast      expArgs { LocExp (atBin (ExpOp BitCast $2) $1 $2) }
    | twosCompCast expArgs { LocExp (atBin (ExpOp TwosCompCast $2) $1 $2) }
    | twosCompRep  expArgs { LocExp (atBin (ExpOp TwosCompRep $2) $1 $2) }

    | toIx         expArgs { LocExp (atBin (ExpOp ToIx $2) $1 $2) }
    | fromIx       expArgs { LocExp (atBin (ExpOp FromIx $2) $1 $2) }
    | ixSize       expArgs { LocExp (atBin (ExpOp IxSize $2) $1 $2) }
    | arrayLen     expArgs { LocExp (atBin (ExpOp ArrayLen $2) $1 $2) }
    | sizeOf       expArgs { LocExp (atBin (ExpOp SizeOf $2) $1 $2) }
    | nullPtr      expArgs { LocExp (atBin (ExpOp NullPtr $2) $1 $2) }
    | refToPtr     expArgs { LocExp (atBin (ExpOp RefToPtr $2) $1 $2) }
    | toCArray     expArgs { LocExp (atBin (ExpOp ToCArray $2) $1 $2) }

//----------------------------------------
//-- Types

//typeDef :: { TypeDef }
typeDef :
  ty tyident '=' type ';' { TypeDef (unLoc $2) $4 (mconcat [$1, getLoc $2, getLoc $4]) }

//type :: { Type }
type :
    baseType         { $1 }
  | cType            { $1 }
  | '(' type ')'     { $2 }

//baseType :: { Type }
baseType :
    simpleCType      { $1 }
  | tyident          { LocTy (TySynonym (unLoc $1) `at` $1) }

//-- C-style types

//simpleCType :: { Type }
simpleCType :
    bool                      { LocTy (TyBool `at` getLoc $1) }
  | char                      { LocTy (TyChar `at` getLoc $1) }
  | float                     { LocTy (TyFloat `at` getLoc $1) }
  | double                    { LocTy (TyDouble `at` getLoc $1) }
  | string                    { LocTy (TyString `at` getLoc $1) }
  | void                      { LocTy (TyVoid `at` getLoc $1) }

  | int8_t                    { LocTy ((TyInt Int8) `at` getLoc $1) }
  | int16_t                   { LocTy ((TyInt Int16) `at` getLoc $1) }
  | int32_t                   { LocTy ((TyInt Int32) `at` getLoc $1) }
  | int64_t                   { LocTy ((TyInt Int64) `at` getLoc $1) }

  | uint8_t                   { LocTy ((TyWord Word8) `at` getLoc $1) }
  | uint16_t                  { LocTy ((TyWord Word16) `at` getLoc $1) }
  | uint32_t                  { LocTy ((TyWord Word32) `at` getLoc $1) }
  | uint64_t                  { LocTy ((TyWord Word64) `at` getLoc $1) }

  | ix_t integer              { let TokInteger i = unLoc $2 in
                                LocTy (atBin (TyIx i) $1 $2) }

//idxs :: { [SzType] }
idxs : idxs '[' szType ']' { unLoc $3 : $1 }
     | {- empty -}         { [] }

//szType :: { Located SzType }
szType : iMacro tyidentifier
           { let TokTyIdent i = unLoc $2 in
             Left i `at` $2
           }
       | integer
           { let TokInteger i = unLoc $1 in
             Right i `at` $1
           }

//cType :: { Type }
cType :
          scopeC '*' type        { LocTy (atBin (TyRef (unLoc $1) $3) $1 $3)  }
  | const scopeC '*' type        { LocTy (atList (TyConstRef (unLoc $2) $4)
                                                 [$1, getLoc $2, getLoc $4]) }
  | baseType '[' szType ']' idxs { LocTy (atBin (tyArray $1 (unLoc $3) $5)
                                                $1 $3)
                                 }
  | struct structName            { LocTy (atBin (TyStruct (unLoc $2)) $1 $2) }
  | '&' type %prec ADDR          { LocTy (atBin (TyStored $2) $1 $2) }

//scopeC :: { Located Scope }
scopeC :
    S           { Stack Nothing `at` $1 }
  | G           { Global `at` $1 }
  | ident       { PolyMem (Just (unLoc $1)) `at` (getLoc $1) }
  | {- empty -} { PolyMem Nothing `at` NoLoc }

//typeHS :: { Type }
typeHS :
    simpleHSType     { $1 }
  | hsType           { $1 }
  | tyident          { LocTy (TySynonym (unLoc $1) `at` $1) }
  | '(' typeHS ')'   { $2 }

//-- Haskell-style types
//simpleHSType :: { Type }
simpleHSType :
    IBool                   { LocTy (TyBool `at` getLoc $1) }
  | IChar                   { LocTy (TyChar `at` getLoc $1) }
  | IFloat                  { LocTy (TyFloat `at` getLoc $1) }
  | IDouble                 { LocTy (TyDouble `at` getLoc $1) }
  | IString                 { LocTy (TyString `at` getLoc $1) }
  | '(' ')'                 { LocTy (TyVoid `at` getLoc $1) }

  | Sint8                   { LocTy ((TyInt Int8) `at` getLoc $1) }
  | Sint16                  { LocTy ((TyInt Int16) `at` getLoc $1) }
  | Sint32                  { LocTy ((TyInt Int32)`at` getLoc $1) }
  | Sint64                  { LocTy ((TyInt Int64) `at` getLoc $1) }

  | Uint8                   { LocTy ((TyWord Word8) `at` getLoc $1) }
  | Uint16                  { LocTy ((TyWord Word16) `at` getLoc $1) }
  | Uint32                  { LocTy ((TyWord Word32) `at` getLoc $1) }
  | Uint64                  { LocTy ((TyWord Word64) `at` getLoc $1) }

  | Ix integer              { let TokInteger i = unLoc $2 in
                              LocTy (atBin (TyIx i) $1 $2) }

//hsType :: { Type }
hsType :
    Ref      scopeHS typeHS { LocTy (atList (TyRef (unLoc $2) $3) [ getLoc $1
                                                                  , getLoc $2
                                                                  , getLoc $3 ]) }
  | ConstRef scopeHS typeHS { LocTy (atList (TyConstRef (unLoc $2) $3) [ getLoc $1
                                                                       , getLoc $2
                                                                       , getLoc $3 ]) }
  | Array    szType  typeHS { LocTy (atList (TyArray $3 (unLoc $2))
                                [ getLoc $1, getLoc $2, getLoc $3]) }
  | Struct   structName     { LocTy (atBin (TyStruct (unLoc $2)) $1 $2) }
  | Stored   typeHS         { LocTy (atBin (TyStored $2) $1 $2) }

//scopeHS :: { Located Scope }
scopeHS : Stack tyident { atBin (Stack (Just (unLoc $2))) $1 $2 }
        | Global        { Global `at` $1 }

//-- Bit types
//bitType :: { BitTy }
bitType :
    Bit                        { LocBitTy (Bit `at` $1) }
  | Bits integer               { let TokInteger i = unLoc $2 in
                                 LocBitTy (atBin (Bits i) $1 $2) }
  | BitArray integer bitType   { let TokInteger i = unLoc $2 in
                                 LocBitTy (atList (BitArray i $3) [ getLoc $1
                                                                  , getLoc $2
                                                                  , getLoc $3 ]) }
  | '(' bitType ')'            { $2 }
  | tyident                    { LocBitTy (BitTySynonym `fmap` $1) }

//----------------------------------------
//-- Struct definitions

//structDef :: { StructDef }
structDef :
    struct structName '{' fields '}' { StructDef (unLoc $2) (reverse $4) (getLoc $2) }
  //-- Remove parsed quotes first
  | abstract struct structName str   { let TokString f = unLoc $4 in
                                       AbstractDef (unLoc $3) (filter (/= '\"') f) (getLoc $3) }
  | string struct structName integer { let TokInteger i = unLoc $4 in
                                       StringDef (unLoc $3) i (getLoc $3) }

//structName :: { Located String }
structName :
    tyident { $1 }
  | ident   { $1 }

//field :: { Field }
field :
  //-- Haskell style
    ident '::' typeHS  { Field (unLoc $1) $3 (getLoc $1 <> getLoc $3) }
  //-- C style
  | type ident         { Field (unLoc $2) $1 (getLoc $1 <> getLoc $2)}

//-- 1 or more fields, separated (but optionally ending with) ';'.
//fields :: { [Field] }
fields :
    fields ';' field   { $3 : $1 }
  | fields ';'         { $1 }
  | field              { [$1] }

//--------------------------------------------------------------------------------
//-- Bit data

//-- Bitdata definition
//bdDef :: { BitDataDef }
bdDef : bitdata tyident '::' bitType
    '=' bdConstrs         { BitDataDef (unLoc $2) $4 (reverse $6)
                            (mconcat [ getLoc $1, getLoc $2, getLoc $3, getLoc $6 ]) }

//-- One or more bitdata constructors, separated by '|'
//bdConstrs :: { [Constr] }
bdConstrs :
    bdConstrs '|' bdConstr { $3 : $1 }
  | bdConstr               { [$1] }

//bdConstr :: { Constr }
bdConstr : ident bdRecord bdLayout { Constr (unLoc $1) $2 $3
                                       (mconcat [ getLoc $1, getLoc $2 ]) }

//-- Zero or more fields.
//bdRecord :: { [BitField] }
bdRecord :
    '{' bdFields '}' { reverse $2 }
  | {- empty -}      { [] }

//bdFields :: { [BitField] }
bdFields :
    bdFields ',' bdField { $3 : $1 }
  | bdField              { [$1] }

//bdField :: { BitField }
bdField :
    ident '::' bitType { BitField (Just (unLoc $1)) $3 (getLoc $1 <> getLoc $3) }
  | '_'   '::' bitType { BitField Nothing   $3 ($1 <> getLoc $3) }

//bdLayout :: { [LayoutItem] }
bdLayout :
    as bdItems   { reverse $2 }
  | {- empty -}  { [] }

//-- One or more items, separated by #
//bdItems :: { [LayoutItem] }
bdItems :
    bdItems '#' bdItem { $3 : $1 }
  | bdItem             { [$1] }

//bdItem :: { LayoutItem }
bdItem :
    ident      { LayoutField (unLoc $1) }
  | integer    { let TokInteger i = unLoc $1 in
                 LayoutConst (BitLitUnknown i) }
  | bitLiteral { LayoutConst $1 }

//-- Parse n-bit natural, e.g.,
//--
//-- 8b0 -- 8 0-bits
//--
//-- 2b01 -- 01
//-- First field is width, second is "b[0,1]+"
//bitLiteral :: { BitLiteral }
bitLiteral : bitlit { let TokBitLit bl = unLoc $1 in
                      BitLitKnown (fst bl) (snd bl) }

//--------------------------------------------------------------------------------
//-- Namespaces

//ident :: { Located String }
ident :
    identifier                  { let TokIdent i = unLoc $1 in
                                  i `at` $1 }
  | tyidentifier '.' identifier { let TokTyIdent t = unLoc $1 in
                                  let TokIdent i   = unLoc $3 in
                                  atBin (t ++ '.':i) $1 $3 }

//tyident :: { Located String }
tyident :
    tyidentifier                  { let TokTyIdent t = unLoc $1 in
                                    t `at` $1 }
  | tyidentifier '.' tyidentifier { let TokTyIdent t0 = unLoc $1 in
                                    let TokTyIdent t1 = unLoc $3 in
                                    atBin (t0 ++ '.':t1) $1 $3 }

//--------------------------------------------------------------------------------
%%
{
mkNegate :: Exp -> Exp
mkNegate e = go e
  where
  go (ExpLit l) = case l of
    LitInteger i -> ExpLit (LitInteger (-i))
    LitFloat   f -> ExpLit (LitFloat   (-f))
    _            -> ExpOp NegateOp [e]
  go (LocExp x) = go (locValue x)
  go _          = ExpOp NegateOp [e]
}
@sorki
Copy link
Member

sorki commented Apr 18, 2022

Interesting, thank you! Maybe we can store this in the repository so people can load it using URL in the generator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants