Lex.x 42.9 KB
Newer Older
1
{
2 3 4 5 6 7 8 9 10 11 12 13 14 15
{- sv2v
 - Author: Zachary Snow <zach@zachjs.com>
 - Original Lexer Author: Tom Hawkins <tomahawkins@gmail.com>
 -
 - Combined source lexing and preprocessing
 -
 - These procedures are combined so that we can simultaneously process macros in
 - a sane way (something analogous to character-by-character) and have our
 - lexemes properly tagged with source file positions.
 -
 - The scariest piece of this module is the use of `unsafePerformIO`. We want to
 - be able to search for and read files whenever we see an include directive.
 - Trying to thread the IO Monad through alex's interface would be very
 - convoluted. The operations performed are not effectful, and are type safe.
16 17 18 19
 -
 - It may be possible to separate the preprocessor from the lexer by having a
 - preprocessor which produces location annotations. This could improve error
 - messaging and remove the include file and macro boundary hacks.
20
 -}
21

22 23 24 25
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
-- The above pragma gets rid of annoying warning caused by alex 3.2.4. This has
-- been fixed on their development branch, so this can be removed once they roll
-- a new release. (no new release as of 3/29/2018)
26

27 28 29 30
module Language.SystemVerilog.Parser.Lex
    ( lexFile
    , Env
    ) where
31 32 33 34 35

import System.FilePath (dropFileName)
import System.Directory (findFile)
import System.IO.Unsafe (unsafePerformIO)
import qualified Data.Map.Strict as Map
36
import qualified Data.Set as Set
37
import Data.List (span, elemIndex, dropWhileEnd)
Zachary Snow committed
38
import Data.Maybe (isJust, fromJust)
39

40
import Language.SystemVerilog.Parser.Keywords (specMap)
41
import Language.SystemVerilog.Parser.Tokens
42 43
}

44
%wrapper "monadUserState"
45 46 47

-- Numbers

Zachary Snow committed
48 49
@nonZeroDecimalDigit = [1-9]
@decimalDigit = [0-9]
50 51 52 53 54
@xDigit       = [xX]
@zDigit       = [zZ\?]
@binaryDigit  = @xDigit | @zDigit | [0-1]
@octalDigit   = @xDigit | @zDigit | [0-7]
@hexDigit     = @xDigit | @zDigit | [0-9a-fA-F]
55

56 57 58 59
@decimalBase = "'" [sS]? [dD]
@binaryBase  = "'" [sS]? [bB]
@octalBase   = "'" [sS]? [oO]
@hexBase     = "'" [sS]? [hH]
60

Zachary Snow committed
61 62 63 64 65
@nonZeroUnsignedNumber = @nonZeroDecimalDigit ("_" | @decimalDigit)*
@unsignedNumber        = @decimalDigit        ("_" | @decimalDigit)*
@binaryValue           = @binaryDigit         ("_" | @binaryDigit )*
@octalValue            = @octalDigit          ("_" | @octalDigit  )*
@hexValue              = @hexDigit            ("_" | @hexDigit    )*
66

Zachary Snow committed
67
@exp = [eE]
68
@sign = [\-\+]
Zachary Snow committed
69 70 71 72
@fixedPointNumber = @unsignedNumber "." @unsignedNumber
@realNumber
    = @fixedPointNumber
    | @unsignedNumber ("." @unsignedNumber)? @exp @sign? @unsignedNumber
73

Zachary Snow committed
74
@size = @nonZeroUnsignedNumber " "?
75

Zachary Snow committed
76 77 78
@binaryNumber = @size? @binaryBase " "? @binaryValue
@octalNumber  = @size? @octalBase  " "? @octalValue
@hexNumber    = @size? @hexBase    " "? @hexValue
79

80 81
@unbasedUnsizedLiteral = "'" ( 0 | 1 | x | X | z | Z )

Zachary Snow committed
82
@decimalNumber
83
    = @unsignedNumber
Zachary Snow committed
84 85 86 87 88
    | @size? @decimalBase " "? @unsignedNumber
    | @size? @decimalBase " "? @xDigit "_"*
    | @size? @decimalBase " "? @zDigit "_"*
@integralNumber
    = @decimalNumber
89 90 91 92
    | @octalNumber
    | @binaryNumber
    | @hexNumber
    | @unbasedUnsizedLiteral
Zachary Snow committed
93 94
@number
    = @integralNumber
95
    | @realNumber
96 97 98

-- Strings

99
@string = \" (\\\"|\\\r?\n|[^\"\r\n])* \"
100

101 102 103 104 105 106 107
-- Times

@timeUnit = s | ms | us | ns | ps | fs
@time
    = @unsignedNumber @timeUnit
    | @fixedPointNumber @timeUnit

108 109 110 111 112 113
-- Identifiers

@escapedIdentifier = "\" ($printable # $white)+ $white
@simpleIdentifier  = [a-zA-Z_] [a-zA-Z0-9_\$]*
@systemIdentifier = "$" [a-zA-Z0-9_\$]+

114 115
-- Comments

116 117
@commentBlock = "/*"
@commentLine = "//"
118 119 120 121 122 123 124 125 126 127

-- Directives

@directive = "`" @simpleIdentifier

-- Whitespace

