The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
{-# OPTIONS -fglasgow-exts #-}

{-
    Higher-level parser for building ASTs.

    I sang of leaves, of leaves of gold, and leaves of gold there grew:
    Of wind I sang, a wind there came and in the branches blew.
    Beyond the Sun, beyond the Moon, the foam was on the Sea,
    And by the strand of Ilmarin there grew a golden Tree...
-}

module Parser where
import Internals
import AST
import Lexer

type StateParser a = GenParser Char () a

operators :: OperatorTable Char () Exp
operators =
    [ methOps  " . .+ .? .* .+ .() .[] .{} .<<>> .= "   -- Method postfix
    , postOps  " ++ -- "                                -- Auto-Increment
    , rightOps " ** "                                   -- Exponentiation
    , preOps   " ! + - ~ ? * ** +^ ~^ ?^ \\ "           -- Symbolic Unary
    , leftOps  " * / % x xx +& +< +> ~& ~< ~> "     -- Multiplicative
    , leftOps  " + - ~ +| +^ ~| ~^ "                    -- Additive
    , leftOps  " & ! "                                  -- Junctive And
    , leftOps  " ^ | "                                  -- Junctive Or
    , preOps   primitiveUnaryFunctions                  -- Name Unary
    , leftOps  " => but does cmp <=> .. ^.. ..^ ^..^ "  -- Non-chaining Binary
      ++ postOps "..."
    , cmpOps $ " != == < <= > >= ~~ !~ " ++
               " eq ne lt le gt ge =:= "                -- Chained Binary
    , leftOps  " && "                                   -- Tight And
    , leftOps  " || ^^ // "                             -- Tight Or
    , ternOps  [("??", "::")]                           -- Ternary
    , leftOps  " = := ::= += **= xx= "                  -- Assignment
    -- XXX rewrite chained Ops using sepBy!
    , rightOps " , "                                    -- List Item Separator
    , preOps   primitiveListFunctions                   -- List Operator
    , leftOps  " ==> "                                  -- Pipe Forward
    , leftOps  " and "                                  -- Loose And
    , leftOps  " or xor err "                           -- Loose Or
    , leftOps  " ; "                                    -- Terminator
    ]

primitiveListFunctions = " not "

parseExp = parseOp

parseOp = buildExpressionParser operators parseTerm

ops f s = [f n | n <- words s]

makeOp op name = do
    reservedOp name
    return $ op name

leftOps     = ops left
rightOps    = ops right
cmpOps      = ops cmp
postOps     = ops postfix
preOps      = ops prefix

left name   = Infix (makeOp Op2 name) AssocLeft
right name  = Infix (makeOp Op2 name) AssocRight
cmp name    = Infix (makeOp OpCmp name) AssocLeft
prefix      = Prefix . makeOp Op1
postfix     = Postfix . makeOp Op1

parseTerm = parens parseOp
    <|> parseLit
--  <|> nonTerm
    <?> "term"

nonTerm = do
    pos <- getPosition
    return $ NonTerm pos

parseLit = choice
    [ numLiteral
    , strLiteral
    , namedLiteral "undef"  VUndef
    , namedLiteral "NaN"    (VNum $ 0/0)
    , namedLiteral "Inf"    (VNum $ 1/0)
    ]

numLiteral = do
    n <- naturalOrFloat  
    case n of
        Left  i -> return . Val $ VInt i
        Right d -> return . Val $ VNum d

strLiteral = return . Val . VStr =<< stringLiteral

namedLiteral n v = do
    lexeme (string n)
    return $ Val v

op_methodPostfix    = []
op_namedUnary       = []
methOps _ = []
primitiveUnaryFunctions = []
ternOps _ = []
listOps _ = []

-- runLex :: Show a => StateParser a -> String -> IO ()
runLex f p input
        = runParse f (do{ whiteSpace
                 ; x <- p
                 ; eof
                 ; return x
                 }) input

-- run :: Show a => StateParser a -> String -> IO ()
runParse f p input
        = case ( runParser p () "" input ) of
            Left err -> do{ putStr "parse error at "
                          ; print err
                          }
            Right x  -> f x