cnc-spec-compiler-0.2.0.0: Intel/Cnc/Spec/CncLexer.x
{
module Intel.Cnc.Spec.CncLexer where
-- import Debug.Trace
}
%wrapper "monad"
-- Based on example from Happy distribution.
-- First some useful macros:
----------------------------------------------------------------------------------------------------
$whitechar = [ \t\n\r\f\v]
-- RRN [2010.07.19] Moving asterisk to special category:
-- This may be a bad idea wrt extensibility.
-- Moving colon to the special category too.
$special = [\(\)\,\;\[\]\`\{\}\*\:]
$digit = 0-9
$large = [A-Z \xc0-\xd6 \xd8-\xde]
$small = [a-z \xdf-\xf6 \xf8-\xff \_]
$alpha = [$small $large]
$ascsymbol = [\!\#\$\%\&\+\.\/\<\=\>\?\@\\\^\|\-\~]
$symbol = [$ascsymbol] # [$special \_\:\"\']
-- Almost everything:
$graphic = [$small $large $symbol $digit $special \:\"\']
$idchar = [$alpha $digit \']
--$symchar = [$symbol \:]
$symchar = [$symbol ]
-- These are the KEYWORDS for the language:
@reservedid =
module|fun|tags|items|reductions|steps|dense|constrain|prescribes|type
--step
@reservedop =
"::" | "|" | "<-" | "->" | "{" | "}" | "<" | ">"
-- | "[" | "]"
-- | "*" | "+"
@varid = $idchar+
@capid = $large $idchar*
-- So called "ops" are usually infix and start with punctuation:
@varop = $symbol $symchar*
--@consym = \: $symchar*
@decimal = $digit+
@exponent = [eE] [\-\+] @decimal
$cntrl = [$large \@\[\\\]\^\_]
@ascii = \^ $cntrl | NUL | SOH | STX | ETX | EOT | ENQ | ACK
| BEL | BS | HT | LF | VT | FF | CR | SO | SI | DLE
| DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN | EM
| SUB | ESC | FS | GS | RS | US | SP | DEL
$charesc = [abfnrtv\\\"\'\&]
@escape = \\ ($charesc | @ascii | @decimal )
@gap = \\ $whitechar+ \\
@string = $graphic # [\"\\] | " " | @escape | @gap
-- Here are the productions themselves:
----------------------------------------------------------------------------------------------------
haskell :-
<0> $white+ { skip }
-- <0> "//"\-*[^$symbol].* { mkL LComment }
<0> "//".* { mkL LComment }
"/*" { nested_comment }
<0> $special { mkL LSpecial }
<0> @reservedid { mkL LReservedId }
<0> ( @capid \. )+ @varid { mkL LQVarId }
<0> @varid { mkL LVarId }
<0> @capid { mkL LCapid }
<0> @reservedop { mkL LReservedOp }
<0> ( @capid \. )+ @varop { mkL LVarOp }
--<0> @capid \. @consym { mkL LConSym }
<0> @varop { mkL LVarOp }
--<0> @consym { mkL LConSym }
<0> @decimal \. @decimal
| @decimal \. @decimal @exponent ?
| @decimal @exponent { mkL LFloat }
<0> @decimal { mkL LInteger }
<0> \' ($graphic # [\'\\] | " " | @escape) \'
{ mkL LChar }
<0> \" @string* \" { mkL LString }
----------------------------------------------------------------------------------------------------
{ -- Begin Haskell code block to include in output:
-- The type of tokens:
data Lexeme = L AlexPosn LexemeClass String
deriving Show
data LexemeClass
= LInteger
| LFloat
| LChar
| LString
| LComment
| LSpecial
| LReservedId
| LReservedOp
| LVarId
| LQVarId
| LCapid
| LQCapid
| LVarOp
| LQVarOp
-- | LConSym
-- | LQConSym
| LEOF
deriving (Eq, Show)
-- Handle a common case: create one token
mkL :: LexemeClass -> AlexInput -> Int -> Alex Lexeme
mkL c (p,_,str) len = return (L p c (take len str))
-- This handles arbitrarily nested comments:
nested_comment :: AlexInput -> Int -> Alex Lexeme
nested_comment (apos, chr, str) int = do
input <- alexGetInput
go 1 input "*/"
-- When finished, set the position to after the comment ('input')
-- where go 0 input acc = do alexSetInput input; alexMonadScan
where go 0 input acc = do alexSetInput input; (mkL LComment (apos,chr, reverse acc) (length acc))
go n input acc = do
-- The 'n' parameter here keepstrack of the nesting.
let prev = alexInputPrevChar input
case alexGetChar input of
Nothing -> err input
Just (c,input) -> do
case c of
-- We've got a potential comment ENDING:
'/' -> do
case prev of
('*') -> go (n-1) input ('/':acc) -- CLOSE a level.
(c) -> go n input ('/':acc)
-- Here we've got another comment BEGINNING:
'*' -> do
case prev of
('/') -> go (n+1) input ('*':acc) -- OPEN a level
(c) -> go n input ('*':acc)
-- Other characters: add to the pile and keep going:
c -> go n input (c:acc)
err input = do alexSetInput input; lexError "error in nested comment"
lexError :: String -> Alex b
lexError s = do
(p,c,input) <- alexGetInput
alexError (showPosn p ++ ": " ++ s ++
(if (not (null input))
then " before " ++ show (head input)
else " at end of file"))
-- Returns either a list of tokens or an error:
scan_to_list :: String -> [Lexeme]
scan_to_list str =
case result of
Left err -> error$ "Error in lexing stage:\n" ++ err
Right ls -> ls
where
result = runAlex str $ do
{-
-- TODO: get line number for lex error:
let loop i = do tok@(L _ cl _) <- alexMonadScan;
-}
-- Maybe we can hack the monad here by replacing bind with our own version.
let loop i = do tok@(L _ cl _) <- alexMonadScan;
if cl == LEOF
then return [tok]
else do ls <- loop $! (i+1)
return (tok:ls)
loop 0
--alexEOF = return (L (error "EOF has no position") LEOF "")
alexEOF = return (L (AlexPn (-1) (-1) (-1)) LEOF "")
--alexEOF = return (L (noSrcLoc) LEOF "")
showPosn (AlexPn _ line col) = "line " ++ show line ++ ", col " ++ show col
main = do
putStrLn "HEllo!\n"
s <- getContents
--s <- getLine
--print (scanner s)
sequence_ (map print $ scan_to_list s)
-- case scan_to_list s of
-- Left err -> print err
-- Right ls -> sequence_ (map print ls)
} -- End final code block: