packages feed

sv2v-0.0.8: src/Language/SystemVerilog/Parser/Parse.y

{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 - Original Parser Author: Tom Hawkins <tomahawkins@gmail.com>
 - vim: filetype=haskell
 -
 - This file has been *heavily* modified and extended from the original version
 - in tomahawkins/verilog. I have added support for numerous SystemVerilog
 - constructs, which has necessitated rewriting nearly all of this.
 -
 - This file is the only remaining one that still uses 2-space indentation. I've
 - decided to leave it this way because I think it is too important to preserve
 - the ability to easily blame/diff this file.
 -}
{
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE TupleSections #-}
module Language.SystemVerilog.Parser.Parse (parse) where

import Control.Monad.Except
import Control.Monad.State.Strict
import Data.Maybe (catMaybes, fromMaybe)
import Language.SystemVerilog.AST
import Language.SystemVerilog.Parser.ParseDecl
import Language.SystemVerilog.Parser.Tokens
}

%monad { ParseState }
%lexer { positionKeep } { TokenEOF }
%name parseMain
%tokentype { Token }
%error { parseErrorTok }

%expect 0

%token

"$bits"                { Token KW_dollar_bits                _ _ }
"$dimensions"          { Token KW_dollar_dimensions          _ _ }
"$unpacked_dimensions" { Token KW_dollar_unpacked_dimensions _ _ }
"$left"                { Token KW_dollar_left                _ _ }
"$right"               { Token KW_dollar_right               _ _ }
"$low"                 { Token KW_dollar_low                 _ _ }
"$high"                { Token KW_dollar_high                _ _ }
"$increment"           { Token KW_dollar_increment           _ _ }
"$size"                { Token KW_dollar_size                _ _ }

"accept_on"        { Token KW_accept_on    _ _ }
"alias"            { Token KW_alias        _ _ }
"always"           { Token KW_always       _ _ }
"always_comb"      { Token KW_always_comb  _ _ }
"always_ff"        { Token KW_always_ff    _ _ }
"always_latch"     { Token KW_always_latch _ _ }
"and"              { Token KW_and          _ _ }
"assert"           { Token KW_assert       _ _ }
"assign"           { Token KW_assign       _ _ }
"assume"           { Token KW_assume       _ _ }
"automatic"        { Token KW_automatic    _ _ }
"before"           { Token KW_before       _ _ }
"begin"            { Token KW_begin        _ _ }
"bind"             { Token KW_bind         _ _ }
"bins"             { Token KW_bins         _ _ }
"binsof"           { Token KW_binsof       _ _ }
"bit"              { Token KW_bit          _ _ }
"break"            { Token KW_break        _ _ }
"buf"              { Token KW_buf          _ _ }
"bufif0"           { Token KW_bufif0       _ _ }
"bufif1"           { Token KW_bufif1       _ _ }
"byte"             { Token KW_byte         _ _ }
"case"             { Token KW_case         _ _ }
"casex"            { Token KW_casex        _ _ }
"casez"            { Token KW_casez        _ _ }
"cell"             { Token KW_cell         _ _ }
"chandle"          { Token KW_chandle      _ _ }
"checker"          { Token KW_checker      _ _ }
"class"            { Token KW_class        _ _ }
"clocking"         { Token KW_clocking     _ _ }
"cmos"             { Token KW_cmos         _ _ }
"config"           { Token KW_config       _ _ }
"const"            { Token KW_const        _ _ }
"constraint"       { Token KW_constraint   _ _ }
"context"          { Token KW_context      _ _ }
"continue"         { Token KW_continue     _ _ }
"cover"            { Token KW_cover        _ _ }
"covergroup"       { Token KW_covergroup   _ _ }
"coverpoint"       { Token KW_coverpoint   _ _ }
"cross"            { Token KW_cross        _ _ }
"deassign"         { Token KW_deassign     _ _ }
"default"          { Token KW_default      _ _ }
"defparam"         { Token KW_defparam     _ _ }
"design"           { Token KW_design       _ _ }
"disable"          { Token KW_disable      _ _ }
"dist"             { Token KW_dist         _ _ }
"do"               { Token KW_do           _ _ }
"edge"             { Token KW_edge         _ _ }
"else"             { Token KW_else         _ _ }
"end"              { Token KW_end          _ _ }
"endcase"          { Token KW_endcase      _ _ }
"endchecker"       { Token KW_endchecker   _ _ }
"endclass"         { Token KW_endclass     _ _ }
"endclocking"      { Token KW_endclocking  _ _ }
"endconfig"        { Token KW_endconfig    _ _ }
"endfunction"      { Token KW_endfunction  _ _ }
"endgenerate"      { Token KW_endgenerate  _ _ }
"endgroup"         { Token KW_endgroup     _ _ }
"endinterface"     { Token KW_endinterface _ _ }
"endmodule"        { Token KW_endmodule    _ _ }
"endpackage"       { Token KW_endpackage   _ _ }
"endprimitive"     { Token KW_endprimitive _ _ }
"endprogram"       { Token KW_endprogram   _ _ }
"endproperty"      { Token KW_endproperty  _ _ }
"endspecify"       { Token KW_endspecify   _ _ }
"endsequence"      { Token KW_endsequence  _ _ }
"endtable"         { Token KW_endtable     _ _ }
"endtask"          { Token KW_endtask      _ _ }
"enum"             { Token KW_enum         _ _ }
"event"            { Token KW_event        _ _ }
"eventually"       { Token KW_eventually   _ _ }
"expect"           { Token KW_expect       _ _ }
"export"           { Token KW_export       _ _ }
"extends"          { Token KW_extends      _ _ }
"extern"           { Token KW_extern       _ _ }
"final"            { Token KW_final        _ _ }
"first_match"      { Token KW_first_match  _ _ }
"for"              { Token KW_for          _ _ }
"force"            { Token KW_force        _ _ }
"foreach"          { Token KW_foreach      _ _ }
"forever"          { Token KW_forever      _ _ }
"fork"             { Token KW_fork         _ _ }
"forkjoin"         { Token KW_forkjoin     _ _ }
"function"         { Token KW_function     _ _ }
"generate"         { Token KW_generate     _ _ }
"genvar"           { Token KW_genvar       _ _ }
"global"           { Token KW_global       _ _ }
"highz0"           { Token KW_highz0       _ _ }
"highz1"           { Token KW_highz1       _ _ }
"if"               { Token KW_if           _ _ }
"iff"              { Token KW_iff          _ _ }
"ifnone"           { Token KW_ifnone       _ _ }
"ignore_bins"      { Token KW_ignore_bins  _ _ }
"illegal_bins"     { Token KW_illegal_bins _ _ }
"implements"       { Token KW_implements   _ _ }
"implies"          { Token KW_implies      _ _ }
"import"           { Token KW_import       _ _ }
"incdir"           { Token KW_incdir       _ _ }
"include"          { Token KW_include      _ _ }
"initial"          { Token KW_initial      _ _ }
"inout"            { Token KW_inout        _ _ }
"input"            { Token KW_input        _ _ }
"inside"           { Token KW_inside       _ _ }
"instance"         { Token KW_instance     _ _ }
"int"              { Token KW_int          _ _ }
"integer"          { Token KW_integer      _ _ }
"interconnect"     { Token KW_interconnect _ _ }
"interface"        { Token KW_interface    _ _ }
"intersect"        { Token KW_intersect    _ _ }
"join"             { Token KW_join         _ _ }
"join_any"         { Token KW_join_any     _ _ }
"join_none"        { Token KW_join_none    _ _ }
"large"            { Token KW_large        _ _ }
"let"              { Token KW_let          _ _ }
"liblist"          { Token KW_liblist      _ _ }
"library"          { Token KW_library      _ _ }
"local"            { Token KW_local        _ _ }
"localparam"       { Token KW_localparam   _ _ }
"logic"            { Token KW_logic        _ _ }
"longint"          { Token KW_longint      _ _ }
"macromodule"      { Token KW_macromodule  _ _ }
"matches"          { Token KW_matches      _ _ }
"medium"           { Token KW_medium       _ _ }
"modport"          { Token KW_modport      _ _ }
"module"           { Token KW_module       _ _ }
"nand"             { Token KW_nand         _ _ }
"negedge"          { Token KW_negedge      _ _ }
"nettype"          { Token KW_nettype      _ _ }
"new"              { Token KW_new          _ _ }
"nexttime"         { Token KW_nexttime     _ _ }
"nmos"             { Token KW_nmos         _ _ }
"nor"              { Token KW_nor          _ _ }
"noshowcancelled"  { Token KW_noshowcancelled _ _ }
"not"              { Token KW_not          _ _ }
"notif0"           { Token KW_notif0       _ _ }
"notif1"           { Token KW_notif1       _ _ }
"null"             { Token KW_null         _ _ }
"or"               { Token KW_or           _ _ }
"output"           { Token KW_output       _ _ }
"package"          { Token KW_package      _ _ }
"packed"           { Token KW_packed       _ _ }
"parameter"        { Token KW_parameter    _ _ }
"pmos"             { Token KW_pmos         _ _ }
"posedge"          { Token KW_posedge      _ _ }
"primitive"        { Token KW_primitive    _ _ }
"priority"         { Token KW_priority     _ _ }
"program"          { Token KW_program      _ _ }
"property"         { Token KW_property     _ _ }
"protected"        { Token KW_protected    _ _ }
"pull0"            { Token KW_pull0        _ _ }
"pull1"            { Token KW_pull1        _ _ }
"pulldown"         { Token KW_pulldown     _ _ }
"pullup"           { Token KW_pullup       _ _ }
"pulsestyle_ondetect" { Token KW_pulsestyle_ondetect _ _ }
"pulsestyle_onevent"  { Token KW_pulsestyle_onevent _ _ }
"pure"             { Token KW_pure         _ _ }
"rand"             { Token KW_rand         _ _ }
"randc"            { Token KW_randc        _ _ }
"randcase"         { Token KW_randcase     _ _ }
"randsequence"     { Token KW_randsequence _ _ }
"rcmos"            { Token KW_rcmos        _ _ }
"real"             { Token KW_real         _ _ }
"realtime"         { Token KW_realtime     _ _ }
"ref"              { Token KW_ref          _ _ }
"reg"              { Token KW_reg          _ _ }
"reject_on"        { Token KW_reject_on    _ _ }
"release"          { Token KW_release      _ _ }
"repeat"           { Token KW_repeat       _ _ }
"restrict"         { Token KW_restrict     _ _ }
"return"           { Token KW_return       _ _ }
"rnmos"            { Token KW_rnmos        _ _ }
"rpmos"            { Token KW_rpmos        _ _ }
"rtran"            { Token KW_rtran        _ _ }
"rtranif0"         { Token KW_rtranif0     _ _ }
"rtranif1"         { Token KW_rtranif1     _ _ }
"s_always"         { Token KW_s_always     _ _ }
"s_eventually"     { Token KW_s_eventually _ _ }
"s_nexttime"       { Token KW_s_nexttime   _ _ }
"s_until"          { Token KW_s_until      _ _ }
"s_until_with"     { Token KW_s_until_with _ _ }
"scalared"         { Token KW_scalared     _ _ }
"sequence"         { Token KW_sequence     _ _ }
"shortint"         { Token KW_shortint     _ _ }
"shortreal"        { Token KW_shortreal    _ _ }
"showcancelled"    { Token KW_showcancelled _ _ }
"signed"           { Token KW_signed       _ _ }
"small"            { Token KW_small        _ _ }
"soft"             { Token KW_soft         _ _ }
"solve"            { Token KW_solve        _ _ }
"specify"          { Token KW_specify      _ _ }
"specparam"        { Token KW_specparam    _ _ }
"static"           { Token KW_static       _ _ }
"string"           { Token KW_string       _ _ }
"strong"           { Token KW_strong       _ _ }
"strong0"          { Token KW_strong0      _ _ }
"strong1"          { Token KW_strong1      _ _ }
"struct"           { Token KW_struct       _ _ }
"super"            { Token KW_super        _ _ }
"supply0"          { Token KW_supply0      _ _ }
"supply1"          { Token KW_supply1      _ _ }
"sync_accept_on"   { Token KW_sync_accept_on _ _ }
"sync_reject_on"   { Token KW_sync_reject_on _ _ }
"table"            { Token KW_table        _ _ }
"tagged"           { Token KW_tagged       _ _ }
"task"             { Token KW_task         _ _ }
"this"             { Token KW_this         _ _ }
"throughout"       { Token KW_throughout   _ _ }
"time"             { Token KW_time         _ _ }
"timeprecision"    { Token KW_timeprecision _ _ }
"timeunit"         { Token KW_timeunit     _ _ }
"tran"             { Token KW_tran         _ _ }
"tranif0"          { Token KW_tranif0      _ _ }
"tranif1"          { Token KW_tranif1      _ _ }
"tri"              { Token KW_tri          _ _ }
"tri0"             { Token KW_tri0         _ _ }
"tri1"             { Token KW_tri1         _ _ }
"triand"           { Token KW_triand       _ _ }
"trior"            { Token KW_trior        _ _ }
"trireg"           { Token KW_trireg       _ _ }
"type"             { Token KW_type         _ _ }
"typedef"          { Token KW_typedef      _ _ }
"union"            { Token KW_union        _ _ }
"unique"           { Token KW_unique       _ _ }
"unique0"          { Token KW_unique0      _ _ }
"unsigned"         { Token KW_unsigned     _ _ }
"until"            { Token KW_until        _ _ }
"until_with"       { Token KW_until_with   _ _ }
"untyped"          { Token KW_untyped      _ _ }
"use"              { Token KW_use          _ _ }
"uwire"            { Token KW_uwire        _ _ }
"var"              { Token KW_var          _ _ }
"vectored"         { Token KW_vectored     _ _ }
"virtual"          { Token KW_virtual      _ _ }
"void"             { Token KW_void         _ _ }
"wait"             { Token KW_wait         _ _ }
"wait_order"       { Token KW_wait_order   _ _ }
"wand"             { Token KW_wand         _ _ }
"weak"             { Token KW_weak         _ _ }
"weak0"            { Token KW_weak0        _ _ }
"weak1"            { Token KW_weak1        _ _ }
"while"            { Token KW_while        _ _ }
"wildcard"         { Token KW_wildcard     _ _ }
"wire"             { Token KW_wire         _ _ }
"with"             { Token KW_with         _ _ }
"within"           { Token KW_within       _ _ }
"wor"              { Token KW_wor          _ _ }
"xnor"             { Token KW_xnor         _ _ }
"xor"              { Token KW_xor          _ _ }

