module Lox.Parser ( ParserError (..), parse ) where import Control.Monad import Control.Monad.State import Lox.Scanner import Lox.Expr data ParserState = ParserState {tokens :: [Token]} data ParserError = MismatchedParenthesesError | ExpectedExpressionError -- expression → equality ; -- equality → comparison ( ( "!=" | "==" ) comparison )* ; -- comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ; -- term → factor ( ( "-" | "+" ) factor )* ; -- factor → unary ( ( "/" | "*" ) unary )* ; -- unary → ( "!" | "-" ) unary -- | primary ; -- primary → NUMBER | STRING | "true" | "false" | "nil" -- | "(" expression ")" ; parse :: [Token] -> Either ParserError Expr parse tokens = evalState expression (ParserState {tokens=tokens}) expression :: State ParserState (Either ParserError Expr) expression = equality equality :: State ParserState (Either ParserError Expr) equality = do exprMaybe <- comparison case exprMaybe of Left err -> return $ Left err Right expr -> fmap (mergeExpressionMaybe expr) <$> matchTail [BANG_EQUAL, EQUAL_EQUAL] comparison comparison :: State ParserState (Either ParserError Expr) comparison = do exprMaybe <- term case exprMaybe of Left err -> return $ Left err Right expr -> fmap (mergeExpressionMaybe expr) <$> matchTail [GREATER, GREATER_EQUAL, LESS, LESS_EQUAL] term term :: State ParserState (Either ParserError Expr) term = do exprMaybe <- factor case exprMaybe of Left err -> return $ Left err Right expr -> fmap (mergeExpressionMaybe expr) <$> matchTail [MINUS, PLUS] factor factor :: State ParserState (Either ParserError Expr) factor = do exprMaybe <- unary case exprMaybe of Left err -> return $ Left err Right expr -> fmap (mergeExpressionMaybe expr) <$> matchTail [SLASH, STAR] unary unary :: State ParserState (Either ParserError Expr) unary = do maybeOperator <- matchToken [BANG, MINUS] case maybeOperator of Nothing -> primary Just op -> do exprMaybe <- unary case exprMaybe of Left err -> return $ Left err Right expr -> return $ Right $ Unary op expr primary :: State ParserState (Either ParserError Expr) primary = do token <- advance case tokenType token of FALSE -> return $ Right $ Literal $ BoolObject False TRUE -> return $ Right $ Literal $ BoolObject True NIL -> return $ Right $ Literal NullObject NUMBER -> return $ Right $ Literal $ tokenObject token STRING -> return $ Right $ Literal $ tokenObject token LEFT_PAREN -> do exprMaybe <- expression case exprMaybe of Left err -> return $ Left err Right expr -> do consume RIGHT_PAREN MismatchedParenthesesError return $ Right $ Grouping expr _ -> return $ Left ExpectedExpressionError matchTail :: [TokenType] -> State ParserState (Either ParserError Expr) -> State ParserState (Either ParserError (Maybe (Token, Expr))) matchTail tokenTypes f = do maybeOperator <- matchToken tokenTypes case maybeOperator of Nothing -> return $ Right Nothing Just op -> do exprMaybe <- comparison restMaybe <- matchTail tokenTypes f case (exprMaybe, restMaybe) of (Left err, _) -> return $ Left err (_, Left err) -> return $ Left err (Right expr, Right rest) -> return $ Right $ Just (op, mergeExpressionMaybe expr rest) mergeExpressionMaybe :: Expr -> Maybe (Token, Expr) -> Expr mergeExpressionMaybe expr Nothing = expr mergeExpressionMaybe left (Just (op, right)) = Binary left op right matchToken :: [TokenType] -> State ParserState (Maybe Token) matchToken [] = return Nothing matchToken (t:ts) = do isMatch <- check t if isMatch then Just <$> advance else matchToken ts check :: TokenType -> State ParserState Bool check t = do atEnd <- isAtEnd if atEnd then return False else (== t) . tokenType <$> peek consume :: TokenType -> ParserError -> State ParserState (Either ParserError Token) consume t err = do isOk <- check t if isOk then Right <$> advance else return $ Left err advance :: State ParserState Token advance = state (\s@ParserState {tokens=(t:ts)} -> (t, s {tokens = ts})) peek :: State ParserState Token peek = gets (head . tokens) isAtEnd :: State ParserState Bool isAtEnd = (== EOF) . tokenType <$> peek