/
N4JS.xcore
2119 lines (1908 loc) · 64 KB
/
N4JS.xcore
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
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* Copyright (c) 2016 NumberFour AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* NumberFour AG - Initial API and implementation
*/
@Ecore(nsURI="http://www.numberfour.eu/ide/n4js/N4JS")
@GenModel(fileExtensions="n4js",
// modelPluginID="eu.numberfour.n4js.model",
rootExtendsClass="eu.numberfour.n4js.utils.emf.ProxyResolvingEObjectImpl",
modelDirectory="/eu.numberfour.n4js.model/emf-gen",
forceOverwrite="true",
updateClasspath="false",
literalsInterface="true",
loadInitialization="false",
complianceLevel="8.0",
copyrightFields="false",
copyrightText="Copyright (c) 2016 NumberFour AG.\nAll rights reserved. This program and the accompanying materials\nare made available under the terms of the Eclipse Public License v1.0\nwhich accompanies this distribution, and is available at\nhttp://www.eclipse.org/legal/epl-v10.html\n\nContributors:\n NumberFour AG - Initial API and implementation",
language="")
package eu.numberfour.n4js.n4JS
import eu.numberfour.n4js.ts.typeRefs.ParameterizedTypeRef
import eu.numberfour.n4js.ts.typeRefs.ParameterizedTypeRefIterable
import eu.numberfour.n4js.ts.typeRefs.TypeRef
import eu.numberfour.n4js.ts.types.IdentifiableElement
import eu.numberfour.n4js.ts.types.TClass
import eu.numberfour.n4js.ts.types.TEnum
import eu.numberfour.n4js.ts.types.TEnumLiteral
import eu.numberfour.n4js.ts.types.TExportableElement
import eu.numberfour.n4js.ts.types.TField
import eu.numberfour.n4js.ts.types.TFormalParameter
import eu.numberfour.n4js.ts.types.TFunction
import eu.numberfour.n4js.ts.types.TGetter
import eu.numberfour.n4js.ts.types.TInterface
import eu.numberfour.n4js.ts.types.TMember
import eu.numberfour.n4js.ts.types.TModule
import eu.numberfour.n4js.ts.types.TSetter
import eu.numberfour.n4js.ts.types.TStructField
import eu.numberfour.n4js.ts.types.TStructGetter
import eu.numberfour.n4js.ts.types.TStructMember
import eu.numberfour.n4js.ts.types.TStructMethod
import eu.numberfour.n4js.ts.types.TStructSetter
import eu.numberfour.n4js.ts.types.TVariable
import eu.numberfour.n4js.ts.types.TypableElement
import eu.numberfour.n4js.ts.types.Type
import eu.numberfour.n4js.ts.types.TypeVariable
import eu.numberfour.n4js.ts.types.TypingStrategy
import eu.numberfour.n4js.utils.EcoreUtilN4
import java.util.Collections
import java.util.Iterator
import org.eclipse.emf.common.util.BasicEList
import org.eclipse.emf.ecore.EObject
type IteratorOfExpression wraps Iterator<Expression>
type IteratorOfYieldExpression wraps Iterator<YieldExpression>
type IteratorOfStatement wraps Iterator<Statement>
type IteratorOfReturnStatement wraps Iterator<ReturnStatement>
/**
* Common interface for all AST elements providing a name.
* Note that this is different from the types base class IdentifiableElement on purpose, in order
* to help distinguishing between AST and types model.
*
* Could be modeled as abstract super class, but we do not want to run into problems with Xcore and
* operation overriding.
*/
interface NamedElement {
op String getName()
}
// ****************************************************************************************************
// Root element is a script,
// cf. http://wiki.ecmascript.org/doku.php?id=harmony:modules
// ****************************************************************************************************
/*
* Root element
*/
class Script extends VariableEnvironmentElement, AnnotableElement {
contains Annotation[] annotations
contains ScriptElement[] scriptElements
refers transient TModule module
/**
* Flag set after linking-phase is closed. Model is in it's final state and computation
* of element-usage is sealed.
*/
transient boolean flaggedUsageMarkingFinished
}
abstract class ScriptElement {
}
// ****************************************************************************************************
// Export and Import
// ****************************************************************************************************
class ExportDeclaration extends AnnotableScriptElement {
contains ExportableElement exportedElement
contains Expression defaultExportedExpression
contains ExportSpecifier[] namedExports
boolean wildcardExport
boolean defaultExport
refers TModule reexportedFrom
}
class ExportSpecifier {
contains IdentifierRef element
String alias
}
/*
* TODO add support for separated export statements, i.e. code like this:
* <pre>
* class C {}
* export {C as X}
* </pre>
*/
abstract class ExportableElement {
op boolean isExported() {
return this.eContainer instanceof ExportDeclaration;
}
op boolean isExportedAsDefault() {
return isExported() && (this.eContainer as ExportDeclaration).isDefaultExport;
}
op String getExportedName() {
if(isExported) {
val exportDecl = this.eContainer as ExportDeclaration;
if(exportDecl.defaultExport) {
// note: using name "default" below seems like a dirty hack, but this is actually consistent with how
// ES6 default import/export is defined ("The default export is actually just a named export with the
// special name default.", A. Rauschmayer in "Exploring ES6", Section 16.4.5.2).
// For example, the following two lines are equivalent in ES6:
// import myFoo from "Other"
// import {default as myFoo} from "Other"
return "default";
}
val me = this;
return switch(me) {
NamedElement: me.name
IdentifiableElement: me.name
};
}
return null;
}
/**
* Returns true if element is a top-level declaration in the script, this is also true if element is actually exported
* (and is a child of an export statements). This should be true for most declarations, however some (as function declaration) may be nested (and
* transpiled to expressions later).
*/
op boolean isToplevel() {
if (eContainer instanceof ExportDeclaration) {
return eContainer.eContainer instanceof Script
}
return eContainer instanceof Script;
}
}
class ImportDeclaration extends AnnotableScriptElement {
contains ImportSpecifier[] importSpecifiers
boolean importFrom
refers TModule module
}
abstract class ImportSpecifier {
/** Transient flag to keep track of actual usage during scoping.
* See {@code eu.numberfour.n4js.scoping.imports.OriginAwareScope}
*/
transient boolean flaggedUsedInCode
}
class NamedImportSpecifier extends ImportSpecifier {
refers TExportableElement importedElement
transient String importedElementAsText
String alias
op boolean isDefaultImport() {
false
}
}
class DefaultImportSpecifier extends NamedImportSpecifier {
op String getAlias() {
return importedElementAsText;
}
op boolean isDefaultImport() {
true
}
}
class NamespaceImportSpecifier extends ImportSpecifier, TypeDefiningElement {
boolean declaredDynamic
String alias
}
// ****************************************************************************************************
// Abstract Base Elements
// ****************************************************************************************************
/*
* Element which may contain a declared type, typically a typed element.
* Some rare elements have a declared type contained in some child element, e.g. getters.
*/
interface TypeProvidingElement {
op TypeRef getDeclaredTypeRef()
}
/* A typed element, such as Variable, a Member or a NameValuePair, may provide a declared type.
* Note firstly that the declared type may be empty, and secondly that the declared type is different from the
* inferred type. The latter means that it is of course possible to infer the type of non-typed elements,
* such as expressions!
* Note that a function definition is not a typed element, as it is a type definition rather than a type reference.
* This is indicated by possibly unbound type parameters!
*/
// TODO reconsider the approach on how to handle inferred types, accessors on the model etc
abstract class TypedElement extends TypeProvidingElement {
contains TypeRef declaredTypeRef
contains TypeRef bogusTypeRef
}
// TODO jvp: check ECMAScript spec and probably update this environment things with function and classes
/* Element to which a lexical environment (containing a dictionary of variables, the variable environment) may be
* associated with
* (cf. ECMAScript Language Specification. International Standard ECMA-262, 5.1 Edition, paragraph 10.2).
* This is true for Script, FunctionDefintion, WithStatement, and CatchBlock.
* Note that this is not true for classes (or object literals in general),
* as members are only accessed via "this" (which is not modeled as a variable, but as an expression
* allowing access to properties of the current context).
*/
abstract class VariableEnvironmentElement {
/**
* Tells if this variable environment element applies only to block scoped elements, i.e. let & const.
* Returns <code>true</code> for {@link Block} and {@link ForStatement}, <code>false</code> otherwise.
*/
op boolean appliesOnlyToBlockScopedElements() {
false // overridden by Block, ForStatement, and SwitchStatment to return true
}
}
/* Possible target to which a this expression may be bound. This is used in the type inferrer.
* Note that this is not bound by local scoping, as \'this\' is not a variable (or cross reference)!
*/
abstract class ThisTarget {
}
/*
* Depending on the mode the this keyword may be bound to the thisArg, implicitly
* set in case of function calls. Thus, a "ThisArgProvider" may be a function,
* which could also be a getter or setter (which are not defined as function definitions).
* (cf ECMAScript 10.4.3 Entering Function Code)
* Note that this is not bound by local scoping, as \'this\' is not a variable (or cross reference)!
*/
abstract class ThisArgProvider {
}
/* Abstract super type of declared variables (VariableDeclaration), formal parameters (FormalParameter),
* and variables declared in catch clause (CatchVariable). Variables may be used on the left-hand side
* of assignments. Note that variables are also named (NamedElement) and typed (TypedElement).
*/
abstract class Variable extends TypedElement, IdentifiableElement, NamedElement {
/**
* Returns true if variable is defined as const. This is only true for variables declared in const statement.
*/
op boolean isConst() {
return false;
}
}
// ****************************************************************************************************
// N4 Annotations
// ****************************************************************************************************
/*
* Abstract base class or all elements which can be annotated.
* The associated annotations are obtained via {@link #getAnnotations()}.
* Concrete annotable elements may use an {@link AbstractAnnotationList} or a containment
* reference to hold the annotations.
*
* Annotations are best retrieved via AnnotationDefinition.hasAnnotation(), as this takes transitivity into account as well.
*/
abstract class AnnotableElement {
/**
* Returns the owned annotations of the AST element; however it is recommended to access
* annotations only via AnnotationDefinition and use type model annotation (or fields) if ever possible.
*/
op Annotation[] getAnnotations()
}
abstract class AnnotableScriptElement extends AnnotableElement, ScriptElement {
contains AnnotationList annotationList
op Annotation[] getAnnotations() {
annotationList?.annotations ?: emptyEList
}
}
abstract class AnnotableExpression extends AnnotableElement, Expression {
contains ExpressionAnnotationList annotationList
op Annotation[] getAnnotations() {
annotationList?.annotations ?: emptyEList
}
}
/*
* A simple container for a bunch of annotations.
*/
abstract class AbstractAnnotationList {
/**
* AST Annotations, it is recommended to use
* TAnnotations or corresponding fields defined in the types model.
*/
contains Annotation[] annotations
}
/*
* An {@link AnnotationList} holds annotations and can be directly contained
* in a script, block or may be a placeholder for exported elements.
* This allows to handle syntax errors in the input file gracefully while
* being able to left factor the grammar to make it parseable.
*/
class AnnotationList extends AbstractAnnotationList, ScriptElement, Statement, ExportableElement {
}
/*
* An {@link ExpressionAnnotationList} holds annotations and can be a placeholder
* where an {@link Expression} is expected.
* This allows to handle syntax errors in the input file gracefully while
* being able to left factor the grammar to make it parseable.
*/
class ExpressionAnnotationList extends AbstractAnnotationList, Expression {
}
/**
* AST Annotation element. Most information defined in annotations is either
* available via fields or corresponding annotations in the type model. It
* is recommended to use the information found in the type model (see
* TAnnotation) instead of recurring to the AST annotations.
*/
class Annotation extends NamedElement {
String name
contains AnnotationArgument[] args
/**
* Returns the annotated element. This is usually an AnnotableElement, but in some cases
* annotations are hold in an annotation list. In the later case, the container of the list
* is returned.
*/
op EObject getAnnotatedElement() {
if (eContainer instanceof AbstractAnnotationList) {
return eContainer.eContainer;
}
return eContainer
}
}
abstract class AnnotationArgument {
/** Convenience method, returns the literal or type reference */
op EObject value()
/**
* Convenience method, returns the value of the argument as string, or null, if no such value is present.
*/
op String getValueAsString() {
val value = value();
if (value == null) {
return null;
}
return switch (value) {
Literal: value.getValueAsString()
TypeRef: value.typeRefAsString
default: value.toString()
}
}
}
/**
* AST Annotation Argument with a literal, it is recommended to use
* TAnnotationStringArgument or corresponding type model related field
* to access this information.
*/
class LiteralAnnotationArgument extends AnnotationArgument {
contains Literal literal
op Literal value() {
return literal;
}
}
/**
* AST Annotation Argument with a type reference, it is recommended to use
* TAnnotationTypeRefArgument or corresponding type model related field
* to access this information.
*/
class TypeRefAnnotationArgument extends AnnotationArgument {
contains TypeRef typeRef
op TypeRef value() {
return typeRef;
}
}
// ****************************************************************************************************
// ECMAScript Elements
// ****************************************************************************************************
/*
* Base class for functions or getter/setter.
*/
abstract class FunctionOrFieldAccessor extends AnnotableElement, VariableEnvironmentElement, ThisArgProvider, TypableElement {
contains Block body
op String getName()
/** Transient local arguments variable. Access through #getLocalArgumentsVariable() */
contains transient LocalArgumentsVariable _lok
/** Lazy initialized reference to transient localArgurmentsVariable */
op LocalArgumentsVariable getLocalArgumentsVariable() {
if (_lok === null) {
val newLok = N4JSFactory::eINSTANCE.createLocalArgumentsVariable;
newLok.name = "arguments";
EcoreUtilN4.doWithDeliver(false,
[|
_lok = newLok;
], this);
}
return _lok;
}
op boolean isReturnValueOptional() {
return false;
}
/**
* Default implementation, always returns false (since accessors cannot be async), overridden in FunctionDefinition
*/
op boolean isAsync() {
return false;
}
op IdentifiableElement getDefinedFunctionOrAccessor() {
val _this = this;
return switch(_this) {
FunctionDefinition: _this.definedType
FieldAccessor: _this.definedAccessor
};
}
}
/* A function definition is either a FunctionDeclaration, a FunctionExpression or a MethodDeclaration.
* Note that, since an anonymous function expression has no name, the function definition is not a named element.
*/
abstract class FunctionDefinition extends FunctionOrFieldAccessor, TypeDefiningElement {
contains FormalParameter[] fpars
/*
* Tells if the return value is optional.
*/
op boolean isReturnValueOptional() {
return (definedFunction!==null && definedFunction.returnValueOptional) // see (*) below
|| (returnTypeRef!==null && returnTypeRef.followedByQuestionMark);
// regarding (*) above: in case of arrow functions, the optionality of the return value can be "inferred" from a
// type expectation and in this case the 'returnValueOptional' flag will be true only in the defined function in
// the TModule; hence, it is important to check for an existing defined function here.
}
/*
* Developer-provided hint for the return type of this FunctionDefinition.
* In case it's not provided, this reference remains null.
* In contrast, reference definedType contains a TFunction whose returnTypeRef is always non-null irrespective of whether the hint was provided or not.
*/
contains TypeRef returnTypeRef
/*
* Whether the function is a generator
*/
boolean generator
/**
* Whether the function has been defined with the async keyword. In order to query if a function definition is
* async, use isAsync as this is maybe derived from other fields.
*/
boolean declaredAsync
/**
* Default implementation just returns declaredAsync value, also overrides default implementation in FunctionOrFieldAcccessor
*/
op boolean isAsync() {
return declaredAsync;
}
/* Convenience method returning the 'definedType' if it is a TFunction, otherwise <code>null</code>. */
op TFunction getDefinedFunction() {
val defType = definedType;
return if(defType instanceof TFunction) { defType };
}
}
/*
* Base class for getters and setter, of either object literals (PropertyG/SetterDeclaration) or classes (N4G/SetterDeclaration).
*/
abstract class FieldAccessor extends FunctionOrFieldAccessor, TypeProvidingElement, PropertyNameOwner { // note: super-class is TypableElement
boolean declaredOptional
/*
* The declared type of the field accessor, which is either the return type of the getter or the type of the formal parameter in case of a setter
*/
op TypeRef getDeclaredTypeRef() {
return null;
}
/*
* Returns the defined FieldAccessor in the TModule, either a TGetter or a TSetter.
*/
op eu.numberfour.n4js.ts.types.FieldAccessor getDefinedAccessor()
op boolean isOptional() {
return declaredOptional;
}
}
// TODO jvp: probably need operation getTypeRef(), should be similarly handled as variables
/**
* Function declarations should only be contained in the script or functions, that is, they should not be nested in blocks.
* This is ensured by the ASTStructureValidator.
*/
class FunctionDeclaration extends AnnotableScriptElement, ModifiableElement, Statement, FunctionDefinition , GenericDeclaration, ExportableElement, NamedElement {
/*
* Function declarations are not treated as identifiable elements, as the binding should
* refer to the inferred TFunction rather than this declaration. The name is mandatory.
*/
String name
/**
* Returns true if type is declared as external.
*/
op boolean isExternal() {
declaredModifiers.contains(N4Modifier.EXTERNAL)
}
}
class FunctionExpression extends FunctionDefinition, AnnotableExpression, GenericDeclaration, NamedElement {
/*
* Function expression are not treated as identifiable elements, as the binding should
* refer to the inferred TFunction rather than this declaration. The name is optional.
*/
String name
/**
* Whether this function expression was defined as an arrow function or not.
* Overridden in subclass ArrowFunction.
*/
op boolean isArrowFunction() { false }
// /*
// * Convenience method, returns true if the function is a property's value of
// * an ObjectLiteral or if it is a method.
// */
// op boolean isPropertyOrMethod() {
// return eContainer!=null && eContainer.eContainer instanceof ObjectLiteral;
// }
}
class ArrowFunction extends FunctionExpression {
/**
* Whether this arrow function has braces around its (single or multiple statements) body.
*/
boolean hasBracesAroundBody
op boolean isArrowFunction() { true }
/**
* This method reports whether the body of the lambda consists of a single expression not enclosed in braces.
*
* The presence of enclosing braces implies block semantics for the lambda's body,
* ie a block encloses statements (even if only one, an expression statement).
* As usual, the block can be void-typed or some-value-typed;
* the latter case requires the presence of explicit return-some-value statements.
*
* An arrow function lacking braces and having a body consisting of just:
* - return-some-value --- is malformed (syntax error)
* - return; --- is malformed (syntax error)
* - expr --- is ok, where expr instanceof ExpressionStatement;
* otherwise malformed and caught by the grammar.
*
* This method returns true only for the last case above.
* Please notice that isSingleExprImplicitReturn() === true doesn't allow drawing conclusion on the type of the lambda body,
* i.e. it could be either void-typed (eg, expr denotes the invocation of a void method)
* or some-value-typed. An implicit return is warranted only in the latter sub-case.
*/
op boolean isSingleExprImplicitReturn() {
arrowFunction &&
!hasBracesAroundBody &&
(body != null) &&
!body.statements.isEmpty &&
(body.statements.get(0) instanceof ExpressionStatement)
}
/**
* The lambda's implicit return expression (precondition: isSingleExprImplicitReturn).
*/
op Expression implicitReturnExpr() {
if (isSingleExprImplicitReturn) {
// the expression below never throws because the condition above guards against that
(body.statements.get(0) as ExpressionStatement).expression
} else {
null
}
}
}
/** Implicit variable in function and method bodies referencing all passed in argument values. */
class LocalArgumentsVariable extends Variable {
op String getName() {
return "arguments"
}
}
class FormalParameter extends AnnotableElement, Variable {
contains Annotation[] annotations
boolean variadic
refers transient TFormalParameter definedTypeElement
/* A formal parameter with a default value in a function 'foo' can look like this:
* foo(defParam = undefined){}, with 'undefined' as the initializing expression, which can be omitted.
* When the initializer is omitted, it evaluates to 'undefined'. To know whether a parameter is a
* default parameter, we need to know about the existence of the initializer assignment sign '='.
* Hence, the following boolean property exists. */
boolean hasInitializerAssignment
/* The initializer can be null despite being a default parameter. */
contains Expression initializer
contains BindingPattern bindingPattern
}
class Block extends Statement, VariableEnvironmentElement {
contains Statement[] statements
/** See {@link VariableEnvironmentElement#appliesOnlyToBlockScopedElements()}. */
op boolean appliesOnlyToBlockScopedElements() {
true
}
/*
* Returns all return expressions contained in this block, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfExpression getAllExpressions() {
EcoreUtilN4.getAllContentsFiltered(this, [!(it instanceof FunctionDefinition)]).filter(Expression)
}
/*
* Returns all return yield expressions contained in this block, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfYieldExpression getAllYieldExpressions() {
getAllExpressions().filter(YieldExpression)
}
/*
* Returns all return yield expressions contained in this block that dont't return any value, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfYieldExpression getAllVoidYieldExpressions() {
getAllYieldExpressions().filter([it.expression == null])
}
/*
* Returns all return yield expressions contained in this block that return some value, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfYieldExpression getAllNonVoidYieldExpressions() {
getAllYieldExpressions().filter([it.expression != null])
}
/*
* True iff one or more yield expressions exist in this block that return some value.
*/
op boolean hasNonVoidYield() {
!(allNonVoidYieldExpressions.isEmpty)
}
/*
* Returns all statements that belong to this block. This includes statements of nested blocks
* but excludes statements in nested classes, nested functions, etc.
*/
op IteratorOfStatement getAllStatements() {
EcoreUtilN4.getAllContentsFiltered(this, [!(it instanceof Expression || it instanceof FunctionDefinition)]).
filter(Statement)
}
/*
* Returns all return statements contained in this block (whether they return a value or not), including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfReturnStatement getAllReturnStatements() {
getAllStatements.filter(ReturnStatement)
}
/*
* Returns all return statements contained in this block that return some value, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfReturnStatement getAllNonVoidReturnStatements() {
getAllReturnStatements.filter([it.expression != null])
}
/*
* Returns all return statements contained in this block that don't return any value, including those in nested blocks
* but without delving into nested classes, or into nested expressions including functions.
*/
op IteratorOfReturnStatement getAllVoidReturnStatements() {
getAllReturnStatements.filter([it.expression == null])
}
/*
* True iff one or more return statements exist in this block that return some value.
*/
op boolean hasNonVoidReturn() {
!(allNonVoidReturnStatements.isEmpty)
}
}
class Statement extends ScriptElement {
}
enum VariableStatementKeyword {
^var
, const
, let
}
/**
* Base class for elements containing variable declarations.
* <p>
* IMPORTANT: the variable declarations need not be <em>directly</em> contained! In order to support destructuring, a
* {@link VariableBinding} and other destructuring-related nodes might appear in between. To get from a
* {@link VariableDeclaration} to its <code>VariableDeclarationContainer</code> use method
* {@link eu.numberfour.n4js.n4JS.N4JSASTUtils#getVariableDeclarationContainer(VariableDeclaration)}.
*/
abstract class VariableDeclarationContainer {
contains VariableDeclarationOrBinding[] varDeclsOrBindings
VariableStatementKeyword varStmtKeyword
op VariableDeclaration[] getVarDecl() {
varDeclsOrBindings.map[it.getVariableDeclarations()].flatten.toEList
}
op boolean isBlockScoped() {
switch(varStmtKeyword) {
case LET: true
case CONST: true
case VAR: false
default: throw new UnsupportedOperationException("unsupported enum literal: " + varStmtKeyword)
}
}
}
class VariableStatement extends Statement, VariableDeclarationContainer {
}
class ExportedVariableStatement extends VariableStatement, ExportableElement, AnnotableScriptElement, ModifiableElement {
/**
* Returns true if type is declared as external.
*/
op boolean isExternal() {
declaredModifiers.contains(N4Modifier.EXTERNAL)
}
}
abstract class VariableDeclarationOrBinding {
op VariableDeclaration[] getVariableDeclarations() {
switch(this) {
VariableDeclaration: #[this as VariableDeclaration].toEList
VariableBinding: eAllContents.filter(VariableDeclaration).toEList
default: #[].toEList
}
}
}
class VariableBinding extends VariableDeclarationOrBinding {
contains BindingPattern pattern
contains Expression expression
}
class ExportedVariableBinding extends VariableBinding {
/*
* Link to variable information stored in the Xtext index.
*/
refers transient TVariable definedVariable
}
class VariableDeclaration extends VariableDeclarationOrBinding, AnnotableElement, Variable {
contains Annotation[] annotations
contains Expression expression
/**
* Returns true if variable is declared as const.
*/
op boolean isConst() {
if (eContainer instanceof VariableStatement) {
return (eContainer as VariableStatement).varStmtKeyword === VariableStatementKeyword.CONST
}
if (eContainer instanceof ForStatement) {
return (eContainer as ForStatement).varStmtKeyword === VariableStatementKeyword.CONST
}
return false;
}
}
class ExportedVariableDeclaration extends VariableDeclaration {
/*
* Link to variable information stored in the Xtext index.
*/
refers transient TVariable definedVariable
}
class EmptyStatement extends Statement {
}
class ExpressionStatement extends Statement {
contains Expression expression
}
class IfStatement extends Statement {
contains Expression expression
contains Statement ifStmt
contains Statement elseStmt
}
class IterationStatement extends Statement {
contains Statement statement
contains Expression expression
}
class DoStatement extends IterationStatement {
}
class WhileStatement extends IterationStatement {
}
class ForStatement extends VariableDeclarationContainer, IterationStatement, VariableEnvironmentElement {
contains Expression initExpr
contains Expression updateExpr
boolean forIn
boolean forOf
op boolean isForPlain() {
return !forIn && !forOf;
}
/** See {@link VariableEnvironmentElement#appliesOnlyToBlockScopedElements()}. */
op boolean appliesOnlyToBlockScopedElements() {
true
}
}
class ContinueStatement extends Statement {
refers LabelledStatement label
}
class BreakStatement extends Statement {
refers LabelledStatement label
}
class ReturnStatement extends Statement {
contains Expression expression
}
class WithStatement extends Statement, VariableEnvironmentElement {
contains Expression expression
contains Statement statement
}
// bug in XCore maven plugin:
// [ERROR] 0 [main] ERROR org.eclipse.xtext.generator.LanguageConfig - Error loading 'classpath:/eu.numberfour.n4js/N4JS.xcore': [XtextLinkingDiagnostic: null:254 Couldn't resolve reference to JvmType 'CaseClause'.
// type CaseClauseIterable wraps Iterable<CaseClause>
class SwitchStatement extends Statement, VariableEnvironmentElement {
/*
* The discriminant of the switch statement.
*/
contains Expression expression
/*
* All cases of the switch statement, i.e. all CaseClauses and not more than one DefaultClause.
*/
contains AbstractCaseClause[] cases
/** See {@link VariableEnvironmentElement#appliesOnlyToBlockScopedElements()}. */
op boolean appliesOnlyToBlockScopedElements() {
true
}
/*
* Convenience method returns default clause if switch statement defines such as clause or null.
*/
op DefaultClause getDefaultClause() {
cases.findFirst[it instanceof DefaultClause] as DefaultClause
}
/*
* Convenience method filters all CaseClauses from clauses.
*/
op CaseClause[] getCaseClauses() {
// TODO change to Iterable
cases.filter(CaseClause).toEList
}
}
abstract class AbstractCaseClause {
/*
* The consequent of the clause.
*/
contains Statement[] statements
}
class CaseClause extends AbstractCaseClause {
/*
* The test of the case clause.
*/
contains Expression expression
}
class DefaultClause extends AbstractCaseClause {
}
/*
* Statement with a label, which can be referred to by a break or continue statement.
* <p><small>"labelled" or "labeled" -- this is a different question, only comparable to
* "modelling" vs. "modeling". The ECMAScript spec uses the british version: labelled.</small>
*/
class LabelledStatement extends Statement, NamedElement {
String name
contains Statement statement
}
class ThrowStatement extends Statement {
/*
* The argument to be thrown.
*/
contains Expression expression
}
class TryStatement extends Statement {
contains Block block
contains CatchBlock ^catch
contains FinallyBlock ^finally
}
abstract class AbstractCatchBlock {
contains Block block
}
class CatchBlock extends AbstractCatchBlock, VariableEnvironmentElement {
contains CatchVariable catchVariable
}
class CatchVariable extends Variable {
contains BindingPattern bindingPattern
}
class FinallyBlock extends AbstractCatchBlock {
}
class DebuggerStatement extends Statement {
}
// TODO (sz): What's the purpose of a type PrimaryExpression?
class PrimaryExpression extends Expression {
}
class ParenExpression extends PrimaryExpression {
contains Expression expression
/**
* Return IsValidSimpleAssignmentTarget of expression.
*/
op boolean isValidSimpleAssignmentTarget() {
if (expression !== null)
return expression.isValidSimpleAssignmentTarget
// avoid follow up problem
return true
}
}
class IdentifierRef extends PrimaryExpression, StrictModeRelevant, eu.numberfour.n4js.ts.typeRefs.Versionable {
refers IdentifiableElement ^id
transient String idAsText
/**
* IdentifierReference : Identifier
* 1. If this IdentifierReference is contained in strict mode code and StringValue of Identifier is "eval" or "arguments", return false.
* 2. Return true.
* IdentifierReference : yield
* 1. Return true.
*/
op boolean isValidSimpleAssignmentTarget() {
if (strictMode) {
return idAsText !== null && 'arguments' != idAsText && 'eval' != idAsText
}
return true
}
}
abstract class StrictModeRelevant {
derived transient boolean strictMode
}
class SuperLiteral extends PrimaryExpression {
/**
* Convenience method, returns true if super literal is directly contained in a call expression.
* This is a call to the super constructor of a class, which is only allowed in a constructor of a subclass.
*/
op boolean isSuperConstructorAccess() {
eContainer instanceof ParameterizedCallExpression
}
/**
* Convenience method, returns true if super literal is directly contained in a property or index access expression.
* This is a call to a super's member, e.g., via "super.foo()".
*/