simpleIdentifier   { Token Id_simple       _ _ }
escapedIdentifier  { Token Id_escaped      _ _ }
systemIdentifier   { Token Id_system       _ _ }
real               { Token Lit_real        _ _ }
number             { Token Lit_number      _ _ }
string             { Token Lit_string      _ _ }
time               { Token Lit_time        _ _ }

"`celldefine"          { Token Dir_celldefine _ _ }
"`endcelldefine"       { Token Dir_endcelldefine _ _ }
"`unconnected_drive"   { Token Dir_unconnected_drive _ _ }
"`nounconnected_drive" { Token Dir_nounconnected_drive _ _ }
"`default_nettype"     { Token Dir_default_nettype _ _ }
"`resetall"            { Token Dir_resetall _ _ }
"`begin_keywords"      { Token Dir_begin_keywords _ _ }
"`end_keywords"        { Token Dir_end_keywords _ _ }

"("                { Token Sym_paren_l _ _ }
")"                { Token Sym_paren_r _ _ }
"["                { Token Sym_brack_l _ _ }
"]"                { Token Sym_brack_r _ _ }
"{"                { Token Sym_brace_l _ _ }
"}"                { Token Sym_brace_r _ _ }
"~"                { Token Sym_tildy _ _ }
"!"                { Token Sym_bang _ _ }
"@"                { Token Sym_at _ _ }
"#"                { Token Sym_pound _ _ }
"%"                { Token Sym_percent _ _ }
"^"                { Token Sym_hat _ _ }
"&"                { Token Sym_amp _ _ }
"|"                { Token Sym_bar _ _ }
"*"                { Token Sym_aster _ _ }
"."                { Token Sym_dot _ _ }
","                { Token Sym_comma _ _ }
":"                { Token Sym_colon _ _ }
";"                { Token Sym_semi _ _ }
"="                { Token Sym_eq _ _ }
"<"                { Token Sym_lt _ _ }
">"                { Token Sym_gt _ _ }
"+"                { Token Sym_plus _ _ }
"-"                { Token Sym_dash _ _ }
"?"                { Token Sym_question _ _ }
"/"                { Token Sym_slash _ _ }
"$"                { Token Sym_dollar _ _ }
"'"                { Token Sym_s_quote _ _ }
"~&"               { Token Sym_tildy_amp _ _ }
"~|"               { Token Sym_tildy_bar _ _ }
"~^"               { Token Sym_tildy_hat _ _ }
"^~"               { Token Sym_hat_tildy _ _ }
"=="               { Token Sym_eq_eq _ _ }
"!="               { Token Sym_bang_eq _ _ }
"&&"               { Token Sym_amp_amp _ _ }
"||"               { Token Sym_bar_bar _ _ }
"**"               { Token Sym_aster_aster _ _ }
"<="               { Token Sym_lt_eq _ _ }
">="               { Token Sym_gt_eq _ _ }
">>"               { Token Sym_gt_gt _ _ }
"<<"               { Token Sym_lt_lt _ _ }
"++"               { Token Sym_plus_plus _ _ }
"--"               { Token Sym_dash_dash _ _ }
"+="               { Token Sym_plus_eq _ _ }
"-="               { Token Sym_dash_eq _ _ }
"*="               { Token Sym_aster_eq _ _ }
"/="               { Token Sym_slash_eq _ _ }
"%="               { Token Sym_percent_eq _ _ }
"&="               { Token Sym_amp_eq _ _ }
"|="               { Token Sym_bar_eq _ _ }
"^="               { Token Sym_hat_eq _ _ }
"+:"               { Token Sym_plus_colon _ _ }
"-:"               { Token Sym_dash_colon _ _ }
"::"               { Token Sym_colon_colon _ _ }
".*"               { Token Sym_dot_aster _ _ }
"->"               { Token Sym_dash_gt _ _ }
":="               { Token Sym_colon_eq _ _ }
":/"               { Token Sym_colon_slash _ _ }
"##"               { Token Sym_pound_pound _ _ }
"[*"               { Token Sym_brack_l_aster _ _ }
"[="               { Token Sym_brack_l_eq _ _ }
"=>"               { Token Sym_eq_gt _ _ }
"@*"               { Token Sym_at_aster _ _ }
"(*"               { Token Sym_paren_l_aster _ _ }
"*)"               { Token Sym_aster_paren_r _ _ }
"*>"               { Token Sym_aster_gt _ _ }
"==="              { Token Sym_eq_eq_eq _ _ }
"!=="              { Token Sym_bang_eq_eq _ _ }
"==?"              { Token Sym_eq_eq_question _ _ }
"!=?"              { Token Sym_bang_eq_question _ _ }
">>>"              { Token Sym_gt_gt_gt _ _ }
"<<<"              { Token Sym_lt_lt_lt _ _ }
"<<="              { Token Sym_lt_lt_eq _ _ }
">>="              { Token Sym_gt_gt_eq _ _ }
"<->"              { Token Sym_lt_dash_gt _ _ }
"|->"              { Token Sym_bar_dash_gt _ _ }
"|=>"              { Token Sym_bar_eq_gt _ _ }
"[->"              { Token Sym_brack_l_dash_gt _ _ }
"#-#"              { Token Sym_pound_dash_pound _ _ }
"#=#"              { Token Sym_pound_eq_pound _ _ }
"@@("              { Token Sym_at_at_paren_l _ _ }
"(*)"              { Token Sym_paren_l_aster_paren_r _ _ }
"->>"              { Token Sym_dash_gt_gt _ _ }
"&&&"              { Token Sym_amp_amp_amp _ _ }
"<<<="             { Token Sym_lt_lt_lt_eq _ _ }
">>>="             { Token Sym_gt_gt_gt_eq _ _ }


-- operator precedences, from *lowest* to *highest*
%nonassoc DefaultStrength
%nonassoc DriveStrength ChargeStrength
%nonassoc NoElse
%nonassoc "else"
%right  "|->" "|=>" "#-#" "#=#"
%right "iff"
%left "or"
%left "and"
%left "intersect"
%left "within"
%right "throughout"
%left "##"
%nonassoc "[*]" "[=]" "[->]"
%right "->" "<->"
%right "?" ":"
%left  "||"
%left  "&&"
%left  "|" "~|"
%left  "^" "^~" "~^"
%left  "&" "~&"
%left  "==" "!=" "===" "!==" "==?" "!=?"
%left  "<" "<=" ">" ">=" "inside" "dist"
%left  "<<" ">>" "<<<" ">>>"
%left  "+" "-"
%left  "*" "/" "%"
%left  "**"
%right REDUCE_OP "!" "~" "++" "--"
%left "'"
%left  "(" ")" "[" "]" "." "::" "#"

%%

opt(p)
  : p { Just $1 }
  |   { Nothing }

