-
Notifications
You must be signed in to change notification settings - Fork 1
/
Interpreter.h
151 lines (122 loc) · 4.84 KB
/
Interpreter.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#pragma once
#include "SharedEnums.h"
#include "Directory.h"
#include "AST.h"
#include "Program.h"
#include "Scope.h"
class Interpreter
{
Program* prog = nullptr;
//The global scope of variables.
Scopelet<Value> globalscope;
//Part of the function stack; the object associated with the current function/method. A nullptr indicates that it's a global (classless) function.
std::stack<Object*> objectscope;
//The scopes of each block of each function in the function stack.
std::stack<Scope<Value>> blockscope;
const bool is_interactive;
public:
//Stores the current, most recent runtime error. Expected to be set to null during most operation.
Value error = Value();
//Something used to handle rvalues in expressions. TODO: Should be blanked sometimes.
Value tempvalue = Value();
int BREAK_COUNTER = 0; // An integer flag used to break (perhaps several levels) out of one or several blocks (which are not Function blocks)
bool FORCE_RETURN = false; // A flag used to allow blocks to force their parent functions to return when they hit a ReturnStatement.
bool CONTINUE_FLAG = false; // Flag used to mark when a "continue;" has been hit.
#ifdef JOAO_SAFE
int value_init_count = 0;
#endif
Interpreter();
Interpreter(Program&,bool);
//Executes the given program. Assumes program already knows about it's parent interp.
Value execute(Program&, Value&);
//Evaluates the result of running the given expression, under the given program.
Value evaluate_expression(ASTNode*);
//Gets function by Directory name.
Function* get_func(const std::string& funkname, ASTNode* caller, bool loud = true);
///Blockscope/omniscope
//Initializes variable at the lowest blockscope available.
void init_var(const ImmutableString&, const Value&, ASTNode*);
//Override the value of an already-existing variable at the lowest scope available.
void override_var(const ImmutableString&, Value, ASTNode*);
//Get variable at the lowest scope available.
Value& get_var(const ImmutableString&, ASTNode*);
bool has_var(std::string, ASTNode*);
Object* get_objectscope() const { return objectscope.top(); }
///Objectscope
Value get_property(const ImmutableString&, ASTNode*);
void set_property(const ImmutableString&, Value, ASTNode*);
Value grand_property(unsigned int, const ImmutableString&, ASTNode*);
Value& grand_handle(unsigned int, const ImmutableString&, ASTNode*);
//Construct an object and return it as a Value.
Value makeObject(std::string,std::vector<ASTNode*>&,ASTNode*);
Value makeObject(std::string,std::vector<Value>&&,ASTNode*);
//Constructs an empty base table.
Value makeBaseTable();
//Constructs an anonymous table with no derived classes and returns it.
Value makeBaseTable( std::vector<Value>, Hashtable<std::string,Value>, ASTNode*);
//Treated as a special, fatal error.
void RuntimeError(ASTNode*, const std::string&);
//Treated as a runtime which can be resumed from.
void RuntimeError(ASTNode* node, ErrorCode err, const std::string&);
//RuntimeError as called by the Throw keyword
//This assumes that err_val has already been type-checked by someone higher up in the stack
void RuntimeError(ASTNode* node, Value& err_val);
void UncaughtRuntime(const Value& err);
//Pushes a new blockstack and objstack layer
void push_stack(std::string name = "", Object* obj = nullptr)
{
#ifdef JOAO_SAFE
if (blockscope.size() > MAX_RECURSION)
{
throw error::maximum_recursion(std::string("Program reached the limit of ") + std::to_string(MAX_RECURSION) + std::string("recursive calls!"));
}
#endif
blockscope.push(Scope<Value>(name));
objectscope.push(obj);
}
//Pops both the blockstack and objstack layer
void pop_stack()
{
blockscope.pop();
objectscope.pop();
}
//Like get_global but returns the pointer instead, and quietly allocates a new global when you ask for one it hasn't seen before.
Value* has_global(const ImmutableString& name, [[maybe_unused]] ASTNode* getter)
{
if (!globalscope.table.count(name))
{
return &(globalscope.table[name]);
}
return globalscope.at(name);
}
Value& get_global(const ImmutableString& name, ASTNode* getter)
{
if (!globalscope.table.count(name))
{
RuntimeError(getter, ErrorCode::BadAccess, "Unable to access global value: " + name.to_string()); // Works, just returns null and yells.
return globalscope.table[name];
}
return globalscope.table.at(name);
}
void set_global(const ImmutableString& name, Value& val, [[maybe_unused]] ASTNode* setter)
{
#ifdef JOAO_SAFE
if(!globalscope.table.contains(name)) {
++value_init_count;
if (value_init_count > MAX_VARIABLES)
{
throw error::max_variables(std::string("Program reached the limit of ") + std::to_string(MAX_VARIABLES) + std::string("instantiated variables!"));
}
}
#endif
globalscope.table[name] = Value(val);
}
void push_block()
{
blockscope.top().push();
}
void pop_block()
{
blockscope.top().pop();
}
};