Structs (IN BETA)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value fndef(std::vector<Value> args);
|
||||
Value fndef(std::vector<Value> args, bool inStruct = false);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
56
src/modules/struct/struct.cpp
Normal file
56
src/modules/struct/struct.cpp
Normal 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("");
|
||||
|
||||
}
|
||||
8
src/modules/struct/struct.h
Normal file
8
src/modules/struct/struct.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../defs/defs.h"
|
||||
#include <vector>
|
||||
|
||||
namespace modules {
|
||||
Value structdef(std::vector<Value> args);
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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
27
tests/struct.kyn
Normal 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
|
||||
Reference in New Issue
Block a user