Web-based NovaForth repl

This commit is contained in:
2023-01-01 17:26:21 -06:00
parent 63d58b63e3
commit 2dea244673
13 changed files with 1097 additions and 0 deletions
+2
View File
@@ -1 +1,3 @@
/target
/vweb/node_modules/
/vweb/build/
BIN
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
{"$BRNZ":28,"$BRZ":27,"$CALL":25,"$JMP":23,"$JMPR":24,"$PUSH":0,"$RET":26,"$SWAP":20,"$end":4585,"$start":1024,"advance_entry":1141,"backslash":3276,"close_paren":3267,"close_paren_stub":3275,"compare":1414,"compile_dict_start":3429,"compile_dictionary":4222,"compile_handleword":3150,"compile_instruction":1537,"compile_instruction_arg":1511,"compile_tick":1196,"cr":1067,"cursor":4210,"data_start":3340,"dict_start":3588,"dictionary":4219,"does_at_runtime":2225,"does_word":2196,"dupnz":1074,"dupnz_done":1080,"emit":1025,"emit_cursor":1042,"emit_hook":4228,"end0_pop1":1093,"end0_pop2":1092,"end1_pop2":1096,"eval":1773,"expected_word_err":3388,"find_in_dict":1149,"find_in_dict_next":1179,"find_word":2584,"handleword_hook":4198,"heap":4195,"heap_start":4586,"hex_is_number":1280,"hex_itoa":1701,"immediate_handleword":3113,"input_number":1595,"invalid_mnemonic":3293,"invalid_mnemonic_str":3353,"is_digit":1084,"is_number":1205,"is_number_hook":4201,"itoa":1618,"itoa_hook":4204,"itoa_loop":1648,"itoa_pos":1637,"itoa_print_loop":1685,"lambda_nesting_level":4216,"lambda_start_ptr":4213,"line_len":4207,"linecomment":3209,"missing_word":3301,"missing_word_str":3340,"mnemonics":2258,"mnemonics_end":2469,"new_dict":1451,"new_dict_error":1497,"nova_asm_to":2563,"nova_bracket_tick":2634,"nova_char":2689,"nova_close_brace":3049,"nova_close_bracket":1998,"nova_colon":2146,"nova_comma":2133,"nova_compile_dotquote":2877,"nova_compile_opcode":2555,"nova_compile_open_brace":3024,"nova_compile_squote":2810,"nova_continue":2007,"nova_create":1946,"nova_dec":2660,"nova_dotquote":2856,"nova_emit":3007,"nova_exit":2099,"nova_here":2579,"nova_hex":2672,"nova_immediate":2105,"nova_immediate_open_brace":3012,"nova_literal":2654,"nova_number":1932,"nova_opcode":2550,"nova_opcode_for_word":2469,"nova_open_bracket":1990,"nova_peekr":2961,"nova_popr":2994,"nova_postpone":2017,"nova_print_stack":2907,"nova_pushr":2981,"nova_quote_string_to":2715,"nova_resolve":1568,"nova_rpick":2969,"nova_safe_opcode":2529,"nova_semicolon":2154,"nova_squote":2780,"nova_tick":2613,"nova_word":1852,"nova_word_to":1902,"nova_word_to_pad":2164,"open_paren":3250,"pad":4231,"parencomment":3211,"parse_hex_digit":1326,"pos_is_number":1234,"pos_is_number_bad":1271,"pos_is_number_done":1276,"pos_is_number_loop":1236,"print":1045,"print_number":1613,"print_stack_end":3426,"print_stack_start":3422,"push_jump":1552,"quit":3317,"quit_vector":4225,"r_stack":4490,"r_stack_ptr":4487,"skip_nonword":1388,"skip_word":1371,"stop":1024,"tick":1187,"unclosed_error":3372,"word_char":1081,"wordeq":1100}
+25
View File
@@ -0,0 +1,25 @@
: if $ brz >asm ; immediate
: then resolve ; immediate
: else r> $ jmpr >asm >r resolve ; immediate
: variable create 0 , does> ;
: arshift $ arshift asm ;
: lshift [ $ lshift asm ] ;
: rshift [ $ rshift asm ] ;
: u> [ $ gt ] asm ;
: u< [ $ lt ] asm ;
: rdrop r> pop ;
: over 1 pick ;
: nip swap pop ;
: -rot rot rot ;
: tuck dup -rot ;
: space 32 emit ;
: cr 13 emit 10 emit ;
: +! dup @ rot + swap ! ;
: 2dup 1 pick 1 pick ;
: allot here swap &heap +! ;
: negate -1 ^ 1 + ;
: free negate &heap +! here ;
: c+! dup c@ rot + swap c! ;
: ror dup 1 rshift swap 23 lshift | ;
: rol dup 23 rshift swap 1 lshift | ;
: abs dup 0 < if negate then ;
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "vweb"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
vcore = { path = "../vcore" }
wasm-bindgen = "0.2"
Executable
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash -e
wasm-pack build --target web
npm run bundle
cp pkg/vweb_bg.wasm src/index.html build
+9
View File
@@ -0,0 +1,9 @@
require('esbuild').build({
entryPoints: ['src/app.js'],
bundle: true,
outdir: 'build',
format: 'esm',
loader: {
'.wasm': 'file'
}
}).catch(() => process.exit(1))
+650
View File
@@ -0,0 +1,650 @@
{
"name": "vweb",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"esbuild": "^0.16.12",
"xterm": "^5.0.0",
"xterm-readline": "^1.1.1"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.12.tgz",
"integrity": "sha512-CTWgMJtpCyCltrvipZrrcjjRu+rzm6pf9V8muCsJqtKujR3kPmU4ffbckvugNNaRmhxAF1ZI3J+0FUIFLFg8KA==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.12.tgz",
"integrity": "sha512-0LacmiIW+X0/LOLMZqYtZ7d4uY9fxYABAYhSSOu+OGQVBqH4N5eIYgkT7bBFnR4Nm3qo6qS3RpHKVrDASqj/uQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.12.tgz",
"integrity": "sha512-sS5CR3XBKQXYpSGMM28VuiUnbX83Z+aWPZzClW+OB2JquKqxoiwdqucJ5qvXS8pM6Up3RtJfDnRQZkz3en2z5g==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.12.tgz",
"integrity": "sha512-Dpe5hOAQiQRH20YkFAg+wOpcd4PEuXud+aGgKBQa/VriPJA8zuVlgCOSTwna1CgYl05lf6o5els4dtuyk1qJxQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.12.tgz",
"integrity": "sha512-ApGRA6X5txIcxV0095X4e4KKv87HAEXfuDRcGTniDWUUN+qPia8sl/BqG/0IomytQWajnUn4C7TOwHduk/FXBQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.12.tgz",
"integrity": "sha512-AMdK2gA9EU83ccXCWS1B/KcWYZCj4P3vDofZZkl/F/sBv/fphi2oUqUTox/g5GMcIxk8CF1CVYTC82+iBSyiUg==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.12.tgz",
"integrity": "sha512-KUKB9w8G/xaAbD39t6gnRBuhQ8vIYYlxGT2I+mT6UGRnCGRr1+ePFIGBQmf5V16nxylgUuuWVW1zU2ktKkf6WQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.12.tgz",
"integrity": "sha512-vhDdIv6z4eL0FJyNVfdr3C/vdd/Wc6h1683GJsFoJzfKb92dU/v88FhWdigg0i6+3TsbSDeWbsPUXb4dif2abg==",
"cpu": [
"arm"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.12.tgz",
"integrity": "sha512-29HXMLpLklDfmw7T2buGqq3HImSUaZ1ArmrPOMaNiZZQptOSZs32SQtOHEl8xWX5vfdwZqrBfNf8Te4nArVzKQ==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.12.tgz",
"integrity": "sha512-JFDuNDTTfgD1LJg7wHA42o2uAO/9VzHYK0leAVnCQE/FdMB599YMH73ux+nS0xGr79pv/BK+hrmdRin3iLgQjg==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.12.tgz",
"integrity": "sha512-xTGzVPqm6WKfCC0iuj1fryIWr1NWEM8DMhAIo+4rFgUtwy/lfHl+Obvus4oddzRDbBetLLmojfVZGmt/g/g+Rw==",
"cpu": [
"loong64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.12.tgz",
"integrity": "sha512-zI1cNgHa3Gol+vPYjIYHzKhU6qMyOQrvZ82REr5Fv7rlh5PG6SkkuCoH7IryPqR+BK2c/7oISGsvPJPGnO2bHQ==",
"cpu": [
"mips64el"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.12.tgz",
"integrity": "sha512-/C8OFXExoMmvTDIOAM54AhtmmuDHKoedUd0Otpfw3+AuuVGemA1nQK99oN909uZbLEU6Bi+7JheFMG3xGfZluQ==",
"cpu": [
"ppc64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.12.tgz",
"integrity": "sha512-qeouyyc8kAGV6Ni6Isz8hUsKMr00EHgVwUKWNp1r4l88fHEoNTDB8mmestvykW6MrstoGI7g2EAsgr0nxmuGYg==",
"cpu": [
"riscv64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.12.tgz",
"integrity": "sha512-s9AyI/5vz1U4NNqnacEGFElqwnHusWa81pskAf8JNDM2eb6b2E6PpBmT8RzeZv6/TxE6/TADn2g9bb0jOUmXwQ==",
"cpu": [
"s390x"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.12.tgz",
"integrity": "sha512-e8YA7GQGLWhvakBecLptUiKxOk4E/EPtSckS1i0MGYctW8ouvNUoh7xnU15PGO2jz7BYl8q1R6g0gE5HFtzpqQ==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.12.tgz",
"integrity": "sha512-z2+kUxmOqBS+6SRVd57iOLIHE8oGOoEnGVAmwjm2aENSP35HPS+5cK+FL1l+rhrsJOFIPrNHqDUNechpuG96Sg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.12.tgz",
"integrity": "sha512-PAonw4LqIybwn2/vJujhbg1N9W2W8lw9RtXIvvZoyzoA/4rA4CpiuahVbASmQohiytRsixbNoIOUSjRygKXpyA==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.12.tgz",
"integrity": "sha512-+wr1tkt1RERi+Zi/iQtkzmMH4nS8+7UIRxjcyRz7lur84wCkAITT50Olq/HiT4JN2X2bjtlOV6vt7ptW5Gw60Q==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.12.tgz",
"integrity": "sha512-XEjeUSHmjsAOJk8+pXJu9pFY2O5KKQbHXZWQylJzQuIBeiGrpMeq9sTVrHefHxMOyxUgoKQTcaTS+VK/K5SviA==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.12.tgz",
"integrity": "sha512-eRKPM7e0IecUAUYr2alW7JGDejrFJXmpjt4MlfonmQ5Rz9HWpKFGCjuuIRgKO7W9C/CWVFXdJ2GjddsBXqQI4A==",
"cpu": [
"ia32"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.12.tgz",
"integrity": "sha512-iPYKN78t3op2+erv2frW568j1q0RpqX6JOLZ7oPPaAV1VaF7dDstOrNw37PVOYoTWE11pV4A1XUitpdEFNIsPg==",
"cpu": [
"x64"
],
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"engines": {
"node": ">=8"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/esbuild": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.12.tgz",
"integrity": "sha512-eq5KcuXajf2OmivCl4e89AD3j8fbV+UTE9vczEzq5haA07U9oOTzBWlh3+6ZdjJR7Rz2QfWZ2uxZyhZxBgJ4+g==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/android-arm": "0.16.12",
"@esbuild/android-arm64": "0.16.12",
"@esbuild/android-x64": "0.16.12",
"@esbuild/darwin-arm64": "0.16.12",
"@esbuild/darwin-x64": "0.16.12",
"@esbuild/freebsd-arm64": "0.16.12",
"@esbuild/freebsd-x64": "0.16.12",
"@esbuild/linux-arm": "0.16.12",
"@esbuild/linux-arm64": "0.16.12",
"@esbuild/linux-ia32": "0.16.12",
"@esbuild/linux-loong64": "0.16.12",
"@esbuild/linux-mips64el": "0.16.12",
"@esbuild/linux-ppc64": "0.16.12",
"@esbuild/linux-riscv64": "0.16.12",
"@esbuild/linux-s390x": "0.16.12",
"@esbuild/linux-x64": "0.16.12",
"@esbuild/netbsd-x64": "0.16.12",
"@esbuild/openbsd-x64": "0.16.12",
"@esbuild/sunos-x64": "0.16.12",
"@esbuild/win32-arm64": "0.16.12",
"@esbuild/win32-ia32": "0.16.12",
"@esbuild/win32-x64": "0.16.12"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/xterm": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.1.0.tgz",
"integrity": "sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ=="
},
"node_modules/xterm-readline": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/xterm-readline/-/xterm-readline-1.1.1.tgz",
"integrity": "sha512-f87S2/jKwRZoZTxE2vkPgBCipDl6k6tTkMTb9pmwC4R6XkfR491fWBuToZd/nZasp6seD2u0jdABinUDWsK6dw==",
"dependencies": {
"string-width": "^4.0.0"
},
"peerDependencies": {
"xterm": "^5.0.0"
}
}
},
"dependencies": {
"@esbuild/android-arm": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.16.12.tgz",
"integrity": "sha512-CTWgMJtpCyCltrvipZrrcjjRu+rzm6pf9V8muCsJqtKujR3kPmU4ffbckvugNNaRmhxAF1ZI3J+0FUIFLFg8KA==",
"optional": true
},
"@esbuild/android-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.16.12.tgz",
"integrity": "sha512-0LacmiIW+X0/LOLMZqYtZ7d4uY9fxYABAYhSSOu+OGQVBqH4N5eIYgkT7bBFnR4Nm3qo6qS3RpHKVrDASqj/uQ==",
"optional": true
},
"@esbuild/android-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.16.12.tgz",
"integrity": "sha512-sS5CR3XBKQXYpSGMM28VuiUnbX83Z+aWPZzClW+OB2JquKqxoiwdqucJ5qvXS8pM6Up3RtJfDnRQZkz3en2z5g==",
"optional": true
},
"@esbuild/darwin-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.16.12.tgz",
"integrity": "sha512-Dpe5hOAQiQRH20YkFAg+wOpcd4PEuXud+aGgKBQa/VriPJA8zuVlgCOSTwna1CgYl05lf6o5els4dtuyk1qJxQ==",
"optional": true
},
"@esbuild/darwin-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.16.12.tgz",
"integrity": "sha512-ApGRA6X5txIcxV0095X4e4KKv87HAEXfuDRcGTniDWUUN+qPia8sl/BqG/0IomytQWajnUn4C7TOwHduk/FXBQ==",
"optional": true
},
"@esbuild/freebsd-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.12.tgz",
"integrity": "sha512-AMdK2gA9EU83ccXCWS1B/KcWYZCj4P3vDofZZkl/F/sBv/fphi2oUqUTox/g5GMcIxk8CF1CVYTC82+iBSyiUg==",
"optional": true
},
"@esbuild/freebsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.16.12.tgz",
"integrity": "sha512-KUKB9w8G/xaAbD39t6gnRBuhQ8vIYYlxGT2I+mT6UGRnCGRr1+ePFIGBQmf5V16nxylgUuuWVW1zU2ktKkf6WQ==",
"optional": true
},
"@esbuild/linux-arm": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.16.12.tgz",
"integrity": "sha512-vhDdIv6z4eL0FJyNVfdr3C/vdd/Wc6h1683GJsFoJzfKb92dU/v88FhWdigg0i6+3TsbSDeWbsPUXb4dif2abg==",
"optional": true
},
"@esbuild/linux-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.16.12.tgz",
"integrity": "sha512-29HXMLpLklDfmw7T2buGqq3HImSUaZ1ArmrPOMaNiZZQptOSZs32SQtOHEl8xWX5vfdwZqrBfNf8Te4nArVzKQ==",
"optional": true
},
"@esbuild/linux-ia32": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.16.12.tgz",
"integrity": "sha512-JFDuNDTTfgD1LJg7wHA42o2uAO/9VzHYK0leAVnCQE/FdMB599YMH73ux+nS0xGr79pv/BK+hrmdRin3iLgQjg==",
"optional": true
},
"@esbuild/linux-loong64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.16.12.tgz",
"integrity": "sha512-xTGzVPqm6WKfCC0iuj1fryIWr1NWEM8DMhAIo+4rFgUtwy/lfHl+Obvus4oddzRDbBetLLmojfVZGmt/g/g+Rw==",
"optional": true
},
"@esbuild/linux-mips64el": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.16.12.tgz",
"integrity": "sha512-zI1cNgHa3Gol+vPYjIYHzKhU6qMyOQrvZ82REr5Fv7rlh5PG6SkkuCoH7IryPqR+BK2c/7oISGsvPJPGnO2bHQ==",
"optional": true
},
"@esbuild/linux-ppc64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.16.12.tgz",
"integrity": "sha512-/C8OFXExoMmvTDIOAM54AhtmmuDHKoedUd0Otpfw3+AuuVGemA1nQK99oN909uZbLEU6Bi+7JheFMG3xGfZluQ==",
"optional": true
},
"@esbuild/linux-riscv64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.16.12.tgz",
"integrity": "sha512-qeouyyc8kAGV6Ni6Isz8hUsKMr00EHgVwUKWNp1r4l88fHEoNTDB8mmestvykW6MrstoGI7g2EAsgr0nxmuGYg==",
"optional": true
},
"@esbuild/linux-s390x": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.16.12.tgz",
"integrity": "sha512-s9AyI/5vz1U4NNqnacEGFElqwnHusWa81pskAf8JNDM2eb6b2E6PpBmT8RzeZv6/TxE6/TADn2g9bb0jOUmXwQ==",
"optional": true
},
"@esbuild/linux-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.16.12.tgz",
"integrity": "sha512-e8YA7GQGLWhvakBecLptUiKxOk4E/EPtSckS1i0MGYctW8ouvNUoh7xnU15PGO2jz7BYl8q1R6g0gE5HFtzpqQ==",
"optional": true
},
"@esbuild/netbsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.16.12.tgz",
"integrity": "sha512-z2+kUxmOqBS+6SRVd57iOLIHE8oGOoEnGVAmwjm2aENSP35HPS+5cK+FL1l+rhrsJOFIPrNHqDUNechpuG96Sg==",
"optional": true
},
"@esbuild/openbsd-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.16.12.tgz",
"integrity": "sha512-PAonw4LqIybwn2/vJujhbg1N9W2W8lw9RtXIvvZoyzoA/4rA4CpiuahVbASmQohiytRsixbNoIOUSjRygKXpyA==",
"optional": true
},
"@esbuild/sunos-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.16.12.tgz",
"integrity": "sha512-+wr1tkt1RERi+Zi/iQtkzmMH4nS8+7UIRxjcyRz7lur84wCkAITT50Olq/HiT4JN2X2bjtlOV6vt7ptW5Gw60Q==",
"optional": true
},
"@esbuild/win32-arm64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.16.12.tgz",
"integrity": "sha512-XEjeUSHmjsAOJk8+pXJu9pFY2O5KKQbHXZWQylJzQuIBeiGrpMeq9sTVrHefHxMOyxUgoKQTcaTS+VK/K5SviA==",
"optional": true
},
"@esbuild/win32-ia32": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.16.12.tgz",
"integrity": "sha512-eRKPM7e0IecUAUYr2alW7JGDejrFJXmpjt4MlfonmQ5Rz9HWpKFGCjuuIRgKO7W9C/CWVFXdJ2GjddsBXqQI4A==",
"optional": true
},
"@esbuild/win32-x64": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.16.12.tgz",
"integrity": "sha512-iPYKN78t3op2+erv2frW568j1q0RpqX6JOLZ7oPPaAV1VaF7dDstOrNw37PVOYoTWE11pV4A1XUitpdEFNIsPg==",
"optional": true
},
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"esbuild": {
"version": "0.16.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.16.12.tgz",
"integrity": "sha512-eq5KcuXajf2OmivCl4e89AD3j8fbV+UTE9vczEzq5haA07U9oOTzBWlh3+6ZdjJR7Rz2QfWZ2uxZyhZxBgJ4+g==",
"requires": {
"@esbuild/android-arm": "0.16.12",
"@esbuild/android-arm64": "0.16.12",
"@esbuild/android-x64": "0.16.12",
"@esbuild/darwin-arm64": "0.16.12",
"@esbuild/darwin-x64": "0.16.12",
"@esbuild/freebsd-arm64": "0.16.12",
"@esbuild/freebsd-x64": "0.16.12",
"@esbuild/linux-arm": "0.16.12",
"@esbuild/linux-arm64": "0.16.12",
"@esbuild/linux-ia32": "0.16.12",
"@esbuild/linux-loong64": "0.16.12",
"@esbuild/linux-mips64el": "0.16.12",
"@esbuild/linux-ppc64": "0.16.12",
"@esbuild/linux-riscv64": "0.16.12",
"@esbuild/linux-s390x": "0.16.12",
"@esbuild/linux-x64": "0.16.12",
"@esbuild/netbsd-x64": "0.16.12",
"@esbuild/openbsd-x64": "0.16.12",
"@esbuild/sunos-x64": "0.16.12",
"@esbuild/win32-arm64": "0.16.12",
"@esbuild/win32-ia32": "0.16.12",
"@esbuild/win32-x64": "0.16.12"
}
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"requires": {
"ansi-regex": "^5.0.1"
}
},
"xterm": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.1.0.tgz",
"integrity": "sha512-LovENH4WDzpwynj+OTkLyZgJPeDom9Gra4DMlGAgz6pZhIDCQ+YuO7yfwanY+gVbn/mmZIStNOnVRU/ikQuAEQ=="
},
"xterm-readline": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/xterm-readline/-/xterm-readline-1.1.1.tgz",
"integrity": "sha512-f87S2/jKwRZoZTxE2vkPgBCipDl6k6tTkMTb9pmwC4R6XkfR491fWBuToZd/nZasp6seD2u0jdABinUDWsK6dw==",
"requires": {
"string-width": "^4.0.0"
}
}
}
}
+10
View File
@@ -0,0 +1,10 @@
{
"dependencies": {
"esbuild": "^0.16.12",
"xterm": "^5.0.0",
"xterm-readline": "^1.1.1"
},
"scripts": {
"bundle": "node esbuild.js"
}
}
+34
View File
@@ -0,0 +1,34 @@
body {
background-color: #333333;
color: #dddddd;
font-family: sans-serif;
}
.content {
display: grid;
grid-template-columns: 1fr 8fr 1fr;
grid-template-rows: auto auto;
}
.terminal {
grid-area: 1 / 2 / 1 / 2;
border: 2px solid #777777;
}
.instructions {
grid-area: 2 / 2 / 2 / 2;
}
a {
color: #ffffff;
}
pre {
background: #0f0f0f;
}
td.word, td.signature {
font-family: monospace;
font-weight: bold;
padding-right: 1em;
}
+72
View File
@@ -0,0 +1,72 @@
import 'xterm/css/xterm.css'
import './app.css'
import { Terminal } from 'xterm'
import { Readline } from 'xterm-readline'
import init, { WasmCPU, NovaForth } from '../pkg/vweb.js'
const term = new Terminal()
const rl = new Readline()
const TIB = 80000 // Just a convenient place to stick a terminal input buffer for the repl. Could be any otherwise-unused address.
const OUT = 0x10000 // A place that NovaForth will use for its output buffer
let syms
term.loadAddon(rl)
term.open(document.getElementsByClassName('terminal')[0])
term.write('Loading...\r\n')
const handleKey = ({key, domEvent: { key: mnemonic }}) => {
if (mnemonic === 'Enter') {
console.log(mnemonic)
} else {
term.write(key)
}
}
init().then(async () => {
const cpu = new WasmCPU()
syms = JSON.parse(NovaForth.symbols()) // Grab the symbol table for the ROM so we can poke things by name
NovaForth.rom().forEach((byte, idx) => {
cpu.poke(0x400 + idx, byte)
})
term.write(`Loaded ${NovaForth.rom().length} bytes\r\n`)
NovaForth.prelude().split(/\n/).forEach((line) => {
const result = eval4th(cpu, line) // If there's anything wrong with the prelude, say so
if (result !== '') {
console.log(`Error with "${line}": ${result}`)
}
})
term.write(`Loaded NovaForth Prelude\r\n`)
document.getElementsByClassName('prelude')[0].appendChild(document.createTextNode(NovaForth.prelude()))
term.focus()
while (true) {
const line = await rl.read('> ') // Read the line of input
const str = eval4th(cpu, line) // Run it on the CPU
rl.println(str) // Println the result
}
})
function eval4th(cpu, line) {
// Copy the line to TIB and null-terminate it
for (let i = 0; i < line.length; i++) {
cpu.poke(TIB + i, line.charCodeAt(i))
}
cpu.poke(TIB + line.length, 0)
cpu.push_call(syms.stop) // Tell the CPU to stop once we're done
cpu.push_data(TIB) // Push the TIB and call eval
cpu.set_pc(syms.eval)
cpu.run() // Run it!
const len = cpu.peek24(syms.emit_cursor) // Find the length of the output
let str = '' // Copy the output from the memory to a JS string
for (let i = OUT; i < OUT + len; i++) {
str = str + String.fromCharCode(cpu.peek(i))
}
cpu.poke24(syms.emit_cursor, 0) // Re-clear the output buffer
return str
}
+187
View File
@@ -0,0 +1,187 @@
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="app.css"/>
<title>NovaForth REPL</title>
<script type="module" src="app.js"></script>
</head>
<body>
<div class="content">
<div class="terminal"></div>
<div class="instructions">
<h2>NovaForth Console</h2>
<p>
This is a REPL for NovaForth, using the Vulcan imaginary CPU.
You can type lines of Forth code and have them evaluated, and see the results.
</p>
<p>
For a full introduction to NovaForth, read
<a href="https://docs.google.com/document/d/12juFyAGWC9TL2qB97Ud3VehlVdge-zpEjy7FSM1BWF4/edit?usp=sharing">
this sample chapter
</a>
from the upcoming Vulcan book. A short tutorial is below:
</p>
<h3>Tutorial and examples:</h3>
<p>
Forth programs are a series of "words" separated by whitespace. NovaForth evaluates words line-by-line, and a
full dictionary of available words is listed below. Here are some example programs:
</p>
<p>
<pre>5 3 + .</pre>
This program pushes the numbers "5" and "3" to the stack, then adds the top two numbers on the stack, and
finally pops the top number from the stack (now "8") and prints it.
</p>
<p>
<pre>." Hello, world!"</pre>
This program prints a standard greeting.
</p>
<p>
<pre>: square dup * ;
4 square .</pre>
This defines a new word, "square", which multiplies the top value on the stack by itself. The second line then
calls that word on the number "4" and prints the result.
</p>
<p>
<pre>: collatz
dup 2 % if
3 * 1 +
else
2 /
then ;
4 collatz 5 collatz .s</pre>
This defines a word "collatz" which showcases conditionals: if the number given to it is odd, it returns triple
that number plus one; if it's even, it returns half that number. The ".s" word prints the stack without modifying
it and is a useful debugging tool.
</p>
<h3>NovaForth Dictionary</h3>
<h4>Arithmetic and stack juggling</h4>
<table>
<tr><td class="word">+</td><td class="signature">a b -- c</td><td class="meaning">Addition</td></tr>
<tr><td class="word">-</td><td class="signature">a b -- c</td><td class="meaning">Subtraction</td></tr>
<tr><td class="word">*</td><td class="signature">a b -- c</td><td class="meaning">Multiplication</td></tr>
<tr><td class="word">/</td><td class="signature">a b -- c</td><td class="meaning">Division (truncates to integer)</td></tr>
<tr><td class="word">%</td><td class="signature">a b -- c</td><td class="meaning">Modulus</td></tr>
<tr><td class="word">pop</td><td class="signature">a --</td><td class="meaning">Throw away the top element of the stack</td></tr>
<tr><td class="word">dup</td><td class="signature">a -- a a</td><td class="meaning">Duplicate the top element of the stack</td></tr>
<tr><td class="word">swap</td><td class="signature">a b -- b a</td><td class="meaning">Swap the top two elements of the stack</td></tr>
<tr>
<td class="word">pick</td>
<td class="signature">* index -- * el</td>
<td class="meaning">Copy an element from deep in the stack to the top. "0 pick" is equivalent to dup,
"1 pick" pushes the value one below the top, and so on</td></tr>
<tr>
<td class="word">rot</td>
<td class="signature">a b c -- b c a</td>
<td class="meaning">Rotate the top three elements on the stack</td>
</tr>
<tr>
<td class="word">-rot</td>
<td class="signature">a b c -- c a b</td>
<td class="meaning">Rotate the top three elements on the stack the other direction (prelude word)</td>
</tr>
<tr><td class="word">?dup</td><td class="signature">a -- a a OR 0 -- 0</td><td class="meaning">Duplicates the top of stack if it's nonzero</td></tr>
<tr><td class="word">&gt;r</td><td class="signature">a --</td><td class="meaning">Pushes the top of the stack to the R stack</td></tr>
<tr><td class="word">r&gt;</td><td class="signature">-- a</td><td class="meaning">Pushes the top of the R stack to the stack</td></tr>
<tr><td class="word">r@</td><td class="signature">-- a</td><td class="meaning">Copies the top of the R stack to the stack, leaving the R stack unchanged</td></tr>
<tr><td class="word">rpick</td><td class="signature">index -- a</td><td class="meaning">Picks a value from deep in the R stack, much like "pick" does with the data stack</td></tr>
<tr><td class="word">rdrop</td><td class="signature">--</td><td class="meaning">Drop the top value from the R stack (prelude word)</td></tr>
<tr><td class="word">lshift</td><td class="signature">a b -- c</td><td class="meaning">Binary-shifts "a" left "b" bits, shifting in zeroes (prelude word)</td></tr>
<tr><td class="word">rshift</td><td class="signature">a b -- c</td><td class="meaning">Binary-shifts "a" right "b" bits, shifting in zeroes (prelude word)</td></tr>
<tr><td class="word">arshift</td><td class="signature">a b -- c</td><td class="meaning">Arithmetic right shift: right-shift leaving the high order bit untouched (to preserve sign) (prelude word)</td></tr>
<tr><td class="word">ror</td><td class="signature">a -- b</td><td class="meaning">Roll one bit right, moving the low bit to the high bit (prelude word)</td></tr>
<tr><td class="word">rol</td><td class="signature">a -- b</td><td class="meaning">Roll one bit left, moving the high bit to the low bit (prelude word)</td></tr>
<tr><td class="word">over</td><td class="signature">a b -- a b a</td><td class="meaning">Duplicate the value one below top (prelude word)</td></tr>
<tr><td class="word">nip</td><td class="signature">a b -- b</td><td class="meaning">Drop the value one below top (prelude word)</td></tr>
<tr><td class="word">tuck</td><td class="signature">a b -- b a b</td><td class="meaning">Duplicate the top value, placing it underneath the one below top (prelude word)</td></tr>
<tr><td class="word">2dup</td><td class="signature">a b -- a b a b</td><td class="meaning">Duplicate the top two values (prelude word)</td></tr>
<tr><td class="word">negate</td><td class="signature">a -- b</td><td class="meaning">Multiply the top of stack by -1 (prelude word)</td></tr>
<tr><td class="word">abs</td><td class="signature">a -- b</td><td class="meaning">Return the absolute value of the top of stack (prelude word)</td></tr>
</table>
<h4>I/O</h4>
<table>
<tr><td class="word">word <em>word</em></td><td class="signature">-- addr</td><td class="meaning">Copies <em>word</em> to the pad, leaving its address on the stack</td></tr>
<tr><td class="word">pad</td><td class="signature">-- addr</td><td class="meaning">Pushes the address of the pad to the stack</td></tr>
<tr><td class="word">number <em>str</em></td><td class="signature">-- num 1 OR -- 0</td><td class="meaning">Tries to parse the given word as a number, pushes the number followed by 1 if it's parseable and 0 if it's not</td></tr>
<tr><td class="word">hex</td><td class="signature">--</td><td class="meaning">Switches all I/O to hexadecimal mode</td></tr>
<tr><td class="word">dec</td><td class="signature">--</td><td class="meaning">Switches all I/O to base-10 mode (the default)</td></tr>
<tr><td class="word">.</td><td class="signature">a --</td><td class="meaning">Prints the number on the top of the stack</td></tr>
<tr><td class="word">s"</td><td class="signature">-- addr</td><td class="meaning">Reads a string until a double-quote, copies them to the heap, and returns the address of the string</td></tr>
<tr><td class="word">."</td><td class="signature">--</td><td class="meaning">Reads a string until a double-quote and prints it</td></tr>
<tr><td class="word">emit</td><td class="signature">ch --</td><td class="meaning">Emits a byte (as ASCII) to the console</td></tr>
<tr><td class="word">print</td><td class="signature">addr --</td><td class="meaning">Prints a null-terminated string to the console</td></tr>
<tr><td class="word">.s</td><td class="signature">--</td><td class="meaning">Prints the stack contents as a debugging aid</td></tr>
<tr><td class="word">space</td><td class="signature">--</td><td class="meaning">Prints a space (prelude word)</td></tr>
<tr><td class="word">cr</td><td class="signature">--</td><td class="meaning">Prints a newline (10 followed by 13, carriage return and line feed) (prelude word)</td></tr>
</table>
<h4>Logic and control structures</h4>
<table>
<tr><td class="word">&gt;</td><td class="signature">a b -- c</td><td class="meaning">Returns a 1 if a is greater than b, 0 otherwise (signed comparison)</td></tr>
<tr><td class="word">&lt;</td><td class="signature">a b -- c</td><td class="meaning">Returns a 1 if a is less than b, 0 otherwise (signed comparison)</td></tr>
<tr><td class="word">u&gt;</td><td class="signature">a b -- c</td><td class="meaning">Returns a 1 if a is greater than b, 0 otherwise (unsigned comparison) (prelude word)</td></tr>
<tr><td class="word">u&lt;</td><td class="signature">a b -- c</td><td class="meaning">Returns a 1 if a is less than b, 0 otherwise (unsigned comparison) (prelude word)</td></tr>
<tr><td class="word">=</td><td class="signature">a b -- c</td><td class="meaning">Returns a 1 if a is equal to b, 0 otherwise</td></tr>
<tr><td class="word">&amp;</td><td class="signature">a b -- c</td><td class="meaning">Bitwise-and of a and b</td></tr>
<tr><td class="word">|</td><td class="signature">a b -- c</td><td class="meaning">Bitwise-or of a and b</td></tr>
<tr><td class="word">^</td><td class="signature">a b -- c</td><td class="meaning">Bitwise-xor of a and b</td></tr>
<tr><td class="word">not</td><td class="signature">a -- b</td><td class="meaning">If the argument is a 0, returns 1. Otherwise returns 0</td></tr>
<tr><td class="word">compare</td><td class="signature">a b -- c</td><td class="meaning">Compares two strings for equality, returns 1 if they're equal and 0 otherwise</td></tr>
<tr><td class="word">if / else / then</td><td class="signature">cond --</td><td class="meaning">A conditional branch: "<em>condition</em> if <em>truepath</em> else <em>falsepath</em> then". The "else" branch is optional (prelude words)</td></tr>
</table>
<h4>Memory management</h4>
<table>
<tr><td class="word">@</td><td class="signature">addr -- val</td><td class="meaning">Read the 3-byte word at the given address</td></tr>
<tr><td class="word">!</td><td class="signature">val addr --</td><td class="meaning">Write a 3-byte word to the given address</td></tr>
<tr><td class="word">c@</td><td class="signature">addr -- val</td><td class="meaning">Read one byte from the given address</td></tr>
<tr><td class="word">c!</td><td class="signature">val addr --</td><td class="meaning">Write one byte to the given address</td></tr>
<tr><td class="word">&amp;heap</td><td class="signature">-- addr</td><td class="meaning">Pushes the address of the heap pointer</td></tr>
<tr><td class="word">here</td><td class="signature">-- addr</td><td class="meaning">Pushes the current value of the heap pointer (equivalent to "&amp;heap @")</td></tr>
<tr><td class="word">variable <em>name</em></td><td class="signature">--</td><td class="meaning">Creates a variable of the given name, which pushes its address when called (prelude word)</td></tr>
<tr><td class="word">+!</td><td class="signature">val addr --</td><td class="meaning">Increments the 3-byte word at "addr" by "val" (prelude word)</td></tr>
<tr><td class="word">c+!</td><td class="signature">val addr --</td><td class="meaning">Increments the byte at "addr" by "val" (prelude word)</td></tr>
<tr><td class="word">allot</td><td class="signature">val -- addr</td><td class="meaning">Increment the heap by "val" bytes, returning the start address of the allotted region (prelude word)</td></tr>
<tr><td class="word">free</td><td class="signature">val -- addr</td><td class="meaning">Decrement the heap by "val" bytes, returning the new heap address (prelude word)</td></tr>
</table>
<h4>Assembly and compilation</h4>
<table>
<tr><td class="word">execute</td><td class="signature">addr --</td><td class="meaning">Calls the given address</td></tr>
<tr><td class="word">[</td><td class="signature">--</td><td class="meaning">Enters interpret mode</td></tr>
<tr><td class="word">]</td><td class="signature">--</td><td class="meaning">Enters compile mode</td></tr>
<tr><td class="word">,</td><td class="signature">a --</td><td class="meaning">Compiles a number to the heap</td></tr>
<tr><td class="word">:</td><td class="signature">--</td><td class="meaning">Begins definition of a new word: calls create and enters compile mode</td></tr>
<tr><td class="word">;</td><td class="signature">--</td><td class="meaning">Ends a word definition: compiles a return instruction and leaves compile mode</td></tr>
<tr><td class="word">$ <em>mnemonic</em></td><td class="signature">-- a</td><td class="meaning">Pushes the equivalent opcode to the given instruction mnemonic</td></tr>
<tr><td class="word">asm</td><td class="signature">op --</td><td class="meaning">Compiles an instruction of the given opcode (with no argument)</td></tr>
<tr><td class="word">#asm</td><td class="signature">op arg --</td><td class="meaning">Compiles an instruction of the given opcode with an argument</td></tr>
<tr><td class="word">&gt;asm</td><td class="signature">op --</td><td class="meaning">Compiles an instruction of the given opcode with a blank argument, and stores the argument's address to the R stack</td></tr>
<tr><td class="word">resolve</td><td class="signature">addr --</td><td class="meaning">Writes the top of the stack to the address on top of the R stack (used to resolve a blank argument left by &gt;asm)</td></tr>
<tr><td class="word">{</td><td class="signature">--</td><td class="meaning">Begins an anonymous function</td></tr>
<tr><td class="word">}</td><td class="signature">-- addr</td><td class="meaning">Ends an anonymous function and leaves its address on the stack</td></tr>
<tr><td class="word">exit</td><td class="signature">--</td><td class="meaning">Early return from a word definition</td></tr>
<tr><td class="word">continue <em>word</em></td><td class="signature">--</td><td class="meaning">Jumps to the given word as a tail call (replacing the current stack frame)</td></tr>
<tr><td class="word">postpone <em>word</em></td><td class="signature">--</td><td class="meaning">Delays execution of the given word: compiles a call to a compile-mode word or compiles instructions to compile a call to an interpret-mode word (see the NovaForth guide)</td></tr>
<tr><td class="word">literal</td><td class="signature">-- addr</td><td class="meaning">Compiles an instruction to push the number at the top of the stack</td></tr>
</table>
<h4>Dictionary manipulation</h4>
<table>
<tr><td class="word">create <em>name</em></td><td class="signature">--</td><td class="meaning">Creates a dictionary entry for a given word (pointing to the heap)</td></tr>
<tr><td class="word">immediate</td><td class="signature">--</td><td class="meaning">Moves the most recently defined word from the interpret dictionary to the compile dictionary</td></tr>
<tr><td class="word">does&gt;</td><td class="signature">-- addr</td><td class="meaning">Defines the runtime behavior of a defining word (see the NovaForth guide)</td></tr>
<tr><td class="word">' <em>word</em></td><td class="signature">-- addr</td><td class="meaning">Consumes a word, looks it up in the dictionary, and returns its address</td></tr>
<tr><td class="word">[']</td><td class="signature">-- addr</td><td class="meaning">Consumes a word at runtime, looks it up in the dictionary, and returns its address</td></tr>
</table>
<h4>Miscellaneous</h4>
<table>
<tr><td class="word">\</td><td class="signature">--</td><td class="meaning">Begins a comment that lasts to the end of the line</td></tr>
<tr><td class="word">(</td><td class="signature">--</td><td class="meaning">Begins a comment that lasts to the matching close paren</td></tr>
</table>
<h4>Prelude</h4>
<p>These definitions are run when the console is loaded:</p>
<pre class="prelude"></pre>
<hr/>
<p>This early-access demo was created by <a href="mailto:ross.andrews@gmail.com">Ross Andrews</a> in January 2023</p>
</div>
</div>
</body>
</html>
+89
View File
@@ -0,0 +1,89 @@
use wasm_bindgen::prelude::*;
use vcore::{CPU, Word};
use vcore::memory::{PeekPoke, PeekPokeExt};
#[wasm_bindgen]
pub struct NovaForth;
#[wasm_bindgen]
impl NovaForth {
pub fn rom() -> Vec<u8> {
Vec::from(*include_bytes!("../4th/4th.rom"))
}
pub fn symbols() -> String {
include_str!("../4th/4th.rom.sym").into()
}
pub fn prelude() -> String { include_str!("../4th/prelude.f").into() }
}
#[wasm_bindgen]
pub struct WasmCPU(CPU);
#[wasm_bindgen]
impl WasmCPU {
#[wasm_bindgen(constructor)]
pub fn new() -> WasmCPU {
WasmCPU(CPU::new_random())
}
pub fn push_data(&mut self, data: i32) {
self.0.push_data(data)
}
pub fn pop_data(&mut self) -> i32 {
self.0.pop_data().into()
}
pub fn push_call(&mut self, data: i32) {
self.0.push_call(data)
}
pub fn pop_call(&mut self) -> i32 {
self.0.pop_call().into()
}
pub fn poke(&mut self, addr: u32, val: u8) {
self.0.poke(addr.into(), val.into())
}
pub fn peek(&self, addr: u32) -> u8 {
self.0.peek(addr.into()).into()
}
pub fn poke24(&mut self, addr: u32, val: i32) {
self.0.poke24(Word::from(addr), Word::from(val))
}
pub fn peek24(&self, addr: u32) -> i32 {
self.0.peek24(Word::from(addr)).into()
}
pub fn pc(&self) -> u32 {
self.0.pc().into()
}
pub fn sp(&self) -> u32 {
self.0.sp().into()
}
pub fn dp(&self) -> u32 {
self.0.dp().into()
}
pub fn set_pc(&mut self, val: u32) {
self.0.set_pc(Word::from(val))
}
pub fn run(&mut self) {
self.0.run_to_halt()
}
pub fn load(&mut self, rom: Vec<u8>) {
for (i, b) in rom.iter().enumerate() {
self.poke(0x400 + i as u32, *b)
}
self.set_pc(0x400)
}
}