Created
November 21, 2023 16:22
-
-
Save bryanburgers/a2837026fff8d32de5131f488336f0c1 to your computer and use it in GitHub Desktop.
Companion code to https://burgers.io/complete-novice-wasm-parsing-numbers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
;; Create a function that returns a single value | |
(func $main (result i32) | |
i32.const 42 | |
) | |
;; Export that function so the test harness can find it | |
(export "main" (func $main)) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
;; Create a function that returns a single value | |
(func $main (result i32) | |
i32.const 42 | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32) | |
i32.const 4 | |
i32.load8_u | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32) | |
i32.const 4 | |
call $load_from_memory | |
) | |
(func $load_from_memory (param $address i32) (result i32) | |
local.get $address ;; Push the $address parameter onto the stack | |
i32.load8_u ;; and use that to load from memory | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32) | |
i32.const 0 | |
call $parse_digit | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; push on a value that represents ascii "0" | |
i32.const 0x30 | |
;; and subtract them! [i32, i32] -> [i32] | |
i32.sub | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_digit ;; expecting 5 | |
i32.const 3 | |
call $parse_digit ;; expecting 0, the second digit of "10" | |
i32.const 15 | |
call $parse_digit ;; expecting 7, the fourth digit of "1007..." | |
i32.const 4 | |
call $parse_digit ;; what do we expect? | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; push on a value that represents ascii "0" | |
i32.const 0x30 | |
;; and subtract them! [i32, i32] -> [i32] | |
i32.sub | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_digit ;; expecting 5 | |
i32.const 3 | |
call $parse_digit ;; expecting 0, the second digit of "10" | |
i32.const 15 | |
call $parse_digit ;; expecting 7, the fourth digit of "1007..." | |
i32.const 4 | |
call $parse_digit ;; what do we expect? | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_digit ;; expecting 5 | |
i32.const 3 | |
call $parse_digit ;; expecting 0, the second digit of "10" | |
i32.const 15 | |
call $parse_digit ;; expecting 7, the fourth digit of "1007..." | |
i32.const 4 | |
call $parse_digit ;; what do we expect? | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
(local $result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
;; if what's on the stack... | |
(if | |
;; ... is true, then ... | |
(then | |
;; set the result to what we parsed | |
local.get $parsed | |
local.set $result | |
) | |
;; ... otherwise ... | |
(else | |
;; set the result to a constant | |
i32.const -1 | |
local.set $result | |
) | |
) | |
;; and return the result | |
local.get $result | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32 i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_digit ;; expecting 5 | |
i32.const 3 | |
call $parse_digit ;; expecting 0, the second digit of "10" | |
i32.const 15 | |
call $parse_digit ;; expecting 7, the fourth digit of "1007..." | |
i32.const 4 | |
call $parse_digit ;; what do we expect? | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result (; the value ;) i32 (; correctly parsed ;) i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
(local $success i32) | |
(local $result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
local.tee $success ;; store the value *and* leave it on the stack | |
;; if what's on the stack... | |
(if | |
;; ... is true, then ... | |
(then | |
;; set the result to what we parsed | |
local.get $parsed | |
local.set $result | |
) | |
;; ... otherwise ... | |
(else | |
;; set the result to a constant | |
i32.const -1 | |
local.set $result | |
) | |
) | |
;; and return the results | |
local.get $result | |
local.get $success | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32 i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_number ;; expecting (5, 1) | |
i32.const 2 | |
call $parse_number ;; expecting (10, 2) | |
i32.const 12 | |
call $parse_number ;; expecting (1007000000, 10) | |
i32.const 4 | |
call $parse_number ;; expecting (0, 0) | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result (; the value ;) i32 (; correctly parsed ;) i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
(local $success i32) | |
(local $result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
local.tee $success ;; store the value *and* leave it on the stack | |
;; if what's on the stack... | |
(if | |
;; ... is true, then ... | |
(then | |
;; set the result to what we parsed | |
local.get $parsed | |
local.set $result | |
) | |
;; ... otherwise ... | |
(else | |
;; set the result to a constant | |
i32.const -1 | |
local.set $result | |
) | |
) | |
;; and return the results | |
local.get $result | |
local.get $success | |
) | |
(func $parse_number (param $address i32) (result (; number ;) i32 (; bytes parsed ;) i32) | |
(local $bytes_parsed i32) | |
(local $current_address i32) | |
(local $parse_digit_success i32) | |
(local $parse_digit_value i32) | |
(local $number i32) | |
;; initialize $number to 0 | |
i32.const 0 | |
local.set $number | |
;; initialize $bytes_parsed to 0 | |
i32.const 0 | |
local.set $bytes_parsed | |
;; initialize $current_address to the input parameter $address | |
local.get $address | |
local.set $current_address | |
(block $outer | |
(loop $inner | |
local.get $current_address | |
call $parse_digit | |
local.set $parse_digit_success | |
local.set $parse_digit_value | |
local.get $parse_digit_success | |
(if | |
(then | |
;; increment the number of bytes parsed | |
local.get $bytes_parsed | |
i32.const 1 | |
i32.add | |
local.set $bytes_parsed | |
;; $number = $number * 10 + $parse_digit_value | |
local.get $number | |
i32.const 10 | |
i32.mul | |
local.get $parse_digit_value | |
i32.add | |
local.set $number | |
) | |
(else | |
;; break; exit the loop | |
br $outer | |
) | |
) | |
;; increment the $current_address | |
local.get $current_address | |
i32.const 1 | |
i32.add | |
local.set $current_address | |
;; and parse the next digit | |
br $inner | |
) | |
) | |
local.get $number | |
local.get $bytes_parsed | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32 i32 i32 i32) | |
i32.const 0 | |
call $parse_space | |
i32.const 4 | |
call $parse_space | |
i32.const 22 | |
call $parse_space | |
i32.const 23 | |
call $parse_space | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result (; the value ;) i32 (; correctly parsed ;) i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
(local $success i32) | |
(local $result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
local.tee $success ;; store the value *and* leave it on the stack | |
;; if what's on the stack... | |
(if | |
;; ... is true, then ... | |
(then | |
;; set the result to what we parsed | |
local.get $parsed | |
local.set $result | |
) | |
;; ... otherwise ... | |
(else | |
;; set the result to a constant | |
i32.const -1 | |
local.set $result | |
) | |
) | |
;; and return the results | |
local.get $result | |
local.get $success | |
) | |
(func $parse_number (param $address i32) (result (; number ;) i32 (; bytes parsed ;) i32) | |
(local $bytes_parsed i32) | |
(local $current_address i32) | |
(local $parse_digit_success i32) | |
(local $parse_digit_value i32) | |
(local $number i32) | |
;; initialize $number to 0 | |
i32.const 0 | |
local.set $number | |
;; initialize $bytes_parsed to 0 | |
i32.const 0 | |
local.set $bytes_parsed | |
;; initialize $current_address to $address | |
local.get $address | |
local.set $current_address | |
(block $outer | |
(loop $inner | |
local.get $current_address | |
call $parse_digit | |
local.set $parse_digit_success | |
local.set $parse_digit_value | |
local.get $parse_digit_success | |
(if | |
(then | |
;; increment the number of bytes parsed | |
local.get $bytes_parsed | |
i32.const 1 | |
i32.add | |
local.set $bytes_parsed | |
;; increment the current address | |
local.get $current_address | |
i32.const 1 | |
i32.add | |
local.set $current_address | |
;; $number = $number * 10 + $parse_digit_value | |
local.get $number | |
i32.const 10 | |
i32.mul | |
local.get $parse_digit_value | |
i32.add | |
local.set $number | |
;; continue; parse the next digit | |
br $inner | |
) | |
(else | |
;; break; exist the loop | |
br $outer | |
) | |
) | |
) | |
) | |
local.get $number | |
local.get $bytes_parsed | |
) | |
(func $parse_space (param $address i32) (result i32) | |
local.get $address | |
i32.load8_u | |
i32.const 0x20 | |
i32.eq | |
) | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(module | |
(export "main" (func $main)) | |
;; Create memory with at least 1 page of 64k of memory | |
(memory $mem 1) | |
;; Initialize the first several bytes of the memory with some text | |
(data (i32.const 0) "5 10 42 193 1007000000 ") | |
(; byte offset 01234567890123456789012 ;) | |
(; 1 2 ;) | |
(func $main (result i32) | |
(local $address i32) | |
(local $sum i32) | |
(local $parse_number_value i32) | |
(local $parse_number_result i32) | |
i32.const 0 | |
local.set $address | |
i32.const 0 | |
local.set $sum | |
(block $block | |
(loop $loop | |
local.get $address | |
call $parse_number | |
local.set $parse_number_result | |
local.set $parse_number_value | |
local.get $parse_number_result | |
(if | |
(then | |
;; increment our next address by the number of bytes | |
;; we parsed | |
local.get $address | |
local.get $parse_number_result | |
i32.add | |
local.set $address | |
;; add to our sum | |
local.get $sum | |
local.get $parse_number_value | |
i32.add | |
local.set $sum | |
) | |
(else | |
;; No number parsed; stop | |
br $block | |
) | |
) | |
local.get $address | |
call $parse_space | |
(if | |
(then | |
local.get $address | |
i32.const 1 | |
i32.add | |
local.set $address | |
) | |
(else | |
br $block | |
) | |
) | |
br $loop | |
) | |
) | |
local.get $sum | |
) | |
;; Load an ascii byte from memory and return its numeric value | |
(func $parse_digit (param $address i32) (result (; the value ;) i32 (; correctly parsed ;) i32) | |
(local $parsed i32) | |
(local $ge_zero i32) | |
(local $lt_ten i32) | |
(local $success i32) | |
(local $result i32) | |
;; get the byte at the provided address | |
local.get $address | |
i32.load8_u | |
;; subtract ascii "0" and store it | |
i32.const 0x30 | |
i32.sub | |
local.set $parsed | |
;; is $parsed >= 0? | |
local.get $parsed | |
i32.const 0 | |
i32.ge_s | |
local.set $ge_zero | |
;; is $parsed < 10? | |
local.get $parsed | |
i32.const 10 | |
i32.lt_s | |
local.set $lt_ten | |
;; is $parsed >= 0 && $parsed < 10 | |
local.get $ge_zero | |
local.get $lt_ten | |
i32.and | |
local.tee $success ;; store the value *and* leave it on the stack | |
;; if what's on the stack... | |
(if | |
;; ... is true, then ... | |
(then | |
;; set the result to what we parsed | |
local.get $parsed | |
local.set $result | |
) | |
;; ... otherwise ... | |
(else | |
;; set the result to a constant | |
i32.const -1 | |
local.set $result | |
) | |
) | |
;; and return the results | |
local.get $result | |
local.get $success | |
) | |
(func $parse_number (param $address i32) (result (; number ;) i32 (; bytes parsed ;) i32) | |
(local $bytes_parsed i32) | |
(local $current_address i32) | |
(local $parse_digit_success i32) | |
(local $parse_digit_value i32) | |
(local $number i32) | |
;; initialize $number to 0 | |
i32.const 0 | |
local.set $number | |
;; initialize $bytes_parsed to 0 | |
i32.const 0 | |
local.set $bytes_parsed | |
;; initialize $current_address to $address | |
local.get $address | |
local.set $current_address | |
(block $outer | |
(loop $inner | |
local.get $current_address | |
call $parse_digit | |
local.set $parse_digit_success | |
local.set $parse_digit_value | |
local.get $parse_digit_success | |
(if | |
(then | |
;; increment the number of bytes parsed | |
local.get $bytes_parsed | |
i32.const 1 | |
i32.add | |
local.set $bytes_parsed | |
;; increment the current address | |
local.get $current_address | |
i32.const 1 | |
i32.add | |
local.set $current_address | |
;; $number = $number * 10 + $parse_digit_value | |
local.get $number | |
i32.const 10 | |
i32.mul | |
local.get $parse_digit_value | |
i32.add | |
local.set $number | |
;; continue; parse the next digit | |
br $inner | |
) | |
(else | |
;; break; exist the loop | |
br $outer | |
) | |
) | |
) | |
) | |
local.get $number | |
local.get $bytes_parsed | |
) | |
(func $parse_space (param $address i32) (result i32) | |
local.get $address | |
i32.load8_u | |
i32.const 0x20 | |
i32.eq | |
) | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment