GIECS Documentation

Giecs Intermediate Environment for Concatenative, Stack-oriented programming.

https://img.shields.io/travis/michaelsippel/giecs/master.svg?label=master https://img.shields.io/travis/michaelsippel/giecs/dev.svg?label=dev https://img.shields.io/github/tag/michaelsippel/giecs.svg https://img.shields.io/github/license/michaelsippel/giecs.svg

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

Components

Virtual Memory

Program Evaluation

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

See full code version

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 >

Public Functions

template<>
CodeAccessor(ProgramIterator &begin_, ProgramIterator const &end_)
template<>
Instruction &front(void)

Return
the next instruction

template<>
bool empty(void) const

Return
if end-of-program is reached

struct Instruction

Instruction to be executed, gets decoded by CodeAccessor from program-stream

Public Types

template<>
using Opcode = brainfuck::Opcode
template<>
using Data = VM<TapeIterator, ProgramIterator>

Public Functions

template<>
Opcode fetch(Data &data)

Get the opcode for this instruction and update the vm-state (load register).

Public Members

template<>
Opcode op
template<>
std::iterator_traits<ProgramIterator>::difference_type jmp_off
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.