Skip to content

Instantly share code, notes, and snippets.

@Mesabloo
Last active June 2, 2022 15:00
Show Gist options
  • Save Mesabloo/96aef8da87903201ee76edd6c909c2ff to your computer and use it in GitHub Desktop.
Save Mesabloo/96aef8da87903201ee76edd6c909c2ff to your computer and use it in GitHub Desktop.
A very simple VM (as a stack script) for a small concatenative programming language which aims to be as performant as possible.

Yes, this is very ugly. However, I managed to squeeze so much out of the original code, and make it really fast (though this could be even better). I think I might have gotten to the point where I cannot really make it go faster, but for a basic VM, I think this is bloody good as it is.

#!/usr/bin/env stack
-- stack --resolver lts-18.28 script --optimize --ghc-options "-O2" --ghc-options "-flate-dmd-anal" --ghc-options "-fspecialise-aggressively" --ghc-options "-flate-specialise" --ghc-options "-fstatic-argument-transformation" --package text,deepseq
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MonomorphismRestriction #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE Strict #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnboxedSums #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE UnliftedNewtypes #-}
{-# LANGUAGE Unsafe #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS -Weverything #-}
{-# OPTIONS -Wno-name-shadowing #-}
{-# OPTIONS -Wno-unused-top-binds #-}
{-# OPTIONS -Wno-missing-import-lists #-}
{-# OPTIONS -Wno-unsafe #-}
#define STACK_SAFE_OPERATIONS 0
#define NUMBER_OF_BUILTINS 10
#define DEBUG 0
import Control.DeepSeq (NFData, force)
import Control.Exception (Exception (..))
import Data.Bool (Bool (..), not, otherwise, (&&))
import Data.Functor ((<$>))
import Data.List qualified as List
#if STACK_SAFE_OPERATIONS == 1
import GHC.Exts (sizeofMutableArray#, (>=#))
#endif
import Data.Maybe (Maybe (Just, Nothing))
import Data.Semigroup ((<>))
import Data.String (String)
import Data.Text (Text)
import Data.Text qualified as Text
import GHC.Exts (Array#, Char (..), Char#, Double (..), Double#, Int (..), Int#, MutVar#, MutableArray#, RealWorld, State#, Void#, catch#, copyMutableArray#, eqChar#, freezeArray#, indexArray#, newArray#, newMutVar#, raise#, raiseIO#, readArray#, readMutVar#, remInt#, sizeofArray#, sizeofMutableArray#, unsafeFreezeArray#, void#, writeArray#, writeMutVar#, (*#), (+#), (-#), (==#), (==##), (>#), (>=#))
import GHC.IO (IO (..), unIO, unsafePerformIO)
import GHC.Int (Int32 (I32#))
import Numeric (showHex)
import System.CPUTime (getCPUTime)
import System.IO (print, putStr, putStrLn)
import Prelude (Integer, Show (..), fromIntegral, read, undefined, ($), ($!), (*), (+), (-), (<), (==), (>=))
-- | An unlifted, unboxed equivalent to the standard data type 'Bool', where @0#@ is false and @1#@ is true.
newtype Bool# = Bool# Int#
-- | Pattern synonyms for convenience.
pattern True#, False# :: Bool#
pattern True# = Bool# 1#
pattern False# = Bool# 0#
-- | Retrieve the 'String' representation of a 'Bool#'.
--
-- Note that this is not inside a 'Show' instance as 'Show' takes a lifted type as first parameter.
showBool# :: Bool# -> String
showBool# True# = "true"
showBool# False# = "false"
showBool# _ = undefined
{-# INLINE showBool# #-}
-- | An expression is a list of atoms.
type Expr = [Atom]
instance {-# OVERLAPPING #-} Show Expr where
show ex = List.unwords $ show <$> ex
-- | Atoms are uncuttable parts of a program.
data Atom
= -- | An unevaluated chunk of code, sometimes refered to as a “thunk” or “quote”.
AQuote Expr
| -- | A shortcut to an expression already evaluated.
AIdentifier Text
| -- | A classic integer literal (most likely to be either 32 or 64-bit).
AInteger Int
| -- | A double-precision floating point literal.
AFloat Double
| -- | A unicode character.
ACharacter Char
| -- | A classic boolean (true or false).
ABoolean Bool
| -- | A simple character string.
AString Text
instance Show Atom where
show (AQuote ex) = "[" <> show ex <> "]"
show (AIdentifier id) = Text.unpack id
show (AInteger i) = show i
show (AFloat d) = show d
show (ACharacter c) = show c
show (ABoolean b) = show b
show (AString txt) = show txt
-- | The stack holds intermediate values at runtime.
data Stack# a
= Stack
!(MutableArray# RealWorld a)
!Int#
{- ORMOLU_DISABLE -}
-- | Remove the value on top of the stack, yielding an exception if the stack is empty.
popStack :: MutVar# RealWorld (Stack# a) -> State# RealWorld -> (# State# RealWorld, a #)
popStack ref s0 =
let !(# s1, Stack !array !ptr #) = readMutVar# ref s0 in
#if STACK_SAFE_OPERATIONS == 1
case ptr of
-1# -> raise# EmptyStackOnPop
_ ->
#endif
readArray# array ptr (writeMutVar# ref (Stack array (ptr -# 1#)) s1)
{-# INLINE popStack #-}
-- | Push the given value on top of the stack.
--
-- If the stack grows more than its maximum capacity, the behavior of this function is undefined.
pushStack :: MutVar# RealWorld (Stack# a) -> a -> State# RealWorld -> (# State# RealWorld, Void# #)
pushStack ref val s0 =
let !(# s1, Stack !array !ptr #) = readMutVar# ref s0 in
let !ptrPlusOne = ptr +# 1# in
#if STACK_SAFE_OPERATIONS == 1
case ptrPlusOne >=# sizeofMutableArray# array of
1# -> raise# StackOverflow
_ ->
#endif
(# writeArray# array ptrPlusOne val (writeMutVar# ref (Stack array ptrPlusOne) s1), void# #)
{-# INLINE pushStack #-}
-- | Get a peak of the value on top of the stack, wihout actually removing it.
peekStack :: MutVar# RealWorld (Stack# a) -> State# RealWorld -> (# State# RealWorld, a #)
peekStack ref s0 =
let !(# s1, Stack !array !ptr #) = readMutVar# ref s0 in
#if STACK_SAFE_OPERATIONS == 1
case ptr of
-1# -> raise# EmptyStackOnPeek
_ ->
#endif
readArray# array ptr s1
{-# INLINE peekStack #-}
{- ORMOLU_ENABLE -}
type Closure = MutVar# RealWorld (Stack# Value#) -> State# RealWorld -> (# State# RealWorld, Void# #)
-- | The kind of runtime values as a unboxed sum.
--
-- (For reasons, this is a @data@ as it cannot be used inside 'Vector's otherwise)
data Value# = Value (# Int32| Double#| Int#| Char#| Bool#| Text| (# Closure, Int# #) #)
pattern VQuote# :: Int32 -> Value#
pattern VQuote# idx = Value (# idx | | | | | | #)
pattern VDouble# :: Double# -> Value#
pattern VDouble# d = Value (# | d | | | | | #)
pattern VInteger# :: Int# -> Value#
pattern VInteger# i = Value (# | | i | | | | #)
pattern VCharacter# :: Char# -> Value#
pattern VCharacter# c = Value (# | | | c | | | #)
pattern VBoolean# :: Bool# -> Value#
pattern VBoolean# b = Value (# | | | | b | | #)
pattern VString# :: Text -> Value#
pattern VString# txt = Value (# | | | | | txt | #)
pattern VPrimitive# :: Closure -> Int# -> Value#
pattern VPrimitive# f id = Value (# | | | | | | (# f, id #) #)
-- | Returns a 'String' representation of a 'Value#'.
showValue# :: Value# -> String
showValue# (VQuote# off) = "#" <> show off
showValue# (VDouble# d) = show (D# d)
showValue# (VInteger# i) = show (I# i)
showValue# (VCharacter# c) = show (C# c)
showValue# (VBoolean# b) = showBool# b
showValue# (VString# txt) = show txt
showValue# (VPrimitive# _ id) = "prim@" <> show (I# id)
showValue# _ = "???"
{-# INLINE showValue# #-}
eqValue# :: Value# -> Value# -> Int#
eqValue# (VDouble# d1) (VDouble# d2) = d1 ==## d2
eqValue# (VInteger# i1) (VInteger# i2) = i1 ==# i2
eqValue# (VCharacter# c1) (VCharacter# c2) = eqChar# c1 c2
eqValue# (VBoolean# (Bool# b1)) (VBoolean# (Bool# b2)) = b1 ==# b2
eqValue# (VString# t1) (VString# t2) = if t1 == t2 then 1# else 0#
eqValue# (VQuote# (I32# o1)) (VQuote# (I32# o2)) = o1 ==# o2
eqValue# (VPrimitive# _ id1) (VPrimitive# _ id2) = id1 ==# id2
eqValue# _ _ = 0#
-- | The evaluation context contains the current stack, environment and expression to evaluate.
data Context
= Context
!(MutVar# RealWorld (Stack# Value#))
!(MutVar# RealWorld (Stack# Int32))
!Int32
!ConstantTable#
!SymbolTable#
!FunctionTable#
!CodeTable#
data EvalError
= EmptyStackOnPop
| EmptyStackOnPeek
| StackOverflow
| UnboundIdentifier !Text
| TypeError !Text
deriving stock (Show)
instance Exception EvalError
------------------------------------------------------------------
----------------------- BYTECODE AND STUFF -----------------------
------------------------------------------------------------------
type ConstantTable# = Array# Value#
type SymbolTable# = Array# Text
type FunctionTable# = Array# Int
type CodeTable# = Array# Int32
-- | The layout of a bytecode memory file.
data BytecodeFile
= File
!ConstantTable#
-- ^ Constants
!SymbolTable#
-- ^ Symbols
!FunctionTable#
-- ^ Function offsets, mapping byte offsets (as integers) into the code.
!CodeTable#
-- ^ The compiled code itself
!Int#
-- ^ The starting instruction pointer
{- ORMOLU_DISABLE -}
printBytecodeFile :: BytecodeFile -> State# RealWorld -> (# State# RealWorld, Void# #)
printBytecodeFile (File cstTable symTable funTable codeTable ip) s0 =
let !cstTableSize = sizeofArray# cstTable in
let !symTableSize = sizeofArray# symTable in
let !funTableSize = sizeofArray# funTable in
let !codeTableSize = sizeofArray# codeTable in
let !(# s1, _ #) = unIO (putStrLn "-----| CONSTANTS |-----") s0 in
let !(# s2, _ #) = printConstantTable cstTable cstTableSize s1 in
let !(# s3, _ #) = unIO (putStrLn "-----| SYMBOLS |-----") s2 in
let !(# s4, _ #) = printSymbolTable symTable symTableSize s3 in
let !(# s5, _ #) = unIO (putStrLn "-----| FUNCTIONS |-----") s4 in
let !(# s6, _ #) = printFunctionTable funTable funTableSize s5 in
let !(# s7, _ #) = unIO (putStr "-----| CODE |-----") s6 in
let !(# s8, _ #) = printCodeTable codeTable codeTableSize s7 in
let !(# s9, _ #) = unIO (putStrLn $ "IP=" <> show (I# ip)) s8 in
(# s9, void# #) -- TODO
printConstantTable :: ConstantTable# -> Int# -> State# RealWorld -> (# State# RealWorld, Void# #)
printConstantTable table size s0 = go 0# s0
where
go x s0 = case x >=# size of
1# -> (# s0, void# #)
0# ->
let !(# val #) = indexArray# table x in
let !(# s1, !_ #) = unIO (putStrLn $ show (I# x) <> ":\t" <> showValue# val) s0 in
go (x +# 1#) s1
_ -> undefined
printSymbolTable :: SymbolTable# -> Int# -> State# RealWorld -> (# State# RealWorld, Void# #)
printSymbolTable table size s0 = go 0# s0
where
go x s0 = case x >=# size of
1# -> (# s0, void# #)
0# ->
let !(# sym #) = indexArray# table x in
let !(# s1, !_ #) = unIO (putStrLn $ show (I# x) <> ":\t" <> show (Text.unpack sym)) s0 in
go (x +# 1#) s1
_ -> undefined
printFunctionTable :: FunctionTable# -> Int# -> State# RealWorld -> (# State# RealWorld, Void# #)
printFunctionTable table size s0 = go 0# s0
where
go x s0 = case x >=# size of
1# -> (# s0, void# #)
0# ->
let !(# off #) = indexArray# table x in
let !(# s1, !_ #) = unIO (putStrLn $ show (I# x) <> ":\t+" <> show off) s0 in
go (x +# 1#) s1
_ -> undefined
printCodeTable :: CodeTable# -> Int# -> State# RealWorld -> (# State# RealWorld, Void# #)
printCodeTable table size s0 = go 0# s0
where
go x s0 = case x >=# size of
1# ->
let !(# s1, _ #) = unIO (putStrLn "") s0 in
(# s1, void# #)
0# ->
let !(# code #) = indexArray# table x in
let !(# s1, !_ #) = case remInt# x 8# of
0# -> unIO (putStr $ "\n" <> show (I# x) <> ":\t") s0
_ -> (# s0, () #) in
let !(# s2, !_ #) = unIO (putStr $ showHex code "\t") s1 in
go (x +# 1#) s2
_ -> undefined
{- ORMOLU_ENABLE -}
-- Followed by the identifier of the VPrimitive# in the constant table.
#define BYTECODE_PRIM 0x0
-- Followed by the identifier of the symbol (in both the symbol table and the function table).
#define BYTECODE_REDUCE 0x1
-- Followed by the identifier of the value in the constant table.
#define BYTECODE_PUSH 0x2
-- Returns to the calling address.
#define BYTECODE_RET 0x3
-- Unquote the last quote on the stack.
#define BYTECODE_UNQUOTE 0x4
{- ORMOLU_DISABLE -}
resizeConstantTableIfNeeded :: Int# -> MutableArray# RealWorld Value# -> State# RealWorld -> (# State# RealWorld, MutableArray# RealWorld Value# #)
resizeConstantTableIfNeeded 0# constants s0 = (# s0, constants #)
resizeConstantTableIfNeeded 1# constants s0 =
let !constantsSize = sizeofMutableArray# constants in
-- let !_ = unsafePerformIO (putStrLn $ ">> Resizing constant table from " <> show (I# constantsSize) <> " to " <> show (I# (constantsSize *# 2#))) in
let !(# s1, arr #) = newArray# (constantsSize *# 2#) (VInteger# 0#) s0 in
let !s2 = copyMutableArray# constants 0# arr 0# constantsSize s1 in
(# s2, arr #)
resizeConstantTableIfNeeded _ _ _ = undefined
resizeSymbolsTableIfNeeded :: Int# -> MutableArray# RealWorld Text -> State# RealWorld -> (# State# RealWorld, MutableArray# RealWorld Text #)
resizeSymbolsTableIfNeeded 0# symbols s0 = (# s0, symbols #)
resizeSymbolsTableIfNeeded 1# symbols s0 =
let !symbolsSize = sizeofMutableArray# symbols in
-- let !_ = unsafePerformIO (putStrLn $ ">> Resizing symbols table from " <> show (I# symbolsSize) <> " to " <> show (I# (symbolsSize *# 2#))) in
let !(# s1, arr #) = newArray# (symbolsSize *# 2#) ("" :: Text) s0 in
let !s2 = copyMutableArray# symbols 0# arr 0# symbolsSize s1 in
(# s2, arr #)
resizeSymbolsTableIfNeeded _ _ _ = undefined
resizeFunctionsTableIfNeeded :: Int# -> MutableArray# RealWorld Int -> State# RealWorld -> (# State# RealWorld, MutableArray# RealWorld Int #)
resizeFunctionsTableIfNeeded 0# functions s0 = (# s0, functions #)
resizeFunctionsTableIfNeeded 1# functions s0 =
let !functionsSize = sizeofMutableArray# functions in
-- let !_ = unsafePerformIO (putStrLn $ ">> Resizing functions table from " <> show (I# functionsSize) <> " to " <> show (I# (functionsSize *# 2#))) in
let !(# s1, arr #) = newArray# (functionsSize *# 2#) (0 :: Int) s0 in
let !s2 = copyMutableArray# functions 0# arr 0# functionsSize s1 in
(# s2, arr #)
resizeFunctionsTableIfNeeded _ _ _ = undefined
resizeCodeTableIfNeeded :: Int# -> MutableArray# RealWorld Int32 -> State# RealWorld -> (# State# RealWorld, MutableArray# RealWorld Int32 #)
resizeCodeTableIfNeeded 0# code s0 = (# s0, code #)
resizeCodeTableIfNeeded 1# code s0 =
let !codeSize = sizeofMutableArray# code in
-- let !_ = unsafePerformIO (putStrLn $ ">> Resizing code table from " <> show (I# codeSize) <> " to " <> show (I# (codeSize *# 2#))) in
let !(# s1, arr #) = newArray# (codeSize *# 2#) (0 :: Int32) s0 in
let !s2 = copyMutableArray# code 0# arr 0# codeSize s1 in
(# s2, arr #)
resizeCodeTableIfNeeded _ _ _ = undefined
-- | Compile a surface language expression into an in-memory bytecode file ready to be evaluated.
compile :: Expr -> [(Text, Expr)] -> State# RealWorld -> (# State# RealWorld, BytecodeFile #)
compile expr bindings s0 =
let !(# s1, (# constants, constantsPtr #) #) = createConstantArray s0 in
let !(# s2, (# symbols, symbolsPtr #) #) = createSymbolsArray s1 in
let !(# s3, (# functions, functionsPtr #) #) = createFunctions s2 in
let !(# s4, (# code, codePtr #) #) = createCode s3 in
let !(# s5, (# ip, constants0, symbols0, functions0, code0 #) #) =
go expr bindings constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s4 in
let !(# s6, I# constantsPtr0 #) = readMutVar# constantsPtr s5 in
let !(# s7, constants1 #) = freezeArray# constants0 0# constantsPtr0 s6 in
let !(# s8, I# symbolsPtr0 #) = readMutVar# symbolsPtr s7 in
let !(# s9, symbols1 #) = freezeArray# symbols0 0# symbolsPtr0 s8 in
let !(# s10, I# functionsPtr0 #) = readMutVar# functionsPtr s9 in
let !(# s11, functions1 #) = freezeArray# functions0 0# functionsPtr0 s10 in
let !(# s12, I# codePtr0 #) = readMutVar# codePtr s11 in
let !(# s13, code1 #) = freezeArray# code0 0# codePtr0 s12 in
-- NOTE: symbolsPtr0 ==# functionsPtr0 must be 1#
(# s13, File constants1 symbols1 functions1 code1 ip #)
where
createConstantArray :: State# RealWorld -> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutVar# RealWorld Int #) #)
createConstantArray s0 =
let !(# s1, constants #) = newArray# 10# (VInteger# 0#) s0 in
let !(# s2, ptr #) = newMutVar# (0 :: Int) s1 in
(# s2, (# constants, ptr #) #)
createSymbolsArray :: State# RealWorld -> (# State# RealWorld, (# MutableArray# RealWorld Text, MutVar# RealWorld Int #) #)
createSymbolsArray s0 =
let !(# s1, symbols #) = newArray# 10# ("" :: Text) s0 in
let !(# s2, ptr #) = newMutVar# (0 :: Int) s1 in
(# s2, (# symbols, ptr #) #)
createFunctions :: State# RealWorld -> (# State# RealWorld, (# MutableArray# RealWorld Int, MutVar# RealWorld Int #) #)
createFunctions s0 =
let !(# s1, functions #) = newArray# NUMBER_OF_BUILTINS# (0 :: Int) s0 in
let !(# s2, functionsPtr #) = newMutVar# (0 :: Int) s1 in
(# s2, (# functions, functionsPtr #) #)
createCode :: State# RealWorld -> (# State# RealWorld, (# MutableArray# RealWorld Int32, MutVar# RealWorld Int #) #)
createCode s0 =
let !(# s1, code #) = newArray# 20# (0 :: Int32) s0 in
let !(# s2, codePtr #) = newMutVar# (0 :: Int) s1 in
(# s2, (# code, codePtr #) #)
go expr bindings constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 =
let !(# s1, (# constants0, symbols0, functions0, code0 #) #) =
compileAdditionalBindings bindings constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 in
compileExpr expr constants0 constantsPtr symbols0 symbolsPtr functions0 functionsPtr code0 codePtr s1
compileAdditionalBindings :: [(Text, Expr)]
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Text -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32 #) #)
compileAdditionalBindings [] constants _ symbols _ functions _ code _ s0 = (# s0, (# constants, symbols, functions, code #) #)
compileAdditionalBindings ((name, val) : binds) constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 =
let !(# s1, !(I# codePtr0) #) = readMutVar# codePtr s0 in
let !(# s2, !(I# symbolsPtr0) #) = readMutVar# symbolsPtr s1 in
let !(# s3, !(I# functionsPtr0) #) = readMutVar# functionsPtr s2 in
let !symbolsSize = sizeofMutableArray# symbols in
let !functionsSize = sizeofMutableArray# functions in
let !(# s4, symbols0 #) = resizeSymbolsTableIfNeeded (symbolsPtr0 >=# symbolsSize) symbols s3 in
let !(# s5, functions0 #) = resizeFunctionsTableIfNeeded (functionsPtr0 >=# functionsSize) functions s4 in
let !s6 = writeArray# symbols0 symbolsPtr0 name s5 in
let !s7 = writeArray# functions0 functionsPtr0 (I# codePtr0) s6 in
let !(# s8, (# ip, constants0, symbols1, functions1, code0 #) #) =
compileExpr val constants constantsPtr symbols0 symbolsPtr functions0 functionsPtr code codePtr s7 in
let !(# s9, I# codePtr1 #) = readMutVar# codePtr s8 in
let !codeSize = sizeofMutableArray# code0 in
let !(# s10, code1 #) = resizeCodeTableIfNeeded (codePtr1 >=# codeSize) code0 s9 in
let !(# s11, code2 #) = pushOpcodes [BYTECODE_RET] codePtr1 code1 codePtr s10 in
let !s12 = writeArray# functions1 functionsPtr0 (I# ip) s11 in
let !s13 = writeMutVar# functionsPtr (I# (functionsPtr0 +# 1#)) s12 in
let !s14 = writeMutVar# symbolsPtr (I# (symbolsPtr0 +# 1#)) s13 in
compileAdditionalBindings binds constants0 constantsPtr symbols1 symbolsPtr functions1 functionsPtr code2 codePtr s14
compileExpr :: Expr
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Text -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# Int#, MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32 #) #)
compileExpr expr constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 =
let !(# s1, (# constants0, symbols0, functions0, code0, expr0 #) #) =
precompileQuotes expr constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 in
let !(# s2, I# ip #) = readMutVar# codePtr s1 in
let !(# s3, (# constants1, symbols1, functions1, code1 #) #) =
compileAtoms expr0 constants0 constantsPtr symbols0 symbolsPtr functions0 functionsPtr code0 codePtr s2 in
(# s3, (# ip, constants1, symbols1, functions1, code1 #) #)
precompileQuotes :: Expr
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Text -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32, Expr #) #)
precompileQuotes = go []
where
go :: Expr
-> Expr
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Text -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32, Expr #) #)
go acc [] constants _ symbols _ functions _ code _ s0 = (# s0, (# constants, symbols, functions, code, List.reverse acc #) #)
go acc (atom : expr) constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 = case atom of
AQuote expr0 ->
let !(# s1, (# constants0, symbols0, functions0, code0, expr1 #) #) =
go [] expr0 constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 in
let !(# s2, I# codePtr0 #) = readMutVar# codePtr s1 in
let !(# s3, (# constants1, symbols1, functions1, code1 #) #) =
compileAtoms expr1 constants0 constantsPtr symbols0 symbolsPtr functions0 functionsPtr code0 codePtr s2 in
let !(# s4, I# codePtr1 #) = readMutVar# codePtr s3 in
let !codeSize = sizeofMutableArray# code1 in
let !(# s5, code2 #) = resizeCodeTableIfNeeded (codePtr1 >=# codeSize) code1 s4 in
let !(# s6, code3 #) = pushOpcodes [BYTECODE_RET] codePtr1 code2 codePtr s5 in
let !quoteId = ("$" <> Text.pack (show (I# codePtr0))) in
go (AIdentifier quoteId : acc) expr constants1 constantsPtr symbols1 symbolsPtr functions1 functionsPtr code3 codePtr s6
_ -> go (atom : acc) expr constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0
compileAtoms :: Expr
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Text -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32 #) #)
compileAtoms [] constants _ symbols _ functions _ code _ s0 = (# s0, (# constants, symbols, functions, code #) #)
compileAtoms (atom : expr) constants constantsPtr symbols symbolsPtr functions functionsPtr code codePtr s0 =
let !(# s1, (# constants0, symbols0, functions0, code0 #) #) = compileAtom atom constants symbols functions code s0 in
compileAtoms expr constants0 constantsPtr symbols0 symbolsPtr functions0 functionsPtr code0 codePtr s1
where
compileAtom :: Atom
-> MutableArray# RealWorld Value#
-> MutableArray# RealWorld Text
-> MutableArray# RealWorld Int
-> MutableArray# RealWorld Int32
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Text, MutableArray# RealWorld Int, MutableArray# RealWorld Int32 #) #)
compileAtom (AInteger (I# i)) constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertConstant (VInteger# i) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (ACharacter (C# c)) constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertConstant (VCharacter# c) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (ABoolean !b) constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertConstant (VBoolean# (if b then True# else False#)) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AFloat (D# d)) constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertConstant (VDouble# d) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AString !txt) constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertConstant (VString# txt) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "unquote") constants symbols functions code s0 =
let !(# s1, I# codePtr0 #) = readMutVar# codePtr s0 in
let !codeSize = sizeofMutableArray# code in
let !(# s2, code0 #) = resizeCodeTableIfNeeded (codePtr0 >=# codeSize) code s1 in
let !(# s3, code1 #) = pushOpcodes [BYTECODE_UNQUOTE] codePtr0 code0 codePtr s2 in
(# s3, (# constants, symbols, functions, code1 #) #)
compileAtom (AIdentifier "pop") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin pop 0# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "dup") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin dup 1# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "swap") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin swap 2# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "if") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin ifthenelse 3# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "rot31") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin rot31 4# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "+") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin add 5# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "-") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin sub 6# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "*") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin times 7# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier "=") constants symbols functions code s0 =
let !(# s1, (# constants0, code0 #) #) = insertBuiltin eq 8# constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
compileAtom (AIdentifier !name) constants symbols functions code s0 =
case (# Text.head name, Text.tail name #) of
(# '$', offset #) ->
let !(I# off) = read (Text.unpack offset) in
let !(# s1, (# constants0, code0 #) #) = insertConstant (VQuote# (I32# off)) constants constantsPtr code codePtr s0 in
(# s1, (# constants0, symbols, functions, code0 #) #)
_ ->
let !(# s1, index #) = findIndexFromReducerName symbols name s0 in
let !(# s2, I# codePtr0 #) = readMutVar# codePtr s1 in
let !codeSize = sizeofMutableArray# code in
let !(# s3, code0 #) = resizeCodeTableIfNeeded (codePtr0 >=# codeSize -# 1#) code s2 in
let !(# s4, code1 #) = pushOpcodes [BYTECODE_REDUCE, I32# index] codePtr0 code0 codePtr s3 in
(# s4, (# constants, symbols, functions, code1 #) #)
compileAtom (AQuote _) _ _ _ _ _ = undefined
pushOpcodes :: [Int32] -> Int# -> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int -> State# RealWorld -> (# State# RealWorld, MutableArray# RealWorld Int32 #)
pushOpcodes opcodes offset code codePtr s0 =
let !size = List.length opcodes in
let !s1 = writeMutVar# codePtr (I# offset + size) s0 in
-- let !_ = unsafePerformIO (putStrLn $ "Pushing " <> show size <> " opcodes onto the code section starting at offset " <> show (I# offset)) in
go opcodes offset s1
where
go [] _ !s0 = (# s0, code #)
go (op : ops) off !s0 = go ops (off +# 1#) (writeArray# code off op s0)
{-# INLINE pushOpcodes #-}
insertConstant :: Value#
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Int32 #) #)
insertConstant cst table cstPtr code codePtr s0 =
let !(# s1, !(I# cstPtr0) #) = readMutVar# cstPtr s0 in
let !(# s2, !(I# codePtr0) #) = readMutVar# codePtr s1 in
let !(# s3, (# index, table0 #) #) = insertConstantInTableOnlyWhenNotFound cst table cstPtr cstPtr0 s2 in
let !codeSize = sizeofMutableArray# code in
let !(# s4, code0 #) = resizeCodeTableIfNeeded (codePtr0 >=# codeSize -# 1#) code s3 in
let !s5 = writeArray# table0 cstPtr0 cst s4 in
let !(# s6, code1 #) = pushOpcodes [BYTECODE_PUSH, I32# index] codePtr0 code0 codePtr s5 in
(# s6, (# table0, code1 #) #)
insertBuiltin :: Closure -> Int#
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int
-> MutableArray# RealWorld Int32 -> MutVar# RealWorld Int
-> State# RealWorld
-> (# State# RealWorld, (# MutableArray# RealWorld Value#, MutableArray# RealWorld Int32 #) #)
insertBuiltin f id constants constantsPtr code codePtr s0 =
let !(# s1, !(I# cstPtr0) #) = readMutVar# constantsPtr s0 in
let !(# s2, !(I# codePtr0) #) = readMutVar# codePtr s1 in
let !cst = VPrimitive# f id in
let !(# s3, (# index, constants0 #) #) = insertConstantInTableOnlyWhenNotFound cst constants constantsPtr cstPtr0 s2 in
let !codeSize = sizeofMutableArray# code in
let !(# s4, code0 #) = resizeCodeTableIfNeeded (codePtr0 >=# codeSize -# 1#) code s3 in
let !s5 = writeArray# constants0 cstPtr0 cst s4 in
let !(# s6, code1 #) = pushOpcodes [BYTECODE_PRIM, I32# index] codePtr0 code0 codePtr s5 in
(# s6, (# constants0, code1 #) #)
insertConstantInTableOnlyWhenNotFound :: Value#
-> MutableArray# RealWorld Value# -> MutVar# RealWorld Int -> Int#
-> State# RealWorld
-> (# State# RealWorld, (# Int#, MutableArray# RealWorld Value# #) #)
insertConstantInTableOnlyWhenNotFound cst constants cstPtr cstPtr0 s0 = go constants 0# cstPtr0 s0
where
go :: MutableArray# RealWorld Value# -> Int# -> Int# -> State# RealWorld -> (# State# RealWorld, (# Int#, MutableArray# RealWorld Value# #) #)
go table x end s0 = case x >=# end of
1# ->
let !cstSize = sizeofMutableArray# table in
let !(# s1, table0 #) = resizeConstantTableIfNeeded (end >=# cstSize) table s0 in
let !s2 = writeMutVar# cstPtr (I# (end +# 1#)) s1 in
(# s2, (# end, table0 #) #)
0# ->
let !(# s1, table0 #) = unsafeFreezeArray# table s0 in
let !(# !value #) = indexArray# table0 x in
case eqValue# cst value of
0# -> go table (x +# 1#) end s1
1# -> (# s1, (# x, table #) #)
_ -> undefined
_ -> undefined
findIndexFromReducerName :: MutableArray# RealWorld Text -> Text -> State# RealWorld -> (# State# RealWorld, Int# #)
findIndexFromReducerName functions name s0 =
let !(# s1, functions0 #) = unsafeFreezeArray# functions s0 in
go functions0 0# (sizeofArray# functions0) s1
where
go :: Array# Text -> Int# -> Int# -> State# RealWorld -> (# State# RealWorld, Int# #)
go funs x end s0 = case x >=# end of
1# -> raise# $! UnboundIdentifier name
0# ->
let !(# !fnName #) = indexArray# funs x in
if fnName == name
then (# s0, x #)
else go funs (x +# 1#) end s0
_ -> undefined
{- ORMOLU_ENABLE -}
------------------------------------------------------------------
------------- SOME EXAMPLES BECAUSE WHY NOT ----------------------
------------------------------------------------------------------
example1 :: Expr
example1 = [AIdentifier "pop"]
example2 :: Expr
example2 = [AInteger 5, AInteger 6, AIdentifier "+"]
example3 :: Expr
example3 = example2 <> example2 <> [AIdentifier "+"]
-- | Computation time: ~6-7us
example4 :: Expr
example4 = example3 <> example3 <> [AIdentifier "pop", AIdentifier "dup", AIdentifier "+"]
-- | Computation time: ~150ms
example5 :: Expr
example5 = [AInteger 3, AInteger 6, AIdentifier "ack"]
-- | Computation time: ~26us
example6 :: Expr
example6 = [AInteger 15, AIdentifier "fact"]
example7 :: Expr
example7 = [AInteger 15, AInteger 6, AInteger 25, AInteger 15]
example8 :: Expr
example8 = [AQuote [AInteger 1], AInteger 2]
example9 :: Expr
example9 = example8 <> [AIdentifier "pop", AIdentifier "unquote"]
example10 :: Expr
example10 = [AQuote [AQuote [AInteger 10], AIdentifier "unquote"], AIdentifier "unquote"]
example11 :: Expr
example11 = [AQuote [AInteger 10, AQuote [AInteger 12], AIdentifier "unquote"], AIdentifier "unquote"]
------------------------------------------------------------------
showTime :: Double -> String
showTime = go "s"
where
next :: String -> String
next "s" = "ms"
next "ms" = "us"
next "us" = "ns"
next _ = "ns"
{-# INLINE next #-}
lastUnit :: String -> Bool
lastUnit "ns" = True
lastUnit _ = False
{-# INLINE lastUnit #-}
go :: String -> Double -> String
go unit d
| d < 1 && not (lastUnit unit) = go (next unit) (d * 1000)
| otherwise = show d <> unit
-------------------------------------------------------------
----------------------- ENTRY POINT -------------------------
-------------------------------------------------------------
main :: IO ()
main = IO main'
{- ORMOLU_DISABLE -}
main' :: State# RealWorld -> (# State# RealWorld, () #)
main' s0 =
let !expr = example5 in
let !(# s1, _ #) = unIO (putStr "> ") s0 in
let !(# s2, _ #) = unIO (print expr) s1 in
let !(# s3, bytecodeFile0 #) = compile expr withBindings s2 in
let !(# s4, _ #) = printBytecodeFile bytecodeFile0 s3 in
let !initialSize = 500000# in
let !(# s5, !stack0 #) = newArray# initialSize (VInteger# 0#) s4 in
let !(# s6, !stack1 #) = newMutVar# (Stack stack0 -1#) s5 in
let !(# s7, !cstack0 #) = newArray# 500000# (0 :: Int32) s6 in
let !(# s8, !cstack1 #) = newMutVar# (Stack cstack0 -1#) s7 in
let !(# s9, !(# !time, !_ #) #) = timeItT (eval' stack1 cstack1 bytecodeFile0) s8 in
let !_ = unsafePerformIO (putStr "\nresult: ") in
let !(# s10, !(Stack arr0 ptr) #) = readMutVar# stack1 s9 in
let !(# s11, arr1 #) = unsafeFreezeArray# arr0 s10 in
let !(# s12, !_ #) = printArrayBounds 0# ptr arr1 s11 in
let !_ = unsafePerformIO (putStrLn $ "time taken: " <> showTime time) in
(# s12, () #)
where
withBindings :: [(Text, Expr)]
withBindings = [
("fact", fact),
("ack", ack)
]
eval' :: MutVar# RealWorld (Stack# Value#) -> MutVar# RealWorld (Stack# Int32) -> BytecodeFile -> State# RealWorld -> (# State# RealWorld, () #)
eval' stack cstack (File constants symbols functions code ip) s0 =
eval (Context stack cstack (I32# ip) constants symbols functions code) s0
{-# INLINE eval' #-}
timeItT :: NFData a => (State# RealWorld -> (# State# RealWorld, a #)) -> State# RealWorld -> (# State# RealWorld, (# Double, a #) #)
timeItT f s0 =
let !(# s1, start #) = unIO getCPUTime s0 in
let !(# s2, !res #) = f s1 in
let !_ = force res in
let !(# s3, end #) = unIO getCPUTime s2 in
let time :: Double = fromIntegral @Integer @Double (end - start) * 1e-12 in
(# s3, (# time, res #) #)
{-# INLINE timeItT #-}
{-# SPECIALIZE timeItT :: (State# RealWorld -> (# State# RealWorld, () #)) -> State# RealWorld -> (# State# RealWorld, (# Double, () #) #) #-}
{-# INLINE main' #-}
-- | Print all the elements of the given array within the specified bounds.
printArrayBounds :: Int# -> Int# -> Array# Value# -> State# RealWorld -> (# State# RealWorld, () #)
printArrayBounds low high arr s0 = go low s0
where
go x s0 =
case x ># high of
1# ->
let !_ = unsafePerformIO (putStrLn "") in
(# s0, () #)
0# ->
let !(# s1, _ #) = printIthOfArray arr x s0 in
go (x +# 1#) s1
_ -> undefined
printIthOfArray :: Array# Value# -> Int# -> State# RealWorld -> (# State# RealWorld, () #)
printIthOfArray arr i s0 =
let !(# val #) = indexArray# arr i in
let !_ = unsafePerformIO (putStr $! showValue# val <> " ") in
(# s0, () #)
{-# INLINE printIthOfArray #-}
{-# INLINE printArrayBounds #-}
{- ORMOLU_ENABLE -}
-------------------------------------------------------------
------------------ INTERPRETER ENTRY POINT ------------------
-------------------------------------------------------------
{- ORMOLU_DISABLE -}
eval :: Context -> State# RealWorld -> (# State# RealWorld, () #)
eval (Context stack callStack ip constants _ functions code) s0 =
let !codeSize = I32# (sizeofArray# code) in
catch# (go codeSize ip) printMachineStateOnError s0
where
go :: Int32 -> Int32 -> State# RealWorld -> (# State# RealWorld, () #)
go size ip@(I32# ip0) !s0
| ip >= size = (# s0, () #)
| otherwise =
case indexArray# code ip0 of
(# BYTECODE_RET #) ->
let !(# s1, I32# off #) = popStack callStack s0 in
#if DEBUG == 1
let !_ = unsafePerformIO (putStrLn $ "> Returning to address " <> show (I32# off)) in
#endif
go size (I32# off) s1
(# BYTECODE_PRIM #) ->
let !(# I32# idx #) = indexArray# code (ip0 +# 1#) in
let !(# VPrimitive# f _ #) = indexArray# constants idx in
#if DEBUG == 1
let !_ = unsafePerformIO (putStrLn $ "> Computing primitive at index " <> show (I32# idx)) in
#endif
let !(# s1, _ #) = f stack s0 in
go size (ip + 2) s1
(# BYTECODE_PUSH #) ->
let !(# I32# idx #) = indexArray# code (ip0 +# 1#) in
let !(# cst #) = indexArray# constants idx in
let !(# s1, _ #) = pushStack stack cst s0 in
#if DEBUG == 1
let !_ = unsafePerformIO (putStrLn $ "> Pushing constant #" <> show (I32# idx) <> " (" <> showValue# cst <> ")") in
#endif
go size (ip + 2) s1
(# BYTECODE_REDUCE #) ->
let !(# I32# idx #) = indexArray# code (ip0 +# 1#) in
let !(# I# off #) = indexArray# functions idx in
let !(# s1, _ #) = pushStack callStack (ip + 2) s0 in
#if DEBUG == 1
let !_ = unsafePerformIO (putStrLn $ "> Jumping to code offset " <> show (I32# off) <> " found at entry #" <> show (I32# idx)) in
#endif
go size (I32# off) s1
(# BYTECODE_UNQUOTE #) ->
let !(# s1, val #) = popStack stack s0 in
case val of
VQuote# off ->
let !(# s2, _ #) = pushStack callStack (ip + 1) s1 in
#if DEBUG == 1
let !_ = unsafePerformIO (putStrLn $ "> Unquotting quote found at offset " <> show off) in
#endif
go size off s2
val -> raise# $! TypeError $ "Not a quote: " <> Text.pack (showValue# val)
(# _ #) -> undefined
printMachineStateOnError e s0 = case fromException @EvalError e of
Just ex ->
let !(# s1, _ #) = unIO (putStrLn $ "/!\\ VM encountered exception: " <> show ex) s0 in
(# s1, () #)
Nothing -> raiseIO# e s0
{-# INLINE printMachineStateOnError #-}
{-# INLINE eval #-}
{- ORMOLU_ENABLE -}
-------------------------------------------
----------- BUILTIN REDUCERS --------------
-------------------------------------------
{- ORMOLU_DISABLE -}
pop :: Closure
pop stack s0 =
let !(# s1, _ #) = popStack stack s0 in
(# s1, void# #)
{-# INLINE pop #-}
add :: Closure
add stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
case (# v1, v2 #) of
(# VInteger# i1, VInteger# i2 #) -> pushStack stack (VInteger# (i2 +# i1)) s2
_ -> raise# $! TypeError $ "Expected two ints for reducer '+' (found " <> Text.pack (showValue# v1) <> ", " <> Text.pack (showValue# v2) <> ")"
{-# INLINE add #-}
times :: Closure
times stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
case (# v1, v2 #) of
(# VInteger# i1, VInteger# i2 #) -> pushStack stack (VInteger# (i2 *# i1)) s2
_ -> raise# $! TypeError $ "Expected two ints for reducer '+' (found " <> Text.pack (showValue# v1) <> ", " <> Text.pack (showValue# v2) <> ")"
{-# INLINE times #-}
sub :: Closure
sub stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
case (# v1, v2 #) of
(# VInteger# i1, VInteger# i2 #) -> pushStack stack (VInteger# (i2 -# i1)) s2
_ -> raise# $! TypeError $ "Expected two ints for reducer '-' (found " <> Text.pack (showValue# v1) <> ", " <> Text.pack (showValue# v2) <> ")"
{-# INLINE sub #-}
dup :: Closure
dup stack s0 =
let !(# s1, v #) = peekStack stack s0 in
pushStack stack v s1
{-# INLINE dup #-}
swap :: Closure
swap stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
let !(# s3, _ #) = pushStack stack v1 s2 in
pushStack stack v2 s3
{-# INLINE swap #-}
rot31 :: Closure
rot31 stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
let !(# s3, v3 #) = popStack stack s2 in
let !(# s4, _ #) = pushStack stack v1 s3 in
let !(# s5, _ #) = pushStack stack v3 s4 in
pushStack stack v2 s5
{-# INLINE rot31 #-}
rot3_1 :: Closure
rot3_1 stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
let !(# s3, v3 #) = popStack stack s2 in
let !(# s4, _ #) = pushStack stack v2 s3 in
let !(# s5, _ #) = pushStack stack v3 s4 in
pushStack stack v1 s5
{-# INLINE rot3_1 #-}
ifthenelse :: Closure
ifthenelse stack s0 =
let !(# s1, vElse #) = popStack stack s0 in
let !(# s2, vThen #) = popStack stack s1 in
let !(# s3, vCond #) = popStack stack s2 in
case vCond of
VBoolean# True# -> pushStack stack vThen s3
VBoolean# False# -> pushStack stack vElse s3
_ -> raise# $! TypeError "Expected boolean as condition for condition"
{-# INLINE ifthenelse #-}
eq :: Closure
eq stack s0 =
let !(# s1, v1 #) = popStack stack s0 in
let !(# s2, v2 #) = popStack stack s1 in
case (# v1, v2 #) of
(# VInteger# i1, VInteger# i2 #) -> pushStack stack (VBoolean# (Bool# (i1 ==# i2))) s2
(# VDouble# d1, VDouble# d2 #) -> pushStack stack (VBoolean# (Bool# (d1 ==## d2))) s2
_ -> undefined -- TODO
{-# INLINE eq #-}
-- unquote :: Closure
-- unquote stack s0 =
-- let !(# s1, v1 #) = popStack stack s0 in
-- case v1 of
-- VQuote# idx ->
-- _ -> throw# $! TypeError $ "Not a quote: " <> Text.pack (showValue# v1)
---------------------------------------------------------------------------
-------------------------------- FOR TESTS --------------------------------
---------------------------------------------------------------------------
{- ORMOLU_ENABLE -}
fact :: Expr
fact =
[ -- [n]
AIdentifier "dup", -- [n n]
AInteger 0, -- [n n 0]
AIdentifier "=", -- [n (n=0)]
AQuote
[ -- > [n]
AIdentifier "pop", -- > []
AInteger 1 -- > [1]
], -- [n (n=0) [1]]
AQuote
[ -- > [n]
AIdentifier "dup", -- > [n n]
AInteger 1, -- > [n n 1]
AIdentifier "-", -- > [n (n-1)]
AIdentifier "fact", -- > [n (n-1) fact]
-- AIdentifier "unquote", -- > [n (fact(n-1))]
AIdentifier "*" -- > [(n*fact(n-1))]
], -- [n (n=0) [1] [(n*fact(n-1))]]
AIdentifier "if", -- [n [1] | [(n*fact(n-1))]]
AIdentifier "unquote" -- [1 | n*fact(n-1)]
]
ack :: Expr
ack =
[ -- [m n]
AIdentifier "swap", -- [n m]
AIdentifier "dup", -- [n m m]
AInteger 0, -- [n m m 0]
AIdentifier "=", -- [n m (m=0)]
AQuote
[ -- > [n m]
AIdentifier "pop", -- > [n]
AInteger 1, -- > [n 1]
AIdentifier "+" -- > [(n+1)]
], -- [n m (m=0) [(n+1)]]
AQuote
[ -- > [n m]
AIdentifier "swap", -- > [m n]
AIdentifier "dup", -- > [m n n]
AInteger 0, -- > [m n n 0]
AIdentifier "=", -- > [m n (n=0)]
AQuote
[ -- >> [m n]
AIdentifier "pop", -- >> [m]
AInteger 1, -- >> [m 1]
AIdentifier "-", -- >> [(m-1)]
AInteger 1, -- >> [(m-1) 1)]
AIdentifier "ack" -- >> [(m-1) 1 ack]
], -- > [m n (n=0) [(ack(m-1,1))]]
AQuote
[ -- >> [m n]
AIdentifier "swap", -- >> [n m]
AIdentifier "dup", -- >> [n m m]
AIdentifier "rot31", -- >> [m n m]
AInteger 1, -- >> [m n m 1]
AIdentifier "-", -- >> [m n (m-1)]
AIdentifier "rot31", -- >> [(m-1) m n]
AInteger 1, -- >> [(m-1) m n 1]
AIdentifier "-", -- >> [(m-1) m (n-1)]
AIdentifier "ack", -- >> [(m-1) (ack(m,n-1))]
AIdentifier "ack" -- >> [(ack(m-1,ack(m,n-1)))]
], -- > [m n (n=0) [ack(m-1,1)] [ack(m-1,ack(m,n-1))]]
AIdentifier "if", -- > [m n ([ack(m-1,1)] | [ack(m-1,ack(m,n-1))])]
AIdentifier "unquote" -- > [(ack(m-1,1) | ack(m-1,ack(m,n-1)))]
], -- [n m (m=0) [(n+1)] [(ack(m-1,1) | ack(m-1,ack(m,n-1)))]]
AIdentifier "if", -- [n m [(n+1) | (ack(m-1,1) | ack(m-1,ack(m,n-1)))]]
AIdentifier "unquote" -- [(n+1 | ack(m-1,1) | ack(m-1,ack(m,n-1)))]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment