forked from skulpt/skulpt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfunction.js
281 lines (253 loc) · 9.19 KB
/
function.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/**
* @namespace Sk.builtin
*/
/**
* Check arguments to Python functions to ensure the correct number of
* arguments are passed.
*
* @param {string} name the name of the function
* @param {Object} args the args passed to the function
* @param {number} minargs the minimum number of allowable arguments
* @param {number=} maxargs optional maximum number of allowable
* arguments (default: Infinity)
* @param {boolean=} kwargs optional true if kwargs, false otherwise
* (default: false)
* @param {boolean=} free optional true if free vars, false otherwise
* (default: false)
*/
Sk.builtin.pyCheckArgs = function (name, args, minargs, maxargs, kwargs, free) {
var nargs = args.length;
var msg = "";
if (maxargs === undefined) {
maxargs = Infinity;
}
if (kwargs) {
nargs -= 1;
}
if (free) {
nargs -= 1;
}
if ((nargs < minargs) || (nargs > maxargs)) {
if (minargs === maxargs) {
msg = name + "() takes exactly " + minargs + " arguments";
} else if (nargs < minargs) {
msg = name + "() takes at least " + minargs + " arguments";
} else {
msg = name + "() takes at most " + maxargs + " arguments";
}
msg += " (" + nargs + " given)";
throw new Sk.builtin.TypeError(msg);
}
};
goog.exportSymbol("Sk.builtin.pyCheckArgs", Sk.builtin.pyCheckArgs);
/**
* Check type of argument to Python functions.
*
* @param {string} name the name of the argument
* @param {string} exptype string of the expected type name
* @param {boolean} check truthy if type check passes, falsy otherwise
*/
Sk.builtin.pyCheckType = function (name, exptype, check) {
if (!check) {
throw new Sk.builtin.TypeError(name + " must be a " + exptype);
}
};
goog.exportSymbol("Sk.builtin.pyCheckType", Sk.builtin.pyCheckType);
Sk.builtin.checkSequence = function (arg) {
return (arg !== null && arg.mp$subscript !== undefined);
};
goog.exportSymbol("Sk.builtin.checkSequence", Sk.builtin.checkSequence);
/**
* Use this to test whether or not a Python object is iterable. You should **not** rely
* on the presence of tp$iter on the object as a good test, as it could be a user defined
* class with `__iter__` defined or ``__getitem__`` This tests for all of those cases
*
* @param arg {Object} A Python object
* @returns {boolean} true if the object is iterable
*/
Sk.builtin.checkIterable = function (arg) {
var ret = false;
if (arg !== null ) {
try {
ret = Sk.abstr.iter(arg);
if (ret) {
return true;
} else {
return false;
}
} catch (e) {
if (e instanceof Sk.builtin.TypeError) {
return false;
} else {
throw e;
}
}
}
return ret;
};
goog.exportSymbol("Sk.builtin.checkIterable", Sk.builtin.checkIterable);
Sk.builtin.checkCallable = function (arg) {
if (typeof arg === "function") {
return (!(arg instanceof Sk.builtin.none) && (arg.ob$type !== undefined));
} else {
return ((arg.tp$call !== undefined) || (arg.__call__ !== undefined));
}
};
Sk.builtin.checkNumber = function (arg) {
return (arg !== null && (typeof arg === "number" ||
arg instanceof Sk.builtin.int_ ||
arg instanceof Sk.builtin.float_ ||
arg instanceof Sk.builtin.lng));
};
goog.exportSymbol("Sk.builtin.checkNumber", Sk.builtin.checkNumber);
/**
* Checks for complex type, delegates to internal method
* Most skulpt users would search here!
*/
Sk.builtin.checkComplex = function (arg) {
return Sk.builtin.complex._complex_check(arg);
};
goog.exportSymbol("Sk.builtin.checkComplex", Sk.builtin.checkComplex);
Sk.builtin.checkInt = function (arg) {
return (arg !== null) && ((typeof arg === "number" && arg === (arg | 0)) ||
arg instanceof Sk.builtin.int_ ||
arg instanceof Sk.builtin.lng);
};
goog.exportSymbol("Sk.builtin.checkInt", Sk.builtin.checkInt);
Sk.builtin.checkFloat = function (arg) {
return (arg !== null) && (arg instanceof Sk.builtin.float_);
};
goog.exportSymbol("Sk.builtin.checkFloat", Sk.builtin.checkFloat);
Sk.builtin.checkString = function (arg) {
return (arg !== null && arg.__class__ == Sk.builtin.str);
};
goog.exportSymbol("Sk.builtin.checkString", Sk.builtin.checkString);
Sk.builtin.checkClass = function (arg) {
return (arg !== null && arg.sk$type);
};
goog.exportSymbol("Sk.builtin.checkClass", Sk.builtin.checkClass);
Sk.builtin.checkBool = function (arg) {
return (arg instanceof Sk.builtin.bool);
};
goog.exportSymbol("Sk.builtin.checkBool", Sk.builtin.checkBool);
Sk.builtin.checkNone = function (arg) {
return (arg instanceof Sk.builtin.none);
};
goog.exportSymbol("Sk.builtin.checkNone", Sk.builtin.checkNone);
Sk.builtin.checkFunction = function (arg) {
return (arg !== null && arg.tp$call !== undefined);
};
goog.exportSymbol("Sk.builtin.checkFunction", Sk.builtin.checkFunction);
/**
* @constructor
* Sk.builtin.func
*
* @description
* This function converts a Javascript function into a Python object that is callable. Or just
* think of it as a Python function rather than a Javascript function now. This is an important
* distinction in skulpt because once you have Python function you cannot just call it.
* You must now use Sk.misceval.callsim to call the Python function.
*
* @param {Function} code the javascript implementation of this function
* @param {Object=} globals the globals where this function was defined.
* Can be undefined (which will be stored as null) for builtins. (is
* that ok?)
* @param {Object=} closure dict of free variables
* @param {Object=} closure2 another dict of free variables that will be
* merged into 'closure'. there's 2 to simplify generated code (one is $free,
* the other is $cell)
*
* closure is the cell variables from the parent scope that we need to close
* over. closure2 is the free variables in the parent scope that we also might
* need to access.
*
* NOTE: co_varnames and co_name are defined by compiled code only, so we have
* to access them via dict-style lookup for closure.
*
*/
Sk.builtin.func = function (code, globals, closure, closure2) {
var k;
this.func_code = code;
this.func_globals = globals || null;
if (closure2 !== undefined) {
// todo; confirm that modification here can't cause problems
for (k in closure2) {
closure[k] = closure2[k];
}
}
this.func_closure = closure;
return this;
};
goog.exportSymbol("Sk.builtin.func", Sk.builtin.func);
Sk.builtin.func.prototype.tp$name = "function";
Sk.builtin.func.prototype.tp$descr_get = function (obj, objtype) {
goog.asserts.assert(obj !== undefined && objtype !== undefined);
if (obj == null) {
return this;
}
return new Sk.builtin.method(this, obj);
};
Sk.builtin.func.prototype.tp$call = function (args, kw) {
var j;
var i;
var numvarnames;
var varnames;
var kwlen;
var kwargsarr;
var expectskw;
var name;
// note: functions expect 'this' to be globals to avoid having to
// slice/unshift onto the main args
if (this.func_closure) {
// todo; OK to modify?
args.push(this.func_closure);
}
expectskw = this.func_code["co_kwargs"];
kwargsarr = [];
if (this.func_code["no_kw"] && kw) {
name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
throw new Sk.builtin.TypeError(name + "() takes no keyword arguments");
}
if (kw) {
// bind the kw args
kwlen = kw.length;
varnames = this.func_code["co_varnames"];
numvarnames = varnames && varnames.length;
for (i = 0; i < kwlen; i += 2) {
// todo; make this a dict mapping name to offset
for (j = 0; j < numvarnames; ++j) {
if (kw[i] === varnames[j]) {
break;
}
}
if (varnames && j !== numvarnames) {
args[j] = kw[i + 1];
} else if (expectskw) {
// build kwargs dict
kwargsarr.push(new Sk.builtin.str(kw[i]));
kwargsarr.push(kw[i + 1]);
} else {
name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
throw new Sk.builtin.TypeError(name + "() got an unexpected keyword argument '" + kw[i] + "'");
}
}
}
if (expectskw) {
args.unshift(kwargsarr);
}
//print(JSON.stringify(args, null, 2));
return this.func_code.apply(this.func_globals, args);
};
Sk.builtin.func.prototype.tp$getattr = function (key) {
return this[key];
};
Sk.builtin.func.prototype.tp$setattr = function (key, value) {
this[key] = value;
};
//todo; investigate why the other doesn't work
//Sk.builtin.type.makeIntoTypeObj('function', Sk.builtin.func);
Sk.builtin.func.prototype.ob$type = Sk.builtin.type.makeTypeObj("function", new Sk.builtin.func(null, null));
Sk.builtin.func.prototype["$r"] = function () {
var name = (this.func_code && this.func_code["co_name"] && this.func_code["co_name"].v) || "<native JS>";
return new Sk.builtin.str("<function " + name + ">");
};