packages feed

vintage-basic 1.0.1 → 1.0.3

raw patch · 22 files changed

+231/−177 lines, 22 filesdep +hashabledep +hashtablesdep ~basesetup-changed

Dependencies added: hashable, hashtables

Dependency ranges changed: base

Files

LICENSE.txt view
@@ -1,4 +1,4 @@-Copyright (c) 2009, Lyle Kopnicky
+Copyright (c) 2009-2017, Lyle Kopnicky
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
README.txt view
@@ -1,15 +1,27 @@ To compile:
 
-runhaskell Setup.hs configure
-runhaskell Setup.hs build
+cabal configure
+cabal build
 
+...or with stack:
+
+stack build
+
 To run unit tests:
 
-runhaskell Setup.hs test
+cabal test
 
+..or with stack:
+
+stack test
+
 To install:
 
-runhaskell Setup.hs install
+cabal install
+
+...or with stack:
+
+stack install
 
 You can then run the resulting program as
 
− Setup.hs
@@ -1,16 +0,0 @@-#!/usr/local/bin/runhaskell
-
-import Distribution.Simple
-import Distribution.PackageDescription (PackageDescription)
-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo)
-import System.Cmd (system)
-import Distribution.Simple.LocalBuildInfo
-import System.Directory (removeFile)
-
-main = defaultMainWithHooks (simpleUserHooks {runTests = runAllTests})
-
-runAllTests :: Args -> Bool -> PackageDescription -> LocalBuildInfo -> IO ()
-runAllTests a b pd lb = do
-    system "runhaskell run_tests.hs"
-    removeFile "test_driver.hs"
-    return ()
doc/Vintage_BASIC_Developer_Notes.html view
@@ -1,6 +1,6 @@ <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html><head><meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"><title>Vintage BASIC Developer Notes</title></head>
-<body><h1>Vintage BASIC Developer Notes 1.0.1</h1><p>© 2009, <a href="mailto://lyle@vintage-basic.net">Lyle Kopnicky</a></p><p>I began Vintage BASIC as a class project at the <a href="http://www.ogi.edu">Oregon Graduate
+<body><h1>Vintage BASIC Developer Notes 1.0.3</h1><p>© 2009&mdash;2017, <a href="mailto://lyle@vintage-basic.net">Lyle Kopnicky</a></p><p>I began Vintage BASIC as a class project at the <a href="http://www.ogi.edu">Oregon Graduate
 Institute</a>. My aim was to demonstrate advanced knowledge of monads in
 <a href="http://www.haskell.org">Haskell</a>.
 It occurred to me that BASIC's dynamic control structures would be
@@ -37,4 +37,4 @@ tests rather than unit tests. They test the interpretation of the BASIC
 language straight from the textual source. However they are still unit
 tests in the sense that they are focused on exercising the behavior of
-the interpreter proper.</p><p>The tests are arranged in a hierarchical directory structure parallel to the implementation: in the <code>test</code> directory rather than <code>src</code>. They are executed by a harness called <code>run_tests.hs</code> in the root directory. This custom-written script finds all of the modules named ending in <code>_test</code>, and the functions within those modules starting with <code>test_</code>. It builds a driver program that calls all the tests, and executes it.</p><p>The tests can be run by calling <code>runhaskell run_tests.hs</code>, or preferably via <code>runhaskell Setup.hs test</code>.</p></body></html>+the interpreter proper.</p><p>The tests are arranged in a hierarchical directory structure parallel to the implementation: in the <code>test</code> directory rather than <code>src</code>. They are executed by a harness called <code>run_tests.hs</code> in the root directory. This custom-written script finds all of the modules named ending in <code>_test</code>, and the functions within those modules starting with <code>test_</code>. It builds a driver program that calls all the tests, and executes it.</p><p>The tests can be run by calling <code>cabal test</code>, or if you have <code>stack</code> installed, then via <code>stack test</code>.</p></body></html>
doc/Vintage_BASIC_Users_Guide.html view
@@ -1,11 +1,11 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">+<!DOCTYPE html>
 <html><head>
 <meta content="text/html; charset=ISO-8859-1" http-equiv="content-type"><title>Vintage BASIC User's Guide</title>
 
 </head>
 <body>
-<h1>Vintage BASIC User's Guide 1.0.1</h1>
-<p>© 2009, <a href="mailto://lyle@vintage-basic.net">Lyle
+<h1>Vintage BASIC User's Guide 1.0.3</h1>
+<p>© 2009&ndash;2017, <a href="mailto://lyle@vintage-basic.net">Lyle
 Kopnicky</a></p>
 <p>Congratulations on installing Vintage BASIC! You are now the
 proud owner of a copy of a versatile tool for BASIC development.</p>
@@ -59,39 +59,23 @@ </ul>
 <h2><a class="mozTocH2" name="mozTocId731362"></a>System
 Requirements and Installation</h2>
