The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
module M0ld.Parser (parseM0ld) where
import Text.ParserCombinators.Parsec hiding (label)
import qualified Data.Map as Map
import M0ld.AST

identifier = do
    first <- choice [letter, char '_']
    rest <- many $ choice [alphaNum, char '_', char '!']
    return $ [first] ++ rest

ws = do
    many1 $ choice
        [ oneOf "\t\n "
        , char '#' >> many (noneOf "\n") >> newline
        ]
    return ()

opt_ws = option () ws

tok r = do
    res <- r
    opt_ws
    return res

symbol x = tok $ string x

parenthesized = between (symbol "(") (symbol ")")

inBraces = between (symbol "{") (symbol "}")

register =  char '$' >> identifier

label = do
    id <- tok $ identifier
    symbol ":"
    return [LabelDef id]


hint = do
    symbol "RI" 
    parenthesized $ do
        reg <- tok $ register
        symbol ","
        ri <- constant_string
        return $ [Hint RI reg (StringConstant ri)]
         
stmt = do 
    l <- many $ (try label)
    body <- choice $ [goto,br,hint,noop,declaration,action]
    return $ concat l ++ body

action = do
   lvalue <- tok $ register
   symbol "="
   val <- value
   option [Assign lvalue val] $ (symbol ".") >> (call val lvalue)


declaration = do
   symbol "my" 
   reg <- tok $ register
   (defaultValue,action) <- option (None,[]) $ symbol "=" >> expression reg
   return $ [Decl reg defaultValue] ++ action

expression lvalue = expression_constant_first lvalue <|> expression_reg_first lvalue

expression_constant_first lvalue = do
    c <- constant
    option (c,[]) $ do
        symbol "."
        reg <- declare_implicitly c
        action <- call reg lvalue
        return (None,action)


expression_reg_first lvalue = do
    r <- tok $ register
    option (None,[Assign lvalue r]) $ symbol "." >>(call r lvalue >>= \x -> return (None,x))


call invocant lvalue = do
    identifier <- value
    arguments <- parenthesized $ sepBy (tok argument) (symbol ",")
    let pos = [ x | Pos x <- arguments]
        named = [x | (Named k v) <- arguments, x <- [k,v]]
        action = [Call (lvalue :: Register) identifier (Capture (invocant :: Register) pos named)]
    return action

    




{- TODO - port over
call2 = do
    target <- value
    symbol "="
    responder <- value
    char '.'
    identifier <- value
    capture <- parenthesized $ symbol "|" >> value
    return [Call2 target responder identifier capture]
-}

constant_string = between (char '"') (char '"') quotedChars >>= return . concat
    where
      quotedChars = many $
        do
        c <- noneOf "\"\\"
        return [c]
        <|> do 
            char '\\'
            c <- anyChar
            return ['\\',c]

constant = choice 
      [ do
          (char 'ยข') <|> (char '?')
          name <- identifier
          return $ Var name
      , do
        digits <- many1 digit 
        return $ IntegerConstant $ read digits
      , do
        content <- constant_string
        return $ StringConstant content
      , submold
      ]

-- implicit_decl :: GenParser Char ImplicitDecls [Char]
--implicit_decl

implicit_decl = do
    c <- constant
    declare_implicitly c

declare_implicitly c = do
    decls <- getState
    case (Map.lookup c decls) of
        Just c -> return c
        Nothing ->
            do 
                let new = "___implicit_register___"++(show $ Map.size decls)
                updateState $ Map.insert c new
                return $ new

value = tok $ choice [register,implicit_decl]


noop = string "noop" >> return []

branch = inBraces $ do
    string "goto"
    ws
    label <- tok identifier
    option ' ' $ char ';'
    return label

br = do
    string "if"
    ws
    cond <- value
    iftrue <- branch
    symbol "else"
    iffalse <- branch
    return [Br cond iftrue iffalse]

goto = do
    string "goto"
    ws
    label <- identifier
    return [Goto label]



argument = do
        char ':'
        k <- value
        v <- parenthesized value
        return $ Named k v
    <|> do 
        arg <- value
        return $ Pos arg

terminator = opt_ws >> ((symbol ";" >> return ()) <|> eof)
top = do 
    opt_ws
    stmts <- tok $ endBy stmt terminator
    eof
    constants <- getState
    return (concat $ stmts,constants)

submold = do
    savedState <- getState
    setState Map.empty

    symbol "mold"
    stmts <- inBraces $ tok $ endBy stmt terminator
    constants <- getState
    setState savedState
    return $ SubMold $ (implicitDecls constants) ++ (concat stmts)
type ImplicitDecls = Map.Map Value [Char]

implicitDecls = concat . map (\(constant,reg) -> [Hint Constant reg constant,Decl reg constant]) . Map.toList

parseM0ld code = 
    case (runParser top (Map.empty :: ImplicitDecls) "" code) of 
        Left err      -> error  $ show err
        Right (stmts,constants) -> (implicitDecls constants) ++ stmts