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::ModuloByZero: return "Modulo by 0";
|
||||||
case ErrorType::BreakNotWithinLoop: return "Break statement not within loop";
|
case ErrorType::BreakNotWithinLoop: return "Break statement not within loop";
|
||||||
case ErrorType::ContinueNotWithinLoop: return "Continue 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";
|
default: return "Unknown error type";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ enum class ErrorType {
|
|||||||
ModuloByZero,
|
ModuloByZero,
|
||||||
BreakNotWithinLoop,
|
BreakNotWithinLoop,
|
||||||
ContinueNotWithinLoop,
|
ContinueNotWithinLoop,
|
||||||
|
ControlReachesEndOfNonVoidFn
|
||||||
};
|
};
|
||||||
|
|
||||||
using ErrorData = variant<monostate, string>;
|
using ErrorData = variant<monostate, string>;
|
||||||
|
@ -25,4 +25,12 @@ public:
|
|||||||
: InternalError(pos) {}
|
: InternalError(pos) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ReturnException : public InternalError {
|
||||||
|
public:
|
||||||
|
EvalResult val;
|
||||||
|
|
||||||
|
explicit ReturnException(EvalResult val)
|
||||||
|
: InternalError(), val(val) {}
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -17,7 +17,7 @@ class Memory {
|
|||||||
|
|
||||||
MemoryVar& get(string identifier);
|
MemoryVar& get(string identifier);
|
||||||
Scope& get_function_scope(void);
|
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);
|
void update(string identifier, EvalResult value);
|
||||||
|
|
||||||
Scope& _debug_top(void);
|
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) {
|
EvalResult eval(Node &ast, Memory &memory) {
|
||||||
if (holds_alternative<InnerNode>(ast)) {
|
if (holds_alternative<InnerNode>(ast)) {
|
||||||
InnerNode node = get<InnerNode>(ast);
|
InnerNode node = get<InnerNode>(ast);
|
||||||
@ -139,12 +149,12 @@ EvalResult eval(Node &ast, Memory &memory) {
|
|||||||
} break;
|
} break;
|
||||||
case NodeType::FunctionPrototype: {
|
case NodeType::FunctionPrototype: {
|
||||||
Token retTypeTok = get<Token>(node.children[0]);
|
Token retTypeTok = get<Token>(node.children[0]);
|
||||||
Token fnIdentifierTok = get<Token>(node.children[1]);
|
Token identifierTok = get<Token>(node.children[1]);
|
||||||
string fnIdentifier = get<string>(fnIdentifierTok.data);
|
string identifier = get<string>(identifierTok.data);
|
||||||
|
|
||||||
FunctionPrototype prototype = make_fn_prototype(node);
|
FunctionPrototype prototype = make_fn_prototype(node);
|
||||||
|
|
||||||
memory.declare(fnIdentifier, {
|
memory.declare(identifier, {
|
||||||
.type = TypeType::Function,
|
.type = TypeType::Function,
|
||||||
.data = prototype
|
.data = prototype
|
||||||
}, retTypeTok.pos);
|
}, retTypeTok.pos);
|
||||||
@ -153,25 +163,61 @@ EvalResult eval(Node &ast, Memory &memory) {
|
|||||||
} break;
|
} break;
|
||||||
case NodeType::FunctionDeclaration: {
|
case NodeType::FunctionDeclaration: {
|
||||||
Token retTypeTok = get<Token>(node.children[0]);
|
Token retTypeTok = get<Token>(node.children[0]);
|
||||||
Token fnIdentifierTok = get<Token>(node.children[1]);
|
Token identifierTok = get<Token>(node.children[1]);
|
||||||
string fnIdentifier = get<string>(fnIdentifierTok.data);
|
string identifier = get<string>(identifierTok.data);
|
||||||
|
|
||||||
FunctionPrototype prototype = make_fn_prototype(node);
|
FunctionPrototype prototype = make_fn_prototype(node);
|
||||||
|
|
||||||
memory.declare(fnIdentifier, {
|
memory.declare(identifier, {
|
||||||
.type = TypeType::Function,
|
.type = TypeType::Function,
|
||||||
.data = prototype
|
.data = prototype
|
||||||
}, retTypeTok.pos);
|
}, retTypeTok.pos);
|
||||||
|
|
||||||
memory.update(fnIdentifier, node.children[3]);
|
memory.update(identifier, node.children[3]);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
} break;
|
} break;
|
||||||
case NodeType::FunctionCall: {
|
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 {
|
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();
|
memory.remove_scope();
|
||||||
}
|
}
|
||||||
@ -181,6 +227,11 @@ EvalResult eval(Node &ast, Memory &memory) {
|
|||||||
exception_ptr e = current_exception();
|
exception_ptr e = current_exception();
|
||||||
if (e) rethrow_exception(e);
|
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;
|
} break;
|
||||||
case NodeType::Eq: {
|
case NodeType::Eq: {
|
||||||
EvalResult e1 = eval(node.children[0], memory);
|
EvalResult e1 = eval(node.children[0], memory);
|
||||||
|
@ -50,6 +50,7 @@ Scope& Memory::get_function_scope(void) {
|
|||||||
if (scope.type == ScopeType::Function) return scope;
|
if (scope.type == ScopeType::Function) return scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tmp: no flow control
|
||||||
throw InternalError();
|
throw InternalError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user