Add suport for block/loop/if/else
This commit is contained in:
130
asm.js
130
asm.js
@@ -78,7 +78,7 @@ const State = Object.freeze({
|
|||||||
LOCAL_NAME: 6,
|
LOCAL_NAME: 6,
|
||||||
LOCAL_TYPE: 7,
|
LOCAL_TYPE: 7,
|
||||||
MEM_NAME: 8,
|
MEM_NAME: 8,
|
||||||
MEM_INITIAL: 9,
|
MEM_INIT: 9,
|
||||||
MEM_MAX: 10,
|
MEM_MAX: 10,
|
||||||
MEM_FLAGS: 11,
|
MEM_FLAGS: 11,
|
||||||
IMPORT_NAME: 12,
|
IMPORT_NAME: 12,
|
||||||
@@ -95,6 +95,8 @@ const State = Object.freeze({
|
|||||||
ALIGN: 23,
|
ALIGN: 23,
|
||||||
DEF_NAME: 24,
|
DEF_NAME: 24,
|
||||||
DEF_VALUE: 25,
|
DEF_VALUE: 25,
|
||||||
|
BLOCK_NAME: 26,
|
||||||
|
BLOCK_TYPE: 27,
|
||||||
});
|
});
|
||||||
|
|
||||||
const Action = Object.freeze({
|
const Action = Object.freeze({
|
||||||
@@ -113,21 +115,32 @@ const Action = Object.freeze({
|
|||||||
ALIGN: 12,
|
ALIGN: 12,
|
||||||
DEF: 13,
|
DEF: 13,
|
||||||
LABEL: 14,
|
LABEL: 14,
|
||||||
|
ENTER: 16,
|
||||||
|
EXIT: 17,
|
||||||
|
ELSE: 18,
|
||||||
});
|
});
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
|
"void": 0x40,
|
||||||
"func": 0x60,
|
"func": 0x60,
|
||||||
"i32": 0x7f,
|
"i32": 0x7f,
|
||||||
};
|
};
|
||||||
|
|
||||||
const opcodes = {
|
const opcodes = {
|
||||||
|
"block": 0x02,
|
||||||
|
"loop": 0x03,
|
||||||
|
"if": 0x04,
|
||||||
|
"else": 0x05,
|
||||||
"end": 0x0b,
|
"end": 0x0b,
|
||||||
|
"br": 0x0c,
|
||||||
|
"br_if": 0x0d,
|
||||||
"local.get": 0x20,
|
"local.get": 0x20,
|
||||||
"local.set": 0x21,
|
"local.set": 0x21,
|
||||||
"local.tee": 0x22,
|
"local.tee": 0x22,
|
||||||
"global.get": 0x23,
|
"global.get": 0x23,
|
||||||
"global.set": 0x24,
|
"global.set": 0x24,
|
||||||
"i32.const": 0x41,
|
"i32.const": 0x41,
|
||||||
|
"i32.gt_u": 0x4b,
|
||||||
"i32.mul": 0x6c,
|
"i32.mul": 0x6c,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -163,6 +176,7 @@ class Parser {
|
|||||||
".align": State.ALIGN,
|
".align": State.ALIGN,
|
||||||
".def": State.DEF_NAME,
|
".def": State.DEF_NAME,
|
||||||
};
|
};
|
||||||
|
this.blocks = new Set(["block", "loop", "if"]);
|
||||||
this.handlers = {
|
this.handlers = {
|
||||||
[State.TOP]: (token) => this.token_top(token),
|
[State.TOP]: (token) => this.token_top(token),
|
||||||
[State.EXPORT]: (token) => this.token_export(token),
|
[State.EXPORT]: (token) => this.token_export(token),
|
||||||
@@ -190,6 +204,8 @@ class Parser {
|
|||||||
[State.ALIGN]: (token) => this.token_align(token),
|
[State.ALIGN]: (token) => this.token_align(token),
|
||||||
[State.DEF_NAME]: (token) => this.token_def_name(token),
|
[State.DEF_NAME]: (token) => this.token_def_name(token),
|
||||||
[State.DEF_VALUE]: (token) => this.token_def_value(token),
|
[State.DEF_VALUE]: (token) => this.token_def_value(token),
|
||||||
|
[State.BLOCK_NAME]: (token) => this.token_block_name(token),
|
||||||
|
[State.BLOCK_TYPE]: (token) => this.token_block_type(token),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.results = [];
|
this.results = [];
|
||||||
@@ -220,18 +236,34 @@ class Parser {
|
|||||||
token_top(token) {
|
token_top(token) {
|
||||||
if (token == LINE_END)
|
if (token == LINE_END)
|
||||||
return;
|
return;
|
||||||
let state;
|
const state = this.directives[token];
|
||||||
if (state = this.directives[token]) {
|
if (state) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.endsWith(":"))
|
if (token.endsWith(":"))
|
||||||
return { type: Action.LABEL, name: token.slice(0, -1) };
|
return { type: Action.LABEL, name: token.slice(0, -1) };
|
||||||
const code = this.translate_code(token);
|
|
||||||
if (code)
|
const opcode = opcodes[token];
|
||||||
return { type: Action.APPEND, code };
|
|
||||||
else
|
if (this.blocks.has(token)) {
|
||||||
return { type: Action.SYMBOL, symbol: token };
|
this.state = State.BLOCK_NAME;
|
||||||
|
this.block = { code: opcode };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (token == "else")
|
||||||
|
return { type: Action.ELSE }
|
||||||
|
if (token == "end")
|
||||||
|
return { type: Action.EXIT };
|
||||||
|
|
||||||
|
if (opcode)
|
||||||
|
return { type: Action.APPEND, opcode };
|
||||||
|
const literal = this.integer(token);
|
||||||
|
if (literal)
|
||||||
|
return { type: Action.APPEND, literal };
|
||||||
|
|
||||||
|
return { type: Action.SYMBOL, symbol: token };
|
||||||
}
|
}
|
||||||
|
|
||||||
token_export(token) {
|
token_export(token) {
|
||||||
@@ -574,6 +606,36 @@ class Parser {
|
|||||||
return action;
|
return action;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
token_block_name(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
this.block.type = types["void"];
|
||||||
|
const action = { type: Action.ENTER, block: this.block };
|
||||||
|
this.state = State.TOP;
|
||||||
|
this.block = undefined;
|
||||||
|
return action;
|
||||||
|
} else {
|
||||||
|
this.block.label = token;
|
||||||
|
this.state = State.BLOCK_TYPE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token_block_type(token) {
|
||||||
|
if (token == LINE_END) {
|
||||||
|
this.block.type = types["void"];
|
||||||
|
} else {
|
||||||
|
this.block.type = types[token];
|
||||||
|
if (this.block.type == undefined) {
|
||||||
|
console.error(
|
||||||
|
`ERROR: Unexpected token ${token}, expected type`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const action = { type: Action.ENTER, block: this.block };
|
||||||
|
this.state = State.TOP;
|
||||||
|
this.block = undefined;
|
||||||
|
return action;
|
||||||
|
}
|
||||||
|
|
||||||
mem_action() {
|
mem_action() {
|
||||||
const action = {
|
const action = {
|
||||||
type: Action.MEM,
|
type: Action.MEM,
|
||||||
@@ -588,8 +650,9 @@ class Parser {
|
|||||||
*handle(src) {
|
*handle(src) {
|
||||||
let action;
|
let action;
|
||||||
for (const token of this.tokenizer.handle(src)) {
|
for (const token of this.tokenizer.handle(src)) {
|
||||||
if (action = this.handlers[this.state](token))
|
if (action = this.handlers[this.state](token)) {
|
||||||
yield action;
|
yield action;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -631,6 +694,9 @@ export class Assembler {
|
|||||||
[Action.ALIGN]: (action) => this.action_align(action),
|
[Action.ALIGN]: (action) => this.action_align(action),
|
||||||
[Action.DEF]: (action) => this.action_def(action),
|
[Action.DEF]: (action) => this.action_def(action),
|
||||||
[Action.LABEL]: (action) => this.action_label(action),
|
[Action.LABEL]: (action) => this.action_label(action),
|
||||||
|
[Action.ENTER]: (action) => this.action_enter(action),
|
||||||
|
[Action.EXIT]: (action) => this.action_exit(action),
|
||||||
|
[Action.ELSE]: (action) => this.action_else(action),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.exports = [];
|
this.exports = [];
|
||||||
@@ -641,10 +707,14 @@ export class Assembler {
|
|||||||
this.pos = { mem: 0, addr: 0 };
|
this.pos = { mem: 0, addr: 0 };
|
||||||
this.data = [];
|
this.data = [];
|
||||||
this.defs = {};
|
this.defs = {};
|
||||||
|
this.blocks = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
action_append(action) {
|
action_append(action) {
|
||||||
this.funcs[this.current_func].body.push(action.code);
|
const code = action.opcode != undefined
|
||||||
|
? [ action.opcode ]
|
||||||
|
: this.leb128(action.literal);
|
||||||
|
this.funcs[this.current_func].body.push(...code);
|
||||||
}
|
}
|
||||||
|
|
||||||
action_export(action) {
|
action_export(action) {
|
||||||
@@ -676,14 +746,14 @@ export class Assembler {
|
|||||||
|
|
||||||
action_symbol(action) {
|
action_symbol(action) {
|
||||||
const func = this.funcs[this.current_func];
|
const func = this.funcs[this.current_func];
|
||||||
const value = this.lookup_param(func, action.symbol)
|
const value = this.lookup_block(action.symbol)
|
||||||
|
?? this.lookup_param(func, action.symbol)
|
||||||
?? this.lookup_local(func, action.symbol)
|
?? this.lookup_local(func, action.symbol)
|
||||||
?? this.lookup_global(action.symbol)
|
?? this.lookup_global(action.symbol)
|
||||||
?? this.lookup_def(action.symbol);
|
?? this.lookup_def(action.symbol);
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
console.error(
|
console.error(
|
||||||
`ERROR: Unable to resolve symbol ${action.symbol}`);
|
`ERROR: Unable to resolve symbol ${action.symbol}`);
|
||||||
value = 0;
|
|
||||||
}
|
}
|
||||||
func.body.push(value);
|
func.body.push(value);
|
||||||
}
|
}
|
||||||
@@ -748,12 +818,34 @@ export class Assembler {
|
|||||||
this.defs[action.name] = this.pos.addr;
|
this.defs[action.name] = this.pos.addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_enter(action) {
|
||||||
|
this.funcs[this.current_func].body.push(action.block.code);
|
||||||
|
this.funcs[this.current_func].body.push(action.block.type);
|
||||||
|
this.blocks.push(action.block.label);
|
||||||
|
}
|
||||||
|
|
||||||
|
action_exit(action) {
|
||||||
|
this.funcs[this.current_func].body.push(opcodes.end);
|
||||||
|
this.blocks.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
action_else(action) {
|
||||||
|
this.blocks.pop();
|
||||||
|
this.funcs[this.current_func].body.push(opcodes.else);
|
||||||
|
this.blocks.push(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
push(chunk) {
|
push(chunk) {
|
||||||
const text = this.decoder.decode(chunk, { stream: true });
|
const text = this.decoder.decode(chunk, { stream: true });
|
||||||
for (const action of this.parser.handle(text))
|
for (const action of this.parser.handle(text))
|
||||||
this.handlers[action.type](action);
|
this.handlers[action.type](action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lookup_block(symbol) {
|
||||||
|
const index = this.blocks.toReversed().indexOf(symbol);
|
||||||
|
return index == -1 ? null : index;
|
||||||
|
}
|
||||||
|
|
||||||
lookup_param(func, symbol) {
|
lookup_param(func, symbol) {
|
||||||
const index = Object.keys(func.params).indexOf(symbol);
|
const index = Object.keys(func.params).indexOf(symbol);
|
||||||
return index == -1 ? null : index;
|
return index == -1 ? null : index;
|
||||||
@@ -921,4 +1013,18 @@ export class Assembler {
|
|||||||
else
|
else
|
||||||
return [ flags, init ];
|
return [ flags, init ];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
leb128(x) {
|
||||||
|
const orig = x;
|
||||||
|
const bytes = [];
|
||||||
|
while (true) {
|
||||||
|
const b = x & 0x7f;
|
||||||
|
x >>= 7;
|
||||||
|
if (x == 0 && (b & 0x40) == 0 || x == -1 && (b & 0x40) != 0) {
|
||||||
|
bytes.push(b);
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
bytes.push(b | 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user