-<p>Vintage BASIC requires minimal resources, and will run on any
-platform with available Haskell compiler. The recommended compiler is <a href="http://www.haskell.org/ghc">GHC</a>. You can
-also download some pre-compiled binaries on the web site, <a href="http://www.vintage-basic.net">vintage-basic.net</a>.</p>
-<h3>Installation of&nbsp;binary version</h3>
-For Windows, you can simply download <code>vintbas.exe</code>,
-and put it it somewhere in your path. For other systems, consult the <a href="http://www.vintage-basic.net/download.html">download page</a>.<br>
-<h3>Installation from source</h3>
-<p>You can download the source code tarball,&nbsp; <code>vintage-basic-1.0.1.tar.gz</code>,&nbsp;from
-either <a href="http://www.vintage-basic.net/downloads.html">vintage-basic.net</a>
-or <a href="http://hackage.haskell.org/cgi-bin/hackage-scripts/package/vintage-basic">Hackage</a>.</p>
-<p>You should first unpack the tarball&nbsp;<code></code>using
-a tool such as tar, 7-Zip or WinZip. Then open a
-shell/console&nbsp;in
-the vintage-basic-1.0.1 directory, and type:
-http://hackage.haskell.org/packages/archive/armada/0.1/armada-0.1.tar.gz<br>
-<br>
-<code>&nbsp;&nbsp;runhaskell Setup.hs configure<br>
-&nbsp; runhaskell Setup.hs build</code></p>
-<p>The executable will be generated in <code>dist/build/vintbas/vintbas</code>
-on Mac/Linux, or <code>dist\build\vintbas\vintbas.exe</code>
-on Windows. You can then move this executable anywhere in your path to
-run it. Or, use</p>
-<p><code>&nbsp;&nbsp;runhaskell Setup.hs install</code></p>
-<p>See the <a href="http://haskell.org/haskellwiki/Cabal/How_to_install_a_Cabal_package">Cabal
-package installation instructions</a> for more details.<br>
-</p>
+<p>Vintage BASIC requires minimal resources, and will run on Windows, Mac, and Linux.
+(It can be compiled from source to run on these or other platforms, too.)
+Please see the <a href="http://www.vintage-basic.net/downloads">downloads page</a>
+for installation instructions.
 <h2><a class="mozTocH2" name="mozTocId230604"></a>Running
 Vintage BASIC</h2>
-<p>The interpreter consists of one command-line tool, <code><span class="">vintbas</span></code>.+<h3>From the command line</h3>
+<p>The interpreter consists of one command-line tool, <code><span class="">vintbas</span></code>.
 It takes one filename parameter, the name of a BASIC source file.
 By convention (but not fiat), it should end in <code>.bas</code>. So, if you've written
 BASIC code and saved it in <code>miracle.bas</code>, you'd
 run it by typing <code>vintbas miracle.bas</code>.</p>
+<h3>On Windows</h3>
+<p>If you have used the Windows installer, you can open Vintage BASIC using the
+Vintage BASIC Prompt item in the Vintage BASIC folder on the Start Menu. Or, you
+can just open Command Prompt. If you have a BASIC program saved with a <code>.bas</code>
+extension, you can double-click on it in Explorer to run it.</p>
 <h2><a class="mozTocH2" name="mozTocId153285"></a>BASIC
 Program Lines</h2>
 <p>Each line in your BASIC source file should start with a BASIC
@@ -122,7 +106,7 @@ <h2><a class="mozTocH2" name="mozTocId839205"></a>BASIC
 Syntax</h2>
 Each line of BASIC consists of a series of statements, separated by
-semicolons. Statements are executed in order, unless an error or
+colons. Statements are executed in order, unless an error or
 control flow statement is encountered.
 <p>BASIC keywords are not case sensitive. You may
 freely use
@@ -748,4 +732,4 @@ <dd>The wrong number of arguments were passed to the builtin or
 user-defined function.</dd>
 </dl>
