about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <{ID}+{username}@users.noreply.github.com>2025-01-02 09:19:14 -0500
committerelioat <{ID}+{username}@users.noreply.github.com>2025-01-02 09:19:14 -0500
commitfbb6bb910bd7d8bddb52c06cfe72b97d44bb2813 (patch)
tree24c566bad1c6a0c4c6b095fce1e76cb8a9720f15
parent2c154a34f478285680efeee7c15ba02116ee882d (diff)
downloadtour-fbb6bb910bd7d8bddb52c06cfe72b97d44bb2813.tar.gz
*
-rwxr-xr-xawk/forth/f.awk504
-rw-r--r--awk/forth/test.forth58
2 files changed, 173 insertions, 389 deletions
diff --git a/awk/forth/f.awk b/awk/forth/f.awk
index dd8baf4..23bc9f0 100755
--- a/awk/forth/f.awk
+++ b/awk/forth/f.awk
@@ -110,6 +110,37 @@ BEGIN {
     # 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
@@ -188,84 +219,61 @@ function pop() {
     return stack[top--]
 }
 
-function add() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
-    }
-    push(pop() + pop())
-}
-
-# Function for subtraction
-function subtract() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
-    }
+function binary_op(operation) {
+    if (!check_stack(2)) return
     second = pop()
     first = pop()
-    push(first - second)
-}
-
-# Function for multiplication
-function multiply() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
+    if (operation == "+") push(first + second)
+    else if (operation == "-") push(first - second)
+    else if (operation == "*") push(first * second)
+    else if (operation == "/") {
+        if (second == 0) {
+            print "Error: Division by zero"
+            push(first)
+            push(second)
+            return
+        }
+        push(first / second)
     }
-    push(pop() * pop())
+    else if (operation == "mod") push(first % second)
+    else if (operation == "=") push(first == second ? 1 : 0)
+    else if (operation == "<") push(first < second ? 1 : 0)
+    else if (operation == ">") push(first > second ? 1 : 0)
 }
 
-# Function for division
-function divide() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
-    }
-    second = pop()
-    if (second == 0) {
-        print "Error: Division by zero"
-        return
-    }
-    first = pop()
-    push(first / second)
-}
+# Then simplify the operation functions:
+function add() { binary_op("+") }
+function subtract() { binary_op("-") }
+function multiply() { binary_op("*") }
+function divide() { binary_op("/") }
+function mod() { binary_op("mod") }
+function equals() { binary_op("=") }
+function less_than() { binary_op("<") }
+function greater_than() { binary_op(">") }
 
 # Function to duplicate the top value
 function dup() {
-    if (top < 0) {
-        print "Error: Stack is empty"
-        return
-    }
-    push(stack[top])  # Push the top value again
+    if (!check_stack(1)) return
+    push(stack[top])
 }
 
 # Function to copy the second value to the top
 function over() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
-    }
-    push(stack[top - 1])  # Push the second value
+    if (!check_stack(2)) return
+    push(stack[top - 1])
 }
 
 # Function to swap the top two values
 function swap() {
-    if (top < 1) {
-        print "Error: Not enough values on the stack"
-        return
-    }
-    temp = pop()  # Pop the top value
-    second = pop()  # Pop the second value
-    push(temp)  # Push the first value back
-    push(second)  # Push the second value back
+    if (!check_stack(2)) return
+    temp = pop()
+    second = pop()
+    push(temp)
+    push(second)
 }
 
 function print_top() {
-    if (top < 0) {
-        print "Error: Stack is empty"
-        return
-    }
+    if (!check_stack(1)) return
     print stack[top]
 }
 
@@ -277,13 +285,8 @@ function list_words() {
     }
 }
 
-# Add these new functions after the existing stack manipulation functions:
-
 function rot() {
-    if (top < 2) {
-        print "Error: Not enough values on stack"
-        return
-    }
+    if (!check_stack(3)) return
     third = pop()
     second = pop()
     first = pop()
@@ -293,29 +296,20 @@ function rot() {
 }
 
 function drop() {
-    if (top < 0) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(1)) 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
+    if (!check_stack(2)) return
+    temp = stack[top]
+    drop()
+    drop()
+    push(temp)
 }
 
 function tuck() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
+    if (!check_stack(2)) return
     temp = pop()
     second = pop()
     push(temp)
@@ -324,104 +318,51 @@ function tuck() {
 }
 
 function roll() {
-    if (top < 0) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(1)) return
     n = int(pop())
-    if (top < n) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(n)) 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
-    }
+    if (!check_stack(1)) return
     n = int(pop())
