-
Notifications
You must be signed in to change notification settings - Fork 9
/
base.nt
1939 lines (1508 loc) · 57.4 KB
/
base.nt
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
module neat.base;
import backend.base;
import helpers;
public import neat.runtime.locrange;
import polyhash;
enum TokenType {
none,
end,
whitespace,
identifier,
number,
comment,
dot,
comma,
singleQuote,
charLiteral,
doubleQuote, // ["]foo $(bar) baz["]
stringLiteral, // "[foo ]$(bar)[ baz]"
formatQuoteStart, // "foo [$](bar)"
backtick,
colon,
semicolon,
lparen,
rparen,
smaller,
greater,
equal,
exclamationmark,
questionmark,
dollar,
lsquarebracket,
rsquarebracket,
lcurlybracket,
rcurlybracket,
plus,
minus,
asterisk,
slash,
backslash,
circumflex,
percent,
tilde,
ampersand,
bar,
hash,
}
// something that can be referenced by a name
abstract class Symbol
{
/**
* The sort of thing that can be implicitly called.
* Marker to shortcut the call() logic to speed up `isExpressionImplCall`:
* if this is not set, implicit call won't even be considered.
*/
abstract bool mayCallImplicit();
string repr() {
// TODO this.__classname
return "TODO Symbol.repr $(cast(void*) this)";
}
string toString() return repr;
}
/**
* Gifted: An expression with an unmanaged lifetime that we must take over.
* Ephemeral: An expression that will be freed at some indeterminate point.
* Lexical: An expression that will be freed at the earliest when the scope ends.
* Permanent: An expression that will never naturally be freed.
* (Can be incremented and decremented, but must remain above 1.)
* TODO: Do we need this anywhere? Most sites should be None.
* None: An expression that should not have rc done on it.
*
* Examples:
* - gifted: `new Class()`
* - ephemeral: `foo.(that)`
* - lexical: `void foo(Class obj) { }` or `Class obj;`
* - permanent: `"string"`
* - none: cast(T) foo
*/
enum Lifetime
{
gifted,
ephemeral,
lexical,
permanent,
none,
}
struct ExprInfo
{
Lifetime lifetime;
string repr() {
mut string ret;
if (lifetime == Lifetime.gifted) ret ~= "gifted";
else if (lifetime == Lifetime.ephemeral) ret ~= "ephemeral";
else if (lifetime == Lifetime.lexical) ret ~= "lexical";
else if (lifetime == Lifetime.permanent) ret ~= "permanent";
else if (lifetime == Lifetime.none) ret ~= "none";
else assert(false);
return ret;
}
string toString() return repr;
}
abstract class Expression : Symbol
{
Type type;
ExprInfo info;
abstract int emit(Generator output);
abstract void hash(Hash hash);
override bool mayCallImplicit() => false;
}
abstract class Statement
{
abstract void emit(Generator output);
abstract void hash(Hash hash);
string repr() => "TODO Statement.repr";
string toString() => repr;
}
abstract class Reference : Expression
{
bool mutable;
abstract int emitLocation(Generator output);
}
enum Protection
{
public_,
protected_,
private_
}
abstract class Type : Symbol
{
abstract BackendType emit(Platform platform);
abstract bool same(Type type);
string mangle() {
print("Don't know how to mangle $(repr)");
assert(false);
}
abstract void hash(Hash hash);
override bool mayCallImplicit() => false;
bool zeroInitializable;
bool hasElaborateCopyConstructor;
(nullable Expression | Error) implicitConvertFrom(Context context, Expression source, LocRange locRange)
{
return null;
}
(nullable Expression | Error) implicitConvertTo(
Context context, Expression source, Type target, LocRange locRange)
{
return null;
}
/**
* Implicitly converts to the target without change in bits.
* Used for implicit array type conversion.
*/
bool triviallyConvertsTo(Type target)
{
return this.same(target);
}
(nullable Expression | Error) truthy(Context context, Expression expr, LocRange locRange)
{
return null;
}
/**
* Called to create a copy of the expression.
* The returned expression will count as a new reference.
*/
(Expression | Error) copy(Context context, Expression source, LocRange locRange)
{
return source;
}
/**
* Called when a scoped expression goes out of scope.
*/
nullable Statement endLifetime(Context context, Expression value)
{
return null;
}
/**
* When looking up a nember in this type at scope 'namespace',
* what is the maximum protection level of the member where
* lookup will succeed?
*/
Protection protectionAt(Namespace namespace) {
// all are visible by default.
return Protection.private_;
}
/**
* Called to resolve `value.field`.
* If `base` is null, this indicates a static field access, `Type.field`.
* Note that `base` will not be refmanaged. Dispose of it correctly!
*/
(nullable Symbol | Error) accessMember(
Context context, nullable Expression base, string field, Protection protection, LocRange locRange)
{
return null;
}
/**
* Called to resolve `value.field(args)`.
*/
(nullable Expression | Error) callMember(
Context context, nullable Expression base, string field, ASTArgument[] args, Protection protection, LocRange locRange)
{
return null;
}
/**
* Called to resolve `value.field = arg`.
*/
(nullable Statement | Error) assignMember(
Context context, Expression base, string field, Expression arg, Protection protection, LocRange locRange)
{
return null;
}
/**
* Called to resolve `base[index]`.
*/
(nullable Expression | Error) index(Context context, Expression base, ASTSymbol index, LocRange locRange)
{
return null;
}
/**
* Called to resolve `base[index] = value`.
*/
(nullable Statement | Error) indexAssignment(
Context context, Expression base, ASTSymbol index, Expression value, LocRange locRange)
{
return null;
}
/**
* Called to resolve 'lhs op rhs' where op is a binary operator.
*/
(nullable Expression | Error) binaryOp(Context context, string op, Expression lhs, Expression rhs, LocRange locRange)
{
return null;
}
/**
* Called to resolve 'Type(a, b, c)'.
*
* TODO: rename to callType
*/
(nullable Expression | Error) call(Context context, LocRange locRange, ASTArgument[] args)
{
return null;
}
/**
* Called to resolve `expr(a, b, c)`, where `typeof(expr)` is `Type`.
*
* TODO: rename to call
*/
(nullable Expression | Error) callExpr(Context context, LocRange locRange, Expression expr, ASTArgument[] args)
{
return null;
}
/**
* Implement lambda quarantine checks.
* So far, only return is affected directly.
*
* See: doc/quarantine.md
*/
(void | Error) checkQuarantine(Context context, LocRange locRange, QuarantineReason reason)
{
}
/**
* Case `a.b = c`:
* Behavior depends on the quarantine policy of `a` and `c`.
* Assignment of a checked value to an occluded container field is an error.
* Assignment of a checked value to a transparent non-lexical container field is an error.
* - But I think it's impossible. How can you lexically export the type?
*/
QuarantinePolicy quarantinePolicy()
{
return QuarantinePolicy(container=:occluded, value=:harmless);
}
}
struct QuarantinePolicy {
// transparent containers allow assignment
(:transparent | :occluded) container;
(:checked | :harmless) value;
}
alias QuarantineReason = (
:returningFrom, Returnable returnable
);
interface Hashable
{
(ASTSymbol | Error) hash(CompilerBase compiler, ASTSymbol value);
}
class Generator
{
BackendModule mod;
mut nullable BackendFunction fun;
FileIdTable fileIdTable;
Platform platform;
mut int frameReg;
mut string[] extraFlags; // from pragma(lib)
this(this.platform, this.mod, this.fileIdTable)
{
this.fun = null;
}
bool once(string name)
{
return this.mod.once(name);
}
void addFlag(string flag) {
for (flag2 in extraFlags) if (flag == flag2) return;
this.extraFlags ~= flag;
}
}
abstract class ASTSymbol
{
LocRange locRange;
this(this.locRange=__CALLER__) {}
abstract (Symbol | Error) compile(Context context);
/**
* Compile a LHS AST node that has special handling for assignment.
* Example: ASTIndex, ASTMember.
*/
(nullable Statement | Error) assign(Context context, Expression value) {
return null;
}
// TODO this.classname
string repr() { return "TODO repr(ASTSymbol): $(locRange.toString)"; }
string toString() => repr;
}
/**
* `foo` or `$bar`, pretty much.
* A symbol that can appear in a declaration like `A B = C`, or `A.B = C`, in position `B`.
* Can be resolved to a string without compiling.
*/
abstract class ASTIdentifierSymbol : ASTSymbol
{
abstract string name(WarmContext warmContext);
}
// TODO Statement, Context multi-return with destructuring
struct StatementCompileResult
{
Statement statement;
Namespace namespace;
}
abstract class ASTStatement
{
LocRange locRange;
this(this.locRange=__CALLER__) {}
abstract (StatementCompileResult | Error) compile(Context context);
// TODO this.classname
string repr() { return "TODO repr(ASTStatement): $(locRange.toString)"; }
}
/**
* Something that updates a namespace without behavioral side effects:
*
* - import
* - alias
* - struct decl
* - class decl
*
* Ironically, a variable declaration is not an ASTDeclaration.
* Find a better name?
*/
abstract class ASTDeclaration
{
LocRange locRange;
abstract (Namespace | Error) compile(Context context);
// TODO this.classname
string repr() { return "TODO repr(ASTDeclaration)"; }
string toString() return repr;
}
abstract class ASTSymbolDeclaration : ASTDeclaration
{
ASTIdentifierSymbol name;
abstract (Symbol | Error) compileSymbol(Context context);
override (Namespace | Error) compile(Context context) {
auto symbol = compileSymbol(context)?;
auto name = this.name.name(context.warmContext);
return context.compiler.exprAlias(context.namespace, name, symbol);
}
}
// 'name' is ASTIdentifier or ASTQuotedSymbol
alias ExtForVarDecl = ((:auto_ | ASTSymbol | :none) type, LocRange locRange, ASTIdentifierSymbol name);
enum LookupReason
{
// normal name lookup
identifier,
// lookup for foo.bar (ufcs, don't match class methods on 'this')
ufcs,
// Lookup for resolving an ASTAssignment, ie. the `x` in `x = y`.
// Might return something magical like an AssignmentHelper, idk.
assignment,
// didYouMean,
}
abstract class AssignmentHelper : Symbol
{
abstract (nullable Statement | Error) assign(Context context, Expression value);
}
/**
* A namespace is a lexical environment that supports identifier lookup.
* Note that this means "foo", not "a.foo".
*/
abstract class Namespace
{
nullable Namespace parent; // lexical parent
bool isContextScope;
this(this.parent, this.isContextScope) { }
/**
* name: The name being looked up.
*
* context: The compiler context originating the lookup.
* The namespace in context is used as the provenance of the lookup,
* meaning it decides whether private members may be read.
*
* loc: The location of the symbol triggering the lookup.
*/
abstract (nullable Symbol | Error) lookup(
string name, Context context, LookupReason reason, LocRange locRange);
string mangle() { return this.parent.mangle; }
abstract string repr();
}
/// Something that 'return' returns from.
interface Returnable
{
/// We want to return `proposed`, so give me a type you can be happy with.
(Type | Error) mergeReturn(Context context, Expression ret, LocRange locRange);
}
/// Something that variables can be declared in.
interface Declarable
{
LatentVariable declare(Context context, string name, Type type, bool mut, bool borrowed, (int | :none) parentId);
}
// Namespace with unwind side effect, such as onExit call.
// Evaluated before declaration cleanup.
interface Unwindable
{
(nullable Statement | Error) unwind(Context context);
}
// variable without stackframe
abstract class LatentVariable
{
int id;
this(this.id) {}
abstract Type type();
abstract Reference access(Context context);
}
// Something that should be cleaned up on unwind.
// TODO remove this - it's redundant with Unwindable. (?)
abstract class DeclarationHolder : Namespace
{
abstract bool freeOnExit();
abstract Reference accessDecl(Context context);
// when we don't strictly need a Reference
Expression access(Context context) => accessDecl(context);
}
final class QuoteScope
{
// used to match lookups to scopes
int scopeId;
nullable QuoteScope parent;
ASTSymbol[] symbols;
ASTStatement[] statements;
ASTIdentifierSymbol[] identifiers;
this(this.scopeId, this.parent, this.symbols, this.statements, this.identifiers) {}
}
/**
* This contains the parts of Context that are never modified at all.
*/
final class ColdContext
{
CompilerBase compiler;
Platform platform;
MacroState macroState;
// here for caching reasons
// TODO replace with once
Type nativeWordType;
int loopPass;
ModulePreProcessor modulePreProcessor;
ModulePostProcessor modulePostProcessor;
this(this.compiler, this.platform, this.macroState, this.nativeWordType, this.loopPass,
this.modulePreProcessor, this.modulePostProcessor) { }
ColdContext withMacroState(MacroState macroState) {
return new ColdContext(this.compiler, this.platform, macroState,
this.nativeWordType, this.loopPass, this.modulePreProcessor,
this.modulePostProcessor);
}
}
/**
* This contains the parts of Context that are modified occasionally,
* but not too often.
*/
final class WarmContext
{
// the reference by which free variables are resolved.
nullable Expression framePointer;
// the lookup depth of the framepointer
(int | :none) frameDepth;
nullable ModuleBase compilingModule;
LabelCounter labelCounter;
IUniqCounter uniqCounter;
nullable QuoteScope quoteScope;
mut LocRange[] errorStack;
this(
this.framePointer, this.frameDepth, this.compilingModule, this.labelCounter,
this.uniqCounter, this.quoteScope, this.errorStack)
{ }
WarmContext withFramePointer(nullable Expression framePointer, (int | :none) frameDepth) {
return new WarmContext(
framePointer, frameDepth, this.compilingModule, this.labelCounter,
this.uniqCounter, this.quoteScope, this.errorStack);
}
WarmContext withCompilingModule(ModuleBase compilingModule) {
return new WarmContext(
this.framePointer, this.frameDepth, compilingModule, this.labelCounter,
this.uniqCounter, this.quoteScope, this.errorStack);
}
WarmContext withLabelPrefix(string prefix) {
auto labelCounter = new LabelCounter(prefix);
auto uniqCounter = this.uniqCounter.dup;
return new WarmContext(
this.framePointer, this.frameDepth, this.compilingModule, labelCounter,
uniqCounter, this.quoteScope, this.errorStack);
}
WarmContext withQuoteScope(QuoteScope quoteScope) {
return new WarmContext(
this.framePointer, this.frameDepth, this.compilingModule, this.labelCounter,
this.uniqCounter, quoteScope, this.errorStack);
}
ASTSymbol getQuoteSymbol(int scopeId, int index) {
mut auto current = this.quoteScope;
while (current) {
if (current.scopeId == scopeId) return current.symbols[index];
current = current.parent;
}
print("cannot get quote symbol @$index with scope id $scopeId");
assert(false);
}
ASTStatement getQuoteStatement(int scopeId, int index) {
mut auto current = this.quoteScope;
while (current) {
if (current.scopeId == scopeId) return current.statements[index];
current = current.parent;
}
print("cannot get quote statement @$index with scope id $scopeId");
assert(false);
}
ASTIdentifierSymbol getQuoteIdentifier(int scopeId, int index) {
mut auto current = this.quoteScope;
while (current) {
if (current.scopeId == scopeId) {
if (index < current.identifiers.length)
return current.identifiers[index];
print("cannot get quote identifier @$index with scope id $scopeId: "
~ "only got $(current.identifiers.length)");
assert(false);
}
current = current.parent;
}
print("cannot get quote identifier @$index with scope id $scopeId");
assert(false);
}
}
/**
* Generates unique text labels with a prefix.
* Usually used for names that will appear in the output and must be stable between runs.
*/
class LabelCounter
{
string prefix;
mut int count;
this(this.prefix = "uniq") { }
string next() {
auto count = this.count++;
return "$(this.prefix)$count";
}
}
/**
* Generate a unique numeric label.
* This class is more performant than `LabelCounter`.
* The value is not stable between runs; however, it is guaranteed to be globally unique per program run.
* `dup` must be called when the UniqCounter will be passed to another thread.
*/
interface IUniqCounter {
int next();
IUniqCounter dup();
}
// Used by main to analyze the AST tree (docgen)
interface ModulePreProcessor
{
void process(ASTModuleBase astModule_);
}
// Will be used by main to start emitting modules as soon as they finish.
interface ModulePostProcessor
{
void process(ModuleBase module_);
}
/**
* Context used when transforming an AST into a semantic tree.
*
* TODO figure out how to reduce refcounts on this.
*/
struct Context
{
Namespace namespace;
WarmContext warmContext;
ColdContext coldContext;
Context withNamespace(Namespace namespace) {
return Context(namespace, this.warmContext, this.coldContext);
}
Context withFramePointer(nullable Expression framePointer, (int | :none) frameDepth) {
return Context(this.namespace, this.warmContext.withFramePointer(framePointer, frameDepth), this.coldContext);
}
Context withCompilingModule(ModuleBase compilingModule) {
return Context(this.namespace, this.warmContext.withCompilingModule(compilingModule), this.coldContext);
}
Context withLabelPrefix(string prefix) {
return Context(this.namespace, this.warmContext.withLabelPrefix(prefix), this.coldContext);
}
Context withQuoteScope(QuoteScope quoteScope) {
return Context(this.namespace, this.warmContext.withQuoteScope(quoteScope), this.coldContext);
}
ASTSymbol getQuoteSymbol(int scopeId, int index) =>
this.warmContext.getQuoteSymbol(scopeId, index);
ASTStatement getQuoteStatement(int scopeId, int index) =>
this.warmContext.getQuoteStatement(scopeId, index);
ASTIdentifierSymbol getQuoteIdentifier(int scopeId, int index) =>
this.warmContext.getQuoteIdentifier(scopeId, index);
alias compiler = (coldContext.compiler);
alias platform = (coldContext.platform);
alias macroState = (coldContext.macroState);
alias nativeWordType = (coldContext.nativeWordType);
alias modulePreProcessor = (coldContext.modulePreProcessor);
alias modulePostProcessor = (coldContext.modulePostProcessor);
alias loopPass = (coldContext.loopPass);
alias framePointer = (warmContext.framePointer);
alias frameDepth = (warmContext.frameDepth);
alias compilingModule = (warmContext.compilingModule);
alias labelCounter = (warmContext.labelCounter);
alias uniqCounter = (warmContext.uniqCounter);
alias quoteScope = (warmContext.quoteScope);
alias errorStack = (warmContext.errorStack);
void pushErrorLoc(LocRange locRange) {
this.warmContext.errorStack ~= locRange;
}
void popErrorLoc() {
this.warmContext.errorStack = this.warmContext.errorStack[0 .. $ - 1];
}
(void | Error) assert2(bool test, LocRange locRange, string msg) {
if (test) return;
return fail(locRange, msg);
}
Error fail(LocRange locRange, string msg) {
return new Error(warmContext.errorStack ~ locRange, msg);
}
string getLabel() { return this.warmContext.labelCounter.next; }
int getUniqueId() { return this.warmContext.uniqCounter.next; }
}
// bleh cycle
abstract class ASTModuleBase
{
string mangledName;
abstract void addEntry(Protection protection, (ASTExternFunctionBase | ASTPragmaBase | ASTDeclaration) target);
abstract void addAliasDecl(string name, Protection protection, ASTSymbol target);
abstract nullable ASTSymbol getSymbolAlias(string name);
}
abstract class ASTPragmaBase
{
abstract (FinishedSymbol | Error) compile(Context context);
}
abstract class ASTExternFunctionBase
{
string name;
abstract string repr();
}
// TODO clear all this up
abstract class FinishedSymbol
{
abstract void emit(Generator generator);
abstract void hash(Hash hash);
}
// cycle breaker helper: needed for CompilerBase
abstract class ModuleBase : Namespace
{
string name;
string mangledName;
Package pak;
abstract void addImport(ModuleBase module_, bool public_, (string name, Symbol symbol)[] symbols);
abstract void addModuleDependency(ModuleBase module_);
abstract (void | Error) compile(Context context);
// Add symbol to list of anonymous symbols that will be emitted.
abstract void track((Symbol | FinishedSymbol) sym);
override string repr() => "Module";
}
struct ASTArgument
{
ASTSymbol sym;
string name;
LocRange locRange;
}
struct ASTParameter
{
string name;
bool mutable;
ASTSymbol type;
nullable ASTSymbol defaultValue;
LocRange locRange;
string toString() {
auto mutstr = "mut " if this.mutable else "";
return "$mutstr$type $name";
}
}
/*
* Stackoverflow insists that "parameters" are the stuff in the function parameter list,
* and "arguments" are the values you pass to a function.
*
* Fine by me.
*
* We could separate "declaration parameters" and "definition parameters."
* For instance, extern(C) declarations are only interesting for generating a call,
* and will never generate a function body. However, usually when meaningful information
* is used to customize the define-site, this is also interesting for the call-site,
* especially with regards to lifetime. As such, it's probably good there is only one type.
*/
struct Parameter
{
bool isThisAssignment;
string name;
bool mutable;
Type type;
(nullable Expression | :callerRange) defaultValue;
LocRange locRange;
static Parameter fromType(Type type) {
return Parameter(false, "", false, type, null, __RANGE__);
}
static Parameter simple(string name, Type type) {
return Parameter(false, name, false, type, null, __RANGE__);
}
string toString() => "Parameter(isThisAssignment=$isThisAssignment, name=$name, mutable=$mutable, "
~ "type=$type, defaultValue=..., locRange=$locRange)";
}
alias LibraryCallRecord = (string soname, string fnname);
abstract class ParserHelper
{
abstract (LibraryCallRecord | Error) proxyCall(ASTModuleBase astModule, string function_, void* ptr,
LocRange locRange);
}
// FIXME class Object
class ASTImportStatementBase
{
}
/**
* Helper class used to break dependency loops between neat.base, macros and the rest of the compiler.
*/
abstract class CompilerBase
{
WorkPoolBase workPool;
// for std.macro.cimport
string[] cFlags;
FileIdTable fileIdTable;
mut ParserHelper parserHelper;
// parser
abstract (Parser | Error) createParser(string filename, string fulltext);
abstract (Parser | Error) createFragmentParser(string fragment, LocRange locRange);
// must be called or LocRange IDs may be wrong.
abstract void disposeParser(Parser parser);
// all packages for the current build
abstract Package[] allPackages();
abstract (nullable ASTSymbol | Error) parseExpression(
Parser parser, LexicalContext lexicalContext);
// Parse an expression that may be arithmetic, but not ternary-if.
abstract (nullable ASTSymbol | Error) parseArithmetic(
Parser parser, LexicalContext lexicalContext);
abstract (nullable ASTSymbol | Error) parseExpressionLeaf(
Parser parser, LexicalContext lexicalContext);
abstract (nullable ASTSymbol | Error) parseType(
Parser parser, LexicalContext lexicalContext);
abstract (ASTStatement | Error) parseStatement(
Parser parser, LexicalContext lexicalContext);
abstract (nullable ASTIdentifierSymbol | Error) parseIdentifierSymbol(
Parser parser, LexicalContext lexicalContext);
abstract (nullable ASTDeclaration | Error) parseDeclaration(
Parser parser, LexicalContext lexicalContext);
abstract (ASTSymbol | Error) parseStringLiteral(
Parser parser, LexicalContext lexicalContext);
(ASTSymbol | Error) parseStringLiteral(
Parser parser, LexicalContext lexicalContext, string separator, LocRange from)
{
return parseStringLiteral(parser, lexicalContext);
}
abstract (nullable ASTSymbolDeclaration | Error) parseTemplateStub(
Parser parser, LexicalContext lexicalContext, ASTIdentifierSymbol name, string comment,
(nullable ASTSymbolDeclaration | Error) delegate!() dg);
abstract (ASTStatement | Error) parseFunctionBody(
Parser parser, LexicalContext lexicalContext);
abstract ASTStatement astExpressionStmt(ASTSymbol expr, LocRange locRange=__CALLER__);
abstract ASTStatement astScope(ASTStatement[] stmts, LocRange locRange=__CALLER__);
// sequence of statements that don't open a new scope
abstract ASTStatement astSequence(ASTStatement[] stmts, LocRange locRange=__CALLER__);
abstract ASTStatement astIf((ASTSymbol | ASTStatement) test, ASTStatement then, nullable ASTStatement else_,
LocRange locRange=__CALLER__);
abstract ASTStatement astForLoop(
ASTStatement init, ASTSymbol test, ASTStatement step, ASTStatement body_, LocRange locRange=__CALLER__);
abstract ASTStatement astExtForLoop(ASTSymbol source, ExtForVarDecl var1, (ExtForVarDecl | :none) var2,
ASTStatement body_, LocRange locRange=__CALLER__);
abstract ASTStatement astAssign(ASTSymbol target, ASTSymbol source, LocRange locRange=__CALLER__);
// a += 2
abstract ASTStatement astOpAssign(ASTSymbol left, string op, ASTSymbol right, LocRange locRange=__CALLER__);
abstract ASTStatement astDeclareVar(ASTIdentifierSymbol name, bool mutable, bool uninitialized,