/
codeCompiler.js
194 lines (163 loc) · 5.71 KB
/
codeCompiler.js
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
apogee.codeCompiler = {};
/** @private */
apogee.codeCompiler.APOGEE_FORBIDDEN_NAMES = {
"apogeeMessenger": true,
"__initFunction": true,
"__memberFunction": true,
"__memberFunctionDebugHook": true
}
/** @private */
apogee.codeCompiler.NAME_PATTERN = /[a-zA-Z_$][0-9a-zA-Z_$]*/;
/** This function validates a table name. It returns
* [valid,errorMsg]. */
apogee.codeCompiler.validateTableName = function(name) {
var nameResult = {};
//check if it is a keyword
if(apogee.codeAnalysis.KEYWORDS[name]) {
nameResult.errorMessage = "Illegal name: " + name + " - Javascript reserved keyword";
nameResult.valid = false;
}
else if(apogee.codeAnalysis.EXCLUSION_NAMES[name]) {
nameResult.errorMessage = "Illegal name: " + name + " - Javascript variable or value name";
nameResult.valid = false;
}
else if(apogee.codeCompiler.APOGEE_FORBIDDEN_NAMES[name]) {
nameResult.errorMessage = "Illegal name: " + name + " - Apogee reserved keyword";
nameResult.valid = false;
}
else {
//check the pattern
var nameResult = apogee.codeCompiler.NAME_PATTERN.exec(name);
if((!nameResult)||(nameResult[0] !== name)) {
nameResult.errorMessage = "Illegal name format: " + name;
nameResult.valid = false;
}
else {
nameResult.valid = true;
}
}
return nameResult;
}
/** This method analyzes the code and creates the object function and dependencies.
* The results are loaded into the passed object processedCodeData. */
apogee.codeCompiler.processCode = function(codeInfo,codeLabel) {
//analyze the code
var combinedFunctionBody = apogee.codeCompiler.createCombinedFunctionBody(
codeInfo.argList,
codeInfo.functionBody,
codeInfo.supplementalCode,
codeLabel);
//get the accessed variables
//
//parse the code and get variable dependencies
var effectiveCombinedFunctionBody = apogee.codeCompiler.MEMBER_LOCALS_TEXT + combinedFunctionBody;
var analyzeOutput = apogee.codeAnalysis.analyzeCode(effectiveCombinedFunctionBody);
if(analyzeOutput.success) {
codeInfo.varInfo = analyzeOutput.varInfo;
}
else {
codeInfo.errors = analyzeOutput.errors;
return codeInfo;
}
//create the object function and context setter from the code text
var generatorFunction = apogee.codeCompiler.createGeneratorFunction(codeInfo.varInfo, combinedFunctionBody);
codeInfo.generatorFunction = generatorFunction;
return codeInfo;
}
/** This method creates the user code object function body.
* @private */
apogee.codeCompiler.createCombinedFunctionBody = function(argList,
functionBody,
supplementalCode,
codeLabel) {
var argListString = argList.join(",");
//create the code body
var combinedFunctionBody = apogee.util.formatString(
apogee.codeCompiler.MEMBER_FUNCTION_FORMAT_TEXT,
codeLabel,
argListString,
functionBody,
supplementalCode
);
return combinedFunctionBody;
}
/** This method creates the wrapped user code object function, including the context variables.
* @private */
apogee.codeCompiler.createGeneratorFunction = function(varInfo, combinedFunctionBody) {
var contextDeclarationText = "";
var initializerBody = "";
//set the context - here we only defined the variables that are actually used.
for(var baseName in varInfo) {
var baseNameInfo = varInfo[baseName];
//do not add context variable for local or "returnValue", which is explicitly defined
if((baseName === "returnValue")||(baseNameInfo.isLocal)) continue;
//add a declaration
contextDeclarationText += "var " + baseName + ";\n";
//add to the context setter
initializerBody += baseName + ' = contextManager.getBaseData("' + baseName + '");\n';
}
//create the generator for the object function
var generatorBody = apogee.util.formatString(
apogee.codeCompiler.GENERATOR_FUNCTION_FORMAT_TEXT,
contextDeclarationText,
initializerBody,
combinedFunctionBody
);
var generatorFunction = new Function("apogeeMessenger",generatorBody);
return generatorFunction;
}
/** This is the format string to create the code body for the object function
* Input indices:
* 0: unique member name
* 1: function argument list with parentheses
* 2: member formula text
* 3: supplemental code text
*
* @private
*/
apogee.codeCompiler.MEMBER_FUNCTION_FORMAT_TEXT = [
"//{0}",
"",
"//supplemental code--------------",
"{3}",
"//end supplemental code----------",
"",
"//member function----------------",
"function __memberFunction({1}) {",
"//overhead code",
"__memberFunctionDebugHook();",
"",
"//user code",
"{2}",
"};",
"//end member function------------",
].join("\n");
/** This line is added when getting the dependencies to account for some local
* variables in the member function.
* @private */
apogee.codeCompiler.MEMBER_LOCALS_TEXT = "var apogeeMessenger, __memberFunction, __memberFunctionDebugHook;";
/** This is the format string to create the code body for the object function
* Input indices:
* 0: context declaration text
* 1: context setter body
* 2: object function body
* @private
*/
apogee.codeCompiler.GENERATOR_FUNCTION_FORMAT_TEXT = [
"'use strict'",
"//declare context variables",
"{0}",
"//context setter",
"function __initializer(contextManager) {",
"{1}};",
"",
"//user code",
"function __memberGenerator() {",
"{2}",
"return __memberFunction",
"}",
"return {",
"'memberGenerator': __memberGenerator,",
"'initializer': __initializer",
"};"
].join("\n");