module ParseForeign (foreignDef) where

import Control.Applicative ((<$>), (<*>))
import Data.Either (partitionEithers)
import Text.Parsec hiding (newline,spaces)

import Ast
import ParseLib
import ParseExpr (term)
import ParseTypes
import Types (signalOf)


foreignDef = do try (reserved "foreign") ; whitespace
                importEvent <|> exportEvent

exportEvent = do
  try (reserved "export" >> whitespace >> reserved "jsevent" >> whitespace)
  js   <- jsVar    ; whitespace
  elm  <- lowVar   ; whitespace
  string "::"      ; whitespace
  tipe <- typeExpr
  case tipe of
    ADTPT "Signal" [pt] ->
        either fail (return . ExportEvent js elm . signalOf) (toForeignType pt)
    _ -> fail "When exporting events, the exported value must be a Signal."

importEvent = do
  try (reserved "import" >> whitespace >> reserved "jsevent" >> whitespace)
  js   <- jsVar ; whitespace
  base <- term <?> "Base case for imported signal (signals cannot be undefined)"
  whitespace
  elm  <- lowVar <?> "Name of imported signal"
  whitespace ; string "::" ; whitespace
  tipe <- typeExpr
  case tipe of
    ADTPT "Signal" [pt] ->
        either fail (return . ImportEvent js base elm . signalOf) (toForeignType pt)
    _ -> fail "When importing events, the imported value must be a Signal."


jsVar :: (Monad m) => ParsecT [Char] u m String
jsVar = betwixt '"' '"' $ do
  v <- (:) <$> (letter <|> char '_') <*> many (alphaNum <|> char '_')
  if v `notElem` jsReserveds then return v else
      fail $ "'" ++ v ++
          "' is not a good name for a importing or exporting JS values."

jsReserveds =
    [ "null", "undefined", "Nan", "Infinity", "true", "false", "eval"
    , "arguments", "int", "byte", "char", "goto", "long", "final", "float"
    , "short", "double", "native", "throws", "boolean", "abstract", "volatile"
    , "transient", "synchronized", "function", "break", "case", "catch"
    , "continue", "debugger", "default", "delete", "do", "else", "finally"
    , "for", "function", "if", "in", "instanceof", "new", "return", "switch"
    , "this", "throw", "try", "typeof", "var", "void", "while", "with", "class"
    , "const", "enum", "export", "extends", "import", "super", "implements"
    , "interface", "let", "package", "private", "protected", "public"
    , "static", "yield"
    ]