diff options
Diffstat (limited to 'awk/forth/f.awk')
-rwxr-xr-x | awk/forth/f.awk | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/awk/forth/f.awk b/awk/forth/f.awk new file mode 100755 index 0000000..16de171 --- /dev/null +++ b/awk/forth/f.awk @@ -0,0 +1,369 @@ +#!/usr/bin/awk -f + +# I wanted to implement something non-trivial using awk. +# If I was clever I wouldn’t implement forth directly in awk, +# instead I’d implement a simple virtual machine using awk, +# and then implement the forth using the virtual machine’s byte code +# ...but there is only so much brain power I can exert on such a silly project. + +BEGIN { + # Initialize stacks and dictionaries + stack_ptr = 0 + dict_size = 0 + + # Built-in words, and some documentation (I could use stack comments, + # but I find those sort of unintuitive) + dict["+"] = "+ : Adds the top two numbers on the stack." + dict["-"] = "- : Subtracts the top number from the second top number on the stack." + dict["*"] = "* : Multiplies the top two numbers on the stack." + dict["/"] = "/ : Divides the second top number by the top number on the stack." + dict["."] = ". : Prints the top of the stack." + dict[".s"] = ".s : Shows all values on the stack." + dict["dup"] = "dup : Duplicates the top value on the stack." + dict["drop"] = "drop : Removes the top value from the stack." + dict["swap"] = "swap : Swaps the top two values on the stack." + dict["over"] = "over : Copies the second top value to the top of the stack." + dict["rot"] = "rot : Rotates the top three values on the stack." + dict["="] = "= : Compares the top two values for equality." + dict["<"] = "< : Checks if the second top value is less than the top value." + dict[">"] = "> : Checks if the second top value is greater than the top value." + dict["bye"] = "bye : Exits the interpreter." + dict["words"] = "words : Lists all available words and their documentation." + + # State flags + compiling = 0 + current_def = "" + def_name = "" + + # If an input file isn't specified, enter REPL mode + if (ARGC == 1) { + repl() + } +} + +# Handle file input +{ + if (FILENAME ~ /\.forth$/) { + interpret($0) + } +} + +function repl() { + print "f.awk! A forth interpreter.\nUse 'bye' to exit.\nUse 'words' to list all available words.\n" + while (1) { + printf "f> " + if (getline input < "/dev/tty" <= 0) break + interpret(input) + } +} + +function interpret(line) { + gsub(/\(.*\)/, "", line) # Remove everything from ( to ) + gsub(/\\.*$/, "", line) # Remove backslash comments, too + + n = split(line, words, /[ \t]+/) + + for (i = 1; i <= n; i++) { + word = words[i] + if (word == "") continue + + # print "Processing word: " word + + if (word == ":") { + compiling = 1 + i++ + def_name = words[i] + current_def = "" + continue + } + + if (compiling) { + if (word == ";") { + # Store user-defined word with its name and definition + dict[def_name] = "word " current_def + compiling = 0 + continue + } + current_def = current_def " " word + continue + } + + # Execute the word and skip further processing if it's .s + if (word == ".s") { + execute_word(word) + break # Exit the loop after executing .s + } + + execute_word(word) + } +} + +function execute_word(word) { + if (word ~ /^-?[0-9]+$/) { + push(word + 0) + } else if (word in dict) { + if (dict[word] ~ /^word /) { + # User-defined word + sequence = substr(dict[word], 6) + split(sequence, subwords, " ") + for (sw in subwords) { + if (subwords[sw] != "") { + execute_word(subwords[sw]) + } + } + } else { + # Built-in words + if (word == "+") math_add() + else if (word == "-") math_sub() + else if (word == "*") math_mul() + else if (word == "/") math_div() + else if (word == ".") stack_print() + else if (word == ".s") { + # print "Executing .s command" + stack_show() + } + else if (word == "dup") stack_dup() + else if (word == "drop") stack_drop() + else if (word == "swap") stack_swap() + else if (word == "over") stack_over() + else if (word == "rot") stack_rot() + else if (word == "=") compare_eq() + else if (word == "<") compare_lt() + else if (word == ">") compare_gt() + else if (word == "bye") exit_program() + else if (word == "words") list_words() + else if (word == "if") { + # Handle the if statement + if_condition = pop() + if (if_condition == 0) { + # Skip to the next part until we find 'then' or 'else' + skip_if = 1 + } + } + else if (word == "else") { + # Handle the else statement + if (skip_if) { + skip_if = 0 # Reset the skip flag + } else { + # Skip to the next part until we find 'then' + skip_else = 1 + } + } + else if (word == "then") { + # End of the conditional + skip_if = 0 + skip_else = 0 + } + } + } else { + print "Error: Unknown word '" word "'" + } +} + +function push(val) { + stack[stack_ptr++] = val +} + +function pop() { + if (stack_ptr <= 0) { + print "Error: Stack underflow" + return 0 + } + return stack[--stack_ptr] +} + +function math_add() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a + b) +} + +function math_sub() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a - b) +} + +function math_mul() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a * b) +} + +function math_div() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + if (b == 0) { + print "Error: Division by zero" + return + } + a = pop() + push(int(a / b)) +} + +function stack_print() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + print pop() +} + +function stack_show() { + print "<", stack_ptr, "> " + for (i = 0; i < stack_ptr; i++) { + printf "%s ", stack[i] + } + print "" + # print "Stack state after .s: " + # for (i = 0; i < stack_ptr; i++) { + # print stack[i] + # } + # print "" +} + +function stack_dup() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + val = stack[stack_ptr - 1] + push(val) +} + +function stack_drop() { + if (stack_ptr < 1) { + print "Error: Stack underflow" + return + } + pop() +} + +function stack_swap() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(b) + push(a) +} + +function stack_over() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a) + push(b) + push(a) +} + +function stack_rot() { + if (stack_ptr < 3) { + print "Error: Stack underflow" + return + } + c = pop() + b = pop() + a = pop() + push(b) + push(c) + push(a) +} + +function compare_eq() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a == b ? -1 : 0) +} + +function compare_lt() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a < b ? -1 : 0) +} + +function compare_gt() { + if (stack_ptr < 2) { + print "Error: Stack underflow" + return + } + b = pop() + a = pop() + push(a > b ? -1 : 0) +} + +function exit_program() { + print "Exiting program." + exit 0 +} + +function list_words() { + print "Available words:" + + # Separate arrays to hold built-in and user-defined words + split("", built_in_words) + split("", user_defined_words) + + for (w in dict) { + split(dict[w], parts, ": ") + if (parts[1] ~ /^word /) { + user_defined_words[w] = parts[2] + } else { + built_in_words[w] = parts[2] + } + } + + # Sort built-in words manually because I'm picky + n = 0 + for (w in built_in_words) { + sorted_words[n++] = w + } + + for (i = 0; i < n; i++) { + for (j = i + 1; j < n; j++) { + if (sorted_words[i] > sorted_words[j]) { + temp = sorted_words[i] + sorted_words[i] = sorted_words[j] + sorted_words[j] = temp + } + } + } + + # First print the built-in words + for (i = 0; i < n; i++) { + print sorted_words[i] ": " built_in_words[sorted_words[i]] + } + + # Then print the user-defined words + for (w in user_defined_words) { + print w ": " user_defined_words[w] " ( User-defined )" + } +} \ No newline at end of file |