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 | 1
1
1
1
1
1
1
112
111
1
1
1
1
1
111
111
111
111
1
16
1
43
1
473
15
32
458
384
74
63
11
1
88
51
88
88
88
51
88
88
88
88
1
14
14
11
28
11
14
1
175
175
219
88
219
175
1
42
42
49
49
42
49
1
22
22
22
22
22
22
22
1
2
2
1
382
172
210
1
1191
1191
2529
2529
1191
1
| (function(sc) {
"use strict";
require("../compiler");
require("./scope");
var slice = [].slice;
var strlib = sc.libs.strlib;
var Scope = sc.lang.compiler.Scope;
function CodeGen(parent, opts) {
if (!parent) {
initialize(this, opts);
} else {
this.parent = parent;
this.opts = parent.opts;
this.state = parent.state;
this.scope = parent.scope;
}
}
function initialize(that, opts) {
that.parent = null;
that.opts = opts || {};
that.state = {
calledSegmentedMethod: false,
syncBlockScope: null,
tempVarId: 0
};
that.scope = new Scope(that);
}
CodeGen.addGenerateMethod = function(name, method) {
CodeGen.prototype[name] = method;
};
CodeGen.prototype.compile = function(ast) {
return this.generate(ast);
};
CodeGen.prototype.generate = function(node, opts) {
if (Array.isArray(node)) {
return [
"(", this.stitchWith(node, ",", function(item) {
return this.generate(item, opts);
}), ")"
];
}
if (node && node.type) {
return toSourceNodeWhenNeeded(this[node.type](node, opts), node);
}
if (typeof node === "string") {
return node.replace(/^(?![_$])/, "$");
}
return "null";
};
CodeGen.prototype.withFunction = function(args, func) {
var argItems = this.stitchWith(args, ",", function(item) {
return this.generate(item);
});
var result = [ "function(", argItems, "){" ];
this.scope.begin();
for (var i = 0, imax = args.length; i < imax; ++i) {
this.scope.add("arg", args[i]);
}
result.push(
this.scope.toVariableStatement(),
func.call(this)
);
this.scope.end();
result.push("}");
return result;
};
CodeGen.prototype.insertArrayElement = function(elements) {
var result = [ "[", "]" ];
if (elements.length) {
var items = this.stitchWith(elements, ",", function(item) {
return this.generate(item);
});
result.splice(1, 0, items);
}
return result;
};
CodeGen.prototype.stitchWith = function(elements, bond, func) {
var result = [];
for (var i = 0, imax = elements.length; i < imax; ++i) {
if (i) {
result.push(bond);
}
result.push(func.call(this, elements[i], i));
}
return result;
};
CodeGen.prototype.generateStatements = function(elements) {
var lastIndex = elements.length - 1;
return elements.map(function(item, i) {
var stmt = this.generate(item);
if (i === lastIndex) {
stmt = [ "return ", stmt ];
}
return [ stmt, ";" ];
}, this);
};
CodeGen.prototype.useTemporaryVariable = function(func) {
var result;
var tempName = "_ref" + this.state.tempVarId;
this.scope.add("var", tempName);
this.state.tempVarId += 1;
result = func.call(this, tempName);
this.state.tempVarId -= 1;
return result;
};
CodeGen.prototype.throwError = function(obj, messageFormat) {
var message = strlib.format(messageFormat, slice.call(arguments, 2));
throw new Error(message);
};
function toSourceNodeWhenNeeded(generated) {
if (Array.isArray(generated)) {
return flattenToString(generated);
}
return generated;
}
function flattenToString(list) {
var result = "";
for (var i = 0, imax = list.length; i < imax; ++i) {
var elem = list[i];
result += Array.isArray(elem) ? flattenToString(elem) : elem;
}
return result;
}
sc.lang.compiler.CodeGen = CodeGen;
})(sc);
|