-    if (top < n) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(n)) return
     if (n < 0) return
-    
     push(stack[top - n])
 }
 
 function negate() {
-    if (top < 0) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(1)) return
     push(-pop())
 }
 
 function abs() {
-    if (top < 0) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(1)) return
     n = pop()
     push(n < 0 ? -n : n)
 }
 
 function max() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
+    if (!check_stack(2)) return
     b = pop()
     a = pop()
     push(a > b ? a : b)
 }
 
 function min() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
+    if (!check_stack(2)) 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]
@@ -441,34 +382,30 @@ function create_variable(name) {
         return
     }
     variables[name] = 0  # Initialize variable to 0
-    push(name)          # Push variable name (address) onto stack
+    # No need to push the variable name onto the stack
 }
 
 # Function to store a value in a variable
-function store() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
-    addr = pop()
+function check_variable(addr, error_msg) {
     if (!(addr in variables)) {
-        print "Error: Invalid variable '" addr "'"
-        return
+        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() {
-    if (top < 0) {
-        print "Error: Stack underflow"
-        return
-    }
+    if (!check_stack(1)) return
     addr = pop()
-    if (!(addr in variables)) {
-        print "Error: Invalid variable '" addr "'"
-        return
-    }
+    if (!check_variable(addr)) return
     push(variables[addr])
 }
 
@@ -543,6 +480,37 @@ function handle_else() {
     cond_stack[cond_top] = !cond_stack[cond_top]
 }
 
+# Add this 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"
+            }
+        }
+        else if (handlers[word] == "store") store()
+        else if (handlers[word] == "fetch") fetch()
+        else if (handlers[word] == "print_var") print_var()
+        else {
+            print "Error: Handler '" handlers[word] "' not implemented"
+            return 0
+        }
+        return 1
+    }
+    return 0
+}
+
 # Move the main command processing into a function
 function process_line() {
     # Process input only if it's not empty
@@ -553,6 +521,18 @@ function process_line() {
         gsub(/[[:space:]]+$/, "")  # Remove trailing whitespace
         gsub(/[[:space:]]+/, " ")  # Normalize internal whitespace
 
+        # 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
@@ -585,190 +565,16 @@ function process_line() {
         for (i = 1; i <= NF; i++) {
             if ($i ~ /^-?[0-9]+$/) {
                 push($i)
-            } else if ($i in variables) {
-                push($i)
-            } else if ($i in words) {
-                # Only check for compile-only words when not in a definition
+            } 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
                 }
-                # 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
+                if (!execute_word($i)) {
+                    print "Error: Unknown command '" $i "'"
                 }
-                else if (words[$i] == "if") {
-                    handle_if()
-                }
-                else if (words[$i] == "then") {
-                    handle_then()
-                }
-                else if (words[$i] == "else") {
-                    handle_else()
-                }
-                else if (words[$i] == "<") less_than()
-                else if (words[$i] == ">") greater_than()
-                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 (command == "<if>") {
-                                handle_if()
-                                if (!cond_stack[cond_top]) {
-                                    drop()  # Now consume the condition value
-                                    # Skip until matching then/else
-                                    skip_level = 1
-                                    while (skip_level > 0 && j < length(cmd)) {
-                                        j++
-                                        if (j >= length(cmd)) break
-                                        if (cmd[j] == "<if>") skip_level++
-                                        else if (cmd[j] == "<then>") {
-                                            skip_level--
-                                            if (skip_level == 0) break
-                                        }
-                                        else if (cmd[j] == "<else>" && skip_level == 1) {
-                                            skip_level = 0
-                                            break
-                                        }
-                                    }
-                                } else {
-                                    drop()  # Consume the condition value only after checking it
-                                }
-                            }
-                            else if (command == "<else>") {
-                                handle_else()
-                                if (cond_stack[cond_top]) {
-                                    # Skip until matching then
-                                    skip_level = 1
-                                    while (skip_level > 0 && j < length(cmd)) {
-                                        j++
-                                        if (j >= length(cmd)) break
-                                        if (cmd[j] == "<if>") skip_level++
-                                        else if (cmd[j] == "<then>") {
-                                            skip_level--
-                                            if (skip_level == 0) break
-                                        }
-                                    }
-                                }
-                            }
-                            else if (command == "<then>") {
-                                handle_then()
-                            }
-                            else if (command == "<string>") {
-                                j++
-                                str = ""
-                                while (j < length(cmd) && cmd[j] != "</string>") {
-                                    str = str (str == "" ? "" : " ") cmd[j]
-                                    j++
-                                }
-                                if (cond_stack[cond_top] != 0) {  # Only print if inside a true condition
-                                    print_string(str)
-                                }
-                                j++  # Skip past </string>
-                            }
-                            else if (words[command] == "<") less_than()
-                            else if (words[command] == ">") greater_than()
-                        }
-                    }
-                }
-            } 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 "'"
             }
