gdritter repos endsay / a7e3603
Beginnings of a PostScript interpreter Getty Ritter 7 years ago
9 changed file(s) with 398 addition(s) and 0 deletion(s). Collapse all Expand all
1 dist
2 dist-newstyle
3 *~
1 name: endsay
2 version: 0.1.0.0
3 -- synopsis:
4 -- description:
5 license: BSD3
6 license-file: LICENSE
7 author: Getty Ritter <gettylefou@gmail.com>
8 maintainer: Getty Ritter <gettylefou@gmail.com>
9 copyright: ©2016 Getty Ritter
10 -- category:
11 build-type: Simple
12 cabal-version: >= 1.12
13
14 executable endsay
15 hs-source-dirs: src
16 main-is: Main.hs
17 other-modules: Types
18 Parse
19 default-extensions: OverloadedStrings,
20 ScopedTypeVariables
21 ghc-options: -Wall
22 build-depends: base >=4.7 && <4.9
23 , containers
24 , text
25 , transformers
26 , vector
27 , bytestring
28 , unordered-containers
29 , megaparsec
30 , semigroups
31 default-language: Haskell2010
1 module Main where
2
3 import Run
4
5 main :: IO ()
6 main = do
7 let program = "5 dup mul ="
8 runEndsay program run >>= print
1 {-# LANGUAGE RankNTypes #-}
2
3 module PSNum where
4
5 import Types
6
7 data PSNum
8 = PSInt Integer
9 | PSReal Double
10 deriving (Eq, Show)
11
12 toPSNum :: Object -> Endsay PSNum
13 toPSNum (OInteger n) = return (PSInt n)
14 toPSNum (OReal n) = return (PSReal n)
15 toPSNum _ = error ".."
16
17 fromPSNum :: PSNum -> Object
18 fromPSNum (PSInt n) = OInteger n
19 fromPSNum (PSReal n) = OReal n
20
21 promoteNum :: (forall a. Num a => a -> a -> a) -> PSNum -> PSNum -> PSNum
22 promoteNum op (PSInt x) (PSInt y) = PSInt (x `op` y)
23 promoteNum op (PSInt x) (PSReal y) = PSReal (fromIntegral x `op` y)
24 promoteNum op (PSReal x) (PSInt y) = PSReal (x `op` fromIntegral y)
25 promoteNum op (PSReal x) (PSReal y) = PSReal (x `op` y)
26
27 resNum :: (forall a. (Num a, Ord a) => a -> a -> b) -> PSNum -> PSNum -> b
28 resNum op (PSInt x) (PSInt y) = (x `op` y)
29 resNum op (PSInt x) (PSReal y) = (fromIntegral x `op` y)
30 resNum op (PSReal x) (PSInt y) = (x `op` fromIntegral y)
31 resNum op (PSReal x) (PSReal y) = (x `op` y)
32
33 overNum :: (forall a. Num a => a -> a) -> PSNum -> PSNum
34 overNum op (PSInt x) = PSInt (op x)
35 overNum op (PSReal x) = PSReal (op x)
36
37 instance Num PSNum where
38 (+) = promoteNum (+)
39 (-) = promoteNum (-)
40 (*) = promoteNum (*)
41 negate = overNum negate
42 abs = overNum abs
43 signum = overNum signum
44 fromInteger = PSInt
45
46 instance Ord PSNum where
47 compare = resNum compare
1 module Parse where
2
3 import Control.Monad (when)
4 import Data.ByteString (ByteString)
5 import qualified Data.ByteString as BS
6 import Data.Char (ord)
7 import Data.List.NonEmpty (NonEmpty(..))
8 import Data.Text (Text)
9 import qualified Data.Text as T
10 import Text.Megaparsec
11 import Text.Megaparsec.Text
12
13 import Types
14
15 -- Okay, yeah, 'tokenize' would be better, sure
16
17 initialParserState :: Text -> State Text
18 initialParserState t = State
19 { stateInput = t
20 , statePos = SourcePos "<file>" (unsafePos 1) (unsafePos 1) :| []
21 , stateTabWidth = unsafePos 8
22 }
23
24 pNextParsedObject :: State Text -> Either String (ParsedObject, State Text)
25 pNextParsedObject st = result
26 where result = case runParser' (pWhitespace *> pParsedObject) st of
27 (_, Left err) -> Left (show err)
28 (s, Right v) -> Right (v, s)
29
30 pWhitespace :: Parser ()
31 pWhitespace = skipMany spaceChar
32
33 specialChars :: String
34 specialChars = "()<>[]{}/% \t\r\n"
35
36 pParsedObject :: Parser ParsedObject
37 pParsedObject =
38 uncurry PORadix <$> try pRadix
39 <|> POReal <$> pReal
40 <|> POInteger <$> pInteger
41 <|> POString <$> pString
42 <|> POByteString <$> pByteString
43 <|> POSymbol <$> pSymbol
44 <|> POName <$> pName
45
46 pSign :: Num a => Parser (a -> a)
47 pSign = negate <$ char '-'
48 <|> id <$ char '+'
49 <|> pure id
50
51 pRadix :: Parser (Int, Integer)
52 pRadix = do
53 base <- read <$> some digitChar
54 _ <- char '#'
55 when (base < 2 || base > 36) $
56 fail ("Invalid base: " ++ show base)
57 num <- some (digitForBase base)
58 return (base, readAsBase base num)
59
60 readAsBase :: Num a => Int -> String -> a
61 readAsBase _ [] = error "[unreachable]"
62 readAsBase _ [c] = numValOf c
63 readAsBase b (c:cs) = (numValOf c * fromIntegral b) + readAsBase b cs
64
65 digitForBase :: Int -> Parser Char
66 digitForBase n = oneOf chars
67 where chars = take n ['0'..'9'] ++
68 take (n-1) ['A'..'Z'] ++
69 take (n-1) ['a'..'z']
70
71 pReal :: Parser Double
72 pReal = fail "unimplemented"
73
74 pInteger :: Parser Integer
75 pInteger = do
76 sign <- pSign
77 num <- some digitChar
78 return $ sign (read num)
79
80 pString :: Parser Text
81 pString = T.pack <$> (char '(' *> go)
82 where go =
83 "" <$ char ')'
84 <|> (++) <$> some (noneOf ['(',')','\\']) <*> go -- XXX: handle rest!
85
86 pByteString :: Parser ByteString
87 pByteString = char '<' *> (go <$> some hexDigitChar) <* char '>'
88 where go = BS.pack . toVals
89 toVals (x:y:xs) = (numValOf x * 16 + numValOf y) : toVals xs
90 toVals (x:[]) = [ numValOf x * 16 ]
91 toVals [] = []
92
93 numValOf :: Num b => Char -> b
94 numValOf n
95 | n >= 'a' && n <= 'z' = fromIntegral (ord n) - 0x57
96 | n >= 'A' && n <= 'Z' = fromIntegral (ord n) - 0x37
97 | otherwise = fromIntegral (ord n) - 0x30
98
99 pSymbol :: Parser Text
100 pSymbol = T.pack <$> (char '/' *> many (noneOf specialChars))
101
102 pName :: Parser Text
103 pName = T.pack <$> some (noneOf specialChars)
1 module Pretty where
2
3 import Data.Monoid ((<>))
4 import qualified Data.Text as T
5 import qualified Data.Text.IO as T
6
7 class PSShow t where
8 psShow :: t -> Text
9
10 instance PSShow Object where
11 psShow (OBool True) = "true"
12 psShow (OBool False) = "false"
13 psShow (OInteger i) = "0"
14 psShow (OString t) = "(" <> t <> ")"
15
16 psPrint :: PSShow t => t -> IO ()
17 psPrint = T.putStrLn . psShow
1 module Run where
2
3 import Control.Monad ((=<<), forever)
4 import Control.Monad.IO.Class
5 import Control.Monad.Trans.Class (lift)
6 import Control.Monad.Trans.Except
7 import Control.Monad.Trans.State
8 import Data.HashMap.Strict (HashMap)
9 import qualified Data.HashMap.Strict as HM
10 import Data.Monoid ((<>), Last(..))
11 import qualified Data.Sequence as S
12 import Data.Sequence (Seq, (<|), ViewL(..))
13 import Data.Text (Text)
14
15 import Types
16 import Parse
17 import PSNum
18
19 pop :: Endsay Object
20 pop = Endsay $ do
21 st <- lift get
22 let x :< xs = S.viewl (isOpStack st)
23 lift $ put st { isOpStack = xs }
24 return x
25
26 push :: Object -> Endsay ()
27 push o = Endsay $ lift $ modify (\ s -> s { isOpStack = o <| isOpStack s })
28
29 pushObject :: ParsedObject -> Endsay ()
30 pushObject (POInteger n) = push (OInteger n)
31 pushObject (POReal n) = push (OReal n)
32 pushObject (PORadix _ n) = push (OInteger n)
33 pushObject (POString t) = push (OString t)
34 pushObject (POByteString t) = return ()
35 pushObject (POSymbol t) = push (OName t)
36 pushObject (POName t) = runCommand t
37
38 runCommand :: Text -> Endsay ()
39 runCommand t = do
40 obj <- findName t
41 case obj of
42 Nothing -> liftIO $ putStrLn "Name not found"
43 Just (OBuiltInOperator (BuiltIn b)) -> b
44
45 runEndsay :: Text -> Endsay a -> IO (Either ESErr a, InterpreterState)
46 runEndsay t (Endsay mote) =
47 runStateT (runExceptT mote) (initialInterpreterState t)
48 -- runStateT (runInterp mote) (runStateT (initialInterpreterState t) (runInterp mote))
49
50 findName :: Text -> Endsay (Maybe Object)
51 findName t = do
52 InterpreterState { isDictStack = ds } <- Endsay $ lift get
53 return $ getLast $ foldr (\ d r -> Last (HM.lookup t d) <> r) (Last Nothing) ds
54
55 step :: Endsay ()
56 step = do
57 st <- Endsay $ lift get
58 case pNextParsedObject (isParseState st) of
59 Left err -> Endsay $ throwE (ESErr "unable to parse another thing")
60 Right (po, st') -> do
61 pushObject po
62 Endsay $ lift $ modify $ \ s -> s { isParseState = st' }
63
64 run :: Endsay ()
65 run = forever step
66
67 initialInterpreterState :: Text -> InterpreterState
68 initialInterpreterState t = InterpreterState
69 { isSource = t
70 , isParseState = initialParserState t
71 , isOpStack = S.empty
72 , isDictStack = S.singleton $ basicDict
73 , isExecStack = S.empty
74 }
75
76 builtin :: Endsay () -> Object
77 builtin = OBuiltInOperator . BuiltIn
78
79 basicDict :: HashMap Text Object
80 basicDict = HM.fromList
81 [ ("add", builtin $ do
82 x <- toPSNum =<< pop
83 y <- toPSNum =<< pop
84 push (fromPSNum (x + y))
85 )
86 , ("sub", builtin $ do
87 x <- toPSNum =<< pop
88 y <- toPSNum =<< pop
89 push (fromPSNum (x - y))
90 )
91 , ("mul", builtin $ do
92 x <- toPSNum =<< pop
93 y <- toPSNum =<< pop
94 push (fromPSNum (x * y))
95 )
96 , ("lt", builtin $ do
97 x <- toPSNum =<< pop
98 y <- toPSNum =<< pop
99 push (OBool (x < y))
100 )
101 , ("gt", builtin $ do
102 x <- toPSNum =<< pop
103 y <- toPSNum =<< pop
104 push (OBool (x > y))
105 )
106 , ("dup", builtin $ do
107 x <- pop
108 push x
109 push x
110 )
111 , ("exch", builtin $ do
112 x <- pop
113 y <- pop
114 push x
115 push x
116 )
117 , ("=", builtin $ pop >>= liftIO . print)
118 ]
1 module Stdlib where
1 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
2 {-# LANGUAGE RankNTypes #-}
3
4 module Types where
5
6 import Control.Monad.Trans.Except (ExceptT)
7 import Control.Monad.Trans.State (StateT)
8 import Control.Monad.IO.Class (MonadIO)
9 import Data.ByteString (ByteString)
10 import Data.HashMap.Strict (HashMap)
11 import Data.Sequence (Seq)
12 import Data.Text (Text)
13 import Data.Vector (Vector)
14 import qualified Text.Megaparsec as MP
15
16 data InterpreterState = InterpreterState
17 { isSource :: Text
18 , isParseState :: MP.State Text
19 , isOpStack :: Seq Object
20 , isDictStack :: Seq (HashMap Text Object)
21 , isExecStack :: Seq Object
22 } deriving (Show)
23
24 data BuiltIn = BuiltIn (Endsay ())
25
26 instance Show BuiltIn where show _ = "BuiltIn { ... }"
27
28 type Fragment = Vector Object
29
30 data ESErr = ESErr String deriving (Eq, Show)
31
32 newtype Endsay a =
33 Endsay { runInterp :: ExceptT ESErr (StateT InterpreterState IO) a }
34 deriving (Functor, Applicative, Monad, MonadIO)
35
36 data Object
37 = OBool Bool
38 | OFontId ()
39 | OInteger Integer
40 | OMark ()
41 | OName Text
42 | ONull
43 | OOperator ()
44 | OBuiltInOperator BuiltIn
45 | OReal Double
46 | OArray (Vector Object)
47 | ODictionary (HashMap Text Object)
48 | OFile ()
49 -- | OGState ()
50 -- | OPackedArray ()
51 | OSave ()
52 | OString Text
53 deriving (Show)
54
55 data ParsedObject
56 = POInteger Integer
57 | POReal Double
58 | PORadix Int Integer
59 | POString Text
60 | POByteString ByteString
61 | POSymbol Text
62 | POName Text
63 deriving (Eq, Show)
64
65 data SourceLiteral = SourceLiteral
66 { slObject :: ParsedObject
67 , slLine :: Int
68 , slColumn :: Int
69 , slText :: Text
70 } deriving (Eq, Show)