@newline = \n
@escapedNewline = \\\n
@whitespace = ($white # \n) | @escapedNewline
128 129 130

tokens :-

131 132 133 134 135 136 137 138 139 140
    "$bits"                { tok KW_dollar_bits                }
    "$dimensions"          { tok KW_dollar_dimensions          }
    "$unpacked_dimensions" { tok KW_dollar_unpacked_dimensions }
    "$left"                { tok KW_dollar_left                }
    "$right"               { tok KW_dollar_right               }
    "$low"                 { tok KW_dollar_low                 }
    "$high"                { tok KW_dollar_high                }
    "$increment"           { tok KW_dollar_increment           }
    "$size"                { tok KW_dollar_size                }

141 142
    "accept_on"        { tok KW_accept_on    }
    "alias"            { tok KW_alias        }
143 144 145 146 147
    "always"           { tok KW_always       }
    "always_comb"      { tok KW_always_comb  }
    "always_ff"        { tok KW_always_ff    }
    "always_latch"     { tok KW_always_latch }
    "and"              { tok KW_and          }
148
    "assert"           { tok KW_assert       }
149
    "assign"           { tok KW_assign       }
150
    "assume"           { tok KW_assume       }
151
    "automatic"        { tok KW_automatic    }
152
    "before"           { tok KW_before       }
153
    "begin"            { tok KW_begin        }
154 155 156
    "bind"             { tok KW_bind         }
    "bins"             { tok KW_bins         }
    "binsof"           { tok KW_binsof       }
157
    "bit"              { tok KW_bit          }
158
    "break"            { tok KW_break        }
159
    "buf"              { tok KW_buf          }
160 161
    "bufif0"           { tok KW_bufif0       }
    "bufif1"           { tok KW_bufif1       }
162 163 164 165
    "byte"             { tok KW_byte         }
    "case"             { tok KW_case         }
    "casex"            { tok KW_casex        }
    "casez"            { tok KW_casez        }
166 167 168 169 170 171 172 173 174 175 176
    "cell"             { tok KW_cell         }
    "chandle"          { tok KW_chandle      }
    "checker"          { tok KW_checker      }
    "class"            { tok KW_class        }
    "clocking"         { tok KW_clocking     }
    "cmos"             { tok KW_cmos         }
    "config"           { tok KW_config       }
    "const"            { tok KW_const        }
    "constraint"       { tok KW_constraint   }
    "context"          { tok KW_context      }
    "continue"         { tok KW_continue     }
177
    "cover"            { tok KW_cover        }
178 179 180 181
    "covergroup"       { tok KW_covergroup   }
    "coverpoint"       { tok KW_coverpoint   }
    "cross"            { tok KW_cross        }
    "deassign"         { tok KW_deassign     }
182 183
    "default"          { tok KW_default      }
    "defparam"         { tok KW_defparam     }
184
    "design"           { tok KW_design       }
185
    "disable"          { tok KW_disable      }
186
    "dist"             { tok KW_dist         }
187
    "do"               { tok KW_do           }
188
    "edge"             { tok KW_edge         }
189 190 191
    "else"             { tok KW_else         }
    "end"              { tok KW_end          }
    "endcase"          { tok KW_endcase      }
192 193 194 195
    "endchecker"       { tok KW_endchecker   }
    "endclass"         { tok KW_endclass     }
    "endclocking"      { tok KW_endclocking  }
    "endconfig"        { tok KW_endconfig    }
196 197
    "endfunction"      { tok KW_endfunction  }
    "endgenerate"      { tok KW_endgenerate  }
198
    "endgroup"         { tok KW_endgroup     }
199 200
    "endinterface"     { tok KW_endinterface }
    "endmodule"        { tok KW_endmodule    }
201
    "endpackage"       { tok KW_endpackage   }
202 203 204 205 206 207
    "endprimitive"     { tok KW_endprimitive }
    "endprogram"       { tok KW_endprogram   }
    "endproperty"      { tok KW_endproperty  }
    "endspecify"       { tok KW_endspecify   }
    "endsequence"      { tok KW_endsequence  }
    "endtable"         { tok KW_endtable     }
208 209
    "endtask"          { tok KW_endtask      }
    "enum"             { tok KW_enum         }
210 211 212
    "event"            { tok KW_event        }
    "eventually"       { tok KW_eventually   }
    "expect"           { tok KW_expect       }
213
    "export"           { tok KW_export       }
214
    "extends"          { tok KW_extends      }
215
    "extern"           { tok KW_extern       }
216
    "final"            { tok KW_final        }
217
    "first_match"      { tok KW_first_match  }
218
    "for"              { tok KW_for          }
219 220
    "force"            { tok KW_force        }
    "foreach"          { tok KW_foreach      }
221
    "forever"          { tok KW_forever      }
222 223
    "fork"             { tok KW_fork         }
    "forkjoin"         { tok KW_forkjoin     }
224 225 226
    "function"         { tok KW_function     }
    "generate"         { tok KW_generate     }
    "genvar"           { tok KW_genvar       }
227 228 229
    "global"           { tok KW_global       }
    "highz0"           { tok KW_highz0       }
    "highz1"           { tok KW_highz1       }
230
    "if"               { tok KW_if           }
231
    "iff"              { tok KW_iff          }
232 233 234 235 236
    "ifnone"           { tok KW_ifnone       }
    "ignore_bins"      { tok KW_ignore_bins  }
    "illegal_bins"     { tok KW_illegal_bins }
    "implements"       { tok KW_implements   }
    "implies"          { tok KW_implies      }
237
    "import"           { tok KW_import       }
238 239
    "incdir"           { tok KW_incdir       }
    "include"          { tok KW_include      }
240 241 242
    "initial"          { tok KW_initial      }
    "inout"            { tok KW_inout        }
    "input"            { tok KW_input        }
243 244
    "inside"           { tok KW_inside       }
    "instance"         { tok KW_instance     }
245 246
    "int"              { tok KW_int          }
    "integer"          { tok KW_integer      }
247
    "interconnect"     { tok KW_interconnect }
248
    "interface"        { tok KW_interface    }
249
    "intersect"        { tok KW_intersect    }
250 251 252 253 254 255 256 257
    "join"             { tok KW_join         }
    "join_any"         { tok KW_join_any     }
    "join_none"        { tok KW_join_none    }
    "large"            { tok KW_large        }
    "let"              { tok KW_let          }
    "liblist"          { tok KW_liblist      }
    "library"          { tok KW_library      }
    "local"            { tok KW_local        }
258 259 260
    "localparam"       { tok KW_localparam   }
    "logic"            { tok KW_logic        }
    "longint"          { tok KW_longint      }
261 262 263
    "macromodule"      { tok KW_macromodule  }
    "matches"          { tok KW_matches      }
    "medium"           { tok KW_medium       }
264 265 266 267
    "modport"          { tok KW_modport      }
    "module"           { tok KW_module       }
    "nand"             { tok KW_nand         }
    "negedge"          { tok KW_negedge      }
268 269 270 271
    "nettype"          { tok KW_nettype      }
    "new"              { tok KW_new          }
    "nexttime"         { tok KW_nexttime     }
    "nmos"             { tok KW_nmos         }
272
    "nor"              { tok KW_nor          }
273
    "noshowcancelled"  { tok KW_noshowcancelled }
274
    "not"              { tok KW_not          }
275 276 277
    "notif0"           { tok KW_notif0       }
    "notif1"           { tok KW_notif1       }
    "null"             { tok KW_null         }
278 279
    "or"               { tok KW_or           }
    "output"           { tok KW_output       }
280
    "package"          { tok KW_package      }
281 282
    "packed"           { tok KW_packed       }
    "parameter"        { tok KW_parameter    }
283
    "pmos"             { tok KW_pmos         }
284
    "posedge"          { tok KW_posedge      }
285
    "primitive"        { tok KW_primitive    }
286
    "priority"         { tok KW_priority     }
287
    "program"          { tok KW_program      }
288
    "property"         { tok KW_property     }
289 290 291 292 293 294 295 296 297 298 299 300 301
    "protected"        { tok KW_protected    }
    "pull0"            { tok KW_pull0        }
    "pull1"            { tok KW_pull1        }
    "pulldown"         { tok KW_pulldown     }
    "pullup"           { tok KW_pullup       }
    "pulsestyle_ondetect" { tok KW_pulsestyle_ondetect }
    "pulsestyle_onevent"  { tok KW_pulsestyle_onevent }
    "pure"             { tok KW_pure         }
    "rand"             { tok KW_rand         }
    "randc"            { tok KW_randc        }
    "randcase"         { tok KW_randcase     }
    "randsequence"     { tok KW_randsequence }
    "rcmos"            { tok KW_rcmos        }
302 303
    "real"             { tok KW_real         }
    "realtime"         { tok KW_realtime     }
304
    "ref"              { tok KW_ref          }
305
    "reg"              { tok KW_reg          }
306 307
    "reject_on"        { tok KW_reject_on    }
    "release"          { tok KW_release      }
308
    "repeat"           { tok KW_repeat       }
309
    "restrict"         { tok KW_restrict     }
310
    "return"           { tok KW_return       }
311 312 313 314 315 316 317 318 319 320 321 322
    "rnmos"            { tok KW_rnmos        }
    "rpmos"            { tok KW_rpmos        }
    "rtran"            { tok KW_rtran        }
    "rtranif0"         { tok KW_rtranif0     }
    "rtranif1"         { tok KW_rtranif1     }
    "s_always"         { tok KW_s_always     }
    "s_eventually"     { tok KW_s_eventually }
    "s_nexttime"       { tok KW_s_nexttime   }
    "s_until"          { tok KW_s_until      }
    "s_until_with"     { tok KW_s_until_with }
    "scalared"         { tok KW_scalared     }
    "sequence"         { tok KW_sequence     }
323 324
    "shortint"         { tok KW_shortint     }
    "shortreal"        { tok KW_shortreal    }
325
    "showcancelled"    { tok KW_showcancelled }
326
    "signed"           { tok KW_signed       }
327 328 329 330 331
    "small"            { tok KW_small        }
    "soft"             { tok KW_soft         }
    "solve"            { tok KW_solve        }
    "specify"          { tok KW_specify      }
    "specparam"        { tok KW_specparam    }
332
    "static"           { tok KW_static       }
333 334 335 336
    "string"           { tok KW_string       }
    "strong"           { tok KW_strong       }
    "strong0"          { tok KW_strong0      }
    "strong1"          { tok KW_strong1      }
337
    "struct"           { tok KW_struct       }
338
    "super"            { tok KW_super        }
339 340
    "supply0"          { tok KW_supply0      }
    "supply1"          { tok KW_supply1      }
341 342 343 344
    "sync_accept_on"   { tok KW_sync_accept_on }
    "sync_reject_on"   { tok KW_sync_reject_on }
    "table"            { tok KW_table        }
    "tagged"           { tok KW_tagged       }
345
    "task"             { tok KW_task         }
346
    "this"             { tok KW_this         }
347
    "throughout"       { tok KW_throughout   }
348
    "time"             { tok KW_time         }
349 350 351 352 353
    "timeprecision"    { tok KW_timeprecision }
    "timeunit"         { tok KW_timeunit     }
    "tran"             { tok KW_tran         }
    "tranif0"          { tok KW_tranif0      }
    "tranif1"          { tok KW_tranif1      }
354 355 356 357 358 359
    "tri"              { tok KW_tri          }
    "tri0"             { tok KW_tri0         }
    "tri1"             { tok KW_tri1         }
    "triand"           { tok KW_triand       }
    "trior"            { tok KW_trior        }
    "trireg"           { tok KW_trireg       }
360
    "type"             { tok KW_type         }
361
    "typedef"          { tok KW_typedef      }
362
    "union"            { tok KW_union        }
363
    "unique"           { tok KW_unique       }
364
    "unique0"          { tok KW_unique0      }
365
    "unsigned"         { tok KW_unsigned     }
366 367 368 369
    "until"            { tok KW_until        }
    "until_with"       { tok KW_until_with   }
    "untyped"          { tok KW_untyped      }
    "use"              { tok KW_use          }
370
    "uwire"            { tok KW_uwire        }
371 372 373 374 375 376
    "var"              { tok KW_var          }
    "vectored"         { tok KW_vectored     }
    "virtual"          { tok KW_virtual      }
    "void"             { tok KW_void         }
    "wait"             { tok KW_wait         }
    "wait_order"       { tok KW_wait_order   }
377
    "wand"             { tok KW_wand         }
378 379 380
    "weak"             { tok KW_weak         }
    "weak0"            { tok KW_weak0        }
    "weak1"            { tok KW_weak1        }
381
    "while"            { tok KW_while        }
382
    "wildcard"         { tok KW_wildcard     }
383
    "wire"             { tok KW_wire         }
384
    "with"             { tok KW_with         }
385
    "within"           { tok KW_within       }
386 387 388 389 390 391 392 393 394 395
    "wor"              { tok KW_wor          }
    "xnor"             { tok KW_xnor         }
    "xor"              { tok KW_xor          }

    @simpleIdentifier  { tok Id_simple  }
    @escapedIdentifier { tok Id_escaped }
    @systemIdentifier  { tok Id_system  }

    @number            { tok Lit_number }
    @string            { tok Lit_string }
396
    @time              { tok Lit_time }
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

    "("                { tok Sym_paren_l }
    ")"                { tok Sym_paren_r }
    "["                { tok Sym_brack_l }
    "]"                { tok Sym_brack_r }
    "{"                { tok Sym_brace_l }
    "}"                { tok Sym_brace_r }
    "~"                { tok Sym_tildy }
    "!"                { tok Sym_bang }
    "@"                { tok Sym_at }
    "#"                { tok Sym_pound }
    "%"                { tok Sym_percent }
    "^"                { tok Sym_hat }
    "&"                { tok Sym_amp }
    "|"                { tok Sym_bar }
    "*"                { tok Sym_aster }
    "."                { tok Sym_dot }
    ","                { tok Sym_comma }
    ":"                { tok Sym_colon }
    ";"                { tok Sym_semi }
    "="                { tok Sym_eq }
    "<"                { tok Sym_lt }
    ">"                { tok Sym_gt }
    "+"                { tok Sym_plus }
    "-"                { tok Sym_dash }
    "?"                { tok Sym_question }
    "/"                { tok Sym_slash }
    "$"                { tok Sym_dollar }
    "'"                { tok Sym_s_quote }

    "~&"               { tok Sym_tildy_amp }
    "~|"               { tok Sym_tildy_bar }
    "~^"               { tok Sym_tildy_hat }
    "^~"               { tok Sym_hat_tildy }
    "=="               { tok Sym_eq_eq }
    "!="               { tok Sym_bang_eq }
    "&&"               { tok Sym_amp_amp }
    "||"               { tok Sym_bar_bar }
    "**"               { tok Sym_aster_aster }
    "<="               { tok Sym_lt_eq }
    ">="               { tok Sym_gt_eq }
    ">>"               { tok Sym_gt_gt }
    "<<"               { tok Sym_lt_lt }
    "++"               { tok Sym_plus_plus }
    "--"               { tok Sym_dash_dash }
    "+="               { tok Sym_plus_eq }
    "-="               { tok Sym_dash_eq }
    "*="               { tok Sym_aster_eq }
    "/="               { tok Sym_slash_eq }
    "%="               { tok Sym_percent_eq }
    "&="               { tok Sym_amp_eq }
    "|="               { tok Sym_bar_eq }
    "^="               { tok Sym_hat_eq }
    "+:"               { tok Sym_plus_colon }
    "-:"               { tok Sym_dash_colon }
    "::"               { tok Sym_colon_colon }
    ".*"               { tok Sym_dot_aster }
    "->"               { tok Sym_dash_gt }
    ":="               { tok Sym_colon_eq }
    ":/"               { tok Sym_colon_slash }
    "##"               { tok Sym_pound_pound }
    "[*"               { tok Sym_brack_l_aster }
    "[="               { tok Sym_brack_l_eq }
    "=>"               { tok Sym_eq_gt }
    "@*"               { tok Sym_at_aster }
    "(*"               { tok Sym_paren_l_aster }
    "*)"               { tok Sym_aster_paren_r }
    "*>"               { tok Sym_aster_gt }

    "==="              { tok Sym_eq_eq_eq }
    "!=="              { tok Sym_bang_eq_eq }
    "==?"              { tok Sym_eq_eq_question }
    "!=?"              { tok Sym_bang_eq_question }
    ">>>"              { tok Sym_gt_gt_gt }
    "<<<"              { tok Sym_lt_lt_lt }
    "<<="              { tok Sym_lt_lt_eq }
    ">>="              { tok Sym_gt_gt_eq }
474
    "<->"              { tok Sym_lt_dash_gt }
475 476 477
    "|->"              { tok Sym_bar_dash_gt }
    "|=>"              { tok Sym_bar_eq_gt }
    "[->"              { tok Sym_brack_l_dash_gt }
478 479
    "#-#"              { tok Sym_pound_dash_pound }
    "#=#"              { tok Sym_pound_eq_pound }
480 481 482 483 484 485 486 487
    "@@("              { tok Sym_at_at_paren_l }
    "(*)"              { tok Sym_paren_l_aster_paren_r }
    "->>"              { tok Sym_dash_gt_gt }
    "&&&"              { tok Sym_amp_amp_amp }

    "<<<="             { tok Sym_lt_lt_lt_eq }
    ">>>="             { tok Sym_gt_gt_gt_eq }

488
    @directive         { handleDirective }
489 490 491
    @commentLine       { removeUntil "\n" }
    @commentBlock      { removeUntil "*/" }

492
    $white             ;
493 494

    .                  { tok Unknown }
495 496

{
497

Zachary Snow committed
498 499 500 501
-- our actions don't return any data
type Action = AlexInput -> Int -> Alex ()

-- keeps track of the state of an if-else cascade level
502 503 504 505 506
data Cond
    = CurrentlyTrue
    | PreviouslyTrue
    | NeverTrue
    deriving (Eq, Show)
507

508 509 510
-- map from macro to definition, plus arguments
type Env = Map.Map String (String, [(String, Maybe String)])

Zachary Snow committed
511
-- our custom lexer state
512
data AlexUserState = LS
513
    { lsToks         :: [Token] -- tokens read so far, *in reverse order* for efficiency
Zachary Snow committed
514
    , lsCurrFile     :: FilePath -- currently active filename
515
    , lsEnv          :: Env -- active macro definitions
Zachary Snow committed
516 517
    , lsCondStack    :: [Cond] -- if-else cascade state
    , lsIncludePaths :: [FilePath] -- folders to search for includes
518
    , lsSpecStack    :: [Set.Set TokenName] -- stack of non-keyword token names
519
    } deriving (Eq, Show)
520

Zachary Snow committed
521 522 523
-- this initial user state does not contain the initial filename, environment,
-- or include paths; alex requires that this be defined; we override it before
-- we begin the actual lexing procedure
524
alexInitUserState :: AlexUserState
525
alexInitUserState = LS [] "" Map.empty [] [] []
526

Zachary Snow committed
527
-- public-facing lexer entrypoint
528
lexFile :: [String] -> Env -> FilePath -> IO (Either String ([Token], Env))
529
lexFile includePaths env path = do
530 531 532 533
    str <-
        if path == "-"
            then getContents
            else readFile path >>= return . normalize
534 535
    let result = runAlex str $ setEnv >> alexMonadScan >> get
    return $ case result of
536
        Left msg -> Left msg
Zachary Snow committed
537
        Right finalState ->
538 539 540 541 542 543 544 545
            if not $ null $ lsCondStack finalState then
                Left $ path ++ ": unfinished conditional directives: " ++
                    (show $ length $ lsCondStack finalState)
            else if not $ null $ lsSpecStack finalState then
                Left $ path ++ ": unterminated begin_keywords blocks: " ++
                    (show $ length $ lsSpecStack finalState)
            else
                Right (finalToks, lsEnv finalState)
546 547 548
            where
                finalToks = coalesce $ combineBoundaries $
                    reverse $ lsToks finalState
549
    where
Zachary Snow committed
550 551
        setEnv = do
            modify $ \s -> s
552
                { lsEnv = env
Zachary Snow committed
553
                , lsIncludePaths = includePaths
554
                , lsCurrFile = path
Zachary Snow committed
555
                }
556

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
-- combines identifiers and numbers that cross macro boundaries
coalesce :: [Token] -> [Token]
coalesce [] = []
coalesce (Token MacroBoundary _ _ : rest) = coalesce rest
coalesce (Token t1 str1 pn1 : Token MacroBoundary _ _ : Token t2 str2 pn2 : rest) =
    case (t1, t2, immediatelyFollows) of
        (Lit_number, Lit_number, _) ->
            Token t1 (str1 ++ str2) pn1 : (coalesce rest)
        (Id_simple, Id_simple, True) ->
            Token t1 (str1 ++ str2) pn1 : (coalesce rest)
        _ ->
            Token t1 str1 pn1 : (coalesce $ Token t2 str2 pn2 : rest)
    where
        Position _ l1 c1 = pn1
        Position _ l2 c2 = pn2
        apn1 = AlexPn 0 l1 c1
        apn2 = AlexPn (length str1) l2 c2
        immediatelyFollows = apn2 == foldl alexMove apn1 str1
coalesce (x : xs) = x : coalesce xs

577 578 579 580 581 582
combineBoundaries :: [Token] -> [Token]
combineBoundaries [] = []
combineBoundaries (Token MacroBoundary s p : Token MacroBoundary _ _ : rest) =
    combineBoundaries $ Token MacroBoundary s p : rest
combineBoundaries (x : xs) = x : combineBoundaries xs

Zachary Snow committed
583 584 585 586 587 588 589
-- invoked by alexMonadScan
alexEOF :: Alex ()
alexEOF = return ()

-- raises an alexError with the current file position appended
lexicalError :: String -> Alex a
lexicalError msg = do
590 591
    (pn, _, _, _) <- alexGetInput
    pos <- toTokPos pn
592
    alexError $ show pos ++ ": Lexical error: " ++ msg
Zachary Snow committed
593 594

-- get the current user state
595 596 597
get :: Alex AlexUserState
get = Alex $ \s -> Right (s, alex_ust s)

Zachary Snow committed
598
-- get the current user state and apply a function to it
599 600 601
gets :: (AlexUserState -> a) -> Alex a
gets f = get >>= return . f

Zachary Snow committed
602
-- apply a transformation to the current user state
603 604 605 606 607
modify :: (AlexUserState -> AlexUserState) -> Alex ()
modify f = Alex func
    where func s = Right (s { alex_ust = new }, ())
            where new = f (alex_ust s)

Zachary Snow committed
608
-- helpers specifically accessing the current file state
609 610 611 612 613
getCurrentFile :: Alex String
getCurrentFile = gets lsCurrFile
setCurrentFile :: String -> Alex ()
setCurrentFile x = modify $ \s -> s { lsCurrFile = x }

Zachary Snow committed
614
-- find the given file for inclusion
615 616 617 618 619 620 621 622
includeSearch :: FilePath -> Alex FilePath
includeSearch file = do
    base <- getCurrentFile
    includePaths <- gets lsIncludePaths
    let directories = dropFileName base : includePaths
    let result = unsafePerformIO $ findFile directories file
    case result of
        Just path -> return path
Zachary Snow committed
623 624
        Nothing -> lexicalError $ "Could not find file " ++ show file ++
                        ", included from " ++ show base
625

Zachary Snow committed
626 627
-- read in the given file
loadFile :: FilePath -> Alex String
628 629 630 631 632 633 634
loadFile = return . normalize . unsafePerformIO . readFile

-- removes carriage returns before newlines
normalize :: String -> String
normalize ('\r' : '\n' : rest) = '\n' : (normalize rest)
normalize (ch : chs) = ch : (normalize chs)
normalize [] = []
635 636 637 638 639 640 641 642 643 644

isIdentChar :: Char -> Bool
isIdentChar ch =
    ('a' <= ch && ch <= 'z') ||
    ('A' <= ch && ch <= 'Z') ||
    ('0' <= ch && ch <= '9') ||
    (ch == '_') || (ch == '$')

takeString :: Alex String
takeString = do
Zachary Snow committed
645
    (pos, _, _, str) <- alexGetInput
646
    let (x, rest) = span isIdentChar str
Zachary Snow committed
647 648
    let lastChar = if null x then ' ' else last x
    alexSetInput (foldl alexMove pos x, lastChar, [], rest)
649 650
    return x

651 652
toTokPos :: AlexPosn -> Alex Position
toTokPos (AlexPn _ l c) = do
653 654 655 656 657 658
    file <- getCurrentFile
    return $ Position file l c

-- read tokens after the name until the first (un-escaped) newline
takeUntilNewline :: Alex String
takeUntilNewline = do
Zachary Snow committed
659
    (pos, _, _, str) <- alexGetInput
660 661 662 663
    case str of
        []                 -> return ""
        '\n' :        _    -> do
            return ""
664 665 666 667 668
        '/' : '/' : _ -> do
            remainder <- takeThrough '\n'
            case last $ init remainder of
                '\\' -> takeUntilNewline >>= return . (' ' :)
                _ -> return ""
669
        '\\' : '\n' : rest -> do
Zachary Snow committed
670 671
            let newPos = alexMove (alexMove pos '\\') '\n'
            alexSetInput (newPos, '\n', [], rest)
672 673
            takeUntilNewline >>= return . (' ' :)
        ch   :        rest -> do
Zachary Snow committed
674 675
            let newPos = alexMove pos ch
            alexSetInput (newPos, ch, [], rest)
676 677
            takeUntilNewline >>= return . (ch :)

Zachary Snow committed
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
-- select characters up to and including the given character
takeThrough :: Char -> Alex String
takeThrough goal = do
    (_, _, _, str) <- alexGetInput
    if null str
        then lexicalError $
                "unexpected end of input, looking for " ++ (show goal)
        else do
            ch <- takeChar
            if ch == goal
                then return [ch]
                else do
                    rest <- takeThrough goal
                    return $ ch : rest

-- pop one character from the input stream
takeChar :: Alex Char
takeChar = do
696 697 698 699 700
    (pos, _, _, str) <- alexGetInput
    (ch, chs) <-
        if null str
            then lexicalError "unexpected end of input"
            else return (head str, tail str)
Zachary Snow committed
701
    let newPos = alexMove pos ch
702
    alexSetInput (newPos, ch, [], chs)
Zachary Snow committed
703 704 705 706 707
    return ch

-- drop spaces in the input until a non-space is reached or EOF
dropSpaces :: Alex ()
dropSpaces = do
708
    (pos, _, _, str) <- alexGetInput
709 710 711 712 713 714
    if null str then
        return ()
    else do
        let ch : rest = str
        if ch == '\t' || ch == ' ' then do
            alexSetInput (alexMove pos ch, ch, [], tail str)
715
            dropSpaces
716 717
        else
            return ()
Zachary Snow committed
718

Zachary Snow committed
719 720 721
isWhitespaceChar :: Char -> Bool
isWhitespaceChar ch = elem ch [' ', '\t', '\n']

722
-- drop all leading whitespace in the input
Zachary Snow committed
723 724
dropWhitespace :: Alex ()
dropWhitespace = do
725 726 727 728 729 730 731 732 733
    (pos, _, _, str) <- alexGetInput
    case str of
        ch : chs ->
            if isWhitespaceChar ch
                then do
                    alexSetInput (alexMove pos ch, ch, [], chs)
                    dropWhitespace
                else return()
        [] -> return ()
Zachary Snow committed
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750

-- removes and returns a quoted string such as <foo.bar> or "foo.bar"
takeQuotedString :: Alex String
takeQuotedString = do
    dropSpaces
    ch <- takeChar
    end <-
        case ch of
            '"' -> return '"'
            '<' -> return '>'
            _ -> lexicalError $ "bad beginning of include arg: " ++ (show ch)
    rest <- takeThrough end
    let res = ch : rest
    if end == '>'
        then lexicalError $ "library includes are not supported: " ++ res
        else return res

751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
-- removes and returns a decimal number
takeNumber :: Alex Int
takeNumber = do
    dropSpaces
    leadCh <- peekChar
    if '0' <= leadCh && leadCh <= '9'
        then step 0
        else lexicalError $ "expected number, but found unexpected char: "
                ++ show leadCh
    where
        step number = do
            ch <- takeChar
            if ch == ' ' || ch == '\n' then
                return number
            else if '0' <= ch && ch <= '9' then do
                let digit = ord ch - ord '0'
                step $ number * 10 + digit
            else
                lexicalError $ "unexpected char while reading number: "
                    ++ show ch

772 773 774 775 776 777 778
peekChar :: Alex Char
peekChar = do
    (_, _, _, str) <- alexGetInput
    return $ if null str
        then '\n'
        else head str

Zachary Snow committed
779
takeMacroDefinition :: Alex (String, [(String, Maybe String)])
780 781 782 783 784 785 786
takeMacroDefinition = do
    leadCh <- peekChar
    if leadCh /= '('
        then do
            body <- takeUntilNewline
            return (body, [])
        else do
Zachary Snow committed
787
            args <- takeMacroArguments
788
            body <- takeUntilNewline
Zachary Snow committed
789
            argsWithDefaults <- mapM splitArg args
790 791
            if null args
                then lexicalError "macros cannot have 0 args"
Zachary Snow committed
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807
                else return (body, argsWithDefaults)
    where
        splitArg :: String -> Alex (String, Maybe String)
        splitArg [] = lexicalError "macro defn. empty argument"
        splitArg str = do
            let (name, rest) = span isIdentChar str
            if null name || not (all isIdentChar name) then
                lexicalError $ "invalid macro arg name: " ++ show name
            else if null rest then
                return (name, Nothing)
            else do
                let trimmed = dropWhile isWhitespaceChar rest
                let leadCh = head trimmed
                if leadCh /= '='
                then lexicalError $ "bad char after arg name: " ++ (show leadCh)
                else return (name, Just $ tail trimmed)
808

809 810
-- commas and right parens are forbidden outside matched pairs of: (), [], {},
-- "", except to delimit arguments or end the list of arguments; see 22.5.1
811 812
takeMacroArguments :: Alex [String]
takeMacroArguments = do
813
    dropWhitespace
814 815 816 817 818
    leadCh <- takeChar
    if leadCh == '('
        then argLoop
        else lexicalError $ "expected begining of macro arguments, but found "
                ++ show leadCh
819
    where
Zachary Snow committed
820 821 822 823 824 825 826 827 828 829 830
        argLoop :: Alex [String]
        argLoop = do
            dropWhitespace
            (arg, isEnd) <- loop "" []
            let arg' = dropWhileEnd isWhitespaceChar arg
            if isEnd
                then return [arg']
                else do
                    rest <- argLoop
                    return $ arg' : rest
        loop :: String -> [Char] -> Alex (String, Bool)
831 832 833 834 835
        loop curr stack = do
            ch <- takeChar
            case (stack, ch) of
                (      s,'\\') -> do
                    ch2 <- takeChar
836
                    loop (curr ++ [ch, ch2]) s
Zachary Snow committed
837 838
                ([     ], ',') -> return (curr, False)
                ([     ], ')') -> return (curr, True)
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862

                ('"' : s, '"') -> loop (curr ++ [ch]) s
                (      s, '"') -> loop (curr ++ [ch]) ('"' : s)
                ('[' : s, ']') -> loop (curr ++ [ch]) s
                (      s, '[') -> loop (curr ++ [ch]) ('[' : s)
                ('(' : s, ')') -> loop (curr ++ [ch]) s
                (      s, '(') -> loop (curr ++ [ch]) ('(' : s)
                ('{' : s, '}') -> loop (curr ++ [ch]) s
                (      s, '{') -> loop (curr ++ [ch]) ('{' : s)

                (      s,'\n') -> loop (curr ++ [' ']) s
                (      s, _  ) -> loop (curr ++ [ch ]) s

findUnescapedQuote :: String -> (String, String)
findUnescapedQuote [] = ([], [])
findUnescapedQuote ('`' : '\\' : '`' : '"' : rest) = ('\\' : '"' : start, end)
    where (start, end) = findUnescapedQuote rest
findUnescapedQuote ('\\' : '"' : rest) = ('\\' : '"' : start, end)
    where (start, end) = findUnescapedQuote rest
findUnescapedQuote ('"' : rest) = ("\"", rest)
findUnescapedQuote (ch : rest) = (ch : start, end)
    where (start, end) = findUnescapedQuote rest

-- substitute in the arguments for a macro expension
863 864
substituteArgs :: String -> [String] -> [String] -> String
substituteArgs "" _ _ = ""
865 866 867 868 869
substituteArgs ('`' : '`' : body) names args =
    substituteArgs body names args
substituteArgs ('"' : body) names args =
    '"' : start ++ substituteArgs rest names args
    where (start, rest) = findUnescapedQuote body
Zachary Snow committed
870 871
substituteArgs ('\\' : '"' : body) names args =
    '\\' : '"' : substituteArgs body names args
872 873 874 875
substituteArgs ('`' : '"' : body) names args =
    '"' : substituteArgs (init start) names args
    ++ '"' : substituteArgs rest names args
    where (start, rest) = findUnescapedQuote body
876
substituteArgs body names args =
Zachary Snow committed
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896
    case span isIdentChar body of
        ([], _) -> head body : substituteArgs (tail body) names args
        (ident, rest) ->
            case elemIndex ident names of
                Nothing -> ident ++ substituteArgs rest names args
                Just idx -> (args !! idx) ++ substituteArgs rest names args

defaultMacroArgs :: [Maybe String] -> [String] -> Alex [String]
defaultMacroArgs [] [] = return []
defaultMacroArgs [] _ = lexicalError "too many macro arguments given"
defaultMacroArgs defaults [] = do
    if all isJust defaults
        then return $ map fromJust defaults
        else lexicalError "too few macro arguments given"
defaultMacroArgs (f : fs) (a : as) = do
    let arg = if a == "" && isJust f
            then fromJust f
            else a
    args <- defaultMacroArgs fs as
    return $ arg : args
897

Zachary Snow committed
898 899 900 901 902 903
-- directives that must always be processed even if the current code block is
-- being excluded; we have to process conditions so we can match them up with
-- their ending tag, even if they're being skipped
unskippableDirectives :: [String]
unskippableDirectives = ["else", "elsif", "endif", "ifdef", "ifndef"]

904
handleDirective :: Action
Zachary Snow committed
905 906 907 908 909
handleDirective (posOrig, _, _, strOrig) len = do
    let thisTokenStr = take len strOrig
    let directive = tail $ thisTokenStr
    let newPos = foldl alexMove posOrig thisTokenStr
    alexSetInput (newPos, last thisTokenStr, [], drop len strOrig)
910 911 912 913

    env <- gets lsEnv
    tempInput <- alexGetInput
    let dropUntilNewline = removeUntil "\n" tempInput 0
914 915 916 917
    let passThrough = do
            rest <- takeUntilNewline
            let str = '`' : directive ++ rest
            tok Spe_Directive (posOrig, ' ', [], strOrig) (length str)
918 919

    condStack <- gets lsCondStack
Zachary Snow committed
920
    if any (/= CurrentlyTrue) condStack
921 922 923 924 925 926
        && not (elem directive unskippableDirectives)
    then alexMonadScan
    else case directive of

        "timescale" -> dropUntilNewline

927 928 929 930 931 932 933
        "celldefine" -> passThrough
        "endcelldefine" -> passThrough

        "unconnected_drive" -> passThrough
        "nounconnected_drive" -> passThrough

        "default_nettype" -> passThrough
934 935 936 937 938
        "pragma" -> do
            leadCh <- peekChar
            if leadCh == '\n' || leadCh == '\r'
                then lexicalError "pragma directive cannot be empty"
                else passThrough
939 940
        "resetall" -> passThrough

941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961
        "begin_keywords" -> do
            quotedSpec <- takeQuotedString
            let spec = tail $ init quotedSpec
            case Map.lookup spec specMap of
                Nothing ->
                    lexicalError $ "invalid keyword set name: " ++ show spec
                Just set -> do
                    specStack <- gets lsSpecStack
                    modify $ \s -> s { lsSpecStack = set : specStack }
                    dropWhitespace
                    alexMonadScan
        "end_keywords" -> do
            specStack <- gets lsSpecStack
            if null specStack
                then
                    lexicalError "unexpected end_keywords before begin_keywords"
                else do
                    modify $ \s -> s { lsSpecStack = tail specStack }
                    dropWhitespace
                    alexMonadScan

962 963 964 965 966 967 968 969 970 971 972 973 974
        "__FILE__" -> do
            tokPos <- toTokPos posOrig
            currFile <- gets lsCurrFile
            let tokStr = show currFile
            modify $ push $ Token Lit_string tokStr tokPos
            alexMonadScan
        "__LINE__" -> do
            tokPos <- toTokPos posOrig
            let Position _ currLine _ = tokPos
            let tokStr = show currLine
            modify $ push $ Token Lit_number tokStr tokPos
            alexMonadScan

975 976 977
        "line" -> do
            lineNumber <- takeNumber
            quotedFilename <- takeQuotedString
978
            levelNumber <- takeNumber -- level, ignored
979 980 981 982
            let filename = init $ tail quotedFilename
            setCurrentFile filename
            (AlexPn f _ c, prev, _, str) <- alexGetInput
            alexSetInput (AlexPn f (lineNumber + 1) c, prev, [], str)
983 984 985
            if 0 <= levelNumber && levelNumber <= 2
                then alexMonadScan
                else lexicalError "line directive invalid level number"
986

Zachary Snow committed
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
        "include" -> do
            quotedFilename <- takeQuotedString
            inputFollow <- alexGetInput
            fileFollow <- getCurrentFile
            -- process the included file
            let filename = init $ tail quotedFilename
            path <- includeSearch filename
            content <- loadFile path
            let inputIncluded = (alexStartPos, ' ', [], content)
            setCurrentFile path
            alexSetInput inputIncluded
            alexMonadScan
            -- resume processing the original file
            setCurrentFile fileFollow
            alexSetInput inputFollow
            alexMonadScan

1004
        "ifdef" -> do
Zachary Snow committed
1005
            dropSpaces
1006 1007 1008 1009 1010 1011 1012
            name <- takeString
            let newCond = if Map.member name env
                    then CurrentlyTrue
                    else NeverTrue
            modify $ \s -> s { lsCondStack = newCond : condStack }
            alexMonadScan
        "ifndef" -> do
Zachary Snow committed
1013
            dropSpaces
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
            name <- takeString
            let newCond = if Map.notMember name env
                    then CurrentlyTrue
                    else NeverTrue
            modify $ \s -> s { lsCondStack = newCond : condStack }
            alexMonadScan
        "else" -> do
            let newCond = if head condStack == NeverTrue
                    then CurrentlyTrue
                    else NeverTrue
            modify $ \s -> s { lsCondStack = newCond : tail condStack }
            alexMonadScan
        "elsif" -> do
Zachary Snow committed
1027
            dropSpaces
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
            name <- takeString
            let currCond = head condStack
            let newCond =
                    if currCond /= NeverTrue then
                        PreviouslyTrue
                    else if Map.member name env then
                        CurrentlyTrue
                    else
                        NeverTrue
            modify $ \s -> s { lsCondStack = newCond : tail condStack }
            alexMonadScan
        "endif" -> do
            modify $ \s -> s { lsCondStack = tail condStack }
            alexMonadScan

        "define" -> do
Zachary Snow committed
1044
            dropSpaces
1045
            name <- takeString
1046
            defn <- takeMacroDefinition
1047 1048 1049
            modify $ \s -> s { lsEnv = Map.insert name defn env }
            alexMonadScan
        "undef" -> do
Zachary Snow committed
1050
            dropSpaces
1051 1052 1053 1054 1055 1056 1057 1058 1059
            name <- takeString
            modify $ \s -> s { lsEnv = Map.delete name env }
            alexMonadScan
        "undefineall" -> do
            modify $ \s -> s { lsEnv = Map.empty }
            alexMonadScan

        _ -> do
            case Map.lookup directive env of
Zachary Snow committed
1060
                Nothing -> lexicalError $ "Undefined macro: " ++ directive
1061
                Just (body, formalArgs) -> do
1062
                    (AlexPn _ l c, _, _, _) <- alexGetInput
1063 1064 1065 1066
                    replacement <- if null formalArgs
                        then return body
                        else do
                            actualArgs <- takeMacroArguments
Zachary Snow committed
1067 1068
                            defaultedArgs <- defaultMacroArgs (map snd formalArgs) actualArgs
                            return $ substituteArgs body (map fst formalArgs) defaultedArgs
1069 1070 1071 1072 1073
                    -- save our current state
                    currInput <- alexGetInput
                    currToks <- gets lsToks
                    modify $ \s -> s { lsToks = [] }
                    -- lex the macro expansion, preserving the file and line
1074
                    alexSetInput (AlexPn 0 l 0, ' ', [], replacement)
1075 1076 1077 1078 1079 1080 1081
                    alexMonadScan
                    -- re-tag and save tokens from the macro expansion
                    newToks <- gets lsToks
                    currFile <- getCurrentFile
                    let loc = "macro expansion of " ++ directive ++ " at " ++ currFile
                    let pos = Position loc l (c - length directive - 1)
                    let reTag (Token a b _) = Token a b pos
1082 1083 1084
                    let boundary = Token MacroBoundary "" (Position "" 0 0)
                    let boundedToks = boundary : (map reTag newToks) ++ boundary : currToks
                    modify $ \s -> s { lsToks = boundedToks }
1085 1086
                    -- continue lexing after the macro
                    alexSetInput currInput
1087 1088
                    alexMonadScan

1089 1090 1091 1092 1093 1094 1095
-- remove characters from the input until the pattern is reached
removeUntil :: String -> Action
removeUntil pattern _ _ = loop
    where
        patternLen = length pattern
        wantNewline = pattern == "\n"
        loop = do
Zachary Snow committed
1096
            (pos, _, _, str) <- alexGetInput
1097 1098
            let found = (null str && wantNewline)
                     || pattern == take patternLen str
Zachary Snow committed
1099
            let nextPos = alexMove pos (head str)
1100
            let afterPos = if wantNewline
Zachary Snow committed
1101 1102
                    then alexMove pos '\n'
                    else foldl alexMove pos pattern
1103 1104 1105
            let (newPos, newStr) = if found
                    then (afterPos, drop patternLen str)
                    else (nextPos, drop 1 str)
Zachary Snow committed
1106 1107 1108 1109 1110 1111 1112 1113
            if not found && null str
                then lexicalError $ "Reached EOF while looking for: " ++
                        show pattern
                else do
                    alexSetInput (newPos, ' ', [], newStr)
                    if found
                        then alexMonadScan
                        else loop
1114

1115
push :: Token -> AlexUserState -> AlexUserState
1116
push t s = s { lsToks = t : (lsToks s) }
1117

1118
tok :: TokenName -> Action
1119
tok tokId (pos, _, _, input) len = do
1120
    let tokStr = take len input
1121
    tokPos <- toTokPos pos
1122
    condStack <- gets lsCondStack
Zachary Snow committed
1123
    () <- if any (/= CurrentlyTrue) condStack
1124 1125 1126 1127 1128 1129
        then return ()
        else do
            specStack <- gets lsSpecStack
            if null specStack || Set.notMember tokId (head specStack)
                then modify (push $ Token tokId tokStr tokPos)
                else modify (push $ Token Id_simple ('_' : tokStr) tokPos)
1130
    alexMonadScan
1131
}