diff options
author | elioat <{ID}+{username}@users.noreply.github.com> | 2025-04-10 16:53:10 -0400 |
---|---|---|
committer | elioat <{ID}+{username}@users.noreply.github.com> | 2025-04-10 16:53:10 -0400 |
commit | 033f1f1196f741e3e943ad51bfc457226649183e (patch) | |
tree | 8388427c82798e4b4e52c0f2f033b43cf99881bf | |
parent | d93fd92a2a92b3b9b3e87911e896a40d728d31a6 (diff) | |
download | tour-033f1f1196f741e3e943ad51bfc457226649183e.tar.gz |
*
-rw-r--r-- | awk/scheme/scheme/TODO.txt | 69 | ||||
-rwxr-xr-x | awk/scheme/scheme/bin/compiler.awk | 153 | ||||
-rwxr-xr-x | awk/scheme/scheme/bin/vm.awk | 210 | ||||
-rw-r--r-- | awk/scheme/scheme/examples/lambda.test.scm | 12 |
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 |