diff options
author | elioat <elioat@tilde.institute> | 2025-01-09 16:57:05 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-01-09 16:57:05 -0500 |
commit | 662c3ceae2a4ffc598a8b99690a0cfdc0b4f47a8 (patch) | |
tree | 22c03fefb86b314341fbea70afaa95d91c7e7bbe /awk | |
parent | b9440ce32de013237d61d2cccab229537ba19caa (diff) | |
download | tour-662c3ceae2a4ffc598a8b99690a0cfdc0b4f47a8.tar.gz |
*
Diffstat (limited to 'awk')
-rwxr-xr-x | awk/forth/f.awk | 449 | ||||
-rw-r--r-- | awk/forth/test.forth | 201 |
2 files changed, 144 insertions, 506 deletions
diff --git a/awk/forth/f.awk b/awk/forth/f.awk index 23bc9f0..eed9774 100755 --- a/awk/forth/f.awk +++ b/awk/forth/f.awk @@ -35,6 +35,36 @@ BEGIN { words["then"] = "then" words["else"] = "else" words[">"] = ">" + words["<"] = "<" + + # Add handlers for all words + handlers["+"] = "add" + handlers["-"] = "subtract" + handlers["*"] = "multiply" + handlers["/"] = "divide" + handlers["dup"] = "dup" + handlers["over"] = "over" + handlers["swap"] = "swap" + handlers["."] = "print_top" + handlers["<"] = "less_than" + handlers[">"] = "greater_than" + handlers["rot"] = "rot" + handlers["drop"] = "drop" + handlers["nip"] = "nip" + handlers["tuck"] = "tuck" + handlers["roll"] = "roll" + handlers["pick"] = "pick" + handlers["negate"] = "negate" + handlers["abs"] = "abs" + handlers["max"] = "max" + handlers["min"] = "min" + handlers["mod"] = "mod" + handlers["="] = "equals" + handlers["if"] = "handle_if" + handlers["then"] = "handle_then" + handlers["else"] = "handle_else" + handlers["bye"] = "bye" + handlers["see"] = "see" # Add descriptions for words desc["+"] = "( n1 n2 -- sum ) Add top two numbers" @@ -60,24 +90,9 @@ BEGIN { desc["then"] = "( -- ) End conditional execution" desc["else"] = "( -- ) Execute if previous condition was false" desc[">"] = "( n1 n2 -- flag ) Returns true if n1 is greater than n2" - - # Add variable-related words to dictionary - words["variable"] = "variable" - words["!"] = "!" - words["@"] = "@" - words["?"] = "?" - - # Add descriptions for the new words - desc["variable"] = "( -- addr ) Create a variable" - desc["!"] = "( n addr -- ) Store n at addr" - desc["@"] = "( addr -- n ) Fetch contents of addr" - desc["?"] = "( addr -- ) Print contents of addr" - - # Initialize test-related words - words["test"] = "test" - words["testing"] = "testing" - desc["test"] = "( n1 n2 -- ) Compare values and report test result" - desc["testing"] = "( -- ) Print test description" + desc["<"] = "( n1 n2 -- flag ) Returns true if n1 is less than n2" + desc["bye"] = "( -- ) Exit the interpreter" + desc["see"] = "( -- ) Show definition of a word" # Initialize condition stack cond_top = -1 @@ -86,127 +101,9 @@ BEGIN { compile_only["if"] = 1 compile_only["then"] = 1 compile_only["else"] = 1 - - # Add dot-quote to dictionary - words[".\""] = ".\"" - desc[".\""] = "( -- ) Print following string up to closing quote" - - # Handle file input - if (ARGC > 1) { - print "Loading file:", ARGV[1] - while ((getline < ARGV[1]) > 0) { - if ($0 ~ /^[[:space:]]*$/) continue # Skip empty lines - print "> " $0 - process_line() - } - close(ARGV[1]) - exit - } - - # Add to dictionary initialization - words["<"] = "<" - words[">"] = ">" - - # Add descriptions - desc["<"] = "( n1 n2 -- flag ) Returns true if n1 is less than n2" - desc[">"] = "( n1 n2 -- flag ) Returns true if n1 is greater than n2" - - # Add handlers - handlers["+" ] = "add" - handlers["-" ] = "subtract" - handlers["*" ] = "multiply" - handlers["/" ] = "divide" - handlers["dup"] = "dup" - handlers["over"] = "over" - handlers["swap"] = "swap" - handlers["."] = "print_top" - handlers["<"] = "less_than" - handlers[">"] = "greater_than" - - # Add variable handler - handlers["variable"] = "create_variable" - handlers["!"] = "store" - handlers["@"] = "fetch" - handlers["?"] = "print_var" -} - -# Add these helper functions near the top -function check_stack(min_items, error_msg) { - if (top < min_items - 1) { - print error_msg ? error_msg : "Error: Not enough values on stack" - return 0 - } - return 1 -} - -function check_underflow() { - return check_stack(1, "Error: Stack underflow") -} - -# Function to define a new word -function define_word(name, definition) { - # Store the raw definition - raw_definitions[name] = definition - print "DEBUG: Raw definition for " name ": " definition - - # Process the definition to handle conditionals - processed = process_definition(definition) - print "DEBUG: Processed definition for " name ": " processed - words[name] = processed -} - -function process_definition(definition) { - # Normalize whitespace in definition - gsub(/^[[:space:]]+/, "", definition) - gsub(/[[:space:]]+$/, "", definition) - gsub(/[[:space:]]+/, " ", definition) - - # Split definition into words - split(definition, def_words, " ") - processed = "" - in_string = 0 - string_content = "" - - for (i = 1; i <= length(def_words); i++) { - word = def_words[i] - - if (word == ".\"") { - # Start collecting string - in_string = 1 - string_content = "" - continue - } - - if (in_string) { - # Check if this word contains the closing quote - if (word ~ /"$/) { - # End of string found - string_content = string_content (string_content == "" ? "" : " ") substr(word, 1, length(word)-1) - processed = processed " <string>" string_content "</string>" - in_string = 0 - } else { - # Add to string content - string_content = string_content (string_content == "" ? "" : " ") word - } - continue - } - - # Handle non-string words - if (word == "if") { - processed = processed " <if>" - } else if (word == "else") { - processed = processed " <else>" - } else if (word == "then") { - processed = processed " <then>" - } else { - processed = processed " " word - } - } - - return processed } -# Stack to hold values +# Stack operations function push(value) { stack[++top] = value } @@ -219,6 +116,15 @@ function pop() { return stack[top--] } +function check_stack(min_items, error_msg) { + if (top < min_items - 1) { + print error_msg ? error_msg : "Error: Not enough values on stack" + return 0 + } + return 1 +} + +# Binary operations function binary_op(operation) { if (!check_stack(2)) return second = pop() @@ -241,7 +147,7 @@ function binary_op(operation) { else if (operation == ">") push(first > second ? 1 : 0) } -# Then simplify the operation functions: +# Handler functions function add() { binary_op("+") } function subtract() { binary_op("-") } function multiply() { binary_op("*") } @@ -251,19 +157,16 @@ function equals() { binary_op("=") } function less_than() { binary_op("<") } function greater_than() { binary_op(">") } -# Function to duplicate the top value function dup() { if (!check_stack(1)) return push(stack[top]) } -# Function to copy the second value to the top function over() { if (!check_stack(2)) return push(stack[top - 1]) } -# Function to swap the top two values function swap() { if (!check_stack(2)) return temp = pop() @@ -272,19 +175,6 @@ function swap() { push(second) } -function print_top() { - if (!check_stack(1)) return - print stack[top] -} - -# Function to list all available words -function list_words() { - print "Available words:" - for (word in words) { - print word - } -} - function rot() { if (!check_stack(3)) return third = pop() @@ -363,147 +253,65 @@ function min() { push(a < b ? a : b) } -function see(word) { - if (word in desc) { - print desc[word] - } else if (word in words) { - print ": " word " " words[word] " ;" - } else { - print "Word '" word "' not found" - } -} - -# Add these new functions for variable handling: - -# Function to create a new variable -function create_variable(name) { - if (name == "") { - print "Error: Variable name required" - return - } - variables[name] = 0 # Initialize variable to 0 - # No need to push the variable name onto the stack -} - -# Function to store a value in a variable -function check_variable(addr, error_msg) { - if (!(addr in variables)) { - print error_msg ? error_msg : "Error: Invalid variable '" addr "'" - return 0 - } - return 1 -} - -function store() { - if (!check_stack(2)) return - addr = pop() - if (!check_variable(addr)) return - variables[addr] = pop() -} - -# Function to fetch a value from a variable -function fetch() { +function print_top() { if (!check_stack(1)) return - addr = pop() - if (!check_variable(addr)) return - push(variables[addr]) -} - -# Function to print a variable's value -function print_var() { - if (top < 0) { - print "Error: Stack underflow" - return - } - addr = pop() - if (!(addr in variables)) { - print "Error: Invalid variable '" addr "'" - return - } - print variables[addr] + print stack[top] + drop() } -# Add these test-related functions: -function run_test(expected) { - if (top < 0) { - print "❌ FAIL: Stack is empty" - return - } - actual = pop() - if (actual == expected) { - print "✓ PASS" - } else { - print "❌ FAIL: Expected", expected, "but got", actual - } +function bye() { + exit } -function print_test_description() { - if (NF < 2) { - print "Error: testing requires a description" - return - } - # Collect all words after 'testing' as the description - test_desc = "" - for (i = 2; i <= NF; i++) { - test_desc = test_desc " " $i - } - print "\nTesting:" test_desc -} - -# Add these new functions for conditional handling: - -# Function to handle if statement -function handle_if() { - if (top < 0) { - print "Error: Stack underflow" +function see(word) { + if (!(word in words)) { + print "Error: Word '" word "' not found" return } - # Save condition without consuming it - cond_stack[++cond_top] = (stack[top] != 0) -} - -# Function to handle then statement -function handle_then() { - if (cond_top < 0) { - print "Error: Unmatched then" - return + if (word in desc) { + print desc[word] } - cond_top-- # Pop condition -} - -# Function to handle else statement -function handle_else() { - if (cond_top < 0) { - print "Error: Unmatched else" - return + if (word in raw_definitions) { + print ": " word " " raw_definitions[word] " ;" } - cond_stack[cond_top] = !cond_stack[cond_top] } -# Add this function +# Main processing function function execute_word(word) { if (word in handlers) { - if (handlers[word] == "add") add() - else if (handlers[word] == "subtract") subtract() - else if (handlers[word] == "multiply") multiply() - else if (handlers[word] == "divide") divide() - else if (handlers[word] == "dup") dup() - else if (handlers[word] == "over") over() - else if (handlers[word] == "swap") swap() - else if (handlers[word] == "print_top") print_top() - else if (handlers[word] == "create_variable") { - # Special handling for variable creation - if (i + 1 <= NF) { - create_variable($(++i)) - } else { - print "Error: variable requires a name" - } + handler = handlers[word] + if (handler == "bye") exit + else if (handler == "see") { + if (i + 1 <= NF) see($(++i)) + else print "Error: see requires a word name" } - else if (handlers[word] == "store") store() - else if (handlers[word] == "fetch") fetch() - else if (handlers[word] == "print_var") print_var() + else if (handler == "add") add() + else if (handler == "subtract") subtract() + else if (handler == "multiply") multiply() + else if (handler == "divide") divide() + else if (handler == "dup") dup() + else if (handler == "over") over() + else if (handler == "swap") swap() + else if (handler == "print_top") print_top() + else if (handler == "less_than") less_than() + else if (handler == "greater_than") greater_than() + else if (handler == "rot") rot() + else if (handler == "drop") drop() + else if (handler == "nip") nip() + else if (handler == "tuck") tuck() + else if (handler == "roll") roll() + else if (handler == "pick") pick() + else if (handler == "negate") negate() + else if (handler == "abs") abs() + else if (handler == "max") max() + else if (handler == "min") min() + else if (handler == "mod") mod() + else if (handler == "equals") equals() + else if (handler == "handle_if") handle_if() + else if (handler == "handle_then") handle_then() + else if (handler == "handle_else") handle_else() else { - print "Error: Handler '" handlers[word] "' not implemented" + print "Error: Handler '" handler "' not implemented" return 0 } return 1 @@ -511,83 +319,26 @@ function execute_word(word) { return 0 } -# Move the main command processing into a function -function process_line() { - # Process input only if it's not empty +# Process each line of input +{ if (NF > 0) { # Remove comments and normalize whitespace - gsub(/\(.*\)/, "") # Remove everything from ( to ) - gsub(/^[[:space:]]+/, "") # Remove leading whitespace - gsub(/[[:space:]]+$/, "") # Remove trailing whitespace - gsub(/[[:space:]]+/, " ") # Normalize internal whitespace + gsub(/\(.*\)/, "") + gsub(/^[[:space:]]+/, "") + gsub(/[[:space:]]+$/, "") + gsub(/[[:space:]]+/, " ") - # Special handling for testing command - if ($1 == "testing") { - # Collect the description string - if ($2 ~ /^".*"$/) { # Check if the second token is a quoted string - test_desc = substr($2, 2, length($2) - 2) # Remove quotes - print "\nTesting: " test_desc - } else { - print "Error: testing requires a description in quotes" - } - return - } - - # Check for word definitions - if ($1 == ":") { - word_name = $2 - definition = "" - in_definition = 1 - # Remove : and word name from the input - for (i = 3; i <= NF; i++) { - definition = definition " " $i - } - # If we don't find a semicolon, keep reading lines - while (definition !~ /;$/) { # Changed to match semicolon at end - if ((getline) <= 0) break - gsub(/\(.*\)/, "") # Remove comments - gsub(/^[[:space:]]+/, "") - gsub(/[[:space:]]+$/, "") - definition = definition " " $0 - } - # Remove the semicolon - sub(/[[:space:]]*;[[:space:]]*$/, "", definition) - - if (definition != "") { - define_word(word_name, definition) - print "Defined new word: " word_name - } - in_definition = 0 - return - } - - # Process remaining input + # Process each token for (i = 1; i <= NF; i++) { if ($i ~ /^-?[0-9]+$/) { push($i) - } else if ($i in variables) { # Check if it's a variable - push(variables[$i]) # Push the value of the variable onto the stack - } else if ($i in words) { # Check if it's a defined word - if (!in_definition && compile_only[$i]) { - print "Error: '" $i "' is compile-only" - return - } + } else if ($i in words) { if (!execute_word($i)) { - print "Error: Unknown command '" $i "'" + print "Error: Failed to execute word '" $i "'" } - } else if ($i != "") { # Ignore empty tokens - print "Error: Unknown command '" $i "'" + } else { + print "Error: Unknown word '" $i "'" } } } -} - -# Main loop to read commands (for interactive mode) -{ - process_line() -} - -# Add function to handle dot-quote -function print_string(str) { - printf "%s", str -} +} \ No newline at end of file diff --git a/awk/forth/test.forth b/awk/forth/test.forth index d746e66..a1f4f50 100644 --- a/awk/forth/test.forth +++ b/awk/forth/test.forth @@ -1,157 +1,44 @@ -( Basic Forth test suite ) - -testing "Basic stack operations" -5 dup . . test 5 test 5 - -testing "Addition" -3 4 + test 7 - -testing "Subtraction" -10 3 - test 7 - -testing "Multiplication" -6 7 * test 42 - -testing "Division" -20 4 / test 5 - -testing "Stack manipulation - rot" -1 2 3 rot test 1 - -testing "Stack manipulation - drop" -1 2 3 drop test 2 - -testing "Stack manipulation - nip" -1 2 3 nip test 3 - -testing "Stack manipulation - tuck" -1 2 tuck test 2 - -testing "Stack manipulation - over" -1 2 over test 1 - -testing "Variables" -variable x -5 x ! -x @ test 5 -10 x ! -x ? ( should print 10 ) - -testing "Negate" -5 negate test -5 - -testing "Absolute value" --7 abs test 7 - -testing "Maximum" -3 8 max test 8 - -testing "Minimum" -3 8 min test 3 - -testing "Modulo" -17 5 mod test 2 - -testing "Equality" -5 5 = test 1 -5 6 = test 0 - -testing "Word definition" -: square dup * ; -5 square test 25 - -testing "Complex word definition" -variable counter -: increment-counter counter @ 1 + counter ! ; -5 counter ! -increment-counter -counter @ test 6 - -testing "Basic conditional - if/then" -: test-if-1 ( n -- n ) dup 5 > if ." Greater than 5" then ; -6 test-if-1 test 6 ( should print "Greater than 5" and leave 6 ) -4 test-if-1 test 4 ( should print nothing and leave 4 ) - -testing "Basic conditional - if/else/then" -: test-if-2 ( n -- n ) dup 5 > if ." Greater" else ." Less=" then ; -6 test-if-2 test 6 ( should print "Greater" and leave 6 ) -4 test-if-2 test 4 ( should print "Less=" and leave 4 ) -5 test-if-2 test 5 ( should print "Less=" and leave 5 ) - -testing "Nested conditionals" -: test-if-3 ( n -- n ) - dup 10 > if - dup 20 > if - ." >20 " - then - ." >10 " - then ; -25 test-if-3 test 25 ( should print ">20 >10 " and leave 25 ) -15 test-if-3 test 15 ( should print ">10 " and leave 15 ) -5 test-if-3 test 5 ( should print nothing and leave 5 ) - -testing "Conditional with stack operations" -: test-if-4 ( n -- n n ) - dup 5 > if - dup - then ; -6 test-if-4 swap test 6 test 6 ( should leave 6 6 ) -4 test-if-4 test 4 ( should leave just 4 ) - -testing "Complex nested conditionals" -: test-if-5 ( n -- n ) - dup 0 < if - ." negative " - else - dup 100 > if - ." big " - else - dup 50 > if - ." medium " - else - ." small " - then - then - then ; --5 test-if-5 test -5 ( should print "negative " ) -150 test-if-5 test 150 ( should print "big " ) -75 test-if-5 test 75 ( should print "medium " ) -25 test-if-5 test 25 ( should print "small " ) - -testing "Conditionals in word definitions" -: abs-test ( n -- |n| ) dup 0 < if negate then ; --5 abs-test test 5 -5 abs-test test 5 - -testing "Complex conditional word" -: max-test ( n1 n2 -- max ) - 2dup > if - drop - else - nip - then ; -5 3 max-test test 5 -3 5 max-test test 5 - -( Try to use if outside of a definition - should error ) -testing "Compile-only words" -5 4 > if 42 then ( should print error about compile-only word ) - -testing "Comparison operators" -5 3 > test 1 ( 5 > 3 is true ) -3 5 > test 0 ( 3 > 5 is false ) -3 5 < test 1 ( 3 < 5 is true ) -5 3 < test 0 ( 5 < 3 is false ) -5 5 < test 0 ( 5 < 5 is false ) -5 5 > test 0 ( 5 > 5 is false ) - -testing "Comparison in conditionals" -: test-compare ( n -- ) -dup 5 > if ." Greater than 5" else -dup 5 < if ." Less than 5" else -." Equal to 5" then then ; -6 test-compare ( should print "Greater than 5" ) -4 test-compare ( should print "Less than 5" ) -5 test-compare ( should print "Equal to 5" ) - -bye \ No newline at end of file +( Basic arithmetic operations ) +2 3 + . ( expect: 5 ) +10 3 - . ( expect: 7 ) +4 5 * . ( expect: 20 ) +20 4 / . ( expect: 5 ) +7 3 mod . ( expect: 1 ) + +( Stack manipulation operations ) +5 dup . . ( expect: 5 5 ) +1 2 swap . . ( expect: 2 1 ) +1 2 over . . . ( expect: 1 2 1 ) +1 2 3 rot . . . ( expect: 2 3 1 ) +1 2 3 4 2 roll . . . . ( expect: 1 3 4 2 ) +5 drop +1 2 nip . ( expect: 2 ) +1 2 tuck . . . ( expect: 2 1 2 ) + +( Comparison operations ) +5 3 > . ( expect: 1 ) +3 5 < . ( expect: 1 ) +4 4 = . ( expect: 1 ) +5 3 < . ( expect: 0 ) +3 5 > . ( expect: 0 ) +4 5 = . ( expect: 0 ) + +( Math operations ) +5 negate . ( expect: -5 ) +-7 abs . ( expect: 7 ) +5 2 max . ( expect: 5 ) +5 2 min . ( expect: 2 ) + +( Complex stack manipulations ) +1 2 3 4 5 \ Put 5 numbers on stack +3 pick . ( expect: 2 ) +2 roll . ( expect: 4 ) +. . . . ( expect: 5 3 1 ) + +( Error handling tests ) +drop drop drop drop drop \ Clear stack +drop ( expect: Error: Stack underflow ) +. ( expect: Error: Stack underflow ) +5 0 / ( expect: Error: Division by zero ) + +bye \ No newline at end of file |