Skip to content

Instantly share code, notes, and snippets.

@m1dnight
Created June 30, 2016 02:32
Show Gist options
  • Save m1dnight/126d6b500175c2c286e3804584e5c4ce to your computer and use it in GitHub Desktop.
Save m1dnight/126d6b500175c2c286e3804584e5c4ce to your computer and use it in GitHub Desktop.
Monadic Parser and Lexer using Alex and Happy
{
module Lexer where
}
%wrapper "monadUserState"
$digit = 0-9 -- digits
$alpha = [a-zA-Z] -- alphabetic characters
-- Each token has a function on the RHS that is a function (String ->
-- Token). We wrap this function in the Alex monad with `pushToken`, which has
-- a result AlexAction ().
tokens :-
$white+ ;
$alpha+ { pushToken $ TString }
$digit+ { pushToken $ TNumber . read }
'@' { pushToken $ \_ -> TAt }
{
------------
-- Tokens --
------------
data Token
= TNumber Integer
| TString String
| TAt
deriving (Show, Eq)
-----------
-- Types --
-----------
type AlexUserState = [Token]
type ParseError = String
--------------------
-- Parser Helpers --
--------------------
alexEOF :: Alex ()
alexEOF = return ()
ignore input len = alexMonadScan
-----------------
-- Lexer State --
-----------------
alexInitUserState :: AlexUserState
alexInitUserState = []
modifyUserState :: (AlexUserState -> AlexUserState) -> Alex ()
modifyUserState f = Alex $ \s -> let current = alex_ust s
new = f current
in
Right (s { alex_ust = new },())
getUserState :: Alex AlexUserState
getUserState = Alex $ \s -> Right (s,alex_ust s)
pushToken :: (String -> Token) -> AlexAction ()
pushToken tokenizer =
\(posn,prevChar,pending,s) len -> modifyUserState (push $ take len s) >> alexMonadScan
where
push :: String -> AlexUserState -> AlexUserState
push s ts = ts ++ [(tokenizer s)]
runAlexScan :: String -> Either ParseError AlexUserState
runAlexScan s = runAlex s $ alexMonadScan >> getUserState
--main = getContents >>= print . runAlexScan
}
{
module Parser where
import Lexer
import Syntax
import Text.Printf
}
%name calc
%tokentype { Token }
%error { parseError }
%monad{ Either String }{ >>= }{ return }
%token
'@' { TAt }
number { TNumber $$ }
var { TString $$ }
%%
AST
: '@' { At }
| number { Numbr $1 }
| var { Name $1 }
{
parseError ts = fail "Parse error"
parse :: String -> Either ParseError AST
parse input = runAlexScan input >>= calc
}
module Syntax where
data AST
= Numbr Integer
| Name String
| At
deriving (Show, Eq)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment