Structs (IN BETA)

This commit is contained in:
2025-10-04 20:37:55 +10:00
parent b3950dcd62
commit 0057d73215
13 changed files with 322 additions and 10 deletions

View File

@@ -9,6 +9,7 @@
namespace data {
std::vector<std::map<std::string, Value>> scopes;
std::map<std::string, Function> functions;
std::map<std::string, Struct> structs;
void initScopes() {
scopes.clear();

View File

@@ -8,6 +8,7 @@
namespace data {
extern std::vector<std::map<std::string, Value>> scopes;
extern std::map<std::string, Function> functions;
extern std::map<std::string, Struct> structs;
void modifyValue(std::string key, Value value);
Value getValue(std::string key);
void pushScope();

View File

@@ -5,7 +5,6 @@
#include <memory>
#include <utility>
#include "../datatypes/lists/lists.h"
#include "../utils/trim/trim.h"
InstructionType strToInstructionType(std::string in) {
if (in == "println") return InstructionType::Println;
@@ -21,6 +20,7 @@ InstructionType strToInstructionType(std::string in) {
else if (in == "concat") return InstructionType::Concat;
else if (in == "split") return InstructionType::Split;
else if (in == "file") return InstructionType::File;
else if (in == "struct") return InstructionType::Struct;
else return InstructionType::Variable;
}
@@ -118,7 +118,11 @@ Value::Value(InstructionGroup instgroup) : valtype(ValueType::InstructionGroup),
Value::Value(std::vector<Value> listval) : valtype(ValueType::List), list(std::move(listval)) {}
Value::Value(const Value& other) : valtype(other.valtype), string_val(other.string_val), int_val(other.int_val), double_val(other.double_val), instructionGroup(other.instructionGroup), list(other.list), varName(other.varName) {
Value::Value(std::map<std::string, Value> mapval) : valtype(ValueType::Map), map(std::move(mapval)) {}
Value::Value(Function func) : fn_val(func) {};
Value::Value(const Value& other) : valtype(other.valtype), string_val(other.string_val), int_val(other.int_val), double_val(other.double_val), instructionGroup(other.instructionGroup), list(other.list), map(other.map), varName(other.varName), fn_val(other.fn_val) {
if (other.processed) {
processed = std::make_unique<Instruction>(*other.processed);
}
@@ -138,6 +142,8 @@ Value& Value::operator=(const Value& other) {
varName = other.varName;
instructionGroup = other.instructionGroup;
list = other.list;
map = other.map;
fn_val = other.fn_val;
if (other.processed) {
processed = std::make_unique<Instruction>(*other.processed);
} else {
@@ -182,6 +188,18 @@ std::string Value::toString() const {
out += "])";
break;
}
case ValueType::Map: {
if (map.count("__name__")) {
out += "Struct<" + map.at("__name__").string_val + ">, members: {";
} else {
out += "Map, items: {";
}
for (const auto& pair : map) {
out += pair.first + ": " + pair.second.toString() + ", ";
}
out += "})";
break;
}
default:
out += "FIXME)";
break;

View File

@@ -3,14 +3,14 @@
#include <vector>
#include <string>
#include <memory>
#include <map>
enum class InstructionType {
None, Print, Println, Math, Let, Variable, Exit, If, While, Input, Compare, Function, Return, Concat, Split, File
None, Print, Println, Math, Let, Variable, Exit, If, While, Input, Compare, Function, Struct, Return, Concat, Split, File
};
enum class ValueType {
Processed, Variable, InstructionGroup, List, Map, String, Int, Double, Identifier
Processed, Variable, InstructionGroup, List, Map, String, Int, Double, Identifier, Custom
};
struct Instruction;
@@ -36,16 +36,20 @@ struct Value {
double double_val = 0.0;
InstructionGroup instructionGroup;
std::vector<Value> list;
std::map<std::string, Value> map;
bool is_return = false;
std::string toString() const;
Varname varName = Varname();
Function fn_val;
Value(std::string stringval);
Value(long long intval);
Value(double doubleval);
Value(Instruction instval);
Value(InstructionGroup instgroup);
Value(std::vector<Value> listval);
Value(std::map<std::string, Value> mapval);
Value(Varname var);
Value(Function func);
Value();
Value(const Value& other);
Value& operator=(const Value& other);
@@ -53,6 +57,11 @@ struct Value {
Value& operator=(Value&& other) = default;
};
struct Struct {
std::map<std::string, Value> values;
std::map<std::string, Function> functions;
};
struct Instruction {
InstructionType instruction = InstructionType::None;
std::vector<Value> args;

View File

@@ -14,6 +14,7 @@
#include "../modules/compare/compare.h"
#include "../modules/input/input.h"
#include "../modules/function/function.h"
#include "../modules/struct/struct.h"
#include "../modules/split/split.h"
#include "../modules/concat/concat.h"
#include "../modules/file/file.h"
@@ -35,6 +36,9 @@ Value execute(Instruction inst) {
if (inst.instruction == InstructionType::Function) {
return modules::fndef(inst.args);
}
if (inst.instruction == InstructionType::Struct) {
return modules::structdef(inst.args);
}
if (inst.instruction == InstructionType::Variable) {
auto& args = inst.args;
@@ -68,6 +72,48 @@ Value execute(Instruction inst) {
return return_val;
}
// Struct instantiation
if (args[0].valtype == ValueType::Identifier && data::structs.count(args[0].string_val)) {
std::string struct_name = args[0].string_val;
const auto& struct_def = data::structs.at(struct_name);
Value instance{std::map<std::string, Value>()};
instance.valtype = ValueType::Map;
instance.map["__name__"] = Value(struct_name);
for (const auto& pair : struct_def.values) {
instance.map[pair.first] = pair.second;
}
if (struct_def.functions.count("init")) {
const auto& init_func = struct_def.functions.at("init");
if (init_func.arg_names.size() != args.size() - 1) {
error("Struct " + struct_name + " init expects " + std::to_string(init_func.arg_names.size()) + " arguments, but got " + std::to_string(args.size() - 1));
return Value();
}
data::pushScope();
data::scopes.back()["self"] = instance;
for (size_t i = 0; i < init_func.arg_names.size(); ++i) {
Value arg_val = evaluate(args[i+1]);
data::scopes.back()[init_func.arg_names[i]] = arg_val;
}
Value return_val;
for (const auto& body_inst : init_func.body) {
return_val = execute(body_inst);
if (return_val.is_return) {
break; // Should not happen in init
}
}
// After init, self might have been modified.
instance = data::scopes.back()["self"];
data::popScope();
}
return instance;
}
// Indexed assignment: $var index = value
if (args.size() == 4 && args[2].valtype == ValueType::Identifier && args[2].string_val == "=") {
std::string var_name;
@@ -84,8 +130,18 @@ Value execute(Instruction inst) {
return handleListOp(args);
} else if (subject.valtype == ValueType::String) { // strings are Real
return handleStringOp(args);
} else if (subject.valtype == ValueType::Map) {
if (args[1].valtype != ValueType::Identifier) {
error("Struct member name must be an identifier.");
return Value();
}
std::string member_name = args[1].string_val;
Value new_val = evaluate(args[3]);
subject.map[member_name] = new_val;
data::modifyValue(var_name, subject);
return Value();
} else {
error("Indexed assignment is only for lists and strings.");
error("Indexed assignment is only for lists, strings and struct members.");
return Value();
}
}
@@ -123,7 +179,52 @@ Value execute(Instruction inst) {
if (subject.valtype == ValueType::List) {
return handleListGet(subject, op_args);
} else if (subject.valtype == ValueType::String) {
return handleStringGet(subject, op_args); }
return handleStringGet(subject, op_args);
} else if (subject.valtype == ValueType::Map) {
if (op_args.empty()) {
return subject;
}
if (op_args[0].valtype != ValueType::Identifier) {
error("Struct member name must be an identifier.");
return Value();
}
std::string member_name = op_args[0].string_val;
// Check for member variable
if (subject.map.count(member_name)) {
return subject.map.at(member_name);
}
// Check for member function
std::string struct_name = subject.map.at("__name__").string_val;
if (data::structs.count(struct_name) && data::structs.at(struct_name).functions.count(member_name)) {
const auto& func = data::structs.at(struct_name).functions.at(member_name);
std::vector<Value> func_args(op_args.begin() + 1, op_args.end());
if (func.arg_names.size() != func_args.size()) {
error("Struct function " + member_name + " expects " + std::to_string(func.arg_names.size()) + " arguments, but got " + std::to_string(func_args.size()));
return Value();
}
data::pushScope();
data::scopes.back()["self"] = subject;
for (size_t i = 0; i < func.arg_names.size(); ++i) {
data::scopes.back()[func.arg_names[i]] = evaluate(func_args[i]);
}
Value return_val;
for (const auto& body_inst : func.body) {
return_val = execute(body_inst);
if (return_val.is_return) {
break;
}
}
subject = data::scopes.back()["self"];
data::popScope();
return_val.is_return = false;
return return_val;
}
error("Struct " + struct_name + " does not have member or method " + member_name);
return Value();
}
error("Unknown variable or function call: " + args[0].toString());
return Value();

View File

@@ -4,7 +4,7 @@
#include "../../error/error.h"
#include <vector>
Value modules::fndef(std::vector<Value> args) {
Value modules::fndef(std::vector<Value> args, bool inStruct) {
if (args.size() < 2) { // At least a name and a body
error("Syntax error: function statement requires a name and a body");
return Value();
@@ -40,6 +40,10 @@ Value modules::fndef(std::vector<Value> args) {
new_func.arg_names = arg_names;
new_func.body = body_val.instructionGroup;
if (inStruct) {
return Value(new_func);
}
data::functions[func_name] = new_func;
return Value("");

View File

@@ -4,5 +4,5 @@
#include <vector>
namespace modules {
Value fndef(std::vector<Value> args);
Value fndef(std::vector<Value> args, bool inStruct = false);
}

View File

@@ -1,6 +1,8 @@
#include "print.h"
#include "../../defs/defs.h"
#include "../../error/error.h"
#include "../../data/data.h"
#include "../../executor/executor.h"
#include <vector>
#include <iostream>
#include <string>
@@ -21,7 +23,7 @@ Value modules::print(std::vector<Value> values) {
case ValueType::List: {
std::cout << "[";
bool afterfirst = false;
for (Value val : value.list) {
for (const auto& val : value.list) {
if (afterfirst) {
std::cout << ", ";
}
@@ -33,6 +35,42 @@ Value modules::print(std::vector<Value> values) {
std::cout << "]";
break;
}
case ValueType::Map: {
if (value.map.count("__name__")) {
std::string struct_name = value.map.at("__name__").string_val;
if (data::structs.count(struct_name) && data::structs.at(struct_name).functions.count("toString")) {
// Call toString method
data::pushScope();
data::scopes.back()["self"] = value;
Value result;
for (const auto& inst : data::structs.at(struct_name).functions.at("toString").body) {
result = execute(inst);
if (result.is_return) {
break;
}
}
data::popScope();
result.is_return = false;
print({result});
break;
}
}
// Fallback to printing members
std::cout << "{";
bool afterfirst = false;
for (const auto& pair : value.map) {
if (pair.first == "__name__") continue;
if (afterfirst) {
std::cout << ", ";
} else {
afterfirst = true;
}
std::cout << pair.first << ": ";
print({pair.second});
}
std::cout << "}";
break;
}
default:
error("FIXME: Unhandled type in print module.\nDebug info: \n" + value.toString());
break;

View File

@@ -0,0 +1,56 @@
#include "struct.h"
#include "../../defs/defs.h"
#include "../../error/error.h"
#include "../../data/data.h"
#include "../function/function.h"
#include <vector>
Value modules::structdef(std::vector<Value> args) {
if (args.size() < 2) { // At least a name and a body
error("Syntax error: struct statement requires a name and a body");
return Value();
}
// First arg is the name
Value name_val = args[0];
if (name_val.valtype != ValueType::Identifier) {
error("Syntax error: struct name must be an identifier");
return Value();
}
// Second arg is the body
Value body_val = args[1];
if (body_val.valtype != ValueType::InstructionGroup) {
error("Syntax error: struct body must be an instruction group");
return Value();
}
std::string structName = name_val.string_val;
InstructionGroup insts = body_val.instructionGroup;
Struct newStruct;
for (Instruction inst : insts) {
if (inst.args.empty()) {
continue;
}
if (inst.instruction == InstructionType::Function) {
newStruct.functions[inst.args[0].string_val] = fndef(inst.args, true).fn_val;
} else if (inst.args[0].valtype == ValueType::Identifier) {
if (inst.args.size() < 3) {
error("Syntax error: struct definition must have identifier, assignment and value");
return Value();
}
if (inst.args[1].valtype == ValueType::Identifier && inst.args[1].string_val == "=") {
newStruct.values[inst.args[0].string_val] = inst.args[2];
}
} else {
error("Only member definitions allowed in struct");
}
}
data::structs[structName] = newStruct;
return Value("");
}

View File

@@ -0,0 +1,8 @@
#pragma once
#include "../../defs/defs.h"
#include <vector>
namespace modules {
Value structdef(std::vector<Value> args);
}

View File

@@ -175,6 +175,48 @@ std::vector<Instruction> parse(std::string program) {
std::string then_content = line.substr(block_start + 1, block_end - block_start - 1);
function_inst.args.push_back(Value(parse(then_content))); // Recursive call
instructions.push_back(function_inst);
} else if (line.rfind("struct", 0) == 0) {
Instruction struct_inst;
struct_inst.instruction = InstructionType::Struct;
size_t block_start = line.find('{');
if (block_start == std::string::npos) {
error("Expected '{' for struct statement");
continue;
}
// 1. Struct name
std::string signature_str = line.substr(6, block_start - 6);
std::vector<Value> signature_parts = split(trim(signature_str));
if (signature_parts.empty()) {
error("Struct definition is missing a name.");
continue;
}
for(const auto& part : signature_parts) {
struct_inst.args.push_back(part);
}
// 2. Find 'then' block
size_t block_end = 0;
int brace_level = 0;
for (size_t i = block_start; i < line.length(); ++i) {
if (line[i] == '{') brace_level++;
else if (line[i] == '}') brace_level--;
if (brace_level == 0) {
block_end = i;
break;
}
}
if (block_end == 0) {
error("Mismatched braces in struct statement");
continue;
}
std::string then_content = line.substr(block_start + 1, block_end - block_start - 1);
struct_inst.args.push_back(Value(parse(then_content))); // Recursive call
instructions.push_back(struct_inst);
} else {
instructions.push_back(Instruction(split(line)));
}

View File

@@ -10,6 +10,13 @@ Value evaluate(Value val) {
}
} else if (val.valtype == ValueType::Variable) {
return data::getValue(val.varName.key);
} else if (val.valtype == ValueType::Identifier) {
// Check if it's a variable that doesn't start with $
for (auto it = data::scopes.rbegin(); it != data::scopes.rend(); ++it) {
if (it->count(val.string_val)) {
return (*it)[val.string_val];
}
}
}
return val;
}

27
tests/struct.kyn Normal file
View File

@@ -0,0 +1,27 @@
struct Data {
status = <Bool>
moreCrap = ["dingus", 32]
fun init status moreCrap {
#assert $status is <Bool>
#assert $morecrap is <List>
println "Initing!"
self status = $status
self moreCrap = $moreCrap
println "Done!"
}
fun toString {
return (concat "Status: " (self status))
}
fun testMemberFn {
println "This is from the test member function! Yay!"
}
}
let myData = (Data "true" ["dingus", "dongus", "mingus", "mongus"])
myData testMemberFn
println $myData