about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <{ID}+{username}@users.noreply.github.com>2025-04-10 16:53:10 -0400
committerelioat <{ID}+{username}@users.noreply.github.com>2025-04-10 16:53:10 -0400
commit033f1f1196f741e3e943ad51bfc457226649183e (patch)
tree8388427c82798e4b4e52c0f2f033b43cf99881bf
parentd93fd92a2a92b3b9b3e87911e896a40d728d31a6 (diff)
downloadtour-033f1f1196f741e3e943ad51bfc457226649183e.tar.gz
*
-rw-r--r--awk/scheme/scheme/TODO.txt69
-rwxr-xr-xawk/scheme/scheme/bin/compiler.awk153
-rwxr-xr-xawk/scheme/scheme/bin/vm.awk210
-rw-r--r--awk/scheme/scheme/examples/lambda.test.scm12
4 files changed, 390 insertions, 54 deletions
diff --git a/awk/scheme/scheme/TODO.txt b/awk/scheme/scheme/TODO.txt
new file mode 100644
index 0000000..31723a4
--- /dev/null
+++ b/awk/scheme/scheme/TODO.txt
@@ -0,0 +1,69 @@
+Scheme Interpreter TODO
+=====================
+
+Current State:
+-------------
+- Basic arithmetic operations (+,-,*,/) are working
+- Let expressions with simple bindings are working
+- Function definitions (define) and lambda expressions are partially implemented
+- Stack-based virtual machine with environment for variable bindings
+
+Recent Changes:
+--------------
+1. Fixed function table array naming conflicts (func_name -> func_def_names)
+2. Modified vm_get_value to handle function names correctly
+3. Attempted to fix argument handling in function calls
+4. Modified lambda compilation to avoid pushing function name twice
+
+Current Issues:
+--------------
+1. Function Definitions (define):
+   - Function calls are failing with "ADD requires numeric operands"
+   - Arguments may not be properly passed to functions
+   - Function environment may not be properly set up
+
+2. Lambda Expressions:
+   - Direct lambda calls are failing
+   - Lambda bindings in let expressions are failing
+   - Function environment for lambda parameters may not be correct
+
+Next Steps:
+-----------
+1. Debug Function Calls:
+   - Add detailed debug logging in vm_call_function
+   - Verify argument handling and environment setup
+   - Check if function code is being properly stored and retrieved
+
+2. Fix Function Environment:
+   - Review how parameters are stored in the environment
+   - Ensure environment is properly restored after function calls
+   - Verify parameter values are correctly passed to functions
+
+3. Improve Lambda Support:
+   - Review lambda compilation process
+   - Ensure lambda functions are properly stored and called
+   - Fix environment handling for lambda parameters
+
+4. Testing Strategy:
+   - Create test cases for each type of function call
+   - Add debug output to track stack and environment state
+   - Verify each step of function compilation and execution
+
+5. Code Cleanup:
+   - Review and document function handling code
+   - Ensure consistent naming and error handling
+   - Add comments explaining complex operations
+
+Technical Notes:
+---------------
+- Function calls use a combination of LOOKUP, GET_VALUE, and CALL instructions
+- Environment stack handles variable bindings and function parameters
+- Function code is stored in FUNCTIONS table with unique names
+- Lambda functions use __lambda_N naming scheme
+
+Debugging Tips:
+--------------
+1. Enable DEBUG=1 to see detailed execution logs
+2. Check stack contents before and after each operation
+3. Verify environment state during function calls
+4. Monitor function code storage and retrieval 
\ No newline at end of file
diff --git a/awk/scheme/scheme/bin/compiler.awk b/awk/scheme/scheme/bin/compiler.awk
index db48388..debaa2c 100755
--- a/awk/scheme/scheme/bin/compiler.awk
+++ b/awk/scheme/scheme/bin/compiler.awk
@@ -232,17 +232,36 @@ function compile_primitive_call(op, args, arg_array, nargs, i) {
     debug("Primitive call: op=" op " args=" args)
     nargs = split_args(args, arg_array)
     
-    # First compile all arguments
-    for (i = 1; i <= nargs; i++) {
-        compile_expr(arg_array[i])
+    # Check if this is a lambda function call
+    if (substr(op, 1, 1) == "(") {
+        # This is a lambda function call
+        # First compile the lambda function
+        compile_expr(op)
+        
+        # Then compile all arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
+        
+        # Call the function
+        print "CALL __lambda_" (next_label - 1)
+        return
     }
     
     # Then emit appropriate operation
     if (op == "+") {
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         for (i = 1; i < nargs; i++)
             print "ADD"
     }
     else if (op == "-") {
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         if (nargs == 1) {
             print "PUSH_CONST N:0"
             print "SWAP"
@@ -251,41 +270,71 @@ function compile_primitive_call(op, args, arg_array, nargs, i) {
             print "SUB"
     }
     else if (op == "*") {
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         for (i = 1; i < nargs; i++)
             print "MUL"
     }
     else if (op == "cons") {
         if (nargs != 2) error("cons requires 2 arguments")
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         print "CONS"
     }
     else if (op == "car") {
         if (nargs != 1) error("car requires 1 argument")
+        # Compile argument
+        compile_expr(arg_array[1])
         print "CAR"
     }
     else if (op == "cdr") {
         if (nargs != 1) error("cdr requires 1 argument")
+        # Compile argument
+        compile_expr(arg_array[1])
         print "CDR"
     }
     else if (op == "<") {
         if (nargs != 2) error("< requires 2 arguments")
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         print "LT"
     }
     else if (op == "=") {
         if (nargs != 2) error("= requires 2 arguments")
+        # Compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
         print "EQ"
     }
     else {
         # Function call for user-defined functions
         debug("Function call: " op)
-        print "CALL " op
+        # Look up the function name
+        print "LOOKUP " op
+        # Get the actual function name
+        print "GET_VALUE"
+        # Then compile arguments
+        for (i = 1; i <= nargs; i++) {
+            compile_expr(arg_array[i])
+        }
+        # Call the function
+        print "CALL"
     }
 }
 
 # Splits let bindings into individual variable/value pairs
-function split_bindings(bindings, binding_array, count, current, paren_count, i, c) {
+function split_bindings(bindings, binding_array, count, current, paren_count, i, c, in_lambda) {
     count = 0
     current = ""
     paren_count = 0
+    in_lambda = 0
     
     for (i = 1; i <= length(bindings); i++) {
         c = substr(bindings, i, 1)
@@ -293,14 +342,14 @@ function split_bindings(bindings, binding_array, count, current, paren_count, i,
         # Track nested parentheses
         if (c == "(") {
             paren_count++
-            if (paren_count == 1) {
+            if (paren_count == 1 && !in_lambda) {
                 current = ""  # Start new binding
                 continue
             }
         }
         if (c == ")") {
             paren_count--
-            if (paren_count == 0) {
+            if (paren_count == 0 && !in_lambda) {
                 # End of binding
                 binding_array[++count] = current
                 current = ""
@@ -308,6 +357,11 @@ function split_bindings(bindings, binding_array, count, current, paren_count, i,
             }
         }
         
+        # Track if we're inside a lambda expression
+        if (substr(bindings, i, 7) == "lambda ") {
+            in_lambda = 1
+        }
+        
         # Only add character if we're inside a binding
         if (paren_count > 0) {
             current = current c
@@ -346,17 +400,34 @@ function compile_let(args, bindings, body, binding_array, nbindings, i, var, val
     nbindings = split_bindings(bindings, binding_array)
     for (i = 1; i <= nbindings; i++) {
         debug("Processing binding: " binding_array[i])
-        split(binding_array[i], binding_parts, " ")
-        var = binding_parts[1]
-        val = binding_parts[2]
+        
+        # Find the variable name (everything up to the first space)
+        var = binding_array[i]
+        sub(/ .*$/, "", var)
+        
+        # Find the value (everything after the first space)
+        val = binding_array[i]
+        sub(/^[^ ]+ /, "", val)
         
         debug("Binding var: " var " val: " val)
         
         # Compile the value
-        compile_expr(val)
-        
-        # Store in environment
-        print "STORE " var
+        if (substr(val, 1, 1) == "(") {
+            # Handle lambda or other compound expressions
+            if (substr(val, 2, 6) == "lambda") {
+                # This is a lambda expression
+                # First compile the lambda
+                compile_lambda(substr(val, 8))  # Skip "(lambda "
+                # Store the function name in the environment
+                print "STORE " var
+            } else {
+                compile_expr(val)
+                print "STORE " var
+            }
+        } else {
+            compile_expr(val)
+            print "STORE " var
+        }
     }
     
     # Compile the body
@@ -420,6 +491,58 @@ function compile_define(args, name, params, body, param_array, nparams, i, paren
     }
 }
 
+# Compiles lambda expressions (anonymous functions)
+function compile_lambda(args, params, body, param_array, nparams, i, lambda_name) {
+    # Generate a unique name for the lambda function
+    lambda_name = "__lambda_" next_label++
+    
+    # Split into parameters and body
+    if (substr(args, 1, 1) != "(") error("Malformed lambda expression")
+    
+    # Find matching closing parenthesis for parameters
+    paren_count = 1
+    i = 2
+    while (paren_count > 0 && i <= length(args)) {
+        if (substr(args, i, 1) == "(") paren_count++
+        if (substr(args, i, 1) == ")") paren_count--
+        i++
+    }
+    if (paren_count > 0) error("Unmatched parenthesis in lambda parameters")
+    
+    params = substr(args, 2, i - 3)  # Remove parentheses
+    body = substr(args, i)
+    
+    # Trim whitespace from body
+    sub(/^[ \t\n]+/, "", body)
+    sub(/[ \t\n]+$/, "", body)
+    
+    debug("Lambda parameters: " params)
+    debug("Lambda body: " body)
+    
+    # Create function label
+    print "LABEL " lambda_name
+    
+    # Process parameters
+    nparams = split(params, param_array, " ")
+    for (i = 1; i <= nparams; i++) {
+        print "STORE " param_array[i]
+    }
+    
+    # Compile function body
+    compile_expr(body)
+    
+    # Clean up parameters and return
+    for (i = nparams; i >= 1; i--) {
+        print "POP_ENV"
+    }
+    print "RETURN"
+    
+    # Only push the function name if we're not in a direct call
+    if (!(args ~ /^\([^)]*\)[^(]*$/)) {
+        print "PUSH_CONST S:" lambda_name
+    }
+}
+
 # Main expression compiler - dispatches based on expression type
 function compile_expr(expr,    split_result, op, args) {
     debug("Compiling expression: " expr)
@@ -453,6 +576,8 @@ function compile_expr(expr,    split_result, op, args) {
             compile_define(args)
         } else if (op == "let") {
             compile_let(args)
+        } else if (op == "lambda") {
+            compile_lambda(args)
         } else {
             compile_primitive_call(op, args)
         }
diff --git a/awk/scheme/scheme/bin/vm.awk b/awk/scheme/scheme/bin/vm.awk
index 2c56fda..4e7d2c7 100755
--- a/awk/scheme/scheme/bin/vm.awk
+++ b/awk/scheme/scheme/bin/vm.awk
@@ -28,10 +28,10 @@ BEGIN {
     env_size = 0     # Current size of environment stack
     
     # Function table for storing defined functions
-    delete func_name  # Function names
-    delete func_pc    # Entry points
-    delete func_code  # Function bodies
-    func_size = 0     # Number of defined functions
+    delete func_def_names  # Function names
+    delete func_def_pc     # Entry points
+    delete func_def_code   # Function bodies
+    func_def_size = 0      # Number of defined functions
     
     # Call stack for function returns
     call_stack_ptr = 0
@@ -54,9 +54,9 @@ BEGIN {
                     debug("Code: " code)
                     
                     # Store function in function table
-                    func_name[func_size] = name
-                    func_code[func_size] = code
-                    func_size++
+                    func_def_names[func_def_size] = name
+                    func_def_code[func_def_size] = code
+                    func_def_size++
                 }
             } while ((getline line < STATE_FILE) > 0)
             close(STATE_FILE)
@@ -97,6 +97,16 @@ BEGIN {
         }
     }
 
+    # Register built-in functions
+    FUNCTIONS["+"] = "add"
+    FUNCTIONS["-"] = "subtract"
+    FUNCTIONS["*"] = "multiply"
+    FUNCTIONS["/"] = "divide"
+    FUNCTIONS["="] = "equals"
+    FUNCTIONS["<"] = "less_than"
+    FUNCTIONS[">"] = "greater_than"
+    FUNCTIONS["add1"] = "add_one"
+
     # Track if VM halted normally (vs error)
     normal_exit = 0
 }
@@ -353,6 +363,9 @@ function execute(instr) {
     else if (op == "RETURN") {
         vm_return()
     }
+    else if (op == "GET_VALUE") {
+        vm_get_value()
+    }
     else {
         error("Unknown instruction: " op)
     }
@@ -404,10 +417,26 @@ function vm_store(name) {
         }
     }
     
+    # Handle lambda functions
+    val = peek()
+    if (isSymbol(val)) {
+        func_name = getValue(val)
+        if (func_name ~ /^__lambda_/) {
+            # Store the function code under the new name
+            FUNCTIONS[name] = FUNCTIONS[func_name]
+            # Store the new name in the environment
+            env_name[env_size] = name
+            env_val[env_size] = makeValue(T_SYMBOL, name)
+            env_size++
+            return
+        }
+    }
+    
     # Add to environment
     env_name[env_size] = name
     env_val[env_size] = peek()
     env_size++
+    
     debug("Environment after store:")
     dump_env()
 }
@@ -428,20 +457,22 @@ function vm_pop_env() {
 }
 
 # Variable lookup implementation
-function vm_lookup(name, i, global_name) {
+function vm_lookup(name, i, global_name, val) {
     debug("Looking up " name " in environment of size: " env_size)
     dump_env()
     
+    # Check if it's a function (built-in or user-defined)
+    if (name in FUNCTIONS) {
+        debug("Found function: " name)
+        push(makeValue(T_SYMBOL, name))
+        return
+    }
+    
     # Try global name first, then local
     global_name = "__global_" name
     for (i = env_size - 1; i >= 0; i--) {
-        if (env_name[i] == global_name) {
-            debug("Found global " name " = " env_val[i] " at position " i)
-            push(env_val[i])
-            return
-        }
-        if (env_name[i] == name) {
-            debug("Found local " name " = " env_val[i] " at position " i)
+        if (env_name[i] == global_name || env_name[i] == name) {
+            debug("Found " name " = " env_val[i] " at position " i)
             push(env_val[i])
             return
         }
@@ -471,44 +502,56 @@ function vm_define_function(name, start_pc) {
 }
 
 # Function call implementation
-function vm_call_function(name, code_lines, j, saved_pc, saved_env_size, arg, param_name) {
-    debug("Calling function: " name)
+function vm_call_function(func_name, code_lines, j, saved_pc, saved_env_size, arg, param_name) {
+    debug("Calling function: " func_name)
     
-    if (!(name in FUNCTIONS)) {
-        error("Undefined function: " name)
+    # If name is a symbol, get its value
+    if (isSymbol(func_name)) {
+        func_name = getValue(func_name)
     }
     
-    # Get argument before modifying program
-    arg = pop()
-    debug("Function argument: " arg)
+    # Handle anonymous functions
+    if (func_name ~ /^__lambda_/) {
+        if (!(func_name in FUNCTIONS)) {
+            error("Undefined lambda function: " func_name)
+        }
+    } else if (!(func_name in FUNCTIONS)) {
+        error("Undefined function: " func_name)
+    }
     
     saved_pc = pc
     saved_env_size = env_size
     
     # Split function code into lines
-    split(FUNCTIONS[name], code_lines, "\n")
+    split(FUNCTIONS[func_name], code_lines, "\n")
+    
+    # Add function code to program at current position
+    for (j in code_lines) {
+        program[pc + j - 1] = code_lines[j]
+    }
     
-    # Get parameter name from STORE instruction
+    # Check if this is a parameterized function
     if (code_lines[1] ~ /^STORE /) {
+        # This is a parameterized function (lambda)
+        # Get parameter name from STORE instruction
         param_name = substr(code_lines[1], 7)
         debug("Found parameter name: " param_name)
+        
+        # Get argument from stack
+        arg = pop()
+        debug("Function argument: " arg)
+        
+        # Create new environment frame
+        debug("Creating new environment frame at size: " env_size)
+        env_name[env_size] = param_name
+        env_val[env_size] = arg
+        env_size++
     } else {
-        error("Function missing parameter definition")
-    }
-    
-    # Create new environment frame
-    debug("Creating new environment frame at size: " env_size)
-    env_name[env_size] = param_name
-    env_val[env_size] = arg
-    env_size++
-    
-    # Add function code to program
-    for (j in code_lines) {
-        program[length(program)] = code_lines[j]
+        # This is a built-in function or non-parameterized function
+        debug("Calling non-parameterized function: " func_name)
     }
     
     # Save return info and jump to function
-    pc = length(program) - length(code_lines)
     call_stack[++call_stack_ptr] = saved_pc
     env_stack[call_stack_ptr] = saved_env_size
     
@@ -559,9 +602,9 @@ function lookup_no_error(name,    i) {
 # State persistence implementation
 function save_state() {
     debug("Saving state to: " STATE_FILE)
-    for (i = 0; i < func_size; i++) {
-        debug("Saving function: " func_name[i])
-        print "FUNC " func_name[i] " " func_code[i] > STATE_FILE
+    for (i = 0; i < func_def_size; i++) {
+        debug("Saving function: " func_def_names[i])
+        print "FUNC " func_def_names[i] " " func_def_code[i] > STATE_FILE
     }
     close(STATE_FILE)
 
@@ -574,4 +617,91 @@ function save_state() {
         }
     }
     close(ENV_STATE_FILE)
+}
+
+# Built-in function implementations
+function equals() {
+    if (stack_ptr < 2) error("= requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("= requires numeric operands")
+    result = (getValue(val1) == getValue(val2)) ? 1 : 0
+    push(makeValue(T_BOOLEAN, result))
+}
+
+function less_than() {
+    if (stack_ptr < 2) error("< requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("< requires numeric operands")
+    result = (getValue(val1) < getValue(val2)) ? 1 : 0
+    push(makeValue(T_BOOLEAN, result))
+}
+
+function greater_than() {
+    if (stack_ptr < 2) error("> requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("> requires numeric operands")
+    result = (getValue(val1) > getValue(val2)) ? 1 : 0
+    push(makeValue(T_BOOLEAN, result))
+}
+
+function add() {
+    if (stack_ptr < 2) error("+ requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("+ requires numeric operands")
+    result = getValue(val1) + getValue(val2)
+    push(makeValue(T_NUMBER, result))
+}
+
+function subtract() {
+    if (stack_ptr < 2) error("- requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("- requires numeric operands")
+    result = getValue(val1) - getValue(val2)
+    push(makeValue(T_NUMBER, result))
+}
+
+function multiply() {
+    if (stack_ptr < 2) error("* requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("* requires numeric operands")
+    result = getValue(val1) * getValue(val2)
+    push(makeValue(T_NUMBER, result))
+}
+
+function divide() {
+    if (stack_ptr < 2) error("/ requires two operands")
+    val2 = pop()
+    val1 = pop()
+    if (!isNumber(val1) || !isNumber(val2)) error("/ requires numeric operands")
+    if (getValue(val2) == 0) error("Division by zero")
+    result = getValue(val1) / getValue(val2)
+    push(makeValue(T_NUMBER, result))
+}
+
+function add_one() {
+    if (stack_ptr < 1) error("add1 requires one operand")
+    val = pop()
+    if (!isNumber(val)) error("add1 requires numeric operand")
+    result = getValue(val) + 1
+    push(makeValue(T_NUMBER, result))
+}
+
+# Get value from top of stack
+function vm_get_value() {
+    val = peek()
+    if (isSymbol(val)) {
+        name = getValue(val)
+        # If it's a function name, just push the name directly
+        if (name in FUNCTIONS) {
+            push(name)
+        } else {
+            push(makeValue(T_SYMBOL, name))
+        }
+    }
 }
\ No newline at end of file
diff --git a/awk/scheme/scheme/examples/lambda.test.scm b/awk/scheme/scheme/examples/lambda.test.scm
new file mode 100644
index 0000000..1f2bb09
--- /dev/null
+++ b/awk/scheme/scheme/examples/lambda.test.scm
@@ -0,0 +1,12 @@
+; Test lambda function support
+((lambda (x) (+ x 1)) 41)  ; Should return 42
+
+; Test lambda with multiple parameters
+((lambda (x y) (+ x y)) 20 22)  ; Should return 42
+
+; Test lambda in let expression
+(let ((add1 (lambda (x) (+ x 1))))
+  (add1 41))  ; Should return 42
+
+; Test nested lambda
+((lambda (x) ((lambda (y) (+ x y)) 1)) 41)  ; Should return 42 
\ No newline at end of file