Replace assembler with preprocessor

This commit is contained in:
Remko Tronçon 2019-11-08 16:55:34 +01:00
parent 46cbda1f6a
commit 9c99a927be
6 changed files with 301 additions and 360 deletions

View file

@ -17,5 +17,4 @@ addons:
apt:
packages:
- make
- racket
# - wabt

View file

@ -16,7 +16,7 @@ dev-server: $(WASM_FILES)
wasm: $(WASM_FILES) src/waforth.assembled.wat src/tools/quadruple.wasm.hex
src/waforth.wasm: src/waforth.wat dist
racket -f $< > src/waforth.wat.tmp
./src/tools/preprocess.js $< > src/waforth.wat.tmp
$(WAT2WASM) $(WAT2WASM_FLAGS) -o $@ src/waforth.wat.tmp
src/waforth.assembled.wat: src/waforth.wasm

View file

@ -51,23 +51,16 @@ You can also run the tests in Node.JS by running
## Design
### The Macro Assembler
### The Preprocessor
The WAForth core is written as [a single
module](https://github.com/remko/waforth/blob/master/src/waforth.wat) in
WebAssembly's [text
format](https://webassembly.github.io/spec/core/text/index.html). The text
format isn't really meant for writing code in, so it has no facilities like a
real assembler (e.g. constant definitions, macro expansion, ...) However, since
the text format uses S-expressions, you can do some small modifications to make
it extensible with Lisp-style macros.
I added some Racket macros to the module definition, and implemented [a mini
assembler](https://github.com/remko/waforth/blob/master/src/tools/assembler.rkt)
to print out the resulting s-expressions in the right format.
The result is something that looks like a standard WebAssembly module, but
sprinkled with some macros for convenience.
real assembler (e.g. constant definitions, macro expansion, ...).
To help with maintenance, the WebAssembly file is piped through a simple string
preprocessor that replaces constants with defined values.
### The Interpreter

View file

@ -1,66 +0,0 @@
#lang racket
(define (asm-symbol? x)
(and (symbol? x) (eq? (string-ref (symbol->string x) 0) #\!)))
(define (preprocess x)
(cond ((list? x)
(cond ((null? x) '())
((and (list? (car x)) (asm-symbol? (caar x)))
(append (eval (car x)) (preprocess (cdr x))))
(else
(cons (preprocess (car x)) (preprocess (cdr x))))))
((asm-symbol? x)
(eval x))
(else x)))
(define (byte->hex byte)
(define digits "0123456789ABCDEF")
(string (string-ref digits (quotient byte 16))
(string-ref digits (remainder byte 16))))
(define (byte->string byte)
(cond ((> byte 255) (raise "Illegal byte"))
((and (> byte 32) (< byte 127) (not (or (eqv? byte 34) (eqv? byte 92))))
(string (integer->char byte)))
(else (string-append "\\" (byte->hex byte)))))
(define (string-escape s)
(string-join (map byte->string (map char->integer (string->list s))) ""))
(define (serialize x)
(cond ((list? x)
(string-append "(" (string-join (map serialize x) " ") ")"))
((string? x)
(string-append "\"" (string-escape x) "\""))
((symbol? x)
(symbol->string x))
((number? x)
(number->string x))
((bytes? x)
(string-append
"\""
(string-join (map byte->string (bytes->list x)) "")
"\""))
(else (raise (list "Unexpected type" x)))))
(define (priority x)
(cond ((eq? x 'module) 0)
((and (list? x) (eq? (car x) 'import)) 1000000)
((and (list? x) (eq? (car x) 'memory)) 2000000)
((and (list? x) (eq? (car x) 'global)) 3000000)
((and (list? x) (eq? (car x) 'data)) (+ 5000000 (car (cdr (car (cdr x))))))
(else 100000000)))
(define (wasm-assemble module)
(display
(serialize
(sort (preprocess module)
(lambda (x y) (< (priority x) (priority y)))))))
(define-syntax module
(syntax-rules ()
((_ arg ...)
(wasm-assemble '(module arg ...)))))
(provide module)

30
src/tools/preprocess.js Executable file
View file

@ -0,0 +1,30 @@
#!/usr/bin/env node
const process = require("process");
const fs = require("fs");
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
}
const file = process.argv[2];
const lines = fs
.readFileSync(file)
.toString()
.split("\n");
const definitions = {};
lines.forEach(line => {
Object.keys(definitions).forEach(k => {
line = line.replace(
new RegExp(escapeRegExp(k) + "(\\s|\\))", "g"),
definitions[k] + " (; = " + k + " ;)$1"
);
});
const m = line.match(/^;; \(define\s+([^\s]+)\s+([^\s]+)\)/);
if (m) {
definitions[m[1]] = m[2];
} else {
console.log(line);
}
});

File diff suppressed because it is too large Load diff