Ganymede 0.0.0.1 → 0.0.0.4
raw patch · 6 files changed
+173/−47 lines, 6 files
Files
- AmalObs.txt +54/−0
- Ganymede.cabal +6/−3
- Ganymede.hs +45/−42
- IoGrammar.txt +59/−0
- LICENSE +2/−2
- README +7/−0
+ AmalObs.txt view
@@ -0,0 +1,54 @@+Observations on Amalthea: + (Notes on Martin Sandin's Io interpreter, his OCaml style, and on OCaml itself.) + +I'm "iffy" on OCaml after this. On the one hand, it seems quite able for writing good programs, + it has an easy way to use C object files, you can use OO programming when you need to, and pro- + cedural programming when you need it. Perhaps that's part of the problem; if you can program + the same old way that you've always done, then where's the incentive to learn how to program + in ways that can do things you can't do normally? Not that Haskell-like "Edelheit Über Alles" + (Purity Over All) is the answer, but...if your types can warn you ahead of time that you're + accessing varying parts of the system, it is helpful to know about it beforehand. +Some of the choices of style seem quirks of Sandin's and others seem quirks of the language. + The end result gives a feeling of not-so-fun quirkiness. I cannot tell if the way the + interpreter is structured was due to some unmentioned, impending requirement, raw "do what + works" thought processes, the influence of Sandin's OCaml teaching style, encouraged language + style conventions, or what, but there are many areas where the design feels uncomfortably + lacking in "separation of concern"-awareness or care. Maybe a rewrite was coming; the version + number is pre-1.0, after all. It just feels as if some things were done in an overly-compli- + cated way. I'm (2/10/11) still translating, so it'll have to wait until at least the first + rewrite on my part before a truer perspective can be gained. Part of the problem may be that + the five-to-ten years between when Amalthea was written and when "Project Ganymede" started + was a significant time of development, not just in Haskell, but in program design, especially + functional design. +Much of the issues gained here seem to be from ML's, especially OCaml's, half-hearted devotion to + design issues. Many (OCaml) language libraries (e.g., Arg, Parser, Lexer) seem to have a polished + API, while others (List, I'm looking at you) feel half-way done. Perhaps they just suffer in + comparison with Haskell's outstanding efforts over the intervening decade. + +Discrepancies between Ganymede and Amalthea behavior: + 1) Ganymede uses Haskell's GetOpt module, so when it prints an error, it has an odd-looking + paren on the last output line; Amalthea's error report isn't chopped up that way. + 2) When multiple "pre-processing" options are passes to Amalthea, it only does one, "chosen" by + the order the argument clauses are written; Ganymede is written to do them all, if possible. + 3) Amalthea's pretty print facility adds an "import prelude." to the program, whether it needs + one or not. Ganymede checks the program to see if it has a prelude statement. + 4) In the spirit of "a motion to adjourn is always in order", Ganymede will never throw an + error when given a 'terminate' action; Amalthea checks the stack to be sure it's empty. + 5) When run, Amalthea's usage blurb is output on stderr; also, when given no quick-exit options + or an input filename, the error message (of no sourcename) is output on stdout. Ganymede + does not copy that behavior, but outputs the error messages on stderr, and information + on stdout. + 6) In the documentation, Martin Sandin remarks that the reason for including mutable variables + is "to support implementations of microthreads, coroutines, exceptions, and similar control + structures" - except that Raph Levien's paper also supported threads, coroutines and thread + messaging without exposing mutable variables to the notation syntax. Plus, they complicate + the interpreter, and reasoning about it. + 7) Typical to OCaml, in fact to most of the programming industry, Amalthea uses separate + scanner/lexer and parser constructs. Haskell can (Alex/Happy) do so, but (Parsec) the more + idiomatic approach is to parse the input directly. + 8) Similar to point 7, in OCaml, as I understand it, the parser holds a declarative description + of the parsing engine (on the tokens), while the Lexing module and the lexer itself deals with + the input stream, the file cursor (the 'pos' type) and tokenization. Parsec does it all in one + idiomatic package. + +
Ganymede.cabal view
@@ -7,7 +7,7 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented. -Version: 0.0.0.1 +Version: 0.0.0.4 -- A short (one-line) description of the package. Synopsis: An Io interpreter in Haskell. @@ -19,6 +19,9 @@ Finkel's 1996 book "Advanced Programming Language Design" (APLD), whereas Ganymede is more faithful to Raph Levien's original 1989 paper. +-- URL for the project homepage or repository. +Homepage: https://github.com/BMeph/Ganymede + -- The license under which the package is released. License: BSD3 @@ -26,7 +29,7 @@ License-file: LICENSE -- The package author(s). -Author: Walt Rorie-Baety +Author: Walt "BMeph" Rorie-Baety -- An email address to which users can send suggestions, bug reports, -- and patches. @@ -41,7 +44,7 @@ -- Extra files to be distributed with the package, such as examples or -- a README. --- Extra-source-files: AmalObs.txt +Extra-source-files: AmalObs.txt, IoGrammar.txt, LICENSE, README -- Constraint on the version of Cabal needed to build this package. Cabal-version: >=1.6
Ganymede.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE DeriveDataTypeable #-} {------------------------------------------------------------ (************************************************************ Ganymede: the main Io interpreter module @@ -16,27 +17,25 @@ import Io.Interpreter as Interp (catch_interpreter_errors, interpret_expr_in_env) import Io.Pretty as Pretty (prettyPrint) import Io.Types -import Io.Util (freshPos, ref) +import Io.Util (ganyOptErr, ganyRunErr) -import Prelude hiding (catch) -import Control.Exception (Handler(..), catch, catches, throw) -import Control.Monad (when) +import Prelude hiding (catch, error) +import Control.Exception (Exception(..), Handler(..), catches) +import Data.Data (Data) import Data.List (foldl') import Data.Maybe (fromMaybe, fromJust, listToMaybe) +import Data.Typeable (Typeable) import System.Console.GetOpt import System.Environment (getArgs) -import System.Exit (ExitCode(ExitSuccess), exitSuccess) -import System.FilePath (FilePath) +import System.Exit (exitSuccess) import System.IO (hFlush, stdout) -import System.IO.Error as Error (annotateIOError) - --- Informative program values: -version = "0.0.0.1" +import System.IO.Error (annotateIOError) -usage_msg = - "Usage: ganymede [options] <sourcefile>\n\ - \Runs the Io source file.\n\ - \Options are:" +ganyOpts :: Options +getOpts :: [String] -> IO (Options, [String]) +speclist :: [OptDescr (Options -> IO Options)] +usage_msg :: String +version :: String data Options = Options { inputOpt :: Maybe FilePath @@ -46,8 +45,15 @@ , traceOpt :: Bool , pLoadOpt :: IoImports , varSuptOpt :: Bool - } deriving (Show) + } deriving (Show, Data, Typeable) +-- Informative program values: +version = "0.0.0.3" +usage_msg = + "Usage: ganymede [options] <sourcefile>\n\ + \Runs the Io source file.\n\ + \Options are:" + ganyOpts = Options { inputOpt = Nothing , ppOpt = False @@ -59,7 +65,6 @@ } -- (* Argument handling *) -speclist :: [OptDescr (Options -> IO Options)] speclist = [ Option ['h','?'] ["help"] (NoArg (\_ -> putStrLn (usageInfo usage_msg speclist) @@ -68,47 +73,40 @@ , Option ['d','t'] ["debug","trace"] (NoArg (\ opts -> return$ opts { traceOpt = True })) " print execution trace" - , Option ['V'] ["version"] + , Option ['V' ] ["version"] (NoArg (\_ -> putStrLn ("Ganymede, version "++ version) >> exitSuccess)) " print the Ganymede version number" - , Option ['c'] ["clean","noprelude"] + , Option ['c' ] ["clean","noprelude"] (NoArg (\opts -> return$ opts { pLoadOpt = [] })) " prevent the prelude from being loaded" - , Option ['P'] ["Prelude"] - (OptArg ((\f opts -> return$ opts { pLoadOpt = [(f, Nothing)] }) . fromMaybe "prelude") "FILE") + , Option ['P' ] ["Prelude"] + (OptArg ((\f opts -> return$ opts { pLoadOpt = [(f, Nothing)] }) . + fromMaybe "prelude") + "FILE") " load FILE as prelude" - , Option ['p'] ["pretty"] + , Option ['p' ] ["pretty"] (NoArg (\opts -> return$ opts { ppOpt = True })) " pretty-print the source file" - , Option ['m'] ["mutable","vars"] + , Option ['m' ] ["mutable","vars"] (NoArg (\ opts -> return$ opts { varSuptOpt = True })) " enable mutable module variables (unpure)" ] -{- [Option ['c'] ["noprelude"], Arg.Unit (fun _ -> Io_module.default_imports := []), " prevent the prelude from being loaded", - Option ['r'] ["prelude"], Arg.String (fun n -> Io_module.default_imports := [(n,None)]), " load module <name> as prelude", - Option ['p'] ["pretty"], Arg.Set pretty_flag, " pretty print the source file", - Option ['d'] ["trace"], Arg.Set Io_interpreter.trace_flag", - Option ['m'] ["vars"], Arg.Set Io_env.variable_flag, " enable mutable module variables (unpure)", - Option ['V','?'] ["version"], Arg.Set version_flag, " print the Ganymede version number"] - -} -ganyRunErr :: String -> IO a -ganyRunErr errs = ioError (userError (errs ++ usageInfo usage_msg speclist)) - -getOpts :: [String] -> IO (Options, [String]) -getOpts args = +getOpts args = let gErr = ganyOptErr usage_msg speclist in case getOpt Permute speclist args of (o,n, [] ) -> do { opts <- foldl' (>>=) (return ganyOpts) $ - (\opts -> return$ opts { inputOpt = listToMaybe n }) : o; + (\opts -> return$ opts { inputOpt = listToMaybe n }) : o; if null n - then ganyRunErr "missing argument <sourcefile>\n" + then gErr "missing argument <sourcefile>\n" else return (opts, drop 1 n) } - (_,_,errs) -> ganyRunErr (concat errs) + (_,_,errs) -> gErr (concat errs) {- --Loads dependent modules and interprets the program -} build_n_run :: (String, IoAST) -> (Bool, Bool) -> IO () +-- ^ The (String, IoAST) pair denotes the filename, and its associates Io AST; +-- the pair of Bools denote mutables and program tracing, respectively. build_n_run p@(_, (_, _, _, ex)) (varStatus, traceStatus) = do { (_,env,_) <- Module.catch_module_errors -- Modules (Module.load_world_of_ast traceStatus) p ; @@ -116,19 +114,24 @@ Interp.interpret_expr_in_env (env, ex, (varStatus, traceStatus)) } {--} +main :: IO () main = do + (opts,_) <- getOpts =<< getArgs +-- print opts let filename = fromJust (inputOpt opts) - ast <- Module.catch_module_errors Module.loadAST filename + ast <- Module.catch_module_errors Module.loadAST filename if ppOpt opts - then putStrLn $ Pretty.prettyPrint ast "\n" + then putStrLn (Pretty.prettyPrint ast "\n") else (do build_n_run (filename, ast) (varSuptOpt opts, traceOpt opts) hFlush stdout - `catches` [ Handler (\(IoError error) -> ioError (userError error)) + `catches` [ Handler (\(IoError err) -> ioError (userError err)) -- if all else fails... - , Handler - (\e -> ioError (Error.annotateIOError e "Ganymede: " Nothing Nothing))]) + , Handler ganyRunErr + ]) -- where +-- ganyRunErr e = ioError (annotateIOError e "Ganymede: " Nothing Nothing) + {- (************************************************************ Amalthea main
+ IoGrammar.txt view
@@ -0,0 +1,59 @@+Freehand Io Grammar Analysis + +[[ non-canonical... +ioModule = <imports> >>= <exports> >>= <defs> >>= <expr> +-- ioASTModule = liftM4 (,,,,) imnports exports defs expr + +imports: + ("import" <moduleName> <idList> ".")* + -- imports = many (reserv'd "import" >> moduleName >>= impList >>= period >> return (mName, idList) + +impList: + optionMaybe (fColon >> idList) + +exports: + ("export" <idList>* ".")? + -- exports = optional (between (reserv'd "export") period idList) +]] + +defs: + (<ident> ": " <expr> ".")* + +atom: + numbers + "'strings'" + ('action') + +ident: + <non-whitespace>* + +lastAction: + "; " <expr> + +lambAction: + -> <ident>* <lastAction> + +appAction: + <ident> <atom>* <lastAction> + +parList: + -> <ident>* <lastAction> -- action (input) abstraction + <ident> <parList> <lastAction> -- action application + <pAtom> <parList>? + +pAtom: + <ident-already defined> + +expr: + <ident> + -> <ident>* <lastAction> -- action (input) abstraction + <ident> <parList> " <lastAction> -- action application + +import: + ("import" <ident>*.)* + +export: + ("export" <ident>*.)* + +reserved:: chan par write terminate +
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c)2011, Walt Rorie-Baety +Copyright (c)2011, Walt "BMeph" Rorie-Baety All rights reserved. @@ -13,7 +13,7 @@ disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Walt Rorie-Baety nor the names of other + * Neither the name of Walt "BMeph" Rorie-Baety nor the names of other contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+ README view
@@ -0,0 +1,7 @@+Ganymede is an Io interpreter, originally based on Martin+"vague" Sandin's Amalthea, which is an Io interpreter+written in OCaml. There are some differences since Amalthea+is based on the description of Io found in Raphael Finkel's+1996 book "Advanced Programming Language Design" (APLD),+whereas Ganymede is more faithful to Raph Levien's original+1989 paper.