summary refs log tree commit diff stats
path: root/lib/system/sets.nim
Commit message (Expand)AuthorAgeFilesLines
* more fixes for --cpu:avr [backport] (#12748)Andreas Rumpf2019-11-271-1/+1
* [backport] fix #12395 (#12590)Miran2019-11-041-3/+3
* use whileBrent Pedersen2019-09-111-4/+5
* add amd64Brent Pedersen2019-09-111-1/+1
* only do 64bts on x86Brent Pedersen2019-09-111-3/+4
* spacingBrent Pedersen2019-09-091-1/+1
* count 64 bits at a timeBrent Pedersen2019-09-091-3/+7
* code cleanup (#10874)Arne Döring2019-03-281-9/+16
* sets: avoid calling countBits32 for 0 (#10619)Brent Pedersen2019-02-131-3/+3
* removes deprecated T/P typesAraq2018-11-161-1/+0
* prepare Nim codebase for upcoming parser changesAndreas Rumpf2016-07-151-1/+1
* fixes #3135Araq2015-08-021-3/+3
* lib/system/g-w - Dropped 'T' from typespdw2015-06-041-2/+3
* the big renamefest: first stepsAraq2014-08-221-1/+1
* Resolve type mismatches.EXetoC2014-05-131-2/+2
* Removes executable bit for text files.Grzegorz Adam Hankiewicz2013-03-161-0/+0
* year 2012 for most copyright headersAraq2012-01-021-1/+1
* fixed pango/pangoutils new wrappersAndreas Rumpf2010-02-261-0/+0
* continued work on html/xmlparserrumpf_a@web.de2010-02-141-0/+0
* added tools and web dirsAndreas Rumpf2009-09-151-72/+12
* version0.7.10Andreas Rumpf2009-06-081-0/+88
a> 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 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 )"
    }
}