root/src/Pugs/Prim.hs

Revision 19198, 85.6 kB (checked in by ferreira, 7 months ago)

r185@dracma: perl | 2007-12-12 17:36:17 -0200
added boolean xor infix operator

  • Property svn:mime-type set to text/plain; charset=UTF-8
  • Property svn:eol-style set to native
<
Line 
1 {-# OPTIONS_GHC -fglasgow-exts -fno-warn-orphans -fno-full-laziness -fno-cse -fallow-overlapping-instances #-}
2
3 {-|
4     Primitive operators.
5
6 >   There hammer on the anvil smote,
7 >   There chisel clove, and graver wrote;
8 >   There forged was blade, and bound was hilt;
9 >   The delver mined, the mason built...
10 -}
11
12 module Pugs.Prim (
13     primOp,
14     primDecl,
15     initSyms,
16     op2ChainedList,
17     op1Exit,
18
19     -- used by Pugs.Compile.Haskell
20     op0, op1, op2,
21
22     -- used Pugs.Eval
23     op1Return, op1Yield,
24     foldParam, op2Hyper, op1HyperPrefix, op1HyperPostfix, retSeq, atomicEval
25 ) where
26 import Pugs.Internals
27 import Pugs.Junc
28 import Pugs.AST
29 import Pugs.Types
30 import Pugs.Monads
31 import Pugs.Pretty
32 import Pugs.DeepSeq
33 import Text.Printf
34 import Pugs.External
35 import Pugs.Embed
36 import Pugs.Eval.Var
37 import Pugs.Meta ()
38 import qualified Data.Map as Map
39 import qualified Data.Set as Set
40 import Data.IORef
41 import System.IO.Error (isEOFError)
42 import Control.Exception (ioErrors)
43
44 import Pugs.Prim.Keyed
45 import Pugs.Prim.Yaml
46 import Pugs.Prim.Match
47 import Pugs.Prim.List
48 import Pugs.Prim.Numeric
49 import Pugs.Prim.Lifts
50 import Pugs.Prim.Eval
51 import Pugs.Prim.Code
52 import Pugs.Prim.Param
53 import qualified Data.IntSet as IntSet
54 import DrIFT.YAML
55 import GHC.Exts (unsafeCoerce#)
56 import GHC.Unicode
57 import qualified Data.HashTable as H
58 import Data.Time.LocalTime
59 import Data.Time.Calendar.OrdinalDate
60 import Data.Time.Calendar.MonthDay
61
62 constMacro :: Exp -> [Val] -> Eval Val
63 constMacro = const . expToEvalVal
64
65 -- |Implementation of 0-ary and variadic primitive operators and functions
66 -- (including list ops).
67 op0 :: String -> [Val] -> Eval Val
68 op0 "&"  = fmap opJuncAll . mapM fromVal
69 op0 "^"  = fmap opJuncOne . mapM fromVal
70 op0 "|"  = fmap opJuncAny . mapM fromVal
71 op0 "want"  = const $ fmap VStr (asks (maybe "Item" envWant . envCaller))
72 op0 "Bool::True"  = const . return $ VBool True
73 op0 "Bool::False" = const . return $ VBool False
74 op0 "True"  = constMacro . Val $ VBool True
75 op0 "False" = constMacro . Val $ VBool False
76 op0 "time"  = const $ do
77     clkt <- guardIO getCurrentTime
78     return $ VRat $ pugsTimeSpec clkt
79 op0 "times"  = const $ do
80     ProcessTimes _ u s cu cs <- guardIO getProcessTimes
81     return . VList $ map (castV . (% (clocksPerSecond :: VInt)) . toInteger . fromEnum)
82         [u, s, cu, cs]
83 op0 "Z" = op0Zip
84 op0 "X" = op0Cross
85     -- op0 "minmax" = op0Minmax
86 op0 "File::Spec::cwd" = const $ do
87     cwd <- guardIO getCurrentDirectory
88     return $ VStr cwd
89 op0 "File::Spec::tmpdir" = const $ do
90     tmp <- guardIO getTemporaryDirectory
91     return $ VStr tmp
92 op0 "Pugs::Internals::pi" = const $ return $ VNum pi
93 op0 "self"    = const $ expToEvalVal (_Var "$__SELF__")
94 op0 "say"     = const $ op1 "IO::say" (VHandle stdout)
95 op0 "print"   = const $ op1 "IO::print" (VHandle stdout)
96 op0 "return"  = const $ op1Return (retControl (ControlLeave (<= SubRoutine) 0 undef))
97 op0 "yield"   = const $ op1Yield (retControl (ControlLeave (<= SubRoutine) 0 undef))
98 op0 "leave"   = const $ retControl (ControlLeave (>= SubBlock) 0 undef)
99 op0 "take"    = const $ assertFrame FrameGather retEmpty
100 op0 "nothing" = const . return $ VBool True
101 op0 "Pugs::Safe::safe_getc"     = const . op1Getc $ VHandle stdin
102 op0 "Pugs::Safe::safe_readline" = const . op1Readline $ VHandle stdin
103 op0 "reverse" = const $ return (VList [])
104 op0 "chomp"   = const $ return (VList [])
105 op0 "fork"    = const $ opPerl5 "fork" []
106 op0 "defer"   = const $ do
107     env <- ask
108     if envAtomic env then guardSTM retry else fail "Cannot call &defer outside a contend block."
109 op0 other = const $ fail ("Unimplemented listOp: " ++ other)
110
111 -- |Implementation of unary primitive operators and functions
112 op1 :: String -> Val -> Eval Val
113 op1 "!"    = op1Cast (VBool . not)
114 op1 "WHICH" = \x -> do
115     val <- fromVal x
116     return $ case val of
117         VObject o   -> castV . unObjectId $ objId o
118         _           -> val
119 op1 "chop" = \x -> do
120     str <- fromVal x
121     return $ if null str
122         then VStr str
123         else VStr $ init str
124 op1 "Scalar::chomp" = \x -> do
125     str <- fromVal x
126     return $ op1Chomp str
127 op1 "Str::split" = op1Cast (castV . words)
128 op1 "lc"         = op1Cast (VStr . map toLower)
129 op1 "lcfirst"    = op1StrFirst toLower
130 op1 "uc"         = op1Cast (VStr . map toUpper)
131 op1 "ucfirst"    = op1StrFirst toUpper
132 op1 "capitalize" = op1Cast $ VStr . (mapEachWord capitalizeWord)
133   where
134     mapEachWord _ [] = []
135     mapEachWord f str@(c:cs)
136         | isSpace c = c:(mapEachWord f cs)
137         | otherwise = f word ++ mapEachWord f rest
138           where (word,rest) = break isSpace str
139     capitalizeWord []     = []
140     capitalizeWord (c:cs) = toUpper c:(map toLower cs)
141 op1 "quotemeta" = op1Cast (VStr . concat . map toQuoteMeta)
142 op1 "undef" = const $ return undef
143 op1 "undefine" = \x -> do
144     when (defined x) $ do
145         ref <- fromVal x
146         clearRef ref
147     return undef
148 op1 "+"    = op1Numeric id
149 op1 "abs"  = op1Numeric abs
150 op1 "Pugs::Internals::truncate" = op1Round truncate
151 op1 "Pugs::Internals::round"    = op1Round round
152 op1 "Pugs::Internals::floor"    = op1Round floor
153 op1 "Pugs::Internals::ceiling"  = op1Round ceiling
154 op1 "cos"  = op1Floating cos
155 op1 "sin"  = op1Floating sin
156 op1 "tan"  = op1Floating tan
157 op1 "sqrt" = op1Floating sqrt
158 op1 "atan" = op1Floating atan
159 op1 "post:i" = \x -> do
160     n <- fromVal x
161     return $ VComplex (0 :+ n)
162 op1 "post:++" = \x -> atomicEval $ do
163     ref <- fromVal x
164     val <- fromVal x
165     val' <- case val of
166         (VStr str)  -> return (VStr $ strInc str)
167         _           -> op1Numeric (+1) val
168     writeRef ref val'
169     case val of
170         (VStr _)    -> return val
171         _           -> op1 "+" val
172 op1 "++"   = \mv -> do
173     op1 "post:++" mv
174     fromVal mv
175 op1 "post:--"   = \x -> atomicEval $ do
176     ref <- fromVal x
177     val <- fromVal x
178     writeRef ref =<< op1Numeric (\x -> x - 1) val
179     return val
180 op1 "--"   = \mv -> do
181     op1 "post:--" mv
182     fromVal mv
183 op1 "-"    = op1Numeric negate
184 op1 "item" = \v -> return $ case v of
185     VList vs    -> VRef . arrayRef $ vs
186     _           -> v
187 op1 "sort" = \v -> do
188     args    <- fromVal v
189     (valList, sortByGiven) <- case args of
190         (v:vs) -> do
191             ifValTypeIsa v "Code"
192                 (return (vs, Just v))
193                 (ifValTypeIsa (last args) "Code"
194                     (return (init args, Just $ last args))
195                     (return (args, Nothing)))
196         _  -> return (args, Nothing)
197     sortBy <- case sortByGiven of
198         Nothing -> readVar (cast "&*infix:cmp")
199         Just subVal -> return subVal
200     sub <- fromVal sortBy
201     sorted <- (`sortByM` valList) $ \v1 v2 -> do
202         rv  <- enterEvalContext (cxtItem "Int") $ App (Val sub) Nothing [Val v1, Val v2]
203         int <- fromVal rv
204         return (int <= (0 :: Int))
205     retSeq sorted
206 op1 "Scalar::reverse" = \v -> do
207     str     <- fromVal v
208     return (VStr $ reverse str)
209 op1 "List::reverse" = \v -> do
210     vlist <- fromVal v
211     return (VList $ reverse vlist)
212 op1 "list" = op1Cast VList
213 op1 "pair" = op1Cast $ VList . (map $ \(k, v) -> castV ((VStr k, v) :: VPair))
214 op1 "~"    = op1Cast VStr
215 op1 "?"    = op1Cast VBool
216 op1 "int"  = op1Cast VInt
217 op1 "+^"   = op1Cast (VInt . pred . negate) -- Arbitrary precision complement- 0 ==> -1 / 1 ==> -2
218 op1 "~^"   = op1Cast (VStr . mapStr complement)
219 op1 "?^"   = op1 "!"
220 op1 "\\"   = \v -> do
221     return $ case v of
222         (VRef (MkRef (IScalar _))) -> VRef . scalarRef $ v
223         (VRef _)    -> v
224         (VList vs)  -> VRef . arrayRef $ vs
225         _           -> VRef . scalarRef $ v
226 op1 "^" = op2RangeExclRight (VNum 0)
227 op1 "post:..."  = op1Range
228 op1 "not"  = op1 "!"
229 op1 "true" = op1 "?"
230 op1 "any"  = op1Cast opJuncAny
231 op1 "all"  = op1Cast opJuncAll
232 op1 "one"  = op1Cast opJuncOne
233 op1 "none" = op1Cast opJuncNone
234 op1 "perl" = op1Pretty $ MkPrettyPrinter pretty
235 op1 "guts" = op1Pretty $ MkPrettyPrinter priggy
236 op1 "yaml" = dumpYaml
237 op1 "require_haskell" = \v -> do
238     name    <- fromVal v
239     externRequire "Haskell" name
240     return $ VBool True
241 op1 "require_parrot" = \v -> do
242     name    <- fromVal v
243     io $ evalParrotFile name
244     return $ VBool True
245 op1 "require_perl5" = \v -> do
246     pkg     <- fromVal v
247     let requireLine = "require " ++ pkg ++ "; '" ++ pkg ++ "'"
248     val     <- evalPerl5WithCurrentEnv requireLine
249     evalExp (_Sym SOur (':':'*':pkg) mempty (Val val) (newMetaType pkg))
250     return val
251 op1 "require_java" = \v -> do
252     pkg     <- fromVal v
253     let requireLine = "package main; use Inline (qw( Java STUDY AUTOSTUDY 1 STUDY ), ['" ++ mod ++ "']); '" ++ pkg ++ "'"
254         lastPart    = last (split "::" pkg)
255         mod         = concat (intersperse "." (split "::" pkg))
256     val     <- evalPerl5WithCurrentEnv requireLine
257     evalExp (_Sym SOur (':':'*':pkg) mempty (Val val) (newMetaType pkg))
258     when (lastPart /= pkg) $ do
259         evalExp_ (_Sym SOur (':':'*':lastPart) mempty (Val val) (newMetaType lastPart))
260     return val
261 op1 "Pugs::Internals::eval_parrot" = \v -> do
262     code    <- fromVal v
263     io . evalParrot $ case code of
264         ('.':_) -> code
265         _       -> unlines
266             [ ".sub pugs_eval_parrot"
267             -- , "trace 1"
268             , code
269             , ".end"
270             ]
271     return $ VBool True
272
273 -- XXX - revert these two to Prelude.pm's ::Disabled version once YAML+Closure is working
274 op1 "use" = opRequire True
275 op1 "require" = opRequire False
276
277 op1 "Pugs::Internals::use" = opRequire True
278 op1 "Pugs::Internals::require" = opRequire False
279 op1 "Pugs::Internals::eval_perl6" = \v -> do
280     str <- fromVal v
281     opEval quiet "<eval>" (encodeUTF8 str)
282     where quiet = MkEvalStyle { evalResult = EvalResultLastValue
283                               , evalError  = EvalErrorUndef }
284 op1 "evalfile" = \v -> do
285     filename <- fromVal v
286     opEvalFile filename
287 op1 "Pugs::Internals::eval_perl5" = \v -> do
288     str     <- fromVal v
289     env     <- ask
290     lex     <- asks envLexical
291     let vars = [ v | v@MkVar{ v_sigil = SScalar, v_twigil = TNil } <- Set.toList (padKeys lex), v /= varTopic ]
292         code = "sub { " ++ codeSafe ++ codeVar ++ str ++ "\n}"
293         codeSafe | safeMode  = "use ops (':default', 'binmode', 'entereval');"
294                  | otherwise = ""
295         codeVar | null vars = ""
296                 | otherwise = "my (" ++ (concat $ intersperse ", " (map cast vars)) ++ ") = @_;"
297     vals    <- mapM readVar vars
298     rv  <- tryIO (Perl5ErrorString "") $ do
299         envSV   <- mkEnv env
300         sub     <- evalPerl5 code envSV 0
301         args    <- mapM newSVval vals
302         invokePerl5 sub nullSV args envSV (enumCxt $ envContext env)
303     case rv of
304         Perl5ReturnValues [x]   -> io $ svToVal x
305         Perl5ReturnValues xs    -> io $ fmap VList (mapM svToVal xs)
306         Perl5ErrorString str    -> fail str
307         Perl5ErrorObject err    -> throwError (PerlSV err)
308 op1 "Pugs::Internals::evalfile_p6y" = op1EvalFileP6Y
309 op1 "Pugs::Internals::eval_p6y"     = op1EvalP6Y
310 op1 "Pugs::Internals::eval_haskell" = op1EvalHaskell
311 op1 "Pugs::Internals::eval_yaml" = evalYaml
312 op1 "contend" = \v -> do
313     env <- ask
314     guardSTM . runEvalSTM env . evalExp $ App (Val v) Nothing []
315 op1 "try" = \v -> do
316     sub <- fromVal v
317     env <- ask
318     val <- tryT $ case envAtomic env of
319         True    -> guardSTM . runEvalSTM env . evalExp $ App (Val $ VCode sub) Nothing []
320         False   -> guardIO . runEvalIO env . evalExp $ App (Val $ VCode sub) Nothing []
321     retEvalResult style val
322     where
323     style = MkEvalStyle
324         { evalResult = EvalResultLastValue
325         , evalError  = EvalErrorUndef
326         }
327 -- Tentative implementation of nothingsmuch's lazy proposal.
328 op1 "lazy" = \v -> do
329     sub     <- fromVal v
330     memo    <- io $ newTVarIO Nothing
331     let exp = App (Val $ VCode sub) Nothing []
332         thunk = do
333             cur <- stm $ readTVar memo
334             maybe eval return cur
335         eval = do
336             res <- evalExp exp
337             stm $ writeTVar memo (Just res)
338             return res
339     typ <- inferExpType exp
340     return . VRef . thunkRef $ MkThunk thunk typ
341
342 op1 "defined" = op1Cast (VBool . defined)
343 op1 "last" = const $ assertFrame FrameLoop $ op1ShiftOut (VControl (ControlLoop LoopLast))
344 op1 "next" = const $ assertFrame FrameLoop $ op1ShiftOut (VControl (ControlLoop LoopNext))
345 op1 "redo" = const $ assertFrame FrameLoop $ op1ShiftOut (VControl (ControlLoop LoopRedo))
346 op1 "continue" = const $ assertFrame FrameWhen $ op1ShiftOut (VControl (ControlWhen WhenContinue))
347 op1 "break" = const $ assertFrame FrameWhen $ op1ShiftOut (VControl (ControlWhen WhenBreak))
348 op1 "return" = op1Return . op1ShiftOut . VControl . ControlLeave (<= SubRoutine) 0
349 op1 "yield" = op1Yield . op1ShiftOut . VControl . ControlLeave (<= SubRoutine) 0
350 op1 "leave" = op1ShiftOut . VControl . ControlLeave (>= SubBlock) 0
351 op1 "take" = \v -> assertFrame FrameGather $ do
352     glob    <- askGlobal
353     arr     <- findSymRef (cast "$*TAKE") glob
354     push    <- doArray (VRef arr) array_push
355     push (listVal v)
356     retEmpty
357 op1 "sign" = \v -> withDefined [v] $
358     op1Cast (VInt . signum) v
359
360 op1 "srand" = \v -> do
361     x <- fromVal v
362     guardSTM . unsafeIOToSTM $ do
363        seed <- if defined v
364           then return x
365           else randomRIO (0, 2^(31::Int))
366        setStdGen $ mkStdGen seed
367     return (castV True)
368 op1 "rand"  = \v -> do
369     x    <- fromVal v
370     rand <- guardSTM . unsafeIOToSTM
371                $ getStdRandom (randomR (0, if x == 0 then 1 else x))
372     return $ VNum rand
373 op1 "say" = op2 "IO::say" (VHandle stdout)
374 op1 "print" = op2 "IO::print" (VHandle stdout)
375 op1 "IO::say" = \v -> op2 "IO::say" v $ VList []
376 op1 "IO::print" = \v -> op2 "IO::print" v $ VList []
377 op1 "IO::next" = \v -> do
378     fh  <- fromVal v
379     guardIO $ fmap (VStr . (++ "\n") . decodeUTF8) (hGetLine fh)
380 op1 "Pugs::Safe::safe_print" = \v -> do
381     str  <- fromVal v
382     guardIO . putStr $ encodeUTF8 str
383     return $ VBool True
384 op1 "die" = \v -> do
385     v'      <- fromVal $! v
386     poss    <- asks envPosStack
387     retShift $! VError (errmsg $! v') poss
388     where
389     errmsg VUndef      = VStr "Died"
390     errmsg VType{}     = VStr "Died"
391     errmsg (VStr "")   = VStr "Died"
392     errmsg (VList [])  = VStr "Died"
393     errmsg (VList [x]) = x
394     errmsg x           = x
395 op1 "warn" = \v -> do
396     strs <- fromVal v
397     errh <- readVar $ cast "$*ERR"
398     poss    <- asks envPosStack
399     op2 "IO::say" errh $ VList [ VStr $ pretty (VError (errmsg strs) poss) ]
400     where
401     errmsg "" = VStr "Warning: something's wrong"
402     errmsg x  = VStr x
403 op1 "fail" = op1 "fail_" -- XXX - to be replaced by Prelude later
404 op1 "fail_" = \v -> do
405     throw <- fromVal =<< readVar (cast "$*FAIL_SHOULD_DIE")
406     if throw then op1 "die" (errmsg v) else do
407     poss    <- asks envPosStack
408     let die = retShift $ VError (errmsg v) poss
409         dieThunk = VRef . thunkRef $ MkThunk die (mkType "Failure")
410     op1Return (retControl (ControlLeave (<= SubRoutine) 0 dieThunk))
411     where
412     errmsg VUndef      = VStr "Failed"
413     errmsg VType{}     = VStr "Failed"
414     errmsg (VStr "")   = VStr "Failed"
415     errmsg (VList [])  = VStr "Failed"
416     errmsg (VList [x]) = x
417     errmsg x           = x
418 op1 "exit" = op1Exit
419 op1 "readlink" = \v -> do
420     str  <- fromVal v
421     guardIO $ fmap VStr (readSymbolicLink str)
422 op1 "sleep" = \v -> do
423     x <- fromVal v :: Eval VNum
424     guardIO $ do
425         start   <- getCurrentTime
426         threadDelay (round $ x * clocksPerSecond)
427         finish  <- getCurrentTime
428         return $ VRat (toRational $ diffUTCTime start finish)
429 op1 "mkdir" = guardedIO createDirectory
430 op1 "rmdir" = guardedIO removeDirectory
431 op1 "chdir" = guardedIO setCurrentDirectory
432 op1 "graphs"= op1Cast (VInt . (genericLength :: String -> VInt)) -- XXX Wrong
433 op1 "codes" = op1Cast (VInt . (genericLength :: String -> VInt))
434 op1 "chars" = op1Cast (VInt . (genericLength :: String -> VInt))
435 op1 "bytes" = op1Cast (VInt . (genericLength :: String -> VInt) . encodeUTF8)
436
437 op1 "unlink" = \v -> do
438     vals <- fromVals v
439     rets <- mapM (doBoolIO removeFile) vals
440     return $ VInt $ sum $ map bool2n rets
441 op1 "readdir" = \v -> do
442     path  <- fromVal v
443     files <- guardIO $ getDirectoryContents path
444     retSeq (map VStr files)
445 op1 "slurp" = \v -> do
446     ifValTypeIsa v "IO"
447         (do h <- fromVal v
448             ifListContext (strictify $! op1 "=" v) $ do
449                 content <- guardIO $ hGetContents h
450                 return . VStr $ decodeUTF8 content)
451         (do
452             fileName    <- fromVal v
453             ifListContext
454                 (slurpList fileName)
455                 (slurpScalar fileName))
456     where
457     strictify action = do
458         VList lines <- action
459         return $ VList (length lines `seq` lines)
460     slurpList file = strictify $! op1 "=" (VList [VStr file])
461     slurpScalar file = do
462         content <- guardIO $ readFile file
463         return . VStr $ decodeUTF8 content
464 op1 "opendir" = \v -> do
465     str <- fromVal v
466     dir <- guardIO $ openDirStream str
467     obj <- createObject (mkType "IO::Dir") []
468     return . VObject $ obj{ objOpaque = Just $ toDyn dir }
469 op1 "IO::Dir::close" = op1 "IO::Dir::closedir"
470 op1 "IO::Dir::closedir" = guardedIO (closeDirStream . fromObject)
471 op1 "IO::Dir::rewind" = op1 "IO::Dir::rewinddir"
472 op1 "IO::Dir::rewinddir" = guardedIO (rewindDirStream . fromObject)
473 op1 "IO::Dir::read" = op1 "IO::Dir::readdir"
474 op1 "IO::Dir::readdir" = \v -> do
475     dir <- fmap fromObject (fromVal v)
476     ifListContext
477         (retSeq =<< readDirStreamList dir)
478         (guardIO $ fmap (\x -> if null x then undef else castV x) $ readDirStream dir)
479     where
480     readDirStreamList dir = do
481         this <- tryIO "" $ readDirStream dir
482         if null this then return [] else do
483         rest <- readDirStreamList dir
484         return (VStr this:rest)
485 op1 "Pugs::Internals::runShellCommand" = \v -> do
486     str <- fromVal v
487     cxt <- asks envContext
488     (res, exitCode) <- tryIO ("", ExitFailure (-1)) $ do
489         (inp,out,_,pid) <- runInteractiveCommand (encodeUTF8 str)
490         hClose inp
491         res             <- fmap (decodeUTF8 . deCRLF) $ hGetContents out
492         exitCode        <- waitForProcess pid
493         return (res, exitCode)
494     handleExitCode exitCode
495     return $ case cxt of
496         CxtSlurpy{} -> VList (map VStr $ lines res)
497         _           -> VStr res
498     where
499     -- XXX - crude CRLF treatment
500     deCRLF []                   = []
501     deCRLF ('\r':xs@('\n':_))   = xs
502     deCRLF (x:xs)               = (x:deCRLF xs)
503 op1 "Pugs::Internals::runInteractiveCommand" = \v -> do
504     str <- fromVal v
505     guardIO $ do
506         (inp,out,err,pid) <- runInteractiveCommand str
507         return $ VList [ VHandle inp
508                        , VHandle out
509                        , VHandle err
510                        , VProcess (MkProcess pid)
511                        ]
512 op1 "Pugs::Internals::check_for_io_leak" = \v -> do
513     rv      <- evalExp (App (Val v) Nothing [])
514     leaked  <- fromVal =<< op2Match rv (VType $ mkType "IO")
515     when leaked $ do
516         fail $ "BEGIN and CHECK blocks may not return IO handles,\n" ++
517                "as they would be invalid at runtime."
518     return rv
519 op1 "system" = \v -> do
520     cmd         <- fromVal v
521     exitCode    <- tryIO (ExitFailure (-1)) $ system (encodeUTF8 cmd)
522     handleExitCode exitCode
523 op1 "accept" = \v -> do
524     socket      <- fromVal v
525     (h, _, _)   <- guardIO $ accept socket
526     return $ VHandle h
527 op1 "detach" = \v -> do
528     case v of
529         VThread thr -> do
530             stm $ tryPutTMVar (threadLock thr) undef
531             return $ VBool True
532         _           -> fail $ "Not a thread: " ++ show v
533 op1 "kill" = \v -> do
534     case v of
535         VThread thr -> do
536             guardIO . killThread $ threadId thr
537             return $ VBool True
538         _           -> fail $ "Not a thread: " ++ show v
539 op1 "join" = \v -> do
540     case v of
541         VThread thr -> stm $ takeTMVar (threadLock thr)
542         _           -> op2Join v (VList [])
543 op1 "async" = \v -> do
544     env     <- ask
545     code    <- fromVal v
546     lock    <- stm $ newEmptyTMVar
547     tid     <- guardIO . forkIO $ do -- (if rtsSupportsBoundThreads then forkOS else forkIO) $ do
548         val <- runEvalIO env $ do
549             enterEvalContext CxtVoid $ App (Val code) Nothing []
550         stm $ tryPutTMVar lock val
551         return ()
552     return . VThread $ MkThread
553         { threadId      = tid
554         , threadLock    = lock
555         }
556 --WV: async should return the thread id!    return undef
557 op1 "listen" = \v -> do
558     port    <- fromVal v
559     socket  <- guardIO $ listenOn (PortNumber $ fromInteger port)
560     return $ VSocket socket
561 op1 "flush" = guardedIO hFlush
562 op1 "IO::close" = guardedIO hClose
563 op1 "Socket::close" = guardedIO sClose
564 op1 "Pair::key" = fmap fst . (fromVal :: Val -> Eval VPair)
565 op1 "Pair::value" = \v -> do
566     ivar <- join $ doPair v pair_fetchElem
567     return . VRef . MkRef $ ivar
568 op1 "pairs" = \v -> do
569     pairs <- pairsFromVal v
570     retSeq pairs
571 op1 "List::kv" = \v -> do
572     pairs <- pairsFromVal v
573     kvs   <- forM pairs $ \(VRef ref) -> do
574         pair   <- readRef ref
575         fromVal pair
576     retSeq $ concat kvs
577 op1 "Pair::kv" = op1 "List::kv"
578 op1 "keys" = keysFromVal
579 op1 "values" = valuesFromVal
580 -- According to Damian
581 -- (http://www.nntp.perl.org/group/perl.perl6.language/21895),
582 -- =$obj should call $obj.next().
583 op1 "="        = \v -> case v of
584     VObject _               -> evalExp $ App (_Var "&shift") (Just $ Val v) []
585     VRef (MkRef IArray{})   -> do
586         ifListContext
587             (fmap VList (join $ doArray v array_fetch))
588             (join $ doArray v array_shift)
589     _           -> op1 "readline" v
590 op1 "readline" = op1Readline
591 op1 "getc"     = op1Getc
592 op1 "WHAT"     = fmap VType . evalValType
593 op1 "List::end"   = \x -> fmap (castV . pred) (join $ doArray x array_fetchSize) -- monadic join
594 op1 "List::elems" = \x -> fmap castV (join $ doArray x array_fetchSize) -- monadic join
595 op1 "List::pop"   = \x -> join $ doArray x array_pop -- monadic join
596 op1 "List::shift" = \x -> join $ doArray x array_shift -- monadic join
597 op1 "pick"  = op1Pick
598 op1 "sum"   = op1Sum
599 op1 "min"   = op1Min
600 op1 "max"   = op1Max
601 op1 "uniq"  = op1Uniq
602 op1 "chr"   = op1Cast (VStr . (:[]) . chr)
603 op1 "ord"   = op1Cast $ \str -> if null str then undef else (castV . ord . head) str
604 op1 "hex"   = fail "hex() is not part of Perl 6 - use :16() instead."
605 op1 "oct"   = fail "oct() is not part of Perl 6 - use :8() instead."
606 op1 "log"   = op1Floating log
607 op1 "log10" = op1Floating (logBase 10)
608 op1 "from"  = op1Cast (castV . matchFrom)
609 op1 "to"    = op1Cast (castV . matchTo)
610 op1 "matches" = op1Cast (VList . matchSubPos)
611 op1 "gather" = \v -> do
612     evl <- asks envEval
613     evl (Syn "gather" [Val v])
614 op1 "Thread::yield" = const $ do
615     guardSTM . unsafeIOToSTM $ yield
616     return $ VBool True
617 op1 "DESTROYALL" = \x -> cascadeMethod id "DESTROY" x VUndef
618 -- [,] is a noop -- It simply returns the input list
619 op1 "prefix:[,]" = return
620 op1 "prefix:$<<" = op1SigilHyper SScalar
621 op1 "prefix:@<<" = op1SigilHyper SArray
622 op1 "prefix:%<<" = op1SigilHyper SHash
623 op1 "prefix:&<<" = op1SigilHyper SCode
624 op1 "Code::assoc" = op1CodeAssoc
625 op1 "Code::name"  = op1CodeName
626 op1 "Code::arity" = op1CodeArity
627 op1 "Code::body"  = op1CodeBody
628 op1 "Code::pos"   = op1CodePos
629 op1 "Code::signature" = op1CodeSignature
630 op1 "IO::tell"    = \v -> do
631     h <- fromVal v
632     res <- guardIO $ hTell h
633     return $ VInt res
634 op1 "Rat::numerator" = \(VRat t) -> return . VInt $ numerator t
635 op1 "Rat::denominator" = \(VRat t) -> return . VInt $ denominator t
636 op1 "TEMP" = \v -> do
637     ref <- fromVal v
638     val <- readRef ref
639     return . VCode $ mkPrim
640         { subBody = Prim . const $ do
641             writeRef ref val
642             retEmpty
643         }
644 op1 "Pugs::Internals::hIsOpen" = op1IO hIsOpen
645 op1 "Pugs::Internals::hIsClosed" = op1IO hIsClosed
646 op1 "Pugs::Internals::hIsReadable" = op1IO hIsReadable
647 op1 "Pugs::Internals::hIsWritable" = op1IO hIsWritable
648 op1 "Pugs::Internals::hIsSeekable" = op1IO hIsSeekable
649 op1 "Pugs::Internals::reduceVar" = \v -> do
650     str <- fromVal v
651     evalExp (_Var str)
652 op1 "Pugs::Internals::rule_pattern" = \v -> do
653     case v of
654         VRule MkRulePGE{rxRule=re} -> return $ VStr re
655         VRule MkRulePCRE{rxRuleStr=re} -> return $ VStr re
656         _ -> fail $ "Not a rule: " ++ show v
657 op1 "Pugs::Internals::rule_adverbs" = \v -> do
658     case v of
659         VRule MkRulePGE{rxAdverbs=hash} -> return hash
660         VRule MkRulePCRE{rxAdverbs=hash} -> return hash
661         _ -> fail $ "Not a rule: " ++ show v
662 op1 "Pugs::Internals::current_pragma_value" = \v -> do
663     name <- fromVal v
664     prags <- asks envPragmas
665     return $ findPrag name prags
666     where
667         findPrag :: String -> [Pragma] -> Val
668         findPrag _ [] = VUndef
669         findPrag n (this:rest)
670             | n == pragName this = VInt $ toInteger $ pragDat this
671             | otherwise          = findPrag n rest
672 op1 "Pugs::Internals::caller_pragma_value" = \v -> do
673     caller <- asks envCaller
674     case caller of
675         Just env -> local (const env) (op1 "Pugs::Internals::current_pragma_value" v)
676         _        -> return $ VUndef
677 op1 "eager" = \v -> do
678     vlist <- fromVal v
679     return $! VList $! deepSeq vlist vlist
680 op1 "Pugs::Internals::emit_yaml" = \v -> do
681     glob <- filterPrim =<< asks envGlobal
682     yml  <- io $ showYaml (filterUserDefinedPad glob, v)
683     return $ VStr yml
684 op1 "Object::HOW" = \v -> do
685     typ     <- evalValType v
686     evalExp $ _Var (':':'*':showType typ)
687 op1 "Class::name" = \v -> do
688     cls     <- fromVal v
689     meta    <- readRef =<< fromVal cls
690     fetch   <- doHash meta hash_fetchVal
691     str     <- fromVal =<< fetch "name"
692     return str
693 op1 "Class::traits" = \v -> do
694     cls     <- fromVal v
695     meta    <- readRef =<< fromVal cls
696     fetch   <- doHash meta hash_fetchVal
697     str     <- fromVal =<< fetch "is"
698     return str
699 op1 "vv" = op1Cast VV
700 op1 "stat" = \x -> opPerl5 "require File::stat; File::stat::stat" [x]
701 op1 "lstat" = \x -> opPerl5 "require File::stat; File::stat::lstat" [x]
702 op1 "Pugs::Internals::localtime"  = \x -> do
703     tz  <- io getCurrentTimeZone
704     tm  <- fromVal x    -- seconds since Perl's epoch
705     let utc   = posixSecondsToUTCTime (fromInteger tm + offset)
706         local = utcToLocalTime tz utc
707         day   = localDay local
708         tod   = localTimeOfDay local
709         (year, month, dayOfMonth)   = toGregorian day
710         (sec, pico)                 = properFraction $ todSec tod
711         (_, dayOfWeek)              = sundayStartWeek day
712     -- if wantString then return . VStr $ formatTime "%c" (ZonedTime local tz) else
713     retSeq [ vI    $ year
714            , vI    $ month
715            , vI    $ dayOfMonth
716            , vI    $ todHour tod
717            , vI    $ todMin tod
718            , VInt  $ sec
719            , vI    $ fromEnum (pico * 1000000000000)
720            , vI    $ dayOfWeek + 1
721            , vI    $ (monthAndDayToDayOfYear (isLeapYear year) month dayOfMonth) - 1
722            , VStr  $ timeZoneName tz
723            , vI    $ timeZoneMinutes tz * 60
724            , VBool $ timeZoneSummerOnly tz
725            ]
726     where
727     offset :: NominalDiffTime
728     offset = 946684800 -- diff between Haskell and Perl epochs (seconds)
729     vI :: Integral a => a -> Val
730     vI = VInt . toInteger
731
732 op1 other   = \_ -> fail ("Unimplemented unaryOp: " ++ other)
733
734 op1IO :: Value a => (Handle -> IO a) -> Val -> Eval Val
735 op1IO = \fun v -> do
736     val <- fromVal v
737     fmap castV (guardIO $ fun val)
738
739 op1SigilHyper :: VarSigil -> Val -> Eval Val
740 op1SigilHyper sig val = do
741     vs <- fromVal val
742     evalExp $ Syn "," (map (\x -> Syn (shows sig "{}") [Val x]) vs)
743
744 retSeq :: VList -> Eval Val
745 retSeq xs = length xs `seq` return (VList xs)
746
747 handleExitCode :: ExitCode -> Eval Val
748 handleExitCode exitCode = do
749     glob    <- askGlobal
750     errSV   <- findSymRef (cast "$!") glob
751     writeRef errSV $ case exitCode of
752         ExitFailure x   -> VInt $ toInteger x
753         ExitSuccess     -> VUndef
754     return (VBool $ exitCode == ExitSuccess)
755
756 cascadeMethod :: ([VStr] -> [VStr]) -> VStr -> Val -> Val -> Eval Val
757 cascadeMethod f meth v args = do
758     typ     <- evalValType v
759     pkgs    <- fmap f (pkgParents $ showType typ)
760     named   <- case args of
761         VUndef -> return Map.empty
762         VType{}-> return Map.empty
763         _      -> join $ doHash args hash_fetch
764
765     -- Here syms is a list of (sym, tvar) tuples where tvar is the physical coderef
766     -- The monad in the "do" below is List.
767     syms <- forM pkgs $ \pkg -> do
768         let sym = cast $ ('&':pkg) ++ "::" ++ meth
769         maybeM (fmap (lookupPad sym) askGlobal) $ \ref -> do
770             return (sym, ref)
771
772     forM_ (nubBy (\(_, x) (_, y) -> x == y) (catMaybes syms)) $ \(sym, _) -> do
773         enterEvalContext CxtVoid $
774             App (Var sym) (Just $ Val v)
775                 [ Syn "named" [Val (VStr key), Val val]
776                 | (key, val) <- Map.assocs named
777                 ]
778     return undef
779
780 op1Return :: Eval Val -> Eval Val
781 op1Return action = assertFrame FrameRoutine $ do
782     sub   <- fromVal =<< readVar (cast "&?ROUTINE")
783     -- If this is a coroutine, reset the entry point
784     case subCont sub of
785         Nothing -> action
786         Just tvar -> do
787             let thunk = (`MkThunk` anyType) . fix $ \redo -> do
788                 evalExp $ subBody sub
789                 stm $ writeTVar tvar thunk
790                 redo
791             stm $ writeTVar tvar thunk
792             action
793
794 op1Yield :: Eval Val -> Eval Val
795 op1Yield action = assertFrame FrameRoutine $ do
796     sub   <- fromVal =<< readVar (cast "&?ROUTINE")
797     case subCont sub of
798         Nothing -> fail $ "cannot yield() from a " ++ pretty (subType sub)
799         Just tvar -> callCC $ \esc -> do
800             stm $ writeTVar tvar (MkThunk (esc undef) anyType)
801             action
802
803 op1ShiftOut :: Val -> Eval Val
804 op1ShiftOut v = retShift =<< do
805     evl <- asks envEval
806     evl $ case v of
807         VList [x]   -> Val x
808         _           -> Val v
809
810 op1Exit :: Val -> Eval a
811 op1Exit v = do
812     rv <- fromVal v
813     retControl . ControlExit $ if rv /= 0
814         then ExitFailure rv else ExitSuccess
815
816 op1StrFirst :: (Char -> Char) -> Val -> Eval Val
817 op1StrFirst f = op1Cast $ VStr .
818     \str -> case str of
819         []      -> []
820         (c:cs)  -> (f c:cs)
821
822 -- op1Readline and op1Getc are precisely the implementation of op1 "readline"
823 -- and op1 "getc", but those may be hidden in safe mode. We still want to use
824 -- the functionality with the safe variants, hence these functions.
825 op1Readline :: Val -> Eval Val
826 op1Readline = \v -> op1Read v (io . getLines) getLine
827     where
828     getLines :: VHandle -> IO Val
829     getLines fh = unsafeInterleaveIO $ do
830         line <- doGetLine fh
831         case line of
832             Just str -> do
833                 ~(VList rest) <- getLines fh
834                 return $ VList (VStr str:rest)
835             _ -> return (VList [])
836     getLine :: VHandle -> Eval Val
837     getLine fh = do
838         line <- io $! doGetLine fh
839         case line of
840             Just str    -> return $! VStr $! (length str `seq` str)
841             _           -> return undef
842     doGetLine :: VHandle -> IO (Maybe VStr)
843     doGetLine fh = guardIOexcept [(isIOError isEOFError, Nothing)] $ do
844         line <- hGetLine fh
845         return . Just . decodeUTF8 $ line
846
847 isIOError :: (IOError -> Bool) -> Exception -> Bool
848 isIOError f err = case ioErrors err of
849     Just ioe    -> f ioe
850     Nothing     -> False
851
852 op1Getc :: Val -> Eval Val
853 op1Getc = \v -> op1Read v (getChar) (getChar)
854     where
855     getChar :: VHandle -> Eval Val
856     getChar fh = guardIOexcept [(isIOError isEOFError, undef)] $ do
857         char <- hGetChar fh
858         str  <- getChar' fh char
859         return $ VStr $ decodeUTF8 str
860     -- We may have to read more than one byte, as one utf-8 char can span
861     -- multiple bytes.
862     getChar' :: VHandle -> Char -> IO String
863     getChar' fh char
864         | ord char < 0x80 = return [char]
865         | ord char < 0xE0 = readNmore 1
866         | ord char < 0xEE = readNmore 2
867         | ord char < 0xF5 = readNmore 3
868         | otherwise       = fail "Invalid utf-8 read by getc()"
869         where
870         readNmore :: Int -> IO String
871         readNmore n = do
872             new <- sequence $ replicate n (hGetChar fh)
873             return $ char:new
874
875 {-|
876 Read a char or a line from a handle.
877 -}
878 op1Read :: Val                   -- ^ The handle to read from (packed in a 'Val')
879         -> (VHandle -> Eval Val) -- ^ The function to call in list context
880         -> (VHandle -> Eval Val) -- ^ The function to call in item context
881         -> Eval Val              -- ^ The return value (a list of strings or a
882                                  --   string, packed in a 'Val')
883 op1Read v fList fScalar = do
884     fh  <- handleOf v
885     ifListContext
886         (fList fh)
887         (fScalar fh)
888     where
889     handleOf x | safeMode, (VHandle h) <- x, h /= stdin = fail "Evil handle detected"
890     handleOf _ | safeMode = return stdin
891     handleOf VUndef = handleOf (VList [])
892     handleOf (VList []) = do
893         argsGV  <- readVar (cast "$*ARGS")
894         gv      <- fromVal argsGV
895         if defined gv
896             then handleOf gv
897             else do
898                 args    <- readVar (cast "@*ARGS")
899                 files   <- fromVal args
900                 if null files
901                     then return stdin
902                     else do
903                         hdl <- handleOf (VStr (head files)) -- XXX wrong
904                         writeVar (cast "$*ARGS") (VHandle hdl)
905                         return hdl
906     handleOf (VStr x) = do
907         return =<< guardIO $ openFile x ReadMode
908     handleOf (VList [x]) = handleOf x
909     handleOf v = fromVal v
910
911 bool2n :: Bool -> VInt
912 bool2n v = if v
913   then 1
914   else 0
915
916 doBoolIO :: Value a => (a -> IO b) -> Val -> Eval Bool
917 doBoolIO f v = do
918     x <- fromVal v
919     tryIO False $ do
920         f x
921         return True
922
923 guardedIO :: Value a => (a -> IO b) -> Val -> Eval Val
924 guardedIO f v = do
925     x <- fromVal v
926     guardIO $ f x
927     return $ VBool True
928
929 guardedIO2 :: (Value a, Value b)
930     => (a -> b -> IO c) -> Val -> Val -> Eval Val
931 guardedIO2 f u v = do
932     x <- fromVal u
933     y <- fromVal v
934     guardIO $ f x y
935     return $ VBool True
936
937 mapStr :: (Word8 -> Word8) -> [Word8] -> String
938 mapStr f = map (chr . fromEnum . f)
939
940 mapStr2 :: (Word8 -> Word8 -> Word8) -> [Word8] -> [Word8] -> String
941 mapStr2 f x y = map (chr . fromEnum . uncurry f) $ x `zip` y
942
943 mapStr2Fill :: (Word8 -> Word8 -> Word8) -> [Word8] -> [Word8] -> String
944 mapStr2Fill f x y = map (chr . fromEnum . uncurry f) $ x `zipFill` y
945     where
946     zipFill [] [] = []
947     zipFill as [] = zip as (repeat 0)
948     zipFill [] bs = zip (repeat 0) bs
949     zipFill (a:as) (b:bs) = (a,b) : zipFill as bs
950
951 op1Chomp :: VStr -> Val
952 op1Chomp "" = VStr ""
953 op1Chomp str
954     | last str == '\n'  = VStr (init str)
955     | otherwise         = VStr str
956
957 perlReplicate :: VInt -> a -> [a]
958 perlReplicate = genericReplicate . max 0
959
960 -- XXX only used at    op2 "?^"   because my Haskell is too poor - ferreira
961 neBool :: VBool -> VBool -> VBool
962 neBool = (==) . not
963
964 -- |Implementation of 2-arity primitive operators and functions
965 op2 :: String -> Val -> Val -> Eval Val
966 op2 "rename" = guardedIO2 rename
967 op2 "symlink" = guardedIO2 createSymbolicLink
968 op2 "link" = guardedIO2 createLink
969 op2 "*"  = op2Numeric (*)
970 op2 "/"  = op2Divide
971 op2 "%"  = op2Modulus
972 op2 "x"  = op2Cast (\x y -> VStr . concat $ (y :: VInt) `perlReplicate` x)
973 op2 "xx" = op2Cast (\x y -> VList . concat $ (y :: VInt) `perlReplicate` x)
974 op2 "+&" = op2Int (.&.)
975 op2 "+<" = op2Int shiftL
976 op2 "+>" = op2Int shiftR
977 op2 "~&" = op2Str $ mapStr2 (.&.)
978 op2 "~<" = op2Cast (\x y -> VStr $ mapStr (`shiftL` y) x)
979 op2 "~>" = op2Cast (\x y -> VStr $ mapStr (`shiftR` y) x)
980 op2 "**" = op2Exp
981 op2 "+"  = op2Numeric (+)
982 op2 "-"  = op2Numeric (-)
983 op2 "atan" = op2Num atan2
984 op2 "~"  = op2Str (++)
985 op2 "+|" = op2Int (.|.)
986 op2 "+^" = op2Int xor
987 op2 "~|" = op2Str $ mapStr2Fill (.|.)
988 op2 "?|" = op2Bool (||)
989 op2 "?&" = op2Bool (&&)
990 op2 "~^" = op2Str $ mapStr2Fill xor
991 op2 "?^" = op2Bool neBool -- for bools, 'xor' is the same as '!=='
992 op2 "=>" = \x y -> return $ castV (x, y)
993 op2 "="  = \x y -> evalExp (Syn "=" [Val x, Val y])
994 op2 "cmp"= op2OrdNumStr
995 op2 "leg"= op2Ord vCastStr
996 op2 "<=>"= op2OrdNumeric compare
997 op2 ".." = op2Range
998 op2 "..^" = op2RangeExclRight
999 op2 "^.." = op2RangeExclLeft
1000 op2 "^..^" = op2RangeExclBoth
1001 op2 "!=" = op2OrdNumeric (/=)
1002 op2 "==" = op2OrdNumeric (==)
1003 op2 "<"  = op2OrdNumeric (<)
1004 op2 "<=" = op2OrdNumeric (<=)
1005 op2 ">"  = op2OrdNumeric (>)
1006 op2 ">=" = op2OrdNumeric (>=)
1007 op2 "ne" = op2Cmp vCastStr (/=)
1008 op2 "eq" = op2Cmp vCastStr (==)
1009 op2 "lt" = op2Cmp vCastStr (<)
1010 op2 "le" = op2Cmp vCastStr (<=)
1011 op2 "gt" = op2Cmp vCastStr (>)
1012 op2 "ge" = op2Cmp vCastStr (>=)
1013 op2 "~~" = op2Match
1014 op2 "=:=" = \x y -> do
1015     return $ castV $ case x of
1016         VRef xr | VRef yr <- y ->
1017             -- Take advantage of the pointer address built-in with (Show VRef)
1018             show xr == show yr
1019         _   ->
1020             W# (unsafeCoerce# x :: Word#) == W# (unsafeCoerce# y :: Word#)
1021 op2 "===" = \x y -> do
1022     return $ castV (x == y)
1023 op2 "eqv" = op2Identity -- XXX wrong, needs to compare full objects
1024 op2 "&&" = op2Logical (fmap not . fromVal)
1025 op2 "||" = op2Logical (fmap id . fromVal)
1026 op2 "^^" = \x y -> do
1027     let xor True True   = VBool False
1028         xor True False  = x
1029         xor False True  = y
1030         xor False False = VBool False
1031     op2Cast xor x y
1032 op2 "//" = op2Logical (return . defined)
1033 op2 ".[]" = \x y -> do
1034     evl <- asks envEval
1035     evl $ Syn "[]" [Val x, Val y]
1036 op2 ".{}" = \x y -> do
1037     evl <- asks envEval
1038     evl $ Syn "{}" [Val x, Val y]
1039 -- XXX pipe forward XXX
1040 op2 "and"= op2 "&&"
1041 op2 "or" = op2 "||"
1042 op2 "xor"= op2 "^^"
1043 op2 "orelse"= op2 "//"  -- XXX wrong
1044 op2 "andthen"= op2 "&&" -- XXX even wronger
1045 op2 "pick" = op2Pick
1046 op2 "grep" = op2Grep
1047 op2 "first" = op2First
1048 op2 "map"  = op2Map
1049 op2 "join" = op2Join
1050 op2 "reduce" = op2ReduceL False
1051 op2 "produce" = op2ReduceL True
1052 op2 "reverse" = op2MaybeListop (VList . reverse) (VStr . reverse)
1053 op2 "chomp" = op2MaybeListop (VList . map op1Chomp) op1Chomp
1054 op2 "kill" = \s v -> do
1055     sig  <- fromVal s
1056     pids <- fromVals v
1057     sig' <- fromVal sig
1058     pids'<- mapM fromVal pids
1059     let doKill pid = do
1060         signalProcess (toEnum sig') (toEnum pid)
1061         return 1
1062     rets <- mapM (tryIO 0 . doKill) pids'
1063     return . VInt $ sum rets
1064 op2 "isa"    = \x y -> do
1065     typY <- case y of
1066         VStr str -> return $ mkType str
1067         _        -> fromVal y
1068     typX <- fromVal x -- XXX consider line 224 of Pugs.Prim.Match case too
1069     typs <- pkgParentClasses (showType typX)
1070     return . VBool $ showType typY `elem` (showType typX:typs)
1071 op2 "does"   = \x y -> do
1072     typY <- case y of
1073         VStr str -> return $ mkType str
1074         _        -> fromVal y
1075     op2Match x (VType typY)
1076 op2 "delete" = \x y -> do
1077     ref <- fromVal x
1078     rv  <- deleteFromRef ref y
1079     -- S29: delete always returns the full list regardless of context.
1080     return $ case rv of
1081         VList [x]   -> x
1082         _           -> rv
1083 op2 "exists" = \x y -> do
1084     ref <- fromVal x
1085     fmap VBool (existsFromRef ref y)
1086 op2 "unshift" = op2Array array_unshift
1087 op2 "push" = op2Array array_push
1088 op2 "split" = op2Split
1089 op2 "Str::split" = flip op2Split
1090 op2 "connect" = \x y -> do
1091     host <- fromVal x
1092     port <- fromVal y
1093     hdl  <- guardIO $ connectTo host (PortNumber $ fromInteger port)
1094     return $ VHandle hdl
1095 op2 "Pugs::Internals::hSetBinaryMode" = \x y -> do
1096     fh    <- fromVal x
1097     mode  <- fromVal y
1098     guardIO $ hSetBinaryMode fh mode
1099     return $ VBool True
1100 op2 "Pugs::Internals::openFile" = \x y -> do
1101     filename <- fromVal x
1102     mode     <- fromVal y
1103     hdl      <- guardIO $ do
1104         h <- openFile filename (modeOf mode)
1105         hSetBuffering h NoBuffering
1106         return h
1107     return $ VHandle hdl
1108     where
1109     modeOf "r"  = ReadMode
1110     modeOf "w"  = WriteMode
1111     modeOf "a"  = AppendMode
1112     modeOf "rw" = ReadWriteMode
1113     modeOf m    = error $ "unknown mode: " ++ m
1114 op2 "exp" = \x y -> if defined y
1115     then op2Num (**) x y
1116