-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/
-- | A simple interpreter for arrayForth, the language used on GreenArrays chips.
--
-- This is a package for working with arrayForth. This is a variant of
-- Forth used by GreenArrays chips. This package contains an arrayForth
-- simulator, two different representations of arrayForth programs and
-- some utilities like parsing. It also supports synthesizing arrayForth
-- programs using MCMC. The basic idea is to find arrayForth programs by
-- taking a simple prior distribution of programs and using a randomized
-- hill-climbing algorithm to find a program fulfilling certain tests.
@package array-forth
@version 0.2.1.4
module Language.ArrayForth.Parse
-- | Possible ways the input string can be malformed.
data ParseError
BadOpcode :: String -> ParseError
NotSlot3 :: String -> ParseError
NotJump :: String -> ParseError
NoAddr :: String -> ParseError
BadNumber :: String -> ParseError
-- | Is the given string a valid number with no other tokens?
isNumber :: String -> Bool
-- | Tries to read a word, giving an error if it fails.
readWord :: Read a => String -> Either ParseError a
instance Show ParseError
module Language.ArrayForth.Opcode
-- | The 18-bit word type used by Greenarrays chips.
type F18Word = Word18
-- | Each F18A instruction, ordered by opcode.
data Opcode
Ret :: Opcode
Exec :: Opcode
Jmp :: Opcode
Call :: Opcode
Unext :: Opcode
Next :: Opcode
If :: Opcode
MinusIf :: Opcode
FetchP :: Opcode
FetchPlus :: Opcode
FetchB :: Opcode
Fetch :: Opcode
StoreP :: Opcode
StorePlus :: Opcode
StoreB :: Opcode
Store :: Opcode
MultiplyStep :: Opcode
Times2 :: Opcode
Div2 :: Opcode
Not :: Opcode
Plus :: Opcode
And :: Opcode
Or :: Opcode
Drop :: Opcode
Dup :: Opcode
Pop :: Opcode
Over :: Opcode
ReadA :: Opcode
Nop :: Opcode
Push :: Opcode
SetB :: Opcode
SetA :: Opcode
-- | The names of the different instructions, ordered by opcode.
names :: [String]
-- | All of the opcodes, in order.
opcodes :: [Opcode]
-- | Tries to read a given string as an opcode from the list of names.
readOpcode :: String -> Either ParseError Opcode
-- | Converts a word to an opcode. The word has to be < 32.
toOpcode :: F18Word -> Opcode
-- | Converts an Opcode to its 18-bit word representation.
fromOpcode :: Opcode -> F18Word
-- | Returns whether the given opcode is a jump instruction expecting an
-- address.
isJump :: Opcode -> Bool
-- | Can the given opcode go in the last slot?
slot3 :: Opcode -> Bool
-- | Estimates how long a given opcode will take to execute. Normal opcodes
-- take 1.5 nanoseconds where ones that access the memory take 5
-- nanoseconds.
opcodeTime :: Opcode -> Double
instance Eq Opcode
instance Bounded Opcode
instance Enum Opcode
instance Read Opcode
instance Show Opcode
module Language.ArrayForth.NativeProgram
-- | Represents a word in memory. This word can either contain opcodes,
-- opcodes and a jump address or just a constant number.
data Instrs
Instrs :: Opcode -> Opcode -> Opcode -> Opcode -> Instrs
Jump3 :: Opcode -> Opcode -> Opcode -> F18Word -> Instrs
Jump2 :: Opcode -> Opcode -> F18Word -> Instrs
Jump1 :: Opcode -> F18Word -> Instrs
Constant :: F18Word -> Instrs
-- | A program in the F18A instruction set.
type NativeProgram = [Instrs]
-- | Splits a list into chunks of at most four, breaking off a chunk
-- whenever it sees an element matching the given predicate. This is
-- useful for splitting a program along word boundaries, accounting for
-- jump addresses.
splitWords :: (a -> Bool) -> [a] -> [[a]]
-- | Read a whole program, splitting instructions up into words.
readNativeProgram :: String -> Either ParseError NativeProgram
-- | Returns the given instructions as an actual word. This assumes the
-- address is sized appropriately.
toBits :: Instrs -> F18Word
-- | Reads in a word as a set of opcodes.
fromBits :: F18Word -> Instrs
-- | Returns the opcodes in the given instruction word. A constant
-- corresponds to not having any opcodes.
toOpcodes :: Instrs -> [Opcode]
-- | Estimates the running time of the program in nanoseconds. This is
-- based on the numbers provided in the manual: faster instructions take
-- 1.5 nanoseconds and slower ones take 5. For now, this estimate ignores
-- control flow like ifs and loops.
runningTime :: NativeProgram -> Double
instance [overlap ok] Eq Instrs
instance [overlap ok] IsString NativeProgram
instance [overlap ok] Read NativeProgram
instance [overlap ok] Show Instrs
module Language.ArrayForth.Stack
-- | A stack containing only 0s.
empty :: Stack
-- | Pushes the given element on top of the stack, discarding the last
-- element.
push :: Stack -> F18Word -> Stack
-- | Pops the top of the stack, returning the value and the new stack.
pop :: Stack -> (Stack, F18Word)
-- | Push the given elements onto the stack one-by-one.
fill :: Stack -> [F18Word] -> Stack
data Stack
instance Eq Stack
instance Show Stack
-- | Defines the basic operations for reading and writing through ports.
--
-- Each core has four ports connecting it to its neighbors. The cores
-- around the edges have ports connected to IO devices. A Channel
-- is just a type containing the four ports that you can write to or read
-- from.
module Language.ArrayForth.Channel
-- | A channel representing the four communication directions a core may
-- use. In practice, these will either be hooked up to other cores or to
-- IO. Nothing represents no message; if there is a word, execution will
-- block.
data Channel
Channel :: Maybe F18Word -> Maybe F18Word -> Maybe F18Word -> Maybe F18Word -> Channel
right :: Channel -> Maybe F18Word
down :: Channel -> Maybe F18Word
left :: Channel -> Maybe F18Word
up :: Channel -> Maybe F18Word
-- | The four possible port directions.
data Port
R :: Port
D :: Port
L :: Port
U :: Port
-- | An empty channel has no reads or writes and doesn't block execution.
emptyChannel :: Channel
-- | Write to the ports specified by the given memory address. This will
-- clear all the channels not being written to (by setting them to
-- Nothing).
--
-- The ports to use are specified by bits 5–8 of the address. These bits
-- correspond respectively to up, left, down and right. Bits 5 and 7 are
-- inverted—0 turns the channel *on*.
writePort :: F18Word -> F18Word -> Channel
-- | Read the inputs from the ports specified by the given address. The
-- address is handled the same way as in writePort.
-- Returns Nothing if blocked on the read.
--
-- If more than one of the read ports has data, this currently just
-- chooses the first one based on the right, down, left, up order. I
-- don't know if this is the correct behavior—perhaps I should just xor
-- them together or something?
readPort :: F18Word -> Channel -> Maybe F18Word
instance Show Channel
instance Eq Channel
instance Show Port
instance Eq Port
instance Bounded Port
instance Enum Port
instance Monoid Channel
-- | This module defines types and functions for working with the state of
-- a single core.
--
-- The most important type is State, which contains all the information
-- about the core. This includes the registers, the memory, both stacks
-- and communication ports. Right now, it's just a big record; in the
-- future, I might make it more polymorphic using lenses.
--
-- There are also some useful types and functions for working with the
-- memory of a chip and its communication channels.
module Language.ArrayForth.State
-- | The chip's RAM, ROM and IO channels. The RAM and ROM should each
-- contain 64 words.
--
-- For now, input and output is split into two different types, even
-- though they're combined on the physical chip. I'm simply not sure how
-- to handle the case that both chips simultaneously write to the same
-- channel.
data Memory
Memory :: Vector Int -> Vector Int -> Channel -> Channel -> Memory
ram :: Memory -> Vector Int
rom :: Memory -> Vector Int
input :: Memory -> Channel
output :: Memory -> Channel
-- | Memory with RAM and ROM zeroed out and nothing on the communication
-- channels.
emptyMem :: Memory
-- | The number of words in memory. Both ram and rom are this size. For
-- some reason, the ram and rom address spaces are *double* this size
-- respectively, wrapping around at the half-way point.
memSize :: Num a => a
-- | A state representing the registers, stacks, memory and communication
-- channels of a core. Note that all the fields are strict; they should
-- also be unboxed thanks to -funbox-strict-fields (set in the
-- .cabal file).
--
-- For now, this is just a record; however, I might rewrite it to use
-- lenses in the near future.
data State
State :: !F18Word -> !F18Word -> !F18Word -> !F18Word -> !F18Word -> !F18Word -> !(Maybe F18Word) -> !Stack -> !Stack -> !Memory -> State
a :: State -> !F18Word
b :: State -> !F18Word
p :: State -> !F18Word
r :: State -> !F18Word
s :: State -> !F18Word
t :: State -> !F18Word
-- | the i register can be Nothing if it is blocked on a
-- communication port.
i :: State -> !(Maybe F18Word)
dataStack :: State -> !Stack
returnStack :: State -> !Stack
memory :: State -> !Memory
-- | The state corresponding to a core with no programs loaded and no
-- instructions executed.
startState :: State
-- | Increment the p register for the given state. If p is in RAM or ROM,
-- this wraps p as appropriate. If p is in IO, this does nothing and p
-- remains unchanged.
incrP :: State -> State
-- | The next word of instructions to execute in the given state. Returns
-- Nothing if p is blocked on a communication channel.
next :: State -> Maybe Instrs
-- | Pops the data stack of the given state, updating s and
-- t.
dpop :: State -> (State, F18Word)
-- | Push a word onto the data stack, updating s and t.
dpush :: State -> F18Word -> State
-- | Pops the return stack of the given state, updating r.
rpop :: State -> (State, F18Word)
-- | Push a word onto the return stack, updating r.
rpush :: State -> F18Word -> State
-- | Force an address to be in range of memory: [0,64), also converting
-- between different integral types.
toMem :: (Integral a, Integral b) => a -> b
-- | Read the memory at a location given by a Forth word. Returns
-- Nothing if blocked on a communication channel.
(!) :: Memory -> F18Word -> Maybe F18Word
-- | Set the memory using Forth words. A state with anything in the output
-- channel remains blocked until one of the active ports is read.
set :: State -> F18Word -> F18Word -> State
-- | Is the state is blocked because it has written to a port? Note that
-- this does *not* consider being blocked on a read!
blocked :: State -> Bool
-- | Loads the given program into memory at the given starting position.
setProgram :: F18Word -> NativeProgram -> State -> State
-- | Load the given memory words into the state starting at the given
-- address.
loadMemory :: F18Word -> [F18Word] -> State -> State
-- | Sets the input value at the given port.
sendInput :: Port -> F18Word -> State -> State
instance Show Memory
instance Eq Memory
instance Show State
module Language.ArrayForth.Interpreter
-- | A trace of a progam is the state after every word is executed.
type Trace = [State]
-- | Runs a single word's worth of instructions starting from the given
-- state, returning the intermediate states for each executed opcode.
wordAll :: Instrs -> State -> [State]
-- | Runs a single word's worth of instructions, returning only the final
-- state.
word :: Instrs -> State -> State
-- | Executes a single word in the given state, incrementing the program
-- counter and returning all the intermediate states.
stepAll :: State -> [State]
-- | Executes a single word in the given state, returning the last
-- resulting state.q
step :: State -> State
-- | Trace the given program, including all the intermediate states.
traceAll :: State -> Trace
-- | Returns a trace of the program's execution. The trace is a list of the
-- state of the chip after each step.
traceProgram :: State -> Trace
-- | Trace a program until it either hits four nops or all 0s.
stepProgram :: State -> Trace
-- | Runs the program unil it hits a terminal state, returning only the
-- resulting state.
eval :: State -> State
-- | Executes the specified program on the given state until it hits a
-- "terminal" word--a word made up of four nops or all 0s.
runNativeProgram :: State -> NativeProgram -> State
-- | Estimates the execution time of a program trace.
countTime :: Trace -> Double
-- | Checks that the program trace terminated in at most n steps, returning
-- Nothing otherwise.
throttle :: Int -> Trace -> Either Trace Trace
-- | Does the given opcode cause the current word to stop executing?
endWord :: Opcode -> Bool
-- | Extends the given trace by a single execution step. The trace cannot
-- be empty.
run :: Opcode -> [State] -> [State]
-- | Executes an opcode on the given state. If the state is blocked on some
-- communication, nothing changes.
execute :: Opcode -> State -> State
-- | Execute a jump instruction to the given address.
jump :: Opcode -> F18Word -> State -> State
module Language.ArrayForth.Distance
type Distance = Sum Double
-- | Counts the number of bits that differ between two numbers.
countBits :: (Integral n, Bits n) => n -> n -> Int
-- | Return a distance function that counts the different bits between the
-- given registers. You could use it like `compareRegisters [s, t]`.
registers :: [State -> F18Word] -> (State -> State -> Distance)
-- | Returns a distance function that counts the different bits between the
-- given memory locations.
locations :: [F18Word] -> (State -> State -> Distance)
-- | Returns a score that counts the number of matching states according to
-- some projection function.
matching :: Eq a => (State -> a) -> (Trace -> Trace -> Distance)
instance Score Distance
module Language.ArrayForth.Program
data Addr
Concrete :: F18Word -> Addr
Abstract :: String -> Addr
-- | Represents a single instruction as viewed by the synthesizer. This can
-- be an opcode, a numeric literal or a token representing an unused
-- slot.
data Instruction
Opcode :: Opcode -> Instruction
Jump :: Opcode -> Addr -> Instruction
Number :: F18Word -> Instruction
Label :: String -> Instruction
Unused :: Instruction
-- | A program to be manipulated by the MCMC synthesizer
type Program = [Instruction]
-- | Tries to parse the given string as an instruction, which can either be
-- a number, an opcode or "_" representing Unused.
readInstruction :: String -> Either ParseError Instruction
-- | Reads a program in the synthesizer's format.
readProgram :: String -> Either ParseError Program
-- | Takes a program as handled by the synthesizer and makes it native by
-- turning literal numbers into @p and fixing any issues with
-- instructions going into the last slot as well as prepending nops
-- before + instructions.
toNative :: Program -> NativeProgram
-- | Does this instruction force a word boundary?
boundary :: Instruction -> Bool
-- | Resolves labels into addresses, assuming the program starts at the
-- given memory location.
labels :: F18Word -> Program -> Program
-- | Insert extra nops to account for instructions that cannot go into the
-- last slot.
fixSlot3 :: Program -> Program
-- | Gets a synthesizer program from a native program. Currently does not
-- support jumps.
fromNative :: NativeProgram -> Program
-- | Runs a given program from the default starting state.
runProgram :: State -> Program -> State
-- | Loads the given synthesizer-friendly program into the given state.
load :: Program -> State -> State
instance [overlap ok] Eq Addr
instance [overlap ok] Eq Instruction
instance [overlap ok] IsString Program
instance [overlap ok] Read Program
instance [overlap ok] Show Instruction
instance [overlap ok] Show Addr
module Language.ArrayForth.Synthesis
-- | A score type that contains a correctness value and a performance
-- value.
data DefaultScore
DefaultScore :: Double -> Double -> DefaultScore
-- | Creates an evaluation function from a spec, a set of inputs and a
-- function for comparing program traces.
trace :: Monoid score => Program -> [State] -> (Trace -> Trace -> score) -> Program -> score
-- | Using a given correctness measure, produce a score also containing
-- performance.
withPerformance :: Score s => (Trace -> Trace -> s) -> (Trace -> Trace -> DefaultScore)
-- | Given a specification program and some inputs, evaluate a program
-- against the specification for both performance and correctness.
-- Normalize the score based on the number of test cases.
evaluate :: Program -> [State] -> (State -> State -> Distance) -> Program -> DefaultScore
-- | The default distribution of instructions. For now, we do not support
-- any sort of jumps. All the other possible instructions along with
-- constant numbers and unused slots are equally likely. The numeric
-- value of constants is currently a uniform distribution over 18-bit
-- words.
defaultOps :: Distr Instruction
pairs :: [(Instruction, Instruction)]
removePairs :: Distr Instruction -> Mutation Program
-- | The default mutations to try. For now, this will either change an
-- instruction or swap two instructions in the program, with equal
-- probability.
defaultMutations :: Mutation Program
instance [overlap ok] Ord DefaultScore
instance [overlap ok] Eq DefaultScore
instance [overlap ok] Random F18Word
instance [overlap ok] Monoid DefaultScore
instance [overlap ok] Show DefaultScore
instance [overlap ok] Score DefaultScore
-- | This module defines a type representing the location of a core in the
-- 8 × 18 grid.
--
-- All of the actually interesting code is in the typeclass instances.
module Language.ArrayForth.Core
-- | The address of a core. There are 144 cores in an 8 × 18 array. The
-- address has the row number followed by the column number.
--
-- As a string, the core addresses are displayed as a single three-digit
-- number, just like in the GreenArray documentation. So Core 7
-- 17 becomes "717".
--
-- Core addresses behave like numbers: you can use numeric literals and
-- add them together. For example, [0..] :: [Core] gets you the
-- list of all the core addresses. (move core = core + Core 1 1)
-- is a function that moves you up and over by one core.
data Core
Core :: !(ℤ / 8) -> !(ℤ / 18) -> Core
-- | Returns all the neighbors of a core. Most cores have four neighbors;
-- the ones along the edges only have three and the ones at the corners
-- two.
--
-- They always come in the order right, down, left up, with Nothing in
-- place of non-existant cores.
neighbors :: Core -> [Maybe Core]
instance Ord Core
instance Eq Core
instance Num Core
instance Bounded Core
instance Enum Core
instance Show Core