summaryrefslogtreecommitdiff
path: root/module/ecmascript.m
blob: 27856d9af553c80bd87b1c3a2ac24d899fd205fc (plain)
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
ESHostobj: module
{
	#
	# extensible interface for adding host objects
	#
	# any implementation must obey the rules of the interpreter.
	# it is an error to return bogus values, and may cause
	# the interpreter to crash.
	#
	# get/put must return/set the value of property in o
	#
	# canput and hasproperty return es->true or es->false
	#
	# defaultval must return a primitive (non-object) value.
	#	this means it can't return a String object, etc.
	#
	# call gets the caller's execution context in ex.
	# the new this value is passed as an argument,
	# but no new scopechain is allocated
	# it returns a reference, which is typically just a value
	#
	# construct should make up a new object
	#
	get:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	put:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
	canput:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	hasproperty:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	delete:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
	defaultval:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
	call:		fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
	construct:	fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;
};

#
# calls to init and mkexec do the following
#	math->FPcontrol(0, Math->INVAL|Math->ZDIV|Math->OVFL|Math->UNFL|Math->INEX);
#
Ecmascript: module
{
	PATH:	con "/dis/lib/ecmascript.dis";

	#
	# an execution context
	#
	Exec: adt
	{
		#
		# well known glop
		#
		objproto:	cyclic ref Obj;
		funcproto:	cyclic ref Obj;
		strproto:	cyclic ref Obj;	
		numproto:	cyclic ref Obj;
		boolproto:	cyclic ref Obj;
		arrayproto:	cyclic ref Obj;
		dateproto:	cyclic ref Obj;
		regexpproto:	cyclic ref Obj;
		errproto:		cyclic ref Obj;
		evlerrproto:		cyclic ref Obj;
		ranerrproto:		cyclic ref Obj;
		referrproto:		cyclic ref Obj;
		synerrproto:		cyclic ref Obj;
		typerrproto:		cyclic ref Obj;
		urierrproto:		cyclic ref Obj;
		interrproto:		cyclic ref Obj;
		
		global:		cyclic ref Obj;
		this:		cyclic ref Obj;
		scopechain:	cyclic list of ref Obj;

		error:		string;
		errval:		cyclic ref Val;

		#
		# private, keep out
		#
		stack:		cyclic array of ref Ref;
		sp:	int;
	};

	#
	# must be called at the dawn of time
	# returns error string
	init:	fn(): string;

	#
	# initialize a new global execution context
	# if go is supplied, it's the global object
	# if not, one is made up automatically
	#
	mkexec:		fn(go: ref Obj): ref Exec;

	#
	# throw a runtime error
	# msg ends up in ex.error, and an
	# "ecmascript runtime error" is raised
	#
	RUNTIME:	con "ecmascript runtime error";
	runtime:	fn(ex: ref Exec, o: ref Obj, msg: string);

	# runtime errors
	EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, InternalError: ref Obj;

	#
	# debug flags: array of 256 indexed by char
	#
	# e	print ops as they are executed
	# f	abort on an internal error
	# p	print parsed code
	# r	abort on any runtime error
	# v	print value of expression statements
	#
	debug: array of int;

	#
	# parse and runt the source string
	#
	eval:	fn(ex: ref Exec, src: string): Completion;

	Re: type ref Arena;

	# the fundamental data structure
	Obj: adt
	{
		props:		cyclic array of ref Prop;
		prototype:	cyclic ref Obj;		# some builtin properties
		val:		cyclic ref Val;
		call:		cyclic ref Call;
		construct:	cyclic ref Call;
		class:		string;
		host:		ESHostobj;		# method suite for host objects
		re:		Re;				# compiled regexp for RegExp objects
	};

	Call: adt
	{
		params:		array of string;
		code:		cyclic ref Code;
		ex:		cyclic ref Exec;
	};

	# attributes
	ReadOnly, DontEnum, DontDelete: con 1 << iota;
	Prop: adt
	{
		attr:		int;
		name:		string;
		val:		cyclic ref RefVal;
	};

	# an extra level of indirection, because sometimes properties are aliased
	RefVal: adt
	{
		val:		cyclic ref Val;
	};

	# types of js values
	TUndef, TNull, TBool, TNum, TStr, TObj, TRegExp, NoHint: con iota;
	Val: adt
	{
		ty:		int;
		num:		real;
		str:		string;
		obj:		cyclic ref Obj;
		rev:		ref REval;
	};

	# regular expression
	REval: adt
	{
		p: string;
		f: string;
		i: int;
	};

	# intermediate result of expression evaluation
	Ref: adt
	{
		isref:		int;
		val:		ref Val;
		base:		ref Obj;
		name:		string;				# name of property within base
	};

	# completion values of statements
	CNormal, CBreak, CContinue, CReturn, CThrow: con iota;
	Completion: adt
	{
		kind:		int;
		val:		ref Val;
		lab:		string;
	};

	Code: adt
	{
		ops:		array of byte;			# all instructions
		npc:		int;				# end of active portion of ops
		vars:		cyclic array of ref Prop;	# variables defined in the code
		ids:		array of string;		# ids used in the code
		strs:		array of string;		# string literal
		nums:		array of real;			# numerical literals
		fexps:	cyclic array of ref Obj;	# function expressions
	};

	#
	# stuff for adding host objects
	#
	# ecmascript is also a host object;
	get:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	put:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string, val: ref Ecmascript->Val);
	canput:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	hasproperty:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string): ref Ecmascript->Val;
	delete:		fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, property: string);
	defaultval:	fn(ex: ref Ecmascript->Exec, o: ref Ecmascript->Obj, tyhint: int): ref Ecmascript->Val;
	call:		fn(ex: ref Ecmascript->Exec, func, this: ref Ecmascript->Obj, args: array of ref Ecmascript->Val, eval: int): ref Ecmascript->Ref;
	construct:	fn(ex: ref Ecmascript->Exec, func: ref Ecmascript->Obj, args: array of ref Ecmascript->Val): ref Ecmascript->Obj;

	#
	# return the named variable from the scope chain sc
	#
	bivar:		fn(ex: ref Exec, sc: list of ref Obj, s: string): ref Val;

	#
	# return the nth argument value, or undefined if too far
	#
	biarg:		fn(args: array of ref Val, n: int): ref Val;

	#
	# make up a new object
	# most often called as mkobj(ex.objproto, "Object")
	#
	mkobj:		fn(proto: ref Obj, class: string): ref Obj;

	#
	# object installation helpers
	#
	Builtin: adt
	{
		name:	string;
		val:	string;
		params:	array of string;
		length:	int;
	};
	biinst:		fn(o: ref Obj, bi: Builtin, proto: ref Obj, h: ESHostobj): ref Obj;
	biminst:	fn(o: ref Obj, bis: array of Builtin, proto: ref Obj, h: ESHostobj);

	#
	# instantiate a new variable inside an object
	#
	varinstant:	fn(in: ref Obj, attr: int, name: string, val: ref RefVal);

	#
	# various constructors
	#
	objval:		fn(o: ref Obj): ref Val;
	strval:		fn(s: string): ref Val;
	numval:		fn(r: real): ref Val;
	valref:		fn(v: ref Val): ref Ref;

	#
	# conversion routines defined in section 9
	#
	toPrimitive:	fn(ex: ref Exec, v: ref Val, ty: int): ref Val;
	toBoolean:	fn(ex: ref Exec, v: ref Val): ref Val;
	toNumber:	fn(ex: ref Exec, v: ref Val): real;
	toInteger:	fn(ex: ref Exec, v: ref Val): real;
	toInt32:	fn(ex: ref Exec, v: ref Val): int;
	toUint32:	fn(ex: ref Exec, v: ref Val): big;
	toUint16:	fn(ex: ref Exec, v: ref Val): int;
	toString:	fn(ex: ref Exec, v: ref Val): string;
	toObject:	fn(ex: ref Exec, v: ref Val): ref Obj;

	#
	# simple coercion routines to force
	# Boolean, String, and Number values to objects and vice versa
	#
	coerceToObj:	fn(ex: ref Exec, v: ref Val): ref Val;
	coerceToVal:	fn(v: ref Val): ref Val;

	#
	# object/value kind checkers
	#
	isstrobj:	fn(o: ref Obj): int;
	isnumobj:	fn(o: ref Obj): int;
	isboolobj:	fn(o: ref Obj): int;
	isdateobj:	fn(o: ref Obj): int;
	isregexpobj: fn(o: ref Obj): int;
	isarray:	fn(o: ref Obj): int;
	isstr:		fn(v: ref Val): int;
	isnum:		fn(v: ref Val): int;
	isbool:		fn(v: ref Val): int;
	isobj:		fn(v: ref Val): int;

	#
	# well-known ecmascript primitive values
	#
	undefined:	ref Val;
	true:		ref Val;
	false:		ref Val;
	null:		ref Val;

	# regexp data structures

	refRex: type int;	# used instead of ref Rex to avoid circularity

	Set: adt {				# character class
		neg: int;			# 0 or 1
		ascii: array of int;		# ascii members, bit array
		unicode: list of (int,int);	# non-ascii char ranges
		subset: cyclic list of ref Set;
	};

	Nstate: adt{
		m: int;
		n: int;
	};

	Rex: adt {		# node in parse of regex, or state of fsm
		kind: int;	# kind of node: char or ALT, CAT, etc
		left: refRex;	# left descendant
		right: refRex;	# right descendant, or next state
		set: ref Set;	# character class
		pno: int;
		greedy: int;
		ns: ref Nstate;
	};

	Arena: adt {		# free store from which nodes are allocated
		rex: array of Rex;		
		ptr: refRex;	# next available space
		start: refRex;	# root of parse, or start of fsm
		pno: int;
	};
};