-</body></html>+</body></html>
run_tests.hs view
@@ -3,8 +3,8 @@ import Data.Array ((!)) import Data.List (intersperse) import Data.Maybe (catMaybes)-import System.Cmd (system)-import System.Directory (doesDirectoryExist,getCurrentDirectory,getDirectoryContents)+import System.Process (system)+import System.Directory (doesDirectoryExist,getCurrentDirectory,getDirectoryContents,removeFile) import System.FilePath ((</>)) import System.Environment (getArgs) import Text.Regex.Base@@ -32,6 +32,7 @@    let testCode = genTestDriver modulesWithTests    writeFile "test_driver.hs" testCode    system ("runhaskell -itest -isrc test_driver.hs")+   removeFile "test_driver.hs"  findFilesInSubdirs :: FilePath -> IO [String] findFilesInSubdirs dir = do
src/Control/Monad/CPST.hs view
@@ -10,18 +10,35 @@  import Control.Monad.Reader import Control.Monad.State-import Control.Monad.Trans  -- | Definition of the CPST (continuation-passing style) monad.-newtype Monad m => CPST o m i =+newtype CPST o m i =+      -- o is the type of the ultimate output+      -- m is the nested monad+      -- i is the type of the intermediate result of this step       CPST { unCPST :: (i -> m o) -> m o } -instance (Monad m) => Functor (CPST o m) where-    fmap f (CPST m') = CPST (\k -> m' (k . f))+instance Functor (CPST o m) where+    -- (a -> b) -> CPST o m a -> CPST o m b+    -- mx :: (a -> m o) -> m o+    -- k :: b -> m o+    -- f :: a -> b+    -- k . f :: a -> m o+    fmap f (CPST mx) = CPST (\k -> mx (k . f)) -instance Monad m => Monad (CPST o m) where+instance Applicative (CPST o m) where+    pure x = CPST (\k -> k x)+    -- CPST o m (a -> b) -> CPST o m a -> CPST o m b+    -- mf :: ((a -> b) -> m o) -> m o+    -- mx :: (a -> m o) -> m o+    -- k :: b -> m o+    -- f :: a -> b+    -- x :: a+    (CPST mf) <*> (CPST mx) = CPST (\k -> mf (\f -> mx (\x -> k (f x))))++instance Monad (CPST o m) where       return x = CPST (\k -> k x)-      (CPST m') >>= f = CPST (\k -> m' (\x -> unCPST (f x) k))+      (CPST mx) >>= f = CPST (\k -> mx (\x -> unCPST (f x) k))  -- The basic monad utilities: runCPST, lift, liftIO 
src/Control/Monad/CPST/DurableTraps.hs view
@@ -4,7 +4,6 @@  module Control.Monad.CPST.DurableTraps where -import Control.Monad import Control.Monad.CPST import Control.Monad.CPST.ExceptionHandlers(capture,install,raise) 
src/Control/Monad/CPST/ExceptionHandlers.hs view
@@ -3,7 +3,6 @@  module Control.Monad.CPST.ExceptionHandlers where -import Control.Monad import Control.Monad.CPST  -- The inward (catching) exception handlers --
src/Language/VintageBasic/BasicMonad.hs view
@@ -9,15 +9,13 @@ import Control.Monad.CPST import Control.Monad.CPST.DurableTraps import Control.Monad.Reader-import Control.Monad.State-import Control.Monad.Trans-import Data.HashTable+import Control.Monad.State (StateT, runStateT, get, put, modify)+import Data.HashTable.IO import Data.IORef import Data.Array.IO import Data.Maybe import Data.Time import IO.IOStream-import Language.VintageBasic.Printer(printVarName) import Language.VintageBasic.Result import Language.VintageBasic.Syntax import System.Random@@ -39,11 +37,11 @@  -- | The store of BASIC variables. data BasicStore = BasicStore {-    scalarTable :: HashTable VarName (IORef Val),+    scalarTable :: BasicHashTable VarName (IORef Val),       -- ^ scalar variable assignments-    arrayTable :: HashTable VarName ([Int], IOArray Int Val),+    arrayTable :: BasicHashTable VarName ([Int], IOArray Int Val),       -- ^ array variable values (@[Int]@ lists the bound of each dimension)-    fnTable :: HashTable VarName (IORef ([Val] -> Code Val))+    fnTable :: BasicHashTable VarName (IORef ([Val] -> Code Val))       -- ^ user-defined function (@DEF FN@) definitions } @@ -90,12 +88,12 @@     -> IO () runProgram inputHandle outputHandle prog = do     vFlush outputHandle-    st <- new (==) (hashString . printVarName)-    at <- new (==) (hashString . printVarName)-    ft <- new (==) (hashString . printVarName)+    st <- new+    at <- new+    ft <- new     let store = BasicStore st at ft     let state = (BasicState inputHandle outputHandle 0 0 0 (mkStdGen 0) [])-    runStateT (runReaderT (runCPST (catchC errorDumper prog)) store) state+    _ <- runStateT (runReaderT (runCPST (catchC errorDumper prog)) store) state     return ()  -- | Raises a runtime error if the assertion is False.
src/Language/VintageBasic/FloatParser.hs view
@@ -32,7 +32,7 @@     float2P :: GenParser tok st String     float2P = do         i <- many digitP-        dotP+        _ <- dotP         f <- many digitP         return ("0"++i++"."++f++"0") @@ -43,7 +43,7 @@      expP :: GenParser tok st String     expP = do-        charEP+        _ <- charEP         esgn <- option "" sgnP         i <- many1 digitP         return ("E"++esgn++i)
src/Language/VintageBasic/Interpreter.hs view
@@ -303,7 +303,7 @@     setVar var val  interpS _ (PrintS xs) = do-    mapM (\x -> eval x >>= printVal) xs+    _ <- mapM (\x -> eval x >>= printVal) xs     if null xs || not (isPrintSeparator (last xs))         then printString "\n"         else return ()@@ -361,7 +361,7 @@ interpS jumpTable (GosubS lab) =     do let maybeCode = programLookup jumpTable lab        assert (isJust maybeCode) (BadGosubTargetError lab)-       catchC gosubHandler (fromJust maybeCode)+       _ <- catchC gosubHandler (fromJust maybeCode)        return () interpS _ ReturnS = raiseRuntimeException Return @@ -494,7 +494,7 @@     is <- checkArrInds inds     -- add 1, so that range is from 0 to user-specified bound     let bounds = map (1+) is-    dimArray vn bounds+    _ <- dimArray vn bounds     return ()  checkArrInds :: [Val] -> Basic (BasicExcep Result ()) [Int]
src/Language/VintageBasic/LineScanner.hs view
@@ -10,12 +10,12 @@  eol :: Parser Char eol = do-    optionally (char '\r')+    _ <- optionally (char '\r')     newline  blankLineP :: Parser () blankLineP = do-    eol <?> ""+    _ <- eol <?> ""     whiteSpace  rawLineP :: Parser RawLine@@ -25,14 +25,14 @@     pos <- getPosition     s <- manyTill (anyChar <?> "CHARACTER") (eol <?> "END OF LINE")     whiteSpace-    many blankLineP+    _ <- many blankLineP     return (Tagged (setSourceLine pos n) s)  rawLinesP :: Parser [RawLine] rawLinesP = do     whiteSpace-    many blankLineP+    _ <- many blankLineP     ls <- many rawLineP-    many blankLineP+    _ <- many blankLineP     eof <?> "END OF FILE"     return ls
src/Language/VintageBasic/Parser.hs view
@@ -62,13 +62,13 @@ intVarNameP :: TokParser VarName intVarNameP = do     name <- varBaseP-    tokenP (==PercentTok)+    _ <- tokenP (==PercentTok)     return (VarName IntType name)  stringVarNameP :: TokParser VarName stringVarNameP = do     name <- varBaseP-    tokenP (==DollarTok)+    _ <- tokenP (==DollarTok)     return (VarName StringType name)  -- Look for string and int vars first because of $ and % suffixes.@@ -114,23 +114,23 @@  argsP :: TokParser [Expr] argsP =-    do tokenP (==LParenTok)+    do _ <- tokenP (==LParenTok)        xs <- sepBy exprP (tokenP (==CommaTok))-       tokenP (==RParenTok)+       _ <- tokenP (==RParenTok)        return xs  fnXP :: TokParser Expr fnXP = do-    tokenP (==FnTok)+    _ <- tokenP (==FnTok)     vn <- varNameP     args <- argsP     return (FnX vn args)  parenXP :: TokParser Expr parenXP =-    do tokenP (==LParenTok)+    do _ <- tokenP (==LParenTok)        x <- exprP-       tokenP (==RParenTok)+       _ <- tokenP (==RParenTok)        return (ParenX x)  primXP :: TokParser Expr@@ -151,10 +151,10 @@  binary :: Token -> (Expr -> Expr -> Expr) -> Assoc -> Operator (Tagged Token) () Expr binary tok fun assoc =-    Infix (do tokenP (==tok); return fun) assoc+    Infix (do _ <- tokenP (==tok); return fun) assoc prefix :: Token -> (Expr -> Expr) -> Operator (Tagged Token) () Expr prefix tok fun =-    Prefix (do tokenP (==tok); return fun)+    Prefix (do _ <- tokenP (==tok); return fun)  -- | Parses a BASIC expression from tokenized source. exprP :: TokParser Expr@@ -164,52 +164,52 @@  letSP :: TokParser Statement letSP = do-    optionally (tokenP (==LetTok))+    _ <- optionally (tokenP (==LetTok))     v <- varP-    tokenP (==EqTok)+    _ <- tokenP (==EqTok)     x <- exprP     return (LetS v x)  gotoSP :: TokParser Statement gotoSP = do-    try (tokenP (==GoTok) >> tokenP (==ToTok))+    _ <- try (tokenP (==GoTok) >> tokenP (==ToTok))     n <- lineNumP     return (GotoS n)  gosubSP :: TokParser Statement gosubSP = do-    try (tokenP (==GoTok) >> tokenP (==SubTok))+    _ <- try (tokenP (==GoTok) >> tokenP (==SubTok))     n <- lineNumP     return (GosubS n)  returnSP :: TokParser Statement returnSP =-    do tokenP (==ReturnTok)+    do _ <- tokenP (==ReturnTok)        return ReturnS  onGotoSP :: TokParser Statement onGotoSP = try $ do-    tokenP (==OnTok)+    _ <- tokenP (==OnTok)     x <- exprP-    tokenP (==GoTok)-    tokenP (==ToTok)+    _ <- tokenP (==GoTok)+    _ <- tokenP (==ToTok)     ns <- sepBy1 lineNumP (tokenP (==CommaTok))     return (OnGotoS x ns)  onGosubSP :: TokParser Statement onGosubSP = try $ do-    tokenP (==OnTok)+    _ <- tokenP (==OnTok)     x <- exprP-    tokenP (==GoTok)-    tokenP (==SubTok)+    _ <- tokenP (==GoTok)+    _ <- tokenP (==SubTok)     ns <- sepBy1 lineNumP (tokenP (==CommaTok))     return (OnGosubS x ns)  ifSP :: TokParser Statement ifSP =-    do tokenP (==IfTok)+    do _ <- tokenP (==IfTok)        x <- exprP-       tokenP (==ThenTok)+       _ <- tokenP (==ThenTok)        target <- try ifSPGoto <|> statementListP        return (IfS x target) @@ -221,11 +221,11 @@  forSP :: TokParser Statement forSP = do-    tokenP (==ForTok)+    _ <- tokenP (==ForTok)     vn <- varNameP-    tokenP (==EqTok)+    _ <- tokenP (==EqTok)     x1 <- exprP-    tokenP (==ToTok)+    _ <- tokenP (==ToTok)     x2 <- exprP     x3 <- option (LitX (FloatLit 1)) (tokenP (==StepTok) >> exprP)     return (ForS vn x1 x2 x3)@@ -233,7 +233,7 @@ -- | Parses a @NEXT@ and an optional variable list. nextSP :: TokParser Statement nextSP = do-    tokenP (==NextTok)+    _ <- tokenP (==NextTok)     vns <- sepBy varNameP (tokenP (==CommaTok))     if length vns > 0         then return (NextS (Just vns))@@ -241,7 +241,7 @@  printSP :: TokParser Statement printSP =-    do tokenP (==PrintTok)+    do _ <- tokenP (==PrintTok)        xs <- many printExprP        return (PrintS xs) @@ -250,17 +250,17 @@  emptySeparatorP :: TokParser Expr emptySeparatorP = do-    tokenP (==SemiTok)+    _ <- tokenP (==SemiTok)     return EmptySeparatorX  nextZoneP :: TokParser Expr nextZoneP = do-    tokenP (==CommaTok)+    _ <- tokenP (==CommaTok)     return NextZoneX  inputSP :: TokParser Statement inputSP =-    do tokenP (==InputTok)+    do _ <- tokenP (==InputTok)        ps <- option Nothing inputPrompt        vs <- sepBy1 varP (tokenP (==CommaTok))        return (InputS ps vs)@@ -268,17 +268,17 @@ inputPrompt :: TokParser (Maybe String) inputPrompt =     do (StringLit p) <- stringLitP-       tokenP (==SemiTok)+       _ <- tokenP (==SemiTok)        return (Just p)  endSP :: TokParser Statement endSP =-    do tokenP (==EndTok)+    do _ <- tokenP (==EndTok)        return EndS  stopSP :: TokParser Statement stopSP =-    do tokenP (==StopTok)+    do _ <- tokenP (==StopTok)        return StopS  arrDeclP :: TokParser (VarName, [Expr])@@ -290,7 +290,7 @@  dimSP :: TokParser Statement dimSP = do-   tokenP (==DimTok)+   _ <- tokenP (==DimTok)    arrDecls <- sepBy1 arrDeclP (tokenP (==CommaTok))    return (DimS arrDecls) @@ -301,13 +301,13 @@  readSP :: TokParser Statement readSP = do-    tokenP (==ReadTok)+    _ <- tokenP (==ReadTok)     vs <- sepBy1 varP (tokenP (==CommaTok))     return (ReadS vs)  restoreSP :: TokParser Statement restoreSP = do-    tokenP (==RestoreTok)+    _ <- tokenP (==RestoreTok)     maybeLineNum <- optionally lineNumP     return (RestoreS maybeLineNum) @@ -318,13 +318,13 @@  defFnSP :: TokParser Statement defFnSP = do-    tokenP (==DefTok)-    tokenP (==FnTok)+    _ <- tokenP (==DefTok)+    _ <- tokenP (==FnTok)     name <- varNameP-    tokenP (==LParenTok)+    _ <- tokenP (==LParenTok)     params <- sepBy1 varNameP (tokenP (==CommaTok))-    tokenP (==RParenTok)-    tokenP (==EqTok)+    _ <- tokenP (==RParenTok)+    _ <- tokenP (==EqTok)     expr <- exprP     return (DefFnS name params expr) @@ -345,7 +345,7 @@ -- | Parses a list of statements from a tokenized BASIC source line. statementListP :: TokParser [Tagged Statement] statementListP = do-    many (tokenP (==ColonTok))+    _ <- many (tokenP (==ColonTok))     sl <- sepEndBy1 statementP (many1 (tokenP (==ColonTok)))     eol <?> ": OR END OF LINE"     return sl
src/Language/VintageBasic/RuntimeParser.hs view
@@ -17,7 +17,7 @@  stringP :: Parser String stringP =-    do char '"'+    do _ <- char '"'        s <- manyTill anyChar (char '"')        spaces        return s
src/Language/VintageBasic/Syntax.hs view
@@ -1,9 +1,13 @@+{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-}+ -- | Describes the abstract syntax of BASIC.  module Language.VintageBasic.Syntax where  import Language.VintageBasic.Builtins(Builtin) import Language.VintageBasic.LexCommon(Tagged(..))+import GHC.Generics (Generic)+import Data.Hashable  -- | A BASIC line number. type Label = Int@@ -11,7 +15,7 @@ -- | BASIC value types. The IntType is used for storage or function arguments -- but is always converted to FloatType for use in expressions. data ValType = FloatType | IntType | StringType-    deriving (Show,Eq)+    deriving (Show,Eq,Generic,Hashable)  class Typeable a where     typeOf :: a -> ValType@@ -29,7 +33,7 @@     typeOf (StringLit _) = StringType  data VarName = VarName ValType String-    deriving (Show,Eq)+    deriving (Show,Eq,Generic,Hashable)  instance Typeable VarName where     typeOf (VarName valType _) = valType
src/Language/VintageBasic/Tokenizer.hs view
@@ -8,7 +8,7 @@     (Token(..),TokenizedLine,isDataTok,isRemTok,charTokTest,taggedCharToksToString,isStringTok,      isBuiltinTok,taggedTokensP,tokenP,printToken) where -import Data.Char(toUpper)+import Data.Char(toLower,toUpper) import Language.VintageBasic.Builtins(Builtin,builtinToStrAssoc) import Language.VintageBasic.LexCommon import Text.ParserCombinators.Parsec@@ -34,13 +34,16 @@            | SpaceTok | DotTok | CharTok { getCharTokChar :: Char }              deriving (Eq,Show) +caseInsensitiveChar :: Char -> Parser Char+caseInsensitiveChar c = char (toLower c) <|> char (toUpper c)+ keyword :: String -> Parser String-keyword s = try (string s) <?> ("keyword " ++ s)+keyword s = try (mapM caseInsensitiveChar s) <?> ("keyword " ++ s)  stringTokP :: Parser Token -- no special escape chars allowed stringTokP =-    do char '"'+    do _ <- char '"'        s <- manyTill anyChar (char '"')        whiteSpace        return (StringTok s)@@ -50,7 +53,7 @@ isStringTok _ = False  remTokP :: Parser Token-remTokP = do keyword "REM"+remTokP = do _ <- keyword "REM"              s <- many anyChar              return (RemTok s) @@ -60,7 +63,7 @@  dataTokP :: Parser Token dataTokP =-    do keyword "DATA"+    do _ <- keyword "DATA"        s <- many anyChar        return (DataTok s) @@ -132,7 +135,7 @@  anyTokP :: Parser Token anyTokP = choice ([spaceTokP, stringTokP, remTokP, dataTokP]-                  ++ [do keyword s; whiteSpace; return t | (s,t) <- strToTokAssoc]+                  ++ [do _ <- keyword s; whiteSpace; return t | (s,t) <- strToTokAssoc]                   ++ [charTokP]) <?> "LEGAL BASIC CHARACTER"  isBuiltinTok :: Token -> Bool
test/Language/VintageBasic/Asserts.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.VintageBasic.Asserts where
 
 import Data.IORef
