module Logo.TokenParser (tokenize) where import Logo.Types import Control.Applicative ((<|>), (<$>), many) import Text.ParserCombinators.Parsec ( char, letter, digit, alphaNum, string, space, parse, many1, skipMany, notFollowedBy, noneOf, try, (), eof, ParseError, Parser) import Text.ParserCombinators.Parsec.Number (natFloat, sign) tokenize :: String -> String -> Either ParseError [LogoToken] tokenize progName = parse logo progName logo :: Parser [LogoToken] logo = do skipMany space expressions <- many1 logoExpr skipMany space eof return $ concat expressions logoExpr :: Parser [LogoToken] logoExpr = try comment <|> try list <|> try binaryExpr <|> try parenExpr <|> try word "Logo Expression" comment :: Parser [LogoToken] comment = do skipMany space string ";" skipMany $ noneOf "\n" skipMany space return [] word :: Parser [LogoToken] word = try identifier <|> try stringLiteral <|> try varLiteral <|> try numLiteral "Logo terminal" identifier :: Parser [LogoToken] identifier = do skipMany space s <- letter i <- many alphaNum return . return $ (Identifier (s:i)) -- FIXME support escaping stringLiteral :: Parser [LogoToken] stringLiteral = do skipMany space char '"' s <- many1 $ noneOf "\t\n []()\"" return . return $ StrLiteral s varLiteral :: Parser [LogoToken] varLiteral = do skipMany space char ':' s <- letter v <- many alphaNum return . return $ VarLiteral (s:v) numLiteral :: Parser [LogoToken] numLiteral = do skipMany space s <- sign n <- natFloat return . return . NumLiteral . s $ case n of Left i -> fromInteger i Right f -> f operExpr :: Parser [LogoToken] operExpr = try parenExpr <|> try word binaryExpr :: Parser [LogoToken] binaryExpr = do lhs <- operExpr op <- operLiteral rhs <- try binaryExpr <|> operExpr return . concat $ [lhs, op, rhs] operLiteral :: Parser [LogoToken] operLiteral = do s <- many space (return . OperLiteral) <$> ( string "+" <|> if (length s) == 0 then (string "-") else ((string "-") >> notFollowedBy digit >> (return "-")) <|> string "*" <|> string "/" <|> string "%" <|> string "^" <|> try (string ">=") <|> try (string "<=") <|> try (string "<>") <|> string "=" <|> string "<" <|> string ">" ) list :: Parser [LogoToken] list = do skipMany space char '[' expr <- many logoExpr skipMany space char ']' return . return $ LogoList (concat expr) parenExpr :: Parser [LogoToken] parenExpr = do skipMany space char '(' skipMany space expr <- many logoExpr skipMany space char ')' return . return $ LogoExpr (concat expr)