|
| 1 | +{ |
| 2 | +module Main (main) where |
| 3 | +import {- "containers" -} Data.Set (Set) |
| 4 | +import {- "containers" -} qualified Data.Set as Set |
| 5 | +import {- "text" -} Data.Text (Text) |
| 6 | +import {- "text" -} qualified Data.Text as Text |
| 7 | +import {- "text" -} qualified Data.Text.Read as Text |
| 8 | +import {- "base" -} Control.Arrow hiding (arr) |
| 9 | +import {- "base" -} Control.Monad (forM_, when) |
| 10 | +import {- "base" -} Control.Monad.Fail (MonadFail) |
| 11 | +import {- "base" -} qualified Control.Monad.Fail as Fail (MonadFail (..)) |
| 12 | +import {- "base" -} Numeric.Natural |
| 13 | +import {- "base" -} System.Exit |
| 14 | +} |
| 15 | + |
| 16 | +%wrapper "monadUserState-strict-text" |
| 17 | +%token "Token integer" |
| 18 | +%typeclass "Integral integer, Read integer, Show integer" |
| 19 | + |
| 20 | +-- ugh |
| 21 | +$digit = 0-9 |
| 22 | +$unidigit = 1-9 |
| 23 | +@number = [0] | $unidigit $digit* |
| 24 | + |
| 25 | +tokens :- |
| 26 | + $white+ { skip } |
| 27 | + @number { \(_, _, _, s) len -> case Text.decimal (Text.take len s) of |
| 28 | + Left e -> Fail.fail e |
| 29 | + Right (n, txt) |
| 30 | + | Text.null txt -> pure $ TokenInt n |
| 31 | + | otherwise -> Fail.fail "got incomplete prefix " } |
| 32 | + [a-z]+ { \(_, _, _, s) len -> do |
| 33 | + let name = Text.take len s |
| 34 | + alexSeenVar name |
| 35 | + pure $ TokenVar name } |
| 36 | + [\+] { mk0ary TokenAdd } |
| 37 | + [\-] { mk0ary TokenSub } |
| 38 | + [\*] { mk0ary TokenMul } |
| 39 | + [\/] { mk0ary TokenDiv } |
| 40 | + [\^] { mk0ary TokenPow } |
| 41 | + [\(] { mk0ary TokenLPar } |
| 42 | + [\)] { mk0ary TokenRPar } |
| 43 | + |
| 44 | +{ |
| 45 | +mk0ary :: (Read integer, Integral integer) => Token integer -> AlexInput -> Int -> Alex (Token integer) |
| 46 | +mk0ary tok _ _ = pure tok |
| 47 | + |
| 48 | +data AlexUserState |
| 49 | + = AlexUserState { |
| 50 | + ausVars :: Set Text |
| 51 | + } deriving (Eq, Read, Show) |
| 52 | + |
| 53 | +alexSeenVar :: Text -> Alex () |
| 54 | +alexSeenVar txt = do |
| 55 | + AlexUserState { ausVars = set } <- alexGetUserState |
| 56 | + alexSetUserState $ AlexUserState { ausVars = txt `Set.insert` set } |
| 57 | + |
| 58 | +alexInitUserState :: AlexUserState |
| 59 | +alexInitUserState = AlexUserState { ausVars = Set.empty } |
| 60 | + |
| 61 | +data Token integer |
| 62 | + = TokenInt integer |
| 63 | + | TokenVar Text |
| 64 | + | TokenLPar |
| 65 | + | TokenRPar |
| 66 | + | TokenPow |
| 67 | + | TokenDiv |
| 68 | + | TokenMul |
| 69 | + | TokenSub |
| 70 | + | TokenAdd |
| 71 | + | EOF |
| 72 | + deriving (Eq, Read, Show) |
| 73 | + |
| 74 | +alexEOF :: (Read integer, Integral integer) => Alex (Token integer) |
| 75 | +alexEOF = pure EOF |
| 76 | + |
| 77 | +instance MonadFail Alex where |
| 78 | + fail s = Alex . const $ Left s |
| 79 | + |
| 80 | +evalAlex :: Text -> Alex t -> Either String (AlexUserState, t) |
| 81 | +evalAlex txt alex = right (first getUserState) $ f state where |
| 82 | + f = unAlex alex |
| 83 | + getUserState AlexState { alex_ust = userState } = userState |
| 84 | + state = AlexState |
| 85 | + { alex_bytes = [] |
| 86 | + , alex_pos = alexStartPos |
| 87 | + , alex_inp = txt |
| 88 | + , alex_chr = '\n' |
| 89 | + , alex_ust = alexInitUserState |
| 90 | + , alex_scd = 0 } |
| 91 | + |
| 92 | +scanAll :: (Eq integer, Integral integer, Read integer, Show integer) => Alex [Token integer] |
| 93 | +scanAll = alexMonadScan >>= \result -> case result of |
| 94 | + EOF -> pure [] |
| 95 | + tok -> (tok :) <$> scanAll |
| 96 | + |
| 97 | +tests :: [(Text, Set Text, [Token Natural])] |
| 98 | +tests = [ (Text.pack "x*y/(x^3+y^3)" |
| 99 | + , Set.fromList [x, y] |
| 100 | + , [TokenVar x, TokenMul, TokenVar y, TokenDiv, TokenLPar, TokenVar x, TokenPow, TokenInt 3, TokenAdd, TokenVar y, TokenPow, TokenInt 3, TokenRPar])] where |
| 101 | + x = Text.pack "x" |
| 102 | + y = Text.pack "y" |
| 103 | + |
| 104 | +main :: IO () |
| 105 | +main = do |
| 106 | + forM_ tests $ \(txt, vars, toks) -> do |
| 107 | + case evalAlex txt scanAll of |
| 108 | + Right (AlexUserState { ausVars = tokVars }, tokList) |
| 109 | + | tokVars == vars && toks == tokList -> pure () |
| 110 | + | otherwise -> do |
| 111 | + when (toks /= tokList) $ do |
| 112 | + putStrLn $ "got " <> show tokList |
| 113 | + putStrLn $ "wanted " <> show toks |
| 114 | + when (tokVars /= vars) $ do |
| 115 | + putStrLn $ "got " <> show tokVars |
| 116 | + putStrLn $ "wanted " <> show vars |
| 117 | + exitFailure |
| 118 | + Left errorString -> do |
| 119 | + putStrLn $ "got error " <> errorString |
| 120 | + exitFailure |
| 121 | + exitSuccess |
| 122 | +} |
0 commit comments