Descriptions :: { [Description] }
  : {- empty -}              { [] }
  | Descriptions ";"         { $1 }
  | Descriptions Description { $1 ++ $2 }

Description :: { [Description] }
  : Part(ModuleKW   , endmodule   ) { [$1] }
  | Part(InterfaceKW, endinterface) { [$1] }
  | PackageDeclaration              { [$1] }
  | PITrace PackageItem { map PackageItem $ addPITrace $1 $2 }

OptAsgn :: { Expr }
  : {- empty -} { Nil }
  | "=" Expr    { $2  }

Type :: { Type }
  : TypeNonIdent { $1 }
  | TypeAlias    { $1 }
TypeAlias :: { Type }
  :                               Identifier Dimensions { Alias   $1       $2 }
  | Identifier               "::" Identifier Dimensions { PSAlias $1    $3 $4 }
  | Identifier ParamBindings "::" Identifier Dimensions { CSAlias $1 $2 $4 $5 }
TypeNonIdent :: { Type }
  : PartialType OptSigning Dimensions { $1 $2 $3 }
  | "type" "(" Expr ")" { TypeOf $3 }
PartialType :: { Signing -> [Range] -> Type }
  : PartialTypeP { snd $1 }
PartialTypeP :: { (Position, Signing -> [Range] -> Type) }
  : IntegerVectorTypeP { (fst $1, makeIntegerVector $1) }
  | IntegerAtomTypeP   { (fst $1, makeIntegerAtom   $1) }
  | NonIntegerTypeP    { (fst $1, makeNonInteger    $1) }
  | "enum" EnumBaseType "{" EnumItems   "}" { makeComplex $1 $ Enum   $2 $4 }
  | "struct" Packing    "{" StructItems "}" { makeComplex $1 $ Struct $2 $4 }
  | "union"  Packing    "{" StructItems "}" { makeComplex $1 $ Union  $2 $4 }
CastingType :: { Type }
  : IntegerVectorTypeP { IntegerVector (snd $1) Unspecified [] }
  | IntegerAtomTypeP   { IntegerAtom   (snd $1) Unspecified    }
  | NonIntegerTypeP    { NonInteger    (snd $1)                }
  | SigningP           { Implicit      (snd $1)             [] }
EnumBaseType :: { Type }
  : Type       { $1 }
  | Dimensions { Implicit Unspecified $1 }

Signing :: { Signing }
  : SigningP { snd $1 }
SigningP :: { (Position, Signing) }
  : "signed"   { withPos $1 Signed   }
  | "unsigned" { withPos $1 Unsigned }
OptSigning :: { Signing }
  : Signing { $1 }
  | {- empty -} { Unspecified }

NetTypeP :: { (Position, NetType) }
  : "supply0"   { withPos $1 TSupply0   }
  | "supply1"   { withPos $1 TSupply1   }
  | "tri"       { withPos $1 TTri       }
  | "triand"    { withPos $1 TTriand    }
  | "trior"     { withPos $1 TTrior     }
  | "trireg"    { withPos $1 TTrireg    }
  | "tri0"      { withPos $1 TTri0      }
  | "tri1"      { withPos $1 TTri1      }
  | "uwire"     { withPos $1 TUwire     }
  | "wire"      { withPos $1 TWire      }
  | "wand"      { withPos $1 TWand      }
  | "wor"       { withPos $1 TWor       }
IntegerVectorTypeP :: { (Position, IntegerVectorType) }
  : "bit"       { withPos $1 TBit       }
  | "logic"     { withPos $1 TLogic     }
  | "reg"       { withPos $1 TReg       }
IntegerAtomTypeP :: { (Position, IntegerAtomType) }
  : "byte"      { withPos $1 TByte      }
  | "shortint"  { withPos $1 TShortint  }
  | "int"       { withPos $1 TInt       }
  | "longint"   { withPos $1 TLongint   }
  | "integer"   { withPos $1 TInteger   }
  | "time"      { withPos $1 TTime      }
NonIntegerTypeP :: { (Position, NonIntegerType) }
  : "shortreal" { withPos $1 TShortreal }
  | "real"      { withPos $1 TReal      }
  | "realtime"  { withPos $1 TRealtime  }
  | "string"    { withPos $1 TString    }
  | "event"     { withPos $1 TEvent     }

EnumItems :: { [(Identifier, Expr)] }
  : VariablePortIdentifiers { $1 }

StructItems :: { [(Type, Identifier)] }
  : StructItem             { $1 }
  | StructItems StructItem { $1 ++ $2 }
StructItem :: { [(Type, Identifier)] }
  : Type FieldDecls ";" { map (fieldDecl $1) $2 }

FieldDecls :: { [(Identifier, [Range])] }
  : FieldDecl                 { [$1] }
  | FieldDecls "," FieldDecl  { $1 ++ [$3] }
FieldDecl :: { (Identifier, [Range]) }
  : Identifier Dimensions { ($1, $2) }

Packing :: { Packing }
  : "packed" OptSigning { Packed $2 }
  | {- empty -}         { Unpacked }

Part(begin, end) :: { Description }
  : AttributeInstances          begin PartHeader ModuleItems end StrTag {% $3 $1 False $2 $4 $6 }
  | AttributeInstances "extern" begin PartHeader                        {% $4 $1 True  $3 [] "" }

PartHeader :: { [Attr] -> Bool -> PartKW -> [ModuleItem] -> Identifier -> ParseState Description }
  : Lifetime Identifier PackageImportDeclarations Params PortDecls ";"
    { \attrs extern kw items label -> checkTag $2 label $ Part attrs extern kw $1 $2 (fst $5) ($3 ++ $4 ++ (snd $5) ++ items) }

ModuleKW :: { PartKW }
  : "module" { Module }
  | "macromodule" { Module }
InterfaceKW :: { PartKW }
  : "interface" { Interface }

PackageDeclaration :: { Description }
  : "package" Lifetime Identifier        ";" PackageItems endpackage StrTag {% checkTag $3 $7 $ Package $2 $3 $5 }
  | "class" Lifetime Identifier PIParams ";" ClassItems   endclass   StrTag {% checkTag $3 $8 $ Class $2 $3 $4 $6 }

StrTag :: { Identifier }
  : {- empty -}    { "" }
  | ":" Identifier { $2 }

PackageImportDeclarations :: { [ModuleItem] }
  : PackageImportDeclaration PackageImportDeclarations { $1 ++ $2 }
  | {- empty -}                                        { [] }

PackageImportDeclaration :: { [ModuleItem] }
  : "import" PackageImportItems ";" { map (MIPackageItem . uncurry Import) $2 }

Params :: { [ModuleItem] }
  : PIParams { map (MIPackageItem . Decl) $1 }
PIParams :: { [Decl] }
  : {- empty -}          { [] }
  | "#" "(" ")"          { [] }
  | "#" "(" ParamsFollow { $3 }
ParamsFollow :: { [Decl] }
  : ParamAsgn ParamsEnd        { $1 }
  | ParamAsgn "," ParamsFollow { $1 ++ $3 }
  |               ParamsDecl   { $1 }
ParamsDecl :: { [Decl] }
  : ModuleParameterDecl(ParamsEnd)      { $1 }
  | ModuleParameterDecl(",") ParamsDecl { $1 ++ $2 }
ParamAsgn :: { [Decl] }
  : DeclTrace Identifier "=" Expr { [$1, Param Parameter (Implicit Unspecified []) $2 $4] }
ParamsEnd
  :     ")" {}
  | "," ")" {}

PortDecls :: { ([Identifier], [ModuleItem]) }
  : "(" PortDeclTokens(")") { parseDTsAsPortDecls $2 }
  | "("                ")"  { ([], []) }
  | {- empty -}             { ([], []) }

ModportItems :: { [(Identifier, [ModportDecl])] }
  : ModportItem                  { [$1] }
  | ModportItems "," ModportItem { $1 ++ [$3] }
ModportItem :: { (Identifier, [ModportDecl]) }
  : Identifier "(" ModportPortsDeclarations { ($1, $3) }
ModportPortsDeclarations :: { [ModportDecl] }
  : ModportPortsDeclaration(")")                          { $1 }
  | ModportPortsDeclaration(",") ModportPortsDeclarations { $1 ++ $2 }
ModportPortsDeclaration(delim) :: { [ModportDecl] }
  : ModportSimplePortsDeclaration(delim) { $1 }
ModportSimplePortsDeclaration(delim) :: { [ModportDecl] }
  : Direction ModportSimplePorts delim { map (\(a, b) -> ($1, a, b)) $2 }
ModportSimplePorts :: { [(Identifier, Expr)] }
  : ModportSimplePort                        { [$1] }
  | ModportSimplePorts "," ModportSimplePort { $1 ++ [$3] }
ModportSimplePort :: { (Identifier, Expr) }
  : "." Identifier "(" ExprOrNil ")" { ($2, $4) }
  | Identifier                       { ($1, Ident $1) }

Identifier :: { Identifier }
  : IdentifierP { snd $1 }
IdentifierP :: { (Position, Identifier) }
  : simpleIdentifier  { withPos $1 $ tokenString $1 }
  | escapedIdentifier { withPos $1 $ tokenString $1 }
  | systemIdentifier  { withPos $1 $ tokenString $1 }

Identifiers :: { [Identifier] }
  :                 Identifier { [$1] }
  | Identifiers "," Identifier { $1 ++ [$3] }

Strength :: { Strength }
  : {- empty -}    %prec DefaultStrength { DefaultStrength }
  | DriveStrength  %prec DriveStrength   { uncurry DriveStrength $1 }
  | ChargeStrength %prec ChargeStrength  { ChargeStrength $1 }

-- uses delimiter propagation hack to avoid conflicts
DeclTokens(delim) :: { [DeclToken] }
  : DeclTokensBase(DeclTokens(delim), delim) { $1 }
DeclTokensBase(repeat, delim) :: { [DeclToken] }
  : DeclToken         DTDelim(delim) { [$1, $2] }
  | DeclToken                 repeat { [$1] ++ $2 }
  | IdentifierP ParamBindings repeat { [uncurry DTIdent $1, DTParams (fst $1) $2] ++ $3 }
  | DeclTokenAsgn ","         repeat { [$1, DTComma (tokenPosition $2)] ++ $3 }
  | DeclTokenAsgn     DTDelim(delim) { [$1, $2] }
DeclToken :: { DeclToken }
  : ","                                { DTComma   $ tokenPosition $1 }
  | "[" "]"                            { DTAutoDim $ tokenPosition $1 }
  | "const"                            { DTConst   $ tokenPosition $1 }
  | "var"                              { DTVar     $ tokenPosition $1 }
  | PartSelectP                        { uncurry (DTRange $ fst $1) (snd $1) }
  | IdentifierP                        { uncurry DTIdent $1 }
  | DirectionP                         { uncurry DTDir $1 }
  | LHSConcatP                         { uncurry DTLHSBase $1 }
  | LHSStreamP                         { uncurry DTLHSBase $1 }
  | PartialTypeP                       { uncurry DTType $1 }
  | NetTypeP Strength                  { uncurry DTNet $1 $2 }
  | PortBindingsP                      { uncurry DTPorts $1 }
  | SigningP                           { uncurry DTSigning $1 }
  | "[" Expr "]"                       { DTBit      (tokenPosition $1) $2 }
  | "." Identifier                     { DTDot      (tokenPosition $1) $2 }
  | "automatic"                        { DTLifetime (tokenPosition $1) Automatic }
  | "type" "(" Expr ")"                { uncurry DTType $ makeTypeOf $1 $3 }
  | IncOrDecOperatorP                  { DTAsgn     (fst $1) (AsgnOp $ snd $1) Nothing (RawNum 1) }
  | IdentifierP               "::" Identifier { uncurry DTPSIdent $1    $3 }
  | IdentifierP ParamBindings "::" Identifier { uncurry DTCSIdent $1 $2 $4 }
DTDelim(delim) :: { DeclToken }
  : delim { DTEnd (tokenPosition $1) (head $ tokenString $1) }
DeclTokenAsgn :: { DeclToken }
  : "=" opt(DelayOrEvent) Expr { DTAsgn (tokenPosition $1) AsgnOpEq $2 $3 }
  | AsgnBinOpP Expr            { uncurry DTAsgn $1 Nothing $2 }
  | "<=" opt(DelayOrEvent) Expr { DTAsgn (tokenPosition $1) AsgnOpNonBlocking $2 $3 }
PortDeclTokens(delim) :: { [DeclToken] }
  : DeclTokensBase(PortDeclTokens(delim), delim) { $1 }
  | GenericInterfaceDecl   PortDeclTokens(delim) { $1 ++ $2}
  | GenericInterfaceDecl          DTDelim(delim) { $1 ++ [$2] }
  | AttributeInstanceP     PortDeclTokens(delim) { uncurry DTAttr $1 : $2 }
ModuleDeclTokens(delim) :: { [DeclToken] }
  : DeclTokensBase(ModuleDeclTokens(delim), delim) { $1 }
  | GenericInterfaceDecl   ModuleDeclTokens(delim) { $1 ++ $2}
  | GenericInterfaceDecl            DTDelim(delim) { $1 ++ [$2] }
GenericInterfaceDecl :: { [DeclToken] }
  : "interface" IdentifierP { [DTType (tokenPosition $1) (\Unspecified -> InterfaceT "" ""), uncurry DTIdent $2] }

VariablePortIdentifiers :: { [(Identifier, Expr)] }
  : VariablePortIdentifier                             { [$1] }
  | VariablePortIdentifiers "," VariablePortIdentifier { $1 ++ [$3] }
VariablePortIdentifier :: { (Identifier, Expr) }
  : Identifier OptAsgn { ($1,$2) }

Direction :: { Direction }
  : DirectionP { snd $1 }
DirectionP :: { (Position, Direction) }
  : "inout"  { (tokenPosition $1, Inout ) }
  | "input"  { (tokenPosition $1, Input ) }
  | "output" { (tokenPosition $1, Output) }

ModuleItems :: { [ModuleItem] }
  : {- empty -}                    { [] }
  | ";" ModuleItems                { $2 }
  | MITrace ModuleItem ModuleItems { addMITrace $1 ($2 ++ $3) }

ModuleItem :: { [ModuleItem] }
  : NonGenerateModuleItem { $1 }
  | ConditionalGenerateConstruct    { [Generate [$1]] }
  | LoopGenerateConstruct           { [Generate [$1]] }
  | "generate" GenItems endgenerate { [Generate $2] }
NonGenerateModuleItem :: { [ModuleItem] }
  -- This item covers module instantiations and all declarations
  : ModuleDeclTokens(";")                { parseDTsAsModuleItems $1 }
  | ParameterDecl(";")                   { map (MIPackageItem . Decl) $1 }
  | "defparam" LHSAsgns ";"              { map (uncurry Defparam) $2 }
  | "assign" AssignOption LHSAsgns ";"   { map (uncurry $ Assign $2) $3 }
  | AlwaysKW Stmt                        { [AlwaysC $1 $2] }
  | "initial" Stmt                       { [Initial $2] }
  | "final"   Stmt                       { [Final   $2] }
  | "genvar" Identifiers ";"             { map Genvar $2 }
  | "modport" ModportItems ";"           { map (uncurry Modport) $2 }
  | NonDeclPackageItem                   { map MIPackageItem $1 }
  | TaskOrFunction                       { [MIPackageItem $1] }
  | NInputGateKW  NInputGates  ";"       { map (\(a, b, c, d) -> NInputGate  $1 a b c d) $2 }
  | NOutputGateKW NOutputGates ";"       { map (\(a, b, c, d) -> NOutputGate $1 a b c d) $2 }
  | AttributeInstance ModuleItem         { map (addMIAttr $1) $2 }
  | AssertionItem                        { [AssertionItem $1] }

AssignOption :: { AssignOption }
  : {- empty -}   { AssignOptionNone }
  | DelayControl  { AssignOptionDelay $1 }
  | DriveStrength { uncurry AssignOptionDrive $1 }

-- for ModuleItem, for now
AssertionItem :: { AssertionItem }
  : ConcurrentAssertionItem { $1 }

-- for Stmt, for now
ProceduralAssertionStatement :: { Assertion }
  : ConcurrentAssertionStatement { $1 }
  | ImmediateAssertionStatement  { $1 }

ConcurrentAssertionItem :: { AssertionItem }
  : Identifier ":" ConcurrentAssertionStatement { ($1, $3) }
  |                ConcurrentAssertionStatement { ("", $1) }
ConcurrentAssertionStatement :: { Assertion }
  : "assert" "property" "(" PropertySpec ")" ActionBlock { Assert (Left $4) $6 }
  | "assume" "property" "(" PropertySpec ")" ActionBlock { Assume (Left $4) $6 }
  | "cover"  "property" "(" PropertySpec ")" Stmt        { Cover  (Left $4) $6 }

ImmediateAssertionStatement :: { Assertion }
  : SimpleImmediateAssertionStatement { $1 }
SimpleImmediateAssertionStatement :: { Assertion }
  : "assert" "(" Expr ")" ActionBlock { Assert (Right $3) $5 }
  | "assume" "(" Expr ")" ActionBlock { Assume (Right $3) $5 }
  | "cover"  "(" Expr ")" Stmt        { Cover  (Right $3) $5 }

PropertySpec :: { PropertySpec }
  : opt(ClockingEvent) "disable" "iff" "(" Expr ")" PropExpr { PropertySpec $1 $5  $7 }
  | opt(ClockingEvent)                              PropExpr { PropertySpec $1 Nil $2 }

PropExpr :: { PropExpr }
  : SeqExpr { PropExpr $1 }
  | PropExprParens { $1 }
PropExprParens :: { PropExpr }
  : "(" PropExprParens ")" { $2 }
  | SeqExpr "|->" PropExpr { PropExprImpliesO  $1 $3 }
  | SeqExpr "|=>" PropExpr { PropExprImpliesNO $1 $3 }
  | SeqExpr "#-#" PropExpr { PropExprFollowsO  $1 $3 }
  | SeqExpr "#=#" PropExpr { PropExprFollowsNO $1 $3 }
  | PropExpr "iff" PropExpr { PropExprIff $1 $3 }
SeqExpr :: { SeqExpr }
  : Expr { SeqExpr $1 }
  | SeqExprParens { $1 }
SeqExprParens :: { SeqExpr }
  : "(" SeqExprParens ")" { $2 }
  | SeqExpr "and"        SeqExpr { SeqExprAnd        $1 $3 }
  | SeqExpr "or"         SeqExpr { SeqExprOr         $1 $3 }
  | SeqExpr "intersect"  SeqExpr { SeqExprIntersect  $1 $3 }
  | Expr    "throughout" SeqExpr { SeqExprThroughout $1 $3 }
  | SeqExpr "within"     SeqExpr { SeqExprWithin     $1 $3 }
  | SeqExpr "##" Number  SeqExpr { SeqExprDelay (Just $1) (Number $3) $4 }
  |         "##" Number  SeqExpr { SeqExprDelay (Nothing) (Number $2) $3 }
  | "first_match" "(" SeqExpr SeqMatchItems ")" { SeqExprFirstMatch $3 $4 }
SeqMatchItems :: { [SeqMatchItem] }
  : "," SeqMatchItem               { [$2] }
  | SeqMatchItems "," SeqMatchItem { $1 ++ [$3] }
SeqMatchItem :: { SeqMatchItem }
  : ForStepAssignment   { SeqMatchAsgn $1 }
  | Identifier CallArgs { SeqMatchCall $1 $2 }

ActionBlock :: { ActionBlock }
  : Stmt %prec NoElse { ActionBlock $1 Null }
  |      "else" Stmt  { ActionBlock Null $2 }
  | Stmt "else" Stmt  { ActionBlock $1   $3 }

AttributeInstances :: { [Attr] }
  : {- empty -}                          { [] }
  | AttributeInstance AttributeInstances { $1 : $2 }
AttributeInstance :: { Attr }
  : AttributeInstanceP { snd $1 }
AttributeInstanceP :: { (Position, Attr) }
  : "(*" AttrSpecs "*)" { withPos $1 $ Attr $2 }
AttrSpecs :: { [AttrSpec] }
  : AttrSpec               { [$1] }
  | AttrSpecs "," AttrSpec { $1 ++ [$3] }
AttrSpec :: { AttrSpec }
  : Identifier OptAsgn { ($1, $2) }

NInputGates :: { [(Expr, Identifier, LHS, [Expr])] }
  : NInputGate                 { [$1] }
  | NInputGates "," NInputGate { $1 ++ [$3]}
NOutputGates :: { [(Expr, Identifier, [LHS], Expr)] }
  : NOutputGate                  { [$1] }
  | NOutputGates "," NOutputGate { $1 ++ [$3]}

NInputGate :: { (Expr, Identifier, LHS, [Expr]) }
  : DelayControlOrNil opt(Identifier) "(" LHS "," Exprs ")" { ($1, fromMaybe "" $2, $4, $6) }
NOutputGate :: { (Expr, Identifier, [LHS], Expr) }
  : DelayControlOrNil opt(Identifier) "(" NOutputGateItems { ($1, fromMaybe "" $2, fst $4, snd $4) }
NOutputGateItems :: { ([LHS], Expr) }
  : Expr ")" { ([], $1) }
  | Expr "," NOutputGateItems { (toLHS $1 : fst $3, snd $3) }
