Skip to content

Commit

Permalink
Merge pull request #11 from thomscoder/expandOperations
Browse files Browse the repository at this point in the history
Expand operations
  • Loading branch information
thomscoder authored Nov 13, 2022
2 parents 2d0c3b9 + 95c4a4c commit 009b20c
Show file tree
Hide file tree
Showing 17 changed files with 160 additions and 30 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Luna is a reeeaaally tiny, yet expanding, compiler for WebAssembly Text Format,

<img src="https://i.ibb.co/hdcV1h0/Screenshot-2022-11-01-alle-17-47-06.png" alt="luna" />

It is so tiny that can only make additions lol.
It is so tiny that can only make the four operations (addition, subtraction, multiplication and division) with `i32` type numbers.

# Why ❓

Expand Down
13 changes: 13 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,19 @@ func Compile(ast []types.AstNode) Module {
functionBody = append(functionBody, sectionData{0x01})
functionBody = append(functionBody, encodeVector(functionBodyData)...)

// Internal instructions (e.g. i32.const)
case texts.InternalInstruction:
var localIndex uint
value, ok := node.Expression.Value.(string)
if !ok {
log.Fatal("Not string")
}

v, _ := strconv.Atoi(value)
localIndex = uint(v)

code = append(code, sectionData{node.MapTo}...)
code = append(code, omologateEncoded(localIndex)...)
}

}
Expand Down
23 changes: 21 additions & 2 deletions compiler/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@ func parseStatement(currentToken *iteratorEmulatorStruct, eatToken func(val stri
Expression: parseExpression(currentToken, eatToken, index),
MapTo: defaults.Opcodes["get_local"],
}
}
switch currentToken.token.Value {
case "i32.add":
eatToken("i32.add")
return types.AstNode{
Expand All @@ -151,6 +149,27 @@ func parseStatement(currentToken *iteratorEmulatorStruct, eatToken func(val stri
Expression: types.ExpressionNode{},
MapTo: defaults.Opcodes["i32_sub"],
}
case "i32.mul":
eatToken("i32.mul")
return types.AstNode{
Type: texts.FuncInstruction,
Expression: types.ExpressionNode{},
MapTo: defaults.Opcodes["i32_mul"],
}
case "i32.div":
eatToken("i32.div")
return types.AstNode{
Type: texts.FuncInstruction,
Expression: types.ExpressionNode{},
MapTo: defaults.Opcodes["i32_div"],
}
case "i32.const":
eatToken("i32.const")
return types.AstNode{
Type: texts.InternalInstruction,
Expression: parseExpression(currentToken, eatToken, index),
MapTo: defaults.Opcodes["i32_const"],
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/tokenizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var tokens = []string{
// A better and more robust regex could be implemented
var instructions = []string{
"local\\.get",
"i32\\.(add|sub)",
"i32\\.(add|sub|mul|div|const)",
}

var numTypes = []string{
Expand Down
4 changes: 4 additions & 0 deletions defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ var (
i32_and = 0x71
i32_add = 0x6a
i32_sub = 0x6b
i32_mul = 0x6c
i32_div = 0x6d
f32_add = 0x92
f32_sub = 0x93
f32_mul = 0x94
Expand All @@ -51,6 +53,8 @@ var Opcodes = map[string]interface{}{
"i32_eqz": i32_eqz,
"i32_add": i32_add,
"i32_sub": i32_sub,
"i32_mul": i32_mul,
"i32_div": i32_div,
"i32_eq": i32_eq,
"f32_eq": f32_eq,
"f32_lt": f32_lt,
Expand Down
23 changes: 18 additions & 5 deletions example/dist/bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ var Opcodes = {
i32_and: 113,
i32_add: 106,
i32_sub: 107,
i32_mul: 108,
i32_div: 109,
f32_add: 146,
f32_sub: 147,
f32_mul: 148,
Expand Down Expand Up @@ -190,14 +192,18 @@ function parseCodeSection(wasm) {
let numberOfLocals = wasm.readByte();
let instructions = [];
let locals = [];
let internals = [];
while (wasm.pos < wasm.data.length) {
const instruction = wasm.readByte();
instructions.push(instruction);
if (instruction == Opcodes.get_local) {
locals.push(wasm.readByte());
}
if (instruction == Opcodes.i32_const) {
internals.push(wasm.readByte());
}
}
code.push([locals, instructions]);
code.push({ locals, internals, instructions });
}
return code;
}
Expand Down Expand Up @@ -227,10 +233,11 @@ var Processor = class {
this.stack = [];
}
executeFunc() {
for (const instruction of this.func[1]) {
if (instruction == Opcodes.get_local) {
this.stack.push(this.params[this.func[0].shift()]);
}
for (const instruction of this.func.instructions) {
if (instruction == Opcodes.get_local)
this.stack.push(this.params[this.func.locals.shift()]);
if (instruction == Opcodes.i32_const)
this.stack.push(this.func.internals.shift());
this.#parseInstruction(instruction);
}
}
Expand All @@ -243,6 +250,12 @@ var Processor = class {
case Opcodes.i32_sub:
result = this.stack.reduce((prev, current) => prev - current);
return this.stack.push(result);
case Opcodes.i32_mul:
result = this.stack.reduce((prev, current) => prev * current, 1);
return this.stack.push(result);
case Opcodes.i32_div:
result = this.stack.reduce((prev, current) => prev / current);
return this.stack.push(result);
}
}
getResult() {
Expand Down
Empty file removed example/editor/editor.js
Empty file.
16 changes: 12 additions & 4 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ <h1>Luna</h1>
A sample compiler for WebAssembly Text Format.<br/>
Check the <a href="http://github.com/thomscoder/luna" target="_blank">github repository</a>
</p>
<small>Try to change the name of the export (e.g. "addNumbers" to "lunaAddition")</small>
<div class="container">
<div class="inner-container">
<small>Try to change the name of the export (e.g. "addNumbers" to "lunaAddition")</small>
<div id="code"></div>
<select id="instruction">
<option value="addition">Addition</option>
<option value="subtraction">Subtraction</option>
<option value="division">Division</option>
<option value="multiplication">Multiplication</option>
<option value="const">Const</option>
</select>
<button id="compile">Compile</button>
</div>
<div class="inner-container">
Expand All @@ -29,11 +36,11 @@ <h1>Luna</h1>
<div class="inner-container">
Calculate (add or subtract):
<div>
<label for="t-input-1">Num 1</label>
<label id="label-1" for="t-input-1">Num 1</label>
<input name="t-input-1" id="input-1" type="text" />
</div>
<div>
<label for="t-input-2">Num 2</label>
<label id="label-2" for="t-input-2">Num 2</label>
<input name="t-input-2" id="input-2" type="text" />
</div>
<div id="btn-result-container"><button id="btn">Result</button></div>
Expand All @@ -46,7 +53,8 @@ <h1>Luna</h1>
as it defines some global objects
-->
<script src="./js/wasm_exec.js"></script>
<script src="./editor/editor.js"></script>
<script type="module" src="./js/index.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/addon/mode/simple.min.js" integrity="sha512-CGM6DWPHs250F/m90YZ9NEiEUhd9a4+u8wAzeKC6uHzZbYyt9/e2dLC5BGGB6Y0HtEdZQdSDYjDsoTyNGdMrMA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/wast/wast.min.js" integrity="sha512-jROitIYzp4fmQniVqF+eAnO5xKsZbKhI9Mue2XHaUtyC6rFxvcS/utxK+e1tMdlN/JP20A1qPEPPHwts42A4JQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</body>
</html>
26 changes: 16 additions & 10 deletions example/js/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import startAeonRuntime from '../dist/bundle.js';
import { defaultText } from './inputDefaultText.js';
import { CONST_MODE } from './texts/texts.js';

const go = new Go(); // Defined in wasm_exec.js. Don't forget to add this in your index.html.

Expand Down Expand Up @@ -43,8 +45,10 @@ const runLunaAddition = async () => {
// Set the result onto the doc
const input1 = document.getElementById('input-1');
const input2 = document.getElementById('input-2');
const label2 = document.getElementById('label-2')
const codeContainer = document.getElementById('code');
const moduleContainer = document.getElementById('module');
const selectInstruction = document.getElementById('instruction');

const compile = document.getElementById('compile');
const btn = document.getElementById('btn')
Expand All @@ -54,21 +58,23 @@ const runLunaAddition = async () => {


btn.setAttribute('disabled', true)
const input = `(module
(func (export "addNumbers") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add)
)
`


const editor = CodeMirror(codeContainer, {
value: input,
let editor = CodeMirror(codeContainer, {
value: defaultText(selectInstruction),
mode: "wast",
lineNumbers: true,
});

// Change editor content based on selected mode
selectInstruction.onchange = (e) => {
editor.setValue(defaultText(selectInstruction));
// Hide second input on const
if (e.target.value === CONST_MODE) {
input2.style.display = 'none';
label2.style.display = 'none';
}
}

compile.addEventListener('click', async () => {
moduleContainer.innerHTML = ""
const textContent = editor.getValue()
Expand Down
47 changes: 47 additions & 0 deletions example/js/inputDefaultText.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ADDITION_MODE, CONST_MODE, DIVISION_MODE, MULTIPLICATION_MODE, SUBTRACTION_MODE } from "./texts/texts.js"

export const defaultText = (select) => {
const instruction = select.value
switch (instruction) {
case ADDITION_MODE:
return `(module
(func (export "addNumbers") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.add)
)
`
case SUBTRACTION_MODE:
return `(module
(func (export "subtractNumbers") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.sub)
)
`
case MULTIPLICATION_MODE:
return `(module
(func (export "multiplyNumbers") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.mul)
)
`
case DIVISION_MODE:
return `(module
(func (export "divideNumbers") (param i32 i32) (result i32)
local.get 0
local.get 1
i32.div)
)
`
case CONST_MODE:
return `(module
(func (export "operationWithInternalVariable") (param i32 i32) (result i32)
local.get 0
i32.const 10
i32.add)
)
`
}
}
5 changes: 5 additions & 0 deletions example/js/texts/texts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const CONST_MODE = 'const';
export const ADDITION_MODE = 'addition';
export const SUBTRACTION_MODE = 'subtraction';
export const MULTIPLICATION_MODE = 'multiplication';
export const DIVISION_MODE = 'division';
Binary file modified example/main.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Aeon was built with Luna in mind and they travel together so whatever Luna can c
(Check the `./example/example.js`)

```js
const startAeonRuntime = require("./runtime/start");
import startAeonRuntime from "./runtime/start"
// This binary
// - takes 3 parameters of type `i32` (3, 127, 127, 127)
// - outputs one `i32` result (1, 127)
Expand Down
7 changes: 6 additions & 1 deletion runtime/runtime/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ export function parseCodeSection(wasm) {

let instructions = [];
let locals = [];
let internals = [];

while (wasm.pos < wasm.data.length) {
const instruction = wasm.readByte();
Expand All @@ -230,8 +231,12 @@ export function parseCodeSection(wasm) {
if (instruction == Opcodes.get_local) {
locals.push(wasm.readByte())
}

if (instruction == Opcodes.i32_const) {
internals.push(wasm.readByte())
}
}
code.push([locals, instructions]);
code.push({locals, internals, instructions});
}

return code
Expand Down
15 changes: 11 additions & 4 deletions runtime/runtime/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ export default class Processor {
}

executeFunc() {
for (const instruction of this.func[1]) {
if (instruction == Opcodes.get_local) {
this.stack.push(this.params[this.func[0].shift()]);
}
for (const instruction of this.func.instructions) {
if (instruction == Opcodes.get_local) this.stack.push(this.params[this.func.locals.shift()]);
if (instruction == Opcodes.i32_const) this.stack.push(this.func.internals.shift());

this.#parseInstruction(instruction)
}
Expand All @@ -31,6 +30,14 @@ export default class Processor {
case Opcodes.i32_sub:
result = this.stack.reduce((prev, current) => prev - current);
return this.stack.push(result);

case Opcodes.i32_mul:
result = this.stack.reduce((prev, current) => prev * current, 1);
return this.stack.push(result);

case Opcodes.i32_div:
result = this.stack.reduce((prev, current) => prev / current);
return this.stack.push(result);
}
}

Expand Down
4 changes: 3 additions & 1 deletion runtime/utils/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export const Opcodes = {
f32_gt : 0x5e,
i32_and : 0x71,
i32_add : 0x6a,
i32_sub : 0x6b,
i32_sub : 0x6b,
i32_mul : 0x6c,
i32_div : 0x6d,
f32_add : 0x92,
f32_sub : 0x93,
f32_mul : 0x94,
Expand Down
1 change: 1 addition & 0 deletions texts/texts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (

TypeInstruction = "instruction"
FuncInstruction = "funcInstruction"
InternalInstruction = "internalInstruction"
GetLocalInstruction = "getLocalInstruction"

Number = "number"
Expand Down

1 comment on commit 009b20c

@vercel
Copy link

@vercel vercel bot commented on 009b20c Nov 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

luna – ./

luna-git-master-thomscoder.vercel.app
luna-demo.vercel.app
luna-thomscoder.vercel.app

Please sign in to comment.