diff options
author | elioat <elioat@tilde.institute> | 2025-01-02 07:31:09 -0500 |
---|---|---|
committer | elioat <elioat@tilde.institute> | 2025-01-02 07:31:09 -0500 |
commit | e170508a1a6ab735b97d79369c1d6fc624ff57b9 (patch) | |
tree | 6e148475ad824d408f4373b4602c55aa4a5ecf00 /awk/forth/f.awk | |
parent | 9c4bde6f5e44454a6c1db0a53cb4b94732711272 (diff) | |
download | tour-e170508a1a6ab735b97d79369c1d6fc624ff57b9.tar.gz |
*
Diffstat (limited to 'awk/forth/f.awk')
-rwxr-xr-x | awk/forth/f.awk | 501 |
1 files changed, 464 insertions, 37 deletions
diff --git a/awk/forth/f.awk b/awk/forth/f.awk index 260310e..52df3a2 100755 --- a/awk/forth/f.awk +++ b/awk/forth/f.awk @@ -2,20 +2,91 @@ # Forth interpreter in AWK -# Add a dictionary array to hold available words BEGIN { print "Welcome to the AWK Forth Interpreter!" print "Type your commands below. Use 'bye' to quit." + # Initialize variables + top = -1 # Initialize stack pointer + # Initialize the dictionary with basic words - words["+"] = "Addition" - words["-"] = "Subtraction" - words["*"] = "Multiplication" - words["/"] = "Division" - words["dup"] = "Duplicate the top value" - words["over"] = "Copy the second value to the top" - words["swap"] = "Swap the top two values" - words["."] = "Print top of stack" - words["bye"] = "Exit the interpreter" + words["+"] = "+" + words["-"] = "-" + words["*"] = "*" + words["/"] = "/" + words["dup"] = "dup" + words["over"] = "over" + words["swap"] = "swap" + words["."] = "." + words["bye"] = "bye" + words["rot"] = "rot" + words["drop"] = "drop" + words["nip"] = "nip" + words["tuck"] = "tuck" + words["roll"] = "roll" + words["pick"] = "pick" + words["negate"] = "negate" + words["abs"] = "abs" + words["max"] = "max" + words["min"] = "min" + words["mod"] = "mod" + words["="] = "=" + words["see"] = "see" + + # Add descriptions for words + desc["+"] = "( n1 n2 -- sum ) Add top two numbers" + desc["-"] = "( n1 n2 -- diff ) Subtract top number from second" + desc["*"] = "( n1 n2 -- prod ) Multiply top two numbers" + desc["/"] = "( n1 n2 -- quot ) Divide second by top" + desc["dup"] = "( n -- n n ) Duplicate top of stack" + desc["over"] = "( n1 n2 -- n1 n2 n1 ) Copy second item to top" + desc["swap"] = "( n1 n2 -- n2 n1 ) Swap top two items" + desc["rot"] = "( n1 n2 n3 -- n2 n3 n1 ) Rotate top three items" + desc["drop"] = "( n -- ) Discard top item" + desc["nip"] = "( n1 n2 -- n2 ) Remove second item" + desc["tuck"] = "( n1 n2 -- n2 n1 n2 ) Copy top item below second" + desc["roll"] = "( nk ... n1 n0 k -- nk-1 ... n1 n0 nk ) Move kth item to top" + desc["pick"] = "( nk ... n1 n0 k -- nk ... n1 n0 nk ) Copy kth item to top" + desc["negate"] = "( n -- -n ) Negate number" + desc["abs"] = "( n -- |n| ) Absolute value" + desc["max"] = "( n1 n2 -- max ) Maximum of top two numbers" + desc["min"] = "( n1 n2 -- min ) Minimum of top two numbers" + desc["mod"] = "( n1 n2 -- rem ) Remainder of n1/n2" + desc["="] = "( n1 n2 -- flag ) Test if equal, leaves 1 if true, 0 if false" + + # 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" + + # 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 + } +} + +# Function to define a new word +function define_word(name, definition) { + words[name] = definition } # Stack to hold values @@ -120,33 +191,389 @@ function list_words() { } } -# Main loop to read commands -{ - for (i = 1; i <= NF; i++) { - if ($i ~ /^[0-9]+$/) { - push($i) - } else if ($i == "+") { - add() - } else if ($i == "-") { - subtract() - } else if ($i == "*") { - multiply() - } else if ($i == "/") { - divide() - } else if ($i == "dup") { - dup() - } else if ($i == "over") { - over() - } else if ($i == "swap") { - swap() - } else if ($i == ".") { - print_top() - } else if ($i == "bye") { - exit - } else if ($i == "words") { - list_words() - } else { - print "Error: Unknown command '" $i "'" +# Add these new functions after the existing stack manipulation functions: + +function rot() { + if (top < 2) { + print "Error: Not enough values on stack" + return + } + third = pop() + second = pop() + first = pop() + push(second) + push(third) + push(first) +} + +function drop() { + if (top < 0) { + print "Error: Stack underflow" + return + } + top-- +} + +function nip() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + temp = stack[top] # Save top value + drop() # Remove top value + drop() # Remove second value + push(temp) # Put original top value back +} + +function tuck() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + temp = pop() + second = pop() + push(temp) + push(second) + push(temp) +} + +function roll() { + if (top < 0) { + print "Error: Stack underflow" + return + } + n = int(pop()) + if (top < n) { + print "Error: Stack underflow" + return + } + if (n <= 0) return + + # Store the item to be moved + temp = stack[top - n + 1] + + # Shift the other items down + for (i = top - n + 1; i < top; i++) { + stack[i] = stack[i + 1] + } + + # Put the rolled item on top + stack[top] = temp +} + +function pick() { + if (top < 0) { + print "Error: Stack underflow" + return + } + n = int(pop()) + if (top < n) { + print "Error: Stack underflow" + return + } + if (n < 0) return + + push(stack[top - n]) +} + +function negate() { + if (top < 0) { + print "Error: Stack underflow" + return + } + push(-pop()) +} + +function abs() { + if (top < 0) { + print "Error: Stack underflow" + return + } + n = pop() + push(n < 0 ? -n : n) +} + +function max() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + b = pop() + a = pop() + push(a > b ? a : b) +} + +function min() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + b = pop() + a = pop() + push(a < b ? a : b) +} + +function mod() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + b = pop() + if (b == 0) { + print "Error: Division by zero" + push(b) + return + } + a = pop() + push(a % b) +} + +function equals() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + push((pop() == pop()) ? 1 : 0) +} + +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 + push(name) # Push variable name (address) onto stack +} + +# Function to store a value in a variable +function store() { + if (top < 1) { + print "Error: Not enough values on stack" + return + } + addr = pop() + if (!(addr in variables)) { + print "Error: Invalid variable '" addr "'" + return + } + variables[addr] = pop() +} + +# Function to fetch a value from a variable +function fetch() { + if (top < 0) { + print "Error: Stack underflow" + return + } + addr = pop() + if (!(addr in variables)) { + print "Error: Invalid 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] +} + +# 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 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 +} + +# Move the main command processing into a function +function process_line() { + # Process input only if it's not empty + if (NF > 0) { + # Remove comments + gsub(/\(.*\)/, "") # Remove everything from ( to ) + + # Check for word definitions + if ($1 == ":") { + word_name = $2 + definition = "" + for (i = 3; i <= NF; i++) { + if ($i == ";") { + define_word(word_name, definition) + print "Defined new word: " word_name + break + } else { + definition = definition " " $i + } + } + return + } + + # Process remaining input + for (i = 1; i <= NF; i++) { + if ($i ~ /^-?[0-9]+$/) { # Modified to handle negative numbers + push($i) # Push numbers onto the stack + } else if ($i in variables) { # Check for variables BEFORE words + push($i) # Push variable name onto stack + } else if ($i in words) { + # If the word is defined, execute its definition + if (words[$i] == "+") add() + else if (words[$i] == "-") subtract() + else if (words[$i] == "*") multiply() + else if (words[$i] == "/") divide() + else if (words[$i] == "dup") dup() + else if (words[$i] == "over") over() + else if (words[$i] == "swap") swap() + else if (words[$i] == ".") print_top() + else if (words[$i] == "bye") exit + else if (words[$i] == "words") list_words() + else if (words[$i] == "rot") rot() + else if (words[$i] == "drop") drop() + else if (words[$i] == "nip") nip() + else if (words[$i] == "tuck") tuck() + else if (words[$i] == "roll") roll() + else if (words[$i] == "pick") pick() + else if (words[$i] == "negate") negate() + else if (words[$i] == "abs") abs() + else if (words[$i] == "max") max() + else if (words[$i] == "min") min() + else if (words[$i] == "mod") mod() + else if (words[$i] == "=") equals() + else if (words[$i] == "see") { + if (i + 1 <= NF) { + see($(i + 1)) + i++ # Skip the word name + } else { + print "Error: see requires a word name" + } + } + else if (words[$i] == "variable") { + if (i + 1 <= NF) { + create_variable($(i + 1)) + i++ # Skip the variable name + } else { + print "Error: variable requires a name" + } + } + else if (words[$i] == "!") store() + else if (words[$i] == "@") fetch() + else if (words[$i] == "?") print_var() + else if (words[$i] == "test") { + if (i + 1 <= NF) { + run_test($(i + 1)) + i++ # Skip the expected value + } else { + print "Error: test requires an expected value" + } + } + else if (words[$i] == "testing") { + print_test_description() + return + } + else { + # Execute user-defined word + split(words[$i], cmd, " ") + for (j = 1; j <= length(cmd); j++) { + command = cmd[j] + if (command ~ /^-?[0-9]+$/) { # Modified for negative numbers + push(command) + } else if (command in variables) { # Check for variables + push(command) + } else if (command in words) { + # Recursive execution of defined words + if (words[command] == "+") add() + else if (words[command] == "-") subtract() + else if (words[command] == "*") multiply() + else if (words[command] == "/") divide() + else if (words[command] == "dup") dup() + else if (words[command] == "over") over() + else if (words[command] == "swap") swap() + else if (words[command] == ".") print_top() + else if (words[command] == "bye") exit + else if (words[command] == "words") list_words() + else if (words[command] == "!") store() + else if (words[command] == "@") fetch() + else if (words[command] == "?") print_var() + else if (words[command] == "see") { + if (i + 1 <= NF) { + see($(i + 1)) + i++ # Skip the word name + } else { + print "Error: see requires a word name" + } + } + } + } + } + } else if ($i == "+") { + add() + } else if ($i == "-") { + subtract() + } else if ($i == "*") { + multiply() + } else if ($i == "/") { + divide() + } else if ($i == "dup") { + dup() + } else if ($i == "over") { + over() + } else if ($i == "swap") { + swap() + } else if ($i == ".") { + print_top() + } else if ($i == "bye") { + exit + } else if ($i == "words") { + list_words() + } else if ($i != "") { # Ignore empty tokens + print "Error: Unknown command '" $i "'" + } } } } + +# Main loop to read commands (for interactive mode) +{ + process_line() +} |