All Projects → skx → Go.vm

skx / Go.vm

Licence: gpl-2.0
A simple virtual machine - compiler & interpreter - written in golang

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Go.vm

Lioness
The Lioness Programming Language
Stars: ✭ 155 (-12.92%)
Mutual labels:  compiler, interpreter, virtual-machine
Cub
The Cub Programming Language
Stars: ✭ 198 (+11.24%)
Mutual labels:  compiler, interpreter, virtual-machine
Quickjs
The official repo is at bellard/quickjs.
Stars: ✭ 1,429 (+702.81%)
Mutual labels:  compiler, interpreter, virtual-machine
Openj9
Eclipse OpenJ9: A Java Virtual Machine for OpenJDK that's optimized for small footprint, fast start-up, and high throughput. Builds on Eclipse OMR (https://github.com/eclipse/omr) and combines with the Extensions for OpenJDK for OpenJ9 repo.
Stars: ✭ 2,802 (+1474.16%)
Mutual labels:  compiler, interpreter, virtual-machine
V8
The official mirror of the V8 Git repository
Stars: ✭ 18,808 (+10466.29%)
Mutual labels:  compiler, interpreter, virtual-machine
Umka Lang
Umka: a statically typed embeddable scripting language
Stars: ✭ 308 (+73.03%)
Mutual labels:  compiler, interpreter, virtual-machine
Swift Lispkit
Interpreter framework for Lisp-based extension and scripting languages on macOS and iOS. LispKit is based on the R7RS standard for Scheme. Its compiler generates bytecode for a virtual machine. LispKit is fully implemented in Swift 5.
Stars: ✭ 228 (+28.09%)
Mutual labels:  compiler, interpreter, virtual-machine
Ph7
An Embedded Implementation of PHP (C Library)
Stars: ✭ 422 (+137.08%)
Mutual labels:  compiler, interpreter, virtual-machine
Cymbal
Yet another Rust implementation of the Monkey language from "Writing an Interpreter in Go" and "Writing a Compiler in Go"
Stars: ✭ 49 (-72.47%)
Mutual labels:  compiler, interpreter, virtual-machine
Fanx
A portable programming language
Stars: ✭ 101 (-43.26%)
Mutual labels:  compiler, virtual-machine
Libforth
libforth: A small Forth interpreter that can be used as a library written in c99
Stars: ✭ 107 (-39.89%)
Mutual labels:  interpreter, virtual-machine
Brain
An esoteric programming language compiler on top of LLVM based on Brainfuck
Stars: ✭ 112 (-37.08%)
Mutual labels:  compiler, interpreter
Selfie
An educational software system of a tiny self-compiling C compiler, a tiny self-executing RISC-V emulator, and a tiny self-hosting RISC-V hypervisor.
Stars: ✭ 1,318 (+640.45%)
Mutual labels:  compiler, virtual-machine
Wasm Forth
A Forth implementation compiling to WebAssembly.
Stars: ✭ 92 (-48.31%)
Mutual labels:  compiler, interpreter
Feral
Feral programming language reference implementation
Stars: ✭ 89 (-50%)
Mutual labels:  compiler, interpreter
Cperl
A perl5 with classes, types, compilable, company friendly, security
Stars: ✭ 125 (-29.78%)
Mutual labels:  compiler, interpreter
Tinyscript
自制的一个编译器, 用于学习,完整实现了词法分析,语法分析,中间代码(SSA)生成,机器码生成,和基于寄存器的虚拟机
Stars: ✭ 132 (-25.84%)
Mutual labels:  compiler, virtual-machine
Tagha
Minimal, low-level, fast, and self-contained register-based bytecode virtual machine/runtime environment.
Stars: ✭ 79 (-55.62%)
Mutual labels:  interpreter, virtual-machine
Simple
The Simple Intelligent and Modular Programming Language and Environment
Stars: ✭ 120 (-32.58%)
Mutual labels:  interpreter, virtual-machine
Kivm
🌟This is a pure C++ implementation of Java Virtual Machine (only Java 8 is supported). Inspired by Hotspot In Action.
Stars: ✭ 137 (-23.03%)
Mutual labels:  interpreter, virtual-machine

Go Report Card license Release

go.vm

This project is a golang based compiler and interpreter for a simple virtual machine. It is a port of the existing project:

You can get a feel for what it looks like by referring to either the parent project, or the examples contained in this repository.

This particular virtual machine is intentionally simple, but despite that it is hopefully implemented in a readable fashion. "Simplicity" here means that we support only a small number of instructions, and the 16-registers the virtual CPU possesses can store strings and integers, but not floating-point values.

If you want to see a real virtual machine, interpreting a scripting language, which you can embed inside your Golang applications:

Installation

There are two ways to install this project from source, which depend on the version of the go version you're using.

If you prefer you can fetch a binary from our release page. Currently there is only a binary for Linux (amd64) due to the use of cgo in our dependencies.

Build without Go Modules (Go before 1.11)

go get -u github.com/skx/go.vm

Build with Go Modules (Go 1.11 or higher)

git clone https://github.com/skx/go.vm ;# make sure to clone outside of GOPATH
cd go.vm
go install

Usage

