about summary refs log blame commit diff stats
path: root/awk/forth/f.awk
blob: eed9774c7bf85ba917707941b91d84a4ca28530a (plain) (tree)
1
2
3
4
5
6
7
8
9
10



                          


                                                        


                                        
                                                





















                              



                          





























                                    




















                                                                                 



                                                                         


                                                                      
 






                                      

 
                  











                                      








                                                                         

                               

                  










                                                   
     



                                                            

 
                   







                                          
 
                

                               

 
                 

                               

 
                 




                               

 
                
                               








                  
                               



                




                               


                 
                               







                  
                               
                  
                               

                      
                             


                                         



                     
                               
                  
                               
                     



                        
                               



                
                               




                        
                               





                       
                               




                       
                      
                               

                    

 

                

 


                                                

              

                        
     

                                                      
     

 
                          

                             




                                                        
         
























                                                          
              
                                                                






                    

                            
                 
                                                  



                                 
 
                            
                                   

                                    
                                     
                                        
                                                                  
                 

                                                    
             

         
 
#!/usr/bin/awk -f

# Forth interpreter in AWK

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["+"] = "+"
    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"
    words["if"] = "if"
    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"
    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"
    desc["if"] = "( flag -- ) Begin conditional execution"
    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"
    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

    # Mark these as compile-only words
    compile_only["if"] = 1
    compile_only["then"] = 1
    compile_only["else"] = 1
}

# Stack operations
function push(value) {
    stack[++top] = value
}

function pop() {
    if (top < 0) {
        print "Error: Stack underflow"
        return 0
    }
    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()
    first = pop()
    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)
    }
    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)
}

# Handler 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 dup() {
    if (!check_stack(1)) return
    push(stack[top])
}

function over() {
    if (!check_stack(2)) return
    push(stack[top - 1])
}

function swap() {
    if (!check_stack(2)) return
    temp = pop()
    second = pop()
    push(temp)
    push(second)
}

function rot() {
    if (!check_stack(3)) return
    third = pop()
    second = pop()
    first = pop()
    push(second)
    push(third)
    push(first)
}

function drop() {
    if (!check_stack(1)) return
    top--
}

function nip() {
    if (!check_stack(2)) return
    temp = stack[top]
    drop()
    drop()
    push(temp)
}

function tuck() {
    if (!check_stack(2)) return
    temp = pop()
    second = pop()
    push(temp)
    push(second)
    push(temp)
}

function roll() {
    if (!check_stack(1)) return
    n = int(pop())
    if (!check_stack(n)) return
    if (n <= 0) return

    temp = stack[top - n + 1]
    for (i = top - n + 1; i < top; i++) {
        stack[i] = stack[i + 1]
    }
    stack[top] = temp
}

function pick() {
    if (!check_stack(1)) return
    n = int(pop())
    if (!check_stack(n)) return
    if (n < 0) return
    push(stack[top - n])
}

function negate() {
    if (!check_stack(1)) return
    push(-pop())
}

function abs() {
    if (!check_stack(1)) return
    n = pop()
    push(n < 0 ? -n : n)
}

function max() {
    if (!check_stack(2)) return
    b = pop()
    a = pop()
    push(a > b ? a : b)
}

function min() {
    if (!check_stack(2)) return
    b = pop()
    a = pop()
    push(a < b ? a : b)
}

function print_top() {
    if (!check_stack(1)) return
    print stack[top]
    drop()
}

function bye() {
    exit
}

function see(word) {
    if (!(word in words)) {
        print "Error: Word '" word "' not found"
        return
    }
    if (word in desc) {
        print desc[word]
    }
    if (word in raw_definitions) {
        print ": " word " " raw_definitions[word] " ;"
    }
}

# Main processing function
function execute_word(word) {
    if (word in handlers) {
        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 (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 '" handler "' not implemented"
            return 0
        }
        return 1
    }
    return 0
}

# Process each line of input
{
    if (NF > 0) {
        # Remove comments and normalize whitespace
        gsub(/\(.*\)/, "")
        gsub(/^[[:space:]]+/, "")
        gsub(/[[:space:]]+$/, "")
        gsub(/[[:space:]]+/, " ")

        # Process each token
        for (i = 1; i <= NF; i++) {
            if ($i ~ /^-?[0-9]+$/) {
                push($i)
            } else if ($i in words) {
                if (!execute_word($i)) {
                    print "Error: Failed to execute word '" $i "'"
                }
            } else {
                print "Error: Unknown word '" $i "'"
            }
        }
    }
}