atomo 0.2.1 → 0.2.2
raw patch · 43 files changed
+1352/−1586 lines, 43 filesdep −split
Dependencies removed: split
Files
- atomo.cabal +7/−13
- prelude/association.atomo +2/−2
- prelude/block.atomo +1/−1
- prelude/boolean.atomo +7/−7
- prelude/condition.atomo +15/−38
- prelude/core.atomo +11/−4
- prelude/eco.atomo +2/−2
- prelude/exception.atomo +3/−3
- prelude/list.atomo +3/−0
- prelude/numeric.atomo +2/−2
- prelude/parameter.atomo +8/−8
- prelude/particle.atomo +1/−1
- prelude/repl.atomo +1/−1
- src/Atomo.hs +2/−0
- src/Atomo/Core.hs +1/−0
- src/Atomo/Environment.hs +69/−492
- src/Atomo/Helpers.hs +313/−0
- src/Atomo/Kernel.hs +3/−14
- src/Atomo/Kernel/Block.hs +5/−0
- src/Atomo/Kernel/Comparable.hs +1/−1
- src/Atomo/Kernel/Expression.hs +57/−8
- src/Atomo/Kernel/List.hs +17/−4
- src/Atomo/Kernel/Numeric.hs +2/−2
- src/Atomo/Kernel/Pattern.hs +16/−2
- src/Atomo/Kernel/Ports.hs +5/−3
- src/Atomo/Kernel/String.hs +36/−1
- src/Atomo/Load.hs +9/−7
- src/Atomo/Method.hs +12/−2
- src/Atomo/Parser.hs +12/−436
- src/Atomo/Parser/Base.hs +47/−53
- src/Atomo/Parser/Pattern.hs +0/−243
- src/Atomo/Parser/Pattern.hs-boot +0/−9
- src/Atomo/Parser/Primitive.hs +19/−3
- src/Atomo/Pattern.hs +292/−0
- src/Atomo/Pretty.hs +53/−22
- src/Atomo/PrettyVM.hs +1/−2
- src/Atomo/QuasiQuotes.hs +22/−129
- src/Atomo/Run.hs +7/−7
- src/Atomo/Spawn.hs +2/−6
- src/Atomo/Types.hs +253/−56
- src/Atomo/VMT.hs +8/−0
- src/Atomo/Valuable.hs +3/−0
- src/Main.hs +22/−2
atomo.cabal view
@@ -1,5 +1,5 @@ name: atomo-version: 0.2.1+version: 0.2.2 synopsis: A highly dynamic, extremely simple, very fun programming language. description:@@ -13,9 +13,9 @@ Neat stuff: first-class continuations, very metaprogramming and DSL -friendly, message-passing concurrency, pattern-matching. .- Documentation (WIP): <http://atomo-lang.org/docs/>+ Documentation: <http://atomo-lang.org/docs/> .- Examples: <http://darcsden.com/alex/atomo/browse/examples>+ Examples: <http://bitbucket.org/alex/atomo/src/tip/examples/> . IRC Channel: <irc://irc.freenode.net/atomo> homepage: http://atomo-lang.org/@@ -34,13 +34,8 @@ data-files: prelude/*.atomo source-repository head- type: darcs- location: http://darcsden.com/alex/atomo--source-repository this- type: darcs- location: http://darcsden.com/alex/atomo- tag: 0.1.1+ type: hg+ location: http://bitbucket.org/alex/atomo library hs-source-dirs: src@@ -56,7 +51,6 @@ mtl, parsec >= 3.0.0, pretty,- split, template-haskell, text, time,@@ -68,12 +62,13 @@ Atomo, Atomo.Core, Atomo.Environment,+ Atomo.Helpers, Atomo.Load, Atomo.Method, Atomo.Parser, Atomo.Parser.Base,- Atomo.Parser.Pattern, Atomo.Parser.Primitive,+ Atomo.Pattern, Atomo.Pretty, Atomo.PrettyVM, Atomo.QuasiQuotes,@@ -127,7 +122,6 @@ mtl, parsec >= 3.0.0, pretty,- split, template-haskell, text, time,
prelude/association.atomo view
@@ -6,13 +6,13 @@ (a: Association) show := a from show .. " -> " .. a to show -[] lookup: _ = @none+[] lookup: _ := @none (a . as) lookup: k := if: (k == a from) then: { @(ok: a to) } else: { as lookup: k } -[] find: _ = @none+[] find: _ := @none (a . as) find: k := if: (k == a from) then: { @(ok: a) }
prelude/block.atomo view
@@ -11,7 +11,7 @@ (b: Block) in-context := b clone do:- { call := b context join: b+ { call := b call-in: b context call: vs := b context join: b with: vs }
prelude/boolean.atomo view
@@ -1,13 +1,13 @@ @no-true-branches describe-error := "no tests in condition: block were true" -False and: _ = False+False and: _ := False True and: (b: Block) := b call -True or: _ = True+True or: _ := True False or: (b: Block) := b call -macro a && b := `(~a and: { ~b })-macro a || b := `(~a or: { ~b })+macro (a && b) `(~a and: { ~b })+macro (a || b) `(~a or: { ~b }) True not := False False not := True@@ -24,7 +24,7 @@ @ok } call -macro e unless: b := `(unless: ~b do: { ~e })+macro (e unless: b) `(unless: ~b do: { ~e }) when: False do: Block = @ok when: True do: (action: Block) :=@@ -32,7 +32,7 @@ @ok } call -macro e when: b := `(when: ~b do: { ~e })+macro (e when: b) `(when: ~b do: { ~e }) while: (test: Block) do: (action: Block) := when: test call do:@@ -45,7 +45,7 @@ otherwise := True -macro condition: (bs: Block) :=+macro (condition: (bs: Block)) bs contents reduce-right: { `(~test -> ~branch) x | `(if: ~test then: { ~branch } else: { ~x })
prelude/condition.atomo view
@@ -1,3 +1,5 @@+for-macro Restart = Object clone+ { define: *handlers* as: [] define: *restarts* as: [] @@ -24,9 +26,6 @@ Simple-Warning = Warning clone } - for-macro Handler = Object clone do: { handle: _ := @ok }- for-macro Restart = Object clone- (e: Simple-Error) describe-error := e value describe-error (w: Simple-Warning) describe-error := w value describe-error @@ -73,7 +72,7 @@ (r: Restart) show := "<restart " .. r action show .. ">" - macro action with-restarts: (restarts: Block) :=+ macro (action with-restarts: (restarts: Block)) { rs = restarts contents map: { `(~n -> ~e) | e type match: {@@ -95,7 +94,7 @@ modify: *restarts* as: { rs | restarts .. rs } do: action (super) signal: v :=- { *handlers* _? map: @(handle: v)+ { *handlers* _? map: { h | h (call: [v]) (call: [v]) } @ok } call @@ -135,43 +134,21 @@ (super) find-restart: name := *restarts* _? lookup: name - (super) with-handler: (h: Handler) do: (action: Block) :=+ (super) with-handler: (h: Block) do: (action: Block) := modify: *handlers* as: { hs | h . hs } do: action - macro a bind: (bs: Block) :=- { h = Handler clone-- (h) in: c :=- { h context = c- h- } call-- -- yield a hygienic expr to define on the handler- expr-for: e :=- condition: {- e type == @block && e arguments empty? not ->- ``(~'~e call: [~s]) -- wow-- e type == @block ->- `'(~e call)-- otherwise -> `'~e+ macro (a bind: (bs: Block))+ { branches = bs contents map:+ { `(~p -> ~e) |+ if: (e type == @block)+ then: { `(~p -> ~e) }+ else: { `(~p -> { ~e }) } } - signals = bs contents map:- { `(~pat -> ~expr) |- Lobby- define: @handle:- on: `(h: ~h) (as: Pattern)- with: [`(s: ~pat) as: Pattern]- as: `(- { match: ~(pat as: Pattern) on: s- (with-delegates: [h context])- evaluate: ~(expr-for: expr)- } call- )- }+ handler = `Block new: (branches .. ['(_ -> { @ok })]) - `(with-handler: (~h in: this) do: ~a)+ `({ h a |+ with-handler: h do: a+ } call: [{ #c | #c match: ~handler }, ~a]) } call } call
prelude/core.atomo view
@@ -1,3 +1,14 @@+macro (x match: (ts: Block))+ `Match new: (ts contents map: { `(~p -> ~e) | p -> e expand }) on: x++operator right 0 = :=++macro (p = e)+ `Set new: p to: e++macro (p := e)+ `Define new: p as: e+ @(parse-error: d) describe-error := "parse error:\n" .. (d indent: 2) @@ -35,7 +46,3 @@ @(dynamic-needed: t) describe-error := "expecting Haskell value of type " .. t---macro x match: (ts: Block) :=- `Match new: (ts contents map: { `(~p -> ~e) | p -> e }) on: x
prelude/eco.atomo view
@@ -202,10 +202,10 @@ context use: (name: String) version: (v: Version) := context use: name version: (evaluate: `({ == ~v })) -macro context use: name version: (v: Primitive) :=+macro (context use: name version: (v: Primitive)) `(~context use: ~name version: { == ~v }) -macro context use: name version: (v: `(~_ . ~_)) :=+macro (context use: name version: (v: `(~_ . ~_))) `(~context use: ~name version: { == ~v }) context use: (name: String) version: (check: Block) :=
prelude/exception.atomo view
@@ -1,4 +1,4 @@-macro action handle: (branches: Block) :=+macro (action handle: (branches: Block)) { handlers = branches contents map: { `(~c -> ~e) | `(~c -> escape-handle yield: ~e) }@@ -6,7 +6,7 @@ `({ escape-handle | ~action bind: ~(`Block new: handlers) } call/cc) } call -macro a handle: (b: Block) ensuring: (c: Block) :=+macro (a handle: (b: Block) ensuring: (c: Block)) `({ ~a handle: ~b } ensuring: ~c) (action: Block) catch: (recover: Block) :=@@ -16,7 +16,7 @@ } } call/cc -macro a catch: (b: Block) ensuring: (c: Block) :=+macro (a catch: (b: Block) ensuring: (c: Block)) `({ ~a catch: ~b } ensuring: ~c) (action: Block) ensuring: (cleanup: Block) :=
prelude/list.atomo view
@@ -1,5 +1,8 @@ @empty-list describe-error := "list is empty" +(l: -> List) show :=+ "[" .. l (map: @show) (join: ", ") .. "]"+ (l: List) each: (b: Block) := { l map: b in-context l
prelude/numeric.atomo view
@@ -1,5 +1,5 @@-macro x += y := `(match: ~(x as: Pattern) on: (~x + ~y))-macro x -= y := `(match: ~(x as: Pattern) on: (~x - ~y))+macro (x += y) `(~x = ~x + ~y)+macro (x -= y) `(~x = ~x - ~y) - (n: Number) := -1 * n
prelude/parameter.atomo view
@@ -7,19 +7,19 @@ (p: Parameter) value: _ := p default -macro define: (x: Dispatch) as: root :=- `(match: ~(x as: Pattern) on: (Parameter new: ~root))+macro (define: (x: Dispatch) as: root)+ `(~x = Parameter new: ~root) (p: Parameter) show := "<" .. p _? show .. ">" (p: Parameter) _? := p value: self -(p: Parameter) =! v :=- (p) value: (self) = v+((p: Parameter) =! v) :=+ p value: self = v (p: Parameter) set-default: v :=- (p) default = v+ p default = v with: (p: Parameter) as: new do: (action: Block) := { old = p _?@@ -35,7 +35,7 @@ } } call -macro with: (bs: List) do: action :=+macro (with: (bs: List) do: action) bs contents (reduce-right: { `(~a -> ~b) x |@@ -43,7 +43,7 @@ } with: action) contents head -macro with-defaults: (bs: List) do: action :=+macro (with-defaults: (bs: List) do: action) bs contents (reduce-right: { `(~a -> ~b) x |@@ -51,5 +51,5 @@ } with: action) contents head -macro modify: param as: change do: action :=+macro (modify: param as: change do: action) `(with: ~param as: (~change call: [~param _?]) do: ~action)
prelude/particle.atomo view
@@ -1,4 +1,4 @@-(p: Particle) show :=+(p: -> Particle) show := p type match: { @keyword -> { operator?: str := str all?: @(in?: "~!@#$%^&*-_=+./\\|<>?:")
prelude/repl.atomo view
@@ -17,7 +17,7 @@ es -> evaluate-all: es in: t } - get-n-values: 0 = []+ get-n-values: 0 := [] get-n-values: n := (get-value: Lobby) . (get-n-values: (n - 1))
src/Atomo.hs view
@@ -1,5 +1,6 @@ module Atomo ( module Atomo.Environment+ , module Atomo.Helpers , module Atomo.QuasiQuotes , module Atomo.Types @@ -17,5 +18,6 @@ import "monads-fd" Control.Monad.Trans import Atomo.Environment+import Atomo.Helpers import Atomo.QuasiQuotes import Atomo.Types
src/Atomo/Core.hs view
@@ -7,6 +7,7 @@ import Atomo.Environment +-- | Defines all primitive objects, including the Lobby. initCore :: VM () initCore = do -- the very root object
src/Atomo/Environment.hs view
@@ -3,20 +3,15 @@ import "monads-fd" Control.Monad.Cont import "monads-fd" Control.Monad.State-import Data.Char (isUpper)-import Data.Dynamic import Data.IORef import Data.List (nub)-import Data.Maybe (isJust)-import System.IO.Unsafe-import qualified Data.Text as T-import qualified Data.Vector as V import Atomo.Method+import Atomo.Pattern import Atomo.Types --- | evaluation+-- | Evaluate an expression, yielding a value. eval :: Expr -> VM Value eval (Define { ePattern = p, eExpr = ev }) = do define p ev@@ -85,6 +80,7 @@ } return (particle "ok")+eval (EForMacro {}) = return (particle "ok") eval (EParticle { eParticle = EPMSingle n }) = return (Particle $ PMSingle n) eval (EParticle { eParticle = EPMKeyword ns mes }) = do@@ -152,14 +148,15 @@ unquote _ t@(ETop {}) = return t unquote _ v@(EVM {}) = return v unquote _ o@(Operator {}) = return o+ unquote _ f@(EForMacro {}) = return f --- | evaluating multiple expressions, returning the last result+-- | Evaluate multiple expressions, returning the last result. evalAll :: [Expr] -> VM Value evalAll [] = throwError NoExpressions evalAll [e] = eval e evalAll (e:es) = eval e >> evalAll es --- | object creation+-- | Create a new empty object, passing a function to initialize it. newObject :: (Object -> Object) -> VM Value newObject f = liftM Reference . liftIO $ newIORef . f $ Object@@ -167,8 +164,8 @@ , oMethods = noMethods } --- | run x with t as its toplevel object-withTop :: Value -> VM Value -> VM Value+-- | Run x with t as its toplevel object.+withTop :: Value -> VM a -> VM a withTop t x = do o <- gets top modify (\e -> e { top = t })@@ -179,11 +176,22 @@ return res +-- | Execute an action with a new toplevel delegating to the old one.+newScope :: VM a -> VM a+newScope x = do+ t <- gets top+ nt <- newObject $ \o -> o+ { oDelegates = [t]+ } + withTop nt x++ ----------------------------------------------------------------------------- -- Define ------------------------------------------------------------------- ----------------------------------------------------------------------------- +-- | Insert a method on a single value. defineOn :: Value -> Method -> VM () defineOn v m' = do o <- orefFor v@@ -199,7 +207,7 @@ where m = m' { mPattern = setSelf v (mPattern m') } --- | define a pattern to evaluate an expression+-- | Define a method on all roles involved in its pattern. define :: Pattern -> Expr -> VM () define !p !e = do is <- gets primitives@@ -224,7 +232,7 @@ method p' e' = gets top >>= \t -> return (Responder p' t e') --- | Swap out a reference match with PThis, for inserting on the object+-- | Swap out a reference match with PThis, for inserting on an object. setSelf :: Value -> Pattern -> Pattern setSelf v (PKeyword i ns ps) = PKeyword i ns (map (setSelf v) ps)@@ -233,13 +241,16 @@ PNamed n (setSelf v p') setSelf v (PSingle i n t) = PSingle i n (setSelf v t)+setSelf v (PInstance p) = PInstance (setSelf v p)+setSelf v (PStrict p) = PStrict (setSelf v p) setSelf _ p' = p' +-- | Pattern-match a value, inserting bindings into the current toplevel. set :: Pattern -> Value -> VM Value set p v = do is <- gets primitives- if match is p v+ if match is Nothing p v then do forM_ (bindings' p v) $ \(p', v') -> define p' (Primitive Nothing v')@@ -248,7 +259,7 @@ else throwError (Mismatch p v) --- | turn any PObject patterns into PMatches+-- | Turn any PObject patterns into PMatches. matchable :: Pattern -> VM Pattern matchable p'@(PSingle { ppTarget = t }) = do t' <- matchable t@@ -258,11 +269,13 @@ return p' { ppTargets = ts' } matchable PThis = liftM PMatch (gets top) matchable (PObject oe) = liftM PMatch (eval oe)+matchable (PInstance p) = liftM PInstance (matchable p)+matchable (PStrict p) = liftM PStrict (matchable p) matchable (PNamed n p') = liftM (PNamed n) (matchable p') matchable p' = return p' --- | find the target objects for a pattern+-- | Find the target objects for a pattern. targets :: IDs -> Pattern -> VM [ORef] targets _ (PMatch v) = liftM (: []) (orefFor v) targets is (PSingle _ _ p) = targets is p@@ -280,6 +293,8 @@ else return [idList is] targets is (PPMKeyword {}) = return [idParticle is] targets is (PExpr _) = return [idExpression is]+targets is (PInstance p) = targets is p+targets is (PStrict p) = targets is p targets _ p = error $ "no targets for " ++ show p @@ -288,7 +303,11 @@ -- Dispatch ----------------------------------------------------------------- ----------------------------------------------------------------------------- --- | dispatch a message and return a value+-- | Dispatch a message to all roles and return a value.+--+-- If the message is not understood, @\@did-not-understand:(at:)@ is sent to all+-- roles until one responds to it. If none of them handle it, a+-- @\@did-not-understand:@ error is raised. dispatch :: Message -> VM Value dispatch !m = do find <- findFirstMethod m (vs m)@@ -324,18 +343,18 @@ [v, Message m] --- | find a method on object `o' that responds to `m', searching its--- delegates if necessary+-- | Find a method on object `o' that responds to `m', searching its+-- delegates if necessary. findMethod :: Value -> Message -> VM (Maybe Method) findMethod v m = do is <- gets primitives r <- orefFor v o <- liftIO (readIORef r)- case relevant (is { idMatch = r }) o m of+ case relevant is r o m of Nothing -> findFirstMethod m (oDelegates o) mt -> return mt --- | find the first value that has a method defiend for `m'+-- | Find the first value that has a method defiend for `m'. findFirstMethod :: Message -> [Value] -> VM (Maybe Method) findFirstMethod _ [] = return Nothing findFirstMethod m (v:vs) = do@@ -344,126 +363,28 @@ Nothing -> findFirstMethod m vs _ -> return r --- | find a relevant method for message `m' on object `o'-relevant :: IDs -> Object -> Message -> Maybe Method-relevant ids o m =- lookupMap (mID m) (methods m) >>= firstMatch ids m+-- | Find a relevant method for message `m' on object `o'.+relevant :: IDs -> ORef -> Object -> Message -> Maybe Method+relevant ids r o m =+ lookupMap (mID m) (methods m) >>= firstMatch ids (Just r) m where methods (Single {}) = fst (oMethods o) methods (Keyword {}) = snd (oMethods o) - firstMatch _ _ [] = Nothing- firstMatch ids' m' (mt:mts)- | match ids' (mPattern mt) (Message m') = Just mt- | otherwise = firstMatch ids' m' mts---- | check if a value matches a given pattern--- note that this is much faster when pure, so it uses unsafePerformIO--- to check things like delegation matches.-match :: IDs -> Pattern -> Value -> Bool-{-# NOINLINE match #-}-match ids PThis (Reference y) =- refMatch ids (idMatch ids) y-match ids PThis y =- match ids (PMatch (Reference (idMatch ids))) (Reference (orefFrom ids y))-match _ (PMatch x) y | x == y = True-match ids (PMatch x) (Reference y) =- delegatesMatch ids (PMatch x) y-match ids (PMatch (Reference x)) y =- match ids (PMatch (Reference x)) (Reference (orefFrom ids y))-match ids- (PSingle { ppTarget = p })- (Message (Single { mTarget = t })) =- match ids p t-match ids- (PKeyword { ppTargets = ps })- (Message (Keyword { mTargets = ts })) =- matchAll ids ps ts-match ids (PNamed _ p) v = match ids p v-match _ PAny _ = True-match ids (PList ps) (List v) = matchAll ids ps (V.toList v)-match ids (PHeadTail hp tp) (List vs) =- V.length vs > 0 && match ids hp h && match ids tp t- where- h = V.head vs- t = List (V.tail vs)-match ids (PHeadTail hp tp) (String t) | not (T.null t) =- match ids hp (Char (T.head t)) && match ids tp (String (T.tail t))-match ids (PPMKeyword ans aps) (Particle (PMKeyword bns mvs)) =- ans == bns && matchParticle ids aps mvs-match _ PEDefine (Expression (Define {})) = True-match _ PESet (Expression (Set {})) = True-match _ PEDispatch (Expression (Dispatch {})) = True-match _ PEOperator (Expression (Operator {})) = True-match _ PEPrimitive (Expression (Primitive {})) = True-match _ PEBlock (Expression (EBlock {})) = True-match _ PEList (Expression (EList {})) = True-match _ PEMacro (Expression (EMacro {})) = True-match _ PEParticle (Expression (EParticle {})) = True-match _ PETop (Expression (ETop {})) = True-match _ PEQuote (Expression (EQuote {})) = True-match _ PEUnquote (Expression (EUnquote {})) = True-match _ (PExpr a) (Expression b) = matchExpr 0 a b-match ids p (Reference y) = delegatesMatch ids p y-match _ _ _ = False--refMatch :: IDs -> ORef -> ORef -> Bool-refMatch ids x y = x == y || delegatesMatch ids (PMatch (Reference x)) y--delegatesMatch :: IDs -> Pattern -> ORef -> Bool-delegatesMatch ids p x =- any (match ids p) (oDelegates (unsafePerformIO (readIORef x)))---- | match multiple patterns with multiple values-matchAll :: IDs -> [Pattern] -> [Value] -> Bool-matchAll _ [] [] = True-matchAll ids (p:ps) (v:vs) = match ids p v && matchAll ids ps vs-matchAll _ _ _ = False--matchEParticle :: Int -> [Maybe Expr] -> [Maybe Expr] -> Bool-matchEParticle _ [] [] = True-matchEParticle n (Just a:as) (Just b:bs) =- matchExpr n a b && matchEParticle n as bs-matchEParticle n (Nothing:as) (Nothing:bs) = matchEParticle n as bs-matchEParticle _ _ _ = False--matchExpr :: Int -> Expr -> Expr -> Bool-matchExpr 0 (EUnquote {}) _ = True-matchExpr n (EUnquote { eExpr = a }) (EUnquote { eExpr = b }) =- matchExpr (n - 1) a b-matchExpr n (Define { ePattern = ap', eExpr = a }) (Define { ePattern = bp, eExpr = b }) =- ap' == bp && matchExpr n a b-matchExpr n (Set { ePattern = ap', eExpr = a }) (Set { ePattern = bp, eExpr = b }) =- ap' == bp && matchExpr n a b-matchExpr n (Dispatch { eMessage = am@(EKeyword {}) }) (Dispatch { eMessage = bm@(EKeyword {}) }) =- emID am == emID bm && length (emTargets am) == length (emTargets bm) && and (zipWith (matchExpr n) (emTargets am) (emTargets bm))-matchExpr n (Dispatch { eMessage = am@(ESingle {}) }) (Dispatch { eMessage = bm@(ESingle {}) }) =- emID am == emID bm && matchExpr n (emTarget am) (emTarget bm)-matchExpr n (EBlock { eArguments = aps, eContents = as }) (EBlock { eArguments = bps, eContents = bs }) =- aps == bps && length as == length bs && and (zipWith (matchExpr n) as bs)-matchExpr n (EList { eContents = as }) (EList { eContents = bs }) =- length as == length bs && and (zipWith (matchExpr n) as bs)-matchExpr n (EMacro { ePattern = ap', eExpr = a }) (EMacro { ePattern = bp, eExpr = b }) =- ap' == bp && matchExpr n a b-matchExpr n (EParticle { eParticle = ap' }) (EParticle { eParticle = bp }) =- case (ap', bp) of- (EPMKeyword ans ames, EPMKeyword bns bmes) ->- ans == bns && matchEParticle n ames bmes- _ -> ap' == bp-matchExpr n (EQuote { eExpr = a }) (EQuote { eExpr = b }) =- matchExpr (n + 1) a b-matchExpr _ a b = a == b--matchParticle :: IDs -> [Pattern] -> [Maybe Value] -> Bool-matchParticle _ [] [] = True-matchParticle ids (PAny:ps) (Nothing:mvs) = matchParticle ids ps mvs-matchParticle ids (PNamed _ p:ps) mvs = matchParticle ids (p:ps) mvs-matchParticle ids (p:ps) (Just v:mvs) =- match ids p v && matchParticle ids ps mvs-matchParticle _ _ _ = False+ firstMatch _ _ _ [] = Nothing+ firstMatch ids' r' m' (mt:mts)+ | match ids' r' (mPattern mt) (Message m') = Just mt+ | otherwise = firstMatch ids' r' m' mts --- | evaluate a method in a scope with the pattern's bindings, delegating to--- the method's context and setting the "dispatch" object+-- | Evaluate a method.+--+-- Responder methods: evaluates its expression in a scope with the pattern's+-- bindings, delegating to the method's context.+--+-- Slot methods: simply returns the value.+--+-- Macro methods: evaluates its expression in a scope with the pattern's+-- bindings. runMethod :: Method -> Message -> VM Value runMethod (Slot { mValue = v }) _ = return v runMethod (Responder { mPattern = p, mContext = c, mExpr = e }) m = do@@ -485,375 +406,31 @@ withTop nt $ eval e --- | evaluate an action in a new scope-newScope :: VM Value -> VM Value-newScope x = do- t <- gets top- nt <- newObject $ \o -> o- { oDelegates = [t]- }-- withTop nt x---- | given a pattern and a message, return the bindings from the pattern-bindings :: Pattern -> Message -> MethodMap-bindings (PSingle { ppTarget = p }) (Single { mTarget = t }) =- toMethods (bindings' p t)-bindings (PKeyword { ppTargets = ps }) (Keyword { mTargets = ts }) =- toMethods $ concat (zipWith bindings' ps ts)-bindings p m = error $ "impossible: bindings on " ++ show (p, m)---- | given a pattern and avalue, return the bindings as a list of pairs-bindings' :: Pattern -> Value -> [(Pattern, Value)]-bindings' (PNamed n p) v = (psingle n PThis, v) : bindings' p v-bindings' (PPMKeyword _ ps) (Particle (PMKeyword _ mvs)) =- concatMap (\(p, Just v) -> bindings' p v)- $ filter (isJust . snd)- $ zip ps mvs-bindings' (PList ps) (List vs) = concat (zipWith bindings' ps (V.toList vs))-bindings' (PHeadTail hp tp) (List vs) =- bindings' hp h ++ bindings' tp t- where- h = V.head vs- t = List (V.tail vs)-bindings' (PHeadTail hp tp) (String t) | not (T.null t) =- bindings' hp (Char (T.head t)) ++ bindings' tp (String (T.tail t))-bindings' (PExpr a) (Expression b) = exprBindings 0 a b-bindings' p (Reference r) =- concatMap (bindings' p) $ oDelegates (unsafePerformIO (readIORef r))-bindings' _ _ = []---exprBindings :: Int -> Expr -> Expr -> [(Pattern, Value)]-exprBindings 0 (EUnquote { eExpr = Dispatch { eMessage = ESingle { emName = n } } }) e =- [(psingle n PThis, Expression e)]-exprBindings n (EUnquote { eExpr = a }) (EUnquote { eExpr = b }) =- exprBindings (n - 1) a b-exprBindings n (Define { eExpr = a }) (Define { eExpr = b }) =- exprBindings n a b-exprBindings n (Set { eExpr = a }) (Set { eExpr = b }) =- exprBindings n a b-exprBindings n (Dispatch { eMessage = am@(EKeyword {}) }) (Dispatch { eMessage = bm@(EKeyword {}) }) =- concat $ zipWith (exprBindings n) (emTargets am) (emTargets bm)-exprBindings n (Dispatch { eMessage = am@(ESingle {}) }) (Dispatch { eMessage = bm@(ESingle {}) }) =- exprBindings n (emTarget am) (emTarget bm)-exprBindings n (EBlock { eContents = as }) (EBlock { eContents = bs }) =- concat $ zipWith (exprBindings n) as bs-exprBindings n (EList { eContents = as }) (EList { eContents = bs }) =- concat $ zipWith (exprBindings n) as bs-exprBindings n (EMacro { eExpr = a }) (EMacro { eExpr = b }) =- exprBindings n a b-exprBindings n (EParticle { eParticle = ap' }) (EParticle { eParticle = bp }) =- case (ap', bp) of- (EPMKeyword _ ames, EPMKeyword _ bmes) ->- concatMap (\(Just a, Just b) -> exprBindings n a b)- $ filter (isJust . fst)- $ zip ames bmes- _ -> []-exprBindings n (EQuote { eExpr = a }) (EQuote { eExpr = b }) =- exprBindings (n + 1) a b-exprBindings _ _ _ = []----------------------------------------------------------------------------------- Helpers --------------------------------------------------------------------------------------------------------------------------------------------------infixr 0 =:, =::---- | define a method as an action returning a value-(=:) :: Pattern -> VM Value -> VM ()-pat =: vm = define pat (EVM Nothing Nothing vm)---- | define a slot to a given value-(=::) :: Pattern -> Value -> VM ()-pat =:: v = define pat (Primitive Nothing v)---- | define a method that evaluates e-(=:::) :: Pattern -> Expr -> VM ()-pat =::: e = define pat e---- | find a value from an object, searching its delegates, throwing--- a descriptive error if it is not found-findValue :: String -> (Value -> Bool) -> Value -> VM Value-findValue _ t v | t v = return v-findValue d t v = findValue' t v >>= maybe die return- where- die = throwError (ValueNotFound d v)---- | findValue, but returning Nothing instead of failing-findValue' :: (Value -> Bool) -> Value -> VM (Maybe Value)-findValue' t v | t v = return (Just v)-findValue' t (Reference r) = do- o <- liftIO (readIORef r)- findDels (oDelegates o)- where- findDels [] = return Nothing- findDels (d:ds) = do- f <- findValue' t d- case f of- Nothing -> findDels ds- Just v -> return (Just v)-findValue' _ _ = return Nothing--findBlock :: Value -> VM Value-findBlock v- | isBlock v = return v- | otherwise = findValue "Block" isBlock v--findBoolean :: Value -> VM Value-findBoolean v- | isBoolean v = return v- | otherwise = findValue "Boolean" isBoolean v--findChar :: Value -> VM Value-findChar v- | isChar v = return v- | otherwise = findValue "Char" isChar v--findContinuation :: Value -> VM Value-findContinuation v- | isContinuation v = return v- | otherwise = findValue "Continuation" isContinuation v--findDouble :: Value -> VM Value-findDouble v- | isDouble v = return v- | otherwise = findValue "Double" isDouble v--findExpression :: Value -> VM Value-findExpression v- | isExpression v = return v- | otherwise = findValue "Expression" isExpression v--findHaskell :: Value -> VM Value-findHaskell v- | isHaskell v = return v- | otherwise = findValue "Haskell" isHaskell v--findInteger :: Value -> VM Value-findInteger v- | isInteger v = return v- | otherwise = findValue "Integer" isInteger v--findList :: Value -> VM Value-findList v- | isList v = return v- | otherwise = findValue "List" isList v--findMessage :: Value -> VM Value-findMessage v- | isMessage v = return v- | otherwise = findValue "Message" isMessage v--findMethod' :: Value -> VM Value-findMethod' v- | isMethod v = return v- | otherwise = findValue "Method" isMethod v--findParticle :: Value -> VM Value-findParticle v- | isParticle v = return v- | otherwise = findValue "Particle" isParticle v--findProcess :: Value -> VM Value-findProcess v- | isProcess v = return v- | otherwise = findValue "Process" isProcess v--findPattern :: Value -> VM Value-findPattern v- | isPattern v = return v- | otherwise = findValue "Pattern" isPattern v--findRational :: Value -> VM Value-findRational v- | isRational v = return v- | otherwise = findValue "Rational" isRational v--findReference :: Value -> VM Value-findReference v- | isReference v = return v- | otherwise = findValue "Reference" isReference v--findString :: Value -> VM Value-findString v- | isString v = return v- | otherwise = findValue "String" isString v--getString :: Expr -> VM String-getString e = eval e >>= liftM (fromText . fromString) . findString--getText :: Expr -> VM T.Text-getText e = eval e >>= findString >>= \(String t) -> return t--getList :: Expr -> VM [Value]-getList = liftM V.toList . getVector--getVector :: Expr -> VM (V.Vector Value)-getVector e = eval e- >>= findList- >>= \(List v) -> return v--here :: String -> VM Value-here n = gets top >>= dispatch . single n--ifVM :: VM Value -> VM a -> VM a -> VM a-ifVM c a b = do- r <- c- if r == Boolean True then a else b--ifVM' :: VM Bool -> VM a -> VM a -> VM a-ifVM' c a b = do- r <- c- if r then a else b--ifE :: Expr -> VM a -> VM a -> VM a-ifE = ifVM . eval--referenceTo :: Value -> VM Value-{-# INLINE referenceTo #-}-referenceTo = liftM Reference . orefFor--callBlock :: Value -> [Value] -> VM Value-callBlock (Block s ps es) vs = do- is <- gets primitives- checkArgs is ps vs- doBlock (toMethods . concat $ zipWith bindings' ps vs) s es- where- checkArgs _ [] _ = return (particle "ok")- checkArgs _ _ [] = throwError (BlockArity (length ps) (length vs))- checkArgs is (p:ps') (v:vs')- | match is p v = checkArgs is ps' vs'- | otherwise = throwError (Mismatch p v)-callBlock x _ = raise ["not-a-block"] [x]--doBlock :: MethodMap -> Value -> [Expr] -> VM Value-{-# INLINE doBlock #-}-doBlock bms s es = do- blockScope <- newObject $ \o -> o- { oDelegates = [s]- , oMethods = (bms, emptyMap)- }-- withTop blockScope (evalAll es)--objectFor :: Value -> VM Object-{-# INLINE objectFor #-}-objectFor v = orefFor v >>= liftIO . readIORef-+-- | Get the object reference for a value. orefFor :: Value -> VM ORef {-# INLINE orefFor #-} orefFor !v = gets primitives >>= \is -> return $ orefFrom is v -orefFrom :: IDs -> Value -> ORef-{-# INLINE orefFrom #-}-orefFrom _ (Reference r) = r-orefFrom ids (Block _ _ _) = idBlock ids-orefFrom ids (Boolean _) = idBoolean ids-orefFrom ids (Char _) = idChar ids-orefFrom ids (Continuation _) = idContinuation ids-orefFrom ids (Double _) = idDouble ids-orefFrom ids (Expression _) = idExpression ids-orefFrom ids (Haskell _) = idHaskell ids-orefFrom ids (Integer _) = idInteger ids-orefFrom ids (List _) = idList ids-orefFrom ids (Message _) = idMessage ids-orefFrom ids (Method _) = idMethod ids-orefFrom ids (Particle _) = idParticle ids-orefFrom ids (Process _ _) = idProcess ids-orefFrom ids (Pattern _) = idPattern ids-orefFrom ids (Rational _) = idRational ids-orefFrom ids (String _) = idString ids---- | does one value delegate to another?-delegatesTo :: Value -> Value -> VM Bool-delegatesTo f t = do- o <- objectFor f- delegatesTo' (oDelegates o)- where- delegatesTo' [] = return False- delegatesTo' (d:ds)- | t `elem` (d:ds) = return True- | otherwise = do- o <- objectFor d- delegatesTo' (oDelegates o ++ ds)---- | is one value an instance of, equal to, or a delegation to another?--- for example, 1 is-a?: Integer, but 1 does not delegates-to?: Integer-isA :: Value -> Value -> VM Bool-isA x y = do- xr <- orefFor x- yr <- orefFor y-- if xr == yr- then return True- else do- ds <- liftM oDelegates (objectFor x)- isA' ds- where- isA' [] = return False- isA' (d:ds) = do- di <- isA d y- if di- then return True- else isA' ds-+-- | Raise a keyword particle as an error. raise :: [String] -> [Value] -> VM a {-# INLINE raise #-} raise ns vs = throwError . Error $ keyParticleN ns vs +-- | Raise a single particle as an error. raise' :: String -> VM a {-# INLINE raise' #-} raise' = throwError . Error . particle -fromHaskell :: Typeable a => String -> Value -> VM a-fromHaskell t (Haskell d) =- case fromDynamic d of- Just a -> return a- Nothing -> raise ["dynamic-needed"] [string t]-fromHaskell t _ = raise ["dynamic-needed"] [string t]-+-- | Convert an AtomoError into a value and raise it as an error. throwError :: AtomoError -> VM a-throwError e = gets top >>= \t ->- ifVM (dispatch (keyword ["responds-to?"] [t, particle "Error"]))- (dispatch (msg t) >> error ("panic: error returned normally for: " ++ show e))- (error ("panic: " ++ show e))+throwError e = gets top >>= \t -> do+ r <- dispatch (keyword ["responds-to?"] [t, particle "Error"])++ if r == Boolean True+ then do+ dispatch (msg t)+ error ("panic: error returned normally for: " ++ show e)+ else error ("panic: " ++ show e) where msg t = keyword ["error"] [t, asValue e] --- convert an expression to the pattern match it represents-toPattern :: Expr -> VM Pattern-toPattern (Dispatch { eMessage = EKeyword { emNames = ["."], emTargets = [h, t] } }) = do- hp <- toPattern h- tp <- toPattern t- return (PHeadTail hp tp)-toPattern (Dispatch { eMessage = EKeyword { emNames = [n], emTargets = [ETop {}, x] } }) = do- p <- toPattern x- return (PNamed n p)-toPattern (Dispatch { eMessage = EKeyword { emNames = ns, emTargets = ts } }) = do- ps <- mapM toPattern ts- return (pkeyword ns ps)-toPattern (Dispatch { eMessage = ESingle { emName = "_" } }) =- return PAny-toPattern d@(Dispatch { eMessage = ESingle { emTarget = ETop {}, emName = n } })- | isUpper (head n) = return (PObject d)- | otherwise = return (PNamed n PAny)-toPattern (Dispatch { eMessage = ESingle { emTarget = d@(Dispatch {}), emName = n } }) =- return (psingle n (PObject d))-toPattern (EList { eContents = es }) = do- ps <- mapM toPattern es- return (PList ps)-toPattern (EParticle { eParticle = EPMSingle n }) =- return (PMatch (Particle (PMSingle n)))-toPattern (EParticle { eParticle = EPMKeyword ns mes }) = do- ps <- forM mes $ \me ->- case me of- Nothing -> return PAny- Just e -> toPattern e-- return (PPMKeyword ns ps)-toPattern (EQuote { eExpr = e }) = return (PExpr e)-toPattern (Primitive { eValue = v }) =- return (PMatch v)-toPattern e = raise ["unknown-pattern"] [Expression e]
+ src/Atomo/Helpers.hs view
@@ -0,0 +1,313 @@+module Atomo.Helpers where++import "monads-fd" Control.Monad.State+import Data.Dynamic+import Data.IORef+import qualified Data.Text as T+import qualified Data.Vector as V++import Atomo.Environment+import Atomo.Method+import Atomo.Pattern+import Atomo.Types+++infixr 0 =:, =::++-- | Define a method as an action returning a value.+(=:) :: Pattern -> VM Value -> VM ()+pat =: vm = define pat (EVM Nothing Nothing vm)++-- | Set a slot to a given value.+(=::) :: Pattern -> Value -> VM ()+pat =:: v = define pat (Primitive Nothing v)++-- | Define a method that evaluates e.+(=:::) :: Pattern -> Expr -> VM ()+pat =::: e = define pat e++-- | Find a value, searching through an object's delegates, and throwing+-- @\@could-not-find:in:@ if it is not found.+findValue :: String -> (Value -> Bool) -> Value -> VM Value+findValue _ t v | t v = return v+findValue d t v = findValue' t v >>= maybe die return+ where+ die = throwError (ValueNotFound d v)++-- | Same as `findValue', but returning Nothing instead of failing+findValue' :: (Value -> Bool) -> Value -> VM (Maybe Value)+findValue' t v | t v = return (Just v)+findValue' t (Reference r) = do+ o <- liftIO (readIORef r)+ findDels (oDelegates o)+ where+ findDels [] = return Nothing+ findDels (d:ds) = do+ f <- findValue' t d+ case f of+ Nothing -> findDels ds+ Just v -> return (Just v)+findValue' _ _ = return Nothing++-- | `findValue' for `Block'+findBlock :: Value -> VM Value+findBlock v+ | isBlock v = return v+ | otherwise = findValue "Block" isBlock v++-- | `findValue' for `Boolean'+findBoolean :: Value -> VM Value+findBoolean v+ | isBoolean v = return v+ | otherwise = findValue "Boolean" isBoolean v++-- | `findValue' for `Char'+findChar :: Value -> VM Value+findChar v+ | isChar v = return v+ | otherwise = findValue "Char" isChar v++-- | `findValue' for `Continuation'+findContinuation :: Value -> VM Value+findContinuation v+ | isContinuation v = return v+ | otherwise = findValue "Continuation" isContinuation v++-- | `findValue' for `Double'+findDouble :: Value -> VM Value+findDouble v+ | isDouble v = return v+ | otherwise = findValue "Double" isDouble v++-- | `findValue' for `Expression'+findExpression :: Value -> VM Value+findExpression v+ | isExpression v = return v+ | otherwise = findValue "Expression" isExpression v++-- | `findValue' for `Haskell'+findHaskell :: Value -> VM Value+findHaskell v+ | isHaskell v = return v+ | otherwise = findValue "Haskell" isHaskell v++-- | `findValue' for `Integer'+findInteger :: Value -> VM Value+findInteger v+ | isInteger v = return v+ | otherwise = findValue "Integer" isInteger v++-- | `findValue' for `List'+findList :: Value -> VM Value+findList v+ | isList v = return v+ | otherwise = findValue "List" isList v++-- | `findValue' for `Message'+findMessage :: Value -> VM Value+findMessage v+ | isMessage v = return v+ | otherwise = findValue "Message" isMessage v++-- | `findValue' for `Method''+findMethod' :: Value -> VM Value+findMethod' v+ | isMethod v = return v+ | otherwise = findValue "Method" isMethod v++-- | `findValue' for `Particle'+findParticle :: Value -> VM Value+findParticle v+ | isParticle v = return v+ | otherwise = findValue "Particle" isParticle v++-- | `findValue' for `Process'+findProcess :: Value -> VM Value+findProcess v+ | isProcess v = return v+ | otherwise = findValue "Process" isProcess v++-- | `findValue' for `Pattern'+findPattern :: Value -> VM Value+findPattern v+ | isPattern v = return v+ | otherwise = findValue "Pattern" isPattern v++-- | `findValue' for `Rational'+findRational :: Value -> VM Value+findRational v+ | isRational v = return v+ | otherwise = findValue "Rational" isRational v++-- | `findValue' for `Reference'+findReference :: Value -> VM Value+findReference v+ | isReference v = return v+ | otherwise = findValue "Reference" isReference v++-- | `findValue' for `String'+findString :: Value -> VM Value+findString v+ | isString v = return v+ | otherwise = findValue "String" isString v++-- | Find a String given an expression to evaluate.+getString :: Expr -> VM String+getString e = eval e >>= liftM (fromText . fromString) . findString++-- | Find a Data.Text.Text given an expression to evaluate.+getText :: Expr -> VM T.Text+getText e = eval e >>= findString >>= \(String t) -> return t++-- | Find a list of values, given an expression to evaluate.+getList :: Expr -> VM [Value]+getList = liftM V.toList . getVector++-- | Find a VVector, given an expression to evaluate.+getVector :: Expr -> VM VVector+getVector e = eval e+ >>= findList+ >>= \(List v) -> return v++-- | Dispatch a single message to the current toplevel.+here :: String -> VM Value+here n = gets top >>= dispatch . single n++-- | if-then-else based on a VM action yielding a Boolean Value.+ifVM :: VM Value -> VM a -> VM a -> VM a+ifVM c a b = do+ r <- c+ if r == Boolean True then a else b++-- | if-then-else based on a VM action yielding a Bool.+ifVM' :: VM Bool -> VM a -> VM a -> VM a+ifVM' c a b = do+ r <- c+ if r then a else b++-- | if-then-else based on an expression to evaluate.+ifE :: Expr -> VM a -> VM a -> VM a+ifE = ifVM . eval++-- | Get a value's object.+referenceTo :: Value -> VM Value+{-# INLINE referenceTo #-}+referenceTo = liftM Reference . orefFor++-- | Call a block with the given arguments. Creates a scope, checks that its+-- argument patterns match, and executes it with the bindings.+callBlock :: Value -> [Value] -> VM Value+callBlock (Block s ps es) vs = do+ is <- gets primitives+ checkArgs is ps vs+ doBlock (toMethods . concat $ zipWith bindings' ps vs) s es+ where+ checkArgs _ [] _ = return (particle "ok")+ checkArgs _ _ [] = throwError (BlockArity (length ps) (length vs))+ checkArgs is (p:ps') (v:vs')+ | match is Nothing p v = checkArgs is ps' vs'+ | otherwise = throwError (Mismatch p v)+callBlock x _ = raise ["not-a-block"] [x]++-- | Evaluate multiple expressions given a context and bindings for the+-- toplevel object.+doBlock :: MethodMap -> Value -> [Expr] -> VM Value+{-# INLINE doBlock #-}+doBlock bms s es = do+ blockScope <- newObject $ \o -> o+ { oDelegates = [s]+ , oMethods = (bms, emptyMap)+ }++ withTop blockScope (evalAll es)++-- | Get the object backing a value.+objectFor :: Value -> VM Object+{-# INLINE objectFor #-}+objectFor v = orefFor v >>= liftIO . readIORef++-- | Does one value delegate to another?+delegatesTo :: Value -> Value -> VM Bool+delegatesTo f t = do+ o <- objectFor f+ delegatesTo' (oDelegates o)+ where+ delegatesTo' [] = return False+ delegatesTo' (d:ds)+ | t `elem` (d:ds) = return True+ | otherwise = do+ o <- objectFor d+ delegatesTo' (oDelegates o ++ ds)++-- | Is one value an instance of, equal to, or a delegation to another?+--+-- For example, 1 is-a?: Integer, but 1 does not delegates-to?: Integer+isA :: Value -> Value -> VM Bool+isA x y = do+ xr <- orefFor x+ yr <- orefFor y++ if xr == yr+ then return True+ else do+ ds <- liftM oDelegates (objectFor x)+ isA' ds+ where+ isA' [] = return False+ isA' (d:ds) = do+ di <- isA d y+ if di+ then return True+ else isA' ds++-- | Attempt conversion from a Haskell Value, given the type we expect as+-- a string.+--+-- If conversion fails, raises @\@dynamic-needed:@ with the given string.+fromHaskell :: Typeable a => String -> Value -> VM a+fromHaskell t (Haskell d) =+ case fromDynamic d of+ Just a -> return a+ Nothing -> raise ["dynamic-needed"] [string t]+fromHaskell t _ = raise ["dynamic-needed"] [string t]++-- | Convert an Atomo Haskell dynamic value into its value, erroring on+-- failure.+fromHaskell' :: Typeable a => String -> Value -> a+fromHaskell' t (Haskell d) =+ case fromDynamic d of+ Just a -> a+ Nothing -> error ("needed Haskell value of type " ++ t)+fromHaskell' t _ = error ("needed haskell value of type " ++ t)++-- | `toPattern', raising @\@unknown-pattern:@ if conversion fails.+toPattern' :: Expr -> VM Pattern+toPattern' = tryPattern toPattern++-- | `toDefinePattern', raising @\@unknown-pattern:@ if conversion fails.+toDefinePattern' :: Expr -> VM Pattern+toDefinePattern' = tryPattern toDefinePattern++-- | `toRolePattern', raising @\@unknown-pattern:@ if conversion fails.+toRolePattern' :: Expr -> VM Pattern+toRolePattern' = tryPattern toRolePattern++-- | `toMacroPattern', raising @\@unknown-pattern:@ if conversion fails.+toMacroPattern' :: Expr -> VM Pattern+toMacroPattern' = tryPattern toMacroPattern++-- | Try a given pattern conversion, raising @\@unknown-pattern:@ if conversion+-- fails.+tryPattern :: (Expr -> Maybe Pattern) -> Expr -> VM Pattern+tryPattern c e = + case c e of+ Nothing -> raise ["unknown-pattern"] [Expression e]+ Just p -> return p++-- | Fill in the empty values of a particle. The number of values missing+-- is expected to be equal to the number of values provided.+completeKP :: [Maybe Value] -> [Value] -> [Value]+completeKP [] _ = []+completeKP (Nothing:mvs') (v:vs') = v : completeKP mvs' vs'+completeKP (Just v:mvs') vs' = v : completeKP mvs' vs'+completeKP mvs' vs' = error $ "impossible: completeKP on " ++ show (mvs', vs')
src/Atomo/Kernel.hs view
@@ -8,6 +8,7 @@ import Atomo import Atomo.Load import Atomo.Method+import Atomo.Pattern (bindings') import Atomo.Pretty import qualified Atomo.Kernel.Numeric as Numeric@@ -91,8 +92,8 @@ let (ss, ks) = oMethods o ([$p|ms|] =::) =<< eval [$e|Object clone|]- [$p|ms singles|] =:: list (map (list . map Method) (elemsMap ss))- [$p|ms keywords|] =:: list (map (list . map Method) (elemsMap ks))+ [$p|(ms) singles|] =:: list (map (list . map Method) (elemsMap ss))+ [$p|(ms) keywords|] =:: list (map (list . map Method) (elemsMap ks)) here "ms" [$p|(x: Object) dump|] =: do@@ -105,18 +106,6 @@ [$p|(s: String) as: String|] =::: [$e|s|] [$p|(x: Object) as: String|] =::: [$e|x show|]-- [$p|(s: String) as: Integer|] =: do- s <- getString [$e|s|]- return (Integer (read s))-- [$p|(s: String) as: Double|] =: do- s <- getString [$e|s|]- return (Double (read s))-- [$p|(s: String) as: Char|] =: do- s <- getString [$e|s|]- return (Char (read s)) [$p|(x: Object) show|] =: liftM (string . show . pretty) (here "x")
src/Atomo/Kernel/Block.hs view
@@ -26,6 +26,11 @@ vs <- getList [$e|l|] callBlock b vs + [$p|(b: Block) call-in: c|] =: do+ Block _ _ es <- here "b" >>= findBlock+ c <- here "c"+ withTop c (evalAll es)+ [$p|(b: Block) context|] =: do Block s _ _ <- here "b" >>= findBlock return s
src/Atomo/Kernel/Comparable.hs view
@@ -9,7 +9,7 @@ load :: VM () load = do mapM_ eval- [ [$e|operator 4 ==, /=, <, <=, >=, >|]+ [ [$e|operator 4 == /= < <= >= >|] , [$e|operator right 3 &&|] , [$e|operator right 2 |||] ]
src/Atomo/Kernel/Expression.hs view
@@ -5,32 +5,48 @@ import Text.PrettyPrint (Doc) import Atomo+import Atomo.Parser (parseInput, withParser)+import Atomo.Parser.Expand+import Atomo.Pattern (match) import Atomo.Pretty (pretty)-import Atomo.Parser (macroExpand, parseInput, withParser) load :: VM () load = do [$p|`Block new: (es: List)|] =::: [$e|`Block new: es arguments: []|] [$p|`Block new: (es: List) arguments: (as: List)|] =: do- es <- getList [$e|es|]- as <- getList [$e|as|]+ es <- getList [$e|es|] >>= mapM findExpression+ as <- getList [$e|as|] >>= mapM findExpression return (Expression (EBlock Nothing (map fromPattern as) (map fromExpression es))) [$p|`List new: (es: List)|] =: do- es <- getList [$e|es|]+ es <- getList [$e|es|] >>= mapM findExpression return (Expression (EList Nothing (map fromExpression es))) [$p|`Match new: (branches: List) on: (value: Expression)|] =: do- pats <- liftM (map fromExpression) $ getList [$e|branches map: @from|]- exprs <- liftM (map fromExpression) $ getList [$e|branches map: @to|]+ pats <- liftM (map fromExpression) $ getList [$e|branches map: @from|] >>= mapM findExpression+ exprs <- liftM (map fromExpression) $ getList [$e|branches map: @to|] >>= mapM findExpression Expression value <- here "value" >>= findExpression - ps <- mapM toPattern pats+ ps <- mapM toRolePattern' pats ids <- gets primitives return . Expression . EVM Nothing (Just $ prettyMatch value (zip pats exprs)) $ eval value >>= matchBranches ids (zip ps exprs) + [$p|`Set new: (pattern: Expression) to: (value: Expression)|] =: do+ Expression pat <- here "pattern" >>= findExpression+ Expression e <- here "value" >>= findExpression++ p <- toPattern' pat+ return (Expression $ Set Nothing p e)++ [$p|`Define new: (pattern: Expression) as: (expr: Expression)|] =: do+ Expression pat <- here "pattern" >>= findExpression+ Expression e <- here "expr" >>= findExpression++ p <- toDefinePattern' pat+ return (Expression $ Define Nothing p e)+ [$p|(s: String) parse-expressions|] =: getString [$e|s|] >>= liftM (list . map Expression) . parseInput @@ -59,6 +75,7 @@ EVM {} -> return (particle "vm") EList {} -> return (particle "list") EMacro {} -> return (particle "macro")+ EForMacro {} -> return (particle "for-macro") ETop {} -> return (particle "top") EQuote {} -> return (particle "quote") EUnquote {} -> return (particle "unquote")@@ -137,6 +154,7 @@ case e of Set { ePattern = p } -> return (Pattern p) Define { ePattern = p } -> return (Pattern p)+ EMacro { ePattern = p } -> return (Pattern p) _ -> raise ["no-pattern-for"] [Expression e] [$p|(e: Expression) expression|] =: do@@ -144,14 +162,45 @@ case e of Set { eExpr = e } -> return (Expression e) Define { eExpr = e } -> return (Expression e)+ EMacro { eExpr = e } -> return (Expression e)+ EForMacro { eExpr = e } -> return (Expression e)+ EQuote { eExpr = e } -> return (Expression e)+ EUnquote { eExpr = e } -> return (Expression e) _ -> raise ["no-expression-for"] [Expression e] + [$p|(e: Expression) associativity|] =: do+ Expression e <- here "e" >>= findExpression+ case e of+ Operator { eAssoc = ALeft } ->+ return (particle "left") + Operator { eAssoc = ARight } ->+ return (particle "right")++ _ -> raise ["no-associativity-for"] [Expression e]++ [$p|(e: Expression) precedence|] =: do+ Expression e <- here "e" >>= findExpression+ case e of+ Operator { ePrec = p } ->+ return (Integer p)++ _ -> raise ["no-precedence-for"] [Expression e]++ [$p|(e: Expression) operators|] =: do+ Expression e <- here "e" >>= findExpression+ case e of+ Operator { eNames = ns } ->+ return (list (map (\n -> keyParticle [n] [Nothing, Nothing]) ns))++ _ -> raise ["no-operators-for"] [Expression e]++ matchBranches :: IDs -> [(Pattern, Expr)] -> Value -> VM Value matchBranches _ [] v = raise ["no-match-for"] [v] matchBranches ids ((p, e):ps) v = do p' <- matchable p- if match ids p' v+ if match ids Nothing p' v then newScope $ set p' v >> eval e else matchBranches ids ps v
src/Atomo/Kernel/List.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE QuasiQuotes #-} module Atomo.Kernel.List (load) where -import Data.List.Split+import Data.List (isPrefixOf) import qualified Data.Vector as V import Atomo@@ -11,9 +11,6 @@ load = do eval [$e|operator right .|] - [$p|(l: List) show|] =:::- [$e|"[" .. l (map: @show) (join: ", ") .. "]"|]- [$p|(l: List) length|] =: liftM (Integer . fromIntegral . V.length) (getVector [$e|l|]) @@ -350,4 +347,20 @@ else do rest <- merge cmp xs (y:ys) return (x:rest)++splitWhen :: (a -> Bool) -> [a] -> [[a]]+splitWhen f vs' = splitWhen' vs' []+ where+ splitWhen' [] acc = [acc]+ splitWhen' (v:vs) acc+ | f v = acc : splitWhen' vs []+ | otherwise = splitWhen' vs (acc ++ [v])++splitOn :: Eq a => [a] -> [a] -> [[a]]+splitOn d vs' = splitOn' vs' []+ where+ splitOn' [] acc = [acc]+ splitOn' vs acc+ | d `isPrefixOf` vs = acc : splitOn' (drop (length d) vs) []+ | otherwise = splitOn' (tail vs) (acc ++ [head vs])
src/Atomo/Kernel/Numeric.hs view
@@ -10,8 +10,8 @@ load = do mapM_ eval [ [$e|operator right 8 ^|]- , [$e|operator 7 %, *, /|]- , [$e|operator 6 +, -|]+ , [$e|operator 7 % * /|]+ , [$e|operator 6 + -|] ] eval [$e|Object clone|] >>= ([$p|Number|] =::)
src/Atomo/Kernel/Pattern.hs view
@@ -3,15 +3,29 @@ module Atomo.Kernel.Pattern (load) where import Atomo+import Atomo.Pattern (match) load :: VM () load = do+ ([$p|Pattern Role|] =::) =<< eval [$e|Pattern clone|]+ ([$p|Pattern Define|] =::) =<< eval [$e|Pattern clone|]+ [$p|(e: Expression) as: Pattern|] =: do Expression e <- here "e" >>= findExpression- p <- toPattern e+ p <- toPattern' e return (Pattern p) + [$p|(e: Expression) as: Pattern Role|] =: do+ Expression e <- here "e" >>= findExpression+ p <- toRolePattern' e+ return (Pattern p)++ [$p|(e: Expression) as: Pattern Define|] =: do+ Expression e <- here "e" >>= findExpression+ p <- toDefinePattern' e+ return (Pattern p)+ [$p|(p: Pattern) name|] =: do Pattern p <- here "p" >>= findPattern @@ -46,7 +60,7 @@ v <- here "v" ids <- gets primitives - if match ids p v+ if match ids Nothing p v then do bs <- eval [$e|Object clone|] withTop bs (set p v)
src/Atomo/Kernel/Ports.hs view
@@ -326,9 +326,11 @@ portObj hdl = newScope $ do port <- eval [$e|Port clone|]- [$p|p|] =:: port- [$p|p handle|] =:: haskell hdl- here "p"++ define (psingle "handle" (PMatch port))+ (Primitive Nothing $ haskell hdl)++ return port getHandle ex = eval ex >>= fromHaskell "Handle"
src/Atomo/Kernel/String.hs view
@@ -2,6 +2,7 @@ module Atomo.Kernel.String where import Data.List (sort)+import Data.Ratio ((%)) import qualified Data.Text as T import Atomo@@ -12,6 +13,28 @@ [$p|(s: String) as: List|] =: liftM (list . map Char) (getString [$e|s|]) + -- TODO: rename these to @read: or @to:+ [$p|(s: String) as: Char|] =: do+ s <- getString [$e|s|]+ case s of+ "$'" -> return (Char '\'')+ '$':rest -> return (Char (read $ "'" ++ rest ++ "'"))+ _ -> raise ["invalid-string"] [String $ T.pack s]+ + [$p|(s: String) as: Integer|] =: do+ s <- getString [$e|s|]+ return (Integer (read s))+ + [$p|(s: String) as: Double|] =: do+ s <- getString [$e|s|]+ return (Double (read s))+ + [$p|(s: String) as: Rational|] =: do+ s <- getString [$e|s|]+ let num = read $ takeWhile (/= '/') s+ denom = read . tail $ dropWhile (/= '/') s+ return (Rational (num % denom))+ [$p|(l: List) to-string|] =: do vs <- getList [$e|l|] @@ -45,7 +68,19 @@ [$p|(s: String) last|] =: liftM (Char . T.last) (getText [$e|s|]) - -- TODO: @from:to:+ [$p|(s: String) from: (n: Integer) to: (m: Integer)|] =: do+ Integer n <- here "n" >>= findInteger+ Integer m <- here "m" >>= findInteger+ t <- getText [$e|s|]++ let start = fromIntegral n+ count = (fromIntegral m) - start++ if count > T.length t || start < 0 || count < 0+ then raise+ ["invalid-slice", "for-string"]+ [keyParticleN ["from", "to"] [Integer n, Integer m], String t]+ else return (String . T.take count . T.drop start $ t) [$p|"" init|] =::: [$e|error: @empty-string|] [$p|(s: String) init|] =:
src/Atomo/Load.hs view
@@ -10,8 +10,8 @@ import Atomo.Types --- load a file, remembering it to prevent repeated loading--- searches with cwd as lowest priority+-- | Load a file, remembering its absolute path to prevent repeated loading.+-- Searches with cwd as lowest priority requireFile :: FilePath -> VM Value requireFile fn = do initialPath <- gets loadPath@@ -26,14 +26,14 @@ doLoad file --- load a file--- searches with cwd as highest priority+-- | Load a file. Searches with cwd as highest priority. loadFile :: FilePath -> VM Value loadFile fn = do initialPath <- gets loadPath findFile ("":initialPath) fn >>= doLoad --- execute a file+-- | Execute a file; for .hs filenames, interprets its `load' function and+-- calls it. for anything else, it interprets it as Atomo source. doLoad :: FilePath -> VM Value doLoad file = case takeExtension file of@@ -64,8 +64,10 @@ return r --- | given a list of paths to search, find the file to load--- attempts to find the filename with .atomo and .hs extensions+-- | Given a list of paths to search, find the file to load. Attempts to find+-- the filename with .atomo and .hs extensions.+--+-- If no file is found, throws a FileNotFound error. findFile :: [FilePath] -> FilePath -> VM FilePath findFile [] fn = throwError (FileNotFound fn) findFile (p:ps) fn = do
src/Atomo/Method.hs view
@@ -85,14 +85,15 @@ ds = oDelegates (unsafePerformIO (readIORef f)) unsafeDelegatesTo _ _ = False +-- | Insert a method into a MethodMap based on its pattern's ID and precision. addMethod :: Method -> MethodMap -> MethodMap addMethod m mm = M.insertWith (\[m'] ms -> insertMethod m' ms) key [m] mm where key = ppID (mPattern m) --- insert a method into a list of existing methods--- most precise goes first, equivalent patterns are replaced+-- | Insert a method into a list of existing methods most precise goes first,+-- equivalent patterns are replaced. insertMethod :: Method -> [Method] -> [Method] insertMethod x [] = [x] insertMethod x ys@(y:ys') =@@ -106,24 +107,33 @@ -- keep looking if we're EQ or GT _ -> y : insertMethod x ys' +-- | Convert a list of slots to a MethodMap. toMethods :: [(Pattern, Value)] -> MethodMap toMethods = foldl (\ss (p, v) -> addMethod (Slot p v) ss) emptyMap +-- | A pair of two empty MethodMaps; one for single methods and one for keyword+-- methods. noMethods :: (MethodMap, MethodMap) noMethods = (M.empty, M.empty) +-- | An empty MethodMap. emptyMap :: MethodMap emptyMap = M.empty +-- | Find methods in a MethodMap by the pattern ID. lookupMap :: Int -> MethodMap -> Maybe [Method] lookupMap = M.lookup +-- | Is a MethodMap empty?. nullMap :: MethodMap -> Bool nullMap = M.null +-- | All of the methods in a MethodMap. elemsMap :: MethodMap -> [[Method]] elemsMap = M.elems +-- | Insert a method into a MethodMap, replacing all other methods with the+-- same ID. insertMap :: Method -> MethodMap -> MethodMap insertMap m mm = M.insert key [m] mm where
src/Atomo/Parser.hs view
@@ -1,391 +1,27 @@ module Atomo.Parser where -import Control.Arrow (first, second) import "monads-fd" Control.Monad.State-import Data.Maybe (fromJust, isJust) import Text.Parsec-import qualified "mtl" Control.Monad.Trans as MTL import Atomo.Environment-import Atomo.Method import Atomo.Parser.Base-import {-# SOURCE #-} Atomo.Parser.Pattern-import Atomo.Parser.Primitive+import Atomo.Parser.Expr+import Atomo.Parser.Expand import Atomo.Types hiding (keyword, string) --- the types of values in Dispatch syntax-data Dispatch- = DParticle EParticle- | DNormal Expr- deriving Show -defaultPrec :: Integer-defaultPrec = 5--pExpr :: Parser Expr-pExpr = choice- [ pOperator- , pMacro- , pForMacro- , try pDispatch- , pDefine- , pSet- , pLiteral- , parens pExpr- ]- <?> "expression"--pLiteral :: Parser Expr-pLiteral = pThis <|> pBlock <|> pList <|> pParticle <|> pQuoted <|> pQuasiQuoted <|> pUnquoted <|> pPrimitive- <?> "literal"--pThis :: Parser Expr-pThis = tagged $ reserved "this" >> return (ETop Nothing)--pQuoted :: Parser Expr-pQuoted = tagged $ do- char '\''- e <- pSpacedExpr- return (Primitive Nothing (Expression e))--pQuasiQuoted :: Parser Expr-pQuasiQuoted = tagged $ do- char '`'- e <- pSpacedExpr- return (EQuote Nothing e)--pUnquoted :: Parser Expr-pUnquoted = tagged $ do- char '~'- e <- pSpacedExpr- return (EUnquote Nothing e)--pSpacedExpr :: Parser Expr-pSpacedExpr = pLiteral <|> simpleDispatch <|> parens pExpr- where- simpleDispatch = tagged $ do- name <- ident- notFollowedBy (char ':')- spacing- return (Dispatch Nothing (esingle name (ETop Nothing)))--pForMacro :: Parser Expr-pForMacro = tagged (do- reserved "for-macro"- e <- pExpr- MTL.lift (eval e)- return (Primitive Nothing (Expression e))) --e)- <?> "for-macro expression"--pMacro :: Parser Expr-pMacro = tagged (do- reserved "macro"- p <- ppMacro- reserved ":="- whiteSpace- e <- pExpr- macroExpand e >>= addMacro p- return (EMacro Nothing p e))- <?> "macro definition"--addMacro :: Pattern -> Expr -> Parser ()-addMacro p e =- case p of- PSingle {} ->- modifyState $ \ps -> ps- { psMacros = (addMethod (Macro p e) (fst (psMacros ps)), snd (psMacros ps))- }-- PKeyword {} ->- modifyState $ \ps -> ps- { psMacros = (fst (psMacros ps), addMethod (Macro p e) (snd (psMacros ps)))- }-- _ -> error $ "impossible: addMacro: p is " ++ show p--pOperator :: Parser Expr-pOperator = tagged (do- reserved "operator"-- info <- choice- [ do- a <- choice- [ symbol "right" >> return ARight- , symbol "left" >> return ALeft- ]- prec <- option defaultPrec (try integer)- return (a, prec)- , liftM ((,) ALeft) integer- ]-- ops <- commaSep1 operator-- forM_ ops $ \name ->- modifyState (\ps -> ps { psOperators = (name, info) : psOperators ps })-- return (uncurry (Operator Nothing ops) info))- <?> "operator pragma"--pParticle :: Parser Expr-pParticle = tagged (do- char '@'- c <- choice- [ cKeyword True- , binary- , try (cSingle True)- , symbols- ]- return (EParticle Nothing c))- <?> "particle"- where- binary = do- op <- operator- return $ EPMKeyword [op] [Nothing, Nothing]-- symbols = do- names <- many1 (anyIdent >>= \n -> char ':' >> return n)- spacing- return $ EPMKeyword names (replicate (length names + 1) Nothing)--pDefine :: Parser Expr-pDefine = tagged (do- pattern <- try $ do- p <- ppDefine- reservedOp ":="- return p-- whiteSpace- expr <- pExpr-- return $ Define Nothing pattern expr)- <?> "definition"--pSet :: Parser Expr-pSet = tagged (do- pattern <- try $ do- p <- ppSet- reservedOp "="- return p-- whiteSpace- expr <- pExpr-- return $ Set Nothing pattern expr)- <?> "set"--pDispatch :: Parser Expr-pDispatch = do- d <- choice- [ try pdKeys- , pdCascade- ]- notFollowedBy (reserved ":=" <|> reserved "=")- return d- <?> "dispatch"--pdKeys :: Parser Expr-pdKeys = do- pos <- getPosition- msg <- keywords ekeyword (ETop (Just pos)) (try pdCascade <|> headless)- ops <- liftM psOperators getState- return $ Dispatch (Just pos) (toBinaryOps ops msg)- <?> "keyword dispatch"- where- headless = do- p <- getPosition- msg <- ckeywd p- ops <- liftM psOperators getState- return (Dispatch (Just p) (toBinaryOps ops msg))-- ckeywd pos = do- ks <- wsMany1 $ keyword pdCascade- let (ns, es) = unzip ks- return $ ekeyword ns (ETop (Just pos):es)- <?> "keyword segment"--pdCascade :: Parser Expr-pdCascade = do- pos <- getPosition-- chain <- wsManyStart- (liftM DNormal (try pLiteral <|> pThis <|> parens pExpr) <|> cascaded)- cascaded-- return $ dispatches pos chain- <?> "single dispatch"- where- cascaded = liftM DParticle $ choice- [ cKeyword False- , cSingle False- ]-- -- start off by dispatching on either a primitive or Top- dispatches :: SourcePos -> [Dispatch] -> Expr- dispatches p (DNormal e:ps) =- dispatches' p ps e- dispatches p (DParticle (EPMSingle n):ps) =- dispatches' p ps (Dispatch (Just p) $ esingle n (ETop (Just p)))- dispatches p (DParticle (EPMKeyword ns (Nothing:es)):ps) =- dispatches' p ps (Dispatch (Just p) $ ekeyword ns (ETop (Just p):map fromJust es))- dispatches _ ds = error $ "impossible: dispatches on " ++ show ds-- -- roll a list of partial messages into a bunch of dispatches- dispatches' :: SourcePos -> [Dispatch] -> Expr -> Expr- dispatches' _ [] acc = acc- dispatches' p (DParticle (EPMKeyword ns (Nothing:es)):ps) acc =- dispatches' p ps (Dispatch (Just p) $ ekeyword ns (acc : map fromJust es))- dispatches' p (DParticle (EPMSingle n):ps) acc =- dispatches' p ps (Dispatch (Just p) $ esingle n acc)- dispatches' _ x y = error $ "impossible: dispatches' on " ++ show (x, y)--pList :: Parser Expr-pList = (tagged . liftM (EList Nothing) $ brackets (wsDelim "," pExpr))- <?> "list"--pBlock :: Parser Expr-pBlock = tagged (braces $ do- arguments <- option [] . try $ do- ps <- many1 pPattern- whiteSpace- string "|"- whiteSpace1- return ps-- code <- wsBlock pExpr-- return $ EBlock Nothing arguments code)- <?> "block"--cSingle :: Bool -> Parser EParticle-cSingle p = do- n <- if p then anyIdent else ident- notFollowedBy colon- spacing- return (EPMSingle n)- <?> "single segment"--cKeyword :: Bool -> Parser EParticle-cKeyword wc = do- ks <- parens $ many1 keyword'- let (ns, mvs) = second (Nothing:) $ unzip ks- if any isOperator (tail ns)- then toDispatch ns mvs- else return $ EPMKeyword ns mvs- <?> "keyword segment"- where- keywordVal- | wc = wildcard <|> value- | otherwise = value-- keywordDispatch- | wc = wildcard <|> disp- | otherwise = disp-- value = liftM Just pdCascade- disp = liftM Just pDispatch-- keyword' = do- name <- try (do- name <- ident- char ':'- return name) <|> operator- whiteSpace1- target <-- if isOperator name- then keywordDispatch- else keywordVal- return (name, target)-- wildcard = symbol "_" >> return Nothing-- toDispatch [] mvs = error $ "impossible: toDispatch on [] and " ++ show mvs- toDispatch (n:ns) mvs- | all isJust opVals = do- os <- getState- pos <- getPosition- let msg = toBinaryOps (psOperators os) $ ekeyword opers (map fromJust opVals)- return . EPMKeyword nonOpers $- partVals ++ [Just $ Dispatch (Just pos) msg]- | otherwise = fail "invalid particle; toplevel operator with wildcards as values"- where- (nonOpers, opers) = first (n:) $ break isOperator ns- (partVals, opVals) = splitAt (length nonOpers) mvs---- work out precadence, associativity, etc. from a stream of operators--- the input is a keyword EMessage with a mix of operators and identifiers--- as its name, e.g. EKeyword { emNames = ["+", "*", "remainder"] }-toBinaryOps :: Operators -> EMessage -> EMessage-toBinaryOps _ done@(EKeyword _ [_] [_, _]) = done-toBinaryOps ops (EKeyword h (n:ns) (v:vs))- | nextFirst =- ekeyword [n]- [ v- , Dispatch (eLocation v)- (toBinaryOps ops (ekeyword ns vs))- ]- | isOperator n =- toBinaryOps ops . ekeyword ns $- (Dispatch (eLocation v) (ekeyword [n] [v, head vs]):tail vs)- | nonOperators == ns = EKeyword h (n:ns) (v:vs)- | null nonOperators && length vs > 2 =- ekeyword [head ns]- [ Dispatch (eLocation v) $- ekeyword [n] [v, head vs]- , Dispatch (eLocation v) $- toBinaryOps ops (ekeyword (tail ns) (tail vs))- ]- | otherwise =- ekeyword- (n : nonOperators)- (concat- [ [v]- , take numNonOps vs- , [ Dispatch (eLocation v) $ toBinaryOps ops- (ekeyword- (drop numNonOps ns)- (drop numNonOps vs)) ]- ])- where- numNonOps = length nonOperators- nonOperators = takeWhile (not . isOperator) ns- nextFirst =- isOperator n && or- [ null ns- , prec next > prec n- , assoc n == ARight && prec next == prec n- ]- where next = head ns-- assoc n' =- case lookup n' ops of- Nothing -> ALeft- Just (a, _) -> a-- prec n' =- case lookup n' ops of- Nothing -> defaultPrec- Just (_, p) -> p-toBinaryOps _ u = error $ "cannot toBinaryOps: " ++ show u--parser :: Parser [Expr]-parser = do- whiteSpace- es <- wsBlock pExpr- whiteSpace- eof- return es--fileParser :: Parser [Expr]-fileParser = do- optional (string "#!" >> manyTill anyToken (eol <|> eof))- parser--parseFile :: String -> VM [Expr]-parseFile fn = liftIO (readFile fn) >>= continue (fileParser >>= mapM macroExpand) fn+-- | Parses an input file, performs macro expansion, and returns an AST.+parseFile :: FilePath -> VM [Expr]+parseFile fn =+ liftIO (readFile fn)+ >>= continue (fileParser >>= mapM macroExpand) fn +-- | Parses an input string, performs macro expansion, and returns an AST. parseInput :: String -> VM [Expr] parseInput = continue (parser >>= mapM macroExpand) "<input>" +-- | Given a Parser action, a source, and the input, perform that action+-- passing the parser state between VM and Parser. continue :: Parser a -> String -> String -> VM a continue p s i = do ps <- gets parserState@@ -396,70 +32,10 @@ modify $ \e -> e { parserState = ps' } return ok --- | parse input i from source s, maintaining parser state between parses+-- | Parse input i from source s, maintaining parser state between parses. continuedParse :: String -> String -> VM [Expr] continuedParse i s = continue parser s i +-- | Run an arbitrary Parser action with the VM's parser state. withParser :: Parser a -> VM a withParser x = continue x "<internal>" ""--macroExpand :: Expr -> Parser Expr-macroExpand d@(Define { eExpr = e }) = do- e' <- macroExpand e- return d { eExpr = e' }-macroExpand s@(Set { eExpr = e }) = do- e' <- macroExpand e- return s { eExpr = e' }-macroExpand d@(Dispatch { eMessage = em }) = do- (msg, nem) <- expandMsg em-- mm <- findMacro msg- case mm of- Just m -> do- Expression e <- MTL.lift $ runMethod m msg- macroExpand e-- _ -> return d { eMessage = nem }- where- expandMsg (ESingle i n t) = do- nt <- macroExpand t- return (Single i n (Expression nt), ESingle i n nt)- expandMsg (EKeyword i ns ts) = do- nts <- mapM macroExpand ts- return (Keyword i ns (map Expression nts), EKeyword i ns nts)-macroExpand b@(EBlock { eContents = es }) = do- nes <- mapM macroExpand es- return b { eContents = nes }-macroExpand l@(EList { eContents = es }) = do- nes <- mapM macroExpand es- return l { eContents = nes }-macroExpand m@(EMacro { eExpr = e }) = do -- TODO: is this sane?- e' <- macroExpand e- return m { eExpr = e' }-macroExpand p@(EParticle { eParticle = ep }) =- case ep of- EPMKeyword ns mes -> do- nmes <- forM mes $ \me ->- case me of- Nothing -> return Nothing- Just e -> liftM Just (macroExpand e)-- return p { eParticle = EPMKeyword ns nmes }-- _ -> return p-macroExpand e = return e---- | find a findMacro method for message `m' on object `o'-findMacro :: Message -> Parser (Maybe Method)-findMacro m = do- ids <- MTL.lift (gets primitives)- ms <- methods m- maybe (return Nothing) (firstMatch ids m) (lookupMap (mID m) ms)- where- methods (Single {}) = liftM (fst . psMacros) getState- methods (Keyword {}) = liftM (snd . psMacros) getState-- firstMatch _ _ [] = return Nothing- firstMatch ids' m' (mt:mts)- | match ids' (mPattern mt) (Message m') = return (Just mt)- | otherwise = firstMatch ids' m' mts
src/Atomo/Parser/Base.hs view
@@ -7,14 +7,14 @@ import Text.Parsec import qualified Text.Parsec.Token as P -import Atomo.Types (Expr(..), ParserState, VM)+import Atomo.Types (Expr(..), ParserState(..), VM) type Parser = ParsecT String ParserState VM isOpLetter :: Char -> Bool-isOpLetter c = c `elem` "!@#%&*-./\\?" || isSymbol c+isOpLetter c = c `elem` "!@#%&*-./\\?:" || isSymbol c isOperator :: String -> Bool isOperator "" = False@@ -26,11 +26,11 @@ , P.commentEnd = "-}" , P.commentLine = "--" , P.nestedComments = True- , P.identStart = letter <|> P.opStart def <|> oneOf "_"- , P.identLetter = alphaNum <|> P.opLetter def+ , P.identStart = satisfy (\c -> c == '_' || isLetter c || (c `notElem` "@$~:" && isOpLetter c))+ , P.identLetter = satisfy (\c -> c == '_' || isAlphaNum c || (c /= ':' && isOpLetter c)) , P.opStart = satisfy (\c -> c `notElem` "@$~" && isOpLetter c) , P.opLetter = satisfy isOpLetter- , P.reservedOpNames = ["=", ":=", ",", "|"]+ , P.reservedOpNames = [",", "|"] , P.reservedNames = ["operator", "macro", "for-macro", "this", "True", "False"] , P.caseSensitive = True }@@ -44,35 +44,32 @@ lexeme :: Parser a -> Parser a lexeme = P.lexeme tp -capIdent :: Parser String-capIdent = do- c <- satisfy isUpper- cs <- many (P.identLetter def)- return (c:cs)--lowIdent :: Parser String-lowIdent = do- c <- satisfy isLower- cs <- many (P.identLetter def)- return (c:cs)--capIdentifier :: Parser String-capIdentifier = lexeme capIdent--lowIdentifier :: Parser String-lowIdentifier = lexeme lowIdent- anyIdent :: Parser String anyIdent = try $ do c <- P.identStart def cs <- many (P.identLetter def) if isOperator (c:cs) then unexpected "operator"+ else do++ ps <- getState+ if c == '#' && psInQuote ps+ then return ((c:cs) ++ ":" ++ show (psClock ps)) else return (c:cs) anyIdentifier :: Parser String anyIdentifier = lexeme anyIdent +identifier :: Parser String+identifier = lexeme ident++ident :: Parser String+ident = do+ name <- anyIdent+ if isReservedName name+ then unexpected ("reserved word " ++ show name)+ else return name+ parens :: Parser a -> Parser a parens = P.parens tp @@ -94,12 +91,6 @@ dot :: Parser String dot = P.dot tp -identifier :: Parser String-identifier = lexeme $ P.identifier tp--ident :: Parser String-ident = P.identifier tp- operator :: Parser String operator = try $ do c <- P.opStart def@@ -213,7 +204,7 @@ keywordName :: Parser String keywordName = do- n <- operator <|> (ident >>= \name -> char ':' >> return name)+ n <- try (ident >>= \name -> char ':' >> return name) <|> operator whiteSpace1 return n @@ -352,7 +343,7 @@ -- escape code tables- escMap = zip "abfnrtv\\\"\'" "\a\b\f\n\r\t\v\\\"\'"+ escMap = zip "abfnrtv\\\"" "\a\b\f\n\r\t\v\\\"" asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2) ascii2codes = ["BS","HT","LF","VT","FF","CR","SO","SI","EM",@@ -524,30 +515,8 @@ else return (c:cs)) <?> "identifier" - isReservedName name- = isReserved theReservedNames caseName- where- caseName | P.caseSensitive languageDef = name- | otherwise = map toLower name - isReserved names name- = scan names- where- scan [] = False- scan (r:rs) = case compare r name of- LT -> scan rs- EQ -> True- GT -> False-- theReservedNames- | P.caseSensitive languageDef = sortedNames- | otherwise = map (map toLower) sortedNames- where- sortedNames = sort (P.reservedNames languageDef)--- ----------------------------------------------------------- -- White space & symbols -----------------------------------------------------------@@ -570,6 +539,31 @@ spacing skipMany (try $ spacing >> newline) spacing++isReservedName :: String -> Bool+isReservedName name = isReserved reservedNames caseName+ where+ caseName+ | P.caseSensitive def = name+ | otherwise = map toLower name++ reservedNames+ | P.caseSensitive def = sortedNames+ | otherwise = map (map toLower) sortedNames+ where+ sortedNames = sort (P.reservedNames def)++isReserved :: [String] -> String -> Bool+isReserved names name+ = scan names+ where+ scan [] = False+ scan (r:rs) =+ case compare r name of+ LT -> scan rs+ EQ -> True+ GT -> False+ whiteSpace :: Parser () whiteSpace = P.whiteSpace tp
− src/Atomo/Parser/Pattern.hs
@@ -1,243 +0,0 @@-module Atomo.Parser.Pattern where--import Control.Monad (liftM)-import Text.Parsec--import Atomo.Debug-import Atomo.Parser-import Atomo.Parser.Base-import Atomo.Parser.Primitive-import Atomo.Types hiding (keyword)--pPattern :: Parser Pattern-pPattern = choice- [ try ppNamed- , try ppHeadTail- , try ppMatch- , ppList- , ppString- , ppParticle- , ppExpr- , ppAny- , parens pPattern- ]--pObjectPattern :: Parser Pattern-pObjectPattern = choice- [ try ppNamedSensitive- , try ppHeadTail- , try ppObject- , try ppMatch- , ppList- , ppString- , ppParticle- , ppExpr- , ppAnySensitive- , parens pObjectPattern- ]--ppSet :: Parser Pattern-ppSet = try ppDefine <|> pPattern--ppMacro :: Parser Pattern-ppMacro = try ppMacroKeywords <|> ppMacroSingle--ppExpr :: Parser Pattern-ppExpr = do- q <- pQuasiQuoted- return (PExpr (eExpr q))--ppMacroSingle :: Parser Pattern-ppMacroSingle = do- (t, v) <- choice- [ try $ do- t <- ppMacroRole- v <- identifier- return (t, v)-- , do- v <- identifier- return (PAny, v)- ]-- return $ psingle v t--ppMacroKeywords :: Parser Pattern-ppMacroKeywords = keywords pkeyword PAny ppMacroRole--ppMacroRole :: Parser Pattern-ppMacroRole = choice- [ ppExpr- , try $ symbol "Define" >> return PEDefine- , try $ symbol "Set" >> return PESet- , try $ symbol "Dispatch" >> return PEDispatch- , try $ symbol "Operator" >> return PEOperator- , try $ symbol "Primitive" >> return PEPrimitive- , try $ symbol "Block" >> return PEBlock- , try $ symbol "List" >> return PEList- , try $ symbol "Macro" >> return PEMacro- , try $ symbol "Particle" >> return PEParticle- , try $ symbol "Top" >> return PETop- , try $ symbol "Quote" >> return PEQuote- , try $ symbol "Unquote" >> return PEUnquote- , ppAny- , ppNamedMacro- ]- where- ppNamedMacro = parens $ do- n <- identifier- delimit ":"- p <- ppMacroRole- return $ PNamed n p--ppDefine :: Parser Pattern-ppDefine = try ppKeywords <|> ppSingle--ppSingle :: Parser Pattern-ppSingle = do- (t, v) <- choice- [ try $ do- t <- pNonExpr- dump ("got pNonExpr", t)- v <- identifier- return (t, v)- , try $ do- ds <- pdCascade- dump ("got pdCascade", ds)- p <- cInit ds- v <- cLast ds- return (PObject p, v)- , do- v <- identifier- return (PObject (ETop Nothing), v)- ]-- dump ("single", t, v)-- return $ psingle v t- where- cLast (Dispatch _ (ESingle _ n _)) = return n- cLast _ = fail "last target of dispatch chain is not a single messsage"-- cInit (Dispatch _ (ESingle _ _ x)) = return x- cInit _ = fail "last target of dispatch chain is not a single messsage"-- -- patterns that would otherwise be mistaken for expressions- -- if the pdCascade pattern were to grab them- pNonExpr = choice- [ try ppNamedSensitive- , try ppHeadTail- , try ppMatch- , ppList- , ppString- , ppParticle- , ppExpr- , ppWildcard- , parens pNonExpr- ]---ppKeywords :: Parser Pattern-ppKeywords = keywords pkeyword (PObject (ETop Nothing)) pObjectPattern--ppNamed :: Parser Pattern-ppNamed = parens $ do- name <- identifier- delimit ":"- p <- pPattern- return $ PNamed name p--ppNamedSensitive :: Parser Pattern-ppNamedSensitive = parens $ do- name <- lowIdentifier- dump ("got ppNamedSensitive", name)- delimit ":"- p <- pObjectPattern- dump ("finished ppNamedSensitive", name, p)- return $ PNamed name p--ppObject :: Parser Pattern-ppObject = choice- [ try $ parens ppHeadTail- , do- p <- name <|> parens pExpr- return $ PObject p- ]- where- name = do- lookAhead capIdentifier- pdCascade--ppAny :: Parser Pattern-ppAny = ppWildcard- <|> ppNamedAny- where- ppNamedAny = do- name <- identifier- return (PNamed name PAny)--ppAnySensitive :: Parser Pattern-ppAnySensitive = ppWildcard- <|> ppNamedAny- where- ppNamedAny = do- name <- lowIdentifier- return (PNamed name PAny)--ppWildcard :: Parser Pattern-ppWildcard = char '_' >> notFollowedBy identifier >> spacing >> return PAny--ppMatch :: Parser Pattern-ppMatch = do- v <- pPrim- return $ PMatch v--ppHeadTail :: Parser Pattern-ppHeadTail = parens subHT- where- subHT = do- h <- pHead- dot- t <- try subHT <|> pPattern- return $ PHeadTail h t- pHead = choice- [ try ppNamed- , try ppMatch- , ppList- , ppString- , ppParticle- , ppAny- , parens pHead- ]--ppList :: Parser Pattern-ppList = brackets $ do- ps <- commaSep pPattern- return $ PList ps--ppString :: Parser Pattern-ppString = do- cs <- stringLiteral- return $ PList (map (PMatch . Char) cs)--ppParticle :: Parser Pattern-ppParticle = do- char '@'- try keywordParticle <|> singleParticle- where- singleParticle = liftM (PMatch . Particle . PMSingle) anyIdentifier-- keywordParticle = choice- [ parens $ do- ks <- many1 (keyword pPattern)- let (ns, ps) = unzip ks- return $ PPMKeyword ns (PAny:ps)- , do- o <- operator- spacing- return $ PPMKeyword [o] [PAny, PAny]- , do- names <- many1 (anyIdent >>= \n -> char ':' >> return n)- spacing- return $ PPMKeyword names (replicate (length names + 1) PAny) - ]
− src/Atomo/Parser/Pattern.hs-boot
@@ -1,9 +0,0 @@-module Atomo.Parser.Pattern (pPattern, ppDefine, ppMacro, ppSet) where--import Atomo.Parser.Base (Parser)-import Atomo.Types--pPattern :: Parser Pattern-ppDefine :: Parser Pattern-ppMacro :: Parser Pattern-ppSet :: Parser Pattern
src/Atomo/Parser/Primitive.hs view
@@ -8,9 +8,7 @@ import Atomo.Types as T -pPrimitive :: Parser Expr-pPrimitive = tagged $ liftM (Primitive Nothing) pPrim-+-- | Parser for all primitive values. pPrim :: Parser Value pPrim = choice [ pvChar@@ -21,24 +19,42 @@ , try pvBoolean ] +-- | Character literal.+--+-- Examples: @$a@, @$ @, @$\\EOT@, @$\\n@ pvChar :: Parser Value pvChar = liftM Char charLiteral +-- | String literal.+--+-- Examples: @\"\"@, @\"foo\"@, @\"foo\\nbar\"@ pvString :: Parser Value pvString = liftM T.string stringLiteral +-- | Double literal.+--+-- Examples: @1.0@, @4.6e10@, @-1.0@ pvDouble :: Parser Value pvDouble = liftM Double float +-- | Integer literal.+--+-- Examples: @1@, @2@, @-1@, @-2@ pvInteger :: Parser Value pvInteger = liftM Integer integer +-- | Boolean literal.+--+-- Examples: @True@, @False@ pvBoolean :: Parser Value pvBoolean = liftM Boolean $ true <|> false where true = reserved "True" >> return True false = reserved "False" >> return False +-- | Rational literal.+--+-- Examples: @1\/2@, @-1\/2@, @1\/-2@ pvRational :: Parser Value pvRational = do n <- integer
+ src/Atomo/Pattern.hs view
@@ -0,0 +1,292 @@+module Atomo.Pattern+ ( bindings+ , bindings'+ , match+ , matchAll+ , toPattern+ , toDefinePattern+ , toRolePattern+ , toMacroPattern+ , toMacroRole+ ) where++import Control.Monad (forM, liftM)+import Data.Char (isUpper)+import Data.IORef (readIORef)+import Data.Maybe (isJust)+import System.IO.Unsafe+import qualified Data.Text as T+import qualified Data.Vector as V++import Atomo.Method+import Atomo.Types+++-- | Check if a value matches a given pattern.+--+-- Note that this is much faster when pure, so it uses unsafePerformIO+-- to check things like delegation matches.+match :: IDs -> Maybe ORef -> Pattern -> Value -> Bool+{-# NOINLINE match #-}+match ids (Just r) PThis (Reference y) =+ refMatch ids (Just r) r y+match ids (Just r) PThis y =+ match ids (Just r) (PMatch (Reference r)) (Reference (orefFrom ids y))+match _ _ (PMatch x) y | x == y = True+match ids r (PMatch x) (Reference y) =+ delegatesMatch ids r (PMatch x) y+match ids r (PMatch (Reference x)) y =+ match ids r (PMatch (Reference x)) (Reference (orefFrom ids y))+match ids r+ (PSingle { ppTarget = p })+ (Message (Single { mTarget = t })) =+ match ids r p t+match ids r+ (PKeyword { ppTargets = ps })+ (Message (Keyword { mTargets = ts })) =+ matchAll ids r ps ts+match ids r (PInstance p) (Reference o) = delegatesMatch ids r p o+match ids r (PInstance p) v = match ids r p v+match _ _ (PStrict (PMatch x)) v = x == v+match ids r (PStrict p) v = match ids r p v+match ids r (PNamed _ p) v = match ids r p v+match _ _ PAny _ = True+match ids r (PList ps) (List v) = matchAll ids r ps (V.toList v)+match ids r (PHeadTail hp tp) (List vs) =+ V.length vs > 0 && match ids r hp h && match ids r tp t+ where+ h = V.head vs+ t = List (V.tail vs)+match ids r (PHeadTail hp tp) (String t) | not (T.null t) =+ match ids r hp (Char (T.head t)) && match ids r tp (String (T.tail t))+match ids r (PPMKeyword ans aps) (Particle (PMKeyword bns mvs)) =+ ans == bns && matchParticle ids r aps mvs+match _ _ PEDispatch (Expression (Dispatch {})) = True+match _ _ PEOperator (Expression (Operator {})) = True+match _ _ PEPrimitive (Expression (Primitive {})) = True+match _ _ PEBlock (Expression (EBlock {})) = True+match _ _ PEList (Expression (EList {})) = True+match _ _ PEMacro (Expression (EMacro {})) = True+match _ _ PEParticle (Expression (EParticle {})) = True+match _ _ PETop (Expression (ETop {})) = True+match _ _ PEQuote (Expression (EQuote {})) = True+match _ _ PEUnquote (Expression (EUnquote {})) = True+match _ _ (PExpr a) (Expression b) = matchExpr 0 a b+match ids r p (Reference y) = delegatesMatch ids r p y+match _ _ _ _ = False++-- | Check if two references are equal or if one delegates to another.+refMatch :: IDs -> Maybe ORef -> ORef -> ORef -> Bool+refMatch ids r x y = x == y || delegatesMatch ids r (PMatch (Reference x)) y++-- | Check if an object's delegates match a pattern.+delegatesMatch :: IDs -> Maybe ORef -> Pattern -> ORef -> Bool+delegatesMatch ids mr p x =+ any (match ids mr p) (oDelegates (unsafePerformIO (readIORef x)))++-- | Match multiple patterns with multiple values.+matchAll :: IDs -> Maybe ORef -> [Pattern] -> [Value] -> Bool+matchAll _ _ [] [] = True+matchAll ids mr (p:ps) (v:vs) = match ids mr p v && matchAll ids mr ps vs+matchAll _ _ _ _ = False++matchEParticle :: Int -> [Maybe Expr] -> [Maybe Expr] -> Bool+matchEParticle _ [] [] = True+matchEParticle n (Just a:as) (Just b:bs) =+ matchExpr n a b && matchEParticle n as bs+matchEParticle n (Nothing:as) (Nothing:bs) = matchEParticle n as bs+matchEParticle _ _ _ = False++matchExpr :: Int -> Expr -> Expr -> Bool+matchExpr 0 (EUnquote {}) _ = True+matchExpr n (EUnquote { eExpr = a }) (EUnquote { eExpr = b }) =+ matchExpr (n - 1) a b+matchExpr n (Define { ePattern = ap', eExpr = a }) (Define { ePattern = bp, eExpr = b }) =+ ap' == bp && matchExpr n a b+matchExpr n (Set { ePattern = ap', eExpr = a }) (Set { ePattern = bp, eExpr = b }) =+ ap' == bp && matchExpr n a b+matchExpr n (Dispatch { eMessage = am@(EKeyword {}) }) (Dispatch { eMessage = bm@(EKeyword {}) }) =+ emID am == emID bm && length (emTargets am) == length (emTargets bm) && and (zipWith (matchExpr n) (emTargets am) (emTargets bm))+matchExpr n (Dispatch { eMessage = am@(ESingle {}) }) (Dispatch { eMessage = bm@(ESingle {}) }) =+ emID am == emID bm && matchExpr n (emTarget am) (emTarget bm)+matchExpr n (EBlock { eArguments = aps, eContents = as }) (EBlock { eArguments = bps, eContents = bs }) =+ aps == bps && length as == length bs && and (zipWith (matchExpr n) as bs)+matchExpr n (EList { eContents = as }) (EList { eContents = bs }) =+ length as == length bs && and (zipWith (matchExpr n) as bs)+matchExpr n (EMacro { ePattern = ap', eExpr = a }) (EMacro { ePattern = bp, eExpr = b }) =+ ap' == bp && matchExpr n a b+matchExpr n (EParticle { eParticle = ap' }) (EParticle { eParticle = bp }) =+ case (ap', bp) of+ (EPMKeyword ans ames, EPMKeyword bns bmes) ->+ ans == bns && matchEParticle n ames bmes+ _ -> ap' == bp+matchExpr n (EQuote { eExpr = a }) (EQuote { eExpr = b }) =+ matchExpr (n + 1) a b+matchExpr _ a b = a == b++matchParticle :: IDs -> Maybe ORef -> [Pattern] -> [Maybe Value] -> Bool+matchParticle _ _ [] [] = True+matchParticle ids mr (PAny:ps) (Nothing:mvs) = matchParticle ids mr ps mvs+matchParticle ids mr (PNamed _ p:ps) mvs = matchParticle ids mr (p:ps) mvs+matchParticle ids mr (p:ps) (Just v:mvs) =+ match ids mr p v && matchParticle ids mr ps mvs+matchParticle _ _ _ _ = False++-- | Given a pattern and a message, return the bindings from the pattern.+bindings :: Pattern -> Message -> MethodMap+bindings (PSingle { ppTarget = p }) (Single { mTarget = t }) =+ toMethods (bindings' p t)+bindings (PKeyword { ppTargets = ps }) (Keyword { mTargets = ts }) =+ toMethods $ concat (zipWith bindings' ps ts)+bindings p m = error $ "impossible: bindings on " ++ show (p, m)++-- | Given a pattern and avalue, return the bindings as a list of pairs.+bindings' :: Pattern -> Value -> [(Pattern, Value)]+bindings' (PNamed n p) v = (psingle n PThis, v) : bindings' p v+bindings' (PPMKeyword _ ps) (Particle (PMKeyword _ mvs)) =+ concatMap (\(p, Just v) -> bindings' p v)+ $ filter (isJust . snd)+ $ zip ps mvs+bindings' (PList ps) (List vs) = concat (zipWith bindings' ps (V.toList vs))+bindings' (PHeadTail hp tp) (List vs) =+ bindings' hp h ++ bindings' tp t+ where+ h = V.head vs+ t = List (V.tail vs)+bindings' (PHeadTail hp tp) (String t) | not (T.null t) =+ bindings' hp (Char (T.head t)) ++ bindings' tp (String (T.tail t))+bindings' (PExpr a) (Expression b) = exprBindings 0 a b+bindings' (PInstance p) (Reference r) =+ concatMap (bindings' p) $ oDelegates (unsafePerformIO (readIORef r))+bindings' (PInstance p) v = bindings' p v+bindings' (PStrict p) v = bindings' p v+bindings' p (Reference r) =+ concatMap (bindings' p) $ oDelegates (unsafePerformIO (readIORef r))+bindings' _ _ = []+++exprBindings :: Int -> Expr -> Expr -> [(Pattern, Value)]+exprBindings 0 (EUnquote { eExpr = Dispatch { eMessage = ESingle { emName = n } } }) e =+ [(psingle n PThis, Expression e)]+exprBindings n (EUnquote { eExpr = a }) (EUnquote { eExpr = b }) =+ exprBindings (n - 1) a b+exprBindings n (Define { eExpr = a }) (Define { eExpr = b }) =+ exprBindings n a b+exprBindings n (Set { eExpr = a }) (Set { eExpr = b }) =+ exprBindings n a b+exprBindings n (Dispatch { eMessage = am@(EKeyword {}) }) (Dispatch { eMessage = bm@(EKeyword {}) }) =+ concat $ zipWith (exprBindings n) (emTargets am) (emTargets bm)+exprBindings n (Dispatch { eMessage = am@(ESingle {}) }) (Dispatch { eMessage = bm@(ESingle {}) }) =+ exprBindings n (emTarget am) (emTarget bm)+exprBindings n (EBlock { eContents = as }) (EBlock { eContents = bs }) =+ concat $ zipWith (exprBindings n) as bs+exprBindings n (EList { eContents = as }) (EList { eContents = bs }) =+ concat $ zipWith (exprBindings n) as bs+exprBindings n (EMacro { eExpr = a }) (EMacro { eExpr = b }) =+ exprBindings n a b+exprBindings n (EParticle { eParticle = ap' }) (EParticle { eParticle = bp }) =+ case (ap', bp) of+ (EPMKeyword _ ames, EPMKeyword _ bmes) ->+ concatMap (\(Just a, Just b) -> exprBindings n a b)+ $ filter (isJust . fst)+ $ zip ames bmes+ _ -> []+exprBindings n (EQuote { eExpr = a }) (EQuote { eExpr = b }) =+ exprBindings (n + 1) a b+exprBindings _ _ _ = []++-- | Convert an expression to the pattern match it represents.+toPattern :: Expr -> Maybe Pattern+toPattern (Dispatch { eMessage = EKeyword { emNames = ["."], emTargets = [h, t] } }) = do+ hp <- toPattern h+ tp <- toPattern t+ return (PHeadTail hp tp)+toPattern (Dispatch { eMessage = EKeyword { emNames = ["->"], emTargets = [ETop {}, o] } }) = do+ liftM PInstance (toPattern o)+toPattern (Dispatch { eMessage = EKeyword { emNames = ["=="], emTargets = [ETop {}, o] } }) = do+ liftM PStrict (toPattern o)+toPattern (Dispatch { eMessage = EKeyword { emNames = [n], emTargets = [ETop {}, x] } }) = do+ p <- toPattern x+ return (PNamed n p)+toPattern (Dispatch { eMessage = EKeyword { emNames = ns, emTargets = ts } }) =+ return (pkeyword ns (map PObject ts))+toPattern (Dispatch { eMessage = ESingle { emName = "_" } }) =+ return PAny+toPattern (Dispatch { eMessage = ESingle { emName = n, emTarget = ETop {} } }) =+ return (PNamed n PAny)+toPattern (Dispatch { eMessage = ESingle { emTarget = d@(Dispatch {}), emName = n } }) =+ return (psingle n (PObject d))+toPattern (EList { eContents = es }) = do+ ps <- mapM toPattern es+ return (PList ps)+toPattern (EParticle { eParticle = EPMSingle n }) =+ return (PMatch (Particle (PMSingle n)))+toPattern (EParticle { eParticle = EPMKeyword ns mes }) = do+ ps <- forM mes $ \me ->+ case me of+ Nothing -> return PAny+ Just e -> toPattern e++ return (PPMKeyword ns ps)+toPattern (EQuote { eExpr = e }) = return (PExpr e)+toPattern (Primitive { eValue = v }) =+ return (PMatch v)+toPattern (ETop {}) =+ return (PObject (ETop Nothing))+toPattern b@(EBlock {}) =+ return (PObject (Dispatch Nothing (esingle "call" b)))+toPattern _ = Nothing++-- | Convert an expression into a definition's message pattern.+toDefinePattern :: Expr -> Maybe Pattern+toDefinePattern (Dispatch { eMessage = ESingle { emName = n, emTarget = t } }) = do+ p <- toRolePattern t+ return (psingle n p)+toDefinePattern (Dispatch { eMessage = EKeyword { emNames = ns, emTargets = ts } }) = do+ ps <- mapM toRolePattern ts+ return (pkeyword ns ps)+toDefinePattern _ = Nothing++-- | Convert an expression into a pattern-match for use as a message's role.+toRolePattern :: Expr -> Maybe Pattern+toRolePattern (Dispatch { eMessage = EKeyword { emNames = ["->"], emTargets = [ETop {}, o] } }) = do+ liftM PInstance (toRolePattern o)+toRolePattern (Dispatch { eMessage = EKeyword { emNames = ["=="], emTargets = [ETop {}, o] } }) = do+ liftM PStrict (toRolePattern o)+toRolePattern (Dispatch { eMessage = EKeyword { emNames = [n], emTargets = [ETop {}, x] } }) = do+ p <- toRolePattern x+ return (PNamed n p)+toRolePattern d@(Dispatch { eMessage = ESingle { emTarget = ETop {}, emName = n } })+ | isUpper (head n) = return (PObject d)+ | otherwise = return (PNamed n PAny)+toRolePattern d@(Dispatch { eMessage = ESingle { emTarget = (Dispatch {}) } }) =+ return (PObject d)+toRolePattern p = toPattern p++-- | Convert an expression into a macro's message pattern.+toMacroPattern :: Expr -> Maybe Pattern+toMacroPattern (Dispatch { eMessage = ESingle { emName = n, emTarget = t } }) = do+ p <- toMacroRole t+ return (psingle n p)+toMacroPattern (Dispatch { eMessage = EKeyword { emNames = ns, emTargets = ts } }) = do+ ps <- mapM toMacroRole ts+ return (pkeyword ns ps)+toMacroPattern _ = Nothing++-- | Convert an expression into a pattern-match for use as a macro's role.+toMacroRole :: Expr -> Maybe Pattern+toMacroRole (Dispatch _ (ESingle _ "Dispatch" _)) = Just PEDispatch+toMacroRole (Dispatch _ (ESingle _ "Operator" _)) = Just PEOperator+toMacroRole (Dispatch _ (ESingle _ "Primitive" _)) = Just PEPrimitive+toMacroRole (Dispatch _ (ESingle _ "Block" _)) = Just PEBlock+toMacroRole (Dispatch _ (ESingle _ "List" _)) = Just PEList+toMacroRole (Dispatch _ (ESingle _ "Macro" _)) = Just PEMacro+toMacroRole (Dispatch _ (ESingle _ "Particle" _)) = Just PEParticle+toMacroRole (Dispatch _ (ESingle _ "Top" _)) = Just PETop+toMacroRole (Dispatch _ (ESingle _ "Quote" _)) = Just PEQuote+toMacroRole (Dispatch _ (ESingle _ "Unquote" _)) = Just PEUnquote+toMacroRole (Dispatch { eMessage = EKeyword { emNames = [n], emTargets = [ETop {}, x] } }) = do+ p <- toMacroRole x+ return (PNamed n p)+toMacroRole (ETop {}) = Just PAny+toMacroRole p = toPattern p
src/Atomo/Pretty.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE TypeSynonymInstances #-}-module Atomo.Pretty (Pretty(..), prettyStack) where+module Atomo.Pretty (Pretty(pretty)) where +import Data.Char (isUpper) import Data.IORef import Data.Maybe (isNothing) import Data.Ratio@@ -23,6 +24,8 @@ | CList class Pretty a where+ -- | Pretty-print a value into a Doc. Typically this should be parseable+ -- back into the original value, or just a nice user-friendly output form. pretty :: a -> Doc prettyFrom :: Context -> a -> Doc @@ -32,7 +35,8 @@ instance Pretty Value where prettyFrom _ (Block _ ps es) | null ps = braces exprs- | otherwise = braces $ sep (map (prettyFrom CArgs) ps) <+> char '|' <+> exprs+ | otherwise = braces $+ sep (map (prettyFrom CArgs) ps) <+> char '|' <+> exprs where exprs = sep . punctuate (text ";") $ map pretty es prettyFrom _ (Boolean b) = text $ show b@@ -47,14 +51,16 @@ where vs = V.toList l prettyFrom _ (Message m) = internal "message" $ pretty m prettyFrom _ (Method (Slot p _)) = internal "slot" $ parens (pretty p)- prettyFrom _ (Method (Responder p _ _)) = internal "responder" $ parens (pretty p)+ prettyFrom _ (Method (Responder p _ _)) =+ internal "responder" $ parens (pretty p) prettyFrom _ (Method (Macro p _)) = internal "macro" $ parens (pretty p) prettyFrom _ (Particle p) = char '@' <> pretty p prettyFrom _ (Pattern p) = internal "pattern" $ pretty p prettyFrom _ (Process _ tid) = internal "process" $ text (words (show tid) !! 1) prettyFrom CNone (Reference r) = pretty (unsafePerformIO (readIORef r))- prettyFrom _ (Rational r) = integer (numerator r) <> char '/' <> integer (denominator r)+ prettyFrom _ (Rational r) =+ integer (numerator r) <> char '/' <> integer (denominator r) prettyFrom _ (Reference _) = internal "object" empty prettyFrom _ (String s) = text (show s) @@ -80,7 +86,7 @@ prettyMethod (Responder { mPattern = p, mExpr = e }) = prettyFrom CDefine p <+> text ":=" <++> prettyFrom CDefine e prettyMethod (Macro { mPattern = p, mExpr = e }) =- text "macro" <+> prettyFrom CDefine p <+> text ":=" <++> prettyFrom CDefine e+ text "macro" <+> parens (pretty p) <++> prettyFrom CDefine e instance Pretty Message where prettyFrom _ (Single _ n t) = prettyFrom CSingle t <+> text n@@ -115,7 +121,24 @@ prettyFrom _ (PMatch v) = prettyFrom CPattern v prettyFrom _ (PNamed n PAny) = text n prettyFrom _ (PNamed n p) = parens $ text n <> colon <+> pretty p- prettyFrom _ (PObject e) = pretty e+ prettyFrom _ (PObject e@(Dispatch { eMessage = msg }))+ | capitalized msg = pretty e+ | isParticular msg = pretty block+ where+ capitalized (ESingle { emName = n, emTarget = ETop {} }) =+ isUpper (head n)+ capitalized (ESingle { emTarget = Dispatch { eMessage = t@(ESingle {}) } }) =+ capitalized t+ capitalized _ = False++ isParticular (ESingle { emName = "call", emTarget = EBlock {} }) =+ True+ isParticular _ = False++ block = emTarget msg+ prettyFrom _ (PObject e) = parens $ pretty e+ prettyFrom _ (PInstance p) = parens $ text "->" <+> pretty p+ prettyFrom _ (PStrict p) = parens $ text "==" <+> pretty p prettyFrom _ (PPMKeyword ns ps) | all isAny ps = char '@' <> text (concatMap keyword ns) | isAny (head ps) =@@ -129,8 +152,6 @@ prettyFrom _ (PSingle _ n p) = pretty p <+> text n prettyFrom _ PThis = text "<this>" - prettyFrom _ PEDefine = text "Define"- prettyFrom _ PESet = text "Set" prettyFrom _ PEDispatch = text "Dispatch" prettyFrom _ PEOperator = text "Operator" prettyFrom _ PEPrimitive = text "Primitive"@@ -146,13 +167,15 @@ instance Pretty Expr where- prettyFrom _ (Define _ p v) = prettyFrom CDefine p <+> text ":=" <++> prettyFrom CDefine v- prettyFrom _ (Set _ p v) = prettyFrom CDefine p <+> text "=" <++> prettyFrom CDefine v+ prettyFrom _ (Define _ p v) =+ prettyFrom CDefine p <+> text ":=" <++> prettyFrom CDefine v+ prettyFrom _ (Set _ p v) =+ prettyFrom CDefine p <+> text "=" <++> prettyFrom CDefine v prettyFrom CKeyword (Dispatch _ m@(EKeyword {})) = parens $ pretty m prettyFrom CSingle (Dispatch _ m@(EKeyword {})) = parens $ pretty m prettyFrom c (Dispatch _ m) = prettyFrom c m prettyFrom _ (Operator _ ns a i) =- text "operator" <+> assoc a <+> integer i <+> sep (punctuate comma (map text ns))+ text "operator" <+> assoc a <+> integer i <+> sep (map text ns) where assoc ALeft = text "left" assoc ARight = text "right"@@ -167,11 +190,13 @@ prettyFrom _ (EVM { ePretty = Just d }) = d prettyFrom _ (EList _ es) = brackets . sep . punctuate comma $ map (prettyFrom CList) es- prettyFrom _ (EMacro _ p v) = text "macro" <+> prettyFrom CDefine p <+> text ":=" <++> prettyFrom CDefine v+ prettyFrom _ (EMacro _ p e) =+ text "macro" <+> parens (pretty p) <++> pretty e+ prettyFrom _ (EForMacro { eExpr = e }) = text "for-macro" <+> pretty e prettyFrom c (EParticle _ p) = char '@' <> prettyFrom c p- prettyFrom _ (ETop {}) = text "<top>"- prettyFrom _ (EQuote _ e) = char '`' <> parens (pretty e)- prettyFrom _ (EUnquote _ e) = char '~' <> parens (pretty e)+ prettyFrom _ (ETop {}) = text "this"+ prettyFrom c (EQuote _ e) = char '`' <> prettySpacedExpr c e+ prettyFrom c (EUnquote _ e) = char '~' <> prettySpacedExpr c e instance Pretty EMessage where@@ -202,13 +227,6 @@ -prettyStack :: Expr -> Doc-prettyStack (EVM {}) = text "... internal ..."-prettyStack e =- case eLocation e of- Nothing -> text "(...)" $$ nest 2 (pretty e)- Just s -> text (show s) $$ nest 2 (pretty e)- internal :: String -> Doc -> Doc internal n d = char '<' <> text n <+> d <> char '>' @@ -240,6 +258,19 @@ keyword k | isOperator k = k | otherwise = k ++ ":"++prettySpacedExpr :: Context -> Expr -> Doc+prettySpacedExpr c e+ | needsParens e = parens (prettyFrom c e)+ | otherwise = prettyFrom c e+ where+ needsParens (Define {}) = True+ needsParens (Set {}) = True+ needsParens (Dispatch { eMessage = EKeyword {} }) = True+ needsParens (Dispatch { eMessage = ESingle { emTarget = ETop {} } }) = False+ needsParens (Dispatch { eMessage = ESingle {} }) = True+ needsParens _ = False+ infixr 4 <++>, <+++>
src/Atomo/PrettyVM.hs view
@@ -5,7 +5,6 @@ import Atomo.Types --- | pretty-print by sending \@show to the object+-- | Pretty-print a value by sending @show@ to it. prettyVM :: Value -> VM String prettyVM = liftM (fromText . fromString) . dispatch . single "show"-
src/Atomo/QuasiQuotes.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE TemplateHaskell #-} {-# OPTIONS -fno-warn-name-shadowing #-} module Atomo.QuasiQuotes ( p@@ -5,58 +6,55 @@ , es ) where -import "monads-fd" Control.Monad.State-import Data.IORef+import "monads-fd" Control.Monad.State hiding (lift)+import Data.Maybe (fromJust) import Data.Typeable import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax import System.IO.Unsafe import Text.Parsec-import qualified Data.Text as T import qualified Language.Haskell.TH as TH import Atomo.Core+import Atomo.Helpers (fromHaskell') import Atomo.Parser-import Atomo.Parser.Pattern import Atomo.Parser.Base+import Atomo.Parser.Expr+import Atomo.Pattern (toDefinePattern) import Atomo.Types -qqEnv :: IORef Env-qqEnv = unsafePerformIO $ do- (_, e) <- runVM (initCore >> return (particle "ok")) startEnv- newIORef e +qqEnv :: Env+qqEnv = snd $ unsafePerformIO $+ runVM (initCore >> return (particle "ok")) startEnv++-- | Pattern quasi-quoter. p :: QuasiQuoter p = QuasiQuoter quotePatternExp undefined +-- | Single expression quasi-quoter. e :: QuasiQuoter e = QuasiQuoter quoteExprExp undefined +-- | Quasi-quoter for multiple expressions (a block of code). es :: QuasiQuoter es = QuasiQuoter quoteExprsExp undefined -withLocation :: (String -> (String, Int, Int) -> Q a) -> (a -> Exp) -> String -> TH.ExpQ+withLocation :: (String -> (String, Int, Int) -> a) -> (a -> Q Exp) -> String -> TH.ExpQ withLocation p c s = do l <- TH.location- r <- p s+ c $ p s ( TH.loc_filename l , fst $ TH.loc_start l , snd $ TH.loc_start l )- return (c r) -parsing :: (Monad m, Typeable a) => Parser a -> String -> (String, Int, Int) -> m a+parsing :: Typeable a => Parser a -> String -> (String, Int, Int) -> a parsing p s (file, line, col) =- -- OH GOD!- -- OH GOD! NO!- -- WHYYYYYYYYYYYYYYYYYYYYYY- case unsafePerformIO (runWith go (unsafePerformIO (readIORef qqEnv))) of- x -> return (fromHaskell' "a" x)+ -- here be dragons+ fromHaskell' "a" $ unsafePerformIO (runWith go (qqEnv)) where- go = do- r <- liftM haskell $ continue pp "<qq>" s- get >>= liftIO . writeIORef qqEnv- return r+ go = liftM haskell $ continue pp "<qq>" s pp = do pos <- getPosition@@ -71,115 +69,10 @@ return e quotePatternExp :: String -> TH.ExpQ-quotePatternExp = withLocation (parsing ppDefine) patternToExp+quotePatternExp = withLocation (parsing (liftM (fromJust . toDefinePattern) pExpr)) lift quoteExprExp :: String -> TH.ExpQ-quoteExprExp = withLocation (parsing pExpr) exprToExp+quoteExprExp = withLocation (parsing pExpr) lift quoteExprsExp :: String -> TH.ExpQ-quoteExprsExp = withLocation (parsing (wsBlock pExpr)) (ListE . map exprToExp)--exprToExp :: Expr -> Exp-exprToExp (Define l p e) = AppE (AppE (expr "Define" l) (patternToExp p)) (exprToExp e)-exprToExp (Set l p e) = AppE (AppE (expr "Set" l) (patternToExp p)) (exprToExp e)-exprToExp (Dispatch l m) = AppE (expr "Dispatch" l) (emessageToExp m)-exprToExp (Operator l ns a p) = AppE (AppE (AppE (expr "Operator" l) (ListE (map (LitE . StringL) ns))) (assocToExp a)) (LitE (IntegerL p))-exprToExp (Primitive l v) = AppE (expr "Primitive" l) (valueToExp v)-exprToExp (EBlock l as es) =- AppE (AppE (expr "EBlock" l) (ListE (map patternToExp as))) (ListE (map exprToExp es))-exprToExp (EVM {}) = error "cannot exprToExp EVM"-exprToExp (EList l es) =- AppE (expr "EList" l) (ListE (map exprToExp es))-exprToExp (ETop l) =- expr "ETop" l-exprToExp (EParticle l p) =- AppE (expr "EParticle" l) (eparticleToExp p)-exprToExp (EMacro l p e) =- AppE (AppE (expr "EMacro" l) (patternToExp p)) (exprToExp e)-exprToExp (EQuote l e) =- AppE (expr "EQuote" l) (exprToExp e)-exprToExp (EUnquote l e) =- AppE (expr "EUnquote" l) (exprToExp e)--assocToExp :: Assoc -> Exp-assocToExp ALeft = ConE (mkName "ALeft")-assocToExp ARight = ConE (mkName "ARight")--messageToExp :: Message -> Exp-messageToExp (Keyword i ns vs) =- AppE (AppE (AppE (ConE (mkName "Keyword")) (LitE (IntegerL (fromIntegral i)))) (ListE (map (LitE . StringL) ns))) (ListE (map valueToExp vs))-messageToExp (Single i n v) =- AppE (AppE (AppE (ConE (mkName "Single")) (LitE (IntegerL (fromIntegral i)))) (LitE (StringL n))) (valueToExp v)--particleToExp :: Particle -> Exp-particleToExp (PMSingle n) =- AppE (ConE (mkName "PMSingle")) (LitE (StringL n))-particleToExp (PMKeyword ns vs) =- AppE (AppE (ConE (mkName "PMKeyword")) (ListE (map (LitE . StringL) ns))) (ListE (map maybeValue vs))- where- maybeValue Nothing = ConE (mkName "Nothing")- maybeValue (Just v) = AppE (ConE (mkName "Just")) (valueToExp v)--emessageToExp :: EMessage -> Exp-emessageToExp (EKeyword i ns es) =- AppE (AppE (AppE (ConE (mkName "EKeyword")) (LitE (IntegerL (fromIntegral i)))) (ListE (map (LitE . StringL) ns))) (ListE (map exprToExp es))-emessageToExp (ESingle i n e) =- AppE (AppE (AppE (ConE (mkName "ESingle")) (LitE (IntegerL (fromIntegral i)))) (LitE (StringL n))) (exprToExp e)--eparticleToExp :: EParticle -> Exp-eparticleToExp (EPMSingle n) =- AppE (ConE (mkName "EPMSingle")) (LitE (StringL n))-eparticleToExp (EPMKeyword ns es) =- AppE (AppE (ConE (mkName "EPMKeyword")) (ListE (map (LitE . StringL) ns))) (ListE (map maybeExpr es))- where- maybeExpr Nothing = ConE (mkName "Nothing")- maybeExpr (Just e) = AppE (ConE (mkName "Just")) (exprToExp e)--expr :: String -> Maybe SourcePos -> Exp-expr n _ = AppE (ConE (mkName n)) (ConE (mkName "Nothing"))--valueToExp :: Value -> Exp-valueToExp (Block s as es) =- AppE (AppE (AppE (ConE (mkName "Block")) (valueToExp s)) (ListE (map patternToExp as))) (ListE (map exprToExp es))-valueToExp (Boolean b) = AppE (ConE (mkName "Boolean")) (ConE (mkName (show b)))-valueToExp (Char c) = AppE (ConE (mkName "Char")) (LitE (CharL c))-valueToExp (Double d) = AppE (ConE (mkName "Double")) (LitE (RationalL (toRational d)))-valueToExp (Expression e) = AppE (ConE (mkName "Expression")) (exprToExp e)-valueToExp (Integer i) = AppE (ConE (mkName "Integer")) (LitE (IntegerL i))-valueToExp (Message m) = AppE (ConE (mkName "Message")) (messageToExp m)-valueToExp (Particle p) = AppE (ConE (mkName "Particle")) (particleToExp p)-valueToExp (Pattern p) = AppE (ConE (mkName "Pattern")) (patternToExp p)-valueToExp (String s) = AppE (VarE (mkName "string")) (LitE (StringL (T.unpack s)))-valueToExp v = error $ "no valueToExp for: " ++ show v--patternToExp :: Pattern -> Exp-patternToExp PAny = ConE (mkName "PAny")-patternToExp (PHeadTail h t) = AppE (AppE (ConE (mkName "PHeadTail")) (patternToExp h)) (patternToExp t)-patternToExp (PKeyword i ns ts) =- AppE (AppE (AppE (ConE (mkName "PKeyword")) (LitE (IntegerL (fromIntegral i)))) (ListE (map (LitE . StringL) ns))) (ListE (map patternToExp ts))-patternToExp (PList ps) =- AppE (ConE (mkName "PList")) (ListE (map patternToExp ps))-patternToExp (PMatch v) =- AppE (ConE (mkName "PMatch")) (valueToExp v)-patternToExp (PNamed n p) =- AppE (AppE (ConE (mkName "PNamed")) (LitE (StringL n))) (patternToExp p)-patternToExp (PObject e) =- AppE (ConE (mkName "PObject")) (exprToExp e)-patternToExp (PPMKeyword ns ts) =- AppE (AppE (ConE (mkName "PPMKeyword")) (ListE (map (LitE . StringL) ns))) (ListE (map patternToExp ts))-patternToExp (PSingle i n t) =- AppE (AppE (AppE (ConE (mkName "PSingle")) (LitE (IntegerL (fromIntegral i)))) (LitE (StringL n))) (patternToExp t)-patternToExp PThis = ConE (mkName "PThis")-patternToExp PEDefine = ConE (mkName "PEDefine")-patternToExp PESet = ConE (mkName "PESet")-patternToExp PEDispatch = ConE (mkName "PEDispatch")-patternToExp PEOperator = ConE (mkName "PEOperator")-patternToExp PEPrimitive = ConE (mkName "PEPrimitive")-patternToExp PEBlock = ConE (mkName "PEBlock")-patternToExp PEList = ConE (mkName "PEList")-patternToExp PEMacro = ConE (mkName "PEMacro")-patternToExp PEParticle = ConE (mkName "PEParticle")-patternToExp PETop = ConE (mkName "PETop")-patternToExp PEQuote = ConE (mkName "PEQuote")-patternToExp PEUnquote = ConE (mkName "PEUnquote")-patternToExp (PExpr e) = AppE (ConE (mkName "PExpr")) (exprToExp e)+quoteExprsExp = withLocation (parsing (wsBlock pExpr)) (fmap ListE . mapM lift)
src/Atomo/Run.hs view
@@ -5,8 +5,8 @@ import Atomo.Core import Atomo.Environment+import Atomo.Helpers (here) import Atomo.Load-import Atomo.Spawn (go) import Atomo.Types import qualified Atomo.Kernel as Kernel @@ -17,18 +17,17 @@ -- Execution ---------------------------------------------------------------- ----------------------------------------------------------------------------- --- | execute an action in a new thread, initializing the environment and--- printing a traceback on error+-- | Execute an action in a new thread, initializing the environment first. exec :: VM Value -> IO () exec x = execWith (initEnv >> x) startEnv --- | execute an action in a new thread, printing a traceback on error+-- | Execute an action in a new thread. execWith :: VM Value -> Env -> IO () execWith x e = do haltChan <- newChan forkIO $ do- runWith (go x >> gets halt >>= liftIO >> return (particle "ok")) e+ runWith (x >> gets halt >>= liftIO >> return (particle "ok")) e { halt = writeChan haltChan () >> myThreadId >>= killThread } @@ -36,15 +35,16 @@ readChan haltChan --- | execute x, initializing the environment with initEnv+-- | Execute x, initializing the environment first. run :: VM Value -> IO Value run x = runWith (initEnv >> x) startEnv --- | set up the primitive objects, etc.+-- | Set up the primitive objects, and load up the kernel and prelude. initEnv :: VM () {-# INLINE initEnv #-} initEnv = initCore >> Kernel.load >> loadPrelude +-- | Load all of the prelude and load the ecosystem. loadPrelude :: VM () loadPrelude = do forM_ preludes $ \p ->
src/Atomo/Spawn.hs view
@@ -6,17 +6,13 @@ import Atomo.Types --- | spawn a process to execute x. returns the Process.+-- | Spawn a process to execute x. Returns the Process value. spawn :: VM Value -> VM Value spawn x = do e <- get chan <- liftIO newChan tid <- liftIO . forkIO $ do- runWith (go x >> return (particle "ok")) (e { channel = chan })+ runWith (x >> return (particle "ok")) (e { channel = chan }) return () return (Process chan tid)---- | execute x, printing an error if there is one-go :: VM Value -> VM Value-go x = x --catchError x (\e -> printError e >> return (particle "ok"))
src/Atomo/Types.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE DeriveDataTypeable, TypeSynonymInstances #-}+{-# LANGUAGE DeriveDataTypeable, TemplateHaskell, TypeSynonymInstances #-} module Atomo.Types where import Control.Concurrent (ThreadId)@@ -8,7 +8,6 @@ import Data.Dynamic import Data.Hashable (hash) import Data.List (nub)-import Data.Maybe (fromMaybe) import Data.IORef import Text.Parsec (ParseError, SourcePos) import Text.PrettyPrint (Doc)@@ -16,60 +15,111 @@ import qualified Data.Text as T import qualified Data.Vector as V import qualified Language.Haskell.Interpreter as H+import qualified Language.Haskell.TH.Syntax as S ++-- | The Atomo VM. A Continuation monad wrapping a State monad. type VM = ContT Value (StateT Env IO) +-- | All values usable in Atomo. data Value+ -- | A block of expressions, bound to a context and with optional arguments. = Block !Value [Pattern] [Expr]++ -- | A boolean value. | Boolean { fromBoolean :: {-# UNPACK #-} !Bool }++ -- | A character value. | Char { fromChar :: {-# UNPACK #-} !Char }++ -- | A continuation reference. | Continuation { fromContinuation :: Continuation }++ -- | A double value. | Double { fromDouble :: {-# UNPACK #-} !Double }++ -- | An expression value. | Expression { fromExpression :: Expr }++ -- | A dynamically-typed Haskell value. | Haskell Dynamic++ -- | An Integer value. | Integer { fromInteger :: !Integer }++ -- | A vector of Values. | List VVector++ -- | A message value. | Message { fromMessage :: Message }++ -- | A method value. | Method { fromMethod :: Method }++ -- | A particle value. | Particle { fromParticle :: Particle }++ -- | A process; a communications channel and the thread's ID. | Process Channel ThreadId++ -- | A pattern value. | Pattern { fromPattern :: Pattern }++ -- | A rational value. | Rational Rational++ -- | An object reference. | Reference { rORef :: {-# UNPACK #-} !ORef }++ -- | A string value; Data.Text.Text. | String { fromString :: !T.Text } deriving (Show, Typeable) +-- | A pure object. data Object = Object- { oDelegates :: !Delegates- , oMethods :: !(MethodMap, MethodMap) -- singles, keywords+ { -- | The object's delegates list.+ oDelegates :: !Delegates++ -- | A pair of (single, keyword) methods.+ , oMethods :: !(MethodMap, MethodMap) } deriving (Show, Typeable) +-- | Methods, slot, and macro methods. data Method+ -- | Responds to a message by evaluating an expression in the given context. = Responder { mPattern :: !Pattern , mContext :: !Value , mExpr :: !Expr }++ -- | Responds to a macro message by evaluating an expression. | Macro { mPattern :: !Pattern , mExpr :: !Expr }++ -- | Responds to a message by returning a value. | Slot { mPattern :: !Pattern , mValue :: !Value } deriving (Eq, Show, Typeable) +-- | Messages sent to objects. data Message+ -- | A keyword-delimited message with multiple targets. = Keyword { mID :: !Int , mNames :: [String] , mTargets :: [Value] }++ -- | A single message sent to one target. | Single { mID :: !Int , mName :: String@@ -77,11 +127,16 @@ } deriving (Eq, Show, Typeable) +-- | Partial messages. data Particle+ -- | A single message with no target. = PMSingle String++ -- | A keyword message with many optional targets. | PMKeyword [String] [Maybe Value] deriving (Eq, Show, Typeable) +-- | Shortcut error values. data AtomoError = Error Value | ParseError ParseError@@ -96,7 +151,7 @@ | DynamicNeeded String deriving (Show, Typeable) --- pattern-matches+-- | Pattern-matching. data Pattern = PAny | PHeadTail Pattern Pattern@@ -107,6 +162,8 @@ } | PList [Pattern] | PMatch Value+ | PInstance Pattern+ | PStrict Pattern | PNamed String Pattern | PObject Expr | PPMKeyword [String] [Pattern]@@ -118,8 +175,6 @@ | PThis -- expression types, used in macros- | PEDefine- | PESet | PEDispatch | PEOperator | PEPrimitive@@ -134,7 +189,7 @@ | PExpr Expr deriving (Show, Typeable) --- expressions+-- | Expressions; the nodes in a syntax tree. data Expr = Define { eLocation :: Maybe SourcePos@@ -174,6 +229,10 @@ , ePattern :: Pattern , eExpr :: Expr }+ | EForMacro+ { eLocation :: Maybe SourcePos+ , eExpr :: Expr+ } | EParticle { eLocation :: Maybe SourcePos , eParticle :: EParticle@@ -196,6 +255,7 @@ } deriving (Show, Typeable) +-- | An unevaluated message dispatch. data EMessage = EKeyword { emID :: !Int@@ -209,34 +269,51 @@ } deriving (Eq, Show, Typeable) +-- | An unevaluated particle. data EParticle = EPMSingle String | EPMKeyword [String] [Maybe Expr] deriving (Eq, Show, Typeable) --- the evaluation environment+-- | Atomo's VM state. data Env = Env- { top :: Value+ { -- | The current toplevel object.+ top :: Value++ -- | A record of objects representing the primitive values. , primitives :: IDs++ -- | This process's communications channel. , channel :: Channel++ -- | Function to call which will shut down the entire VM when called+ -- from any thread. , halt :: IO ()++ -- | Paths to search for files. , loadPath :: [FilePath]++ -- | Files aready loaded. , loaded :: [FilePath]++ -- | The last N expressions evaluated up to the current error. , stack :: [Expr]++ -- | The parser's state, passed around when a parser action needs to+ -- be run. , parserState :: ParserState } deriving Typeable --- operator associativity+-- | Operator associativity. data Assoc = ALeft | ARight deriving (Eq, Show, Typeable) --- a giant record of the objects for each primitive value+-- | A giant record of the objects for each primitive value. data IDs = IDs- { idMatch :: ORef -- used in dispatch to refer to the object currently being searched- , idObject :: ORef -- root object+ { idObject :: ORef , idBlock :: ORef , idBoolean :: ORef , idChar :: ORef@@ -256,28 +333,45 @@ } deriving (Show, Typeable) -+-- | State underlying the parser, and saved in the VM. data ParserState = ParserState- { psOperators :: Operators+ { -- | Operator precedence and associativity.+ psOperators :: Operators++ -- | All defined macros; (single macros, keyword macros). , psMacros :: (MethodMap, MethodMap)++ -- | Are we parsing in a quasiquote?+ , psInQuote :: Bool++ -- | The number of macros we've expanded.+ , psClock :: Integer } deriving (Show, Typeable) -startParserState :: ParserState-startParserState = ParserState [] (M.empty, M.empty)+-- | A map of operators to their associativity and precedence.+type Operators = [(String, (Assoc, Integer))] --- helper synonyms-type Operators = [(String, (Assoc, Integer))] -- name -> assoc, precedence+-- | The list of values an object delegates to. type Delegates = [Value]++-- | A channel for sending values through to processes. type Channel = Chan Value++-- | A selector-indexed and precision-sorted map of methods. type MethodMap = M.IntMap [Method]++-- | A reference to an object. type ORef = IORef Object++-- | A vector containing values. type VVector = V.Vector Value++-- | A reference to a continuation function. type Continuation = IORef (Value -> VM Value) --- a basic Eq instance instance Eq Value where (==) (Block at aps aes) (Block bt bps bes) = at == bt && aps == bps && aes == bes@@ -353,13 +447,92 @@ typeOf _ = mkTyConApp (mkTyCon "VM") [typeOf ()] +instance S.Lift Expr where+ lift (Define _ p e) = [| Define Nothing p e |]+ lift (Set _ p e) = [| Set Nothing p e |]+ lift (Dispatch _ m) = [| Dispatch Nothing m |]+ lift (Operator _ ns a p) = [| Operator Nothing ns a p |]+ lift (Primitive _ v) = [| Primitive Nothing v |]+ lift (EBlock _ as es) = [| EBlock Nothing as es |]+ lift (EVM {}) = error "cannot lift EVM"+ lift (EList _ es) = [| EList Nothing es |]+ lift (ETop _) = [| ETop Nothing |]+ lift (EParticle _ p) = [| EParticle Nothing p |]+ lift (EMacro _ p e) = [| EMacro Nothing p e |]+ lift (EForMacro _ e) = [| EForMacro Nothing e |]+ lift (EQuote _ e) = [| EQuote Nothing e |]+ lift (EUnquote _ e) = [| EUnquote Nothing e |]++instance S.Lift Assoc where+ lift ALeft = [| ALeft |]+ lift ARight = [| ARight |]++instance S.Lift Message where+ lift (Keyword i ns vs) = [| Keyword i ns vs |]+ lift (Single i n v) = [| Single i n v |]++instance S.Lift Particle where+ lift (PMSingle n) = [| PMSingle n |]+ lift (PMKeyword ns vs) = [| PMKeyword ns vs |]++instance S.Lift EMessage where+ lift (EKeyword i ns es) = [| EKeyword i ns es |]+ lift (ESingle i n e) = [| ESingle i n e |]++instance S.Lift EParticle where+ lift (EPMSingle n) = [| EPMSingle n |]+ lift (EPMKeyword ns es) = [| EPMKeyword ns es |]++instance S.Lift Value where+ lift (Block s as es) = [| Block s as es |]+ lift (Boolean b) = [| Boolean b |]+ lift (Char c) = [| Char c |]+ lift (Double d) = [| Double $(return $ S.LitE (S.RationalL (toRational d))) |]+ lift (Expression e) = [| Expression e |]+ lift (Integer i) = [| Integer i |]+ lift (Message m) = [| Message m |]+ lift (Particle p) = [| Particle p |]+ lift (Pattern p) = [| Pattern p |]+ lift (String s) = [| String (T.pack $(return $ S.LitE (S.StringL (T.unpack s)))) |]+ lift v = error $ "no lift for: " ++ show v++instance S.Lift Pattern where+ lift PAny = [| PAny |]+ lift (PHeadTail h t) = [| PHeadTail h t |]+ lift (PKeyword i ns ts) = [| PKeyword i ns ts |]+ lift (PList ps) = [| PList ps |]+ lift (PMatch v) = [| PMatch v |]+ lift (PNamed n p) = [| PNamed n p |]+ lift (PObject e) = [| PObject e |]+ lift (PPMKeyword ns ts) = [| PPMKeyword ns ts |]+ lift (PSingle i n t) = [| PSingle i n t |]+ lift PThis = [| PThis |]+ lift PEDispatch = [| PEDispatch |]+ lift PEOperator = [| PEOperator |]+ lift PEPrimitive = [| PEPrimitive |]+ lift PEBlock = [| PEBlock |]+ lift PEList = [| PEList |]+ lift PEMacro = [| PEMacro |]+ lift PEParticle = [| PEParticle |]+ lift PETop = [| PETop |]+ lift PEQuote = [| PEQuote |]+ lift PEUnquote = [| PEUnquote |]+ lift (PExpr e) = [| PExpr e |]+ lift (PInstance p) = [| PInstance p |]+ lift (PStrict p) = [| PStrict p |]+++-- | Initial "empty" parser state.+startParserState :: ParserState+startParserState = ParserState [] (M.empty, M.empty) False 0++-- | Initial "empty" environment state. startEnv :: Env startEnv = Env { top = error "top object not set" , primitives = IDs- { idMatch = error "idMatch not set"- , idObject = error "idObject not set"+ { idObject = error "idObject not set" , idBlock = error "idBlock not set" , idBoolean = error "idBoolean not set" , idChar = error "idChar not set"@@ -385,11 +558,11 @@ , parserState = startParserState } --- | evaluate x with e as the environment+-- | Evaluate x with e as the environment. runWith :: VM Value -> Env -> IO Value runWith x = evalStateT (runContT x return) --- | evaluate x with e as the environment+-- | Evaluate x with e as the environment. runVM :: VM Value -> Env -> IO (Value, Env) runVM x = runStateT (runContT x return) @@ -398,153 +571,162 @@ -- Helpers ------------------------------------------------------------------ ----------------------------------------------------------------------------- +-- | Create a single particle with a given name. particle :: String -> Value {-# INLINE particle #-} particle = Particle . PMSingle +-- | Create a keyword particle with a given name and optional values. keyParticle :: [String] -> [Maybe Value] -> Value {-# INLINE keyParticle #-} keyParticle ns vs = Particle $ PMKeyword ns vs +-- | Create a keyword particle with no first role and the given values. keyParticleN :: [String] -> [Value] -> Value {-# INLINE keyParticleN #-} keyParticleN ns vs = keyParticle ns (Nothing:map Just vs) +-- | Convert a String into a Value. string :: String -> Value {-# INLINE string #-} string = String . T.pack +-- | Convert a Typeable value into a Haskell Value. haskell :: Typeable a => a -> Value {-# INLINE haskell #-} haskell = Haskell . toDyn +-- | Convert a list of values into a List Value. list :: [Value] -> Value+{-# INLINE list #-} list = List . V.fromList +-- | Convert a Text string into a String. fromText :: T.Text -> String+{-# INLINE fromText #-} fromText = T.unpack +-- | Convert a List Value into a list of Values. fromList :: Value -> [Value] fromList (List vr) = V.toList vr fromList v = error $ "no fromList for: " ++ show v +-- | Create a single message with a given name and target. single :: String -> Value -> Message {-# INLINE single #-} single n = Single (hash n) n +-- | Create a keyword message with a given name and targets. keyword :: [String] -> [Value] -> Message {-# INLINE keyword #-} keyword ns = Keyword (hash ns) ns +-- | Create a single message pattern with a given name and target pattern. psingle :: String -> Pattern -> Pattern {-# INLINE psingle #-} psingle n = PSingle (hash n) n +-- | Create a keyword message pattern with a given name and target patterns. pkeyword :: [String] -> [Pattern] -> Pattern {-# INLINE pkeyword #-} pkeyword ns = PKeyword (hash ns) ns +-- | Create a single message expression with a given name and target expression. esingle :: String -> Expr -> EMessage {-# INLINE esingle #-} esingle n = ESingle (hash n) n +-- | Create a keyword message expression with a given name and target expressions. ekeyword :: [String] -> [Expr] -> EMessage {-# INLINE ekeyword #-} ekeyword ns = EKeyword (hash ns) ns --- | Fill in the empty values of a particle. The number of values missing--- is expected to be equal to the number of values provided.-completeKP :: [Maybe Value] -> [Value] -> [Value]-completeKP [] [] = []-completeKP (Nothing:mvs') (v:vs') = v : completeKP mvs' vs'-completeKP (Just v:mvs') vs' = v : completeKP mvs' vs'-completeKP mvs' vs' = error $ "impossible: completeKP on " ++ show (mvs', vs')---- | Is a value a Block?+-- | Is a value a `Block'? isBlock :: Value -> Bool isBlock (Block _ _ _) = True isBlock _ = False --- | Is a value a Boolean?+-- | Is a value a `Boolean'? isBoolean :: Value -> Bool isBoolean (Boolean _) = True isBoolean _ = False --- | Is a value a Char?+-- | Is a value a `Char'? isChar :: Value -> Bool isChar (Char _) = True isChar _ = False --- | Is a value a Continuation?+-- | Is a value a `Continuation'? isContinuation :: Value -> Bool isContinuation (Continuation _) = True isContinuation _ = False --- | Is a value a Double?+-- | Is a value a `Double'? isDouble :: Value -> Bool isDouble (Double _) = True isDouble _ = False --- | Is a value an Expression?+-- | Is a value an `Expression'? isExpression :: Value -> Bool isExpression (Expression _) = True isExpression _ = False --- | Is a value a Haskell value?+-- | Is a value a `Haskell'? isHaskell :: Value -> Bool isHaskell (Haskell _) = True isHaskell _ = False --- | Is a value an Integer?+-- | Is a value an `Integer'? isInteger :: Value -> Bool isInteger (Integer _) = True isInteger _ = False --- | Is a value a List?+-- | Is a value a `List'? isList :: Value -> Bool isList (List _) = True isList _ = False --- | Is a value a Message?+-- | Is a value a `Message'? isMessage :: Value -> Bool isMessage (Message _) = True isMessage _ = False --- | Is a value a Method?+-- | Is a value a `Method'? isMethod :: Value -> Bool isMethod (Method _) = True isMethod _ = False --- | Is a value a Particle?+-- | Is a value a `Particle'? isParticle :: Value -> Bool isParticle (Particle _) = True isParticle _ = False --- | Is a value a Pattern?+-- | Is a value a `Pattern'? isPattern :: Value -> Bool isPattern (Pattern _) = True isPattern _ = False --- | Is a value a Process?+-- | Is a value a `Process'? isProcess :: Value -> Bool isProcess (Process _ _) = True isProcess _ = False --- | Is a value a Rational?+-- | Is a value a `Rational'? isRational :: Value -> Bool isRational (Rational _) = True isRational _ = False --- | Is a value a Reference?+-- | Is a value a `Reference'? isReference :: Value -> Bool isReference (Reference _) = True isReference _ = False --- | Is a value a String?+-- | Is a value a `String'? isString :: Value -> Bool isString (String _) = True isString _ = False +-- | Convert an AtomoError into the Value we want to error with. asValue :: AtomoError -> Value asValue (Error v) = v asValue (ParseError pe) =@@ -575,12 +757,27 @@ [Integer (fromIntegral e'), Integer (fromIntegral g)] asValue NoExpressions = particle "no-expressions" asValue (ValueNotFound d v) =- keyParticleN ["could-not-found", "in"] [string d, v]+ keyParticleN ["could-not-find", "in"] [string d, v] asValue (DynamicNeeded t) = keyParticleN ["dynamic-needed"] [string t] -fromHaskell' :: Typeable a => String -> Value -> a-fromHaskell' t (Haskell d) =- fromMaybe (error ("needed Haskell value of type " ++ t))- (fromDynamic d)-fromHaskell' t _ = error ("needed haskell value of type " ++ t)+-- | Given a record of primitive IDs, get the object reference backing a value.+orefFrom :: IDs -> Value -> ORef+{-# INLINE orefFrom #-}+orefFrom _ (Reference r) = r+orefFrom ids (Block _ _ _) = idBlock ids+orefFrom ids (Boolean _) = idBoolean ids+orefFrom ids (Char _) = idChar ids+orefFrom ids (Continuation _) = idContinuation ids+orefFrom ids (Double _) = idDouble ids+orefFrom ids (Expression _) = idExpression ids+orefFrom ids (Haskell _) = idHaskell ids+orefFrom ids (Integer _) = idInteger ids+orefFrom ids (List _) = idList ids+orefFrom ids (Message _) = idMessage ids+orefFrom ids (Method _) = idMethod ids+orefFrom ids (Particle _) = idParticle ids+orefFrom ids (Process _ _) = idProcess ids+orefFrom ids (Pattern _) = idPattern ids+orefFrom ids (Rational _) = idRational ids+orefFrom ids (String _) = idString ids
src/Atomo/VMT.hs view
@@ -6,6 +6,8 @@ import Atomo.Types +-- | A monad transformer for Atomo's VM which just passes around the VM+-- environment. newtype VMT m a = VMT { runVMT :: Env -> m (a, Env)@@ -32,17 +34,23 @@ callCC f = VMT $ \e -> callCC $ \c -> runVMT (f (\a -> VMT (\e' -> c (a, e')))) e ++-- | Execute a VM action inside a VMT monad. vm :: MonadIO m => VM Value -> VMT m Value vm x = VMT (liftIO . runStateT (runContT x return)) +-- | Like `vm', but ignores the result. vm_ :: MonadIO m => VM a -> VMT m () vm_ x = vm (x >> return (particle "ok")) >> return () +-- | Grab the underlying VM envionment. getEnv :: Monad m => VMT m Env getEnv = VMT $ \e -> return (e, e) +-- | Replace the underlying VM environment. putEnv :: Monad m => Env -> VMT m () putEnv e = VMT $ \_ -> return ((), e) +-- | Execute a VMT monad with an initial environment, returning its result. execVM :: Monad m => VMT m a -> Env -> m a execVM x e = liftM fst (runVMT x e)
src/Atomo/Valuable.hs view
@@ -10,7 +10,10 @@ class Valuable a where+ -- | Convert to an Atomo value. toValue :: a -> VM Value++ -- | Convert from an Atomo value. fromValue :: Value -> VM a instance Valuable Value where
src/Main.hs view
@@ -5,10 +5,12 @@ import System.Environment (getArgs) import Atomo+import Atomo.Core import Atomo.Load import Atomo.Parser import Atomo.PrettyVM import Atomo.Run+import qualified Atomo.Kernel as Kernel main :: IO ()@@ -18,15 +20,18 @@ case args of [] -> exec repl + ["-x"] -> noPrelude primRepl+ ("-x":fn:_) -> noPrelude (parseFile fn >>= liftIO . mapM_ print >> liftIO (putStrLn "result:") >> loadFile fn)+ ("-e":expr:_) -> exec $ do- ast <- continuedParse expr "<input>"+ ast <- parseInput expr r <- evalAll ast d <- prettyVM r liftIO (putStrLn d) return (particle "ok") ("-s":expr:_) -> exec $ do- ast <- continuedParse expr "<input>"+ ast <- parseInput expr evalAll ast repl @@ -46,5 +51,20 @@ , "\tatomo FILE\texecute FILE" ] +noPrelude :: VM Value -> IO ()+noPrelude x = do+ runWith (initCore >> Kernel.load >> x) startEnv+ return ()+ repl :: VM Value repl = eval [$e|Lobby clone repl|]++primRepl :: VM Value+primRepl = do+ liftIO (putStrLn "next:")+ expr <- liftIO getLine+ ast <- parseInput expr+ r <- evalAll ast+ d <- prettyVM r+ liftIO (putStrLn d)+ primRepl