From 08d3c1545bf019f57903b9880e048f67940af513 Mon Sep 17 00:00:00 2001 From: Maxwell Jeffress Date: Thu, 2 Oct 2025 12:41:11 +1000 Subject: [PATCH] Function arguments --- src/data/data.cpp | 46 ++++++++++++++++++++++--------- src/data/data.h | 7 +++-- src/defs/defs.h | 5 ++++ src/executor/executor.cpp | 19 ++++++++++++- src/main.cpp | 2 ++ src/modules/function/function.cpp | 37 +++++++++++++++++++------ src/modules/let/let.cpp | 4 +-- src/parser/parser.cpp | 15 ++++++++-- src/repl/repl.cpp | 45 ++++++++++++++++++++++++++---- tests/func.kyn | 6 ++-- 10 files changed, 148 insertions(+), 38 deletions(-) diff --git a/src/data/data.cpp b/src/data/data.cpp index 1f98962..7725570 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -7,21 +7,41 @@ #include "../error/error.h" namespace data { - std::map vars; - std::map> functions; - void modifyValue(std::string key, Value value) { - if (vars.find(key) != vars.end()) { - vars[key] = value; - } else { - error("Could not find variable " + key + "to modify (try defining first with let?)"); + std::vector> scopes; + std::map functions; + + void initScopes() { + scopes.clear(); + scopes.push_back({}); // Global scope + } + + void pushScope() { + scopes.push_back({}); + } + + void popScope() { + if (!scopes.empty()) { + scopes.pop_back(); } } - Value getValue(std::string key) { - if (vars.find(key) != vars.end()) { - return vars[key]; - } else { - error("Could not find variable " + key + " to access (try defining first with let?)"); - return Value(); + + void modifyValue(std::string key, Value value) { + for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) { + if (it->count(key)) { + (*it)[key] = value; + return; + } } + error("Could not find variable " + key + " to modify (try defining first with let?)"); + } + + Value getValue(std::string key) { + for (auto it = scopes.rbegin(); it != scopes.rend(); ++it) { + if (it->count(key)) { + return (*it)[key]; + } + } + error("Could not find variable " + key + " to access (try defining first with let?)"); + return Value(); } } diff --git a/src/data/data.h b/src/data/data.h index 5182be1..369186d 100644 --- a/src/data/data.h +++ b/src/data/data.h @@ -6,8 +6,11 @@ #include "../defs/defs.h" namespace data { - extern std::map vars; - extern std::map> functions; + extern std::vector> scopes; + extern std::map functions; void modifyValue(std::string key, Value value); Value getValue(std::string key); + void pushScope(); + void popScope(); + void initScopes(); } diff --git a/src/defs/defs.h b/src/defs/defs.h index fa9cf29..b3cf323 100644 --- a/src/defs/defs.h +++ b/src/defs/defs.h @@ -17,6 +17,11 @@ struct Instruction; typedef std::vector InstructionGroup; +struct Function { + std::vector arg_names; + InstructionGroup body; +}; + struct Varname { std::string key; Varname(std::string in); diff --git a/src/executor/executor.cpp b/src/executor/executor.cpp index 984c0cb..e4f90fb 100644 --- a/src/executor/executor.cpp +++ b/src/executor/executor.cpp @@ -72,10 +72,27 @@ Value execute(Instruction inst) { if (!inst.args.empty()) { const std::string& name = inst.args[0].real; if (data::functions.count(name)) { + const auto& func = data::functions.at(name); + + if (func.arg_names.size() != inst.args.size() - 1) { + error("Function " + name + " expects " + std::to_string(func.arg_names.size()) + " arguments, but got " + std::to_string(inst.args.size() - 1)); + return Value(); + } + + data::pushScope(); + + for (size_t i = 0; i < func.arg_names.size(); ++i) { + Value arg_val = inst.args[i+1]; + data::scopes.back()[func.arg_names[i]] = arg_val; + } + Value return_val; - for (const auto& body_inst : data::functions.at(name)) { + for (const auto& body_inst : func.body) { return_val = execute(body_inst); } + + data::popScope(); + return return_val; } } diff --git a/src/main.cpp b/src/main.cpp index 036399b..5bf93bf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,10 @@ #include "repl/repl.h" #include #include +#include "data/data.h" int main(int argc, char** argv) { + data::initScopes(); if (argc < 2) { repl(); } else { diff --git a/src/modules/function/function.cpp b/src/modules/function/function.cpp index cfa27ba..9c31e05 100644 --- a/src/modules/function/function.cpp +++ b/src/modules/function/function.cpp @@ -5,21 +5,42 @@ #include Value modules::fndef(std::vector args) { - if (args.size() < 2) { + if (args.size() < 2) { // At least a name and a body error("Syntax error: function statement requires a name and a body"); return Value(); } - if (args[0].valtype != ValueType::Processed) { - error("Syntax error: function name must be a processed value"); - return Value(); - } - if (args[1].valtype != ValueType::InstructionGroup) { + + // Last arg is the body + Value body_val = args.back(); + if (body_val.valtype != ValueType::InstructionGroup) { error("Syntax error: function body must be an instruction group"); return Value(); } - std::string func_name = args[0].processed->args[0].real; - data::functions[func_name] = args[1].instructionGroup; + // First arg is the name + Value name_val = args[0]; + if (name_val.valtype != ValueType::Real) { + error("Syntax error: function name must be a real value"); + return Value(); + } + std::string func_name = name_val.real; + + // In-between args are the argument names + std::vector arg_names; + for (size_t i = 1; i < args.size() - 1; ++i) { + if (args[i].valtype != ValueType::Real) { + error("Syntax error: function argument names must be real values"); + return Value(); + } + arg_names.push_back(args[i].real); + } + + // Create and store the function + Function new_func; + new_func.arg_names = arg_names; + new_func.body = body_val.instructionGroup; + + data::functions[func_name] = new_func; return Value(""); } diff --git a/src/modules/let/let.cpp b/src/modules/let/let.cpp index 413754c..791ef31 100644 --- a/src/modules/let/let.cpp +++ b/src/modules/let/let.cpp @@ -16,7 +16,7 @@ Value modules::let(std::vector values) { if (values.size() > 1) { if (values[1].valtype == ValueType::Real && values[1].real == "=") { if (values.size() > 2) { - data::vars[varName] = values[2]; + data::scopes.back()[varName] = values[2]; } else { error("Expecting value after token '=' (in let module)"); } @@ -24,7 +24,7 @@ Value modules::let(std::vector values) { error("Expecting token '=' (in let module)"); } } else { - data::vars[varName] = Value(""); + data::scopes.back()[varName] = Value(""); } } else { error("Expecting variable name (in let module)"); diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index 3ac4d99..764b7ab 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -147,9 +147,18 @@ std::vector parse(std::string program) { continue; } - // 1. Function name - std::string name = line.substr(3, block_start - 3); - function_inst.args.push_back(Value(Instruction(split(trim(name))))); + // 1. Function signature (name + args) + std::string signature_str = line.substr(3, block_start - 3); + std::vector signature_parts = split(trim(signature_str)); + + if (signature_parts.empty()) { + error("Function definition is missing a name."); + continue; + } + + for(const auto& part : signature_parts) { + function_inst.args.push_back(part); + } // 2. Find 'then' block size_t block_end = 0; diff --git a/src/repl/repl.cpp b/src/repl/repl.cpp index f113e0d..b806308 100644 --- a/src/repl/repl.cpp +++ b/src/repl/repl.cpp @@ -4,18 +4,51 @@ #include "../defs/defs.h" #include #include +#include "../data/data.h" void repl() { + data::initScopes(); inReplMode = true; std::cout << "Kyn REPL v0.0.1" << std::endl; std::cout << "Type 'exit' to exit" << std::endl; - std::string buf; + + std::string full_command; + std::string line_buf; + int brace_level = 0; + while (true) { - std::cout << "kyn> "; - getline(std::cin, buf); - std::vector parsed = parse(buf); - for (Instruction inst : parsed) { - execute(inst); + if (brace_level == 0) { + std::cout << "kyn> "; + } else { + std::cout << ".. > "; + } + + if (!getline(std::cin, line_buf)) { + break; // Handle EOF (Ctrl+D) + } + + for (char c : line_buf) { + if (c == '{') { + brace_level++; + } else if (c == '}') { + brace_level--; + } + } + + if (!full_command.empty()) { + full_command += "\n"; + } + full_command += line_buf; + + if (brace_level <= 0 && !full_command.empty()) { + if (full_command.find_first_not_of(" \t\n\r") != std::string::npos) { + std::vector parsed = parse(full_command); + for (Instruction inst : parsed) { + execute(inst); + } + } + full_command.clear(); + brace_level = 0; } } } diff --git a/tests/func.kyn b/tests/func.kyn index b72cc67..ed37123 100644 --- a/tests/func.kyn +++ b/tests/func.kyn @@ -1,5 +1,5 @@ -fun dingus { - println "hello" +fun dingus in { + println $in } -dingus +dingus "hi there"