DelayControlOrNil :: { Expr }
  : DelayControl { $1 }
  | {- empty -} { Nil }

NInputGateKW :: { NInputGateKW }
  : "and"  { GateAnd  }
  | "nand" { GateNand }
  | "or"   { GateOr   }
  | "nor"  { GateNor  }
  | "xor"  { GateXor  }
  | "xnor" { GateXnor }
NOutputGateKW :: { NOutputGateKW }
  : "buf"  { GateBuf  }
  | "not"  { GateNot  }

DriveStrength :: { (Strength0, Strength1) }
  : "(" Strength0 "," Strength1 ")" { ($2    , $4    ) }
  | "(" Strength1 "," Strength0 ")" { ($4    , $2    ) }
  | "(" Strength0 "," "highz1"  ")" { ($2    , Highz1) }
  | "(" Strength1 "," "highz0"  ")" { (Highz0, $2    ) }
  | "(" "highz0"  "," Strength1 ")" { (Highz0, $4    ) }
  | "(" "highz1"  "," Strength0 ")" { ($4    , Highz1) }
Strength0 :: { Strength0 }
  : "supply0" { Supply0 }
  | "strong0" { Strong0 }
  | "pull0"   { Pull0   }
  | "weak0"   { Weak0   }
Strength1 :: { Strength1 }
  : "supply1" { Supply1 }
  | "strong1" { Strong1 }
  | "pull1"   { Pull1   }
  | "weak1"   { Weak1   }
ChargeStrength :: { ChargeStrength }
  : "(" "small"  ")" { Small  }
  | "(" "medium" ")" { Medium }
  | "(" "large"  ")" { Large  }

LHSAsgns :: { [(LHS, Expr)] }
  : LHSAsgn                   { [$1] }
  | LHSAsgns "," LHSAsgn { $1 ++ [$3] }
LHSAsgn :: { (LHS, Expr) }
  : LHS "=" Expr { ($1, $3) }

PackageItems :: { [PackageItem] }
  : {- empty -}                      { [] }
  | ";" PackageItems                 { $2 }
  | PITrace PackageItem PackageItems { addPITrace $1 ($2 ++ $3) }
PackageItem :: { [PackageItem] }
  : PackageOrClassItem { $1}
  | TaskOrFunction { [$1] }

ClassItems :: { [ClassItem] }
  : {- empty -}                  { [] }
  | ";" ClassItems               { $2 }
  | CITrace ClassItem ClassItems { addCITrace $1 ($2 ++ $3) }
ClassItem :: { [ClassItem] }
  : ClassItemQualifier PackageOrClassItem { map ($1, ) $2 }
  | ClassItemQualifier TaskOrFunction     { [($1, $2)] }
ClassItemQualifier :: { Qualifier }
  : {- empty -} { QNone }
  | "static"    { QStatic }
  | "local"     { QLocal }
  | "protected" { QProtected }

PackageOrClassItem :: { [PackageItem] }
  : DeclTokens(";")    { map Decl $ parseDTsAsDecl $1 }
  | ParameterDecl(";") { map Decl $1 }
  | NonDeclPackageItem { $1 }
NonDeclPackageItem :: { [PackageItem] }
  : Typedef              { [Decl $1] }
  | ImportOrExport       { $1 }
  | ForwardTypedef ";"   { $1 }
  | TimeunitsDeclaration { $1 }
  | Directive            { [Directive $1] }

ImportOrExport :: { [PackageItem] }
  : "import" PackageImportItems ";" { map (uncurry Import) $2 }
  | "export" PackageImportItems ";" { map (uncurry Export) $2 }
  | "export" "*" "::" "*" ";"       { [Export "" ""] }
TaskOrFunction :: { PackageItem }
  : "function" Lifetime FuncRetAndName    TFItems DeclsAndStmts endfunction StrTag {% checkTag (snd $3) $7 $ Function $2 (fst $3) (snd $3) (map makeInput $4 ++ fst $5) (snd $5) }
  | "function" Lifetime "void" Identifier TFItems DeclsAndStmts endfunction StrTag {% checkTag $4       $8 $ Task     $2 $4                ($5 ++ fst $6) (snd $6) }
  | "task"     Lifetime Identifier        TFItems DeclsAndStmts endtask     StrTag {% checkTag $3       $7 $ Task     $2 $3                ($4 ++ fst $5) (snd $5) }
Typedef :: { Decl }
  : "typedef" Type Identifier ";" { ParamType Localparam $3 $2 }
  | "typedef" Type Identifier DimensionsNonEmpty ";" { ParamType Localparam $3 (UnpackedType $2 $4) }
  | "typedef" TypedefRef Identifier ";" { ParamType Localparam $3 $2 }
TypedefRef :: { Type }
  : Identifier              "." Identifier { TypedefRef $ Dot      (Ident $1)     $3 }
  | Identifier "[" Expr "]" "." Identifier { TypedefRef $ Dot (Bit (Ident $1) $3) $6 }
ForwardTypedef :: { [PackageItem] }
  : "typedef"          Identifier { [] }
  | "typedef" "enum"   Identifier { [] }
  | "typedef" "struct" Identifier { [] }
  | "typedef" "union"  Identifier { [] }
TimeunitsDeclaration :: { [PackageItem] }
  : "timeunit" Time          ";" { [] }
  | "timeunit" Time "/" Time ";" { [] }
  | "timeprecision" Time     ";" { [] }

Directive :: { String }
  : "`celldefine"          { tokenString $1 }
  | "`endcelldefine"       { tokenString $1 }
  | "`unconnected_drive" Drive { tokenString $1 ++ " " ++ $2 }
  | "`nounconnected_drive" { tokenString $1 }
  | "`default_nettype" DefaultNetType { tokenString $1 ++ " " ++ $2 }
  | "`resetall"            { tokenString $1 }
Drive :: { String }
  : "pull0" { tokenString $1 }
  | "pull1" { tokenString $1 }
DefaultNetType :: { String }
  : NetTypeP { show $ snd $1 }
  | Identifier { $1 }

PackageImportItems :: { [(Identifier, Identifier)] }
  : PackageImportItem                        { [$1] }
  | PackageImportItems "," PackageImportItem { $1 ++ [$3] }
PackageImportItem :: { (Identifier, Identifier) }
  : Identifier "::" Identifier { ($1, $3) }
  | Identifier "::" "*"        { ($1, "") }

FuncRetAndName :: { (Type, Identifier) }
  : Type                       Identifier { ($1                     , $2) }
  |                            Identifier { (Implicit Unspecified [], $1) }
  | Signing                    Identifier { (Implicit $1          [], $2) }
  |         DimensionsNonEmpty Identifier { (Implicit Unspecified $1, $2) }
  | Signing DimensionsNonEmpty Identifier { (Implicit $1          $2, $3) }

AlwaysKW :: { AlwaysKW }
  : "always"       { Always      }
  | "always_comb"  { AlwaysComb  }
  | "always_ff"    { AlwaysFF    }
  | "always_latch" { AlwaysLatch }

Lifetime :: { Lifetime }
  : {- empty -} { Inherit }
  | ExplicitLifetime { $1 }
ExplicitLifetime :: { Lifetime }
  : "static"    { Static    }
  | "automatic" { Automatic }

TFItems :: { [Decl] }
  : "(" DeclTokens(")") ";" { parseDTsAsTFDecls $2 }
  | "("            ")"  ";" { [] }
  |                     ";" { [] }

ParamType :: { Type }
  : PartialType OptSigning Dimensions { $1 $2 $3 }
  | DimensionsNonEmpty { Implicit Unspecified $1 }
  | Signing Dimensions { Implicit $1          $2 }

Dimensions :: { [Range] }
  : {- empty -}        { [] }
  | DimensionsNonEmpty { $1 }
DimensionsNonEmpty :: { [Range] }
  : Dimension                    { [$1] }
  | DimensionsNonEmpty Dimension { $1 ++ [$2] }
Dimension :: { Range }
  : Range        { $1 }
  | "[" Expr "]" { (RawNum 0, BinOp Sub $2 (RawNum 1)) }

DeclAsgns :: { [(Identifier, Expr, [Range])] }
  : DeclAsgn               { [$1] }
  | DeclAsgns "," DeclAsgn { $1 ++ [$3] }
DeclAsgn :: { (Identifier, Expr, [Range]) }
  : Identifier Dimensions OptAsgn { ($1, $3, $2) }

Range :: { Range }
  : "[" Expr ":"  Expr "]" { ($2, $4) }

PartSelectP :: { (Position, (PartSelectMode, Range)) }
  : "[" Expr ":"  Expr "]" { (tokenPosition $1, (NonIndexed  , ($2, $4))) }
  | "[" Expr "+:" Expr "]" { (tokenPosition $1, (IndexedPlus , ($2, $4))) }
  | "[" Expr "-:" Expr "]" { (tokenPosition $1, (IndexedMinus, ($2, $4))) }

LHS :: { LHS }
  : Identifier         { LHSIdent  $1    }
  | LHS PartSelectP    { uncurry (LHSRange $1) (snd $2) }
  | LHS "[" Expr "]"   { LHSBit    $1 $3 }
  | LHS "." Identifier { LHSDot    $1 $3 }
  | LHSConcatP         { snd $1 }
  | LHSStreamP         { snd $1 }

LHSStreamP :: { (Position, LHS) }
  : "{" StreamOp StreamSize Concat "}" { withPos $1 $ LHSStream $2 $3         (map toLHS $4) }
  | "{" StreamOp            Concat "}" { withPos $1 $ LHSStream $2 (RawNum 1) (map toLHS $3) }

LHSConcatP :: { (Position, LHS) }
  : "{" LHSs "}" { withPos $1 $ LHSConcat $2 }
LHSs :: { [LHS] }
  : LHS           { [$1] }
  | LHSs "," LHS  { $1 ++ [$3] }

PortBindingsP:: { (Position, [PortBinding]) }
  : "(" PortBindingsInside ")" {% checkPortBindings $2 >>= return . withPos $1 }
PortBindingsInside :: { [PortBinding] }
  : OptPortBinding                        { [$1] }
  | OptPortBinding "," PortBindingsInside { $1 : $3}
OptPortBinding :: { PortBinding }
  : {- empty -} { ("", Nil) }
  | PortBinding { $1 }

PortBinding :: { PortBinding }
  : "." Identifier "(" ExprOrNil ")" { ($2, $4) }
  | "." Identifier                   { ($2, Ident $2) }
  | Expr                             { ("", $1) }
  | ".*"                             { ("*", Nil) }

ParamBindings :: { [ParamBinding] }
  : "#" "("                     ")" { [] }
  | "#" "(" ParamBindingsInside ")" {% checkParamBindings $3 }