Once installed there are three sub-commands of interest:

  • go.vm compile $file.in
    • Compiles the given program into bytecode.
  • go.vm execute $file.raw
    • Given the path to a file of bytecode, then interpret it.
  • go.vm run $file.in
    • Compiles the specified program, then directly executes it.

So to compile the input-file examples/hello.in into bytecode:

 $ go.vm compile examples/hello.in

Then to execute the resulting bytecode:

 $ go.vm execute examples/hello.raw

Or you can handle both steps at once:

 $ go.vm run examples/hello.in

Opcodes

The virtual machine has 16 registers, each of which can store an integer or a string. For example to set the first two registers you might write:

 store #0, "This is a string"
 store #1, 0xFFFF

In addition to this there are several mathematical operations which have the general form:

 $operation $result, $src1, $src2

For example to add the contents of register #1 and register #2, storing the result in register #0 you would write:

 add #0, #1, #2

Strings and integers may be displayed to STDOUT via:

 print_str #1
 print_int #3

Control-flow is supported via call, ret (for subroutines) and jmp for absolute jumps. You can also use the Z-flag which is set by comparisons and the inc/dec instructions and make conditional jumps:

    store #1, 0x42
    cmp #1, 0x42
    jmpz ok

    store #1, "Something weird happened!\n"
    print_str #1
    exit
  :ok
    store #1, "Comparing register #01 to 0x42 succeeded!\n"
    print_str #1
    exit

Further instructions are available and can be viewed beneath examples/. The instruction-set is pretty limited, for example there is no notion of reading from STDIN - however this is supported via the use of traps, as documented below.

Notes

Some brief notes on parts of the code / operation:

The compiler

The compiler is built in a traditional fashion:

  • Input is split into tokens via lexer.go
    • This uses the token.go for the definition of constants.
  • The stream of tokens is iterated over by compiler.go
    • This uses the constants in opcode.go for the bytecode generation.

The approach to labels is the same as in the inspiring-project: Every time we come across a label we output a pair of temporary bytes in our bytecode. Later, once we've read the whole program and assume we've found all existing labels, we go back up and fix the generated addresses.

You can use the dump command to see the structure the lexer generates:

 $ go.vm dump ./examples/hello.in
 {STORE store}
 {IDENT #1}
 {, ,}
 {STRING Hello, World!
 }
 {PRINT_STR print_str}
 {IDENT #1}
 {EXIT exit}

The interpreter

The core of the interpreter is located in the file cpu.go and is as simple and naive as you would expect. There are some supporting files in the same directory:

Changes

Compared to the original project there are two main changes:

  • The DB/DATA operation allows storing string data directly in the generated bytecode.
  • There is a notion of traps.
    • Rather than defining opcodes for complex tasks it is now possible to callback into the CPU-emulator to do work.

DB/DATA Changes

For example in simple.vm project this is possible:

DB 0x01, 0x02,

But this is not:

 DB "This is a string, with terminator to follow"
 DB 0x00

go.vm supports this, and it is demonstrated in examples/peek-strlen.in.

Traps

The instruction int can be used to call back to the emulator to do some work on behalf of a program. The following traps are currently defined & available:

  • int 0x00
    • Set the contents of the register #0 with the length of the string in register #0.
  • int 0x01
  • int 0x02
    • Update the (string) contents of register #0 to remove any trailing newline.
    • See examples/trap.box.in.

Adding your own trap-functions should be as simple as editing cpu/traps.go.

Fuzzing

Fuzz-testing is a powerful technique to discover bugs, in brief it consists of running a program with numerous random inputs and waiting for it to die.

I've fuzzed this repository repeatedly via go-fuzz and fixed a couple of minor issues.

Note however that fuzzing will trigger some expected failures. Our virtual CPU has only 16 registers, so for example a program that tries to set register #30 to a particular value is invalid, and will terminate the virtual machine.

Because fuzzing involves using "random" input it is possible there are bugs lurking in the virtual-machine which I've not been lucky enough to catch, so if you wish to fuzz this is how you do it. First of all install the tool:

 $ go get github.com/dvyukov/go-fuzz/go-fuzz
 $ go get github.com/dvyukov/go-fuzz/go-fuzz-build

Now you can build the interpreter using it:

 $ go-fuzz-build github.com/skx/go.vm/fuzz

Finally you can launch the fuzzer:

 $ go-fuzz -nprocs=1 -bin=fuzz-fuzz.zip -workdir=workdir

Interesting results will appear in workdir/crashers/ some crashes will be invalid and you can see that via the *.output files which contain STDOUT from the run (more or less). For example this is an "expected" failure:

 $ cat workdir/crashers/92108737efbd0ac6b42ae4473db5a257314b36cf.output
 Register 99 out of range
 exit status 1

See-Also

The original virtual-machine compiler and interpreter is available from the following repository:

In addition to that you can find a real virtual-machine inside the embedded scripting engine I wrote, also for golang. In that case a scripting language is parsed and converted into a series of bytecode instructions, which are executed by a virtual machine. Similar to this project, but the bytecode operations are more complex and high-level:

If you're interested in compilers, and interpreters, generally you might enjoy these other projects too:

Github Setup

This repository is configured to run tests upon every commit, and when pull-requests are created/updated. The testing is carried out via .github/run-tests.sh which is used by the github-action-tester action.

Releases are automated in a similar fashion via .github/build, and the github-action-publish-binaries action.

Steve

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].