Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/skx/math-compiler

A simple intel/AMD64 assembly-language compiler for mathematical operations
https://github.com/skx/math-compiler

compiler golang maths reverse-polish toy trivial

Last synced: 7 days ago
JSON representation

A simple intel/AMD64 assembly-language compiler for mathematical operations

Awesome Lists containing this project

README

        

[![Go Report Card](https://goreportcard.com/badge/github.com/skx/math-compiler)](https://goreportcard.com/report/github.com/skx/math-compiler)
[![license](https://img.shields.io/github/license/skx/math-compiler.svg)](https://github.com/skx/math-compiler/blob/master/LICENSE)

Table of Contents
=================

* [math-compiler](#math-compiler)
* [Installation](#installation)
* [Quick Overview](#quick-overview)
* [About Our Output](#about-our-output)
* [Test Cases](#test-cases)
* [Debugging the generated programs](#debugging-the-generated-programs)
* [Possible Expansion?](#possible-expansion)
* [See Also](#see-also)
* [Github Setup](#github-setup)

# math-compiler

This project contains the simplest possible compiler, which converts mathematical operations into assembly language, allowing all the speed in your sums!

Because this is a simple project it provides only a small number of primitives:

* `+` - Plus
* `-` - Minus
* `*` - Multiply
* `/` - Divide
* `^` - Raise to a power
* `%` - Modulus
* `!` - Factorial
* `abs`
* `sin`
* `cos`
* `tan`
* `sqrt`
* Stack operations:
* `swap` - Swap the top-two items on the stack
* `dup` - Duplicate the topmost stack-entry.
* Built-in constants:
* `e`
* `pi`

Despite this toy-functionality there is a lot going on, and we support:

* Full RPN input
* Floating-point numbers (i.e. one-third multipled by nine is 3)
* `1 3 / 9 *`
* Negative numbers work as you'd expect.

Some errors will be caught at run-time, as the generated code has support for:

* Detecting, and preventing, division by zero.
* Detecting insufficient arguments being present upon the stack.
* For example this program is invalid `3 +`, because the addition operator requires two operands. (i.e. `3 4 +`)

## Installation

If you just need a binary you can find them upon the [project release page](https://github.com/skx/math-compiler/releases), however if you wish to build and install locally you can do that in either of the standard ways:

1. Install from the latest revision:

```sh
$ go install github.com/skx/math-compiler@master
```

2. Or you can clone the source, and build from it:

```sh
$ git clone https://github.com/skx/math-compiler
$ cd math-compiler
$ go install .
```

## Quick Overview

The intention of this project is mostly to say "I wrote a compiler", because I've already [experimented with a language](https://github.com/skx/monkey/), an [embedded evaluation engine](https://github.com/skx/evalfilter/), and [implemented a BASIC interpreter](https://github.com/skx/gobasic/). The things learned from those projects were pretty useful, even if the actual results were not so obviously useful in themselves.

Because there are no shortages of toy-languages, and there is a lot of complexity in writing another for no real gain, I decided to just focus upon a simple core:

* Allowing "maths stuff" to be "compiled".

In theory this would allow me to compile things like this:

2 + ( 4 * 54 )

However I even simplified that, via the use of a "[Reverse Polish](https://en.wikipedia.org/wiki/Reverse_Polish_notation)" notation, so if you want to run that example you'd enter the expression as:

4 54 * 2 +

## About Our Output

The output of `math-compiler` will be an assembly-language file, which then needs to be compiled before it may be executed.

Given our previous example of `2 + ( 4 * 54)` we can compile & execute that program like so:

$ math-compiler '4 54 * 2+' > sample.s
$ gcc -static -o sample ./sample.s
$ ./sample
Result 218

There you see:

* `math-compiler` was invoked, and the output written to the file `sample.s`.
* `gcc` was used to assemble `sample.s` into the binary `sample`.
* The actual binary was then executed, which showed the result of the calculation.

If you prefer you can also let the compiler do the heavy-lifting, and generate an executable for you directly. Simply add `-compile`, and execute the generated `a.out` binary:

$ math-compiler -compile=true '2 8 ^'
$ ./a.out
Result 256

Or to compile __and__ execute directly:

$ math-compiler -run '3 45 * 9 + 12 /'
Result 12

## Test Cases

The codebase itself contains some simple test-cases, however these are not comprehensive as a large part of our operation is merely to populate a simple template-file, and it is hard to test that.

To execute the integrated tests use the standard go approach:

$ go test [-race] ./...

In addition to the internal test cases there are also some functional tests
contained in [test.sh](test.sh) - these perform some calculations and verify
they produce the correct result.

frodo ~/go/src/github.com/skx/math-compiler $ ./test.sh
...
Expected output found for '2 0 ^' [0]
Expected output found for '2 1 ^' [2]
Expected output found for '2 2 ^' [4]
Expected output found for '2 3 ^' [8]
Expected output found for '2 4 ^' [16]
Expected output found for '2 5 ^' [32]
...
Expected output found for '2 30 ^' [1073741824]
...

### Debugging the generated programs

If you run the compiler with the `-debug` flag a breakpoint will be generated
immediately at the start of the program. You can use that breakpoint to easily
debug the generated binary via `gdb`.

For example you might generate a program "`2 3 + 4 /`" like so:

$ math-compiler -compile -debug '2 3 + 4 /'

Now you can launch that binary under `gdb`, and run it:

$ gdb ./a.out
(gdb) run
..
Program received signal SIGTRAP, Trace/breakpoint trap.
0x00000000006b20cd in main ()

Dissassemble the code via `disassemble`, and step over instructions one at a time via `stepi`. If your program is long you might see a lot of output from the `disassemble` step:

(gdb) disassemble
Dump of assembler code for function main:
0x00000000006b20cb: push %rbp
0x00000000006b20cc: int3
=> 0x00000000006b20cd: fldl 0x6b20b3
0x00000000006b20d4: fstpl 0x6b2090
0x00000000006b20db: mov 0x6b2090,%rax
0x00000000006b20e3: push %rax
0x00000000006b20e4: fldl 0x6b20bb
0x00000000006b20eb: fstpl 0x6b2090
0x00000000006b20f2: mov 0x6b2090,%rax
0x00000000006b20fa: push %rax
...
...

You can set a breakpoint at a line in the future, and continue running till
you hit it, with something like this:

(gdb) break *0x00000000006b20fa
(gdb) cont

Once there inspect the registers with commands like these two:

(gdb) print $rax
(gdb) info registers

My favourite is `info registers float`, which shows you the floating-point
values as well as the raw values:

(gdb) info registers float
st0 0.140652076786443369638 (raw 0x3ffc90071917a6263000)
st1 0 (raw 0x00000000000000000000)
st2 0 (raw 0x00000000000000000000)
...
...

Further documentation can be found in the `gdb` manual, which is worth reading
if you've an interest in compilers, debuggers, and decompilers.

## Possible Expansion?

The obvious thing to improve in this compiler is to add support for more operations. At the moment support for the most obvious/common operations is present, but perhaps more functions could be added.

## See Also

If you enjoyed this repository, then you might also enjoy my compiler for the [Brainfuck](https://en.wikipedia.org/wiki/Brainfuck) language. The compiler there compiles brainfuck programs to x86-64 assembly-language:

* [https://github.com/skx/bfcc](https://github.com/skx/bfcc)

## 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](.github/run-tests.sh) which is used by the
[github-action-tester](https://github.com/skx/github-action-tester) action.

Releases are automated in a similar fashion via [.github/build](.github/build),
and the [github-action-publish-binaries](https://github.com/skx/github-action-publish-binaries) action.

Steve
--