Add interpretation of function calls
This commit is contained in:
parent
fc53bf3113
commit
5f232be286
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ enum class ErrorType {
|
||||
ModuloByZero,
|
||||
BreakNotWithinLoop,
|
||||
ContinueNotWithinLoop,
|
||||
ControlReachesEndOfNonVoidFn
|
||||
};
|
||||
|
||||
using ErrorData = variant<monostate, string>;
|
||||
|
@ -25,4 +25,12 @@ public:
|
||||
: InternalError(pos) {}
|
||||
};
|
||||
|
||||
class ReturnException : public InternalError {
|
||||
public:
|
||||
EvalResult val;
|
||||
|
||||
explicit ReturnException(EvalResult val)
|
||||
: InternalError(), val(val) {}
|
||||
};
|
||||
|
||||
#endif
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -50,6 +50,7 @@ Scope& Memory::get_function_scope(void) {
|
||||
if (scope.type == ScopeType::Function) return scope;
|
||||
}
|
||||
|
||||
// Tmp: no flow control
|
||||
throw InternalError();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user