-
Notifications
You must be signed in to change notification settings - Fork 11
/
eval.es6
191 lines (160 loc) · 7.15 KB
/
eval.es6
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
// == Evaluates Expressions ==
// Perhaps add config item to specify optimizations
// This first version will be a preliminary test
// and won't have very many features
// Class operators and tokens will be abstracted so
// changed to `class` won't be deterimental to the
// existing code.
// This will also allow classes to be forged for testing
// == Info ==
// config:link contains class linktage to
// primitives, which functions as a basic
// abstraction layer between the expression
// parser and the tokenizer.
// The CheddarClass itself provides a
// more thougrough abstraction between
// the tokenizer and the rest of the code
// itself
// Primitive <-> Class Links
import {PRIMITIVE_LINKS} from '../config/link';
import {TYPE as OP_TYPE} from '../../../tokenizer/consts/ops';
// Reference tokens
import CheddarPropertyToken from '../../../tokenizer/parsers/property';
import CheddarPrimitive from '../../../tokenizer/literals/primitive';
import CheddarLiteral from '../../../tokenizer/parsers/any';
import CheddarOperatorToken from '../../../tokenizer/literals/op';
import CheddarArrayToken from '../../../tokenizer/parsers/array';
import CheddarVariableToken from '../../../tokenizer/literals/var';
import CheddarVariable from '../env/var';
import NIL from '../consts/nil';
// Call stack wrapper
import CheddarCallStack from './callstack';
// Standard Error class
import CheddarError from '../consts/err';
import CheddarErrorDesc from '../consts/err_msg';
export default class CheddarEval extends CheddarCallStack {
// To iterativaley evaluate the expression
// individual repeated steps would be taken
// which would also allow the debugger to
// function on the same foundation
step() {
const Operation = this.next();
let OPERATOR,
TOKEN,
DATA;
// Handle Operator
if (Operation instanceof CheddarOperatorToken) {
TOKEN = this.shift(); // Get the value to operate upon
// SPECIAL BEHAVIOR FOR ASSIGNMENT
if (Operation.tok(0) === "=") {
DATA = this.shift();
if ((
!(DATA.Scope instanceof this.Scope.constructor) ||
DATA.Reference === null
) || Operation.tok(1) === OP_TYPE.UNARY) {
return CheddarErrorDesc.get(CheddarError.NOT_A_REFERENCE);
}
// NAVIGATE TOKEN AND DATA
// Check if variable needs to be wrapped
if ((TOKEN.Reference === null || !TOKEN.Reference)) {
TOKEN.Scope = DATA.Scope;
TOKEN.Reference = DATA.Reference;
DATA.Scope.manage(
DATA.Reference,
new CheddarVariable(TOKEN, {
Writeable: !DATA.const,
StrictType: null
})
);
} else {
DATA.Scope.manage(DATA.Reference, TOKEN.Scope.Scope.get(TOKEN.Reference));
}
OPERATOR = TOKEN;
} else if (!TOKEN.constructor.Operator.has(Operation.Tokens[0])) {
// Ensure behavior exists for the types
OPERATOR = CheddarError.NO_OP_BEHAVIOR;
} else if (Operation.Tokens[1] === OP_TYPE.UNARY) {
// Check if unary or binary operator, then execute
OPERATOR = TOKEN.constructor.Operator.get(Operation.Tokens[0])(null, TOKEN);
} else {
DATA = this.shift(); // Get the other arg
OPERATOR = TOKEN.constructor.Operator.get(Operation.Tokens[0])(DATA, TOKEN);
}
if (OPERATOR === CheddarError.NO_OP_BEHAVIOR) {
return CheddarErrorDesc.get(OPERATOR)
.replace(/\$0/g, Operation.Tokens[0])
.replace(/\$1/g, TOKEN ? TOKEN.constructor.Name : "nil")
.replace(/\$2/g, DATA ? DATA.constructor.Name : "nil");
} else {
this.put(OPERATOR);
}
} else if (Operation instanceof CheddarLiteral) {
// If it is a token, pass tokens to the associated class constructor
TOKEN = Operation._Tokens[0]; // Get token
// Do a lookup in the PRIMITIVE_LINKS class and get the link class
if ((OPERATOR = PRIMITIVE_LINKS.get(TOKEN.constructor.name))) {
// OPERATOR has the class to construct upon
// Operator Construction
OPERATOR = new OPERATOR(this.Scope);
// Initialize operator class
// check if successful (true)
if ((TOKEN = OPERATOR.init(...TOKEN.Tokens)) === true) {
// place on stack
this.put( OPERATOR );
} else {
// return error
return TOKEN;
}
} else {
return CheddarError.UNLINKED_CLASS;
}
} else if (Operation instanceof CheddarPropertyToken) {
// If it's a property
// this includes functions
// Lookup in cuurrent scope
TOKEN = [];
// Is a primitive
// this includes `"foo".bar`
if (Operation._Tokens[0] instanceof CheddarPrimitive) {
OPERATOR = PRIMITIVE_LINKS.get(Operation._Tokens[0].constructor.name);
if (OPERATOR) {
OPERATOR = new OPERATOR(...Operation._Tokens[0].Tokens);
} else {
return CheddarError.UNLINKED_CLASS;
}
} else if (Operation._Tokens[0] instanceof CheddarVariableToken) {
// Lookup variable -> initial variable name
OPERATOR = this.Scope.accessor(Operation._Tokens[0]._Tokens[0]).Value;
if (OPERATOR === CheddarError.KEY_NOT_FOUND || !OPERATOR) {
return CheddarErrorDesc.get(CheddarError.KEY_NOT_FOUND)
.replace('$0', Operation._Tokens[0]._Tokens[0]);
}
} else {
return CheddarError.MALFORMED_TOKEN;
}
// Advance variable tree
// i dunno how to do this shit so ill do it later
for (let i = 1; i < Operation._Tokens.length; i++) {
// if it is a function call, call the function
if (Operation._Tokens[i] instanceof CheddarArrayToken)
console.log("Yeah... no functions yet...\nIf you're complaining that why I haven't made them, make them yourself and make a PR\nwhy do I have to make everything?");
else
console.log(":/ this is also not finished... will get to after a bit");
}
this.put( OPERATOR );
} else {
return "An unhandled token was encountered";
}
return true;
}
// Evaluate entire call stack
// this stepts until the call
// stack or `InStack` is empty
exec() {
let step;
while (this.InStack.length)
if ((step = this.step()) !== true)
return step;
return this.close();
}
}