module BasicParser where
import Control.Applicative
import AmethystSyntax
newtype Parser a = Parser {runParser :: String -> Maybe (String, a)}
instance Functor Parser where
fmap f (Parser p) =
Parser $
\input -> do
(input', x) <- p input
Just (input', f x)
instance Applicative Parser where
pure x = Parser $ \input -> Just (input, x)
(Parser p1) <*> (Parser p2) =
Parser $
\input -> do
(input1, f) <- p1 input
(input2, x) <- p2 input1
Just (input2, f x)
instance Alternative Parser where
empty = Parser $ const Nothing
(Parser p1) <|> (Parser p2) =
Parser $
\input ->
p1 input <|> p2 input
charP :: Char -> Parser Char
charP x = Parser f
where
f [] = Nothing
f (y:ys)
|y == x = Just (ys, x)
|otherwise = Nothing
stringP :: String -> Parser String
stringP = traverse charP
spanP :: (Char -> Bool) -> Parser String
spanP f = Parser $ \input ->
let (token, rest) = span f input
in Just (rest, token)
notNull :: Parser [a] -> Parser [a]
notNull (Parser p) = Parser $ \input -> do
(input', xs) <- p input
if null xs
then Nothing
else Just (input', xs)
sepBy :: Parser a -> Parser b -> Parser [b]
sepBy sep element = (:) <$> element <*> many (sep *> element) <|> pure []
ws :: Parser String
ws = spanP (\c -> c == ' ' || c == '\n')
ws2 :: Parser String
ws2 = notNull ws
numberP :: Parser Int
numberP = read <$> spanP (`elem` "0123456789")
wordP :: Parser String
wordP = spanP (`elem` allowedNameSymbols)
tapeP :: Parser String
tapeP = spanP (`elem` allowedTapeSymbols)
symbolP :: Parser Char
symbolP = foldr1 (<|>) $ map charP allowedTapeSymbols
moveP :: Parser Move
moveP = L <$ charP 'L'
<|> R <$ charP 'R'
<|> N <$ charP 'N'
transitionP :: Parser Transition
transitionP = Transition
<$> (ws *> symbolP <* ws <* charP '/')
<*> (ws *> symbolP <* ws <* charP ',')
<*> (ws *> moveP <* ws <* stringP "->")
<*> (ws *> wordP <* ws <* charP ';')
stateP :: Parser State
stateP = Reject <$> rejectP
<|> Accept <$> acceptP
<|> makeState True <$> initialP <*> trP
<|> makeState False <$> normalP <*> trP
where
rejectP = stringP "reject" *> ws2
*> stringP "state" *> ws2
*> wordP <* ws <* charP ';'
acceptP = stringP "accept" *> ws2
*> stringP "state" *> ws2
*> wordP <* ws <* charP ';'
initialP = stringP "initial" *> ws2
*> stringP "state" *> ws2
*> wordP <* ws
normalP = stringP "state" *> ws
*> wordP <* ws
trP = charP '{' *> some transitionP <* ws <* charP '}'
makeState :: Bool -> String -> [Transition] -> State
makeState initial name transitions = State name transitions initial
complementP :: Parser MacroKeyword
complementP = Complement
<$> (stringP "complement" *> ws *> charP '(' *> ws
*> wordP <* ws <* charP ')')
intersectP :: Parser MacroKeyword
intersectP = Intersect
<$> (stringP "intersect" *> ws *> charP '(' *> ws *>
sepBy (ws *> charP ',' <* ws) wordP <* ws <* charP ')')
reunionP :: Parser MacroKeyword
reunionP = Reunion
<$> (stringP "reunion" *> ws *> charP '(' *> ws *>
sepBy (ws *> charP ',' <* ws) wordP <* ws <* charP ')')
chainP :: Parser MacroKeyword
chainP = Chain
<$> (stringP "chain" *> ws *> charP '(' *> ws *>
sepBy (ws *> charP ',' <* ws) wordP <* ws <* charP ')')
repeatP :: Parser MacroKeyword
repeatP = Repeat
<$> (stringP "repeat" *> ws *> charP '(' *> ws *> wordP <* ws)
<*> (charP ',' *> ws *> numberP <* ws <* charP ')')
moveMP :: Parser MacroKeyword
moveMP = Move
<$> (stringP "move" *> ws *> charP '(' *> ws *> moveP <* ws)
<*> (charP ',' *> ws *> numberP <* ws <* charP ')')
overrideP :: Parser MacroKeyword
overrideP = Override
<$> (stringP "override" *> ws *> charP '(' *> ws *> moveP <* ws)
<*> (charP ',' *> ws *> numberP <* ws <* charP ',' <* ws)
<*> (charP '\'' *> symbolP <* charP '\'' <* ws <* charP ')')
placeP :: Parser MacroKeyword
placeP = Place
<$> (stringP "place" *> ws *> charP '(' *> ws *>
charP '"' *> tapeP <* charP '"' <* ws <* charP ')')
shiftP :: Parser MacroKeyword
shiftP = Shift
<$> (stringP "shift" *> ws *> charP '(' *> ws *> moveP <* ws)
<*> (charP ',' *> ws *> numberP <* ws <* charP ')')
macroP :: Parser Automaton
macroP = Macro
<$> (stringP "automaton" *> ws2 *> wordP <* ws <* charP '=' <* ws)
<*> (complementP <|> intersectP <|> reunionP <|> chainP <|> repeatP
<|> moveMP <|> overrideP <|> placeP <|> shiftP) <* ws <* charP ';'
machineP :: Parser Automaton
machineP = Machine
<$> (stringP "automaton" *> ws2 *> wordP <* ws)
<*> (charP '(' *> sepBy comma pair <* charP ')' <* ws)
<*> (charP '{' *> some (ws *> stateP <* ws) <* charP '}')
where
comma = ws *> charP ',' <* ws
pair :: Parser (String, String)
pair = (,) <$> wordP <*> (ws2 *> wordP)
automataP :: Parser Automaton
automataP = macroP <|> machineP
programP :: Parser Program
programP = Program <$> many (ws *> automataP <* ws)