packages feed

ivory-0.1.0.3: src/Ivory/Language/Syntax/Concrete/Parser.y

-- # -*- mode: haskell -*-
{
--
-- Ivory parser.
--
-- Copyright (C) 2014, Galois, Inc.
-- All rights reserved.
--

-- TODO
-- types for allocs

module Ivory.Language.Syntax.Concrete.Parser where

import Prelude ()
import Prelude.Compat

import Ivory.Language.Syntax.Concrete.ParseCore
import Ivory.Language.Syntax.Concrete.ParseAST
import Ivory.Language.Syntax.Concrete.Lexer
import Ivory.Language.Syntax.Concrete.Lexeme ( Token(..), Lexeme )
import Ivory.Language.Syntax.Concrete.Location

import Data.Monoid.Compat

}

%name      topParser   defs
%name      stmtsParser stmts
%tokentype { Lexeme }
%monad     { Parser } { (>>=) } { return }
%lexer     { lexer } { Located mempty TokEOF }
%error     { parseError }
%token

  integer      { $$@Located { locValue = TokInteger _ } }
  floatlit     { $$@Located { locValue = TokFloat   _ } }
  bitlit       { $$@Located { locValue = TokBitLit _  } }
  identifier   { $$@Located { locValue = TokIdent _   } }
  tyidentifier { $$@Located { locValue = TokTyIdent _ } }
  str          { $$@Located { locValue = TokString _  } }

  -- 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 }
     | {- empty -}        { [] }

----------------------------------------
-- 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 '(' ident '.' ident ',' ident ')' type ident '(' args ')'
    { IncludeProc $9 (unLoc $10) (reverse $12) (unLoc $3 ++ ('.':unLoc $5), unLoc $7)
        (mconcat [ getLoc $3
                 , getLoc $7
                 , getLoc $10
                 ]) }

-- Externally-defined symbols
importExtern :: { Extern }
importExtern :
  extern ident '.' ident type ident
    { Extern (unLoc $6) (unLoc $2 ++ ('.' : unLoc $4)) $5
             (mconcat [ getLoc $2, getLoc $4, getLoc $6])
    }

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 }

----------------------------------------
-- 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) }

  -- Allocation
  | alloc '*' ident               { LocStmt (atBin (AllocRef (AllocBase (unLoc $3) Nothing))
                                               $1 $3) }
  | alloc '*' ident '=' exp       { LocStmt (atList (AllocRef (AllocBase (unLoc $3) (Just $5)))
                                               [$1, getLoc $3, getLoc $5]) }

  | alloc ident '[' ']'           { LocStmt (atBin (AllocRef (AllocArr (unLoc $2) []))
                                               $1 $2) }
  | alloc ident '[' ']' '='
      '{' exps '}'                { LocStmt (atList (AllocRef (AllocArr (unLoc $2) (reverse $7)))
                                               [ $1, getLoc $2, getLoc $7]) }

  | alloc ident '{' '}'           { LocStmt (atBin (AllocRef (AllocStruct (unLoc $2) Empty))
                                               $1 $2) }
  | alloc ident '{' '}' '='
      structInit                  { LocStmt (atBin (AllocRef (AllocStruct (unLoc $2) $6))
                                               $1 $2) }

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

  -- 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]) }
  | upFromTo exp exp ident '{' stmts '}'   { LocStmt (atList (UpFromTo $2 $3 (unLoc $4) (reverse $6))
                                                        [$1, getLoc $2, getLoc $3, getLoc $4, getLoc $6]) }
  | downFrom exp ident '{' stmts '}'       { LocStmt (atList (DownFrom $2 (unLoc $3) (reverse $5))
                                                        [$1, getLoc $2, getLoc $3, getLoc $5]) }
  | downFromTo exp exp ident '{' stmts '}' { LocStmt (atList (DownFromTo $2 $3 (unLoc $4) (reverse $6))
                                                        [$1, getLoc $2, getLoc $3, getLoc $4, getLoc $6]) }
  | 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 (ExpOp NegateOp [$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 :
    simpleCType      { $1 }
  | cType            { $1 }
  | tyident          { LocTy (TySynonym (unLoc $1) `at` $1) }
  | '(' type ')'     { $2 }

-- 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) }

szType :: { Located (Either String Integer) }
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]) }
  | type '[' szType ']'          { LocTy (atBin (TyArray $1 (unLoc $3)) $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 }

--------------------------------------------------------------------------------