@@ -785,25 +591,3 @@ function process_line() {
 function print_string(str) {
     printf "%s", str
 }
-
-# Add these new functions for comparisons:
-
-function less_than() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
-    second = pop()
-    first = pop()
-    push(first < second ? 1 : 0)
-}
-
-function greater_than() {
-    if (top < 1) {
-        print "Error: Not enough values on stack"
-        return
-    }
-    second = pop()
-    first = pop()
-    push(first > second ? 1 : 0)
-}
diff --git a/awk/forth/test.forth b/awk/forth/test.forth
index 2d4197b..d746e66 100644
--- a/awk/forth/test.forth
+++ b/awk/forth/test.forth
@@ -1,84 +1,84 @@
 ( Basic Forth test suite )
 
-testing Basic stack operations
+testing "Basic stack operations"
 5 dup . . test 5 test 5
 
-testing Addition
+testing "Addition"
 3 4 + test 7
 
-testing Subtraction
+testing "Subtraction"
 10 3 - test 7
 
-testing Multiplication
+testing "Multiplication"
 6 7 * test 42
 
-testing Division
+testing "Division"
 20 4 / test 5
 
-testing Stack manipulation - rot
+testing "Stack manipulation - rot"
 1 2 3 rot test 1
 
-testing Stack manipulation - drop
+testing "Stack manipulation - drop"
 1 2 3 drop test 2
 
-testing Stack manipulation - nip
+testing "Stack manipulation - nip"
 1 2 3 nip test 3
 
-testing Stack manipulation - tuck
+testing "Stack manipulation - tuck"
 1 2 tuck test 2
 
-testing Stack manipulation - over
+testing "Stack manipulation - over"
 1 2 over test 1
 
-testing Variables
+testing "Variables"
 variable x
 5 x !
 x @ test 5
 10 x !
 x ? ( should print 10 )
 
-testing Negate
+testing "Negate"
 5 negate test -5
 
-testing Absolute value
+testing "Absolute value"
 -7 abs test 7
 
-testing Maximum
+testing "Maximum"
 3 8 max test 8
 
-testing Minimum
+testing "Minimum"
 3 8 min test 3
 
-testing Modulo
+testing "Modulo"
 17 5 mod test 2
 
-testing Equality
+testing "Equality"
 5 5 = test 1
 5 6 = test 0
 
-testing Word definition
+testing "Word definition"
 : square dup * ;
 5 square test 25
 
-testing Complex word definition
+testing "Complex word definition"
 variable counter
 : increment-counter counter @ 1 + counter ! ;
 5 counter !
 increment-counter
 counter @ test 6
 
-testing Basic conditional - if/then
+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
+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
+testing "Nested conditionals"
 : test-if-3 ( n -- n ) 
     dup 10 > if 
         dup 20 > if 
@@ -90,7 +90,7 @@ testing Nested conditionals
 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
+testing "Conditional with stack operations"
 : test-if-4 ( n -- n n ) 
     dup 5 > if 
         dup 
@@ -98,7 +98,7 @@ testing Conditional with stack operations
 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
+testing "Complex nested conditionals"
 : test-if-5 ( n -- n )
     dup 0 < if
         ." negative "
@@ -118,12 +118,12 @@ testing Complex nested conditionals
 75 test-if-5 test 75 ( should print "medium " )
 25 test-if-5 test 25 ( should print "small " )
 
-testing Conditionals in word definitions
+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
+testing "Complex conditional word"
 : max-test ( n1 n2 -- max ) 
     2dup > if 
         drop 
@@ -134,10 +134,10 @@ testing Complex conditional word
 3 5 max-test test 5
 
 ( Try to use if outside of a definition - should error )
-testing Compile-only words
+testing "Compile-only words"
 5 4 > if 42 then ( should print error about compile-only word )
 
-testing Comparison operators
+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 )
@@ -145,7 +145,7 @@ testing Comparison operators
 5 5 < test 0  ( 5 < 5 is false )
 5 5 > test 0  ( 5 > 5 is false )
 
-testing Comparison in conditionals
+testing "Comparison in conditionals"
 : test-compare ( n -- )
 dup 5 > if ." Greater than 5" else
 dup 5 < if ." Less than 5" else