-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathJD_NOTES
executable file
·723 lines (492 loc) · 18.2 KB
/
JD_NOTES
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
JEFF'S NOTES ON THE REFERENCE IMPLEMENTATION
###
BUGS
- importing a non-existing package is not an error
TO DO
- remove redundancy in definer implemenation of inheritance
- set ty field in InstanceType & use ty to check member access
- set subtypes in InstanceType & use subtypes to check subtype
relationship between instance types
ISSUES
- implementation of import aliases using fixtures
- nil qualified names (e.g. local vars), do they exist?
###
JUN-13-2007
SELF HOSTED COMPILER
incrementally expand capabilities of esc until it is compiling
the whole language
- expressions
- definitions
- statements
- pragmas
- self
- full test suite
###
JUN-09-2007
CLASSES AND INTERFACES
- analyze class body -> instance and class definitions
- resolve extends -> list of base classes, list of inherited fixtures
- resolve implements -> list of base interfaces, list of inherited fixtures
- inherit fixtures -> list of instance fixtures
- implement fixtures -> error nor not
###
JUN-04-2007
NO NAMESPACE
Proposition: Public "" and no namespace have the same meaning
The following statements all create a property in Public "" if one does not
already exist
var x // outside of a package
public var x // in the unnamed package
o.x = 10
o.public::x = 10
o = {x:10}
The following expressions refer to that property
o.x
o.public::x
x
public::x
For this to be true, the qualifier 'public' is a set of two namespaces, the
current package public and Public "". In case of ambiguity, the package Public
is preferred. This mirrors the behavior open namespaces.
###
MAY-27-2007
STRUCTURAL TYPE CHECKS
type T = {x: int, y: string}
var v = {x: 10, y: "hi"}
var o : T = v
o.x // int
o.y // string
Dave's example
type ToInt = function () : int
function callThunk (f:ToInt) : int {
f();
}
callThunk (function ():* 10)
callThunk (function ():* "fish")
Both calls are allowed by the static type checker because functions returning
* are compatible with functions returning int. But the second call results in
a runtime error when the result of the call to f is returned
A program the uses a structural type can be written to a equivalent program
without the use of that structual type
function callThunk (f:*) : int {
f() to int
}
Giving users the ability to express such programs may lead to unnecessary
inefficiency, but the trade-off is easy to understand.
o.x => o.x to int
o.y => o.y to string
o.x = 46 => o.x = 46 to int
o.y = "bamboo" => o.x = "bamboo" to string
structural types allow clear expression of type expectations for components
of an object, not just a single member.
MANAGING COMPILATION UNIT
use unit foo "http://myco.com"
use unit foo {
...
}
###
MAY-24-2007
TYPE SYSTEM OVERVIEW
(from Dave)
- strict mode is an opt in static checker that does not have any affect
on programs that pass the verifier
- compatibility (~=) -- the static type check will allow but might fail
at runtime
- strict mode (conceptually) returns a new program with extra conversions
inserted. Compatible (but not subtype) assignment means insert a conversion.
let x : t = v;
... x = u;
let x : t; x = v to t;
... x = u to t;
v to t ~ (v is t) ? v : t.meta::convert (v)
- convert methods are special:
+ arguments don't go through conversion
+ constrains result type
type ToInt = function () : int
function callThunk (f:ToInt) : int {
f();
}
- what are the compatibility checks
- compatiblity
+ subtypes
+ from *
- convertibilty
+ there exists a conversion
var x : T = v
- At runtime
+ if subtype, then no op
+ else if compatible, then no op
+ else if conversion exists, then convert
+ else raise type error
- expected type captures the expected type of a field
function f(o:{x:int}) : int {
o.x = 10 // check 10 is an int
... // possibly mess with o.x indirectly
return o.x // check o.x is an int
}
- cast are guarenteed to do the same thing each time
- conversions are not guaranteed to do the same thing each time
- Return types and tail calls
- not dynamic conversion
- not allow type annotation
- don't have tail calls
- elide them under certain circumstances
- conversion is idempotent
function f() : *
g()
function g() : int
h()
function h() : *
i()
function i() : string
f()
PROPER TAIL PRAGMA
An form of retun statment that indicates that guarantees a proper tail
call is rejected in favor of a pragma such as,
use check tail
This means check that every return statement of the form
return f()
is a proper tail call. if not, raise and exception
I'm not sure this proposal is necessary. I think that smart programmers
will be able to figure it out, by testing or by help from the tool
chain, and dumb programmers won't care. The whole point of proper tail
calls is that they guarantee that memory (stack) is managed in a
certain way. No where else do we allow programmatic control over checking
of memory.
Perhpas we sholud have other 'use check' pragmas to check for other kinds
of alternative meanings of a single syntax
###
MAY-18-2007
NAMES
Names get resolved at various stages of compilation, the definition,
verification or evaluation phase. The lexical scope of a name is the
region of program text in which that name is visible without reference
to the object it lives in. The lifetime of a names is the period in
which the name exists and can be reference by any means.
HOISTING
Hoisting is a peculilar feature of ES by which a name might reside in
a scope represented by a block outside of the block it is defined in.
Except names defined by 'let' definitions, are hoisted to the nearest
enclosing global, class, or function scope. Inside of a class, static
definitions are hoisted to the class scope, and instance definitions
are hoisted to the instance scope.
DOMINATION
A name dominates a point in a program if, before hoisting, there is
no path to that point in the program that does not pass through
that definition. The principle of domination is used to constrain
the location of definitions.
* class and inteface definitions shall dominate the end of the program
* configuration names shall dominate the end of the program
* namespace definitions shall dominate the end of the region
* class and instance definitions shall dominate the end of the class
* function definitions shall dominate the end of the region
* type and let definitions shall dominate the end of the block
There are no domination constraints on var and const definitions.
DEFINITION PHASE
Names that get resolved during the definition phase are in scope from the
point of definition to the end of the scope they reside in. Because of
hoisting, the scope a name resides in may be a lexical block outside it
point of definition.
References that are resolved during the definition phase are:
* extends and implements clauses
* namespace attributes
* type definitions
* namespace definitions
* pragmas
VERIFICATION AND EVALUATION
Names that are resolved during the verification and evaluation phases
are in scope from the beginning of the block they reside in.
References that get resolved during verification are:
* type annotations
* type expressions not in type definitions
All other references are resolved during evaluation.
###
03-MAY-2007
TOPLEVEL ENVIRONMENT
Programs can be loaded dynamically, but not unloaded until the session
is over.
* To avoid definition conflicts, use packages and namespaces
* Ambiguous reference errors still may occur at compile or run time, but
those can be resolved by qualification
AS3 uses shadowing to resolve duplicate definitions. Each program closes
over its global definitions. Programs are in scope of earlier loaded
programs, but
SEPARATE COMPILATION
Separately compiled programs must behave as though they are compiled
together with the following exceptions
* top level functions in earlier loaded programs are shadowed by
those in the current program. Fixed properties, including those
of earlier programs, shadow dynamic properties that result from
execution of the current program. In other words, the global
object is shadowed by the global fixed properties
Programs compiled together or separately may share the dyanamic properties
of a global object, or not. Separately compiled programs in AS3 do not.
###
10-APR-2007
FOR-IN STATEMENTS
for ( x in y ) ;
for ( var x in y ) ;
for ( var x = f() in y ) ;
obj - expr after in
next - desugaring before each iteration
init - expression after = in binding form evaluated once before obj
###
29-MAR-2007
TYPE IDENTIFIERS
class A.<t> extends B.<t> {}
###
16-MAR-2007
IMPORT
An import pragma opens a public namespace for one or more identifiers.
import p.q.A
import r.s.*
LimitedNamespace ("A", Public "p.q")
Public "r.s"
When name resolution encounters a limited namespace, it checks that the
sought after identifier matches the namespace govenor. It is only necessary
to search for LimitedNamespaces in LexicalRefs, but doing so for all
references is simpler and compatible.
PUBLIC
'public' has two meanings inside of a package. At the top level it means
Public "packagename", at nested levels it means Public "". This is so that
programs such as the following have the expected meaning:
package p {
public class A { public static var x = 10 }
}
import p.A
print(p.A.x) // prints 10
DEFAULT NAMESPACE
Inside of a package the default namespace is Internal "packagename".
Outside of a package the default namespace is Internal "".
Users may set the default namespace with the 'use default namespace'
pragma
###
15-MAR-2007
PACKAGES
A package block introduces a package name into the global environment.
A package name used in an import pragma must be known at the time
the import pragma is encountered by the definers. Only those package
names that are imported can are used to resolve UnresolvedPath
expressions.
package p.q { }
import p.q.*
14-MAR-2007
PATH EXPRESSIONS
Dot separated lists of identifiers appear in the grammar in two places:
in package names and in member expressions. The meaning of such lists
is determined by the definer according to the following rules:
* If the first identifier in the list matches a fixture binding in
scope, then that identifier is a lexical reference and the path
is one or more object references
* If the first N identifiers match a known package name then the path
is a package name
* Otherwise the path is assumed to begin with a lexical reference to
an unknown global name followed by one or more object references
The definer builds a table of PATHs from the import directives (but not
the package definitions) it has seen.
import p.q.* => Path (["p","q"])
import r.s.A => Path (["r","s"])
var r
p.q.x.f
parser => (Path ["p","q","x","f"])
definer => (ObjRef (LexRef (QualId (PackageName "p.q", "x"), "f")))
* p.q is a package name so it qualifies x
* f is referenced through p.q::x
r.s.A
parser => (Path ["r","s","A"])
definer => (ObjRef (ObjRef (LexRef "r"), "s"), "A")
* r is a fixture, so path is an object path
a.b.c
parser => (Path ["a","b","c"])
definer => (ObjRef (ObjRef (LexRef "a"), "b"), "c")
* 'a' is neither a fixture nor the head of a package name,
so path is an object path
###
09-MAR-2007
CLASSES
Classes are compiled in two steps
* The parser creates a ClassBlock and a ClassDefinition which contains
the instanceDefinitions and classDefinitions. The ClassDefinition is
put into the ClassBlock defns list.
* The definer translates the ClassDefinition into a ClassFixture and
hoists it into the global object
###
03-MAR-2007
ERRORS
Errors might be reported by the scanner, parser, definer, verifier,
or evaluator
* Definition errors occur when a program contains conflicting definitions
or other context sensitive syntax errors that make translating the concrete
syntax of definitions into fixtures and initialisers
* Initialisation errors occur before the a block is entered when a scope
object cannot be ininitialised due to an unresolved type annotation or
uninitialised property (non-nullable without default)
* Runtime errors occur when an expression is evaluated with invalid operands
* Verifier errors occur in strict mode when certain static semantic rules
are violated. In standard mode, errors that would have been reported
by the verifier might result in initialisation or runtime errors
###
02-MAR-2007
TYPE REFS
type TYPE_EXPR = TypeRef of (TYPE_EXPR * IDENT)
Since patterns are desugared during parsing, we have to defer
the desugaring of type annotations on TypedPatterns until
they are fully known during the verifier or evaluator phases.
type t = [int]
var [x] : t = y
The type of the fixture of x gets desugared to
TypeRef (TypeName "t", "0")
which partially evaluates to,
TypeRef (ArrayType [int], "0")
and then finally,
int
###
INITS
datatype EXPR = InitExpr of INITS
type INITS = (FIXTURE_NAME * EXPR) list
Variable definitions get desugared by the parser into bindings and
initialiser statement with a list of INIT_STEPs. The definer turns
the bindings into a FIXTURES and the INIT_STEPS into an InitExpr
which wraps the fixture INITS. The definer then hoists the FIXTURES
according to their kind.
This causes a reference problem because the FIXTURES and the fixture
INITS might now be separated by any number of scopes.
The problem is solved by adding an INIT_TARGET to InitExpr. This
value is used by the evaluator to determine whether the target
property was hoisted and if so to find it on the nearest enclosing
variable scope. Changes to the ASTs are:
datatype EXPR = InitExpr of (INIT_TARGET * INITS)
and INIT_TARGET = Hoisted
| Local
| Prototype
| Static
Scopes have a flag to indicate whether or not they are a variable
object.
Initialisers with a Prototype target only occur in a ClassBlock,
and target the properties of that classes prototype object. Initialiser
with a Class target also only occur at the top level of a ClassBlock
and target properties of the class object.
###
01-MAR-2007
LET STATEMENTS
The following are equivalent,
let (x=10) { print(x) }
{let x=10; print(x) }
They translate to,
LetStmt Block {head=([x],[x=10]),body=[print(x)]}
###
EVALUATING FUNCTIONS
function ([x1,x2],[y]=a,z=10) { print('hi') }
defaults = [a,10]
head = {fxtrs=[x1,x2,y,z,$t1],
inits=[x1=$t1[0],
x2=$t1[1],
y=$t2[0],
z=$t3]}
block = {head=([],[]),
body=[print('hi')]}
Param heads (f+i) are instantiated with a list of actual
arguments and a list of default expressions. The argument
or default values get bound to temporaries, and are accessed
by the inits via GetTemp n instructions
If a required arg is not given, then either an exception
is thrown or the default value of undefined is used,
depending on the kind of function the head belongs to
Steps:
obj = evalHead env args defaults paramHead
env = pushScope env obj
ret = evalBlock env funcBlock
### 28-FEB-2007
In general a definition results in a BINDING and zero or more
INIT_STEPS.
var x:t
let (x:t=10) ...
function (x:t=10) ...
switch type .... case (x:t) ...
catch (x:t) ...
Binding {ident="x", ty="t"}
InitStep ("x","10")
These get translated by the definer into a FIXTURE binding and
an INIT binding call FIXTURES and INITS
defBinds = BINDINGS -> FIXTURES * INITS
BINDINGS = BINDING list * INIT_STEP list
BINDING = BINDING_IDENT * TYPE_EXPR option
INIT_STEP = InitStep of (BINDING_IDENT * EXPR)
| AssignStep of (EXPR * EXPR)
BINDING_IDENT = TempIdent of int
| PropIdent of IDENT
FIXTURES = (FIXTURE_NAME * FIXTURE) list
INITS = (FIXTURE_NAME * EXPR) list
FIXTURE_NAME = TempName of int
| PropName of NAME
var [x,y,[z]] = o
$t1 = o ; ValFixture, InitStep
x = $t1[0] ; ValFixture, InitStep
y = $t1[1] ; ValFixture, InitStep
$t2 = $t1[2] ; ValFixture, InitStep
z = $t2[0] ; ValFixture, InitStep
InitStmt INIT_STEPS
[x.y,[q::z]] = o
$t1 = o ; ValFixture, InitStep
x.y = $t1[0] ; AssignStep
$t2 = $t1[1] ; ValFixture, InitStep
q::z= $t2[0] ; AssignStep
Results in two temporary fixtures and several
init steps including InitSteps and AssignSteps
The parser produces a BINDINGS and a local block
statement, the definer produces the FIXTURES and
INITS
{ f=[$t1,$t2],
i=[$t1=o,$t2=$t1[1]],
s=[x.y=$t1[0],q::z=$t2[0]] }
[x.y,[q::z]] = o
let ($t1=o,$t2=$t1[1]) { x.y=$t1[0], q::z=$t2[0] }
Desugaring a pattern results in a LetStmt and a FIXTURES
ns var [x,y,[z]] = o
ns var x,y,z
let ($t1=o,$t2=$t1[2]) InitStmt ns static prototype [x=$t1[0], y=$t1[1], z = $t2[0]]
###
23-FEB-2007
Refactor Ast.Cls to reflect the various sets of fixtures and
initializers involved in creating instances. The AST types
now look like this:
and CLS =
Cls of
{ extends: NAME option,
implements: NAME list,
classFixtures: FIXTURES,
instanceFixtures: FIXTURES,
instanceInits: INITS,
constructor: CTOR option,
classType: TYPE_EXPR,
instanceType: TYPE_EXPR }
and CTOR =
Ctor of
{ settings: INITS,
func: FUNC }
and FUNC =
Func of
{ name: FUNC_NAME,
fsig: FUNC_SIG,
fixtures: FIXTURES option,
inits: STMT list,
body: BLOCK }
Instatiation goes like this:
val scope = [globalObj,classObj]
val thisObj = newObj
evalFixtures scope thisObj instanceFixtures
evalInits scope thisObj instanceInits
val paramsObj = newObj
val paramsFixtures = (#fixtures (#func (#constructor cls)))
evalFixtures scope paramsObj paramsFixtures
val paramsInits = (#inits (#func (#constructor class)))
evalInits scope paramsObj paramsInits
val settingsInits = (#settings (#constructor cls))
evalInits paramsObj::scope thisObj settingsInits
val ctorBody = (#body (#func (#constructor cls)))
evalBlock paramsObj::(thisObj::scope) thisObj ctorBody
where,
evalFixtures - allocates fixed properties on an object
evalInits - sets the value of some properties on an object
evalBlock - evaluates a block with the implicit 'this' set to an object
Changes to eval.sml are pending