├── .gitignore ├── .npmignore ├── README.md ├── api.poem ├── boot.poem ├── cli.js ├── concept.poem ├── concept.wast ├── debug.wast ├── images └── Poetree_256.png ├── index.js ├── package-lock.json ├── package.json ├── poetry.json ├── prettify_wast.js ├── runtime.wast ├── stdlib.wast ├── test.html └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/docs/gitignore 2 | # https://help.github.com/articles/ignoring-files 3 | # Example .gitignore files: https://github.com/github/gitignore 4 | /node_modules/ 5 | /build/ 6 | /build-desktop/ 7 | /deploy.json 8 | *.log 9 | Thumbs.db 10 | *~ 11 | *.kra-autosave.kra 12 | boot.wa* 13 | boot.debug.* 14 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/docs/gitignore 2 | # https://help.github.com/articles/ignoring-files 3 | # Example .gitignore files: https://github.com/github/gitignore 4 | /node_modules/ 5 | /src/ 6 | /images/ 7 | /deploy.json 8 | *.log 9 | /Jakefile.* 10 | /.jshintrc 11 | Thumbs.db 12 | *~ 13 | *.kra-autosave.kra 14 | boot.* 15 | api.* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Poetry 4 | ====== 5 | A poetically dynamic and simple programming language that compiles to WebAssembly! 6 | 7 | Much features! Very hype! 8 | ------------------------ 9 | - Easy to learn and use 10 | - Minimalistic syntax 11 | - Customizable 12 | - Full control of wasm imports and exports 13 | - Bundle multiple source files (including `.wa(s)t`-files!) 14 | 15 | Usage 16 | ----- 17 | $ npm i -g poetry-compiler 18 | $ poetry my_program.poem -b my_program.wasm 19 | 20 | Example 21 | ------- 22 | export_memory "memory" 23 | import "env" "log" _log 2 0 24 | 25 | func log message 26 | _log (address_of message) (size_of message) 27 | 28 | export "init" init 29 | log "Hello Poetry! 🌳" 30 | 31 | Check the [wiki for documentation](https://github.com/FantasyInternet/poetry/wiki)! 32 | -------------------------------------------------------------------------------- /api.poem: -------------------------------------------------------------------------------- 1 | @import "env" "logNumber" log_number 1 2 | @import "env" "log" _log 2 3 | 4 | @export_memory "memory" 5 | 6 | @func log msg 7 | msg += "" 8 | _log (address_of msg) (size_of msg) 9 | -------------------------------------------------------------------------------- /boot.poem: -------------------------------------------------------------------------------- 1 | include "./debug.wast" 2 | include "./api.poem" 3 | 4 | var meaning_of_life = 42.5 5 | var meaning_of_life2 = false 6 | var name = '"Hello \"world"! 🤖' 7 | 8 | var my_pet = 9 | :name = "Fido" 10 | :species = "dog" 11 | :age = 3 12 | :male = true 13 | :nutered = false 14 | :mother = 15 | :name = "Rita" 16 | :age = 10 17 | :male = false 18 | :father = null 19 | 20 | 21 | var friends = 22 | "Alice" 23 | 'Bob' 24 | 'Charlie' 25 | "Dennis" 26 | 27 | func sum a b 28 | var tableindex = #init 29 | return a + b + meaning_of_life 30 | 31 | export "init" init 32 | test_json 33 | ~ make_a_cup_of tea 34 | 35 | func test_json 36 | var val = "Små søde\njordbær!🍓" 37 | log val 38 | log json_encode val 39 | val = "\"\\\/\b\f\n\t\u0000" 40 | log val 41 | log json_encode val 42 | log json_encode friends 43 | log json_encode my_pet 44 | log "" + json_decode "42" 45 | log "" + json_decode "-42.50E-1" 46 | log json_decode '"Sm\\u00e5 s\\u00f8de\\njordb\\u00e6r!\\ud83c\\udf53"' 47 | log json_encode (json_decode (json_encode friends)) 48 | log json_encode (json_decode "{\"form\":96,\"params\":[127,127],\"returns\":[127]}") 49 | log json_encode json_decode json_encode json_decode "{\n \"pi\": 3.141592653589793,\n \"name\": \"poetry-compiler\",\n \"version\": \"0.34.0\",\n \"description\": \"Poetic programming language that compiles to webassembly\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"build\": \"node cli.js boot.poem -t boot.wast -b boot.wasm --debug boot.debug.json\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"bin\": {\n \"poetry\": \".\/cli.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https:\/\/github.com\/FantasyInternet\/poetry.git\"\n },\n \"keywords\": [\n \"wasm\",\n \"webassembly\",\n \"compiler\",\n \"programming\",\n \"language\",\n \"poetry\"\n ],\n \"author\": \"\",\n \"license\": \"ISC\",\n \"bugs\": {\n \"url\": \"https:\/\/github.com\/FantasyInternet\/poetry\/issues\"\n },\n \"homepage\": \"https:\/\/github.com\/FantasyInternet\/poetry#readme\",\n \"dependencies\": {\n \"commander\": \"^2.17.1\",\n \"wabt\": \"^1.0.0\"\n }\n}" 50 | 51 | func test_unicode 52 | var danish = "æøå$¢€𐍈💩" 53 | for i in range 0 (string_length danish) 54 | log (char (char_code danish i)) 55 | 56 | func test_if 57 | if false 58 | log "one" 59 | elsif false 60 | log "two" 61 | elsif false 62 | log "three" 63 | else 64 | log "four" 65 | 66 | func test_search 67 | log "Charlie is my " + (array_search friends "Charlie") + "th friend!" 68 | var friend = "Chøarlie" 69 | var sub = "lie" 70 | log friend + " has '"+sub+"' at " + (string_search friend sub 0 ) 71 | 72 | func test_split 73 | var list = ",one,two,,three,four," 74 | var arr = string_split list "," 75 | for item in arr 76 | log item 77 | log array_join arr ";" 78 | arr = array 79 | log array_join arr ";" 80 | array_push arr 81 | log array_join arr ";" 82 | array_push arr 83 | log array_join arr ";" 84 | 85 | func test_tableindex 86 | var tableindex = #sum 87 | log_number #tableindex tableindex meaning_of_life2 88 | log_number echo sum tableindex meaning_of_life2 89 | 90 | func test_whatever 91 | log name + " " + -32.00125 92 | var friend = friends[1 + 1] 93 | log friend 94 | var i = 10 95 | if i > 0 96 | log "yay!" 97 | else 98 | log "nay..." 99 | log "Greetings to all of my friends!" 100 | for frend in friends 101 | log "Hello " + frend + "! How are you!" 102 | 103 | func test_shifting 104 | log (array_unshift (friends) (array_pop (friends))) 105 | log (array_unshift friends (array_pop friends)) 106 | log array_unshift friends string_upper array_pop friends 107 | log array_unshift friends `to` array_pop friends 108 | log array_unshift friends string_lower array_pop friends 109 | log array_unshift friends array_pop friends 110 | log array_unshift friends array_pop friends 111 | log array_unshift friends array_pop friends 112 | 113 | func introduce 114 | for friend1 in friends 115 | for friend2 in friends 116 | log friend1+", meet "+friend2+"!" 117 | 118 | export "echo" echo num 119 | ~ log_number num 120 | return num 121 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const compile = require("./index"), 3 | program = require('commander') 4 | 5 | program 6 | .arguments('') 7 | .option('-b, --wasm ', 'Compile to wasm binary') 8 | .option('-t, --wast ', 'Compile to wast text file') 9 | .option('--debug ', '(dump some debugging stuff to file)') 10 | .action(function (file) { 11 | compile(file, program) 12 | }) 13 | .parse(process.argv) 14 | -------------------------------------------------------------------------------- /concept.poem: -------------------------------------------------------------------------------- 1 | ~ This is a comment? 2 | print "some string" 3 | pset 100 100 red 4 | parse ( stringify obj ) 5 | 6 | if age >= 18 7 | print "Get a job!" 8 | 9 | var meaning_of_life = 42 10 | var pi = 3.14 11 | var name = "John" 12 | var married = false 13 | var friends = 14 | "Alice" 15 | "Bob" 16 | "Carol" 17 | "Daniel" 18 | 19 | print "Hello! my name is $name" 20 | for friend in friends 21 | print "My friend is $friend!" 22 | 23 | if name == "John" 24 | print "You are my best friend!" 25 | else 26 | print "Go away!" 27 | 28 | func double n 29 | return n * 2 30 | 31 | include "./graphics.poem" gfx\ 32 | 33 | gfx\create_image 320 200 34 | 35 | 36 | class Person 37 | var string first_name be "John" 38 | var string last_name be "Doe" 39 | 40 | init name 41 | let string[] names 42 | be (split name " ") 43 | let first_name be names[0] 44 | let last_name 45 | be names[1] 46 | 47 | func full_name 48 | return "$first_name $last_name" -------------------------------------------------------------------------------- /concept.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; imports 3 | 4 | ;; runtime 5 | 6 | ;; globals 7 | (global $meaning_of_life (mut i32) (i32.const 0)) 8 | (global $pi (mut i32) (i32.const 0)) 9 | (global $name (mut i32) (i32.const 0)) 10 | (global $married (mut i32) (i32.const 0)) 11 | (global $friends (mut i32) (i32.const 0)) 12 | 13 | ;; functions 14 | (func $double (param $n i32) (result i32) 15 | (return (call $-mul (get_local $n) (call $-number (f32.const 2)))) 16 | 17 | ) 18 | 19 | 20 | ;; exports 21 | 22 | ) -------------------------------------------------------------------------------- /debug.wast: -------------------------------------------------------------------------------- 1 | (func $get_mindex (result i32) 2 | (get_global $-mindex) 3 | ) 4 | (export "get_mindex" (func $get_mindex)) 5 | 6 | (func $load_f64 (param $offset i32) (result f64) 7 | (f64.load (get_local $offset)) 8 | ) 9 | (export "load_f64" (func $load_f64)) 10 | (export "garbagecollect" (func $-garbagecollect)) 11 | ;; (export "traceGC" (func $-traceGC)) 12 | -------------------------------------------------------------------------------- /images/Poetree_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FantasyInternet/poetry/ac0bf73502f792d3c541d5c09f29b10f23150b9b/images/Poetree_256.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"), 2 | path = require("path"), 3 | wabt = require("wabt")(), 4 | pretty_wast = require("./prettify_wast.js") 5 | 6 | function compile(filename, options = {}) { 7 | let compilation = { 8 | tokenTree: [], 9 | strings: [null, false, 0, "", null, true, true, true], 10 | bundle: [] 11 | } 12 | compilation.bundle.push(require.resolve(filename, { paths: [path.dirname(filename)] })) 13 | compilation.ns = 0 14 | compilation.namespaces = { "": [] } 15 | while (compilation.bundle[compilation.ns]) { 16 | compilation.path = compilation.bundle[compilation.ns] 17 | compilation.src = ("\n" + fs.readFileSync(compilation.path)).replace(/\r/g, "") + "\n;;~end`\n;" 18 | compilation.config = findConfig(compilation.path) 19 | compilation.metaphors = compilation.config.metaphors 20 | if (compilation.config.includes) { 21 | for (let include of compilation.config.includes) { 22 | if (!compilation.bundle.includes(include)) { 23 | compilation.namespaces[""].push("ns" + compilation.bundle.length + "\\") 24 | compilation.bundle.push(include) 25 | } 26 | } 27 | } 28 | 29 | rewind(compilation) 30 | if (compilation.path.match(/.+\.was?t$/)) { 31 | compilation.tokenTree = compilation.tokenTree.concat(createWastTokenTree(compilation)) 32 | } else { 33 | compilation.tokenTree = compilation.tokenTree.concat(createTokenTree(compilation)) 34 | } 35 | compilation.ns++ 36 | } 37 | 38 | compilation.globals = scanForGlobals(compilation.tokenTree) 39 | if (options.debug) { 40 | fs.writeFileSync(options.debug, JSON.stringify(compilation, null, 2)) 41 | } 42 | compilation.globals["-namespaces"] = compilation.namespaces 43 | let wast = compileModule(compilation) 44 | if (options.wast) { 45 | wast = pretty_wast(wast) 46 | fs.writeFileSync(options.wast, wast) 47 | } 48 | if (options.debug) { 49 | fs.writeFileSync(options.debug, JSON.stringify(compilation, null, 2)) 50 | } 51 | if (options.wasm) { 52 | let wasm = compileWast(wast, { write_debug_names: !!(options.wast || options.debug) }) 53 | fs.writeFileSync(options.wasm, wasm) 54 | } 55 | } 56 | 57 | function findConfig(dir) { 58 | let config = {} 59 | let tries = dir.split(path.sep).length + 1 60 | while (tries--) { 61 | try { 62 | config = { ...require(path.join(dir, "poetry.json")), ...config } 63 | if (config.includes) { 64 | for (let i = 0; i < config.includes.length; i++) { 65 | config.includes[i] = require.resolve(config.includes[i], { paths: [dir] }) 66 | } 67 | } 68 | } catch (error) { } 69 | if (dir !== path.dirname(dir)) { 70 | dir = path.dirname(dir) 71 | } else { 72 | dir = __dirname 73 | } 74 | } 75 | return config 76 | } 77 | 78 | function isKeyword(token) { return token && typeof token === "string" && (token[0] === "@") } 79 | function isProperty(token) { return token && typeof token === "string" && (token[0] === ":") } 80 | function isIdentifier(token) { return token && typeof token === "string" && (token[0].match(/[\$A-Z_a-z]/) || token.charCodeAt(0) > 127) } 81 | function isNumber(token) { return token && typeof token === "string" && (token[0].match(/[0-9]/)) } 82 | function isString(token) { return token && typeof token === "string" && (token[0].match(/["']/)) } 83 | function isSymbol(token) { return token && typeof token === "string" && (token[0].match(/[!#%&*+,\-/;<=>?^|]/)) } 84 | function isAssigner(token) { return token && typeof token === "string" && (token.substr(-1) === "=") } 85 | function isWast(token) { return token && typeof token === "object" && ["import", "global", "func", "export"].includes(token[0]) } 86 | 87 | function nextChar(c, peek) { 88 | let char = c.src[c.position] 89 | if (!peek) { 90 | c.position++ 91 | c.col++ 92 | if (char === "\n") { 93 | c.line++ 94 | c.col = 1 95 | c.lastNl = c.position - 1 96 | } 97 | } 98 | return char 99 | } 100 | function rewind(c) { 101 | c.position = 0 102 | c.line = 1 103 | c.col = 1 104 | c.indents = [0] 105 | c.lastNl = 0 106 | } 107 | function rewindToNl(c) { 108 | c.position = c.lastNl 109 | c.line-- 110 | } 111 | function croak(c, msg) { 112 | throw `${msg} at ${c.path}[${c.line}:${c.col}]!` 113 | } 114 | 115 | function escapeStr(str) { 116 | let buf = new Buffer(str) 117 | str = "" 118 | for (let i = 0; i < buf.length; i++) { 119 | if (buf[i] < 32 || buf[i] >= 127 || buf[i] === 34 || buf[i] === 92) { 120 | str += "\\" + ("0" + buf[i].toString(16)).substr(-2) 121 | } else { 122 | str += String.fromCharCode(buf[i]) 123 | } 124 | } 125 | return str 126 | } 127 | 128 | function nextToken(c) { 129 | let token = " " 130 | let char 131 | while (" \t".includes(token)) { 132 | token = nextChar(c) 133 | if (!token) return 134 | if (token === "~") { 135 | while (token !== "\n") { 136 | token = nextChar(c) 137 | } 138 | } 139 | if (token === "`") { 140 | token = nextChar(c) 141 | while (token !== "`") { 142 | token = nextChar(c) 143 | } 144 | token = nextChar(c) 145 | } 146 | } 147 | 148 | if (token.match(/\n/)) { 149 | let i = 0 150 | char = nextChar(c, true) 151 | while (!char.trim()) { 152 | nextChar(c) 153 | i++ 154 | if (char === "\n") i = 0 155 | char = nextChar(c, true) 156 | if (char === "~") { 157 | while (char !== "\n") { 158 | nextChar(c) 159 | char = nextChar(c, true) 160 | } 161 | } 162 | if (char === "`") { 163 | char = nextChar(c) 164 | char = nextChar(c) 165 | while (char !== "`") { 166 | char = nextChar(c) 167 | } 168 | char = nextChar(c, true) 169 | } 170 | } 171 | if (i > c.indents[c.indents.length - 1]) { 172 | c.indents.push(i) 173 | token = "{" 174 | } else if (i < c.indents[c.indents.length - 1]) { 175 | c.indents.pop() 176 | token = "}" 177 | rewindToNl(c) 178 | } else { 179 | token = ";" 180 | } 181 | } else if (token.match(/[\@]/)) { 182 | char = nextChar(c, true) 183 | while (char.match(/[A-Z_a-z]/)) { 184 | token += nextChar(c) 185 | char = nextChar(c, true) 186 | } 187 | token = token.toLowerCase() 188 | } else if (token.match(/[\:]/)) { 189 | char = nextChar(c, true) 190 | while (char.match(/[\$A-Z_a-z0-9.\\]/) || char.charCodeAt(0) > 127) { 191 | token += nextChar(c) 192 | char = nextChar(c, true) 193 | } 194 | } else if (token.match(/[\$A-Z_a-z]/) || token.charCodeAt(0) > 127) { 195 | char = nextChar(c, true) 196 | while (char.match(/[\$A-Z_a-z0-9.\\]/) || char.charCodeAt(0) > 127) { 197 | token += nextChar(c) 198 | char = nextChar(c, true) 199 | } 200 | token = token.toLowerCase() 201 | } else if (token.match(/[0-9.]/)) { 202 | let dot = token === "." 203 | char = nextChar(c, true).toLowerCase() 204 | if (token === "0" && char === "x") { 205 | token += nextChar(c).toLowerCase() 206 | char = nextChar(c, true).toLowerCase() 207 | while (char.match(/[0-9a-f]/)) { 208 | token += nextChar(c).toLowerCase() 209 | char = nextChar(c, true).toLowerCase() 210 | } 211 | } else { 212 | while (char.match(/[0-9]/) || (char === "." && !dot)) { 213 | if (char === ".") dot = true 214 | token += nextChar(c) 215 | char = nextChar(c, true) 216 | } 217 | } 218 | if (token.length > 1 && token[0] === ".") token = "0" + token 219 | } else if (token.match(/["']/)) { 220 | while (char !== token[0]) { 221 | if (char === "\\") token += nextChar(c) 222 | char = nextChar(c) 223 | token += char 224 | } 225 | token = '"' + token.substr(1, token.length - 2).replace(/(^|[^\\])"/g, '$1\\"') + '"' 226 | } else if (token.match(/[\{\(\[\;\]\)\}]/)) { 227 | } else while (token.substr(-1) === nextChar(c, true) || nextChar(c, true) === "=") { 228 | token += nextChar(c) 229 | } 230 | 231 | if (c.metaphors[token]) token = c.metaphors[token] 232 | if (isProperty(token)) { 233 | let str = token.substr(1) 234 | if (isNaN(str) && !c.strings.includes(str)) { 235 | c.strings.push(str) 236 | } 237 | } 238 | if (c.lastToken === "@import") { 239 | } else if (c.lastToken === "@import_memory") { 240 | } else if (c.lastToken === "@import_table") { 241 | } else if (c.lastToken === "@export") { 242 | } else if (c.lastToken === "@export_memory") { 243 | } else if (c.lastToken === "@export_table") { 244 | } else if (c.lastToken === "@include") { 245 | let filename = require.resolve(JSON.parse(token), { paths: [path.dirname(c.path)] }) 246 | if (!c.bundle.includes(filename)) { 247 | c.bundle.push(filename) 248 | } 249 | token = JSON.stringify(filename) 250 | } else if (isString(token)) { 251 | let str = JSON.parse(token) 252 | if (str && !c.strings.includes(str)) { 253 | c.strings.push(str) 254 | } 255 | } 256 | if (isIdentifier(token)) { 257 | token = "ns" + c.ns + "\\" + token.toLowerCase() 258 | } 259 | if (c.lastToken === "@import") { 260 | c.lastToken = "@export" 261 | } else { 262 | c.lastToken = token 263 | } 264 | return token 265 | } 266 | function nextWastToken(c) { 267 | let token = " " 268 | let char 269 | while (token.charCodeAt(0) <= 32) { 270 | token = nextChar(c) 271 | if (!token) return 272 | if (token === ";" && nextChar(c, true) === ";") { 273 | token = nextChar(c) 274 | while (token !== "\n") { 275 | token = nextChar(c) 276 | } 277 | } 278 | } 279 | 280 | if (token.match(/[\w]/)) { 281 | char = nextChar(c, true) 282 | while (char.match(/[^\(\)\s]/)) { 283 | token += nextChar(c) 284 | char = nextChar(c, true) 285 | } 286 | } else if (token.match(/[\$]/)) { 287 | char = nextChar(c, true) 288 | while (char.match(/[^\(\)\s]/)) { 289 | token += nextChar(c) 290 | char = nextChar(c, true) 291 | } 292 | } else if (token.match(/[\-0-9.]/)) { 293 | let dot = token === "." 294 | char = nextChar(c, true) 295 | while (char.match(/[0-9x]/) || (char === "." && !dot)) { 296 | if (char === ".") dot = true 297 | token += nextChar(c) 298 | char = nextChar(c, true) 299 | } 300 | if (token.length > 1 && token[0] === ".") token = "0" + token 301 | } else if (token.match(/["']/)) { 302 | while (char !== token[0]) { 303 | if (char === "\\") token += nextChar(c) 304 | char = nextChar(c) 305 | token += char 306 | } 307 | token = '"' + token.substr(1, token.length - 2).replace(/(^|[^\\])"/g, '$1\\"') + '"' 308 | } 309 | 310 | if (token[0] === "$" && token[1] !== "-") { 311 | token = "$ns" + c.ns + "\\" + token.substr(1).toLowerCase() 312 | } 313 | return token 314 | } 315 | 316 | function createTokenTree(c) { 317 | let token 318 | let tree = [] 319 | while ((token = nextToken(c)) && ("])}".includes(token) === false)) { 320 | if ("{([".includes(token)) { 321 | token = [token].concat(createTokenTree(c)) 322 | } else if (isSymbol(tree[tree.length - 1]) && token === "-") { 323 | token = [token, nextToken(c)] 324 | } 325 | tree.push(token) 326 | } 327 | tree.push(token) 328 | return tree 329 | } 330 | function createWastTokenTree(c) { 331 | let token 332 | let tree = [] 333 | while ((token = nextWastToken(c)) && (token !== ")")) { 334 | if (token === "(") { 335 | token = createWastTokenTree(c) 336 | } 337 | tree.push(token) 338 | } 339 | return tree 340 | } 341 | 342 | function scanForGlobals(tokenTree) { 343 | let globals = [] 344 | 345 | let statement = [] 346 | for (let token of tokenTree) { 347 | if (isWast(token)) { 348 | if (token[0] === "global") { 349 | if (isIdentifier(token[1].substr(1))) { 350 | if (globals[token[1].substr(1)]) throw `duplicate identifier "${token[1].substr(1)}"` 351 | globals[token[1].substr(1)] = true 352 | } 353 | } 354 | if (token[0] === "import") { 355 | token = token[3] 356 | } 357 | if (token[0] === "func") { 358 | if (isIdentifier(token[1].substr(1))) { 359 | if (globals[token[1].substr(1)]) throw `duplicate identifier "${token[1].substr(1)}"` 360 | let locals = [] 361 | for (let i = 2; (typeof token === "object") && (token[i][0] === "param"); i++) { 362 | if (isIdentifier(token[i][1].substr(1))) { 363 | if (locals.includes(token[i][1].substr(1))) throw `duplicate parameter "${token[i][1].substr(1)}"` 364 | locals.push(token[i][1].substr(1)) 365 | } 366 | } 367 | globals[token[1].substr(1)] = locals 368 | } 369 | } 370 | } else if (";}".includes(token)) { 371 | if (statement[0] === "@var") { 372 | if (isIdentifier(statement[1])) { 373 | if (globals[statement[1]]) throw `duplicate identifier "${statement[1]}"` 374 | globals[statement[1]] = true 375 | } 376 | } 377 | if (statement[0] === "@func" || statement[0] === "@import" || statement[0] === "@export") { 378 | if (statement[0] === "@import") { 379 | statement.splice(1, 2) 380 | } 381 | if (statement[0] === "@export") { 382 | statement.splice(1, 1) 383 | } 384 | if (isIdentifier(statement[1])) { 385 | if (globals[statement[1]]) throw `duplicate identifier "${statement[1]}"` 386 | let locals = [] 387 | if (statement[0] === "@import") { 388 | let params = parseInt(statement[2]) 389 | for (let i = 0; i < params; i++) { 390 | locals.push("p" + i) 391 | } 392 | } else { 393 | for (let i = 2; i < statement.length; i++) { 394 | if (isIdentifier(statement[i])) { 395 | if (locals.includes(statement[i])) throw `duplicate parameter "${statement[i]}"` 396 | locals.push(statement[i]) 397 | } 398 | } 399 | } 400 | globals[statement[1]] = locals 401 | } 402 | } 403 | statement = [] 404 | } else { 405 | statement.push(token) 406 | } 407 | } 408 | 409 | return globals 410 | } 411 | 412 | 413 | function compileModule(c) { 414 | let imports = "" 415 | let memory = `(memory $-memory 2) \n` 416 | let table = "" 417 | let globals = "" 418 | let functions = "" 419 | let start = "(call $-initruntime)\n" 420 | let startLocals = ["-ret"] 421 | let exports = "" 422 | let runtime = fs.readFileSync(path.join(__dirname, "runtime.wast")) 423 | let gc = "" 424 | 425 | let offset = 1024 * 64 426 | start += `(call $-resize (i32.const -1) (i32.const ${8 * c.strings.length}))` 427 | for (let i = 8; i < c.strings.length; i++) { 428 | let len = Buffer.byteLength(c.strings[i], 'utf8') 429 | memory += `(data (i32.const ${offset}) "${escapeStr(c.strings[i])}")\n` 430 | start += `(drop (call $-string (i32.const ${offset}) (i32.const ${len})))\n` 431 | offset += len 432 | offset = Math.floor(offset / 8) * 8 + 16 433 | } 434 | start += `(call $-funcstart)\n` 435 | c.globals["-string"] = c.strings 436 | c.globals["-table"] = [] 437 | 438 | let statement = [] 439 | for (let token of c.tokenTree) { 440 | if (isWast(token)) { 441 | if (token[0] === "import") imports += compileWastTokens(token) 442 | if (token[0] === "global") globals += compileWastTokens(token) 443 | if (token[0] === "func") functions += compileWastTokens(token) 444 | if (token[0] === "export") exports += compileWastTokens(token) 445 | } else if (";}".includes(token)) { 446 | if (statement[0] === "@var") { 447 | globals += `(global $${statement[1]} (mut i32) (i32.const 0))\n` 448 | statement.shift() 449 | } 450 | if (statement[0] === "@export") { 451 | exports += `(func $--${statement[2]}\n` 452 | for (let i = 3; i < statement.length - 1; i++) { 453 | exports += `(param $${statement[i]} f64)` 454 | } 455 | exports += `(result f64)(call $-funcstart)` 456 | exports += `(call $-funcend (call $${statement[2]}` 457 | for (let i = 3; i < statement.length - 1; i++) { 458 | exports += `(call $-number (get_local $${statement[i]}))` 459 | } 460 | exports += `))(call $-f64))(export ${statement[1]} (func $--${statement[2]}))\n` 461 | statement.shift() 462 | statement[0] = "@func" 463 | } 464 | if (statement[0] === "@import") { 465 | let name = statement[3] 466 | imports += `(import ${statement[1]} ${statement[2]} (func $--${name} ` 467 | let params = parseInt(statement[4]) 468 | let results = parseInt(statement[5]) || 1 469 | for (let i = 0; i < params; i++) { 470 | imports += `(param f64) ` 471 | } 472 | for (let i = 0; i < results; i++) { 473 | imports += `(result f64) ` 474 | } 475 | imports += `))\n` 476 | 477 | functions += `(func $${name}` 478 | for (let i = 0; i < params; i++) { 479 | functions += `(param $p${i} i32) ` 480 | } 481 | functions += `(result i32)\n` 482 | functions += `(call $--${name} ` 483 | for (let i = 0; i < params; i++) { 484 | functions += `(call $-f64 (call $-to_number (get_local $p${i}))) ` 485 | } 486 | functions += `)` 487 | if (results) { 488 | functions += `(call $-number)` 489 | } else { 490 | functions += `(i32.const 0)` 491 | } 492 | functions += `)\n` 493 | } else if (statement[0] === "@import_memory") { 494 | imports += `(import ${statement[1]} ${statement[2]} (memory 2))\n` 495 | memory = memory.substr(memory.indexOf(")") + 1) 496 | } else if (statement[0] === "@export_memory") { 497 | exports += `(export ${statement[1]} (memory $-memory))\n` 498 | } else if (statement[0] === "@import_table") { 499 | table += `(import ${statement[1]} ${statement[2]} (table $-table 1 anyfunc))\n` 500 | } else if (statement[0] === "@export_table") { 501 | exports += `(export ${statement[1]} (table $-table))\n` 502 | } else if (statement[0] === "@func") { 503 | functions += compileFunction(statement, c.globals) + "\n" 504 | } else if (statement[0] === "@include") { 505 | let filename = JSON.parse(statement[1]) 506 | let prefix = statement[2] || "" 507 | let ns = c.bundle.indexOf(filename) 508 | if (ns >= 0) { 509 | c.globals["-namespaces"][prefix] = c.globals["-namespaces"][prefix] || [] 510 | c.globals["-namespaces"][prefix].push("ns" + ns + "\\") 511 | } 512 | } else { 513 | start += compileStatement(statement, c.globals, startLocals) 514 | } 515 | statement = [] 516 | } else { 517 | statement.push(token) 518 | } 519 | } 520 | for (let i = 0; i < startLocals.length; i++) { 521 | start = `(local $${startLocals[i]} i32)` + start 522 | } 523 | if (!table) table = `(table $-table ${c.globals["-table"].length * 2} anyfunc)\n` 524 | for (let i = 0; i < c.globals["-table"].length; i++) { 525 | table += `(elem (i32.const ${i * 2}) $--${c.globals["-table"][i]})\n` 526 | table += `(elem (i32.const ${i * 2 + 1}) $${c.globals["-table"][i]})\n` 527 | 528 | exports += `(func $--${c.globals["-table"][i]}\n` 529 | for (let p = 0; p < c.globals[c.globals["-table"][i]].length; p++) { 530 | exports += `(param $${c.globals[c.globals["-table"][i]][p]} f64)` 531 | } 532 | exports += `(result f64)(call $-funcstart)` 533 | exports += `(call $-funcend (call $${c.globals["-table"][i]}` 534 | for (let p = 0; p < c.globals[c.globals["-table"][i]].length; p++) { 535 | exports += `(call $-number (get_local $${c.globals[c.globals["-table"][i]][p]}))` 536 | } 537 | exports += `))(call $-f64))\n` 538 | } 539 | gc += `(func $-passdown_globals\n` 540 | // gc += `(call $-zerorefs)\n` 541 | for (let g in c.globals) { 542 | if (c.globals[g] === true && !g.includes("~")) { 543 | gc += `(call $-passdown (get_global $${g}))\n` 544 | } 545 | } 546 | // gc += `(call $-garbagecollect)\n` 547 | gc += `)\n` 548 | 549 | return ` 550 | (module 551 | ;; imports 552 | ${imports} 553 | 554 | ;; memory 555 | ${memory} 556 | 557 | ;; table 558 | ${table} 559 | 560 | ;; globals 561 | ${globals} 562 | 563 | ;; functions 564 | ${functions} 565 | 566 | ;; start 567 | (func $-start 568 | (local $-success i32) 569 | ${start}) 570 | (start $-start) 571 | 572 | ;; exports 573 | ${exports} 574 | 575 | ;; runtime 576 | ${runtime} 577 | 578 | ;; gc 579 | ${gc} 580 | ) 581 | `.trim() 582 | } 583 | 584 | function compileFunction(tokenTree, globals) { 585 | let wast = "" 586 | let locals = [] 587 | tokenTree = deparens(tokenTree, true) 588 | globals["-blocks"] = -1 589 | 590 | wast += `\n;; function $${tokenTree[1]} \n` 591 | wast += `(func $${tokenTree[1]} ` 592 | for (let i = 2; i < tokenTree.length; i++) { 593 | if (isIdentifier(tokenTree[i])) { 594 | wast += `(param $${tokenTree[i]} i32) ` 595 | locals.push(tokenTree[i]) 596 | } 597 | if (tokenTree[i][0] === "{") { 598 | wast += `(result i32)\n` 599 | let paramlength = locals.length 600 | let block = compileBlock(tokenTree[i], globals, locals) 601 | for (let i = paramlength; i < locals.length; i++) { 602 | wast += `(local $${locals[i]} i32)` 603 | } 604 | wast += `(local $-ret i32)(local $-success i32)` 605 | wast += `(call $-funcstart)` 606 | wast += block 607 | wast += `(call $-funcend (get_local $-ret))` 608 | // wast += `(call $-passdown)` 609 | // wast += `(get_local $-ret)` 610 | } 611 | } 612 | wast += `)\n` 613 | 614 | return wast 615 | } 616 | 617 | function compileBlock(tokenTree, globals, locals) { 618 | let wast = "(block\n" 619 | globals["-blocks"]++ 620 | tokenTree = deparens(tokenTree, true) 621 | 622 | let statement = [] 623 | for (let token of tokenTree) { 624 | if (";}".includes(token)) { 625 | wast += compileStatement(statement, globals, locals) + "\n" 626 | statement = [] 627 | } else { 628 | statement.push(token) 629 | } 630 | } 631 | if (statement.length) { 632 | wast += compileStatement(statement, globals, locals) + "\n" 633 | } 634 | wast += "(set_local $-success (i32.const 1)))\n" 635 | globals["-blocks"]-- 636 | return wast 637 | } 638 | 639 | function compileStatement(tokenTree, globals, locals) { 640 | let wast = "" 641 | tokenTree = deparens(tokenTree, true) 642 | 643 | if (tokenTree[0] === "@var" || tokenTree[0] === "@for") { 644 | if (!locals.includes(tokenTree[1])) { 645 | locals.push(tokenTree[1]) 646 | } 647 | if (tokenTree[0] === "@var") tokenTree.shift() 648 | } 649 | if (tokenTree[0] === "@if") { 650 | wast += `(if (call $-truthy ${compileExpression(tokenTree.slice(1, tokenTree.length - 1), globals, locals)})\n` 651 | wast += `${compileBlock(tokenTree[tokenTree.length - 1], globals, locals)}`.replace("(block", "(then") 652 | wast += `(else (set_local $-success (i32.const 0))))` 653 | } else if (tokenTree[0] === "@elsif") { 654 | wast += `(if (i32.and (i32.eqz(get_local $-success)) (call $-truthy ${compileExpression(tokenTree.slice(1, tokenTree.length - 1), globals, locals)}))\n` 655 | wast += `${compileBlock(tokenTree[tokenTree.length - 1], globals, locals)})`.replace("(block", "(then") 656 | } else if (tokenTree[0] === "@else") { 657 | wast += `(if (i32.eqz (get_local $-success))\n` 658 | wast += `${compileBlock(tokenTree[tokenTree.length - 1], globals, locals)})`.replace("(block", "(then") 659 | } else if (tokenTree[0] === "@while") { 660 | wast += `(block(loop` 661 | globals["-blocks"] += 2 662 | wast += `(br_if 1 (call $-falsy ${compileExpression(tokenTree.slice(1, tokenTree.length - 1), globals, locals)}))` 663 | wast += ` ${compileBlock(tokenTree[tokenTree.length - 1], globals, locals)}` 664 | wast += `(br 0)))` 665 | globals["-blocks"] -= 2 666 | } else if (tokenTree[0] === "@for") { 667 | let fori = allocVar(locals, "-fori") 668 | let forl = allocVar(locals, "-forl") 669 | let fora = allocVar(locals, "-fora") 670 | let item = tokenTree[1] 671 | let array = tokenTree.slice(3, tokenTree.length - 1) 672 | wast += `(set_local $${fori} (i32.const 0) )` 673 | wast += `(set_local $${fora} ${compileExpression(array, globals, locals)} )` 674 | wast += `(set_local $${forl} (i32.div_u (call $-len (get_local $${fora}) ) (i32.const 4)) )` 675 | wast += `(block(loop` 676 | globals["-blocks"] += 2 677 | wast += `(br_if 1 (i32.ge_u (get_local $${fori}) (get_local $${forl}) ) )` 678 | wast += `(set_local $${item} (call $-get_from_obj (get_local $${fora}) (call $-integer_u (get_local $${fori}) )))` 679 | wast += ` ${compileBlock(tokenTree[tokenTree.length - 1], globals, locals)}` 680 | wast += `(set_local $${fori} (i32.add (get_local $${fori}) (i32.const 1)))(br 0)))` 681 | globals["-blocks"] -= 2 682 | } else if (tokenTree[0] === "@return") { 683 | wast += `(set_local $-ret ${compileExpression(tokenTree.slice(1), globals, locals)})(br ${globals["-blocks"]})\n` 684 | } else { 685 | let exprs = compileExpression(tokenTree, globals, locals, true) 686 | for (let expr of exprs) { 687 | if (expr) wast += `(drop ${expr})\n` 688 | } 689 | } 690 | 691 | return wast 692 | } 693 | 694 | function compileExpression(tokenTree, globals, locals, list) { 695 | let wast = "" 696 | let _values, values = deparens(tokenTree) 697 | if (values[0] === "{") return compileObjLit(tokenTree, globals, locals) 698 | 699 | for (let i = 0; i < values.length; i++) { 700 | let token = values[i] 701 | if (token === "#") { 702 | let operand = values[i + 1] 703 | if (isIdentifier(operand) && typeof globals[resolveIdentifier(operand, globals)] === "object" && !locals.includes(operand)) { 704 | let name = resolveIdentifier(operand, globals) 705 | if (!globals["-table"].includes(name)) globals["-table"].push(name) 706 | values.splice(i, 1) 707 | values[i] = `(call $-integer_u (i32.const ${globals["-table"].indexOf(name) * 2}))` 708 | } else { 709 | let index = compileExpression([operand], globals, locals) 710 | let a = values.slice(0, i) 711 | let b = values.slice(i + 2, values.length) 712 | values = a 713 | let args = compileExpression(b, globals, locals, true) 714 | let wast = `(call_indirect ` 715 | for (let i = 0; i < args.length; i++) { 716 | wast += `(param i32) ` 717 | } 718 | wast += `(result i32)\n` 719 | wast += args.join(" ") 720 | wast += `(i32.add (i32.const 1) (call $-i32_u ${index})))` 721 | 722 | values.push(wast) 723 | } 724 | } 725 | if (isIdentifier(token) && typeof globals[resolveIdentifier(token, globals)] === "object" && !locals.includes(token)) { 726 | let name = resolveIdentifier(token, globals) 727 | let a = values.slice(0, i) 728 | let b = values.slice(i + 1, values.length) 729 | values = a 730 | let args = compileExpression(b, globals, locals, true) 731 | 732 | while (args.length > globals[name].length) args.pop() 733 | while (args.length < globals[name].length) args.push("(i32.const 0)") 734 | values.push(`(call $${name} ${args.join(" ")})`) 735 | } 736 | } 737 | 738 | _values = values 739 | values = [] 740 | for (let token of _values) { 741 | values.push(token) 742 | if (typeof token === "object" && token[0] !== "[") { 743 | values.pop() 744 | values.push(compileExpression(token, globals, locals)) 745 | } 746 | if (isIdentifier(token)) { 747 | let id = resolveIdentifier(values.pop(), globals) 748 | if (locals.includes(token)) { 749 | values.push(`(get_local $${token})`) 750 | } else if (globals[id]) { 751 | if (typeof globals[id] === "object") { 752 | values.push(id) 753 | } else { 754 | values.push(`(get_global $${id})`) 755 | } 756 | } else { 757 | throw `undeclared identifier '${id}'` 758 | // values.push(`(i32.const 0)`) 759 | } 760 | } 761 | if (token === "@null") { 762 | values.pop() 763 | values.push(`(i32.const 0)`) 764 | } 765 | if (token === "@false") { 766 | values.pop() 767 | values.push(`(i32.const 1)`) 768 | } 769 | if (token === "@true") { 770 | values.pop() 771 | values.push(`(i32.const 5)`) 772 | } 773 | if (token === "@array") { 774 | values.pop() 775 | values.push(`(call $-new_value (i32.const 4) (i32.const 0))`) 776 | } 777 | if (token === "@object") { 778 | values.pop() 779 | values.push(`(call $-new_value (i32.const 5) (i32.const 0))`) 780 | } 781 | if (token === "@binary") { 782 | values.pop() 783 | values.push(`(call $-new_value (i32.const 6) (i32.const 0))`) 784 | } 785 | if (isNumber(token)) { 786 | let num = values.pop() 787 | values.push(`(call $-number (f64.const ${num}))`) 788 | } 789 | if (isString(token)) { 790 | let str = JSON.parse(values.pop()) 791 | values.push(`(i32.const ${globals["-string"].indexOf(str)})`) 792 | } 793 | } 794 | _values = values 795 | values = [] 796 | for (let token of _values) { 797 | values.push(token) 798 | if (isProperty(token)) { 799 | let prop = values.pop().substr(1) 800 | let obj = values.pop() 801 | if (isNaN(prop)) { 802 | prop = `(i32.const ${globals["-string"].indexOf(prop)})` 803 | } else { 804 | prop = `(call $-number (f64.const ${prop}))` 805 | } 806 | values.push(`(call $-get_from_obj ${obj} ${prop})`) 807 | } 808 | if (typeof token === "object" && token[0] === "[") { 809 | let prop = compileExpression(values.pop(), globals, locals) 810 | let obj = values.pop() 811 | values.push(`(call $-get_from_obj ${obj} ${prop})`) 812 | } 813 | } 814 | _values = values 815 | values = [] 816 | for (let token of _values) { 817 | values.push(token) 818 | if (values[values.length - 1] === "++") { 819 | values.pop() 820 | let operand = values.pop() 821 | values.push(operand) 822 | values.push(`=`) 823 | values.push(`(call $-inc ${operand} (f64.const 1))`) 824 | } 825 | if (values[values.length - 1] === "--") { 826 | values.pop() 827 | let operand = values.pop() 828 | values.push(operand) 829 | values.push(`=`) 830 | values.push(`(call $-inc ${operand} (f64.const -1))`) 831 | } 832 | } 833 | _values = values 834 | values = [] 835 | for (let token of _values) { 836 | values.push(token) 837 | if (values[values.length - 2] === "*") { 838 | let operand2 = values.pop() 839 | values.pop() 840 | let operand1 = values.pop() 841 | values.push(`(call $-mul ${operand1} ${operand2})`) 842 | } 843 | if (values[values.length - 2] === "/") { 844 | let operand2 = values.pop() 845 | values.pop() 846 | let operand1 = values.pop() 847 | values.push(`(call $-div ${operand1} ${operand2})`) 848 | } 849 | if (values[values.length - 2] === "%") { 850 | let operand2 = values.pop() 851 | values.pop() 852 | let operand1 = values.pop() 853 | values.push(`(call $-mod ${operand1} ${operand2})`) 854 | } 855 | } 856 | _values = values 857 | values = [] 858 | for (let token of _values) { 859 | values.push(token) 860 | if (values[values.length - 2] === "+") { 861 | let operand2 = values.pop() 862 | values.pop() 863 | let operand1 = values.pop() 864 | values.push(`(call $-add ${operand1} ${operand2})`) 865 | } 866 | if (values[values.length - 2] === "-") { 867 | let operand2 = values.pop() 868 | values.pop() 869 | let operand1 = values.pop() || "(i32.const 2)" 870 | values.push(`(call $-sub ${operand1} ${operand2})`) 871 | } 872 | } 873 | _values = values 874 | values = [] 875 | for (let token of _values) { 876 | values.push(token) 877 | if (values[values.length - 2] === "<") { 878 | let operand2 = values.pop() 879 | values.pop() 880 | let operand1 = values.pop() 881 | values.push(`(call $-lt ${operand1} ${operand2})`) 882 | } 883 | if (values[values.length - 2] === "<=") { 884 | let operand2 = values.pop() 885 | values.pop() 886 | let operand1 = values.pop() 887 | values.push(`(call $-le ${operand1} ${operand2})`) 888 | } 889 | if (values[values.length - 2] === ">") { 890 | let operand2 = values.pop() 891 | values.pop() 892 | let operand1 = values.pop() 893 | values.push(`(call $-gt ${operand1} ${operand2})`) 894 | } 895 | if (values[values.length - 2] === ">=") { 896 | let operand2 = values.pop() 897 | values.pop() 898 | let operand1 = values.pop() 899 | values.push(`(call $-ge ${operand1} ${operand2})`) 900 | } 901 | } 902 | _values = values 903 | values = [] 904 | for (let token of _values) { 905 | values.push(token) 906 | if (values[values.length - 2] === "==") { 907 | let operand2 = values.pop() 908 | values.pop() 909 | let operand1 = values.pop() 910 | values.push(`(call $-equal ${operand1} ${operand2})`) 911 | } 912 | if (values[values.length - 2] === "!=") { 913 | let operand2 = values.pop() 914 | values.pop() 915 | let operand1 = values.pop() 916 | values.push(`(call $-unequal ${operand1} ${operand2})`) 917 | } 918 | } 919 | _values = values 920 | values = [] 921 | for (let token of _values) { 922 | values.push(token) 923 | if (values[values.length - 2] === "&&") { 924 | let operand2 = values.pop() 925 | values.pop() 926 | let operand1 = values.pop() 927 | values.push(`(call $-and ${operand1} ${operand2})`) 928 | } 929 | if (values[values.length - 2] === "||") { 930 | let operand2 = values.pop() 931 | values.pop() 932 | let operand1 = values.pop() 933 | values.push(`(call $-or ${operand1} ${operand2})`) 934 | } 935 | } 936 | _values = values 937 | values = [] 938 | for (let token of _values) { 939 | values.push(token) 940 | if (isAssigner(values[values.length - 2])) { 941 | let operand2 = values.pop() 942 | let op = values.pop() 943 | let operand1 = values.pop() 944 | let setter = operand1 945 | if (setter.indexOf(`(get_local `) === 0) { 946 | setter = setter.replace(`(get_local `, `(set_local `) 947 | } else if (setter.indexOf(`(get_global `) === 0) { 948 | setter = setter.replace(`(get_global `, `(set_global `) 949 | } else if (setter.indexOf(`(call $-get_from_obj `) === 0) { 950 | setter = setter.replace(`(call $-get_from_obj `, `(call $-set_to_obj `) 951 | } else { 952 | throw `cannot assign to ${setter}` 953 | } 954 | if (op[0] === "+") { 955 | operand2 = `(call $-add ${operand1} ${operand2})` 956 | } 957 | if (op[0] === "-") { 958 | operand2 = `(call $-sub ${operand1} ${operand2})` 959 | } 960 | if (op[0] === "*") { 961 | operand2 = `(call $-mul ${operand1} ${operand2})` 962 | } 963 | if (op[0] === "/") { 964 | operand2 = `(call $-div ${operand1} ${operand2})` 965 | } 966 | if (op[0] === "%") { 967 | operand2 = `(call $-mod ${operand1} ${operand2})` 968 | } 969 | setter = setter.substr(0, setter.lastIndexOf(")")) 970 | operand2 += ")" 971 | values.push(`${setter} ${operand2} ${operand1}\n`) 972 | } 973 | } 974 | 975 | if (list) return values 976 | return values.join(" ") 977 | } 978 | 979 | function compileObjLit(tokenTree, globals, locals) { 980 | let wast = "" 981 | let datatype = 5 982 | let index = 0 983 | tokenTree = deparens(tokenTree, true) 984 | tokenTree.push(";") 985 | 986 | let name = 0 987 | while (locals.includes("-obj" + name)) name++ 988 | locals.push(name = "-obj" + name) 989 | 990 | let statement = [] 991 | for (let token of tokenTree) { 992 | if (";}".includes(token)) { 993 | if (statement.length > 1 && isProperty(statement[0])) { 994 | statement.unshift(`(get_local $${name})`) 995 | wast += `(drop ${compileExpression(statement, globals, locals)})\n` 996 | index += 2 997 | } else { 998 | wast += `(call $-set_to_obj (get_local $${name}) (call $-number (f64.const ${index})) ${compileExpression(statement, globals, locals)})\n` 999 | index++ 1000 | datatype = 4 1001 | } 1002 | statement = [] 1003 | } else { 1004 | statement.push(token) 1005 | } 1006 | } 1007 | wast = `(tee_local $${name} (call $-new_value (i32.const ${datatype}) (i32.const 0)))\n` + wast 1008 | 1009 | 1010 | return wast 1011 | } 1012 | 1013 | function compileWastTokens(tokens, indent = "\n") { 1014 | wast = "" 1015 | for (let token of tokens) { 1016 | if (typeof token === "object") { 1017 | wast += compileWastTokens(token, indent + " ") 1018 | } else { 1019 | wast += token + " " 1020 | } 1021 | } 1022 | return `${indent}(${wast})` 1023 | } 1024 | 1025 | function deparens(tokens, all) { 1026 | let start = tokens[0] 1027 | let end = tokens[tokens.length - 1] 1028 | if (tokens.length === 1 && typeof start === "object") return deparens(start, all) 1029 | if (all && start === "{" && end === "}") return deparens(tokens.slice(1, tokens.length - 1), all) 1030 | if (start === "(" && end === ")") return deparens(tokens.slice(1, tokens.length - 1), all) 1031 | if (start === "[" && end === "]") return deparens(tokens.slice(1, tokens.length - 1), all) 1032 | return tokens.slice() 1033 | } 1034 | 1035 | function compileWast(wast, options) { 1036 | return wabt.parseWat("boot", wast).toBinary(options).buffer 1037 | } 1038 | 1039 | function resolveIdentifier(identifier, globals) { 1040 | let ns = identifier.substr(0, identifier.indexOf("\\") + 1) 1041 | let name = identifier.substr(identifier.indexOf("\\") + 1) 1042 | if (globals[identifier]) { 1043 | return identifier 1044 | } else { 1045 | for (let prefix in globals["-namespaces"]) { 1046 | let namespaces = globals["-namespaces"][prefix] 1047 | prefix = prefix || ns 1048 | for (let namespace of namespaces) { 1049 | if (identifier.substr(0, prefix.length) === prefix && 1050 | globals[identifier.replace(prefix, namespace)]) { 1051 | return identifier.replace(prefix, namespace) 1052 | } 1053 | } 1054 | } 1055 | } 1056 | return identifier 1057 | } 1058 | 1059 | function allocVar(pool, prefix) { 1060 | let i = 0 1061 | while (pool.includes(prefix + i)) i++ 1062 | pool.push(prefix + i) 1063 | return prefix + i 1064 | } 1065 | 1066 | module.exports = compile 1067 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poetry-compiler", 3 | "version": "0.48.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "commander": { 8 | "version": "2.18.0", 9 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz", 10 | "integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ==" 11 | }, 12 | "wabt": { 13 | "version": "1.0.5", 14 | "resolved": "https://registry.npmjs.org/wabt/-/wabt-1.0.5.tgz", 15 | "integrity": "sha512-RVZyd7G3jTKVkXLgClKKxEkUqglKQ/NNnCln7wWP851+VhetU7Z2UdKm6ztMdLXWtSiFhIvqgGWJbkvwZYHoKg==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "poetry-compiler", 3 | "version": "0.48.0", 4 | "description": "Poetic programming language that compiles to webassembly", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "node cli.js boot.poem -t boot.wast -b boot.wasm --debug boot.debug.json", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "bin": { 11 | "poetry": "./cli.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/FantasyInternet/poetry.git" 16 | }, 17 | "keywords": [ 18 | "wasm", 19 | "webassembly", 20 | "compiler", 21 | "programming", 22 | "language", 23 | "poetry" 24 | ], 25 | "author": "", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/FantasyInternet/poetry/issues" 29 | }, 30 | "homepage": "https://github.com/FantasyInternet/poetry#readme", 31 | "dependencies": { 32 | "commander": "^2.17.1", 33 | "wabt": "^1.0.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /poetry.json: -------------------------------------------------------------------------------- 1 | { 2 | "includes": [ 3 | "./stdlib.wast" 4 | ], 5 | "metaphors": { 6 | "array": "@array", 7 | "binary": "@binary", 8 | "else": "@else", 9 | "elsif": "@elsif", 10 | "export": "@export", 11 | "export_memory": "@export_memory", 12 | "export_table": "@export_table", 13 | "false": "@false", 14 | "for": "@for", 15 | "func": "@func", 16 | "if": "@if", 17 | "in": "@in", 18 | "import": "@import", 19 | "import_memory": "@import_memory", 20 | "import_table": "@import_table", 21 | "include": "@include", 22 | "var": "@var", 23 | "null": "@null", 24 | "object": "@object", 25 | "return": "@return", 26 | "true": "@true", 27 | "while": "@while" 28 | } 29 | } -------------------------------------------------------------------------------- /prettify_wast.js: -------------------------------------------------------------------------------- 1 | 2 | function prettify(wast) { 3 | let out = "" 4 | let indent = true 5 | let tokens = tokenize(wast.trim() + "\n") 6 | //return JSON.stringify(tokens,null,2) 7 | let stack = [] 8 | tokens = tokens[0] 9 | while (tokens) { 10 | let token = tokens.shift() 11 | if (typeof token === "object") { 12 | if (indent) { 13 | out += "\n" 14 | for (let i = 0; i < stack.length; i++) out += " " 15 | } 16 | out += "(" 17 | stack.push(tokens) 18 | tokens = token 19 | //out += countArrays(tokens) 20 | indent = countArrays(tokens) > 1 21 | } else if (token.substr(0, 2) === ";;") { 22 | if (indent) { 23 | out += "\n\n" 24 | for (let i = 0; i < stack.length; i++) out += " " 25 | } 26 | out += token + " " 27 | } else { 28 | out += token + " " 29 | } 30 | while (tokens && !tokens.length) { 31 | out += ") " 32 | tokens = stack.pop() 33 | indent = true 34 | //indent = countArrays(tokens) > 1 35 | } 36 | } 37 | return trimLineEnds(out.substr(0, out.lastIndexOf(")"))) 38 | } 39 | 40 | function tokenize(wast) { 41 | let stack = [] 42 | let tokens = [] 43 | let pos = 0 44 | let char = "" 45 | while (pos < wast.length) { 46 | //console.log(pos) 47 | let token = "" 48 | while (char && !char.trim()) { 49 | char = wast[pos++] 50 | } 51 | if (char === ";" && wast[pos] === ";") { 52 | //token="\n\n" 53 | while (char && char !== "\n") { 54 | token += char 55 | char = wast[pos++] 56 | } 57 | token += char 58 | char = wast[pos++] 59 | } else if (char === "(" && wast[pos] === ";") { 60 | while (char && token.substr(-2) !== ";)") { 61 | token += char 62 | char = wast[pos++] 63 | } 64 | } else if ("(".includes(char)) { 65 | stack.push(tokens) 66 | tokens.push(tokens = []) 67 | char = wast[pos++] 68 | } else if (")".includes(char)) { 69 | tokens = stack.pop() 70 | char = wast[pos++] 71 | } else if ("\"\'".includes(char)) { 72 | while (char && char !== token[0]) { 73 | if (char === "\\") { 74 | token += char 75 | char = wast[pos++] 76 | } 77 | token += char 78 | char = wast[pos++] 79 | } 80 | token += char 81 | char = wast[pos++] 82 | } else { 83 | do { 84 | token += char 85 | char = wast[pos++] 86 | } while (char && char.trim() && !"(\";\')".includes(char)) 87 | } 88 | token && tokens.push(token) 89 | //console.log(token) 90 | } 91 | while (stack.length) tokens = stack.pop() 92 | return tokens 93 | } 94 | 95 | function countArrays(arr) { 96 | if (!arr) return 0 97 | let count = 0 98 | for (let elem of arr) { 99 | if (typeof elem === "object") count++ 100 | } 101 | return count 102 | } 103 | 104 | function trimLineEnds(txt) { 105 | while (txt.includes(" \n")) { 106 | txt = txt.replace(/ \n/g, "\n"); 107 | } 108 | return txt.trim() + "\n" 109 | } 110 | 111 | module.exports = prettify -------------------------------------------------------------------------------- /runtime.wast: -------------------------------------------------------------------------------- 1 | ;; memory management 2 | (global $-totmem (mut i32) (i32.const 0)) 3 | (func $-initruntime 4 | (set_global $-totmem (i32.mul (i32.const 65536) (current_memory))) 5 | (i32.store (i32.const 0) (i32.sub (i32.mul (i32.const 65536) (current_memory)) (i32.const 8))) 6 | (set_global $-mindex (call $-alloc (i32.const 8))) 7 | ) 8 | 9 | ;; function wrapper 10 | (global $-calls (mut i32) (i32.const 0)) 11 | (func $-funcstart 12 | (if (i32.le_u (get_global $-calls) (i32.const 1))(then 13 | ;; (set_global $-passdown_mark (i32.and (i32.add (get_global $-passdown_mark) (i32.const 1)) (i32.const 127))) 14 | (call $-zerorefs) 15 | (call $-garbagecollect) 16 | )) 17 | (set_global $-calls (i32.add (get_global $-calls) (i32.const 1))) 18 | ) 19 | (global $-gc_pending (mut i32) (i32.const 1)) 20 | (func $-funcend (param $ret i32) (result i32) 21 | (if (get_global $-calls) (then 22 | (set_global $-calls (i32.sub (get_global $-calls) (i32.const 1))) 23 | (if (get_local $ret)(then 24 | (set_global $-passdown_mark (i32.and (i32.add (get_global $-passdown_mark) (i32.const 1)) (i32.const 127))) 25 | (call $-passdown (get_local $ret)) 26 | )) 27 | (if (i32.gt_u (get_global $-gc_pending) (get_global $-calls))(then 28 | (call $-garbagecollect) 29 | )) 30 | )) 31 | (get_local $ret) 32 | ) 33 | 34 | ;; allocate memory 35 | (global $-last_alloc (mut i32) (i32.const 0)) 36 | (func $-alloc (param $len i32) (result i32) 37 | (local $offset i32) 38 | (local $offset2 i32) 39 | (local $space i32) 40 | (local $space2 i32) 41 | (local $totmem i32) 42 | (local $allowgrow i32) 43 | 44 | (if (get_global $-last_alloc)(then 45 | (set_local $offset (i32.sub (get_global $-last_alloc) (i32.const 8))) 46 | (set_local $offset (i32.sub (get_local $offset) (i32.load (get_local $offset)))) 47 | )(else 48 | (set_local $allowgrow (i32.const 1)) 49 | )) 50 | ;; how much space is here at the beginning? 51 | (set_local $space (i32.load (get_local $offset))) 52 | ;; round down to nearest multiple of 8 53 | (set_local $space (i32.and (get_local $space) (i32.const -8) ) ) 54 | (block(loop 55 | ;; is there enough space here? 56 | (br_if 1 (i32.gt_u (get_local $space) (i32.add (get_local $len) (i32.const 32)))) 57 | ;; skip the space 58 | (set_local $offset (i32.add (i32.add (get_local $offset) (get_local $space)) (i32.const 4))) 59 | ;; how much data is here? 60 | (set_local $space (i32.load (get_local $offset))) 61 | 62 | ;; is this the end of memory? 63 | (if (i32.le_u (i32.sub (get_global $-totmem) (get_local $offset)) (i32.const 8))(then 64 | ;; are we allowed to grow memory? 65 | (if (get_local $allowgrow)(then 66 | (set_local $offset2 (i32.add (get_global $-totmem) (i32.const 8))) 67 | (drop (grow_memory (current_memory))) 68 | (set_global $-totmem (i32.mul (i32.const 65536) (current_memory))) 69 | (i32.store (get_local $offset2) (i32.sub (get_global $-totmem) (i32.add (i32.const 8) (get_local $offset2)))) 70 | (call $-dealloc (i32.sub (get_local $offset2) (i32.const 8))) 71 | (set_local $space (i32.load (i32.const 0))) 72 | (set_local $offset (i32.add (get_local $space) (i32.const 4))) 73 | (set_local $space (i32.load (get_local $offset))) 74 | )(else ;; first time? start from beginning 75 | (call $-garbagecollect) 76 | (set_local $allowgrow (i32.const 1)) 77 | (set_local $offset (i32.const 0)) 78 | (set_local $space (i32.load (get_local $offset))) 79 | (set_local $space (i32.and (get_local $space) (i32.const -8) ) ) 80 | (set_local $offset (i32.add (i32.add (get_local $offset) (get_local $space)) (i32.const 4))) 81 | (set_local $space (i32.load (get_local $offset))) 82 | )) 83 | )) 84 | 85 | ;; skip the data 86 | (set_local $offset (i32.add (i32.add (get_local $offset) (get_local $space)) (i32.const 4))) 87 | ;; align to next multiple of 8 88 | (set_local $offset (i32.add (i32.and (get_local $offset) (i32.const -8)) (i32.const 8))) 89 | ;; how much space is here? 90 | (set_local $space (i32.load (get_local $offset))) 91 | ;; round down to nearest multiple of 8 92 | (set_local $space (i32.and (get_local $space) (i32.const -8) ) ) 93 | (br 0) 94 | )) 95 | ;; claim the space 96 | (i32.store (get_local $offset) (i32.const 0)) 97 | (set_local $offset2 (i32.add (get_local $offset) (i32.const 4))) 98 | (i32.store (get_local $offset2) (get_local $len)) 99 | 100 | ;; skip allocation 101 | (set_local $offset2 (i32.add (i32.add (get_local $offset2) (get_local $len)) (i32.const 4))) 102 | ;; round down to nearest multiple of 8 103 | (set_local $offset2 (i32.and (get_local $offset2) (i32.const -8))) 104 | ;; set terminator 105 | (i64.store (get_local $offset2) (i64.const 0)) 106 | (set_local $offset2 (i32.add (get_local $offset2) (i32.const 8))) 107 | ;; how much less space is there now? 108 | (set_local $space2 (i32.sub (get_local $space) (i32.sub (get_local $offset2) (get_local $offset)))) 109 | ;; mark the space at both ends 110 | (i32.store (get_local $offset2) (get_local $space2)) 111 | (set_local $offset2 (i32.add (get_local $offset2) (get_local $space2))) 112 | (i32.store (get_local $offset2) (get_local $space2)) 113 | 114 | ;; zerofill allocation 115 | (set_local $offset (i32.add (i32.const 8) (get_local $offset))) 116 | (call $-memzero (get_local $offset) (get_local $len)) 117 | 118 | ;; return offset where the data is supposed to begin 119 | (set_global $-last_alloc (get_local $offset)) 120 | (return (get_local $offset)) 121 | ) 122 | 123 | ;; deallocate memory 124 | (func $-dealloc (param $offset i32) 125 | (local $offset i32) 126 | (local $offset2 i32) 127 | (local $space i32) 128 | (local $space2 i32) 129 | 130 | (if (i32.eq (get_local $offset) (get_global $-last_alloc))(then 131 | (set_global $-last_alloc (i32.const 0)) 132 | )) 133 | (set_local $offset (i32.sub (i32.and (get_local $offset) (i32.const -8)) (i32.const 8))) 134 | (set_local $space (i32.load (get_local $offset))) 135 | (set_local $space (i32.and (get_local $space) (i32.const -8) ) ) 136 | (set_local $offset (i32.sub (get_local $offset) (get_local $space))) 137 | 138 | (set_local $offset2 (i32.add (i32.add (get_local $offset) (get_local $space)) (i32.const 4))) 139 | (set_local $space2 (i32.load (get_local $offset2))) 140 | (set_local $offset2 (i32.add (i32.add (get_local $offset2) (get_local $space2)) (i32.const 4))) 141 | (set_local $offset2 (i32.add (i32.and (get_local $offset2) (i32.const -8)) (i32.const 8))) 142 | (set_local $space2 (i32.load (get_local $offset2))) 143 | (set_local $space2 (i32.and (get_local $space2) (i32.const -8) ) ) 144 | (set_local $offset2 (i32.add (get_local $offset2) (get_local $space2))) 145 | 146 | (set_local $space (i32.sub (get_local $offset2) (get_local $offset))) 147 | (i32.store (get_local $offset) (get_local $space)) 148 | (i32.store (get_local $offset2) (get_local $space)) 149 | ) 150 | 151 | ;; zerofill memory 152 | (func $-memzero (param $offset i32) (param $len i32) 153 | (block(loop (br_if 1 (i32.lt_u (get_local $len) (i32.const 8))) 154 | (i64.store (get_local $offset) (i64.const 0)) 155 | (set_local $offset (i32.add (get_local $offset) (i32.const 8))) 156 | (set_local $len (i32.sub (get_local $len) (i32.const 8))) 157 | (br 0))) 158 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 159 | (i32.store8 (get_local $offset) (i32.const 0)) 160 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 161 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 162 | (br 0))) 163 | ) 164 | 165 | ;; copy memory 166 | (func $-memcopy (param $from i32) (param $to i32) (param $len i32) 167 | (local $delta i32) 168 | (if (i32.lt_u (get_local $from) (get_local $to))(then 169 | (set_local $delta (i32.const -8)) 170 | (set_local $from (i32.add (get_local $from) (get_local $len))) 171 | (set_local $to (i32.add (get_local $to) (get_local $len))) 172 | (block(loop (br_if 1 (i32.lt_s (get_local $len) (i32.const 8))) 173 | (set_local $from (i32.add (get_local $from) (get_local $delta))) 174 | (set_local $to (i32.add (get_local $to) (get_local $delta))) 175 | (i64.store (get_local $to) (i64.load (get_local $from))) 176 | (set_local $len (i32.sub (get_local $len) (i32.const 8))) 177 | (br 0) )) 178 | (set_local $delta (i32.const -1)) 179 | (block(loop (br_if 1 (i32.lt_s (get_local $len) (i32.const 1))) 180 | (set_local $from (i32.add (get_local $from) (get_local $delta))) 181 | (set_local $to (i32.add (get_local $to) (get_local $delta))) 182 | (i32.store8 (get_local $to) (i32.load8_u (get_local $from))) 183 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 184 | (br 0) )) 185 | )(else 186 | (set_local $delta (i32.const 8)) 187 | (block(loop (br_if 1 (i32.lt_s (get_local $len) (i32.const 8))) 188 | (i64.store (get_local $to) (i64.load (get_local $from))) 189 | (set_local $from (i32.add (get_local $from) (get_local $delta))) 190 | (set_local $to (i32.add (get_local $to) (get_local $delta))) 191 | (set_local $len (i32.sub (get_local $len) (i32.const 8))) 192 | (br 0) )) 193 | (set_local $delta (i32.const 1)) 194 | (block(loop (br_if 1 (i32.lt_s (get_local $len) (i32.const 1))) 195 | (i32.store8 (get_local $to) (i32.load8_u (get_local $from))) 196 | (set_local $from (i32.add (get_local $from) (get_local $delta))) 197 | (set_local $to (i32.add (get_local $to) (get_local $delta))) 198 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 199 | (br 0) )) 200 | )) 201 | ) 202 | 203 | ;; memory index 204 | (global $-mindex (mut i32) (i32.const 0)) 205 | 206 | ;; offset of memory allocation 207 | (func $-offset (param $id i32) (result i32) 208 | (local $offset i32) 209 | (if (i32.eq (get_local $id) (i32.const -1))(then 210 | (set_local $offset (get_global $-mindex)) 211 | )(else 212 | (if (i32.lt_u (get_local $id) (i32.const 8))(then 213 | (set_local $offset (i32.const 0)) 214 | )(else 215 | (set_local $id (i32.sub (get_local $id) (i32.const 8))) 216 | (if (i32.gt_u (call $-len (i32.const -1)) (i32.mul (i32.const 8) (get_local $id)))(then 217 | (set_local $offset (i32.add (get_global $-mindex) (i32.mul (i32.const 8) (get_local $id)))) 218 | (set_local $offset (i32.load (get_local $offset))) 219 | )) 220 | )) 221 | )) 222 | (set_local $offset (i32.and (get_local $offset) (i32.const -8))) 223 | (get_local $offset) 224 | ) 225 | 226 | ;; datatype of memory allocation 227 | (func $-datatype (param $id i32) (result i32) 228 | (local $datatype i32) 229 | (if (i32.eq (get_local $id) (i32.const -1))(then 230 | (set_local $datatype (i32.const 7)) 231 | )(else 232 | (if (i32.lt_u (get_local $id) (i32.const 8))(then 233 | (set_local $datatype (i32.and (get_local $id) (i32.const 3))) 234 | )(else 235 | (set_local $datatype (i32.sub (get_global $-mindex) (i32.const 64))) 236 | (set_local $datatype (i32.add (get_local $datatype) (i32.add (i32.mul (i32.const 8) (get_local $id)) (i32.const 6)))) 237 | (set_local $datatype (i32.load8_u (get_local $datatype))) 238 | )) 239 | )) 240 | (get_local $datatype) 241 | ) 242 | 243 | ;; length of memory allocation 244 | (func $-len (param $id i32) (result i32) 245 | (local $offset i32) 246 | (local $len i32) 247 | (set_local $offset (call $-offset (get_local $id))) 248 | (if (get_local $offset)(then 249 | (set_local $len (i32.load (i32.sub (get_local $offset) (i32.const 4)))) 250 | )) 251 | (get_local $len) 252 | ) 253 | 254 | ;; callstack-depth of memory allocation 255 | (func $-callstack_depth (param $id i32) (result i32) 256 | (local $depth i32) 257 | (if (i32.ge_u (get_local $id) (i32.const 8))(then 258 | (set_local $depth (i32.sub (get_global $-mindex) (i32.const 64))) 259 | (set_local $depth (i32.add (get_local $depth) (i32.add (i32.mul (i32.const 8) (get_local $id)) (i32.const 4)))) 260 | (set_local $depth (i32.load16_u (get_local $depth))) 261 | )) 262 | (get_local $depth) 263 | ) 264 | 265 | ;; resize memory allocation 266 | (func $-resize (param $id i32) (param $newlen i32) 267 | (local $offset i32) 268 | (local $len i32) 269 | (local $spaceafter i32) 270 | (local $newoffset i32) 271 | (if (i32.eq (get_local $id) (i32.const -1))(then 272 | (set_local $len (i32.const 1)) 273 | (block(loop (br_if 1 (i32.ge_u (get_local $len) (get_local $newlen))) 274 | (set_local $len (i32.mul (get_local $len) (i32.const 2))) 275 | (br 0))) 276 | (set_local $newlen (get_local $len)) 277 | )) 278 | (set_local $offset (call $-offset (get_local $id))) 279 | (if (get_local $offset)(then ;; the value is in memory 280 | (set_local $len (i32.load (i32.sub (get_local $offset) (i32.const 4)))) 281 | (if (i32.eq 282 | (i32.and (get_local $len) (i32.const -8)) 283 | (i32.and (get_local $newlen) (i32.const -8)) 284 | )(then ;; the old and new lengths are (almost) the same 285 | (i32.store (i32.sub (get_local $offset) (i32.const 4)) (get_local $newlen)) 286 | (if (i32.gt_u (get_local $len) (get_local $newlen))(then ;; shrink 287 | (call $-memzero 288 | (i32.add (get_local $offset) (get_local $newlen)) 289 | (i32.sub (get_local $len) (get_local $newlen)) 290 | ) 291 | )(else ;; grow 292 | (call $-memzero 293 | (i32.add (get_local $offset) (get_local $len)) 294 | (i32.sub (get_local $newlen) (get_local $len)) 295 | ) 296 | )) 297 | )(else 298 | (set_local $spaceafter 299 | (i32.load 300 | (i32.add 301 | (get_local $offset) 302 | (i32.add (i32.and (get_local $len) (i32.const -8)) (i32.const 8)) 303 | ) 304 | ) 305 | ) 306 | (if (i32.or 307 | (i32.gt_u (get_local $len) (get_local $newlen)) 308 | (i32.gt_u (get_local $spaceafter) (i32.sub (get_local $newlen) (get_local $len))) 309 | )(then ;; we can resize in-place 310 | (set_local $spaceafter (i32.add 311 | (get_local $spaceafter) 312 | (i32.sub 313 | (i32.and (get_local $len) (i32.const -8)) 314 | (i32.and (get_local $newlen) (i32.const -8)) 315 | ) 316 | )) 317 | (i32.store (i32.sub (get_local $offset) (i32.const 4)) (get_local $newlen)) 318 | (if (i32.gt_u (get_local $len) (get_local $newlen))(then ;; shrink 319 | (call $-memzero 320 | (i32.add (get_local $offset) (get_local $newlen)) 321 | (i32.sub (get_local $len) (get_local $newlen)) 322 | ) 323 | )(else ;; grow 324 | (call $-memzero 325 | (i32.add (get_local $offset) (get_local $len)) 326 | (i32.sub (get_local $newlen) (get_local $len)) 327 | ) 328 | )) 329 | (i32.store 330 | (i32.add 331 | (get_local $offset) 332 | (i32.add (i32.and (get_local $newlen) (i32.const -8)) (i32.const 8)) 333 | ) 334 | (get_local $spaceafter) 335 | ) 336 | (i32.store 337 | (i32.add 338 | (i32.add 339 | (get_local $offset) 340 | (i32.add (i32.and (get_local $newlen) (i32.const -8)) (i32.const 8)) 341 | ) 342 | (get_local $spaceafter) 343 | ) 344 | (get_local $spaceafter) 345 | ) 346 | )(else ;; we need to re-allocate 347 | (set_local $newoffset (call $-alloc (i32.mul (get_local $newlen) (i32.const 2)))) 348 | (call $-memcopy (get_local $offset) (get_local $newoffset) (get_local $len)) 349 | (call $-dealloc (get_local $offset)) 350 | (if (i32.eq (get_local $id) (i32.const -1))(then 351 | (set_global $-mindex (get_local $newoffset)) 352 | )(else 353 | (call $-write32 (i32.const -1) (i32.mul (i32.sub (get_local $id) (i32.const 8)) (i32.const 8)) (get_local $newoffset)) 354 | )) 355 | (drop (call $-new_value (i32.const 6) (i32.const 1))) 356 | (drop (call $-new_value (i32.const 6) (i32.const 1))) 357 | (call $-resize (get_local $id) (get_local $newlen)) 358 | )) 359 | )) 360 | )) 361 | ) 362 | 363 | ;; set datatype of memory allocation 364 | (func $-set_datatype (param $id i32) (param $datatype i32) (result i32) 365 | (local $offset i32) 366 | (if (i32.eq (get_local $id) (i32.const -1))(then 367 | (set_local $datatype (i32.const 7)) 368 | )(else 369 | (if (i32.lt_u (get_local $id) (i32.const 8))(then 370 | (set_local $datatype (i32.and (get_local $id) (i32.const 3))) 371 | )(else 372 | (set_local $offset (i32.sub (get_global $-mindex) (i32.const 64))) 373 | (set_local $offset (i32.add (get_local $offset) (i32.add (i32.mul (i32.const 8) (get_local $id)) (i32.const 6)))) 374 | (i32.store8 (get_local $offset) (get_local $datatype)) 375 | )) 376 | )) 377 | (get_local $id) 378 | ) 379 | 380 | 381 | ;; read from memory allocation 382 | (func $-read8 (param $id i32) (param $pos i32) (result i32) 383 | (local $offset i32) 384 | (local $data i32) 385 | (set_local $offset (call $-offset (get_local $id))) 386 | (if (i32.lt_u (get_local $pos) (call $-len (get_local $id)))(then 387 | (set_local $data (i32.load8_u (i32.add (get_local $offset) (get_local $pos)))) 388 | )) 389 | (get_local $data) 390 | ) 391 | (func $-read16 (param $id i32) (param $pos i32) (result i32) 392 | (local $offset i32) 393 | (local $data i32) 394 | (set_local $offset (call $-offset (get_local $id))) 395 | (if (i32.lt_u (i32.add (get_local $pos) (i32.const 1)) (call $-len (get_local $id)))(then 396 | (set_local $data (i32.load16_u (i32.add (get_local $offset) (get_local $pos)))) 397 | )) 398 | (get_local $data) 399 | ) 400 | (func $-read32 (param $id i32) (param $pos i32) (result i32) 401 | (local $offset i32) 402 | (local $data i32) 403 | (set_local $offset (call $-offset (get_local $id))) 404 | (if (i32.lt_u (i32.add (get_local $pos) (i32.const 3)) (call $-len (get_local $id)))(then 405 | (set_local $data (i32.load (i32.add (get_local $offset) (get_local $pos)))) 406 | )) 407 | (get_local $data) 408 | ) 409 | ;; write to memory allocation 410 | (func $-write8 (param $id i32) (param $pos i32) (param $data i32) 411 | (local $offset i32) 412 | (set_local $offset (call $-offset (get_local $id))) 413 | (if (get_local $offset)(then 414 | (if (i32.ge_u (get_local $pos) (call $-len (get_local $id)))(then 415 | (call $-resize (get_local $id) (i32.add (get_local $pos) (i32.const 1))) 416 | (set_local $offset (call $-offset (get_local $id))) 417 | )) 418 | (i32.store8 (i32.add (get_local $offset) (get_local $pos)) (get_local $data)) 419 | )) 420 | ) 421 | (func $-write16 (param $id i32) (param $pos i32) (param $data i32) 422 | (local $offset i32) 423 | (set_local $offset (call $-offset (get_local $id))) 424 | (if (get_local $offset)(then 425 | (if (i32.ge_u (i32.add (get_local $pos) (i32.const 1)) (call $-len (get_local $id)) )(then 426 | (call $-resize (get_local $id) (i32.add (get_local $pos) (i32.const 2))) 427 | (set_local $offset (call $-offset (get_local $id))) 428 | )) 429 | (i32.store16 (i32.add (get_local $offset) (get_local $pos)) (get_local $data)) 430 | )) 431 | ) 432 | (func $-write32 (param $id i32) (param $pos i32) (param $data i32) 433 | (local $offset i32) 434 | (set_local $offset (call $-offset (get_local $id))) 435 | (if (get_local $offset)(then 436 | (if (i32.ge_u (i32.add (get_local $pos) (i32.const 3)) (call $-len (get_local $id)) )(then 437 | (call $-resize (get_local $id) (i32.add (get_local $pos) (i32.const 4))) 438 | (set_local $offset (call $-offset (get_local $id))) 439 | )) 440 | (i32.store (i32.add (get_local $offset) (get_local $pos)) (get_local $data)) 441 | )) 442 | ) 443 | (func $-write_to (param $id i32) (param $pos i32) (param $data_id i32) 444 | (local $offset i32) 445 | (local $len i32) 446 | (set_local $offset (call $-offset (get_local $id))) 447 | (set_local $len (call $-len (get_local $data_id))) 448 | (if (get_local $offset)(then 449 | (if (i32.gt_u (i32.add (get_local $pos) (get_local $len)) (call $-len (get_local $id)) )(then 450 | (call $-resize (get_local $id) (i32.add (get_local $pos) (get_local $len))) 451 | (set_local $offset (call $-offset (get_local $id))) 452 | )) 453 | (call $-memcopy (call $-offset (get_local $data_id)) (i32.add (get_local $offset) (get_local $pos)) (get_local $len)) 454 | )) 455 | ) 456 | 457 | ;; make room for a new value 458 | (global $-next_id (mut i32) (i32.const 0)) 459 | (func $-new_value (param $datatype i32) (param $len i32) (result i32) 460 | (local $offset i32) 461 | (local $id i32) 462 | (set_local $id (get_global $-next_id)) 463 | (set_local $offset (call $-alloc (get_local $len))) 464 | (block(loop 465 | (br_if 1 (i32.eqz (call $-read32 (i32.const -1) (i32.mul (get_local $id) (i32.const 8))))) 466 | (set_local $id (i32.add (get_local $id) (i32.const 1))) 467 | (br 0) 468 | )) 469 | (call $-write32 (i32.const -1) (i32.mul (get_local $id) (i32.const 8)) (get_local $offset)) 470 | (call $-write16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)) (get_global $-calls)) 471 | (call $-write8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 6)) (get_local $datatype)) 472 | (call $-write8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 7)) (i32.const 130)) 473 | (set_global $-next_id (i32.add (get_local $id) (i32.const 1))) 474 | (i32.add (get_local $id) (i32.const 8)) 475 | ) 476 | 477 | ;; mark id as referenced 478 | (func $-ref (param $id i32) 479 | (local $refs i32) 480 | (if (call $-offset (get_local $id))(then 481 | (set_local $id (i32.sub (get_local $id) (i32.const 8))) 482 | (call $-write8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 7)) (i32.const 1)) 483 | )) 484 | ) 485 | (global $-hard_value (mut i32) (i32.const 0)) 486 | ;; (global $-high_id (mut i32) (i32.const 0)) 487 | ;; clear all references in index 488 | (func $-zerorefs 489 | (local $id i32) 490 | (if (i32.eqz (get_global $-hard_value))(then 491 | (set_global $-hard_value (get_global $-next_id)) 492 | )) 493 | (set_local $id (i32.div_u (call $-len (i32.const -1)) (i32.const 8))) 494 | ;; (set_global $-high_id (get_global $-hard_value)) 495 | (block(loop (br_if 1 (i32.eqz (get_local $id))) 496 | (set_local $id (i32.sub (get_local $id) (i32.const 1))) 497 | (if (i32.lt_u (get_local $id) (get_global $-hard_value))(then 498 | (call $-write16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)) (i32.const 0)) 499 | )(else 500 | (call $-write16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)) (i32.const 4)) 501 | )) 502 | (br 0))) 503 | ) 504 | (global $-passdown_mark (mut i32) (i32.const 1)) 505 | ;; pass down reference recursively 506 | (func $-passdown (param $id i32) 507 | (local $offset i32) 508 | (local $datatype i32) 509 | (local $csdepth i32) 510 | (local $mark i32) 511 | (set_local $offset (call $-offset (get_local $id))) 512 | ;; is it even in memory? 513 | (if (get_local $offset) (then 514 | (set_local $id (i32.sub (get_local $id) (i32.const 8))) 515 | (set_local $mark (call $-read8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 7)))) 516 | (set_local $csdepth (call $-read16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)))) 517 | ;; is it unreferenced? 518 | (if (i32.or 519 | (i32.ne (get_local $mark) (get_global $-passdown_mark)) 520 | (i32.gt_u (get_local $csdepth) (get_global $-calls)) 521 | )(then 522 | (call $-write8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 7)) (get_global $-passdown_mark)) 523 | (if (i32.gt_u (get_local $csdepth) (get_global $-calls))(then 524 | (call $-write16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)) (get_global $-calls)) 525 | )) 526 | (set_local $datatype (call $-read8 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 6)))) 527 | (set_local $id (i32.add (get_local $id) (i32.const 8))) 528 | ;; is it array/object? 529 | (if (i32.eq (i32.and (get_local $datatype) (i32.const 6)) (i32.const 4))(then 530 | (set_local $offset (call $-len (get_local $id))) 531 | (block(loop (br_if 1 (i32.eqz (get_local $offset))) 532 | (set_local $offset (i32.sub (get_local $offset) (i32.const 4))) 533 | (call $-passdown (call $-read32 (get_local $id) (get_local $offset))) 534 | (br 0))) 535 | )) 536 | )) 537 | )) 538 | ) 539 | 540 | ;; garbage collector 541 | (func $-garbagecollect 542 | (local $id i32) 543 | (local $csdepth i32) 544 | (local $offset i32) 545 | (local $last_id i32) 546 | 547 | (set_global $-passdown_mark (i32.and (i32.add (get_global $-passdown_mark) (i32.const 1)) (i32.const 127))) 548 | (call $-passdown_globals) 549 | (set_local $id (i32.div_u (call $-len (i32.const -1)) (i32.const 8))) 550 | (block(loop (br_if 1 (i32.eqz (get_local $id))) 551 | (set_local $id (i32.sub (get_local $id) (i32.const 1))) 552 | (set_local $offset (call $-read32 (i32.const -1) (i32.mul (get_local $id) (i32.const 8)))) 553 | (set_local $csdepth (call $-read16 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)))) 554 | (if (get_local $offset)(then 555 | (if (i32.gt_u (get_local $csdepth) (get_global $-calls))(then 556 | (call $-dealloc (get_local $offset)) 557 | (set_global $-next_id (get_local $id)) 558 | (call $-write32 (i32.const -1) (i32.mul (get_local $id) (i32.const 8)) (i32.const 0)) 559 | (call $-write32 (i32.const -1) (i32.add (i32.mul (get_local $id) (i32.const 8)) (i32.const 4)) (i32.const 0)) 560 | )(else 561 | (if (i32.eqz (get_local $last_id))(then 562 | (set_local $last_id (get_local $id)) 563 | )) 564 | )) 565 | )) 566 | (br 0))) 567 | (set_global $-last_alloc (i32.const 0)) 568 | (set_global $-gc_pending (get_global $-calls)) 569 | (call $-resize (i32.const -1) (i32.mul (i32.add (get_local $last_id) (i32.const 1)) (i32.const 8))) 570 | ) 571 | 572 | (func $-truthy (param $id i32) (result i32) 573 | (local $datatype i32) 574 | (local $truthy i32) 575 | (if (i32.gt_u (get_local $id) (i32.const 4))(then 576 | (set_local $truthy (i32.const 1)) 577 | (set_local $datatype (call $-datatype (get_local $id))) 578 | (if (i32.and 579 | (i32.eq (get_local $datatype) (i32.const 2)) 580 | (f64.eq (call $-f64 (get_local $id)) (f64.const 0)) 581 | )(then 582 | (set_local $truthy (i32.const 0)) 583 | )) 584 | (if (i32.and 585 | (i32.eq (get_local $datatype) (i32.const 3)) 586 | (i32.eq (call $-len (get_local $id)) (i32.const 0)) 587 | )(then 588 | (set_local $truthy (i32.const 0)) 589 | )) 590 | )) 591 | (get_local $truthy) 592 | ) 593 | (func $-falsy (param $id i32) (result i32) 594 | (i32.eqz (call $-truthy (get_local $id))) 595 | ) 596 | (func $-compare (param $id1 i32) (param $id2 i32) (result i32) 597 | (local $res f64) 598 | (local $len i32) 599 | (local $pos i32) 600 | ;; equal reference 601 | (if (i32.eq (get_local $id1) (get_local $id2))(then 602 | (return (i32.const 0)) 603 | )) 604 | ;; equal datatype 605 | (if (i32.eq (call $-datatype (get_local $id1)) (call $-datatype (get_local $id2)))(then 606 | ;; array/object 607 | (if (i32.eq 608 | (i32.and (call $-datatype (get_local $id1)) (i32.const 6)) 609 | (i32.const 4) 610 | )(then 611 | (return (i32.sub 612 | (get_local $id1) 613 | (get_local $id2) 614 | )) 615 | )) 616 | ;; numerical values 617 | (if (i32.lt_u (call $-datatype (get_local $id1)) (i32.const 3))(then 618 | (set_local $res (f64.sub 619 | (call $-f64 (call $-to_number (get_local $id1))) 620 | (call $-f64 (call $-to_number (get_local $id2))) 621 | )) 622 | (if (f64.eq (get_local $res) (f64.const 0))(then 623 | (return (i32.const 0)) 624 | )) 625 | (if (f64.gt (get_local $res) (f64.const 0))(then 626 | (return (i32.const 1)) 627 | )) 628 | (if (f64.lt (get_local $res) (f64.const 0))(then 629 | (return (i32.const -1)) 630 | )) 631 | )(else ;; strings/binaries 632 | (set_local $pos (i32.const 0)) 633 | (if (i32.lt_u 634 | (call $-len (get_local $id1)) 635 | (call $-len (get_local $id2)) 636 | )(then 637 | (set_local $len (call $-len (get_local $id1))) 638 | )(else 639 | (set_local $len (call $-len (get_local $id2))) 640 | )) 641 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 642 | (if (i32.ne 643 | (call $-read8 (get_local $id1) (get_local $pos)) 644 | (call $-read8 (get_local $id2) (get_local $pos)) 645 | )(then 646 | (return (i32.sub 647 | (call $-read8 (get_local $id1) (get_local $pos)) 648 | (call $-read8 (get_local $id2) (get_local $pos)) 649 | )) 650 | )) 651 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 652 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 653 | (br 0))) 654 | (return (i32.sub 655 | (call $-len (get_local $id1)) 656 | (call $-len (get_local $id2)) 657 | )) 658 | )) 659 | )(else ;; unequal datatypes 660 | (return (i32.sub 661 | (call $-datatype (get_local $id1)) 662 | (call $-datatype (get_local $id2)) 663 | )) 664 | )) 665 | (return (i32.const 0)) 666 | ) 667 | 668 | (func $-equal (param $id1 i32) (param $id2 i32) (result i32) 669 | (if (call $-compare (get_local $id1) (get_local $id2)) (then 670 | (return (i32.const 1)) 671 | )) 672 | (i32.const 5) 673 | ) 674 | (func $-unequal (param $id1 i32) (param $id2 i32) (result i32) 675 | (i32.sub (i32.const 6) (call $-equal (get_local $id1) (get_local $id2))) 676 | ) 677 | (func $-lt (param $id1 i32) (param $id2 i32) (result i32) 678 | (if (i32.lt_s (call $-compare (get_local $id1) (get_local $id2)) (i32.const 0))(then 679 | (return (i32.const 5)) 680 | )) 681 | (i32.const 1) 682 | ) 683 | (func $-le (param $id1 i32) (param $id2 i32) (result i32) 684 | (if (i32.le_s (call $-compare (get_local $id1) (get_local $id2)) (i32.const 0))(then 685 | (return (i32.const 5)) 686 | )) 687 | (i32.const 1) 688 | ) 689 | (func $-gt (param $id1 i32) (param $id2 i32) (result i32) 690 | (if (i32.gt_s (call $-compare (get_local $id1) (get_local $id2)) (i32.const 0))(then 691 | (return (i32.const 5)) 692 | )) 693 | (i32.const 1) 694 | ) 695 | (func $-ge (param $id1 i32) (param $id2 i32) (result i32) 696 | (if (i32.ge_s (call $-compare (get_local $id1) (get_local $id2)) (i32.const 0))(then 697 | (return (i32.const 5)) 698 | )) 699 | (i32.const 1) 700 | ) 701 | (func $-and (param $id1 i32) (param $id2 i32) (result i32) 702 | (local $success i32) 703 | (set_local $success (i32.const 1)) 704 | (if (call $-truthy (get_local $id1))(then 705 | (set_local $success (get_local $id2)) 706 | )(else 707 | (set_local $success (get_local $id1)) 708 | )) 709 | (get_local $success) 710 | ) 711 | (func $-or (param $id1 i32) (param $id2 i32) (result i32) 712 | (local $success i32) 713 | (set_local $success (i32.const 1)) 714 | (if (call $-truthy (get_local $id1))(then 715 | (set_local $success (get_local $id1)) 716 | )(else 717 | (set_local $success (get_local $id2)) 718 | )) 719 | (get_local $success) 720 | ) 721 | 722 | (func $-concat (param $id1 i32) (param $id2 i32) (result i32) 723 | (local $len1 i32) 724 | (local $len2 i32) 725 | (local $datatype i32) 726 | (local $id3 i32) 727 | (local $offset i32) 728 | (set_local $len1 (call $-len (get_local $id1))) 729 | (set_local $len2 (call $-len (get_local $id2))) 730 | (set_local $datatype (call $-datatype (get_local $id1))) 731 | (set_local $id3 (call $-new_value (get_local $datatype) (i32.add (get_local $len1) (get_local $len2)))) 732 | (call $-memcopy (call $-offset (get_local $id1)) (call $-offset (get_local $id3)) (get_local $len1)) 733 | (call $-memcopy (call $-offset (get_local $id2)) (i32.add (call $-offset (get_local $id3)) (get_local $len1)) (get_local $len2)) 734 | ;; (call $-resize (get_local $id3) (i32.add (get_local $len1) (get_local $len2))) 735 | (get_local $id3) 736 | ) 737 | 738 | (func $-to_number (param $id i32) (result i32) 739 | (local $datatype i32) 740 | (local $id3 i32) 741 | (set_local $datatype (call $-datatype (get_local $id))) 742 | (set_local $id3 (i32.const 2)) 743 | (if (i32.lt_u (get_local $id) (i32.const 2))(then 744 | (set_local $id3 (i32.const 2)) 745 | )) 746 | (if (i32.eq (get_local $id) (i32.const 5))(then 747 | (set_local $id3 (call $-integer_u (i32.const 1))) 748 | )) 749 | (if (i32.eq (get_local $datatype) (i32.const 2))(then 750 | (set_local $id3 (get_local $id)) 751 | )) 752 | (get_local $id3) 753 | ) 754 | 755 | (func $-to_string (param $id i32) (result i32) 756 | (local $datatype i32) 757 | (local $id3 i32) 758 | (local $digit f64) 759 | (local $decimals i32) 760 | (local $pos i32) 761 | (set_local $datatype (call $-datatype (get_local $id))) 762 | (set_local $id3 (get_local $id)) 763 | (if (i32.eq (get_local $id) (i32.const 0))(then 764 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 4))) 765 | (call $-write32 (get_local $id3) (i32.const 0) (i32.const 0x6c6c756e));;null 766 | )) 767 | (if (i32.eq (get_local $id) (i32.const 1))(then 768 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 5))) 769 | (call $-write32 (get_local $id3) (i32.const 0) (i32.const 0x736c6166));;fals 770 | (call $-write8 (get_local $id3) (i32.const 4) (i32.const 0x65));;e 771 | )) 772 | (if (i32.eq (get_local $id) (i32.const 5))(then 773 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 4))) 774 | (call $-write32 (get_local $id3) (i32.const 0) (i32.const 0x65757274));;true 775 | )) 776 | (if (i32.eq (get_local $datatype) (i32.const 2))(then ;; number 777 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 0))) 778 | (set_local $digit (call $-f64 (get_local $id))) 779 | (if (f64.lt (get_local $digit) (f64.const 0))(then 780 | (call $-write8 (get_local $id3) (get_local $pos) (i32.const 0x2d));; - 781 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 782 | (set_local $digit (f64.mul (get_local $digit) (f64.const -1))) 783 | )) 784 | (call $-write8 (get_local $id3) (get_local $pos) (i32.const 0x30));; 0 785 | (block(loop (br_if 1 (f64.lt (get_local $digit) (f64.const 1))) 786 | (set_local $digit (f64.div (get_local $digit) (f64.const 10))) 787 | (call $-write8 (get_local $id3) (get_local $pos) (i32.const 0x30));; 0 788 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 789 | (br 0) 790 | )) 791 | (set_local $decimals (i32.trunc_u/f64 (f64.trunc (f64.abs (call $-f64 (get_local $id)))))) 792 | (block(loop (br_if 1 (i32.eqz (get_local $decimals))) 793 | (set_local $pos (i32.sub (get_local $pos) (i32.const 1))) 794 | (call $-write8 (get_local $id3) (get_local $pos) (i32.add (i32.const 0x30) (i32.rem_u (get_local $decimals) (i32.const 10)))) 795 | (set_local $decimals (i32.div_u (get_local $decimals) (i32.const 10))) 796 | (br 0))) 797 | (set_local $pos (call $-len (get_local $id3))) 798 | (set_local $decimals (i32.const 0)) 799 | (set_local $digit (f64.abs (call $-f64 (get_local $id)))) 800 | (set_local $digit (f64.sub (get_local $digit) (f64.trunc (get_local $digit)))) 801 | (if (f64.gt (get_local $digit) (f64.const 0.00001))(then 802 | (call $-write8 (get_local $id3) (get_local $pos) (i32.const 0x2e));; . 803 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 804 | (set_local $digit (f64.mul (get_local $digit) (f64.const 10))) 805 | (block(loop 806 | (br_if 1 (i32.ge_s (get_local $decimals) (i32.const 16))) 807 | (call $-write8 (get_local $id3) (get_local $pos) (i32.add (i32.const 0x30) (i32.trunc_s/f64 (f64.trunc (get_local $digit))))) 808 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 809 | (set_local $digit (f64.sub (get_local $digit) (f64.trunc (get_local $digit)))) 810 | (set_local $digit (f64.mul (get_local $digit) (f64.const 10))) 811 | (if (f64.le (get_local $digit) (f64.const 0.00001))(then 812 | (set_local $decimals (i32.const 1024)) 813 | )) 814 | (set_local $decimals (i32.add (get_local $decimals) (i32.const 1))) 815 | (br 0) 816 | )) 817 | )) 818 | )) 819 | (if (i32.eq (get_local $datatype) (i32.const 4))(then 820 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 5))) 821 | (call $-write32 (get_local $id3) (i32.const 0) (i32.const 0x61727261));;arra 822 | (call $-write8 (get_local $id3) (i32.const 4) (i32.const 0x79));;y 823 | )) 824 | (if (i32.eq (get_local $datatype) (i32.const 5))(then 825 | (set_local $id3 (call $-new_value (i32.const 3) (i32.const 6))) 826 | (call $-write32 (get_local $id3) (i32.const 0) (i32.const 0x656a626f));;obje 827 | (call $-write16 (get_local $id3) (i32.const 4) (i32.const 0x7463));;ct 828 | )) 829 | (if (i32.eq (get_local $datatype) (i32.const 6))(then 830 | (set_local $id3 (call $-concat (i32.const 3) (get_local $id))) 831 | )) 832 | (get_local $id3) 833 | ) 834 | 835 | (func $-to_hex (param $int i32) (param $digits i32) (result i32) 836 | (local $str i32) 837 | (local $dig i32) 838 | (set_local $str (call $-new_value (i32.const 3) (get_local $digits))) 839 | (block(loop (br_if 1 (i32.eqz (get_local $digits))) 840 | (set_local $digits (i32.sub (get_local $digits) (i32.const 1))) 841 | (set_local $dig (i32.and (get_local $int) (i32.const 0xf))) 842 | (set_local $int (i32.div_u (get_local $int) (i32.const 0x10))) 843 | (if (i32.lt_u (get_local $dig) (i32.const 0xa))(then 844 | (call $-write8 (get_local $str) (get_local $digits) (i32.add (i32.const 0x30) (get_local $dig))) 845 | )(else 846 | (call $-write8 (get_local $str) (get_local $digits) (i32.add (i32.const 0x57) (get_local $dig))) 847 | )) 848 | (br 0))) 849 | (get_local $str) 850 | ) 851 | (func $-from_hex (param $str i32) (result i32) 852 | (local $int i32) 853 | (local $dig i32) 854 | (local $pos i32) 855 | (local $len i32) 856 | (set_local $len (call $-len (get_local $str))) 857 | (block(loop (br_if 1 (i32.ge_u (get_local $pos) (get_local $len))) 858 | (set_local $int (i32.mul (get_local $int) (i32.const 0x10))) 859 | (set_local $dig (call $-read8 (get_local $str) (get_local $pos))) 860 | (if (i32.gt_u (get_local $dig) (i32.const 0x5f))(then 861 | (set_local $dig (i32.sub (get_local $dig) (i32.const 0x20))) 862 | )) 863 | (if (i32.lt_u (get_local $dig) (i32.const 0x40))(then 864 | (set_local $int (i32.add (get_local $int) (i32.sub (get_local $dig) (i32.const 0x30)))) 865 | )(else 866 | (set_local $int (i32.add (get_local $int) (i32.sub (get_local $dig) (i32.const 0x37)))) 867 | )) 868 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 869 | (br 0))) 870 | (get_local $int) 871 | ) 872 | 873 | (global $-parsing_offset (mut i32) (i32.const 0)) 874 | (func $-parse_integer (param $offset i32) (param $base i32) (result i64) 875 | (local $result i64) 876 | (local $neg i32) 877 | (local $char i32) 878 | (local $digit i32) 879 | (if (i32.eqz (get_local $offset))(then 880 | (set_local $offset (get_global $-parsing_offset)) 881 | )) 882 | (if (i32.eqz (get_local $base))(then 883 | (set_local $base (i32.const 10)) 884 | )) 885 | (set_local $neg (i32.const 1)) 886 | (set_local $char (i32.load8_u (get_local $offset))) 887 | (if (i32.eq (get_local $char) (i32.const 0x2d))(then ;; - 888 | (set_local $neg (i32.const -1)) 889 | (set_local $char (i32.const 0x30)) 890 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 891 | )) 892 | (if (i32.eq (get_local $char) (i32.const 0x2b))(then ;; + 893 | (set_local $neg (i32.const 1)) 894 | (set_local $char (i32.const 0x30)) 895 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 896 | )) 897 | (block(loop 898 | (set_local $char (i32.load8_u (get_local $offset))) 899 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 900 | (br_if 1 (i32.lt_u (get_local $char) (i32.const 0x30))) 901 | (set_local $digit (i32.sub (get_local $char) (i32.const 0x30))) 902 | (if (i32.gt_u (get_local $digit) (i32.const 0x9))(then 903 | (set_local $digit (i32.sub (get_local $digit) (i32.const 0x7))) 904 | )) 905 | (if (i32.gt_u (get_local $digit) (i32.const 0x29))(then 906 | (set_local $digit (i32.sub (get_local $digit) (i32.const 0x20))) 907 | )) 908 | (if (i32.eq (get_local $digit) (i32.const 0x21))(then ;; x 909 | (set_local $base (i32.const 0x10)) 910 | (set_local $digit (i32.const 0x0)) 911 | )) 912 | (br_if 1 (i32.ge_u (get_local $digit) (get_local $base))) 913 | (set_local $result (i64.mul (get_local $result) (i64.extend_u/i32 (get_local $base)))) 914 | (set_local $result (i64.add (get_local $result) (i64.extend_u/i32 (get_local $digit)))) 915 | (br 0))) 916 | (set_local $offset (i32.sub (get_local $offset) (i32.const 1))) 917 | (set_global $-parsing_offset (get_local $offset)) 918 | (tee_local $result (i64.mul (get_local $result) (i64.extend_s/i32 (get_local $neg)))) 919 | ;; (call $logNumber (f64.convert_s/i64 (get_local $result))) 920 | ) 921 | (func $-parse_float (param $offset i32) (param $base i32) (result f64) 922 | (local $iresult f64) 923 | (local $dresult f64) 924 | (local $int i64) 925 | (local $dec i64) 926 | (local $declen i64) 927 | (local $exp i64) 928 | (local $pow i64) 929 | (local $neg f64) 930 | (local $k f64) 931 | (local $char i32) 932 | ;; (call $logNumber (f64.const 0xcafebabe) ) 933 | ;; default params 934 | (if (i32.eqz (get_local $offset))(then 935 | (set_local $offset (get_global $-parsing_offset)) 936 | )) 937 | (if (i32.eqz (get_local $base))(then 938 | (set_local $base (i32.const 10)) 939 | )) 940 | ;; read sign 941 | (set_local $neg (f64.const 1)) 942 | (set_local $char (i32.load8_u (get_local $offset))) 943 | (if (i32.eq (get_local $char) (i32.const 0x2d))(then ;; - 944 | (set_local $neg (f64.const -1)) 945 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 946 | )) 947 | (if (i32.eq (get_local $char) (i32.const 0x2b))(then ;; + 948 | (set_local $neg (f64.const 1)) 949 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 950 | )) 951 | ;; read base 952 | (set_local $char (i32.load16_u (get_local $offset))) 953 | (if (i32.eq (get_local $char) (i32.const 0x5830))(then ;; 0X 954 | (set_local $base (i32.const 0x10)) 955 | (set_local $offset (i32.add (get_local $offset) (i32.const 2))) 956 | )) 957 | (if (i32.eq (get_local $char) (i32.const 0x7830))(then ;; 0x 958 | (set_local $base (i32.const 0x10)) 959 | (set_local $offset (i32.add (get_local $offset) (i32.const 2))) 960 | )) 961 | ;; read integer 962 | (set_local $int (call $-parse_integer (get_local $offset) (get_local $base))) 963 | (set_local $offset (get_global $-parsing_offset)) 964 | ;; read decimals 965 | (set_local $char (i32.load8_u (get_local $offset))) 966 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 967 | (if (i32.eq (get_local $char) (i32.const 0x2e))(then ;; . 968 | (set_local $dec (call $-parse_integer (get_local $offset) (get_local $base))) 969 | (set_local $declen (i64.extend_u/i32 (i32.sub (get_global $-parsing_offset) (get_local $offset)))) 970 | (set_local $offset (get_global $-parsing_offset)) 971 | (set_local $char (i32.load8_u (get_local $offset))) 972 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 973 | )) 974 | ;; read exponent 975 | (if (i32.ge_u (get_local $char) (i32.const 0x60))(then 976 | (set_local $char (i32.sub (get_local $char) (i32.const 0x20))) 977 | )) 978 | (if (i32.eq (get_local $char) (i32.const 0x45))(then ;; E 979 | (set_local $exp (call $-parse_integer (get_local $offset) (i32.const 0))) 980 | )) 981 | (if (i32.eq (get_local $char) (i32.const 0x50))(then ;; P 982 | (set_local $pow (call $-parse_integer (get_local $offset) (i32.const 0))) 983 | )) 984 | 985 | ;; calc decimals 986 | (set_local $dresult (f64.convert_u/i64 (get_local $dec))) 987 | (set_local $declen (i64.sub (get_local $exp) (get_local $declen))) 988 | (if (i64.lt_s (get_local $declen) (i64.const 0))(then 989 | (set_local $k (f64.const -1)) 990 | )(else 991 | (set_local $k (f64.const 1)) 992 | )) 993 | (block(loop (br_if 1 (i64.ge_s (get_local $declen) (i64.const 0))) 994 | (set_local $k (f64.mul (get_local $k) (f64.convert_u/i32 (get_local $base)))) 995 | (set_local $declen (i64.add (get_local $declen) (i64.const 1))) 996 | (br 0))) 997 | (block(loop (br_if 1 (i64.le_s (get_local $declen) (i64.const 0))) 998 | (set_local $k (f64.mul (get_local $k) (f64.convert_u/i32 (get_local $base)))) 999 | (set_local $declen (i64.sub (get_local $declen) (i64.const 1))) 1000 | (br 0))) 1001 | (if (f64.lt (get_local $k) (f64.const 0))(then 1002 | (set_local $k (f64.mul (get_local $k) (f64.const -1))) 1003 | (set_local $dresult (f64.div (get_local $dresult) (get_local $k))) 1004 | )(else 1005 | (set_local $dresult (f64.mul (get_local $dresult) (get_local $k))) 1006 | )) 1007 | 1008 | ;; calc integer 1009 | (set_local $iresult (f64.convert_u/i64 (get_local $int))) 1010 | (if (i64.lt_s (get_local $exp) (i64.const 0))(then 1011 | (set_local $k (f64.const -1)) 1012 | )(else 1013 | (set_local $k (f64.const 1)) 1014 | )) 1015 | (block(loop (br_if 1 (i64.ge_s (get_local $exp) (i64.const 0))) 1016 | (set_local $k (f64.mul (get_local $k) (f64.convert_u/i32 (get_local $base)))) 1017 | (set_local $exp (i64.add (get_local $exp) (i64.const 1))) 1018 | (br 0))) 1019 | (block(loop (br_if 1 (i64.le_s (get_local $exp) (i64.const 0))) 1020 | (set_local $k (f64.mul (get_local $k) (f64.convert_u/i32 (get_local $base)))) 1021 | (set_local $exp (i64.sub (get_local $exp) (i64.const 1))) 1022 | (br 0))) 1023 | (if (f64.lt (get_local $k) (f64.const 0))(then 1024 | (set_local $k (f64.mul (get_local $k) (f64.const -1))) 1025 | (set_local $iresult (f64.div (get_local $iresult) (get_local $k))) 1026 | )(else 1027 | (set_local $iresult (f64.mul (get_local $iresult) (get_local $k))) 1028 | )) 1029 | 1030 | ;; apply power 1031 | (block(loop (br_if 1 (i64.ge_s (get_local $pow) (i64.const 0))) 1032 | (set_local $iresult (f64.div (get_local $iresult) (f64.const 2))) 1033 | (set_local $dresult (f64.div (get_local $dresult) (f64.const 2))) 1034 | (set_local $pow (i64.add (get_local $pow) (i64.const 1))) 1035 | (br 0))) 1036 | (block(loop (br_if 1 (i64.le_s (get_local $pow) (i64.const 0))) 1037 | (set_local $iresult (f64.mul (get_local $iresult) (f64.const 2))) 1038 | (set_local $dresult (f64.mul (get_local $dresult) (f64.const 2))) 1039 | (set_local $pow (i64.sub (get_local $pow) (i64.const 1))) 1040 | (br 0))) 1041 | 1042 | ;; put it all together 1043 | (f64.mul (get_local $neg) (f64.add (get_local $iresult) (get_local $dresult))) 1044 | ) 1045 | 1046 | (func $-inc (param $num i32) (param $delta f64) (result i32) 1047 | (local $offset i32) 1048 | (local $float f64) 1049 | (set_local $offset (call $-offset (get_local $num))) 1050 | (if (get_local $offset)(then 1051 | (set_local $float (f64.load (get_local $offset))) 1052 | (f64.store (get_local $offset) (f64.add (get_local $float) (get_local $delta))) 1053 | )(else 1054 | (set_local $num (call $-number (get_local $delta))) 1055 | )) 1056 | (get_local $num) 1057 | ) 1058 | 1059 | (func $-add (param $id1 i32) (param $id2 i32) (result i32) 1060 | (local $datatype1 i32) 1061 | (local $datatype2 i32) 1062 | (local $id3 i32) 1063 | (set_local $datatype1 (call $-datatype (get_local $id1))) 1064 | (set_local $datatype2 (call $-datatype (get_local $id2))) 1065 | ;; numerical values 1066 | (if (i32.and (i32.lt_u (get_local $datatype1) (i32.const 3)) (i32.lt_u (get_local $datatype2) (i32.const 3)))(then 1067 | (set_local $id3 1068 | (call $-number 1069 | (f64.add 1070 | (call $-f64 (call $-to_number(get_local $id1)) ) 1071 | (call $-f64 (call $-to_number(get_local $id2)) ) 1072 | ) 1073 | ) 1074 | ) 1075 | )(else 1076 | ;; is one of them a string? 1077 | (if (i32.or (i32.eq (get_local $datatype1) (i32.const 3)) (i32.eq (get_local $datatype2) (i32.const 3)))(then 1078 | (set_local $id3 (call $-concat 1079 | (call $-to_string (get_local $id1)) 1080 | (call $-to_string (get_local $id2)) 1081 | )) 1082 | )(else 1083 | ;; both the same datatype? 1084 | (if (i32.eq (get_local $datatype1) (get_local $datatype2) )(then 1085 | (set_local $id3 (call $-concat 1086 | (get_local $id1) 1087 | (get_local $id2) 1088 | )) 1089 | )) 1090 | )) 1091 | )) 1092 | (get_local $id3) 1093 | ) 1094 | (func $-sub (param $id1 i32) (param $id2 i32) (result i32) 1095 | (local $datatype1 i32) 1096 | (local $datatype2 i32) 1097 | (local $id3 i32) 1098 | (set_local $datatype1 (call $-datatype (get_local $id1))) 1099 | (set_local $datatype2 (call $-datatype (get_local $id2))) 1100 | ;; numerical values 1101 | (if (i32.and (i32.lt_u (get_local $datatype1) (i32.const 3)) (i32.lt_u (get_local $datatype2) (i32.const 3)))(then 1102 | (set_local $id3 1103 | (call $-number 1104 | (f64.sub 1105 | (call $-f64 (call $-to_number(get_local $id1)) ) 1106 | (call $-f64 (call $-to_number(get_local $id2)) ) 1107 | ) 1108 | ) 1109 | ) 1110 | )) 1111 | (get_local $id3) 1112 | ) 1113 | (func $-mul (param $id1 i32) (param $id2 i32) (result i32) 1114 | (local $datatype1 i32) 1115 | (local $datatype2 i32) 1116 | (local $id3 i32) 1117 | (set_local $datatype1 (call $-datatype (get_local $id1))) 1118 | (set_local $datatype2 (call $-datatype (get_local $id2))) 1119 | ;; numerical values 1120 | (if (i32.and (i32.lt_u (get_local $datatype1) (i32.const 3)) (i32.lt_u (get_local $datatype2) (i32.const 3)))(then 1121 | (set_local $id3 1122 | (call $-number 1123 | (f64.mul 1124 | (call $-f64 (call $-to_number(get_local $id1)) ) 1125 | (call $-f64 (call $-to_number(get_local $id2)) ) 1126 | ) 1127 | ) 1128 | ) 1129 | )) 1130 | (get_local $id3) 1131 | ) 1132 | (func $-div (param $id1 i32) (param $id2 i32) (result i32) 1133 | (local $datatype1 i32) 1134 | (local $datatype2 i32) 1135 | (local $id3 i32) 1136 | (set_local $datatype1 (call $-datatype (get_local $id1))) 1137 | (set_local $datatype2 (call $-datatype (get_local $id2))) 1138 | ;; numerical values 1139 | (if (i32.and (i32.lt_u (get_local $datatype1) (i32.const 3)) (i32.lt_u (get_local $datatype2) (i32.const 3)))(then 1140 | (set_local $id3 1141 | (call $-number 1142 | (f64.div 1143 | (call $-f64 (call $-to_number(get_local $id1)) ) 1144 | (call $-f64 (call $-to_number(get_local $id2)) ) 1145 | ) 1146 | ) 1147 | ) 1148 | )) 1149 | (get_local $id3) 1150 | ) 1151 | (func $-mod (param $id1 i32) (param $id2 i32) (result i32) 1152 | (local $datatype1 i32) 1153 | (local $datatype2 i32) 1154 | (local $f1 f64) 1155 | (local $f2 f64) 1156 | (local $f3 f64) 1157 | (local $id3 i32) 1158 | (set_local $datatype1 (call $-datatype (get_local $id1))) 1159 | (set_local $datatype2 (call $-datatype (get_local $id2))) 1160 | ;; numerical values 1161 | (if (i32.and (i32.lt_u (get_local $datatype1) (i32.const 3)) (i32.lt_u (get_local $datatype2) (i32.const 3)))(then 1162 | (set_local $f1 (call $-f64 (call $-to_number(get_local $id1)) )) 1163 | (set_local $f2 (f64.abs (call $-f64 (call $-to_number(get_local $id2)) ))) 1164 | (set_local $f3 (f64.trunc (f64.div (get_local $f1) (get_local $f2)))) 1165 | (set_local $f1 (f64.sub (get_local $f1) (f64.mul (get_local $f2) (get_local $f3)))) 1166 | (set_local $id3 (call $-number (get_local $f1) ) ) 1167 | )) 1168 | (get_local $id3) 1169 | ) 1170 | 1171 | (func $-f64 (param $id i32) (result f64) 1172 | (local $val f64) 1173 | (if (i32.gt_u (get_local $id) (i32.const 4))(then 1174 | (set_local $val (f64.load (call $-offset (get_local $id)))) 1175 | )) 1176 | (get_local $val) 1177 | ) 1178 | (func $-i32_s (param $id i32) (result i32) 1179 | (i32.trunc_s/f64 (call $-f64 (get_local $id))) 1180 | ) 1181 | (func $-i32_u (param $id i32) (result i32) 1182 | (i32.trunc_u/f64 (call $-f64 (get_local $id))) 1183 | ) 1184 | 1185 | (func $-number (param $val f64) (result i32) 1186 | (local $id i32) 1187 | (set_local $id (i32.const 2)) 1188 | (if (f64.ne (get_local $val) (f64.const 0))(then 1189 | (set_local $id (call $-new_value (i32.const 2) (i32.const 0))) 1190 | (f64.store (call $-offset (get_local $id)) (get_local $val)) 1191 | )) 1192 | (get_local $id) 1193 | ) 1194 | (func $-integer_s (param $val i32) (result i32) 1195 | (call $-number (f64.convert_s/i32 (get_local $val))) 1196 | ) 1197 | (func $-integer_u (param $val i32) (result i32) 1198 | (call $-number (f64.convert_u/i32 (get_local $val))) 1199 | ) 1200 | 1201 | (func $-string (param $offset i32) (param $len i32) (result i32) 1202 | (local $id i32) 1203 | (set_local $id (call $-new_value (i32.const 3) (get_local $len))) 1204 | (call $-memcopy (get_local $offset) (call $-offset (get_local $id)) (get_local $len)) 1205 | (call $-ref (get_local $id)) 1206 | (get_local $id) 1207 | ) 1208 | (func $-char_size (param $byte i32) (result i32) 1209 | (local $size i32) 1210 | (if (i32.ge_u (get_local $byte) (i32.const 0x01))(then 1211 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1212 | )) 1213 | (if (i32.ge_u (get_local $byte) (i32.const 0xc0))(then 1214 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1215 | )) 1216 | (if (i32.ge_u (get_local $byte) (i32.const 0xe0))(then 1217 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1218 | )) 1219 | (if (i32.ge_u (get_local $byte) (i32.const 0xf0))(then 1220 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1221 | )) 1222 | (if (i32.ge_u (get_local $byte) (i32.const 0xf8))(then 1223 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1224 | )) 1225 | (if (i32.ge_u (get_local $byte) (i32.const 0xfc))(then 1226 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1227 | )) 1228 | (if (i32.ge_u (get_local $byte) (i32.const 0xfe))(then 1229 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1230 | )) 1231 | (if (i32.ge_u (get_local $byte) (i32.const 0xff))(then 1232 | (set_local $size (i32.add (get_local $size) (i32.const 1))) 1233 | )) 1234 | (get_local $size) 1235 | ) 1236 | (func $-bytes_to_chars (param $start i32) (param $bytes i32) (result i32) 1237 | (local $pos i32) 1238 | (local $len i32) 1239 | (local $charlen i32) 1240 | (local $chars i32) 1241 | (set_local $pos (get_local $start)) 1242 | (set_local $len (get_local $bytes)) 1243 | (block(loop (br_if 1 (i32.le_s (get_local $len) (i32.const 0))) 1244 | (set_local $charlen (call $-char_size (i32.load8_u (get_local $pos)))) 1245 | (if (get_local $charlen)(then 1246 | (set_local $len (i32.sub (get_local $len) (get_local $charlen))) 1247 | (set_local $pos (i32.add (get_local $pos) (get_local $charlen))) 1248 | (set_local $chars (i32.add (get_local $chars) (i32.const 1))) 1249 | )(else 1250 | (set_local $len (i32.const 0)) 1251 | )) 1252 | (br 0) )) 1253 | (get_local $chars) 1254 | ) 1255 | (func $-chars_to_bytes (param $start i32) (param $chars i32) (result i32) 1256 | (local $pos i32) 1257 | (local $byte i32) 1258 | (set_local $pos (get_local $start)) 1259 | (block(loop (br_if 1 (i32.le_s (get_local $chars) (i32.const 0))) 1260 | (set_local $byte (i32.load8_u (get_local $pos))) 1261 | (set_local $pos (i32.add (get_local $pos) (call $-char_size (get_local $byte)))) 1262 | (set_local $chars (i32.sub (get_local $chars) (i32.const 1))) 1263 | (br 0) )) 1264 | (i32.sub (get_local $pos) (get_local $start)) 1265 | ) 1266 | (func $-char (param $code i32) (result i32) 1267 | (local $str i32) 1268 | (local $pos i32) 1269 | (local $max i32) 1270 | (local $charlen i32) 1271 | (if (i32.lt_u (get_local $code) (i32.const 0x80))(then 1272 | (set_local $str (call $-new_value (i32.const 3) (i32.const 1))) 1273 | (call $-write8 (get_local $str) (get_local $pos) (get_local $code)) 1274 | )(else 1275 | (set_local $max (i32.const 1)) 1276 | (block(loop (br_if 1 (i32.gt_u (get_local $max) (get_local $code))) 1277 | (set_local $charlen (i32.add (get_local $charlen) (i32.const 1))) 1278 | (set_local $max (i32.shl (get_local $max) (i32.const 5))) 1279 | (br 0))) 1280 | (set_local $str (call $-new_value (i32.const 3) (get_local $charlen))) 1281 | (block(loop (br_if 1 (i32.eqz (get_local $charlen))) 1282 | (set_local $charlen (i32.sub (get_local $charlen) (i32.const 1))) 1283 | (call $-write8 (get_local $str) (get_local $charlen) (i32.or 1284 | (i32.const 128) 1285 | (i32.and 1286 | (get_local $code) 1287 | (i32.const 0x3f) 1288 | ) 1289 | )) 1290 | (set_local $code (i32.shr_u (get_local $code) (i32.const 6))) 1291 | (br 0))) 1292 | (set_local $max (i32.const 0xffff00)) 1293 | (set_local $max (i32.shr_u (get_local $max) (call $-len (get_local $str)))) 1294 | (call $-write8 (get_local $str) (get_local $charlen) (i32.or 1295 | (get_local $max) 1296 | (call $-read8 (get_local $str) (get_local $charlen)) 1297 | )) 1298 | )) 1299 | (get_local $str) 1300 | ) 1301 | (func $-char_code (param $offset i32) (result i32) 1302 | (local $code i32) 1303 | (local $charlen i32) 1304 | (local $mask i32) 1305 | (set_local $charlen (call $-char_size (i32.load8_u (get_local $offset)))) 1306 | (set_local $mask (i32.const 0xff)) 1307 | (set_local $mask (i32.shr_u (get_local $mask) (get_local $charlen))) 1308 | (block(loop (br_if 1 (i32.eqz (get_local $charlen))) 1309 | (set_local $code (i32.shl (get_local $code) (i32.const 6))) 1310 | (set_local $code (i32.add 1311 | (get_local $code) 1312 | (i32.and 1313 | (i32.load8_u (get_local $offset)) 1314 | (get_local $mask) 1315 | ) 1316 | )) 1317 | (set_local $mask (i32.const 0x3f)) 1318 | (set_local $offset (i32.add (get_local $offset) (i32.const 1))) 1319 | (set_local $charlen (i32.sub (get_local $charlen) (i32.const 1))) 1320 | (br 0))) 1321 | (get_local $code) 1322 | ) 1323 | 1324 | 1325 | (func $-get_from_obj (param $objId i32) (param $indexId i32) (result i32) 1326 | (local $elem i32) 1327 | (local $index i32) 1328 | (if (i32.eq (call $-datatype (get_local $indexId)) (i32.const 2))(then 1329 | (set_local $index (call $-i32_u (get_local $indexId))) 1330 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1331 | )(else 1332 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1333 | (block(loop 1334 | (if (i32.eqz (get_local $elem))(then 1335 | (set_local $elem (get_local $indexId)) 1336 | )) 1337 | (br_if 1 (call $-truthy (call $-equal (get_local $elem) (get_local $indexId)))) 1338 | (set_local $index (i32.add (get_local $index) (i32.const 2))) 1339 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1340 | (br 0))) 1341 | (set_local $index (i32.add (get_local $index) (i32.const 1))) 1342 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1343 | )) 1344 | (get_local $elem) 1345 | ) 1346 | (func $-set_to_obj (param $objId i32) (param $indexId i32) (param $valId i32) 1347 | (local $elem i32) 1348 | (local $index i32) 1349 | (local $len i32) 1350 | (if (i32.eq (call $-datatype (get_local $indexId)) (i32.const 2))(then 1351 | (set_local $index (call $-i32_u (get_local $indexId))) 1352 | (call $-write32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)) (get_local $valId)) 1353 | )(else 1354 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1355 | (block(loop 1356 | (if (i32.eqz (get_local $elem))(then 1357 | (call $-write32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)) (get_local $indexId)) 1358 | (set_local $elem (get_local $indexId)) 1359 | )) 1360 | (br_if 1 (call $-truthy (call $-equal (get_local $elem) (get_local $indexId)))) 1361 | (set_local $index (i32.add (get_local $index) (i32.const 2))) 1362 | (set_local $elem (call $-read32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)))) 1363 | (br 0))) 1364 | (set_local $index (i32.add (get_local $index) (i32.const 1))) 1365 | (call $-write32 (get_local $objId) (i32.mul (get_local $index) (i32.const 4)) (get_local $valId)) 1366 | (if (i32.eqz (get_local $valId))(then 1367 | (set_local $len (call $-len (get_local $objId))) 1368 | (set_local $len (i32.sub (get_local $len) (i32.mul (get_local $index) (i32.const 4)))) 1369 | (call $-memcopy 1370 | (i32.add (call $-offset (get_local $objId)) (i32.mul (i32.add (get_local $index) (i32.const 1)) (i32.const 4))) 1371 | (i32.add (call $-offset (get_local $objId)) (i32.mul (i32.sub (get_local $index) (i32.const 1)) (i32.const 4))) 1372 | (get_local $len) 1373 | ) 1374 | (call $-resize (get_local $objId) (i32.sub (call $-len (get_local $objId)) (i32.const 8))) 1375 | )) 1376 | )) 1377 | ) 1378 | -------------------------------------------------------------------------------- /stdlib.wast: -------------------------------------------------------------------------------- 1 | ;; general value functions 2 | (func $address_of (param $value i32) (result i32) 3 | (call $-integer_u (call $-offset (get_local $value))) 4 | ) 5 | (func $size_of (param $value i32) (result i32) 6 | (call $-integer_u (call $-len (get_local $value))) 7 | ) 8 | (func $datatype_of (param $value i32) (result i32) 9 | (call $-integer_u (call $-datatype (get_local $value))) 10 | ) 11 | 12 | ;; binary functions 13 | (func $binary_string (param $len i32) (result i32) 14 | (call $-new_value (i32.const 6) (call $-i32_u (get_local $len))) 15 | ) 16 | (func $binary_slice (param $binary i32) (param $start i32) (param $len i32) (result i32) 17 | (local $out i32) 18 | (set_local $start (call $-i32_u (get_local $start))) 19 | (set_local $len (call $-i32_u (get_local $len)) ) 20 | (if (i32.lt_u (call $-len (get_local $binary)) (get_local $start))(then 21 | (set_local $start (call $-len (get_local $binary))) 22 | )) 23 | (if (i32.lt_u (i32.sub (call $-len (get_local $binary)) (get_local $start)) (get_local $len))(then 24 | (set_local $len (i32.sub (call $-len (get_local $binary)) (get_local $start))) 25 | )) 26 | (set_local $out (call $-new_value (i32.const 6) (get_local $len))) 27 | (call $-memcopy 28 | (i32.add (call $-offset (get_local $binary)) (get_local $start)) 29 | (call $-offset (get_local $out)) 30 | (get_local $len) 31 | ) 32 | (get_local $out) 33 | ) 34 | (func $binary_search (param $binary i32) (param $subbin i32) (param $start i32) (result i32) 35 | (local $sub i32) 36 | (if (i32.lt_u (call $-len (get_local $binary)) (call $-len (get_local $subbin))) (then 37 | (return (i32.const 0)) 38 | )) 39 | (set_local $start (call $-i32_u (get_local $start))) 40 | (set_local $sub (call $-new_value (call $-datatype (get_local $subbin)) (call $-len (get_local $subbin)))) 41 | (block(loop 42 | (br_if 1 (i32.gt_u (get_local $start) (i32.sub (call $-len (get_local $binary)) (call $-len (get_local $subbin))))) 43 | (call $-memcopy 44 | (i32.add (call $-offset (get_local $binary)) (get_local $start)) 45 | (call $-offset (get_local $sub)) 46 | (call $-len (get_local $sub)) 47 | ) 48 | (if (i32.eqz (call $-compare (get_local $sub) (get_local $subbin))) (then 49 | (return (call $-integer_u (get_local $start))) 50 | )) 51 | (set_local $start (i32.add (get_local $start) (i32.const 1))) 52 | (br 0) 53 | )) 54 | (i32.const 0) 55 | ) 56 | (func $binary_read (param $binary i32) (param $pos i32) (result i32) 57 | (if (i32.ge_u (call $-i32_u (get_local $pos)) (call $-len (get_local $binary)))(then 58 | (return (i32.const 0)) 59 | )) 60 | (call $-integer_u (call $-read8 (get_local $binary) (call $-i32_u (get_local $pos)))) 61 | ) 62 | (func $binary_write (param $binary i32) (param $pos i32) (param $data i32) (result i32) 63 | (local $subbin i32) 64 | (local $len i32) 65 | (set_local $pos (call $-i32_u (get_local $pos))) 66 | (if (i32.lt_u (call $-datatype (get_local $data)) (i32.const 3))(then 67 | (set_local $subbin (call $-new_value (i32.const 6) (i32.const 4))) 68 | (call $-write32 (get_local $subbin) (i32.const 0) (call $-i32_u (get_local $data))) 69 | (if (i32.eqz (call $-read8 (get_local $subbin) (i32.const 3)))(then 70 | (if (i32.eqz (call $-read8 (get_local $subbin) (i32.const 2)))(then 71 | (if (i32.eqz (call $-read8 (get_local $subbin) (i32.const 1)))(then 72 | (call $-resize (get_local $subbin) (i32.const 1)) 73 | )(else 74 | (call $-resize (get_local $subbin) (i32.const 2)) 75 | )) 76 | )(else 77 | (call $-resize (get_local $subbin) (i32.const 3)) 78 | )) 79 | )) 80 | (set_local $data (get_local $subbin)) 81 | )) 82 | (set_local $len (i32.add (get_local $pos) (call $-len (get_local $data)))) 83 | (if (i32.lt_u (call $-len (get_local $binary)) (get_local $len) )(then 84 | (call $-resize (get_local $binary) (get_local $len)) 85 | )) 86 | (call $-memcopy 87 | (call $-offset (get_local $data)) 88 | (i32.add (call $-offset (get_local $binary)) (get_local $pos)) 89 | (call $-len (get_local $data)) 90 | ) 91 | (i32.const 0) 92 | ) 93 | 94 | 95 | ;; string functions 96 | (func $string_length (param $str i32) (result i32) 97 | (call $-integer_u (call $-bytes_to_chars (call $-offset (get_local $str)) (call $-len (get_local $str)))) 98 | ) 99 | (func $string_slice (param $string i32) (param $start i32) (param $len i32) (result i32) 100 | (set_local $start (call $-chars_to_bytes 101 | (call $-offset (get_local $string)) 102 | (call $-i32_u (get_local $start)) 103 | )) 104 | (set_local $len (call $-chars_to_bytes 105 | (i32.add (call $-offset (get_local $string)) (get_local $start)) 106 | (call $-i32_u (get_local $len)) 107 | )) 108 | (call $-set_datatype (call $binary_slice 109 | (get_local $string) 110 | (call $-integer_u (get_local $start)) 111 | (call $-integer_u (get_local $len)) 112 | ) (i32.const 3)) 113 | ) 114 | (func $string_search (param $string i32) (param $substr i32) (param $start i32) (result i32) 115 | (local $res i32) 116 | (set_local $start (call $-chars_to_bytes 117 | (call $-offset (get_local $string)) 118 | (call $-i32_u (get_local $start)) 119 | )) 120 | (set_local $res (call $binary_search 121 | (get_local $string) 122 | (get_local $substr) 123 | (call $-integer_u (get_local $start)) 124 | )) 125 | (if (get_local $res)(then 126 | (set_local $res (call $-integer_u (call $-bytes_to_chars 127 | (call $-offset (get_local $string)) 128 | (call $-i32_u (get_local $res)) 129 | ))) 130 | )) 131 | (get_local $res) 132 | ) 133 | (func $string_lower (param $string i32) (result i32) 134 | (local $out i32) 135 | (local $len i32) 136 | (local $byte i32) 137 | (set_local $len (call $-len (get_local $string))) 138 | (set_local $out (call $-new_value (i32.const 3) (get_local $len))) 139 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 140 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 141 | (set_local $byte (call $-read8 (get_local $string) (get_local $len))) 142 | (if (i32.and 143 | (i32.gt_u 144 | (get_local $byte) 145 | (i32.const 0x40) 146 | ) 147 | (i32.lt_u 148 | (get_local $byte) 149 | (i32.const 0x5B) 150 | ) 151 | )(then 152 | (call $-write8 153 | (get_local $out) 154 | (get_local $len) 155 | (i32.add 156 | (get_local $byte) 157 | (i32.const 0x20) 158 | ) 159 | ) 160 | )(else 161 | (call $-write8 162 | (get_local $out) 163 | (get_local $len) 164 | (get_local $byte) 165 | ) 166 | )) 167 | (br 0))) 168 | (get_local $out) 169 | ) 170 | (func $string_upper (param $string i32) (result i32) 171 | (local $out i32) 172 | (local $len i32) 173 | (local $byte i32) 174 | (set_local $len (call $-len (get_local $string))) 175 | (set_local $out (call $-new_value (i32.const 3) (get_local $len))) 176 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 177 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 178 | (set_local $byte (call $-read8 (get_local $string) (get_local $len))) 179 | (if (i32.and 180 | (i32.gt_u 181 | (get_local $byte) 182 | (i32.const 0x60) 183 | ) 184 | (i32.lt_u 185 | (get_local $byte) 186 | (i32.const 0x7B) 187 | ) 188 | )(then 189 | (call $-write8 190 | (get_local $out) 191 | (get_local $len) 192 | (i32.sub 193 | (get_local $byte) 194 | (i32.const 0x20) 195 | ) 196 | ) 197 | )(else 198 | (call $-write8 199 | (get_local $out) 200 | (get_local $len) 201 | (get_local $byte) 202 | ) 203 | )) 204 | (br 0))) 205 | (get_local $out) 206 | ) 207 | (func $string_split (param $string i32) (param $seperator i32) (result i32) 208 | (local $parts i32) 209 | (local $sub i32) 210 | (local $start i32) 211 | (local $pos i32) 212 | (if (i32.lt_u (call $-len (get_local $string)) (call $-len (get_local $seperator))) (then 213 | (return (i32.const 0)) 214 | )) 215 | (set_local $parts (call $-new_value (i32.const 4) (i32.const 0))) 216 | (set_local $sub (call $-new_value (call $-datatype (get_local $seperator)) (call $-len (get_local $seperator)))) 217 | (block(loop 218 | (br_if 1 (i32.gt_u (get_local $pos) (i32.sub (call $-len (get_local $string)) (call $-len (get_local $seperator))))) 219 | (call $-memcopy 220 | (i32.add (call $-offset (get_local $string)) (get_local $pos)) 221 | (call $-offset (get_local $sub)) 222 | (call $-len (get_local $sub)) 223 | ) 224 | (if (i32.eqz (call $-compare (get_local $sub) (get_local $seperator))) (then 225 | (call $-write32 226 | (get_local $parts) 227 | (call $-len (get_local $parts)) 228 | (call $-string 229 | (i32.add 230 | (call $-offset (get_local $string)) 231 | (get_local $start) 232 | ) 233 | (i32.sub 234 | (get_local $pos) 235 | (get_local $start) 236 | ) 237 | ) 238 | ) 239 | (set_local $start (i32.add 240 | (get_local $pos) 241 | (call $-len (get_local $seperator)) 242 | )) 243 | (set_local $pos (get_local $start)) 244 | )(else 245 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 246 | )) 247 | (br 0) 248 | )) 249 | (call $-write32 250 | (get_local $parts) 251 | (call $-len (get_local $parts)) 252 | (call $-string 253 | (i32.add 254 | (call $-offset (get_local $string)) 255 | (get_local $start) 256 | ) 257 | (i32.sub 258 | (call $-len (get_local $string)) 259 | (get_local $start) 260 | ) 261 | ) 262 | ) 263 | (get_local $parts) 264 | ) 265 | (func $char (param $code i32) (result i32) 266 | (set_local $code (call $-i32_u (get_local $code))) 267 | (call $-char (get_local $code)) 268 | ) 269 | (func $char_code (param $string i32) (param $pos i32) (result i32) 270 | (set_local $pos (i32.add 271 | (call $-offset (get_local $string)) 272 | (call $-chars_to_bytes 273 | (call $-offset (get_local $string)) 274 | (call $-i32_u (get_local $pos)) 275 | ) 276 | )) 277 | (call $-integer_u (call $-char_code (get_local $pos))) 278 | ) 279 | 280 | ;; array functions 281 | (func $array_length (param $array i32) (result i32) 282 | (call $-integer_u (i32.div_u (call $-len (get_local $array)) (i32.const 4))) 283 | ) 284 | (func $array_insert (param $array i32) (param $index i32) (param $value i32) (result i32) 285 | (local $rest i32) 286 | (set_local $index (i32.mul (call $-i32_u (get_local $index)) (i32.const 4))) 287 | (if (i32.lt_u (call $-len (get_local $array)) (get_local $index))(then 288 | (set_local $index (call $-len (get_local $array))) 289 | )) 290 | (set_local $rest (i32.sub (call $-len (get_local $array)) (get_local $index))) 291 | (call $-resize 292 | (get_local $array) 293 | (i32.add (i32.add (get_local $index) (get_local $rest)) (i32.const 4)) 294 | ) 295 | (call $-memcopy 296 | (i32.add (call $-offset (get_local $array)) (get_local $index)) 297 | (i32.add (i32.add (call $-offset (get_local $array)) (get_local $index)) (i32.const 4)) 298 | (get_local $rest) 299 | ) 300 | (call $-write32 (get_local $array) (get_local $index) (get_local $value)) 301 | (get_local $value) 302 | ) 303 | (func $array_remove (param $array i32) (param $index i32) (result i32) 304 | (local $value i32) 305 | (local $rest i32) 306 | (set_local $index (i32.mul (call $-i32_u (get_local $index)) (i32.const 4))) 307 | (if (i32.le_u (call $-len (get_local $array)) (get_local $index))(then 308 | (return (i32.const 0)) 309 | )) 310 | (set_local $rest (i32.sub (i32.sub (call $-len (get_local $array)) (get_local $index)) (i32.const 4))) 311 | (set_local $value (call $-read32 (get_local $array) (get_local $index))) 312 | (call $-memcopy 313 | (i32.add (i32.add (call $-offset (get_local $array)) (get_local $index)) (i32.const 4)) 314 | (i32.add (call $-offset (get_local $array)) (get_local $index)) 315 | (get_local $rest) 316 | ) 317 | (call $-resize 318 | (get_local $array) 319 | (i32.add (get_local $index) (get_local $rest)) 320 | ) 321 | (get_local $value) 322 | ) 323 | (func $array_push (param $array i32) (param $value i32) (result i32) 324 | (call $array_insert (get_local $array) (call $array_length (get_local $array)) (get_local $value)) 325 | ) 326 | (func $array_pop (param $array i32) (result i32) 327 | (call $array_remove 328 | (get_local $array) 329 | (call $-sub 330 | (call $array_length (get_local $array)) 331 | (call $-integer_u (i32.const 1)) 332 | ) 333 | ) 334 | ) 335 | (func $array_unshift (param $array i32) (param $value i32) (result i32) 336 | (call $array_insert (get_local $array) (i32.const 2) (get_local $value)) 337 | ) 338 | (func $array_shift (param $array i32) (result i32) 339 | (call $array_remove 340 | (get_local $array) 341 | (i32.const 2) 342 | ) 343 | ) 344 | (func $array_search (param $array i32) (param $value i32) (param $start i32) (result i32) 345 | (local $index i32) 346 | (local $len i32) 347 | (local $pos i32) 348 | (set_local $start (i32.mul (call $-i32_u (get_local $start)) (i32.const 4))) 349 | (if (i32.lt_u (call $-len (get_local $array)) (get_local $start))(then 350 | (return (i32.const 0)) 351 | )) 352 | (set_local $len (i32.sub (call $-len (get_local $array)) (get_local $start))) 353 | (set_local $pos (i32.add (call $-offset (get_local $array)) (get_local $start))) 354 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 355 | (if (call $-truthy (call $-equal (get_local $value) (i32.load (get_local $pos))))(then 356 | (set_local $index (call $-integer_u (i32.div_u 357 | (i32.sub 358 | (get_local $pos ) 359 | (call $-offset 360 | (get_local $array ) 361 | ) 362 | ) 363 | (i32.const 4 ) 364 | ))) 365 | (br 2) 366 | )) 367 | (set_local $pos (i32.add (get_local $pos) (i32.const 4))) 368 | (set_local $len (i32.sub (get_local $len) (i32.const 4))) 369 | (br 0))) 370 | 371 | (get_local $index) 372 | ) 373 | (func $array_slice (param $array i32) (param $start i32) (param $len i32) (result i32) 374 | (local $out i32) 375 | (set_local $start (i32.mul (call $-i32_u (get_local $start)) (i32.const 4))) 376 | (set_local $len (i32.mul (call $-i32_u (get_local $len)) (i32.const 4))) 377 | (if (i32.lt_u (call $-len (get_local $array)) (get_local $start))(then 378 | (set_local $start (call $-len (get_local $array))) 379 | )) 380 | (if (i32.lt_u (i32.sub (call $-len (get_local $array)) (get_local $start)) (get_local $len))(then 381 | (set_local $len (i32.sub (call $-len (get_local $array)) (get_local $start))) 382 | )) 383 | (set_local $out (call $-new_value (i32.const 4) (call $-i32_u (get_local $len)))) 384 | (call $-memcopy (i32.add (call $-offset (get_local $array)) (get_local $start)) (call $-offset (get_local $out)) (get_local $len)) 385 | (get_local $out) 386 | ) 387 | (func $array_splice (param $array i32) (param $start i32) (param $delete i32) (param $replace i32) (result i32) 388 | (local $rest i32) 389 | (set_local $start (i32.mul (call $-i32_u (get_local $start)) (i32.const 4))) 390 | (set_local $delete (i32.mul (call $-i32_u (get_local $delete)) (i32.const 4))) 391 | (if (i32.lt_u (call $-len (get_local $array)) (get_local $start))(then 392 | (set_local $start (call $-len (get_local $array))) 393 | )) 394 | (if (i32.lt_u (i32.sub (call $-len (get_local $array)) (get_local $start)) (get_local $delete))(then 395 | (set_local $delete (i32.sub (call $-len (get_local $array)) (get_local $start))) 396 | )) 397 | (set_local $rest (i32.sub (i32.sub (call $-len (get_local $array)) (get_local $start)) (get_local $delete))) 398 | (call $-memcopy 399 | (i32.add (i32.add (call $-offset (get_local $array)) (get_local $start)) (get_local $delete)) 400 | (i32.add (call $-offset (get_local $array)) (get_local $start)) 401 | (get_local $rest) 402 | ) 403 | (call $-resize 404 | (get_local $array) 405 | (i32.add (i32.add (get_local $start) (get_local $rest)) (call $-len (get_local $replace))) 406 | ) 407 | (call $-memcopy 408 | (i32.add (call $-offset (get_local $array)) (get_local $start)) 409 | (i32.add (i32.add (call $-offset (get_local $array)) (get_local $start)) (call $-len (get_local $replace))) 410 | (get_local $rest) 411 | ) 412 | (call $-memcopy 413 | (call $-offset (get_local $replace)) 414 | (i32.add (call $-offset (get_local $array)) (get_local $start)) 415 | (call $-len (get_local $replace)) 416 | ) 417 | (i32.const 0) 418 | ) 419 | (func $array_sort (param $array i32) (result i32) 420 | (local $out i32) 421 | (local $len i32) 422 | (local $pos i32) 423 | (local $ins i32) 424 | (local $val i32) 425 | (set_local $out (call $-new_value (i32.const 4) (call $-len (get_local $array)))) 426 | (set_local $len (i32.div_u (call $-len (get_local $array)) (i32.const 4))) 427 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 428 | (set_local $val 429 | (call $-read32 430 | (get_local $array) 431 | (i32.mul (get_local $pos) (i32.const 4)) 432 | ) 433 | ) 434 | (set_local $ins (get_local $pos)) 435 | (block(loop (br_if 1 (i32.eqz (get_local $ins))) 436 | (br_if 1 (call $-ge 437 | (get_local $val) 438 | (call $-read32 439 | (get_local $out) 440 | (i32.mul (i32.sub (get_local $ins) (i32.const 1)) (i32.const 4)) 441 | ) 442 | )) 443 | (call $-write32 444 | (get_local $out) 445 | (i32.mul (get_local $ins) (i32.const 4)) 446 | (call $-read32 447 | (get_local $out) 448 | (i32.mul (i32.sub (get_local $ins) (i32.const 1)) (i32.const 4)) 449 | ) 450 | ) 451 | (set_local $ins (i32.sub (get_local $ins) (i32.const 1))) 452 | (br 0))) 453 | (call $-write32 454 | (get_local $out) 455 | (i32.mul (get_local $ins) (i32.const 4)) 456 | (get_local $val) 457 | ) 458 | (set_local $pos (i32.add (get_local $pos) (i32.const 1))) 459 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 460 | (br 0))) 461 | (get_local $out) 462 | ) 463 | (func $array_join (param $array i32) (param $glue i32) (result i32) 464 | (local $string i32) 465 | (local $strlen i32) 466 | (local $part i32) 467 | (local $pos i32) 468 | (local $len i32) 469 | (set_local $string (call $-new_value (i32.const 3) (get_local $strlen))) 470 | (set_local $len (call $-len (get_local $array))) 471 | (if (get_local $len)(then 472 | (set_local $part (call $-to_string (call $-read32 (get_local $array) (get_local $pos)))) 473 | (call $-resize (get_local $string) (i32.add 474 | (get_local $strlen) 475 | (call $-len (get_local $part)) 476 | )) 477 | (call $-memcopy 478 | (call $-offset (get_local $part)) 479 | (i32.add 480 | (call $-offset (get_local $string)) 481 | (get_local $strlen) 482 | ) 483 | (call $-len (get_local $part)) 484 | ) 485 | (set_local $strlen (i32.add (get_local $strlen) (call $-len (get_local $part)))) 486 | (set_local $pos (i32.add (get_local $pos) (i32.const 4))) 487 | (set_local $len (i32.sub (get_local $len) (i32.const 4))) 488 | )) 489 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 490 | (set_local $part (call $-to_string (call $-read32 (get_local $array) (get_local $pos)))) 491 | (call $-resize (get_local $string) (i32.add 492 | (get_local $strlen) 493 | (i32.add 494 | (call $-len (get_local $glue)) 495 | (call $-len (get_local $part)) 496 | ) 497 | )) 498 | (call $-memcopy 499 | (call $-offset (get_local $glue)) 500 | (i32.add 501 | (call $-offset (get_local $string)) 502 | (get_local $strlen) 503 | ) 504 | (call $-len (get_local $glue)) 505 | ) 506 | (set_local $strlen (i32.add (get_local $strlen) (call $-len (get_local $glue)))) 507 | (call $-memcopy 508 | (call $-offset (get_local $part)) 509 | (i32.add 510 | (call $-offset (get_local $string)) 511 | (get_local $strlen) 512 | ) 513 | (call $-len (get_local $part)) 514 | ) 515 | (set_local $strlen (i32.add (get_local $strlen) (call $-len (get_local $part)))) 516 | (set_local $pos (i32.add (get_local $pos) (i32.const 4))) 517 | (set_local $len (i32.sub (get_local $len) (i32.const 4))) 518 | (br 0))) 519 | (get_local $string) 520 | ) 521 | (func $range (param $start i32) (param $end i32) (param $step i32) (result i32) 522 | (local $_start f64) 523 | (local $_end f64) 524 | (local $_step f64) 525 | (local $out i32) 526 | (local $offset i32) 527 | (local $len i32) 528 | (set_local $_start (call $-f64 (get_local $start))) 529 | (set_local $_end (call $-f64 (get_local $end))) 530 | (set_local $_step (call $-f64 (get_local $step))) 531 | (if (f64.eq (get_local $_step) (f64.const 0))(then 532 | (if (f64.gt (get_local $_start) (get_local $_end))(then 533 | (set_local $_step (f64.const -1)) 534 | )(else 535 | (set_local $_step (f64.const 1)) 536 | )) 537 | )) 538 | (set_local $out (call $-new_value (i32.const 4) (i32.trunc_u/f64 539 | (f64.mul 540 | (f64.floor (f64.div 541 | (f64.sub 542 | (get_local $_end) 543 | (get_local $_start) 544 | ) 545 | (get_local $_step) 546 | )) 547 | (f64.const 4) 548 | ) 549 | ))) 550 | (set_local $offset (call $-offset (get_local $out))) 551 | (set_local $len (call $-len (get_local $out))) 552 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 553 | (i32.store (get_local $offset) (call $-number (get_local $_start))) 554 | (set_local $_start (f64.add (get_local $_start) (get_local $_step))) 555 | (set_local $offset (i32.add (get_local $offset) (i32.const 4))) 556 | (set_local $len (i32.sub (get_local $len) (i32.const 4))) 557 | (br 0))) 558 | (get_local $out) 559 | ) 560 | 561 | ;; object functions 562 | (func $object_keys (param $object i32) (result i32) 563 | (local $out i32) 564 | (local $len i32) 565 | (set_local $out (call $-new_value (i32.const 4) (i32.div_u (call $-len (get_local $object)) (i32.const 2)))) 566 | (set_local $len (i32.div_u (call $-len (get_local $out)) (i32.const 4))) 567 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 568 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 569 | (call $-write32 570 | (get_local $out) 571 | (i32.mul (get_local $len) (i32.const 4)) 572 | (call $-read32 573 | (get_local $object) 574 | (i32.mul (get_local $len) (i32.const 8)) 575 | ) 576 | ) 577 | (br 0))) 578 | (get_local $out) 579 | ) 580 | (func $object_values (param $object i32) (result i32) 581 | (local $out i32) 582 | (local $len i32) 583 | (set_local $out (call $-new_value (i32.const 4) (i32.div_u (call $-len (get_local $object)) (i32.const 2)))) 584 | (set_local $len (i32.div_u (call $-len (get_local $out)) (i32.const 4))) 585 | (block(loop (br_if 1 (i32.eqz (get_local $len))) 586 | (set_local $len (i32.sub (get_local $len) (i32.const 1))) 587 | (call $-write32 588 | (get_local $out) 589 | (i32.mul (get_local $len) (i32.const 4)) 590 | (call $-read32 591 | (get_local $object) 592 | (i32.add (i32.mul (get_local $len) (i32.const 8)) (i32.const 4)) 593 | ) 594 | ) 595 | (br 0))) 596 | (get_local $out) 597 | ) 598 | 599 | ;; math functions 600 | (func $abs (param $number i32) (result i32) 601 | (call $-number (f64.abs (call $-f64 (get_local $number)))) 602 | ) 603 | (func $ceil (param $number i32) (result i32) 604 | (call $-number (f64.ceil (call $-f64 (get_local $number)))) 605 | ) 606 | (func $floor (param $number i32) (result i32) 607 | (call $-number (f64.floor (call $-f64 (get_local $number)))) 608 | ) 609 | (func $nearest (param $number i32) (result i32) 610 | (call $-number (f64.nearest (call $-f64 (get_local $number)))) 611 | ) 612 | (func $sqrt (param $number i32) (result i32) 613 | (call $-number (f64.sqrt (call $-f64 (get_local $number)))) 614 | ) 615 | (func $min (param $number1 i32) (param $number2 i32) (result i32) 616 | (call $-number (f64.min 617 | (call $-f64 (get_local $number1)) 618 | (call $-f64 (get_local $number2)) 619 | )) 620 | ) 621 | (func $max (param $number1 i32) (param $number2 i32) (result i32) 622 | (call $-number (f64.max 623 | (call $-f64 (get_local $number1)) 624 | (call $-f64 (get_local $number2)) 625 | )) 626 | ) 627 | 628 | ;; JSON 629 | (func $json_encode (param $value i32) (result i32) 630 | (local $datatype i32) 631 | (local $len i32) 632 | (local $json_string i32) 633 | (local $char i32) 634 | (local $done i32) 635 | (local $ipos i32) 636 | (local $opos i32) 637 | (set_local $datatype (call $-datatype (get_local $value))) 638 | (set_local $len (call $-len (get_local $value))) 639 | (if (i32.lt_u (get_local $datatype) (i32.const 3))(then 640 | (set_local $json_string (call $-to_string (get_local $value))) 641 | )) 642 | (if (i32.gt_u (get_local $datatype) (i32.const 5))(then 643 | (set_local $value (call $-to_string (get_local $value))) 644 | (set_local $datatype (i32.const 3)) 645 | )) 646 | (if (i32.eq (get_local $datatype) (i32.const 3))(then ;; string 647 | (set_local $json_string (call $-new_value (i32.const 3) (call $-len (get_local $value)))) 648 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x22)) ;; " 649 | (set_local $opos (i32.add (get_local $opos) (i32.const 1))) 650 | (block(loop (br_if 1 (i32.ge_u (get_local $ipos) (get_local $len))) 651 | (set_local $done (i32.const 0)) 652 | (set_local $char (call $-read8 (get_local $value) (get_local $ipos))) 653 | (if (i32.eq (get_local $char) (i32.const 0x08))(then 654 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x625c)) ;; \b 655 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 656 | (set_local $done (i32.const 1)) 657 | )) 658 | (if (i32.eq (get_local $char) (i32.const 0x09))(then 659 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x745c)) ;; \t 660 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 661 | (set_local $done (i32.const 1)) 662 | )) 663 | (if (i32.eq (get_local $char) (i32.const 0x0a))(then 664 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x6e5c)) ;; \n 665 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 666 | (set_local $done (i32.const 1)) 667 | )) 668 | (if (i32.eq (get_local $char) (i32.const 0x0c))(then 669 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x665c)) ;; \f 670 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 671 | (set_local $done (i32.const 1)) 672 | )) 673 | (if (i32.eq (get_local $char) (i32.const 0x0d))(then 674 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x725c)) ;; \r 675 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 676 | (set_local $done (i32.const 1)) 677 | )) 678 | (if (i32.eq (get_local $char) (i32.const 0x22))(then 679 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x225c)) ;; \" 680 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 681 | (set_local $done (i32.const 1)) 682 | )) 683 | (if (i32.eq (get_local $char) (i32.const 0x5c))(then 684 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x5c5c)) ;; \\ 685 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 686 | (set_local $done (i32.const 1)) 687 | )) 688 | (if (i32.eq (get_local $char) (i32.const 0x7f))(then 689 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x755c)) ;; \u 690 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 691 | (call $-write32 (get_local $json_string) (get_local $opos) (i32.const 0x66373030)) ;; 007f 692 | (set_local $opos (i32.add (get_local $opos) (i32.const 4))) 693 | (set_local $done (i32.const 1)) 694 | )) 695 | (if (i32.eqz (get_local $done))(then 696 | (if (i32.lt_u (get_local $char) (i32.const 0x20))(then 697 | (call $-write16 (get_local $json_string) (get_local $opos) (i32.const 0x755c)) ;; \u 698 | (set_local $opos (i32.add (get_local $opos) (i32.const 2))) 699 | (set_local $done (call $-to_hex (get_local $char) (i32.const 4))) 700 | (call $-write32 (get_local $json_string) (get_local $opos) (call $-read32 (get_local $done) (i32.const 0))) 701 | (set_local $opos (i32.add (get_local $opos) (i32.const 4))) 702 | )(else 703 | (call $-write8 (get_local $json_string) (get_local $opos) (get_local $char)) 704 | (set_local $opos (i32.add (get_local $opos) (i32.const 1))) 705 | )) 706 | )) 707 | (set_local $ipos (i32.add (get_local $ipos) (i32.const 1))) 708 | (br 0))) 709 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x22)) ;; " 710 | )) 711 | (if (i32.eq (get_local $datatype) (i32.const 4))(then ;; array 712 | (set_local $json_string (call $-new_value (i32.const 3) (i32.const 0))) 713 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x5b)) ;; [ 714 | (set_local $opos (i32.add (get_local $opos) (i32.const 1))) 715 | (block(loop (br_if 1 (i32.ge_u (get_local $ipos) (get_local $len))) 716 | (set_local $char (call $-read32 (get_local $value) (get_local $ipos))) 717 | (set_local $ipos (i32.add (get_local $ipos) (i32.const 4))) 718 | (set_local $opos (call $-len (get_local $json_string))) 719 | (call $-write_to (get_local $json_string) (get_local $opos) (call $json_encode (get_local $char))) 720 | (set_local $opos (call $-len (get_local $json_string))) 721 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x2c)) ;; , 722 | (br 0))) 723 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x5d)) ;; ] 724 | )) 725 | (if (i32.eq (get_local $datatype) (i32.const 5))(then ;; object 726 | (set_local $json_string (call $-new_value (i32.const 3) (i32.const 0))) 727 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x7b)) ;; { 728 | (set_local $opos (i32.add (get_local $opos) (i32.const 1))) 729 | (block(loop (br_if 1 (i32.ge_u (get_local $ipos) (get_local $len))) 730 | (set_local $char (call $-read32 (get_local $value) (get_local $ipos))) 731 | (set_local $ipos (i32.add (get_local $ipos) (i32.const 4))) 732 | (set_local $opos (call $-len (get_local $json_string))) 733 | (call $-write_to (get_local $json_string) (get_local $opos) (call $json_encode (call $-to_string (get_local $char)))) 734 | (set_local $opos (call $-len (get_local $json_string))) 735 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x3a)) ;; : 736 | (set_local $char (call $-read32 (get_local $value) (get_local $ipos))) 737 | (set_local $ipos (i32.add (get_local $ipos) (i32.const 4))) 738 | (set_local $opos (call $-len (get_local $json_string))) 739 | (call $-write_to (get_local $json_string) (get_local $opos) (call $json_encode (get_local $char))) 740 | (set_local $opos (call $-len (get_local $json_string))) 741 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x2c)) ;; , 742 | (br 0))) 743 | (call $-write8 (get_local $json_string) (get_local $opos) (i32.const 0x7d)) ;; } 744 | )) 745 | (get_local $json_string) 746 | ) 747 | (func $json_decode (param $json_string i32) (result i32) 748 | (local $datatype i32) 749 | (local $value i32) 750 | (set_local $datatype (call $-datatype (get_local $json_string))) 751 | (set_local $value (get_local $json_string)) 752 | (set_global $~pos (call $-offset (get_local $json_string))) 753 | (if (i32.eq (get_local $datatype) (i32.const 3))(then 754 | (set_local $value (call $~json_decode)) 755 | )) 756 | (if (i32.eq (get_local $datatype) (i32.const 6))(then 757 | (set_local $value (call $~json_decode)) 758 | )) 759 | (get_local $value) 760 | ) 761 | (global $~pos (mut i32) (i32.const 0)) 762 | (func $~json_decode (result i32) 763 | (local $value i32) 764 | (local $err i32) 765 | (local $char i32) 766 | (local $pos i32) 767 | (local $hex i32) 768 | (local $num f64) 769 | (local $neg f64) 770 | (local $exp f64) 771 | (local $eneg f64) 772 | (set_local $err (i32.eqz (call $~skip_whitespace))) 773 | (set_local $char (i32.load8_u (get_global $~pos))) 774 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 775 | (if (i32.eq (get_local $char) (i32.const 0x6e))(then ;; n 776 | (set_global $~pos (i32.sub (get_global $~pos) (i32.const 1))) 777 | (set_local $char (i32.load (get_global $~pos))) 778 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 4))) 779 | (if (i32.eq (get_local $char) (i32.const 0x6c6c756e))(then ;; null 780 | (set_local $value (i32.const 0)) 781 | )(else 782 | (set_local $char (i32.const 0)) 783 | (set_local $err (i32.const 1)) 784 | )) 785 | )) 786 | (if (i32.eq (get_local $char) (i32.const 0x66))(then ;; f 787 | (set_local $char (i32.load (get_global $~pos))) 788 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 4))) 789 | (if (i32.eq (get_local $char) (i32.const 0x65736c61))(then ;; alse 790 | (set_local $value (i32.const 1)) 791 | )(else 792 | (set_local $char (i32.const 0)) 793 | (set_local $err (i32.const 1)) 794 | )) 795 | )) 796 | (if (i32.eq (get_local $char) (i32.const 0x74))(then ;; t 797 | (set_global $~pos (i32.sub (get_global $~pos) (i32.const 1))) 798 | (set_local $char (i32.load (get_global $~pos))) 799 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 4))) 800 | (if (i32.eq (get_local $char) (i32.const 0x65757274))(then ;; true 801 | (set_local $value (i32.const 5)) 802 | )(else 803 | (set_local $char (i32.const 0)) 804 | (set_local $err (i32.const 1)) 805 | )) 806 | )) 807 | (if (i32.or 808 | (i32.eq (get_local $char) (i32.const 0x2d)) ;; - 809 | (i32.and 810 | (i32.ge_u (get_local $char) (i32.const 0x30)) ;; 0 811 | (i32.le_u (get_local $char) (i32.const 0x39)) ;; 9 812 | )) (then 813 | (set_global $~pos (i32.sub (get_global $~pos) (i32.const 1))) 814 | (set_local $value (call $-number (call $-parse_float (get_global $~pos) (i32.const 10)))) 815 | (set_global $~pos (get_global $-parsing_offset)) 816 | )) 817 | (if (i32.eq (get_local $char) (i32.const 0x22))(then ;; " 818 | (set_local $char (i32.load8_u (get_global $~pos))) 819 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 820 | (set_local $value (call $-new_value (i32.const 3) (i32.const 0))) 821 | (block(loop (br_if 1 (i32.eq (get_local $char) (i32.const 0x22))) ;; " 822 | (set_local $pos (call $-len (get_local $value))) 823 | (if (i32.eq (get_local $char) (i32.const 0x5c))(then ;; \ 824 | (set_local $char (i32.load8_u (get_global $~pos))) 825 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 826 | (call $-write8 (get_local $value) (get_local $pos) (get_local $char)) 827 | (if (i32.eq (get_local $char) (i32.const 0x62))(then ;; b 828 | (call $-write8 (get_local $value) (get_local $pos) (i32.const 0x08)) 829 | )) 830 | (if (i32.eq (get_local $char) (i32.const 0x66))(then ;; f 831 | (call $-write8 (get_local $value) (get_local $pos) (i32.const 0x0c)) 832 | )) 833 | (if (i32.eq (get_local $char) (i32.const 0x6e))(then ;; n 834 | (call $-write8 (get_local $value) (get_local $pos) (i32.const 0x0a)) 835 | )) 836 | (if (i32.eq (get_local $char) (i32.const 0x72))(then ;; r 837 | (call $-write8 (get_local $value) (get_local $pos) (i32.const 0x0d)) 838 | )) 839 | (if (i32.eq (get_local $char) (i32.const 0x74))(then ;; t 840 | (call $-write8 (get_local $value) (get_local $pos) (i32.const 0x09)) 841 | )) 842 | (if (i32.eq (get_local $char) (i32.const 0x75))(then ;; u 843 | (if (i32.eqz (get_local $hex))(then 844 | (set_local $hex (call $-new_value (i32.const 3) (i32.const 4))) 845 | )) 846 | (set_local $char (i32.load (get_global $~pos))) 847 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 4))) 848 | (call $-write32 (get_local $hex) (i32.const 0) (get_local $char)) 849 | (set_local $char (call $-from_hex (get_local $hex))) 850 | (if (i32.eq (i32.and (get_local $char) (i32.const 0xfc00)) (i32.const 0xd800))(then ;; surrogate pair 851 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 2))) 852 | (call $-write32 (get_local $hex) (i32.const 0) (i32.load (get_global $~pos))) 853 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 4))) 854 | (set_local $hex (call $-from_hex (get_local $hex))) 855 | (set_local $char (i32.mul (i32.sub (get_local $char) (i32.const 0xd800)) (i32.const 0x400))) 856 | (set_local $hex (i32.sub (get_local $hex) (i32.const 0xdc00))) 857 | (set_local $char (i32.add (i32.add (get_local $char) (get_local $hex)) (i32.const 0x10000))) 858 | (set_local $char (call $-char (get_local $char))) 859 | (set_local $hex (i32.const 0)) 860 | )(else 861 | (set_local $char (call $-char (get_local $char))) 862 | )) 863 | (call $-write_to (get_local $value) (get_local $pos) (get_local $char)) 864 | )) 865 | )(else 866 | (call $-write8 (get_local $value) (get_local $pos) (get_local $char)) 867 | )) 868 | 869 | (set_local $char (i32.load8_u (get_global $~pos))) 870 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 871 | (br 0))) 872 | )) 873 | (if (i32.eq (get_local $char) (i32.const 0x5b))(then ;; [ 874 | (set_local $value (call $-new_value (i32.const 4) (i32.const 0))) 875 | (set_local $char (call $~skip_whitespace)) 876 | (set_local $err (i32.eqz (call $~skip_whitespace))) 877 | (block(loop (br_if 1 (i32.or (get_local $err) (i32.eq (get_local $char) (i32.const 0x5d)))) ;; ] 878 | (if (i32.eq (get_local $char) (i32.const 0x2c))(then ;; , 879 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 880 | )) 881 | (call $-write32 (get_local $value) (call $-len (get_local $value)) (call $~json_decode)) 882 | (set_local $char (call $~skip_whitespace)) 883 | (set_local $err (i32.eqz (call $~skip_whitespace))) 884 | (br 0))) 885 | (if (i32.eq (get_local $char) (i32.const 0x5d))(then ;; ] 886 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 887 | )) 888 | )) 889 | (if (i32.eq (get_local $char) (i32.const 0x7b))(then ;; { 890 | (set_local $value (call $-new_value (i32.const 5) (i32.const 0))) 891 | (set_local $char (call $~skip_whitespace)) 892 | (set_local $err (i32.eqz (call $~skip_whitespace))) 893 | (block(loop (br_if 1 (i32.or (get_local $err) (i32.eq (get_local $char) (i32.const 0x7d)))) ;; } 894 | (if (i32.eq (get_local $char) (i32.const 0x2c))(then ;; , 895 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 896 | )) 897 | (call $-write32 (get_local $value) (call $-len (get_local $value)) (call $~json_decode)) 898 | (set_local $char (call $~skip_whitespace)) 899 | (set_local $err (i32.eqz (call $~skip_whitespace))) 900 | (if (i32.eq (get_local $char) (i32.const 0x3a))(then ;; : 901 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 902 | )) 903 | (call $-write32 (get_local $value) (call $-len (get_local $value)) (call $~json_decode)) 904 | (set_local $char (call $~skip_whitespace)) 905 | (set_local $err (i32.eqz (call $~skip_whitespace))) 906 | (br 0))) 907 | (if (i32.eq (get_local $char) (i32.const 0x7d))(then ;; } 908 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 909 | )) 910 | )) 911 | (get_local $value) 912 | ) 913 | (func $~skip_whitespace (result i32) 914 | (local $char i32) 915 | (local $err i32) 916 | (set_local $char (i32.load8_u (get_global $~pos))) 917 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 918 | (block(loop (br_if 1 (i32.or (get_local $err) (i32.gt_u (get_local $char) (i32.const 0x20)))) 919 | (if (i32.eqz (get_local $char)) (then 920 | (set_local $err (i32.const 1)) 921 | )(else 922 | (set_local $char (i32.load8_u (get_global $~pos))) 923 | (set_global $~pos (i32.add (get_global $~pos) (i32.const 1))) 924 | )) 925 | (br 0))) 926 | (set_global $~pos (i32.sub (get_global $~pos) (i32.const 1))) 927 | (get_local $char) 928 | ) 929 | -------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Experiment 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | function read_bin(offset, len) { 2 | return wasm.memory.buffer.slice(offset, offset + len) 3 | } 4 | function read_string(offset, len) { 5 | let dec = new TextDecoder() 6 | let buf = read_bin(offset, len) 7 | return dec.decode(buf) 8 | } 9 | 10 | let importObject = { 11 | env: { 12 | logNumber: console.log, 13 | log: (offset, len) => { 14 | console.log(read_string(offset, len)) 15 | } 16 | } 17 | } 18 | 19 | fetch('boot.wasm').then(response => 20 | response.arrayBuffer() 21 | ).then(bytes => 22 | WebAssembly.instantiate(bytes, importObject) 23 | ).then(obj => { 24 | window.wasm = obj.instance.exports 25 | console.groupCollapsed() 26 | wasm.init() 27 | console.groupEnd() 28 | console.groupCollapsed() 29 | wasm.init() 30 | console.groupEnd() 31 | console.group() 32 | wasm.init() 33 | console.groupEnd() 34 | } 35 | ) 36 | 37 | var bits = 1 38 | document.write('') 39 | document.getElementsByTagName("button")[0].addEventListener("click", () => { 40 | console.log(++bits, max = Math.pow(2, bits)) 41 | for (let i = -8; i < 8; i++) { 42 | let j = max + i 43 | console.log(j === wasm.echo(j)) 44 | } 45 | }) 46 | 47 | function memoryIndex() { 48 | let mem = new Int32Array(wasm.memory.buffer) 49 | let mindex = wasm.get_mindex() 50 | let mindexLen = mem[mindex / 4 - 1] 51 | let id = 8 52 | let index = [] 53 | for (let i = mindex; i < mindex + mindexLen; i += 8) { 54 | let item = {} 55 | index.push(item) 56 | item.offset = mem[i / 4] 57 | item.len = mem[item.offset / 4 - 1] 58 | item.datatype = mem[i / 4 + 1] / Math.pow(2, 16) 59 | item.used = item.datatype > 255 60 | item.datatype %= 256 61 | try { 62 | item.value = getValue(id++) 63 | } catch (error) { 64 | item.value = undefined 65 | } 66 | } 67 | console.table(index) 68 | } 69 | 70 | function getValue(id) { 71 | if (id < 8) { 72 | return [null, false, 0, "", undefined, true][id] 73 | } 74 | let mem = new Int32Array(wasm.memory.buffer) 75 | let mindex = wasm.get_mindex() 76 | id -= 8 77 | let i = mindex + 8 * id 78 | 79 | let item = {} 80 | item.offset = mem[i / 4] 81 | item.len = mem[item.offset / 4 - 1] 82 | item.datatype = (mem[i / 4 + 1] / Math.pow(2, 16)) % 256 83 | switch (item.datatype) { 84 | case 0: 85 | return null 86 | case 1: 87 | return undefined 88 | case 2: 89 | return wasm.load_f64(item.offset) 90 | case 3: 91 | return read_string(item.offset, item.len) 92 | case 4: 93 | let ar = [] 94 | for (let i = item.offset; i < item.offset + item.len; i += 4) { 95 | ar.push(getValue(mem[i / 4])) 96 | } 97 | return ar 98 | case 5: 99 | let obj = {} 100 | for (let i = item.offset; i < item.offset + item.len; i += 8) { 101 | obj[getValue(mem[i / 4])] = getValue(mem[i / 4 + 1]) 102 | } 103 | return obj 104 | case 6: 105 | return read_bin(item.offset, item.len) 106 | 107 | default: 108 | return undefined 109 | } 110 | } 111 | 112 | function validateAlloc() { 113 | let mem = new Int32Array(wasm.memory.buffer) 114 | let allocs = [] 115 | let offset = 0 116 | let space = mem[offset / 4] 117 | while (offset < mem.length - 1) { 118 | let alloc = {} 119 | allocs.push(alloc) 120 | alloc.free = space 121 | offset += space 122 | if (space !== mem[offset / 4]) console.error("memory error at", offset) 123 | offset += 4 124 | space = mem[offset / 4] 125 | 126 | alloc.offset = offset + 4 127 | alloc.used = space 128 | offset += space + 4 129 | offset = Math.floor(offset / 8) * 8 + 8 130 | space = mem[offset / 4] 131 | space = Math.floor(space / 8) * 8 132 | } 133 | console.table(allocs) 134 | } --------------------------------------------------------------------------------