module Main where

-- import the the library functions from uulib
import UU.Parsing
import UU.Scanner

-- import our custom made Alex-scanner
import Scanner


-- Some boilerplate code to use the parser
-- Give `parsetokens' your parser and a list of tokens, returned by the `scanTokens'
-- function exported by the Scanner module, then you get either a list of error
-- messages in case of a parse error, or the parse tree.
type TokenParser a = Parser Token a

parseTokens :: TokenParser a -> [Token] -> Either [String] a
parseTokens p tks
  = if null msgs
    then final `seq` Right v
    else Left (map show msgs)
  where
    steps = parse p tks
    msgs  = getMsgs steps
    (Pair v final) = evalSteps steps


-- define a parse tree
data Expr
  = Identifier String
  | Integer Int
  | String String
  | Plus Expr Expr
  | Times Expr Expr
  | Let String  -- variable
        Expr    --   = expr
        Expr    -- body
  deriving Show


-- write a parser for it
-- Note: * make sure that the parser is not left recursive
--           (to the left is never a pExpr, or always a terminal first)
--       * make sure that the parser is not ambiguous
--           (by introducing priority levels for the operators)


-- Term -> let var = Expr in Expr | Add
pExpr :: TokenParser Expr
pExpr
  =   (\_ x _ e _ b -> Let x e b) <$> pKey "let" <*> pVarid <*> pKey "=" <*> pExpr <*> pKey "in" <*> pExpr
  <|> pAdd

-- Add -> Factor | Factor + Expr
pAdd :: TokenParser Expr
pAdd
  =   pFactor 
  <|> (\l _ r -> Plus l r) <$> pFactor <*> pKey "+" <*> pExpr

-- Factor -> Term | Term * Factor
pFactor :: TokenParser Expr
pFactor
  =   pTerm
  <|> (\l _ r -> Times l r) <$> pTerm <*> pKey "*" <*> pFactor

-- Term -> var
-- Term -> String
-- Term -> Int
-- Term -> (Expr)
pTerm :: TokenParser Expr
pTerm
  =   Identifier <$> pVarid
  <|> (Integer . read) <$> pInteger16
  <|> (String  . read) <$> pString
  <|> (\_ e _ -> e) <$> pKey "(" <*> pExpr <*> pKey ")"


-- test it
main :: IO ()
main
  = let res = parseTokens pExpr (tokenize "nofile" "let x = 3 in x*y+z")
    in case res of
         Left errs -> mapM_ putStrLn errs
         Right tree -> putStrLn $ show tree
