-
Notifications
You must be signed in to change notification settings - Fork 679
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Generate hello.wasm using AssemblyScript compiler (#395)
* Add hello.wasm source code * Add buildable source for hello.wasm * Add .DS_Store to ignore
- Loading branch information
1 parent
3e9f529
commit 3694b97
Showing
11 changed files
with
1,083 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,6 @@ nearlib/dist/ | |
|
||
# Integration tests | ||
tmp/ | ||
|
||
# OS X | ||
.DS_Store |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,334 @@ | ||
declare function logStr(str: string): void; | ||
declare function logF64(val: f64): void; | ||
|
||
/** | ||
* Extend from this class to handle events from parser. | ||
* Default implementation traverses whole object tree and does nothing. | ||
*/ | ||
export abstract class JSONHandler { | ||
setString(name: string, value: string): void { | ||
} | ||
|
||
setBoolean(name: string, value: bool): void { | ||
} | ||
|
||
setNull(name: string): void { | ||
} | ||
|
||
setInteger(name: string, value: i32): void { | ||
} | ||
|
||
pushArray(name: string): bool { | ||
return true; | ||
} | ||
|
||
popArray(): void { | ||
} | ||
|
||
pushObject(name: string): bool { | ||
return true; | ||
} | ||
|
||
popObject(): void { | ||
} | ||
} | ||
|
||
/** | ||
* Extend from this class to handle events from parser. | ||
* This implementation crashes on every unimplemented set/push method | ||
* to allow easier validation of input. | ||
*/ | ||
export class ThrowingJSONHandler extends JSONHandler { | ||
setString(name: string, value: string): void { | ||
assert(false, 'Unexpected string field ' + name + ' : "' + value + '"'); | ||
} | ||
|
||
setBoolean(name: string, value: bool): void { | ||
assert(false, 'Unexpected bool field ' + name + ' : ' + (value ? 'true' : 'false')); | ||
} | ||
|
||
setNull(name: string): void { | ||
assert(false, 'Unexpected null field ' + name); | ||
} | ||
|
||
setInteger(name: string, value: i32): void { | ||
let arr: Array<i32> = [value]; | ||
assert(false, 'Unexpected integer field ' + name + ' : ' + arr.toString()); | ||
} | ||
|
||
pushArray(name: string): bool { | ||
assert(false, 'Unexpected array field ' + name); | ||
return true; | ||
} | ||
|
||
pushObject(name: string): bool { | ||
assert(false, 'Unexpected object field ' + name); | ||
return true; | ||
} | ||
} | ||
|
||
const TRUE_STR = "true"; | ||
const FALSE_STR = "false"; | ||
const NULL_STR = "null"; | ||
let CHAR_0 = "0".charCodeAt(0); | ||
let CHAR_9 = "9".charCodeAt(0); | ||
let CHAR_A = "A".charCodeAt(0); | ||
let CHAR_A_LOWER = "a".charCodeAt(0); | ||
|
||
export class DecoderState { | ||
readIndex: i32 = 0; | ||
buffer: Uint8Array = null; | ||
lastKey: string = null; | ||
} | ||
|
||
export class JSONDecoder<JSONHandlerT extends JSONHandler> { | ||
|
||
handler: JSONHandlerT; | ||
state: DecoderState; | ||
|
||
constructor(handler: JSONHandlerT) { | ||
this.handler = handler; | ||
} | ||
|
||
deserialize(buffer: Uint8Array, decoderState: DecoderState = null): void { | ||
if (decoderState) { | ||
this.state = decoderState; | ||
} else { | ||
this.state = new DecoderState(); | ||
this.state.readIndex = 0; | ||
this.state.buffer = buffer; | ||
this.state.lastKey = null; | ||
} | ||
|
||
assert(this.parseValue(), "Cannot parse JSON"); | ||
// TODO: Error if input left | ||
} | ||
|
||
private peekChar(): i32 { | ||
if (this.state.readIndex >= this.state.buffer.length) { | ||
return -1; | ||
} | ||
return this.state.buffer[this.state.readIndex]; | ||
} | ||
|
||
private readChar(): i32 { | ||
assert(this.state.readIndex < this.state.buffer.length, "Unexpected input end"); | ||
return this.state.buffer[this.state.readIndex++]; | ||
} | ||
|
||
private parseValue(): bool { | ||
this.skipWhitespace(); | ||
let result = this.parseObject() | ||
|| this.parseArray() | ||
|| this.parseString() | ||
|| this.parseBoolean() | ||
|| this.parseNumber() | ||
|| this.parseNull() | ||
this.skipWhitespace(); | ||
return result; | ||
} | ||
|
||
private parseObject(): bool { | ||
if (this.peekChar() != "{".charCodeAt(0)) { | ||
return false; | ||
} | ||
let key = this.state.lastKey; | ||
this.state.lastKey = null; | ||
if (this.handler.pushObject(key)) { | ||
this.readChar(); | ||
this.skipWhitespace(); | ||
|
||
let firstItem = true; | ||
while (this.peekChar() != "}".charCodeAt(0)) { | ||
if (!firstItem) { | ||
assert(this.readChar() == ",".charCodeAt(0), "Expected ','"); | ||
} else { | ||
firstItem = false; | ||
} | ||
this.parseKey(); | ||
this.parseValue(); | ||
} | ||
assert(this.readChar() == "}".charCodeAt(0), "Unexpected end of object"); | ||
} | ||
this.handler.popObject(); | ||
return true; | ||
} | ||
|
||
private parseKey(): void { | ||
this.skipWhitespace(); | ||
this.state.lastKey = this.readString(); | ||
this.skipWhitespace(); | ||
assert(this.readChar() == ":".charCodeAt(0), "Expected ':'"); | ||
} | ||
|
||
private parseArray(): bool { | ||
if (this.peekChar() != "[".charCodeAt(0)) { | ||
return false; | ||
} | ||
let key = this.state.lastKey; | ||
this.state.lastKey = null; | ||
if (this.handler.pushArray(key)) { | ||
this.readChar(); | ||
this.skipWhitespace(); | ||
|
||
let firstItem = true; | ||
while (this.peekChar() != "]".charCodeAt(0)) { | ||
if (!firstItem) { | ||
assert(this.readChar() == ",".charCodeAt(0), "Expected ','"); | ||
} else { | ||
firstItem = false; | ||
} | ||
this.parseValue(); | ||
} | ||
assert(this.readChar() == "]".charCodeAt(0), "Unexpected end of array"); | ||
} | ||
this.handler.popArray(); | ||
return true;; | ||
} | ||
|
||
private parseString(): bool { | ||
if (this.peekChar() != '"'.charCodeAt(0)) { | ||
return false; | ||
} | ||
this.handler.setString(this.state.lastKey, this.readString()); | ||
return true; | ||
} | ||
|
||
private readString(): string { | ||
assert(this.readChar() == '"'.charCodeAt(0), "Expected double-quoted string"); | ||
let savedIndex = this.state.readIndex; | ||
let stringParts: Array<string> = new Array<string>(); | ||
for (;;) { | ||
let byte = this.readChar(); | ||
assert(byte >= 0x20, "Unexpected control character"); | ||
// TODO: Make sure unicode handled properly | ||
if (byte == '"'.charCodeAt(0)) { | ||
stringParts.push( | ||
String.fromUTF8(this.state.buffer.buffer.data + savedIndex, this.state.readIndex - savedIndex - 1)); | ||
return stringParts.join(""); | ||
} | ||
if (byte == "\\".charCodeAt(0)) { | ||
if (this.state.readIndex > savedIndex + 1) { | ||
stringParts.push( | ||
String.fromUTF8(this.state.buffer.buffer.data + savedIndex, this.state.readIndex - savedIndex - 1)); | ||
} | ||
stringParts.push(this.readEscapedChar()); | ||
savedIndex = this.state.readIndex; | ||
} | ||
} | ||
// Should never happen | ||
return ""; | ||
} | ||
|
||
private readEscapedChar(): string { | ||
let byte = this.readChar(); | ||
// TODO: Use lookup table for anything except \u | ||
if (byte == '"'.charCodeAt(0)) { | ||
return '"'; | ||
} | ||
if (byte == "\\".charCodeAt(0)) { | ||
return "\\"; | ||
} | ||
if (byte == "/".charCodeAt(0)) { | ||
return "/"; | ||
} | ||
if (byte == "b".charCodeAt(0)) { | ||
return "\b"; | ||
} | ||
if (byte == "n".charCodeAt(0)) { | ||
return "\n"; | ||
} | ||
if (byte == "r".charCodeAt(0)) { | ||
return "\r"; | ||
} | ||
if (byte == "t".charCodeAt(0)) { | ||
return "\t"; | ||
} | ||
if (byte == "u".charCodeAt(0)) { | ||
let d1 = this.readHexDigit(); | ||
let d2 = this.readHexDigit(); | ||
let d3 = this.readHexDigit(); | ||
let d4 = this.readHexDigit(); | ||
let charCode = d1 * 0x1000 + d2 * 0x100 + d3 * 0x10 + d4; | ||
return String.fromCodePoint(charCode); | ||
} | ||
assert(false, "Unexpected escaped character: " + String.fromCharCode(byte)); | ||
return ""; | ||
} | ||
|
||
private readHexDigit(): i32 { | ||
let byte = this.readChar(); | ||
let digit = byte - CHAR_0; | ||
if (digit > 9) { | ||
digit = byte - CHAR_A + 10; | ||
if (digit < 10 || digit > 15) { | ||
digit = byte - CHAR_A_LOWER + 10; | ||
} | ||
} | ||
let arr: Array<i32> = [byte, digit]; | ||
assert(digit >= 0 && digit < 16, "Unexpected \\u digit"); | ||
return digit; | ||
} | ||
|
||
private parseNumber(): bool { | ||
// TODO: Parse floats | ||
let number: i32 = 0; | ||
let sign: i32 = 1; | ||
if (this.peekChar() == "-".charCodeAt(0)) { | ||
sign = -1; | ||
this.readChar(); | ||
} | ||
let digits = 0; | ||
while (CHAR_0 <= this.peekChar() && this.peekChar() <= CHAR_9 ) { | ||
let byte = this.readChar(); | ||
number *= 10; | ||
number += byte - CHAR_0; | ||
digits++; | ||
} | ||
if (digits > 0) { | ||
this.handler.setInteger(this.state.lastKey, number * sign); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
private parseBoolean(): bool { | ||
if (this.peekChar() == FALSE_STR.charCodeAt(0)) { | ||
this.readAndAssert(FALSE_STR); | ||
this.handler.setBoolean(this.state.lastKey, false); | ||
return true; | ||
} | ||
if (this.peekChar() == TRUE_STR.charCodeAt(0)) { | ||
this.readAndAssert(TRUE_STR); | ||
this.handler.setBoolean(this.state.lastKey, true); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private parseNull(): bool { | ||
if (this.peekChar() == NULL_STR.charCodeAt(0)) { | ||
this.readAndAssert(NULL_STR); | ||
this.handler.setNull(this.state.lastKey); | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
private readAndAssert(str: string): void { | ||
for (let i = 0; i < str.length; i++) { | ||
assert(str.charCodeAt(i) == this.readChar(), "Expected '" + str + "'"); | ||
} | ||
} | ||
|
||
private skipWhitespace(): void { | ||
while (this.isWhitespace(this.peekChar())) { | ||
this.readChar(); | ||
} | ||
} | ||
|
||
private isWhitespace(charCode: i32): bool { | ||
return charCode == 0x9 || charCode == 0xa || charCode == 0xd || charCode == 0x20; | ||
} | ||
} |
Oops, something went wrong.