module Fun.Parser where import Data.Char import Fun.Tree type Context = Maybe String type Parser a = String -> Either Context (a, String) char :: Parser Char char [] = Left Nothing char (x : xs) = Right (x, xs) digit :: Parser Char digit = char <=> isDigit digits :: Parser String digits = iter digit number :: Parser Integer number = literal '-' <-+> digits >>> (\n -> -1 * (read n :: Integer)) <|> digits >>> (\n -> read n :: Integer) space :: Parser Char space = char <=> isSpace newline :: Parser Char newline = char <=> (== '\n') notSpace :: Parser Char notSpace = char <=> (not . isSpace) letter :: Parser Char letter = char <=> isAlpha letters :: Parser String letters = iter letter alphanum :: Parser Char alphanum = digit <|> letter special :: Parser Char special = oneOf "+-*/<^>$@#&!?" -- TODO: Higher-order function? linkOr :: [Parser Char] -> Parser Char linkOr [] = invalid ' ' linkOr (x : []) = x linkOr (x : xs) = x <|> linkOr xs oneOf :: [Char] -> Parser Char oneOf s = linkOr $ map (\c -> (char <=> (== c))) s string :: Parser String string = literal '"' <-+> iter (char <=> (/= '"')) <+-> literal '"' literal :: Char -> Parser Char literal c = char <=> (== c) result :: a -> Parser a result a cs = Right (a, cs) invalid :: a -> Parser a invalid a cs = Left $ Just "Invalid" iter :: Parser Char -> Parser String iter m = (iterS m) <=> (/= "") iterS :: Parser a -> Parser [a] iterS m = m <+> iterS m >>> (\(x, y) -> x : y) <|> result [] iterFail :: Parser a -> Parser [a] iterFail m = m <+> iterFail m >>> (\(x, y) -> x : y) token :: Parser a -> Parser a token = (<+-> iterS space) -- A parser that will accept a given alpha string acceptWord :: String -> Parser String acceptWord w = token (letters <=> (== w)) -- A parser that will accept a given string accept :: String -> Parser String accept w = token ((iter notSpace) <=> (== w)) -- Given a parser and a predicate return the parser only if it satisfies the predicate infix 7 <=> (<=>) :: Parser a -> (a -> Bool) -> Parser a (parser <=> predicate) input = case parser input of Left a -> Left a Right (a, rest) -> if (predicate a) then Right (a, rest) else Left $ Just (head . lines $ input) -- Combine two parser together pairing their results up in a tuple infixl 6 <+> (<+>) :: Parser a -> Parser b -> Parser (a, b) (parserA <+> parserB) input = case parserA input of Left a -> Left a Right (resultA, remainder) -> case parserB remainder of Left a -> Left a Right (resultB, cs) -> Right ((resultA, resultB), cs) -- Sequence operator that discards the second result infixl 6 <+-> (<+->) :: Parser a -> Parser b -> Parser a (parserA <+-> parserB) input = case parserA input of Left a -> Left a Right (resultA, remainder) -> case parserB remainder of Left a -> Left a Right (_, cs) -> Right (resultA, cs) -- Sequence operator that discards the first result infixl 6 <-+> (<-+>) :: Parser a -> Parser b -> Parser b (parserA <-+> parserB) input = case parserA input of Left a -> Left a Right (resultA, remainder) -> case parserB remainder of Left a -> Left a Right (resultB, cs) -> Right (resultB, cs) -- Transform a parsers result infixl 5 >>> (>>>) :: Parser a -> (a -> b) -> Parser b (parser >>> transformer) input = case parser input of Left a -> Left a Right (resultA, remainder) -> Right ((transformer resultA), remainder) -- Extract a parsers result infix 4 +> (+>) :: Parser a -> (a -> Parser b) -> Parser b (parser +> function) input = case parser input of Left a -> Left a Right (a, cs) -> function a cs -- Combine two parsers using a 'or' type operation infixl 3 <|> (<|>) :: Parser a -> Parser a -> Parser a (parserA <|> parserB) input = case parserA input of Left _ -> parserB input result -> result ---- tree :: Parser Tree tree = iterFail program >>> Tree program :: Parser Program program = iterS block >>> Program block :: Parser Block block = functionBlock <+-> newline >>> Block visibility :: Parser Visibility visibility = (literal '+' >>> const PublicVisibility) <|> (literal '-' >>> const PrivateVisibility) functionBlock :: Parser FunctionBlock functionBlock = functionDeclaration <+> iterS functionDefinition >>> build where build (decl, defn) = FunctionBlock decl defn functionDeclaration :: Parser FunctionDeclaration functionDeclaration = functionDeclarationWithoutFlags <|> functionDeclarationWithFlags functionDeclarationWithoutFlags :: Parser FunctionDeclaration functionDeclarationWithoutFlags = functionName <+> functionDeclarationDelimiter <+> functionTypes <+-> newline >>> build where build ((name, vis), types) = FunctionDeclaration name vis types [] functionDeclarationWithFlags :: Parser FunctionDeclaration functionDeclarationWithFlags = functionName <+> functionDeclarationDelimiter <+> functionTypes <+-> space <+-> literal '%' <+> functionFlags >>> build where build (((name, vis), types), flags) = FunctionDeclaration name vis types flags functionDeclarationDelimiter :: Parser Visibility functionDeclarationDelimiter = space <-+> literal ':' <-+> visibility <+-> literal ':' <+-> space functionName :: Parser String functionName = (special <|> letter) <+> iterS (special <|> alphanum) >>> build where build (first, rest) = first : rest functionTypes :: Parser [String] functionTypes = (iterS (functionType <+-> space <+-> literal ':' <+-> space)) <+> functionType >>> build where build (a, b) = a ++ [b] functionType :: Parser String functionType = (letter <=> isUpper) <+> iterS alphanum >>> build where build (first, rest) = first : rest functionFlags :: Parser [FunctionFlag] -- TODO: Fix flags functionFlags = iterS (space <-+> letters) <+-> newline >>> build where build list = map read list functionDefinition :: Parser FunctionDefinition functionDefinition = functionPattern <+-> literal ':' <+> functionBody >>> build where build (pattern, body) = FunctionDefinition pattern body functionPattern :: Parser FunctionPattern functionPattern = iterS (functionPatternElement <+-> space) >>> FunctionPattern functionPatternElement :: Parser FunctionPatternElement functionPatternElement = (functionParameter >>> FunctionPatternParameter) <|> (literal '_' >>> const Wildcard) <|> (number >>> FunctionPatternNumber) <|> (string >>> FunctionPatternString) functionParameter :: Parser String functionParameter = letter <+> iterS alphanum >>> build where build (a, b) = a : b functionBody :: Parser FunctionBody functionBody = iterS (space <-+> functionBodyElement) <+-> newline >>> FunctionBody functionBodyElement :: Parser FunctionBodyElement functionBodyElement = statement <|> (functionName >>> FunctionBodyIdentifier) <|> (functionParameter >>> FunctionBodyIdentifier) <|> (string >>> FunctionBodyString) <|> (number >>> FunctionBodyNumber) statement :: Parser FunctionBodyElement -- TODO statement = accept "if" >>> Statement