GIECS Documentation¶
Giecs Intermediate Environment for Concatenative, Stack-oriented programming.
GIECS allows you to create a custom intermediate environment or virtual machine for scripting and compiling. Its design is concatenative and stack-based, making it easy to implement frontends for LISP, Forth, Joy or functional languages like Haskell.
Overview¶
Motivation¶
“Programming languages appear to be in trouble. Each successive language incorporates, with a little cleaning up, all the features of its predecessors plus a few more.“ — John Backus, 1978
Use Cases¶
Language Interopability, Compilers, Interpreters, Language Design
Getting Started¶
This Section shows exemplary how to use GIECS for writing a Brainfuck-Interpreter.
Building GIECS¶
GIECS is a C++ header-only template library. In order to use it, you only have to set your includepath to the root of the giecs-directory.
Requirements¶
- C++11 compiler
- boost >= 1.60
Using Boost.Build¶
The testcases and examples are built using Boost.Build. To include GIECS using Boost.Build, the following option in your jamfile is enough:
<library>/giecs
Overall, Boost.Build requires the environment-variable BOOST_ROOT to be set:
export BOOST_ROOT=<your boost installation>
Run Unittests¶
b2 test
Build Interpreters¶
b2 languages/brainfuck
b2 languages/forth
b2 languages/lisp
Writing A Brainfuck Interpreter¶
The Virtual Machine¶
The Complete VM-Implementation weights ~100 lines of readable C++.
The VM consists of an Instruction -type and an Operator, which executes the Instructions. Brainfuck-Instructions are simple, they only need an Opcode (representing ,, ., +, -, <, >, [, ]) and an optional data register for jump-instructions.
First lets define our VM-Opcodes with an enum:
-
enum
brainfuck::
Opcode
¶ Opcodes for the Virtual-Brainfuck-Machine
Values:
-
in
¶ Get character from input and write it to current cell.
-
out
¶ Write current cell as character to output.
-
inc
¶ Increment the current cell.
-
dec
¶ Decrement the current cell.
-
next
¶ Move cell-pointer right.
-
prev
¶ Move cell-pointer left.
-
jmp
¶ Jump unconditionally.
-
jz
¶ Jump, if current cell is zero.
-
Now we need to represent the state of the VM. The neccesary values are members of brainfuck::VM: Tape and Program pointers have templated types and are expected to behave as iterators on a container (e.g. std::array). It contains a struct which defines Instructions. When the Operator (defined later) wants to execute an instruction, the method fetch is called, which updates the VM-State (in this case load the register) and returns the Opcode to be executed. Note that Data is the type for the VM-State. The last thing to do is to get the Instructions. This is done by CodeAccessor.
- template <typename TapeIterator, typename ProgramIterator>
-
class
brainfuck::
VM
¶ Stores the state of the virtual-machine and provides all operations.
Public Functions
-
brainfuck::VM::GIECS_CORE_OPERATOR(Operator, ((Opcode::in, TAPE_FN(std::cin >> * tape )))((Opcode::out, TAPE_FN(std::cout<< (char)* tape )))((Opcode::inc, TAPE_FN(++(* tape ))))((Opcode::dec, TAPE_FN(--(* tape ))))((Opcode::next, TAPE_FN(++ tape )))((Opcode::prev, TAPE_FN(-- tape )))((Opcode::jmp, DATA_FN(std::advance(d.pc, d.jmp_off))))((Opcode::jz, TAPE_FN(if(* tape ==0) std::advance(d.pc, d.jmp_off)))))
Public Members
-
TapeIterator
tape
¶ Pointer to current memory-cell.
-
ProgramIterator
pc
¶ Program counter.
-
std::iterator_traits<ProgramIterator>::difference_type
jmp_off
¶ Register (used by jmp and jz)
-
class
CodeAccessor
¶ Decodes the program-stream to instructions and buffers them. Emulates a queue.
Inherits from boost::circular_buffer< Instruction >
-
struct
Instruction
¶ Instruction to be executed, gets decoded by CodeAccessor from program-stream
Public Types
-
template<>
usingData
= VM<TapeIterator, ProgramIterator>¶
Public Functions
-
template<>
-
Opcode fetch(Data& data)
{
data.jmp_off = this->jmp_off;
return this->op;
}
Now we need to implement the opcodes using an Operator. This generates a class which can be used with giecs::eval.
#define DATA_FN(def) ([](typename Instruction::Data& d){ def ; })
#define TAPE_FN(def) DATA_FN( auto& tape = d.tape; def )
GIECS_CORE_OPERATOR(Operator,
((Opcode::in, TAPE_FN(std::cin >> *tape)))
((Opcode::out, TAPE_FN(std::cout << (char)*tape)))
((Opcode::inc, TAPE_FN(++(*tape))))
((Opcode::dec, TAPE_FN(--(*tape))))
((Opcode::next, TAPE_FN(++tape)))
((Opcode::prev, TAPE_FN(--tape)))
((Opcode::jmp, DATA_FN(_jmp(d))))
((Opcode::jz, TAPE_FN(if(*tape == 0) _jmp(d))))
);
Compiling Syntax for the VM¶
See syntax.hpp
License¶
Copyright (c) 2016, Michael Sippel. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name “GIECS” nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.