packages feed

vintage-basic-1.0: src/Language/VintageBasic/Result.hs

-- | Results of BASIC computations, including errors.

module Language.VintageBasic.Result(Result(..),RuntimeException(..),RuntimeError(..)) where

import Control.Monad.CPST.DurableTraps(ResultType(..))
import Language.VintageBasic.Printer(printVarName)
import Language.VintageBasic.Syntax(Label,VarName)
import Text.ParserCombinators.Parsec(sourceLine,sourceColumn)
import Text.ParserCombinators.Parsec.Error(ParseError,errorMessages,errorPos,showErrorMessages)

-- | The ultimate result value of a BASIC program.
data Result =
    Pass                                             -- ^ normal termination
    | ScanError ParseError                           -- ^ error in scanning line numbers
    | SyntaxError ParseError                         -- ^ tokenization or parsing error
    | LabeledRuntimeException Label RuntimeException -- ^ runtime exception (with line number)

instance ResultType Result where
    okValue = Pass

data RuntimeException =
    RuntimeError RuntimeError
    | Next (Maybe String)     -- ^ generated by @NEXT@
    | Return                  -- ^ generated by @RETURN@

instance Show Result where
    show Pass = "NORMAL TERMINATION"
    show (ScanError pe) = showParseError "LINE NUMBERING" "RAW LINE" "END OF FILE" pe
    show (SyntaxError pe) = showParseError "SYNTAX" "LINE" "END OF LINE" pe
    show (LabeledRuntimeException label x) = show x ++ " IN LINE " ++ show label

instance Show RuntimeException where
    show (RuntimeError err) = show err
    show (Next Nothing) = "!NEXT WITHOUT FOR ERROR"
    show (Next (Just s)) = "!NEXT WITHOUT FOR ERROR (VAR "++s++")"
    show Return = "!RETURN WITHOUT GOSUB ERROR"

data RuntimeError =
    TypeMismatchError
    | WrongNumberOfArgumentsError
    | InvalidArgumentError
    | DivisionByZeroError
    | BadGotoTargetError Label
    | BadGosubTargetError Label
    | BadRestoreTargetError Label
    | NegativeArrayDimError
    | ReDimensionedArrayError
    | MismatchedArrayDimensionsError
    | OutOfArrayBoundsError
    | UndefinedFunctionError VarName
    | OutOfDataError
    | EndOfInputError
  deriving Eq

instance Show RuntimeError where
    show TypeMismatchError = "!TYPE MISMATCH"
    show WrongNumberOfArgumentsError = "!WRONG NUMBER OF ARGUMENTS"
    show InvalidArgumentError = "!INVALID ARGUMENT"
    show DivisionByZeroError = "!DIVISION BY ZERO"
    show (BadGotoTargetError lab) = "!BAD GOTO TARGET " ++ show lab
    show (BadGosubTargetError lab) = "!BAD GOSUB TARGET " ++ show lab
    show (BadRestoreTargetError lab) = "!BAD RESTORE TARGET " ++ show lab
    show NegativeArrayDimError = "!NEGATIVE ARRAY DIM"
    show ReDimensionedArrayError = "!REDIM'D ARRAY"
    show MismatchedArrayDimensionsError = "!MISMATCHED ARRAY DIMENSIONS"
    show OutOfArrayBoundsError = "!OUT OF ARRAY BOUNDS"
    show (UndefinedFunctionError vn) = "!UNDEFINED FUNCTION " ++ printVarName vn
    show OutOfDataError = "!OUT OF DATA"
    show EndOfInputError = "!END OF INPUT"

showParseError
    :: String     -- ^ message describing the type of error
    -> String     -- ^ description of source line: @LINE@ or @RAW LINE@
    -> String     -- ^ what to call end of input
    -> ParseError -- ^ the parse error to show as text
    -> String
showParseError msgErrorType msgLine msgEndOfInput parseError =
    let pos = errorPos parseError
        messages = errorMessages parseError
        line = sourceLine pos
        col = sourceColumn pos
    in
        "!" ++ msgErrorType ++ " ERROR IN " ++ msgLine ++ " " ++ show line
        ++ ", COLUMN " ++ show col
        ++ showErrorMessages "OR" " UNKNOWN" " EXPECTING" " UNEXPECTED" msgEndOfInput messages