Skip to content

Commit

Permalink
monadUserState
Browse files Browse the repository at this point in the history
add the monadUserState wrapper, and its variant
monadUserState-bytestring, allowing to define a custom state monad in
the lex specification
  • Loading branch information
alcremi@pobox.com committed Sep 2, 2008
1 parent 197317b commit 57d9d6d
Show file tree
Hide file tree
Showing 5 changed files with 588 additions and 3 deletions.
2 changes: 2 additions & 0 deletions Setup.lhs
Expand Up @@ -70,6 +70,8 @@ wrappers = [
("AlexWrapper-posn-bytestring", ["-DALEX_POSN_BYTESTRING"]),
("AlexWrapper-monad", ["-DALEX_MONAD"]),
("AlexWrapper-monad-bytestring", ["-DALEX_MONAD_BYTESTRING"]),
("AlexWrapper-monadUserState", ["-DALEX_MONAD", "-DALEX_MONAD_USER_STATE"]),
("AlexWrapper-monadUserState-bytestring", ["-DALEX_MONAD_BYTESTRING", "-DALEX_MONAD_USER_STATE"]),
("AlexWrapper-gscan", ["-DALEX_GSCAN"])
]

Expand Down
3 changes: 0 additions & 3 deletions TODO
Expand Up @@ -21,9 +21,6 @@

- AlexEOF doesn't provide a way to get at the text position of the EOF.

- AlexState should include some user state - would make this monad
more useful in general.

- Allow user-defined wrappers? Wrappers in files relative to the
current directory, for example?

Expand Down
103 changes: 103 additions & 0 deletions doc/alex.xml
Expand Up @@ -1288,6 +1288,78 @@ begin code = skip `andBegin` code
token t input len = return (t input len)</programlisting>
</section>

<section>
<title>The "monadUserState" wrapper</title>

<para>The <literal>monadUserState</literal> wrapper is built
upon the <literal>monad</literal> wrapper. It includes a reference
to a type which must be defined in the user's program,
<literal>AlexUserState</literal>, and a call to an initialization
function which must also be defined in the user's program,
<literal>alexInitUserState</literal>. It gives great flexibility
because it is now possible to add any needed information and carry
it during the whole lexing phase.</para>

<para>The generated code is the same as in the <literal>monad</literal>
wrapper, except in 2 places:</para>
<para>1) The definition of the general state, which now refers to a
type (<literal>AlexUserState</literal>) that must be defined in the Alex file.</para>

<programlisting>data AlexState = AlexState {
alex_pos :: !AlexPosn, -- position at current input location
alex_inp :: String, -- the current input
alex_chr :: !Char, -- the character before the input
alex_scd :: !Int -- the current startcode
, alex_ust :: AlexUserState -- AlexUserState will be defined in the user program
}
</programlisting>

<para>2) The initialization code, where a user-specified routine (<literal>alexInitUserState</literal>) will be
called.</para>

<programlisting>runAlex :: String -> Alex a -> Either String a
runAlex input (Alex f)
= case f (AlexState {alex_pos = alexStartPos,
alex_inp = input,
alex_chr = '\n',
alex_ust = alexInitUserState,
alex_scd = 0}) of Left msg -> Left msg
Right ( _, a ) -> Right a
</programlisting>

<para>Here is an example of code in the user's Alex file defining
the type and function:</para>

<programlisting>data AlexUserState = AlexUserState
{
lexerCommentDepth :: Int
, lexerStringValue :: String
}

alexInitUserState :: AlexUserState
alexInitUserState = AlexUserState
{
lexerCommentDepth = 0
, lexerStringValue = ""
}

getLexerCommentDepth :: Alex Int
getLexerCommentDepth = Alex $ \s@AlexState{alex_ust=ust} -> Right (s, lexerCommentDepth ust)

setLexerCommentDepth :: Int -> Alex ()
setLexerCommentDepth ss = Alex $ \s -> Right (s{alex_ust=(alex_ust s){lexerCommentDepth=ss}}, ())

getLexerStringValue :: Alex String
getLexerStringValue = Alex $ \s@AlexState{alex_ust=ust} -> Right (s, lexerStringValue ust)

setLexerStringValue :: String -> Alex ()
setLexerStringValue ss = Alex $ \s -> Right (s{alex_ust=(alex_ust s){lexerStringValue=ss}}, ())

addCharToLexerStringValue :: Char -> Alex ()
addCharToLexerStringValue c = Alex $ \s -> Right (s{alex_ust=(alex_ust s){lexerStringValue=c:lexerStringValue (alex_ust s)}}, ())
</programlisting>
</section>

<section>
<title>The "gscan" wrapper</title>

Expand Down Expand Up @@ -1424,6 +1496,37 @@ runAlex :: ByteString -> Alex a -> Either String a
only the types of the function to run the monad and the type of the
<literal>token</literal> function that change.</para>
</section>

<section>
<title>The "monadUserState-bytestring" wrapper</title>
<para>The <literal>monadUserState-bytestring</literal> wrapper is the same as
the <literal>monadUserState</literal> wrapper but with lazy
<literal>ByteString</literal> instead of <literal>String</literal>:</para>

<programlisting>
import qualified Data.ByteString.Lazy.Char8 as ByteString

ata AlexState = AlexState {
alex_pos :: !AlexPosn, -- position at current input location
alex_inp :: ByteString, -- the current input
alex_chr :: !Char, -- the character before the input
alex_scd :: !Int -- the current startcode
, alex_ust :: AlexUserState -- AlexUserState will be defined in the user program
}

newtype Alex a = Alex { unAlex :: AlexState
-> Either String (AlexState, a) }

runAlex :: ByteString -> Alex a -> Either String a

-- token :: (ByteString -> Int -> token) -> AlexAction token
</programlisting>

<para>All of the actions in your lexical specification
have the same type as in the <literal>monadUserState</literal> wrapper. It is
only the types of the function to run the monad and the type of the
<literal>token</literal> function that change.</para>
</section>
</section>
</section>
</chapter>
Expand Down

0 comments on commit 57d9d6d

Please sign in to comment.