ParamBindingsInside :: { [ParamBinding] }
  : ParamBinding opt(",")                { [$1] }
  | ParamBinding "," ParamBindingsInside { $1 : $3}
ParamBinding :: { ParamBinding }
  : "." Identifier "(" TypeOrExpr ")" { ($2, $4) }
  | "." Identifier "("            ")" { ($2, Right Nil) }
  | TypeOrExpr                        { ("", $1) }

Stmts :: { [Stmt] }
  : {- empty -} { [] }
  | Stmts Stmt  { $1 ++ [$2] }

Stmt :: { Stmt }
  : StmtTrace StmtAsgn    { Block Seq "" [] [$1, $2] }
  | StmtTrace StmtNonAsgn { $2 }

StmtAsgn :: { Stmt }
  : LHS "="  opt(DelayOrEvent) Expr ";" { Asgn AsgnOpEq $3 $1 $4 }
  | LHS "<=" opt(DelayOrEvent) Expr ";" { Asgn AsgnOpNonBlocking $3 $1 $4 }
  | LHS AsgnBinOp              Expr ";" { Asgn $2  Nothing $1 $3 }
  | LHS IncOrDecOperator ";" { Asgn (AsgnOp $2) Nothing $1 (RawNum 1) }
  | IncOrDecOperator LHS ";" { Asgn (AsgnOp $1) Nothing $2 (RawNum 1) }
  | LHS          ";" { Subroutine (lhsToExpr $1) (Args [] []) }
  | LHS CallArgs ";" { Subroutine (lhsToExpr $1) $2 }
  | Identifier "::" Identifier          ";" { Subroutine (PSIdent $1 $3) (Args [] []) }
  | Identifier "::" Identifier CallArgs ";" { Subroutine (PSIdent $1 $3) $4 }
  | Identifier ParamBindings "::" Identifier          ";" { Subroutine (CSIdent $1 $2 $4) (Args [] []) }
  | Identifier ParamBindings "::" Identifier CallArgs ";" { Subroutine (CSIdent $1 $2 $4) $5 }
StmtNonAsgn :: { Stmt }
  : StmtBlock(BlockKWSeq, end ) { $1 }
  | StmtBlock(BlockKWPar, join) { $1 }
  |                StmtNonBlock { $1 }
  | Identifier ":" StmtNonBlock { Block Seq $1 [] [$3] }
StmtBlock(begin, end) :: { Stmt }
  :                begin StrTag DeclsAndStmts end StrTag {% checkTag $2 $5 $ uncurry (Block $1 $2) $3 }
  | Identifier ":" begin        DeclsAndStmts end StrTag {% checkTag $1 $6 $ uncurry (Block $3 $1) $4 }
StmtNonBlock :: { Stmt }
  : ";" { Null }
  | Unique "if" "(" Expr ")" Stmt "else" Stmt  { If $1 $4 $6 $8   }
  | Unique "if" "(" Expr ")" Stmt %prec NoElse { If $1 $4 $6 Null }
  | "for" "(" ForInit ForCond ForStep ")" Stmt { makeFor $3 $4 $5 $7 }
  | CaseStmt                                   { $1 }
  | TimingControl Stmt                         { Timing $1 $2 }
  | "return" ExprOrNil ";"                     { Return $2 }
  | "break"       ";"                          { Break }
  | "continue"    ";"                          { Continue }
  | "while"  "(" Expr ")" Stmt                 { While   $3 $5 }
  | "repeat" "(" Expr ")" Stmt                 { RepeatL $3 $5 }
  | "do"      Stmt "while" "(" Expr ")" ";"    { DoWhile $5 $2 }
  | "forever" Stmt                             { Forever $2 }
  | "foreach" "(" Identifier IdxVars ")" Stmt  { Foreach $3 $4 $6 }
  | "->"  Identifier ";"                       { Trigger True  $2 }
  | "->>" Identifier ";"                       { Trigger False $2 }
  | AttributeInstance Stmt                     { StmtAttr $1 $2 }
  | ProceduralAssertionStatement               { Assertion $1 }
  | "void" "'" "(" Expr CallArgs ")" ";"       { Subroutine $4 $5 }

CaseStmt :: { Stmt }
  : Unique CaseKW "(" Expr ")"          Cases       "endcase" { Case $1 $2                   $4 $ validateCases $5 $6 }
  | Unique CaseKW "(" Expr ")" "inside" InsideCases "endcase" { Case $1 (caseInsideKW $3 $2) $4 $ validateCases $5 $7 }

BlockKWPar :: { BlockKW }
  : "fork" { Par }
BlockKWSeq :: { BlockKW }
  : "begin" { Seq }

Unique :: { ViolationCheck }
  : {- empty -} { NoCheck  }
  | "unique"    { Unique   }
  | "unique0"   { Unique0  }
  | "priority"  { Priority }

ForInit :: { Either [Decl] [(LHS, Expr)] }
  :            ";"  { Right [] }
  | DeclTokens(";") { parseDTsAsDeclsOrAsgns $1 }

ForCond :: { Expr }
  :      ";" { RawNum 1 }
  | Expr ";" { $1 }

ForStep :: { [(LHS, AsgnOp, Expr)] }
  : {- empty -}     { [] }
  | ForStepNonEmpty { $1 }
ForStepNonEmpty :: { [(LHS, AsgnOp, Expr)] }
  : ForStepAssignment                     { [$1] }
  | ForStepNonEmpty "," ForStepAssignment { $1 ++ [$3] }
ForStepAssignment :: { (LHS, AsgnOp, Expr) }
  : LHS AsgnOp Expr { ($1, $2, $3) }
  | IncOrDecOperator LHS { ($2, AsgnOp $1, RawNum 1) }
  | LHS IncOrDecOperator { ($1, AsgnOp $2, RawNum 1) }

IdxVars :: { [Identifier] }
  : "[" IdxVarsInside "]" { $2 }
IdxVarsInside :: { [Identifier] }
  : IdxVar                   { [$1] }
  | IdxVar "," IdxVarsInside { $1 : $3 }
IdxVar :: { Identifier }
  : {- empty -} { "" }
  | Identifier  { $1 }

DeclsAndStmts :: { ([Decl], [Stmt]) }
  : StmtTrace DeclOrStmt DeclsAndStmts {% combineDeclsAndStmts $2 $3 }
  | StmtTrace StmtNonAsgn Stmts        { ([], $1 : $2 : $3) }
  | StmtTrace {- empty -}              { ([], []) }
DeclOrStmt :: { ([Decl], [Stmt]) }
  : DeclTokens(";")    { parseDTsAsDeclOrStmt $1 }
  | ParameterDecl(";") { ($1, []) }

ModuleParameterDecl(delim) :: { [Decl] }
  : ParameterDecl(delim) { $1 }
  | DeclTrace "type" TypeAsgns delim { $1 : map (uncurry $ ParamType Parameter) $3 }
ParameterDecl(delim) :: { [Decl] }
  : ParameterDeclKW           DeclAsgns delim { makeParamDecls $1 (Implicit Unspecified []) $2 }
  | ParameterDeclKW ParamType DeclAsgns delim { makeParamDecls $1 $2 $3 }
  | ParameterDeclKW TypeAlias DeclAsgns delim { makeParamDecls $1 $2 $3 }
  | ParameterDeclKW "type"    TypeAsgns delim { makeParamTypeDecls $1 $3 }
ParameterDeclKW :: { (Position, ParamScope) }
  : "parameter"  { withPos $1 Parameter  }
  | "localparam" { withPos $1 Localparam }

TypeAsgns :: { [(Identifier, Type)] }
  : TypeAsgn               { [$1] }
  | TypeAsgns "," TypeAsgn { $1 ++ [$3] }
TypeAsgn :: { (Identifier, Type) }
  : Identifier "=" Type { ($1, $3) }
  | Identifier          { ($1, UnknownType) }

-- TODO: This does not allow for @identifier
ClockingEvent :: { Sense }
  : "@" "(" Senses ")" { $3 }

TimingControl :: { Timing }
  : DelayOrEvent { $1 }
  | CycleDelay   { Cycle $1 }
DelayOrEvent :: { Timing }
  : DelayControl { Delay $1 }
  | EventControl { Event $1 }
DelayControl :: { Expr }
  : "#" Number { Number $2 }
  | "#" Time   { Time   $2 }
  | "#" "(" Expr ")"  { $3 }
  | "#" "(" Expr ":" Expr ":" Expr ")" { MinTypMax $3 $5 $7 }
  | "#"                               Identifier { Ident         $2 }
  | "#" Identifier               "::" Identifier { PSIdent $2    $4 }
  | "#" Identifier ParamBindings "::" Identifier { CSIdent $2 $3 $5 }
CycleDelay :: { Expr }
  : "##" Expr { $2 }
EventControl :: { Sense }
  : "@" "(" Senses ")" { $3 }
  | "@" "(*)"          { SenseStar }
  | "@*"               { SenseStar }
  | "@" Identifier     { Sense $ LHSIdent $2 }
Senses :: { Sense }
  : Sense             { $1 }
  | Senses "or" Sense { SenseOr $1 $3 }
  | Senses ","  Sense { SenseOr $1 $3 }
Sense :: { Sense }
  : "(" Sense ")"         {              $2 }
  |               LHS     { Sense        $1 }
  | "posedge"     LHS     { SensePosedge $2 }
  | "negedge"     LHS     { SenseNegedge $2 }
  | "posedge" "(" LHS ")" { SensePosedge $3 }
  | "negedge" "(" LHS ")" { SenseNegedge $3 }

CaseKW :: { CaseKW }
  : "case"  { CaseN }
  | "casex" { CaseX }
  | "casez" { CaseZ }

Cases :: { [Case] }
  : Case       { [$1] }
  | Case Cases { $1 : $2 }
Case :: { Case }
  : Exprs ":"  Stmt { ($1, $3) }
  | "default" opt(":") Stmt { ([], $3) }
InsideCases :: { [Case] }
  : InsideCase             { [$1] }
  | InsideCase InsideCases { $1 : $2 }
InsideCase :: { Case }
  : OpenRangeList ":"  Stmt { ($1, $3) }
  | "default" opt(":") Stmt { ([], $3) }

Real :: { String }
  : real { tokenString $1 }

Number :: { Number }
  : number { parseNumber $ tokenString $1 }

String :: { String }
  : string { tail $ init $ tokenString $1 }

Time :: { String }
  : time { tokenString $1 }

CallArgs :: { Args }
  : "(" CallArgsInside ")" { $2 }
CallArgsInside :: { Args }
  : {- empty -}                        { Args [        ] [] }
  |                NamedCallArgsFollow { Args [        ] $1 }
  | Expr                 NamedCallArgs { Args [$1      ] $2 }
  |      UnnamedCallArgs NamedCallArgs { Args (Nil : $1) $2 }
  | Expr UnnamedCallArgs NamedCallArgs { Args ($1  : $2) $3 }
UnnamedCallArgs :: { [Expr] }
  : "," ExprOrNil                 { [$2] }
  | UnnamedCallArgs "," ExprOrNil { $1 ++ [$3] }
NamedCallArgs :: { [(Identifier, Expr)] }
  : {- empty -}        { [] }
  | "," NamedCallArgsFollow  { $2 }
NamedCallArgsFollow :: { [(Identifier, Expr)] }
  : NamedCallArg                         { [$1] }
  | NamedCallArgsFollow "," NamedCallArg { $1 ++ [$3] }
NamedCallArg :: { (Identifier, Expr) }
  : "." Identifier "(" ExprOrNil ")" { ($2, $4) }

Exprs :: { [Expr] }
  :           Expr  { [$1] }
  | Exprs "," Expr  { $1 ++ [$3] }

TypeOrExpr :: { TypeOrExpr }
  : TypeNonIdent { Left $1 }
  | Expr         { Right $1 }

OpenRangeList :: { [Expr] }
  : ValueRange                   { [$1] }
  | OpenRangeList "," ValueRange { $1 ++ [$3] }
ValueRange :: { Expr }
  : Expr  { $1 }
  | Range { Range Nil NonIndexed $1 }

Expr :: { Expr }
  : "(" Expr ")"                { $2 }
  | String                      { String $1 }
  | Real                        { Real   $1 }
  | Number                      { Number $1 }
  | Time                        { Time   $1 }
  | Expr CallArgs               { Call $1 $2 }
  | DimsFn "(" TypeOrExpr ")"   { DimsFn $1 $3 }
  | DimFn  "(" TypeOrExpr ")"   { DimFn  $1 $3 (RawNum 1) }
  | DimFn  "(" TypeOrExpr "," Expr ")" { DimFn $1 $3 $5 }
  | Expr PartSelectP            { uncurry (Range $1) (snd $2) }
  | Expr "[" Expr "]"           { Bit   $1 $3 }
  | "{" Expr Concat "}"         { Repeat $2 $3 }
  | Concat                      { Concat $1 }
  | Expr "?" Expr ":" Expr      { Mux $1 $3 $5 }
  | Expr "." Identifier         { Dot $1 $3 }
  | "'" "{" PatternItems "}"    { Pattern $3 }
  | Expr "'" "{" PatternItems "}"{ Cast (Right $1) (Pattern $4) }
  | CastingType "'" "(" Expr ")" { Cast (Left  $1) $4 }
  | Expr        "'" "(" Expr ")" { Cast (Right $1) $4 }
  | "{" StreamOp StreamSize Concat "}" { Stream $2 $3         $4 }
  | "{" StreamOp            Concat "}" { Stream $2 (RawNum 1) $3 }
  | Expr "inside" "{" OpenRangeList "}" { Inside $1 $4 }
  | "(" Expr ":" Expr ":" Expr ")" { MinTypMax $2 $4 $6 }
  | Identifier %prec REDUCE_OP {- defer -}   { Ident         $1 }
  | Identifier               "::" Identifier { PSIdent $1    $3 }
  | Identifier ParamBindings "::" Identifier { CSIdent $1 $2 $4 }
  -- binary expressions
  | Expr "||"  Expr { BinOp LogOr  $1 $3 }
  | Expr "&&"  Expr { BinOp LogAnd $1 $3 }
  | Expr "->"  Expr { BinOp LogImp $1 $3 }
  | Expr "<->" Expr { BinOp LogEq  $1 $3 }
  | Expr "|"   Expr { BinOp BitOr  $1 $3 }
  | Expr "^"   Expr { BinOp BitXor $1 $3 }
  | Expr "&"   Expr { BinOp BitAnd $1 $3 }
  | Expr "~^"  Expr { BinOp BitXnor $1 $3 }
  | Expr "^~"  Expr { BinOp BitXnor $1 $3 }
  | Expr "+"   Expr { BinOp Add $1 $3 }
  | Expr "-"   Expr { BinOp Sub $1 $3 }
  | Expr "*"   Expr { BinOp Mul $1 $3 }
  | Expr "/"   Expr { BinOp Div $1 $3 }
  | Expr "%"   Expr { BinOp Mod $1 $3 }
  | Expr "**"  Expr { BinOp Pow $1 $3 }
  | Expr "=="  Expr { BinOp Eq $1 $3 }
  | Expr "!="  Expr { BinOp Ne $1 $3 }
  | Expr "<"   Expr { BinOp Lt $1 $3 }
  | Expr "<="  Expr { BinOp Le $1 $3 }
  | Expr ">"   Expr { BinOp Gt $1 $3 }
  | Expr ">="  Expr { BinOp Ge $1 $3 }
  | Expr "===" Expr { BinOp TEq $1 $3 }
  | Expr "!==" Expr { BinOp TNe $1 $3 }
  | Expr "==?" Expr { BinOp WEq $1 $3 }
  | Expr "!=?" Expr { BinOp WNe $1 $3 }
  | Expr "<<"  Expr { BinOp ShiftL $1 $3 }
  | Expr ">>"  Expr { BinOp ShiftR $1 $3 }
  | Expr "<<<" Expr { BinOp ShiftAL $1 $3 }
  | Expr ">>>" Expr { BinOp ShiftAR $1 $3 }
  -- unary expressions
  | "!"  Expr                 { UniOp LogNot $2 }
  | "~"  Expr                 { UniOp BitNot $2 }
  | "+"  Expr %prec REDUCE_OP { UniOp UniAdd $2 }
  | "-"  Expr %prec REDUCE_OP { UniOp UniSub $2 }
  | "&"  Expr %prec REDUCE_OP { UniOp RedAnd  $2 }
  | "~&" Expr %prec REDUCE_OP { UniOp RedNand $2 }
  | "|"  Expr %prec REDUCE_OP { UniOp RedOr   $2 }
  | "~|" Expr %prec REDUCE_OP { UniOp RedNor  $2 }
  | "^"  Expr %prec REDUCE_OP { UniOp RedXor  $2 }
  | "~^" Expr %prec REDUCE_OP { UniOp RedXnor $2 }
  | "^~" Expr %prec REDUCE_OP { UniOp RedXnor $2 }

ExprOrNil :: { Expr }
  : Expr        { $1 }
  | {- empty -} { Nil }

PatternItems :: { [(TypeOrExpr, Expr)] }
  : PatternNamedItems   { $1 }
  | PatternUnnamedItems { zip (repeat $ Right Nil) $1 }
PatternNamedItems :: { [(TypeOrExpr, Expr)] }
  : PatternNamedItem                       { [$1] }
  | PatternNamedItems "," PatternNamedItem { $1 ++ [$3] }
PatternNamedItem :: { (TypeOrExpr, Expr) }
  : PatternName ":" Expr { ($1, $3) }
PatternName :: { TypeOrExpr }
  : Expr        { Right $1 }
  | PartialType { Left $ $1 Unspecified [] }
  | "default"   { Left UnknownType }
PatternUnnamedItems :: { [Expr] }
  :                         PatternUnnamedItem { [$1] }
  | PatternUnnamedItems "," PatternUnnamedItem { $1 ++ [$3] }
PatternUnnamedItem :: { Expr }
  : Expr        { $1 }
  | Expr Concat { Repeat $1 $2 }

Concat :: { [Expr] }
  : "{" Exprs "}" { $2 }

StreamOp :: { StreamOp }
  : "<<" { StreamL }
  | ">>" { StreamR }
StreamSize :: { Expr }
  : TypeNonIdent { DimsFn FnBits (Left $1) }
  | Expr         { $1 }

GenItemOrNull :: { GenItem }
  : GenItem { $1 }
  | ";"     { GenNull }

GenItems :: { [GenItem] }
  : {- empty -}      { [] }
  | GenItems ";"     { $1 }
  | GenItems GenItem { $1 ++ [$2] }

GenItem :: { GenItem }
  : MITrace GenBlock              { uncurry GenBlock $2 }
  | MITrace NonGenerateModuleItem { genItemsToGenItem $ map GenModuleItem $ addMITrace $1 $2 }
  | MITrace "generate" GenItems "endgenerate" { genItemsToGenItem $3 }
  | MITrace ConditionalGenerateConstruct { $2 }
  | MITrace LoopGenerateConstruct        { $2 }
ConditionalGenerateConstruct :: { GenItem }
  : "if" "(" Expr ")" GenItemOrNull "else" GenItemOrNull { GenIf $3 $5 $7      }
  | "if" "(" Expr ")" GenItemOrNull %prec NoElse         { GenIf $3 $5 GenNull }
  | "case" "(" Expr ")" GenCases "endcase" { GenCase $3 $ validateCases $4 $5 }
LoopGenerateConstruct :: { GenItem }
  : "for" "(" GenvarInitialization ";" Expr ";" GenvarIteration ")" GenItem { $3 $5 $7 $9 }

GenBlock :: { (Identifier, [GenItem]) }
  : GenBlockBegin GenItems end StrTag {% checkTag $1 $4 ($1, $2) }
GenBlockBegin :: { Identifier }
  : "begin" StrTag         { $2 }
  | Identifier ":" "begin" { $1 }

GenCases :: { [GenCase] }
  : GenCase          { [$1] }
  | GenCase GenCases { $1 : $2 }
GenCase :: { GenCase }
  : Exprs         ":"  GenItemOrNull { ($1, $3) }
  | "default" opt(":") GenItemOrNull { ([], $3) }

GenvarInitialization :: { Expr -> (Identifier, AsgnOp, Expr) -> GenItem -> GenItem }
  : "genvar" Identifier "=" Expr { \a b c -> genItemsToGenItem [GenModuleItem (Genvar $2), GenFor ($2, $4) a b c] }
  |          Identifier "=" Expr { GenFor ($1, $3) }

GenvarIteration :: { (Identifier, AsgnOp, Expr) }
  : Identifier AsgnOp Expr { ($1, $2, $3) }
  | IncOrDecOperator Identifier { ($2, AsgnOp $1, RawNum 1) }
  | Identifier IncOrDecOperator { ($1, AsgnOp $2, RawNum 1) }

AsgnOp :: { AsgnOp }
  : "=" { AsgnOpEq }
  | AsgnBinOp { $1 }
AsgnBinOp :: { AsgnOp }
  : AsgnBinOpP { snd $1 }
