{
module SourceParser where

import Lexer
import PureSyntax
import SugarSyntax

import Control.Monad.Except
}

%name parseProg PROGRAM
%name parseExpr EXPR
%name parseComm COMMAND
%name parseLVal LVAL

%monad { ExceptT String IO }
%tokentype { Token }
%error { parseError }

%right IsEq
%right ConsPre
%right ConsInf
%right Hd Tl
%left SemiCo

%token
    Dot       { Token ( _, TkDot         , _ ) }
    OpenBrc   { Token ( _, TkOpenBrc     , _ ) }
    ClosBrc   { Token ( _, TkClosBrc     , _ ) }
    OpenCur   { Token ( _, TkOpenCur     , _ ) }
    ClosCur   { Token ( _, TkClosCur     , _ ) }
    OpenSqu   { Token ( _, TkOpenSqu     , _ ) }
    ClosSqu   { Token ( _, TkClosSqu     , _ ) }
    OpenAng   { Token ( _, TkOpenAng     , _ ) }
    ClosAng   { Token ( _, TkClosAng     , _ ) }
    Comma     { Token ( _, TkComma       , _ ) }
    IsEq      { Token ( _, TkIsEq        , _ ) }
    Assign    { Token ( _, TkAssign      , _ ) }
    Nil       { Token ( _, TkNil         , _ ) }
    SemiCo    { Token ( _, TkSemiCo      , _ ) }
    Cons      { Token ( _, TkCons        , _ ) }
    Hd        { Token ( _, TkHd          , _ ) }
    Tl        { Token ( _, TkTl          , _ ) }
    While     { Token ( _, TkWhile       , _ ) }
    Switch    { Token ( _, TkSwitch      , _ ) }
    Case      { Token ( _, TkCase        , _ ) }
    Default   { Token ( _, TkDefault     , _ ) }
    Colon     { Token ( _, TkColon       , _ ) }
    If        { Token ( _, TkIf          , _ ) }
    Else      { Token ( _, TkElse        , _ ) }
    Read      { Token ( _, TkRead        , _ ) }
    Write     { Token ( _, TkWrite       , _ ) }
    True      { Token ( _, TkTrue        , _ ) }
    False     { Token ( _, TkFalse       , _ ) }
    AtAsgn    { Token ( _, TkAtomAsgn    , _ ) }
    AtDoAsgn  { Token ( _, TkAtomDoAsgn  , _ ) }
    AtWhile   { Token ( _, TkAtomWhile   , _ ) }
    AtDoWhile { Token ( _, TkAtomDoWhile , _ ) }
    AtIf      { Token ( _, TkAtomIf      , _ ) }
    AtDoIf    { Token ( _, TkAtomDoIf    , _ ) }
    AtVar     { Token ( _, TkAtomVar     , _ ) }
    AtQuote   { Token ( _, TkAtomQuote   , _ ) }
    AtHd      { Token ( _, TkAtomHd      , _ ) }
    AtDoHd    { Token ( _, TkAtomDoHd    , _ ) }
    AtTl      { Token ( _, TkAtomTl      , _ ) }
    AtDoTl    { Token ( _, TkAtomDoTl    , _ ) }
    AtCons    { Token ( _, TkAtomCons    , _ ) }
    AtDoCons  { Token ( _, TkAtomDoCons  , _ ) }
    Var       { Token ( _, ITkVar _      , _ ) }
    Int       { Token ( _, ITkInt $$     , _ ) }

%%

PROGRAM :: { SuProgram }
PROGRAM : VAR Read VAR BLOCK Write VAR { SuProgram $1 $3 $4 $6 }

VAR :: { Name }
VAR : Var { Name (tkPath $1, tkVarName $1) }

EXPR :: { Expression }
EXPR : Cons EXPR EXPR       { Cons $2 $3            }
     | VAR                  { Var $1                }
     | OpenBrc EXPR ClosBrc { $2                    }
     | Hd EXPR              { Hd $2                 }
     | Tl EXPR              { Tl $2                 }
     | EXPR IsEq EXPR       { IsEq $1 $3            }
     | EXPLIST              { expFromHaskellList $1 }
     | VAL                  { Lit $1                }

ATOM :: { ETree }
ATOM : AtAsgn    { intToTree  2 }
     | AtDoAsgn  { intToTree  3 }
     | AtWhile   { intToTree  5 }
     | AtDoWhile { intToTree  7 }
     | AtIf      { intToTree 11 }
     | AtDoIf    { intToTree 13 }
     | AtVar     { intToTree 17 }
     | AtQuote   { intToTree 19 }
     | AtHd      { intToTree 23 }
     | AtDoHd    { intToTree 29 }
     | AtTl      { intToTree 31 }
     | AtDoTl    { intToTree 37 }
     | AtCons    { intToTree 41 }
     | AtDoCons  { intToTree 43 }

EXPLIST :: { [Expression] }
EXPLIST : OpenSqu ClosSqu          { []      }
        | OpenSqu EXPR RESTEXPLIST { $2 : $3 }

RESTEXPLIST :: { [Expression] }
RESTEXPLIST : Comma EXPR RESTEXPLIST { $2 : $3 }
            | ClosSqu                { []      }

COMMAND :: { SuCommand }
COMMAND : VAR Assign EXPR
            { SuAssign (Info (tkPath $2, tkLineNo $2)) $1 $3                }
        | VAR Assign OpenAng VAR ClosAng EXPR
            { Macro    (Info (tkPath $2, tkLineNo $2)) $1 (nameName $4) $6  }
        | While EXPR BLOCK
            { SuWhile  (Info (tkPath $1, tkLineNo $1)) $2 $3                }
        | If EXPR BLOCK Else BLOCK
            { SuIfElse (Info (tkPath $1, tkLineNo $1)) $2 $3 $5             }
        | If EXPR BLOCK
            { SuIfElse (Info (tkPath $1, tkLineNo $1)) $2 $3 []             }
        | Switch EXPR OpenCur SWITCHCONT
            { Switch   (Info (tkPath $1, tkLineNo $1)) $2 (fst $4) (snd $4) }

BLOCK :: { SuBlock }
BLOCK : OpenCur      ClosCur { [] }
      | OpenCur CMDS ClosCur { $2 }

CMDS :: { SuBlock }
CMDS : COMMAND SemiCo CMDS { $1 : $3 }
     | COMMAND             { [$1]    }

SWITCHCONT :: { ([(Expression, SuBlock)], SuBlock) }
SWITCHCONT : Case EXPR Colon CMDS SWITCHCONT { (($2, $4) : fst $5, snd $5) }
           | ClosCur                         { ([]               , []    ) }
           | Default Colon CMDS ClosCur      { ([]               , $3    ) }

VAL :: { ETree }
VAL : Nil                         { ENil         }
    | OpenAng VAL Dot VAL ClosAng { ECons $2 $4  }
    | Int                         { intToTree $1 }
    | True                        { intToTree 1  }
    | False                       { intToTree 0  }
    | ATOM                        { $1           }

-- LVALs are VALs, but also with lists allowed. They are only used for command
-- line input, as adding lists to general VALs that occur within EXPs casues
-- ambiguity.
LVAL :: { ETree }
LVAL : Nil                           { ENil                   }
     | OpenAng LVAL Dot LVAL ClosAng { ECons $2 $4            }
     | Int                           { intToTree $1           }
     | True                          { intToTree 1            }
     | False                         { intToTree 0            }
     | LVALLIST                      { treeFromHaskellList $1 }
     | ATOM                          { $1                     }

LVALLIST :: { [ETree] }
LVALLIST : OpenSqu ClosSqu           { []      }
         | OpenSqu LVAL RESTLVALLIST { $2 : $3 }

RESTLVALLIST :: { [ETree] }
RESTLVALLIST : Comma LVAL RESTLVALLIST { $2 : $3 }
             | ClosSqu                 { []      }
{
parseError :: [Token] -> ExceptT String IO a
parseError [] = throwError "Parse error: reached end of file while parsing"
parseError (tok : rest) = throwError $ concat
    [ "Parse error: "
    , (prettyPrintToken tok)
    , " in program/macro '"
    , tkPath tok
    , "', at line "
    , (show (tkLineNo tok))
    , ", char "
    , (show (tkCharNo tok))
    ]
}
