about summary refs log tree commit diff stats
path: root/awk/forth/f.awk
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-01-02 07:31:09 -0500
committerelioat <elioat@tilde.institute>2025-01-02 07:31:09 -0500
commite170508a1a6ab735b97d79369c1d6fc624ff57b9 (patch)
tree6e148475ad824d408f4373b4602c55aa4a5ecf00 /awk/forth/f.awk
parent9c4bde6f5e44454a6c1db0a53cb4b94732711272 (diff)
downloadtour-e170508a1a6ab735b97d79369c1d6fc624ff57b9.tar.gz
*
Diffstat (limited to 'awk/forth/f.awk')
-rwxr-xr-xawk/forth/f.awk501
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()
+}