vintage-basic-1.0: src/Language/VintageBasic/Printer.hs
-- | Prettyprinter for BASIC syntax and values.
module Language.VintageBasic.Printer where
import Numeric
import Data.List
import Language.VintageBasic.LexCommon
import Language.VintageBasic.Syntax
import Language.VintageBasic.Builtins(Builtin,builtinToStrAssoc)
import Data.Maybe(fromJust)
-- | Prettyprint a BASIC literal.
printLit :: Literal -> String
printLit (FloatLit v) = let i = floor v :: Integer
in if fromInteger i == v
then show i
else show v
printLit (StringLit s) = show s
-- | Prettyprint a floating point value, BASIC style.
printFloat :: Float -> String
printFloat x | x == 0 = " 0"
printFloat x | x < 0 = "-" ++ printPosFloat (-x)
printFloat x | x > 0 = " " ++ printPosFloat x
printFloat _ = error "illegal number for printFloat"
-- | The maximum number of decimal digits needed to display the mantissa of a
-- Float. For IEEE 754-2008 binary32 format, this is 8 decimal digits.
maxFloatDigits :: Int
maxFloatDigits = ceiling (log 2 / log 10 * fromIntegral(floatDigits (0::Float)) :: Float)
printPosFloat :: Float -> String
printPosFloat x =
let (digits, ex) = floatToDigits 10 x
in
if ex <= maxFloatDigits && length digits - ex <= maxFloatDigits
then
if ex >= length digits
then concatMap show (padDigitsRight digits ex)
else
if ex > 0
then concatMap show (take ex digits) ++ "."
++ concatMap show (drop ex digits)
else "." ++ concatMap show (padDigitsLeft digits ex)
else
concatMap show (take 1 digits) ++ "."
++ concatMap show (drop 1 digits) ++ "E"
++ (if ex >= 1 then "+" else "") ++ show (ex - 1)
padDigitsRight :: [Int] -> Int -> [Int]
padDigitsRight digits ex = digits ++ replicate (ex - length digits) 0
padDigitsLeft :: [Int] -> Int -> [Int]
padDigitsLeft digits ex = replicate (-ex) 0 ++ digits
-- | Prettyprint a variable name.
printVarName :: VarName -> String
printVarName (VarName varType name) = name ++ case varType of
FloatType -> ""
IntType -> "%"
StringType -> "$"
printVar :: Var -> String
printVar (ScalarVar vn) = printVarName vn
printVar (ArrVar vn xs) = printVarName vn ++ printArgs xs
printArgs :: [Expr] -> String
printArgs xs = "(" ++ concat (intersperse "," (map printExpr xs)) ++ ")"
printOp :: BinOp -> String
printOp AddOp = "+"
printOp SubOp = "-"
printOp MulOp = "*"
printOp DivOp = "/"
printOp PowOp = "^"
printOp EqOp = "="
printOp NEOp = "<>"
printOp LTOp = "<"
printOp LEOp = "<="
printOp GTOp = ">"
printOp GEOp = ">="
printOp AndOp = " AND "
printOp OrOp = " OR "
printBuiltin :: Builtin -> String
printBuiltin b = fromJust $ lookup b builtinToStrAssoc
printExpr :: Expr -> String
printExpr (LitX lit) = printLit lit
printExpr (VarX var) = printVar var
printExpr (FnX vn xs) = printVarName vn ++ printArgs xs
printExpr (MinusX x) = "-" ++ printExpr x
printExpr NextZoneX = ","
printExpr EmptySeparatorX = ";"
printExpr (NotX x) = "NOT " ++ printExpr x
printExpr (ParenX x) = "(" ++ printExpr x ++ ")"
printExpr (BinX op x1 x2) = printExpr x1 ++ printOp op ++ printExpr x2
printExpr (BuiltinX b xs) = printBuiltin b ++ printArgs xs
printTaggedStatement :: Tagged Statement -> String
printTaggedStatement (Tagged _ statement) = printStatement statement
printStatement :: Statement -> String
printStatement (LetS v x) = "LET " ++ printVar v ++ "=" ++ printExpr x
printStatement (DimS arrs) =
"DIM " ++ concat (intersperse "," [printVarName vn ++ printArgs xs | (vn, xs) <- arrs])
printStatement (GotoS label) = "GOTO " ++ show label
printStatement (GosubS label) = "GOSUB " ++ show label
printStatement (OnGotoS x labels) = "ON " ++ printExpr x ++ " GOTO " ++ (concat (intersperse ", " (map show labels)))
printStatement (OnGosubS x labels) = "ON " ++ printExpr x ++ " GOSUB " ++ (concat (intersperse ", " (map show labels)))
printStatement ReturnS = "RETURN"
printStatement (IfS x ss) =
"IF " ++ printExpr x ++ " THEN " ++ printStatementList ss
printStatement (ForS vn x1 x2 x3) =
"FOR " ++ printVarName vn ++ "=" ++ printExpr x1 ++ " TO " ++ printExpr x2
++ (case x3
of (LitX (FloatLit 1)) -> ""
_ -> " STEP " ++ printExpr x3)
printStatement (NextS Nothing) = "NEXT"
printStatement (NextS (Just vns)) =
"NEXT " ++ (concat $ intersperse "," (map printVarName vns))
printStatement (PrintS xs) =
"PRINT " ++ (concat $ intersperse " " (map printExpr xs))
printStatement (InputS prompt vs) =
"INPUT " ++ (case prompt of Nothing -> ""; Just ps -> show ps ++ ";")
++ (concat $ intersperse "," (map printVar vs))
printStatement EndS = "END"
printStatement StopS = "STOP"
printStatement RandomizeS = "RANDOMIZE"
printStatement (ReadS vars) = "READ " ++ (concat (intersperse ", " (map printVar vars)))
printStatement (RestoreS Nothing) = "RESTORE"
printStatement (RestoreS (Just n)) = "RESTORE " ++ show n
printStatement (DataS s) = "DATA " ++ s
printStatement (DefFnS vn vns x) =
"DEF FN" ++ printVarName vn ++ "(" ++ (concat (intersperse ", " (map printVarName vns))) ++ ")"
++ " = " ++ printExpr x
printStatement (RemS s) = "REM" ++ s
printStatementList :: [Tagged Statement] -> String
printStatementList ss = concat $ intersperse ":" (map printTaggedStatement ss)
printLine :: Line -> String
printLine (Line n ss) = show n ++ " " ++ printStatementList ss ++ "\n"
printLines :: [Line] -> String
printLines progLines = concatMap printLine progLines