Port of abfc macro-language-to-brainfuck compiler to Haskell.
This includes its own Parsec parser for the macro language.
Successfully compiles self_hosting_bf_compiler_x86_64_test.py. The resulting brainfuck program functions correctly when used in abfc's pipeline.
mainis the main definition for the command-line programParseris the parsec macro parserMacrosis the datatype for the compiler's internal macro representationCompileperforms macro-level transformations : rewriting local statements, if blocks, while blocks, and inlining all function calls, starting from the body of the main macro. This results in one huge list of simplified statements.Evalevaluates the list of simplified statements generated byCompile. It deals mainly with managing the environments (i.e. scopes) of the program. It handles local variable allocation and release when scopes are entered and exited, and resolves lookups of variable values by name (to either allocated memory addresses or constant values). The only thingEvaldoesn't do is handle built-in calls - the evaluation of built in calls is delegated toBuiltInDispatch.Envis a nested environment data structure used byEvalto manage variable scopes and lookup.Allocatoris the data structure used to track the locations of allocated addresses. It is used by bothEvalandMachineCodegento allocate local variables to the stack.ResolvedArgsis a simple data type used byEvalto pass resolved built-in function call arguments toBuiltInDispatch.BuiltInDispatchdefines the list of built-in function calls that are recognised. These calls are dispatched to functions inMachineCodegen.MachineCodegendefines the built-in functions. These functions are basic operations such asMOVE,LOGICAL_NOT,READ,BEGIN_LOOPthat take a small number of arguments (typically stack offsets for local variable addresses, but sometimes int or string constants) and map them to actions. Actions update the code generation state - this is a 3-tuple of type(String, Machine, Allocator), where the first element tracks the generated opcodes, the second element tracks the state of the simple brainfuck stack machine (i.e. the stack and data pointers, and the stack of loop addresses), while the third element is the Allocator in charge of assigning and tracking stack addresses of local variables. TheAllocatorstate needs to be tracked because many of the built-in functions require allocation of local variables for temporaries. The built-in functions themselves are defined as the composition of lists of basic actions that act upon the code generation state. This maps quite naturally over to the semantics of the stack-machine and is relatively boilerplate-free.Machinedefines the data type for the brainfuck stack-machine.
- learn how to eliminate boilerplate for "stateful" bits of computation in Machine, Allocator, Env, Eval, etc. State monad?
- refine the syntax accepted by the parser to be nicer - e.g. not an ugly python dsl