AsgnBinOpP :: { (Position, AsgnOp) }
  : "+="   { withPos $1 $ AsgnOp Add }
  | "-="   { withPos $1 $ AsgnOp Sub }
  | "*="   { withPos $1 $ AsgnOp Mul }
  | "/="   { withPos $1 $ AsgnOp Div }
  | "%="   { withPos $1 $ AsgnOp Mod }
  | "&="   { withPos $1 $ AsgnOp BitAnd }
  | "|="   { withPos $1 $ AsgnOp BitOr  }
  | "^="   { withPos $1 $ AsgnOp BitXor }
  | "<<="  { withPos $1 $ AsgnOp ShiftL }
  | ">>="  { withPos $1 $ AsgnOp ShiftR }
  | "<<<=" { withPos $1 $ AsgnOp ShiftAL }
  | ">>>=" { withPos $1 $ AsgnOp ShiftAR }

IncOrDecOperator :: { BinOp }
  : IncOrDecOperatorP { snd $1 }
IncOrDecOperatorP :: { (Position, BinOp) }
  : "++" { withPos $1 Add }
  | "--" { withPos $1 Sub }

DimsFn :: { DimsFn }
  : "$bits"                { FnBits               }
  | "$dimensions"          { FnDimensions         }
  | "$unpacked_dimensions" { FnUnpackedDimensions }
DimFn :: { DimFn }
  : "$left"                { FnLeft               }
  | "$right"               { FnRight              }
  | "$low"                 { FnLow                }
  | "$high"                { FnHigh               }
  | "$increment"           { FnIncrement          }
  | "$size"                { FnSize               }

MITrace :: { ModuleItem }
  : PITrace { MIPackageItem $1 }
PITrace :: { PackageItem }
  : DeclTrace { Decl $1 }
CITrace :: { ClassItem }
  : PITrace { (QNone, $1) }
DeclTrace :: { Decl }
  : Trace { CommentDecl $1 }
StmtTrace :: { Stmt }
  : Trace { CommentStmt $1 }
Trace :: { String }
  : position { "Trace: " ++ show $1 }
position :: { Position }
  : {- empty -} {% gets pPosition }

end          : "end"          {} | error {% missingToken "end"          }
endclass     : "endclass"     {} | error {% missingToken "endclass"     }
endfunction  : "endfunction"  {} | error {% missingToken "endfunction"  }
endgenerate  : "endgenerate"  {} | error {% missingToken "endgenerate"  }
endinterface : "endinterface" {} | error {% missingToken "endinterface" }
endmodule    : "endmodule"    {} | error {% missingToken "endmodule"    }
endpackage   : "endpackage"   {} | error {% missingToken "endpackage"   }
endtask      : "endtask"      {} | error {% missingToken "endtask"      }
join         : "join"         {} | error {% missingToken "join"         }

{

data ParseData = ParseData
  { pPosition :: Position
  , pTokens :: [Token]
  }

type ParseState = StateT ParseData (ExceptT String IO)

parse :: [Token] -> ExceptT String IO AST
parse [] = return []
parse tokens =
  evalStateT parseMain initialState
  where
    position = tokenPosition $ head tokens
    initialState = ParseData position tokens

positionKeep :: (Token -> ParseState a) -> ParseState a
positionKeep cont = do
  tokens <- gets pTokens
  case tokens of
    [] -> cont TokenEOF
    tok : toks -> do
      put $ ParseData (tokenPosition tok) toks
      cont tok

parseErrorTok :: Token -> ParseState a
parseErrorTok a = case a of
  TokenEOF    -> do
    p <- gets pPosition
    parseErrorM p "unexpected end of file"
  Token t s p ->
    parseErrorM p $ "unexpected token '" ++ s ++ "' (" ++ show t ++ ")"

parseErrorM :: Position -> String -> ParseState a
parseErrorM pos msg = throwError $ show pos ++ ": Parse error: " ++ msg

parseError :: Position -> String -> a
parseError pos msg = error $ show pos ++ ": Parse error: " ++ msg

genItemsToGenItem :: [GenItem] -> GenItem
genItemsToGenItem [x] = x
genItemsToGenItem xs = GenModuleItem $ Generate xs

combineDeclsAndStmts :: ([Decl], [Stmt]) -> ([Decl], [Stmt]) ->
  ParseState ([Decl], [Stmt])
combineDeclsAndStmts (a1, b1) (a2, b2) =
  if not (null b1) && not (null a2)
    then do
      p <- gets pPosition
      parseErrorM p
        "procedural block contains a declaration after a statement"
    else return (a1 ++ a2, b1 ++ b2)

makeInput :: Decl -> Decl
makeInput (Variable Local t x a e) = Variable Input t x a e
makeInput (Variable Input t x a e) = Variable Input t x a e
makeInput (CommentDecl c) = CommentDecl c
makeInput other =
  error $ "unexpected non-var or non-input decl: " ++ (show other)

checkTag :: String -> String -> a -> ParseState a
checkTag _ "" x = return x
checkTag "" b _ = do
  p <- gets pPosition
  parseErrorM p $ "block has only end label " ++ show b
checkTag a b x =
  if a == b
    then return x
    else do
      p <- gets pPosition
      parseErrorM p $ "element " ++ show a
        ++ " has mismatched end label " ++ show b

toLHS :: Expr -> LHS
toLHS expr =
  case exprToLHS expr of
    Just lhs -> lhs
    Nothing -> error $ "Parse error: cannot convert expression to LHS: "
                ++ show expr

traceCommentDecl :: Position -> Decl
traceCommentDecl = CommentDecl . ("Trace: " ++) . show

makeParamDecls
  :: (Position, ParamScope)
  -> Type
  -> [(Identifier, Expr, [Range])]
  -> [Decl]
makeParamDecls (p, s) t items =
  traceCommentDecl p :
  map mapper items
  where
    (tf, rs) = typeRanges t
    mapper (x, e, a) = Param s (tf $ a ++ rs) x e

makeParamTypeDecls :: (Position, ParamScope) -> [(Identifier, Type)] -> [Decl]
makeParamTypeDecls (p, s) items =
  traceCommentDecl p :
  map (uncurry $ ParamType s) items

fieldDecl :: Type -> (Identifier, [Range]) -> (Type, Identifier)
fieldDecl t (x, rs2) =
  (tf $ rs2 ++ rs1, x)
  where (tf, rs1) = typeRanges t

validateCases :: Token -> [([Expr], a)] -> [([Expr], a)]
validateCases tok items =
  if length (filter (null . fst) items) <= 1
    then items
    else parseError (tokenPosition tok) "case has multiple defaults"

caseInsideKW :: Token -> CaseKW -> CaseKW
caseInsideKW _ CaseN = CaseInside
caseInsideKW tok kw =
  parseError (tokenPosition tok) $ "cannot use inside with " ++ show kw

addMIAttr :: Attr -> ModuleItem -> ModuleItem
addMIAttr _ (item @ (MIPackageItem (Decl CommentDecl{}))) = item
addMIAttr attr item = MIAttr attr item

missingToken :: String -> ParseState a
missingToken expected = do
  p <- gets pPosition
  parseErrorM p $ "missing expected `" ++ expected ++ "`"

checkPortBindings :: [PortBinding] -> ParseState [PortBinding]
checkPortBindings [] = return []
checkPortBindings bindings =
  checkBindings "port connections" $
  if last bindings == ("", Nil)
    then init bindings
    else bindings

checkParamBindings :: [ParamBinding] -> ParseState [ParamBinding]
checkParamBindings = checkBindings "parameter overrides"

checkBindings :: String -> [(Identifier, a)] -> ParseState [(Identifier, a)]
checkBindings kind bindings =
  if all null bindingNames || all (not . null) bindingNames then
    return bindings
  else do
    p <- gets pPosition
    parseErrorM p $ "illegal mix of ordered and named " ++ kind
  where bindingNames = map fst bindings

withPos :: Token -> a -> (Position, a)
withPos tok = (tokenPosition tok, )

type PartialType = Signing -> [Range] -> Type

unexpectedPackedRanges :: Position -> String -> a
unexpectedPackedRanges pos typ =
  parseError pos $ "unexpected packed range(s) applied to " ++ typ

unexpectedSigning :: Position -> Signing -> String -> a
unexpectedSigning pos sg typ =
  parseError pos $ "unexpected " ++ show sg ++ " applied to " ++ typ

makeIntegerVector :: (Position, IntegerVectorType) -> PartialType
makeIntegerVector (_, typ) = IntegerVector typ

makeIntegerAtom :: (Position, IntegerAtomType) -> PartialType
makeIntegerAtom (_, typ) sg [] = IntegerAtom typ sg
makeIntegerAtom (pos, typ) _ _ = unexpectedPackedRanges pos (show typ)

makeNonInteger :: (Position, NonIntegerType) -> PartialType
makeNonInteger (_, typ) Unspecified [] = NonInteger typ
makeNonInteger (pos, typ) sg [] = unexpectedSigning pos sg (show typ)
makeNonInteger (pos, typ) Unspecified _ = unexpectedPackedRanges pos (show typ)

makeComplex :: Token -> ([Range] -> Type) -> (Position, PartialType)
makeComplex (Token _ str pos) tf = (pos, check)
  where
    check Unspecified = tf
    check sg = unexpectedSigning pos sg str

makeTypeOf :: Token -> Expr -> (Position, PartialType)
makeTypeOf (Token _ _ pos) expr = (pos, check)
  where
    typ = TypeOf expr
    check Unspecified [] = typ
    check Unspecified _  = unexpectedPackedRanges pos (show typ)
    check sg [] = unexpectedSigning pos sg (show typ)

addMITrace :: ModuleItem -> [ModuleItem] -> [ModuleItem]
addMITrace _ items @ (MIPackageItem (Decl CommentDecl{}) : _) = items
addMITrace trace items = trace : items

addPITrace :: PackageItem -> [PackageItem] -> [PackageItem]
addPITrace _ items @ (Decl CommentDecl{} : _) = items
addPITrace trace items = trace : items

addCITrace :: ClassItem -> [ClassItem] -> [ClassItem]
addCITrace _ items @ ((_, Decl CommentDecl{}) : _) = items
addCITrace trace items = trace : items

makeFor :: Either [Decl] [(LHS, Expr)] -> Expr -> [(LHS, AsgnOp, Expr)] -> Stmt -> Stmt
makeFor (Left inits) cond incr stmt =
  Block Seq "" decls
    [ CommentStmt msg
    , For (catMaybes maybeAsgns) cond incr stmt
    ]
  where
    (decls, maybeAsgns) = unzip $ map splitInit inits
    CommentDecl msg : _ = inits
makeFor (Right asgns) cond incr stmt = For asgns cond incr stmt

splitInit :: Decl -> (Decl, Maybe (LHS, Expr))
splitInit decl@CommentDecl{} = (decl, Nothing)
splitInit decl =
  (Variable d t ident a Nil, Just (LHSIdent ident, e))
  where Variable d t ident a e = decl

}