Add interpretation of function calls

This commit is contained in:
ala89 2024-01-03 15:52:41 +01:00
parent fc53bf3113
commit 5f232be286
6 changed files with 72 additions and 10 deletions

View File

@ -35,6 +35,7 @@ string UserError::get_message(void) const {
case ErrorType::ModuloByZero: return "Modulo by 0";
case ErrorType::BreakNotWithinLoop: return "Break statement not within loop";
case ErrorType::ContinueNotWithinLoop: return "Continue statement not within loop";
case ErrorType::ControlReachesEndOfNonVoidFn: return "Control reaches end of non-void function";
default: return "Unknown error type";
}
}

View File

@ -45,6 +45,7 @@ enum class ErrorType {
ModuloByZero,
BreakNotWithinLoop,
ContinueNotWithinLoop,
ControlReachesEndOfNonVoidFn
};
using ErrorData = variant<monostate, string>;

View File

@ -25,4 +25,12 @@ public:
: InternalError(pos) {}
};
class ReturnException : public InternalError {
public:
EvalResult val;
explicit ReturnException(EvalResult val)
: InternalError(), val(val) {}
};
#endif

View File

@ -17,7 +17,7 @@ class Memory {
MemoryVar& get(string identifier);
Scope& get_function_scope(void);
void declare(string identifier, Type type, CodePosition pos);
void declare(string identifier, Type type, CodePosition pos = { });
void update(string identifier, EvalResult value);
Scope& _debug_top(void);

View File

@ -42,6 +42,16 @@ double double_cast(EvalResult value) {
}
}
EvalResult cast_from_type(Type type, EvalResult val) {
switch (type.type) {
case TypeType::Int: return int_cast(val);
case TypeType::Double: return double_cast(val);
case TypeType::Void: return {};
default:
throw exception();
}
}
EvalResult eval(Node &ast, Memory &memory) {
if (holds_alternative<InnerNode>(ast)) {
InnerNode node = get<InnerNode>(ast);
@ -139,12 +149,12 @@ EvalResult eval(Node &ast, Memory &memory) {
} break;
case NodeType::FunctionPrototype: {
Token retTypeTok = get<Token>(node.children[0]);
Token fnIdentifierTok = get<Token>(node.children[1]);
string fnIdentifier = get<string>(fnIdentifierTok.data);
Token identifierTok = get<Token>(node.children[1]);
string identifier = get<string>(identifierTok.data);
FunctionPrototype prototype = make_fn_prototype(node);
memory.declare(fnIdentifier, {
memory.declare(identifier, {
.type = TypeType::Function,
.data = prototype
}, retTypeTok.pos);
@ -153,25 +163,61 @@ EvalResult eval(Node &ast, Memory &memory) {
} break;
case NodeType::FunctionDeclaration: {
Token retTypeTok = get<Token>(node.children[0]);
Token fnIdentifierTok = get<Token>(node.children[1]);
string fnIdentifier = get<string>(fnIdentifierTok.data);
Token identifierTok = get<Token>(node.children[1]);
string identifier = get<string>(identifierTok.data);
FunctionPrototype prototype = make_fn_prototype(node);
memory.declare(fnIdentifier, {
memory.declare(identifier, {
.type = TypeType::Function,
.data = prototype
}, retTypeTok.pos);
memory.update(fnIdentifier, node.children[3]);
memory.update(identifier, node.children[3]);
return {};
} break;
case NodeType::FunctionCall: {
memory.add_scope(ScopeType::Function);
Token identifierTok = get<Token>(node.children[0]);
string identifier = get<string>(identifierTok.data);
vector<Node> args = get<InnerNode>(node.children[1]).children;
MemoryVar& var = memory.get(identifier);
if (!var.initialized)
throw RuntimeError(ErrorType::UninitializedIdentifier, identifierTok.pos, identifier);
vector<EvalResult> argValues = {};
for (Node arg : args) {
argValues.push_back(eval(arg, memory));
}
FunctionPrototype prototype = get<FunctionPrototype>(var.type.data);
EvalResult res = {};
memory.add_scope(ScopeType::Function, identifierTok.pos, &var);
try {
for (size_t i = 1; i < prototype.size(); i++) {
tuple<Type, string> argDef = prototype[i];
Type argType = get<0>(argDef);
string argName = get<1>(argDef);
memory.declare(argName, argType);
memory.update(argName, cast_from_type(argType, argValues[i - 1]));
}
try {
eval(get<Node>(var.value), memory);
// Tmp: no flow control
if (get<0>(prototype[0]).type != TypeType::Void) {
throw RuntimeError(ErrorType::ControlReachesEndOfNonVoidFn, identifierTok.pos);
}
}
catch (const ReturnException& e) {
res = e.val;
}
memory.remove_scope();
}
@ -181,6 +227,11 @@ EvalResult eval(Node &ast, Memory &memory) {
exception_ptr e = current_exception();
if (e) rethrow_exception(e);
}
return cast_from_type(get<0>(prototype[0]), res);
} break;
case NodeType::Return: {
throw ReturnException(eval(node.children[0], memory));
} break;
case NodeType::Eq: {
EvalResult e1 = eval(node.children[0], memory);

View File

@ -50,6 +50,7 @@ Scope& Memory::get_function_scope(void) {
if (scope.type == ScopeType::Function) return scope;
}
// Tmp: no flow control
throw InternalError();
}