test/Language/VintageBasic/FloatParser_test.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE FlexibleContexts #-}
+
 module Language.VintageBasic.FloatParser_test where
 
 import Test.HUnit
test/Language/VintageBasic/Interpreter_test.hs view
@@ -270,7 +270,7 @@ 
 test_rnd = testProgramOutput
     "1 ?RND(-1):?RND(1):?RND(1):?RND(0):?RND(10):?RND(-2.5)\n"
-    " .35925463 \n .3306234 \n .13648525 \n .13648525 \n .53864 \n .10321328 \n"
+    " .72774446 \n .6806801 \n .2628854 \n .2628854 \n .8841321 \n .2695619 \n"
 
 test_rnd_errors = testExpressions [
     ("RND()", "!WRONG NUMBER OF ARGUMENTS IN LINE 1"),
test/Language/VintageBasic/Tokenizer_test.hs view
@@ -1,5 +1,6 @@ module Language.VintageBasic.Tokenizer_test where +import Data.Char(toLower) import Test.HUnit import Language.VintageBasic.Asserts import Language.VintageBasic.Builtins(Builtin(..))@@ -10,41 +11,63 @@ assertTokenizerResult = assertParseResult withCol SyntaxError taggedTokensP assertTokenizerError  = assertParseError  withCol SyntaxError taggedTokensP -test_taggedTokensP = TestCase $ do-  let source = [-                ",:;()$%=<><=>=><+-*/^.?",-                "ABSASCCHR$COSEXPINTLEFT$LENLOGMID$RIGHT$RNDSGNSINSPCSQRSTR$TABTANVAL",-                "ANDORNOTLETDIMONGOSUBRETURNIFTHENFORTOSTEPNEXTPRINTINPUT",-                "RANDOMIZEREADRESTOREDEFFNENDSTOP",-                "\"hello\"REMGOFORTH\"ANDREAD\"",-                "XDATATODATA"-               ]-  let expectedLenAndTokss = [-                        [(1,CommaTok), (1,ColonTok), (1,SemiTok), (1,LParenTok), (1,RParenTok),-                         (1,DollarTok), (1,PercentTok), (1,EqTok), (2,NETok), (2,LETok), (2,GETok),-                         (1,GTTok), (1,LTTok), (1,PlusTok), (1,MinusTok), (1,MulTok), (1,DivTok),-                         (1,PowTok), (1,DotTok), (1,PrintTok)],-                        [(3,BuiltinTok AbsBI), (3,BuiltinTok AscBI), (4,BuiltinTok ChrBI),-                         (3,BuiltinTok CosBI), (3,BuiltinTok ExpBI), (3,BuiltinTok IntBI),-                         (5,BuiltinTok LeftBI), (3,BuiltinTok LenBI), (3,BuiltinTok LogBI),-                         (4,BuiltinTok MidBI), (6,BuiltinTok RightBI), (3,BuiltinTok RndBI),-                         (3,BuiltinTok SgnBI), (3,BuiltinTok SinBI), (3,BuiltinTok SpcBI),-                         (3,BuiltinTok SqrBI), (4,BuiltinTok StrBI), (3,BuiltinTok TabBI),-                         (3,BuiltinTok TanBI), (3,BuiltinTok ValBI)],-                        [(3,AndTok), (2,OrTok), (3,NotTok), (3,LetTok), (3,DimTok), (2,OnTok),-                         (2,GoTok), (3,SubTok), (6,ReturnTok), (2,IfTok), (4,ThenTok), (3,ForTok),-                         (2,ToTok), (4,StepTok), (4,NextTok), (5,PrintTok), (5,InputTok)],-                        [(9,RandomizeTok), (4,ReadTok), (7,RestoreTok), (3,DefTok), (2,FnTok),-                         (3,EndTok), (4,StopTok)],-                        [(7,StringTok "hello"), (19,RemTok "GOFORTH\"ANDREAD\"")],-                        [(1,CharTok 'X'), (10,DataTok "TODATA")]-                       ]-  let accumLensWToks lenAndToks =-          let (lens, toks) = unzip lenAndToks-              cols = scanl (+) 1 lens-              in zip cols toks-  let expectedColAndTokss = map accumLensWToks expectedLenAndTokss-  sequence_ $ zipWith assertTokenizerResult source expectedColAndTokss+tokenLengthsToPositions :: [(Int, Token)] -> [(Int, Token)]+tokenLengthsToPositions lenAndToks =+  let (lens, toks) = unzip lenAndToks+      cols = scanl (+) 1 lens+  in zip cols toks++test_tokenizes_punctuation = TestCase $ do+  let source = ",:;()$%=<><=>=><+-*/^.?"+  let expected = tokenLengthsToPositions+                 [(1,CommaTok), (1,ColonTok), (1,SemiTok), (1,LParenTok), (1,RParenTok),+                 (1,DollarTok), (1,PercentTok), (1,EqTok), (2,NETok), (2,LETok), (2,GETok),+                 (1,GTTok), (1,LTTok), (1,PlusTok), (1,MinusTok), (1,MulTok), (1,DivTok),+                 (1,PowTok), (1,DotTok), (1,PrintTok)]+  assertTokenizerResult source expected++test_tokenizes_builtins = TestCase $ do+  let ucSource = "ABSASCCHR$COSEXPINTLEFT$LENLOGMID$RIGHT$RNDSGNSINSPCSQRSTR$TABTANVAL"+  let lcSource = map toLower ucSource+  let expected =  tokenLengthsToPositions+                  [(3,BuiltinTok AbsBI), (3,BuiltinTok AscBI), (4,BuiltinTok ChrBI),+                  (3,BuiltinTok CosBI), (3,BuiltinTok ExpBI), (3,BuiltinTok IntBI),+                  (5,BuiltinTok LeftBI), (3,BuiltinTok LenBI), (3,BuiltinTok LogBI),+                  (4,BuiltinTok MidBI), (6,BuiltinTok RightBI), (3,BuiltinTok RndBI),+                  (3,BuiltinTok SgnBI), (3,BuiltinTok SinBI), (3,BuiltinTok SpcBI),+                  (3,BuiltinTok SqrBI), (4,BuiltinTok StrBI), (3,BuiltinTok TabBI),+                  (3,BuiltinTok TanBI), (3,BuiltinTok ValBI)]+  assertTokenizerResult ucSource expected+  assertTokenizerResult lcSource expected++test_tokenizes_other = TestCase $ do+  let ucSource = "ANDORNOTLETDIMONGOSUBRETURNIFTHENFORTOSTEPNEXTPRINTINPUT"+        ++ "RANDOMIZEREADRESTOREDEFFNENDSTOP"+  let lcSource = map toLower ucSource+  let expected = tokenLengthsToPositions+                 [(3,AndTok), (2,OrTok), (3,NotTok), (3,LetTok), (3,DimTok), (2,OnTok),+                 (2,GoTok), (3,SubTok), (6,ReturnTok), (2,IfTok), (4,ThenTok), (3,ForTok),+                 (2,ToTok), (4,StepTok), (4,NextTok), (5,PrintTok), (5,InputTok),+                 (9,RandomizeTok), (4,ReadTok), (7,RestoreTok), (3,DefTok), (2,FnTok),+                 (3,EndTok), (4,StopTok)]+  assertTokenizerResult ucSource expected+  assertTokenizerResult lcSource expected++test_tokenizes_string_and_rem = TestCase $ do+  let ucSource = "\"HELLO\"REMGOFORTH\"ANDREAD\""+  let lcSource = map toLower ucSource+  let ucExpected = tokenLengthsToPositions [(7,StringTok "HELLO"), (19,RemTok "GOFORTH\"ANDREAD\"")]+  let lcExpected = tokenLengthsToPositions [(7,StringTok "hello"), (19,RemTok "goforth\"andread\"")]+  assertTokenizerResult ucSource ucExpected+  assertTokenizerResult lcSource lcExpected++test_tokenizes_plain_char_and_data = TestCase $ do+  let ucSource = "XDATATODATA"+  let lcSource = map toLower ucSource+  let ucExpected = tokenLengthsToPositions [(1,CharTok 'X'), (10,DataTok "TODATA")]+  let lcExpected = tokenLengthsToPositions [(1,CharTok 'X'), (10,DataTok "todata")]+  assertTokenizerResult ucSource ucExpected+  assertTokenizerResult lcSource lcExpected  test_capitalizes_lowercase_chars = TestCase $ do    let source = "azAZ"
vintage-basic.cabal view
@@ -1,6 +1,6 @@ Name:          vintage-basic
-Version:       1.0.1
-Cabal-Version: >= 1.6
+Version:       1.0.3
+Cabal-Version: >= 1.10
 Stability:     experimental
 Synopsis:      Interpreter for microcomputer-era BASIC
 Description:
@@ -19,13 +19,15 @@ Maintainer:         lyle@vintage-basic.net
 Homepage:           http://www.vintage-basic.net
 Build-Type:         Simple
-Tested-With:        GHC==6.10.1
+Tested-With:        GHC==8.0.2
 Extra-Source-Files: test/Language/VintageBasic/*.hs run_tests.hs examples/*.bas README.txt doc/*.html
 
 Executable vintbas
   Main-is:        Basic.hs
-  Build-Depends:  base >=3,
+  Build-Depends:  base >=4.9 && <5,
                   array >=0.1,
+                  hashable >=1.2,
+                  hashtables >=1.2,
                   mtl >=1.1,
                   parsec >=2.1,
                   random >=1,
@@ -38,3 +40,27 @@                   filepath >=1.1
   HS-Source-Dirs: src
   GHC-Options:    -Wall
+  Default-Language: Haskell2010
+  Other-Modules: Control.Monad.CPST,
+                 Control.Monad.CPST.DurableTraps,
+                 Control.Monad.CPST.ExceptionHandlers,
+                 IO.IOStream,
+                 Language.VintageBasic.BasicMonad,
+                 Language.VintageBasic.Builtins,
+                 Language.VintageBasic.Executer,
+                 Language.VintageBasic.FloatParser,
+                 Language.VintageBasic.Interpreter,
+                 Language.VintageBasic.LexCommon,
+                 Language.VintageBasic.LineScanner,
+                 Language.VintageBasic.Parser,
+                 Language.VintageBasic.Printer,
+                 Language.VintageBasic.Result,
+                 Language.VintageBasic.RuntimeParser,
+                 Language.VintageBasic.Syntax,
+                 Language.VintageBasic.Tokenizer
+
+Test-Suite test-vintbas
+    type:       exitcode-stdio-1.0
+    main-is:    run_tests.hs
+    build-depends: base
+    default-language: Haskell2010