-
Notifications
You must be signed in to change notification settings - Fork 25
/
LNode.cs
1175 lines (1073 loc) · 57.3 KB
/
LNode.cs
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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using Loyc.Utilities;
using Loyc.Threading;
using Loyc.Collections;
namespace Loyc.Syntax
{
/// <summary>The three kinds of nodes in a Loyc tree</summary>
public enum LNodeKind { Id, Literal, Call }
/// <summary>All nodes in a Loyc syntax tree share this base class.</summary>
/// <remarks>
/// Loyc defines only three types of nodes: simple symbols, literals, and calls.
/// <ul>
/// <li>A <see cref="IdNode"/> is a simple identifier, such as a VariableName</li>
/// <li>A <see cref="LiteralNode"/> is a literal constant, such as 123 or "hello"</li>
/// <li>A <see cref="CallNode"/> encompasses all other kinds of nodes, such as
/// normal function calls like <c>f(x)</c>, generic specifications like <c>f<x></c>
/// (represented as <c>#of(f, x)</c>), braced blocks of statements (represented as
/// <c>@`{}`(stmt1, stmt2, ...)</c>), and so on. Also, parenthesized expressions
/// are represented as a call with one argument and <c>null</c> as the <see cref="Target"/>.</li>
/// </ul>
/// This class provides access to all properties of all three types of nodes,
/// in order to make this class easier to access from plain C#, and to avoid
/// unnecessary downcasting in some cases.
/// <para/>
/// Loyc nodes are always immutable, except for the 8-bit <see cref="Style"/>
/// property which normally affects printing only.
/// <para/>
/// <h3>Background information</h3>
/// <para/>
/// EC# (enhanced C#) is intended to be the starting point of the Loyc
/// (Language of your choice) project, which will be a family of programming
/// languages that will share a common representation for the syntax tree and
/// other compiler-related data structures.
/// <para/>
/// Just as LLVM assembly has emerged as a nearly universal standard
/// intermediate representation for back-ends, Loyc nodes are intended to be a
/// universal intermediate representation for syntax trees, and Loyc will
/// (eventually) include a generic set of tools for semantic analysis so that
/// it provides a generic representation for front-ends.
/// <para/>
/// EC#, then, will be the first language to use the Loyc syntax tree
/// representation, known as the "Loyc tree" for short. Most syntax trees are
/// very strongly typed, with separate data types for, say, variable
/// declarations, binary operators, method calls, method declarations, unary
/// operators, and so forth. Loyc, however, defines only three types of Nodes,
/// and this one class provides access to all the parts of a node. There are
/// several reasons for this design:
/// <ul>
/// <li>Simplicity. Many compilers have thousands of lines of code dedicated
/// to the AST (abstract syntax tree) data structure itself, because each
/// kind of AST node has its own class.</li>
/// <li>Serializability. Loyc nodes can always be serialized to a plain text
/// "prefix tree" and deserialized back to objects, even by programs that
/// are not designed to handle the language that the tree represents*. This
/// makes it easy to visualize syntax trees or exchange them between
/// programs.</li>
/// <li>Extensibility. Loyc nodes can represent any language imaginable, and
/// they are suitable for embedded DSLs (domain-specific languages). Since
/// nodes do not enforce a particular structure, they can be used in
/// different ways than originally envisioned. For example, most languages
/// only have "+" as a binary operator, that is, with two arguments. If
/// Loyc had a separate class for each AST, there would probably be a
/// PlusOperator class derived from BinaryOperator, or something, with
/// properties "Left" and "Right". But since there is only one node class,
/// a "+" operator with three arguments is always possible; this is denoted
/// by #+(a, b, c) in EC# source code.</li>
/// </ul>
/// * Currently, the only supported languages for plain-text Loyc trees are
/// LES and EC# (either normal EC# or prefix-tree notation).
/// <para/>
/// Loyc trees are comparable to LISP trees, except that "attributes" and
/// position information are added to the tree, and the concept of a "list"
/// is replaced with the concept of a "call", which I feel is a more
/// intuitive notion in most programming languages that are not LISP.
/// <para/>
/// Loyc's representation is both an blessing and a curse. The advantage is
/// that Loyc nodes can be used for almost any purpose, perhaps even
/// representing data instead of code in some cases. However, there is no
/// guarantee that a given AST follows the structure prescribed by a particular
/// programming language, unless a special validation step is performed after
/// parsing. In this way, Loyc trees are similar to XML trees, only simpler.
/// <para/>
/// Another major disadvantage is that it is more difficult to interpret a
/// syntax tree correctly: you have to remember that a method definition has
/// the structure <c>#fn(return_type, name, args, body)</c>, so if "node" is
/// a method definition then <c>node.Args[2]</c> represents the argument
/// list, for example. In contrast, most compilers have an AST class called
/// <c>MethodDefinition</c> or something, that provides properties such as
/// Name and ReturnType. If EC# is ever done, however, aliases could help
/// avoid this problem by providing a more friendly veneer over the raw nodes.
/// <para/>
/// For optimization purposes, the node class is a class hierarchy, but most
/// users should only use this class.
/// <para/>
/// <see cref="LNode"/>s are "persistent" in the comp-sci sense, which
/// means that they are immutable, that a subtree can be shared among
/// multiple syntax trees, and that nodes do not know their own parents,
/// which allows a single node to exist at multiple locations in a syntax tree.
/// This makes manipulation of trees convenient, as there is no need to
/// "detach" a node from one place, or duplicate it, before it can be inserted
/// in another place.
///
/// <h3>The reimplementation</h3>
///
/// This implementation has been redesigned (in Subversion, the last version
/// based on the old design is revision 289.) The core concept is the same as
/// described in my blog at
/// http://loyc-etc.blogspot.ca/2013/04/the-loyc-tree-and-prefix-notation-in-ec.html
/// except that the concept of a "Head" has mostly been eliminated, although
/// you might see it occasionally because it still has a meaning. The "head"
/// of a node refers either to the Name of a symbol, the Value of a literal,
/// or the Target of a call (i.e. the name of the method being called, which
/// could be an arbitrarily complex node). In the original implementation, it
/// was also possible to have a complex head (a head that is itself a node)
/// even when the node was not a call; this situation was used to represent
/// an expression in parenthesis.
/// <para/>
/// This didn't quite feel right, so I changed it. Now, only calls can be
/// "complex", and the head of a call (the method being called) is called
/// the Target.
/// <para/>
/// In the new version, there are explicitly three types of nodes: symbols,
/// literals, and calls. There is no longer a Head property, instead there
/// are three separate properties for the three kinds of heads, <see
/// cref="Name"/> (a Symbol), <see cref="Value"/> (an Object), and <see
/// cref="Target"/> (an LNode). Only call nodes have a Target, and only
/// literal nodes have a Value. Identifier nodes have a Name, but I thought
/// it would be useful for some call nodes to also have a Name, which is
/// defined as the name of the Target if the Target is an identifier (if
/// the Target is not a symbol, the call has no name.)
/// <para/>
/// An expression in parenthesis is now represented by a call with the
/// #trivia_inParens attribute; use <see cref="LNodeExt.IsParenthesizedExpr"/> to
/// detect the parentheses.
/// <para/>
/// The problems that motivated a redesign are described at
/// http://loyc-etc.blogspot.ca/2013/05/redesigning-loyc-tree-code.html
/// <para/>
/// One very common use of mutable nodes is building lists of statements, e.g.
/// you might create an empty braced block or an empty loop and then add
/// statements to the body of the block or loop. To do this without mutable
/// nodes, create a mutable <see cref="WList{LNode}"/> instead and add
/// statements there; once the list is finished, create the braced block or
/// loop afterward. The new design stores arguments and attributes in
/// <see cref="VList{LNode}"/> objects; you can instantly convert your WList
/// to a VList by calling <see cref="WListBase{LNode}.ToVList()"/>.
/// <para/>
/// During the redesign I've decided on some small changes to the representation
/// of certain expressions in EC#.
/// <ul>
/// <li>The '.' operator is now treated more like a normal binary operator;
/// <c>a.b.c</c> is now represented <c>@.(@.(a, b), c)</c> rather than
/// <c>@.(a, b, c)</c> mainly because it's easier that way, and because the
/// second representation doesn't buy anything significant other than a
/// need for special-casing.</li>
/// <li><c>int x = 0</c> will now be represented <c>#var(int, x = 0)</c>
/// rather than <c>#var(int, x(0))</c>. I chose the latter representation
/// initially because it is slightly more convenient, because you can
/// always learn the name of the declared variable by calling
/// <c>var.Args[1].Name</c>. However, I decided that it was more important
/// for the syntax tree to be predictable, with obvious connections between
/// normal and prefix notations. Since I decided that <c>alias X = Y;</c>
/// was to be represented <c>#alias(X = Y, #())</c>, it made sense for the
/// syntax tree of a variable declaration to also resemble its C# syntax.
/// There's another small reason: C++ has both styles <c>Foo x(y)</c> and
/// <c>Foo x = y</c>; if Loyc were to ever support C++, it would make sense
/// to use <c>#var(Foo, x(y))</c> and <c>#var(Foo, x = y)</c> for these two
/// cases, and I believe C#'s variable declarations are semantically closer
/// to the latter. (Note: another possibility was #var(int, x) = 0, but I
/// decided this wasn't an improvement, it would just shift the pain around.)</li>
/// <li>A constructor argument list is required on <i>all</i> types using the #new
/// operator, e.g. <c>new int[] { x }</c> must have an empty set of arguments
/// on int[], i.e. <c>#new(#of(@`'[]`,int)(), x)</c>; this rule makes the
/// different kinds of new expressions easier to interpret by making them
/// consistent with each other.</li>
/// <li>A missing syntax element is now represented by the empty identifier
/// instead of the identifier #missing.</li>
/// <li>I've decided to adopt the "in-expression" generics syntax from Nemerle
/// and the binary ! from D as unambiguous alternatives to angle brackets:
/// List.[int] and List!int mean List<int> and the printer will use
/// one of these in cases where angle brackets are ambiguous.</li>
/// <li>By popular demand, constructors will be written this(...) instead
/// of new(...), since both D and Nemerle use the latter notation.</li>
/// <li>The \ and $ characters have been changed; @@S now denotes a symbol S,
/// $S now denotes a substitution, and \S doesn't mean anything.
/// Originally EC# was designed just as an extension of C#, so \ made
/// sense as a substitution operator for string interpolation because it
/// doesn't hurt backward compatibility: "Loaded '\(filename)' successfully".
/// But now that my focus has shifted to multi-language interoperability,
/// $ makes more sense, as it is used for string interpolation in at least
/// five other languages and it makes sense to use the same character for
/// both string substitution and code substitution.</li>
/// </ul>
///
/// <h3>Important properties</h3>
///
/// The main properties of a node are
/// <ol>
/// <li><see cref="Attrs"/>: holds the attributes of the node, if any.</li>
/// <li><see cref="Name"/>: the name of an <see cref="IdNode"/>, or the name
/// of the <see cref="IdNode"/> that is acting as the <see cref="Target"/>
/// of a <see cref="CallNode"/>.</li>
/// <li><see cref="Value"/>: the value of a <see cref="LiteralNode"/>.</li>
/// <li><see cref="Target"/>: the target of a <see cref="CallNode"/>. It
/// represents a method, macro, or special identifier that is being called.</li>
/// <li><see cref="Args"/>: holds the arguments to a <see cref="CallNode"/>,
/// if any. Returns an empty list if the node does not have an argument list.</li>
/// <li><see cref="Range"/>: indicates the source file that the node came from
/// and location in that source file.</li>
/// <li><see cref="Style"/>: an 8-bit flag value that is used as a hint to the
/// node printer about how the node should be printed. For example, a hex
/// literal like 0x10 has the <see cref="NodeStyle.Alternate"/> style to
/// distinguish it from decimal literals such as 16. Custom display styles
/// that do not fit in the Style property can be expressed with attributes.</li>
/// </ol>
/// <para/>
/// The argument and attribute lists cannot be null, since they have type
/// <see cref="VList{Node}"/> which is a struct.
/// <para/>
/// <c>LNode</c> implements <see cref="INegListSource{T}"/>, so you can loop
/// through all children of the node like this:
/// <code>
/// for (int i = node.Min; i <= node.Max; i++) {
/// LNode child = node[i];
/// }
/// </code>
/// You can also use <c>foreach</c>. The children are numbered like this:
/// <ul>
/// <li>if i is less than -1, node[i] refers to an attribute. Specifically, node[i]
/// means node.Attrs[i + node.Attrs.Count + 1] in that case.</li>
/// <li>node[-1] refers to <c>Target</c> (but throws if there is no target)</li>
/// <li>Non-negative values are indexes of Args, e.g. node[i] means node.Args[i].</li>
/// </ul>
/// LNode also provides <c>Select(child => result)</c> and <c>ReplaceRecursive(child => result)</c>
/// methods which allows you to transform all children (Atrrs, Target and Args).
/// Currently there is no <c>Where(child => bool)</c> method because it is not
/// possible to remove the <see cref="Target"/> of an LNode (you can still use
/// standard LINQ Where(), of course, but the result is not an LNode).
///
/// <h3>Note</h3>
///
/// The argument and attribute lists should never contain null nodes. Any code
/// that puts nulls in <see cref="Args"/> or <see cref="Attrs"/> is buggy.
/// However, we can't ensure nulls are not placed into <see cref="VList{T}"/>
/// since it's a general-purpose data type, not specialized for LNode. There is
/// code to ensure nulls are not placed in Args and Attrs (<see cref="NoNulls"/>),
/// but only in debug builds, since null-checking is fairly expensive.
/// </remarks>
public abstract class LNode : ILNode, ICloneable<LNode>, IEquatable<LNode>, IHasLocation, IHasValue<object>, INegListSource<LNode>
{
#region Constructors and static node creator methods
protected LNode(LNode prototype)
{
RAS = (prototype ?? LNode.Missing).RAS;
}
protected LNode(SourceRange range, NodeStyle style)
{
RAS = new RangeAndStyle(range, style);
if (RAS.Source == null)
RAS.Source = SyntheticSource;
}
public static readonly EmptySourceFile SyntheticSource = new EmptySourceFile("<Synthetic Code>");
/// <summary>The empty identifier, used to represent missing information.</summary>
public static readonly IdNode Missing = Id(CodeSymbols.Missing);
protected static readonly CallNode EmptySplice = Call(CodeSymbols.Splice);
public static IdNode Id(Symbol name, LNode prototype) { return new StdIdNode(name, prototype); }
public static IdNode Id(string name, LNode prototype) { return new StdIdNode(GSymbol.Get(name), prototype); }
public static IdNode Id(VList<LNode> attrs, Symbol name, LNode prototype) { return new StdIdNodeWithAttrs(attrs, name, prototype); }
public static IdNode Id(VList<LNode> attrs, string name, LNode prototype) { return new StdIdNodeWithAttrs(attrs, GSymbol.Get(name), prototype); }
public static LiteralNode Literal(object value, LNode prototype) { return new StdLiteralNode(value, prototype); }
public static LiteralNode Literal(VList<LNode> attrs, object value, LNode prototype) { return new StdLiteralNode(value, prototype); }
public static CallNode Call(Symbol name, LNode prototype) { return new StdSimpleCallNode(name, VList<LNode>.Empty, prototype); }
public static CallNode Call(LNode target, LNode prototype) { return new StdComplexCallNode(target, VList<LNode>.Empty, prototype); }
public static CallNode Call(Symbol name, VList<LNode> args, LNode prototype) { return new StdSimpleCallNode(name, args, prototype); }
public static CallNode Call(LNode target, VList<LNode> args, LNode prototype) { return new StdComplexCallNode(target, args, prototype); }
public static CallNode Call(VList<LNode> attrs, Symbol name, VList<LNode> args, LNode prototype) { return new StdSimpleCallNodeWithAttrs(attrs, name, args, prototype); }
public static CallNode Call(VList<LNode> attrs, LNode target, VList<LNode> args, LNode prototype) { return new StdComplexCallNodeWithAttrs(attrs, target, args, prototype); }
public static CallNode Trivia(Symbol name, object value, LNode prototype) { return new StdTriviaNode(name, value, prototype); }
public static IdNode Id(Symbol name, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdIdNode(name, range, style); }
public static IdNode Id(string name, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdIdNode(GSymbol.Get(name), range, style); }
public static IdNode Id(VList<LNode> attrs, Symbol name, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdIdNodeWithAttrs(attrs, name, range, style); }
public static IdNode Id(VList<LNode> attrs, string name, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdIdNodeWithAttrs(attrs, GSymbol.Get(name), range, style); }
public static LiteralNode Literal(object value, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdLiteralNode(value, range, style); }
public static LiteralNode Literal(VList<LNode> attrs, object value, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdLiteralNodeWithAttrs(attrs, value, range, style); }
public static CallNode Call(Symbol name, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, VList<LNode>.Empty, range, style); }
public static CallNode Call(Symbol name, SourceRange range, int targetStart, int targetEnd, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, VList<LNode>.Empty, range, targetStart, targetEnd, style); }
public static CallNode Call(LNode target, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNode(target, VList<LNode>.Empty, range, style); }
public static CallNode Call(Symbol name, VList<LNode> args, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, args, range, style); }
public static CallNode Call(Symbol name, VList<LNode> args, SourceRange range, int targetStart, int targetEnd, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, args, range, targetStart, targetEnd, style); }
public static CallNode Call(LNode target, VList<LNode> args, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNode(target, args, range, style); }
public static CallNode Call(VList<LNode> attrs, Symbol name, VList<LNode> args, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNodeWithAttrs(attrs, name, args, range, style); }
public static CallNode Call(VList<LNode> attrs, LNode target, VList<LNode> args, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNodeWithAttrs(attrs, target, args, range, style); }
public static CallNode Trivia(Symbol name, object value, SourceRange range, NodeStyle style = NodeStyle.Default) { return new StdTriviaNode(name, value, range, style); }
public static IdNode Id(Symbol name, ISourceFile file = null) { return new StdIdNode(name, new SourceRange(file)); }
public static IdNode Id(string name, ISourceFile file = null) { return new StdIdNode(GSymbol.Get(name), new SourceRange(file)); }
public static IdNode Id(VList<LNode> attrs, Symbol name, ISourceFile file = null) { return new StdIdNodeWithAttrs(attrs, name, new SourceRange(file)); }
public static IdNode Id(VList<LNode> attrs, string name, ISourceFile file = null) { return new StdIdNodeWithAttrs(attrs, GSymbol.Get(name), new SourceRange(file)); }
public static LiteralNode Literal(object value, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdLiteralNode(value, new SourceRange(file), style); }
public static LiteralNode Literal(VList<LNode> attrs, object value, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdLiteralNode(value, new SourceRange(file), style); }
public static CallNode Call(Symbol name, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, VList<LNode>.Empty, new SourceRange(file), style); }
public static CallNode Call(LNode target, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNode(target, VList<LNode>.Empty, new SourceRange(file), style); }
public static CallNode Call(Symbol name, VList<LNode> args, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNode(name, args, new SourceRange(file), style); }
public static CallNode Call(LNode target, VList<LNode> args, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNode(target, args, new SourceRange(file), style); }
public static CallNode Call(VList<LNode> attrs, Symbol name, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNodeWithAttrs(attrs, name, VList<LNode>.Empty, new SourceRange(file), style); }
public static CallNode Call(VList<LNode> attrs, LNode target, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNodeWithAttrs(attrs, target, VList<LNode>.Empty, new SourceRange(file), style); }
public static CallNode Call(VList<LNode> attrs, Symbol name, VList<LNode> args, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdSimpleCallNodeWithAttrs(attrs, name, args, new SourceRange(file), style); }
public static CallNode Call(VList<LNode> attrs, LNode target, VList<LNode> args, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdComplexCallNodeWithAttrs(attrs, target, args, new SourceRange(file), style); }
public static CallNode Trivia(Symbol name, object value, ISourceFile file = null, NodeStyle style = NodeStyle.Default) { return new StdTriviaNode(name, value, new SourceRange(file), style); }
/// <summary>Used by the <c>quote {...}</c> macro.</summary>
public static readonly LNode InParensTrivia = Id(CodeSymbols.TriviaInParens);
// List creation with null check (even in release builds)
public static VList<LNode> List() { return new VList<LNode>(); }
public static VList<LNode> List(LNode item_0) {
if (item_0 == null) throw new ArgumentNullException("item_0");
return new VList<LNode>(item_0);
}
public static VList<LNode> List(LNode item_0, LNode item_1) {
if (item_0 == null) throw new ArgumentNullException("item_0");
if (item_1 == null) throw new ArgumentNullException("item_1");
return new VList<LNode>(item_0, item_1);
}
public static VList<LNode> List(params LNode[] list) { return List(new VList<LNode>(list)); }
public static VList<LNode> List(IEnumerable<LNode> list) { return List(new VList<LNode>(list)); }
public static VList<LNode> List(VList<LNode> list) {
int i = 0;
foreach (var n in list.ToFVList()) { // FVList enumerate faster
i++;
if (n == null) throw new ArgumentNullException("list[" + (list.Count - i) + "]");
}
return list;
}
// It's difficult to enforce "nulls not allowed" with high performance,
// especially since often what's being done is creating a new node with
// the same list as an old node. Compromise: only check in debug builds.
// This is called by node types that accept lists of args or attrs.
[Conditional("DEBUG")]
protected static void NoNulls(VList<LNode> list, string propName)
{
int i = 0;
foreach (var n in list.ToFVList()) { // FVList enumerate faster
i++;
if (n == null) throw new ArgumentNullException(propName,
string.Format("Attempted to construct an LNode with a null reference at {0}[{1}].", propName, list.Count - i));
}
}
#endregion
#region Fields
protected RangeAndStyle RAS;
protected internal struct RangeAndStyle
{
public RangeAndStyle(SourceRange range, NodeStyle style)
{
Source = range.Source;
StartIndex = range.StartIndex;
_stuff = (range.Length & LengthMask) | ((int)style << StyleShift);
}
public RangeAndStyle(ISourceFile source, int beginIndex, int length, NodeStyle style)
{
Source = source;
StartIndex = beginIndex;
_stuff = (length & LengthMask) | ((int)style << StyleShift);
}
public ISourceFile Source;
public int StartIndex;
private int _stuff;
const int StyleShift = 24;
const int LengthMask = (1 << StyleShift) - 1;
public int Length {
[DebuggerStepThrough] get { return _stuff << 8 >> 8; }
[DebuggerStepThrough] set { _stuff = (_stuff & ~LengthMask) | (value & LengthMask); }
}
public int EndIndex { get { return StartIndex + Length; } }
public NodeStyle Style {
[DebuggerStepThrough] get { return (NodeStyle)(_stuff >> StyleShift); }
[DebuggerStepThrough] set { _stuff = (_stuff & ~(0xFF << StyleShift)) | ((int)value << StyleShift); }
}
public static explicit operator SourceRange(RangeAndStyle ras) { return new SourceRange(ras.Source, ras.StartIndex, ras.Length); }
}
#endregion
#region Common to all nodes
/// <summary>Returns the location and range in source code of this node.</summary>
/// <remarks>
/// A parser should record a sufficiently wide range for each parent node,
/// such that all children are fully contained within the range. However,
/// this is not an invariant; macros can splice together syntax trees from
/// different source files or add synthetic nodes, so that the parent range
/// does not necessarily include all child ranges. (In fact, in general it
/// is impossible to ensure that parent ranges include child ranges because
/// a parent can only specify a single source file, while children can come
/// from several source files.)
/// </remarks>
[DebuggerDisplay("{ToString()}")]
public virtual SourceRange Range { get { return (SourceRange)RAS; } }
/// <summary>Returns the source file (shortcut for <c><see cref="Range"/>.Source</c>).</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public ISourceFile Source { get { return RAS.Source; } }
/// <summary>Returns Range.</summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
object IHasLocation.Location
{
get { return Range; }
}
/// <summary>Indicates the preferred style to use when printing the node to a text string.</summary>
/// <remarks>
/// The Style is an 8-bit value that acts as a hint to the node printer about
/// how the node should be printed. Custom display styles that do not fit in
/// the Style property can be expressed with special attributes that have a
/// <see cref="Name"/> starting with "#trivia_". ("#trivia" attributes, which
/// are also used to store comments in the syntax tree, are not printed like
/// normal attributes and are normally ignored if the node printer does not
/// specifically recognize them.)
/// </remarks>
public NodeStyle Style
{
get { return RAS.Style; }
set { RAS.Style = value; }
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public NodeStyle BaseStyle
{
get { return RAS.Style & NodeStyle.BaseStyleMask; }
set { Style = (RAS.Style & ~NodeStyle.BaseStyleMask) | (value & NodeStyle.BaseStyleMask); }
}
public LNode SetBaseStyle(NodeStyle s)
{
BaseStyle = s;
return this;
}
public LNode SetStyle(NodeStyle s)
{
Style = s;
return this;
}
/// <summary>Returns the attribute list for this node.</summary>
public virtual VList<LNode> Attrs { get { return VList<LNode>.Empty; } }
/// <summary>Returns true if the node is immutable, and false if any part of it can be edited.
/// Currently, mutable nodes are not implemented.</summary>
/// <remarks>Debugger-hidden until such time as mutable nodes actually exist.</remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public virtual bool IsFrozen { get { return true; } }
/// <summary>Returns the <see cref="LNodeKind"/>: Symbol, Literal, or Call.</summary>
public abstract LNodeKind Kind { get; }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsCall { get { return Kind == LNodeKind.Call; } }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsId { get { return Kind == LNodeKind.Id; } }
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsLiteral { get { return Kind == LNodeKind.Literal; } }
#endregion
#region Properties and methods for Symbol nodes (and simple calls)
/// <summary>Returns the Symbol if <see cref="IsId"/>. If this node is
/// a call (<see cref="IsCall"/>) and <c>Target.IsId</c> is true,
/// this property returns <c>Target.Name</c>. In all other cases, the name
/// is <see cref="GSymbol.Empty"/>. Shall not return null.</summary>
/// <remarks>Examples (using C#/LES syntax):
/// <pre>
/// Expression Kind Name (blank if empty)
/// hello Id hello
/// @#if Id #if
/// Foo(x, y) Call Foo
/// x += y Call +=
/// x.Foo(y) Call
/// 5.0 Literal
/// </pre>
/// </remarks>
public abstract Symbol Name { get; }
/// <summary>Returns true if <see cref="Name"/> is a "special" name
/// (i.e. starts with '#' or '\'' or '.' or any character below 48 in ASCII).</summary>
/// <remarks>Note that this property returns false for the empty identifier <c>@``</c>.</remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool HasSpecialName { get { return IsSpecialName(Name.Name); } }
/// <summary>Returns true if <c>name</c> is considered a "special" name that
/// starts with any character below 48 in ASCII, such as '#', '\'', and '.'.</summary>
/// <remarks>
/// This returns false for the empty string or null.
/// <para/>
/// In order to keep the check trivially simple, this returns true for '$'
/// even though it is <i>not</i> special in some languages (e.g. JavaScript).
/// <para/>
/// Letters, underscores, digits, and some punctuation do not count as special.
/// The full list of specials is <c>! " # $ % & ' ( ) * + , - . /</c> plus
/// the space character and the control characters.
/// </remarks>
public static bool IsSpecialName(string name) { return name != null && name.Length > 0 && name[0] <= '\''; }
public static bool IsSpecialName(Symbol name) { return name != null && name.Name.Length > 0 && name.Name[0] <= '\''; }
/// <summary>Creates a node with a new value for Name.</summary>
/// <remarks>If IsId, the Name is simply changed. If <see cref="IsCall"/>,
/// this method returns the equivalent of <c>WithTarget(Target.WithName(name))</c>
/// (which may be optimized for the particular call type). If <see
/// cref="IsLiteral"/>, the <see cref="Kind"/> changes to <see cref="LNodeKind.Id"/> in
/// order to set the name.</remarks>
public virtual LNode WithName(Symbol name)
{
var attrs = Attrs;
if (attrs.Count == 0)
return new StdIdNode(name, this);
else
return new StdIdNodeWithAttrs(attrs, name, this);
}
#endregion
#region Properties and methods for Literal nodes
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool HasValue { get { return Value != NoValue.Value; } }
/// <summary>Returns the value of a literal node, or <see cref="NoValue.Value"/>
/// if this node is not a literal (<see cref="IsLiteral"/> is false).</summary>
public abstract object Value { get; }
/// <summary>Creates a new literal node with a different Value than the current literal node.</summary>
/// <exception cref="InvalidOperationException">The node was not a literal already.</exception>
public abstract LiteralNode WithValue(object value);
#endregion
#region Properties and methods for Call nodes
/// <summary>Returns the target of a method call, or null if <see cref="IsCall"/>
/// is false. The target can be a symbol with no name (<see cref="GSymbol.Empty"/>)
/// to represent a parenthesized expression, if there is one argument.</summary>
public abstract LNode Target { get; }
/// <summary>Returns the argument list of this node. Always empty when <c><see cref="IsCall"/>==false</c>.</summary>
/// <remarks>
/// Depending on the <see cref="Target"/>, Args may represent an actual
/// argument list, or it may represent some other kind of list. For
/// example, if the target is "{}" then Args represents a list of
/// statements in a braced block, and if the target is ">=" then Args
/// represents the two arguments to the ">=" operator.
/// </remarks>
public abstract VList<LNode> Args { get; }
public virtual CallNode WithTarget(LNode target) { return With(target, Args); }
public virtual CallNode WithTarget(Symbol name) { return With(name, Args); }
/// <summary>Creates a Node with a new argument list. If this node is not a
/// call, a new node is created using this node as its target. Otherwise,
/// the existing argument list is replaced.</summary>
/// <param name="args">New argument list</param>
public abstract CallNode WithArgs(VList<LNode> args);
/// <summary>Creates a <see cref="CallNode"/> with the same attributes and
/// <see cref="Range"/>, but a different target and argument list. If the
/// current node is not a CallNode, it becomes one (the Range, Style and
/// attributes of the current node are kept, but the Kind, Value, and
/// Name are discarded.)</summary>
public virtual CallNode With(LNode target, VList<LNode> args)
{
if (target == null) throw new ArgumentNullException("target");
if (Target == target && Args == args)
return (CallNode)this;
var attrs = Attrs;
if (attrs.Count == 0)
return new StdComplexCallNode(target, args, this);
else
return new StdComplexCallNodeWithAttrs(attrs, target, args, this);
}
/// <summary>Creates a <see cref="CallNode"/> with the same attributes and
/// <see cref="Range"/>, but a different target and argument list. If the
/// current node is not a CallNode, it becomes one (the Range, Style and
/// attributes of the current node are kept, but the Kind, Value, and
/// Name are discarded.)</summary>
public virtual CallNode With(Symbol target, VList<LNode> args)
{
var attrs = Attrs;
if (attrs.Count == 0)
return new StdSimpleCallNode(target, args, this);
else
return new StdSimpleCallNodeWithAttrs(attrs, target, args, this);
}
public CallNode With(Symbol target, params LNode[] args) { return With(target, new VList<LNode>(args)); }
#endregion
#region Other WithXyz methods, and Clone()
/// <summary>Creates a copy of the node. Since nodes are immutable, there
/// is little reason for an end-user to call this, but Clone() is used
/// internally as a helper method by the WithXyz() methods.</summary>
public abstract LNode Clone();
public LNode WithRange(SourceRange range) { return With(range, Style); }
public virtual LNode WithRange(int startIndex, int endIndex)
{
var copy = Clone();
copy.RAS = new RangeAndStyle(RAS.Source, startIndex, endIndex - startIndex, RAS.Style);
return copy;
}
public LNode WithStyle(NodeStyle style) { return With(Range, style); }
public virtual LNode With(SourceRange range, NodeStyle style)
{
var copy = Clone();
copy.RAS = new RangeAndStyle(range, style);
return copy;
}
public virtual LNode WithoutAttrs() { return WithAttrs(VList<LNode>.Empty); }
public abstract LNode WithAttrs(VList<LNode> attrs);
public LNode WithAttrs(params LNode[] attrs) { return WithAttrs(new VList<LNode>(attrs)); }
public CallNode WithArgs(params LNode[] args) { return WithArgs(new VList<LNode>(args)); }
public LNode PlusAttr(LNode attr) { return WithAttrs(Attrs.Add(attr)); }
public LNode PlusAttrs(IEnumerable<LNode> attrs) { return WithAttrs(Attrs.AddRange(attrs)); }
public LNode PlusAttrs(params LNode[] attrs) { return WithAttrs(Attrs.AddRange(attrs)); }
public LNode PlusAttrs(VList<LNode> attrs) { return attrs.IsEmpty ? this : WithAttrs(Attrs.AddRange(attrs)); }
public LNode PlusAttrsBefore(VList<LNode> attrs) { return attrs.IsEmpty ? this : WithAttrs(attrs.AddRange(Attrs)); }
public LNode PlusAttrsBefore(params LNode[] attrs) { return WithAttrs(Attrs.InsertRange(0, attrs)); }
public LNode PlusAttrBefore(LNode attr) { return WithAttrs(Attrs.Insert(0, attr)); }
public LNode PlusArg(LNode arg) { return WithArgs(Args.Add(arg)); }
public LNode PlusArgs(VList<LNode> args) { return args.IsEmpty ? this : WithArgs(Args.AddRange(args)); }
public LNode PlusArgs(IEnumerable<LNode> args) { return WithArgs(Args.AddRange(args)); }
public LNode PlusArgs(params LNode[] args) { return WithArgs(Args.AddRange(args)); }
public LNode WithArgChanged(int index, Func<LNode, LNode> selector)
{
var args = Args;
var arg = args[index];
var newValue = selector(arg);
CheckParam.IsNotNull("return value of selector", newValue);
if (newValue != arg) {
args[index] = newValue;
return WithArgs(args);
} else
return this;
}
public LNode WithArgChanged(int index, LNode newValue)
{
CheckParam.IsNotNull("newValue", newValue);
var args = Args;
if (newValue != args[index]) {
args[index] = newValue;
return WithArgs(args);
} else
return this;
}
public LNode WithAttrChanged(int index, LNode newValue)
{
CheckParam.IsNotNull("newValue", newValue);
var a = Attrs;
if (newValue != a[index]) {
a[index] = newValue;
return WithAttrs(a);
} else
return this;
}
public LNode WithChildChanged(int index, LNode newValue)
{
if (index >= 0) {
return WithArgChanged(index, newValue);
} else if (index < -1) {
return WithAttrChanged(index + 1 + AttrCount, newValue);
} else { // index == -1
return WithTarget(newValue);
}
}
#endregion
public abstract void Call(LNodeVisitor visitor);
public abstract void Call(ILNodeVisitor visitor);
#region Node printer service (used by ToString())
static ILNodePrinter _defaultPrinter = Loyc.Syntax.Les.Les2LanguageService.Value;
static ThreadLocalVariable<ILNodePrinter> _printer = new ThreadLocalVariable<ILNodePrinter>();
/// <summary>Gets or sets the default node printer on the current thread,
/// which controls how nodes are serialized to text by default.</summary>
/// <remarks>The LES printer is the default, and will be used if you try
/// to set this property to null.</remarks>
public static ILNodePrinter Printer
{
get { return _printer.Value ?? _defaultPrinter; }
set { _printer.Value = value; }
}
public static SavedValue<ILNodePrinter> SetPrinter(ILNodePrinter newValue)
{
return new SavedValue<ILNodePrinter>(_printer, newValue);
}
/// <summary>Helps you change printers temporarily. Usage in C#:
/// <c>using (LNode.PushPrinter(myPrinter)) { ... }</c></summary>
/// <remarks>For example, to switch to the EC# printer, use
/// <c>using (LNode.PushPrinter(EcsNodePrinter.Printer)) { ... }</c>.
/// This changes the default printer. If you don't want to change the
/// default printer, please invoke the printer directly:
/// <code>
/// var sb = new StringBuilder();
/// EcsNodePrinter.Printer(node, sb, MessageSink.Trace);
/// </code>
/// </remarks>
[Obsolete("Please use using(LNode.SetPrinter(...)) instead")]
public static PushedPrinter PushPrinter(ILNodePrinter printer) { return new PushedPrinter(printer); }
/// <summary>Returned by <see cref="PushPrinter(ILNodePrinter)"/>.</summary>
[Obsolete]
public struct PushedPrinter : IDisposable
{
ILNodePrinter old;
public PushedPrinter(ILNodePrinter @new) { old = Printer; Printer = @new; }
public void Dispose() { Printer = old; }
}
public virtual string Print(ParsingMode mode = null, ILNodePrinterOptions options = null)
{
return Printer.Print(this, MessageSink.Null, mode, options);
}
#endregion
#region Other stuff
/// <summary>Gets the value of <c>Args[0].Value</c>, if Args[0] exists;
/// otherwise, returns <see cref="NoValue.Value"/>.</summary>
/// <remarks>"Trivia nodes" are used to efficiently represent the value of
/// trivia and non-tree <see cref="Lexing.Token"/>s; they can be created by
/// calling the <see cref="LNode.Trivia"/> function. Since an LNode is not
/// allowed to have both a Name and a Value (as there is no syntax in LES
/// or EC# for such a node), a trivia node pretends that there is an
/// argument list with one item, and that one item is always a literal
/// whose Value is the value stored in the trivia node. Thus, a token node
/// is printed out as <c>TokenType(Value)</c> where <c>Value</c> is some
/// literal.
/// <para/>
/// If you suspect you're dealing with a trivia node, it is wasteful to
/// actually call <c>node.Args[0].Value</c> since this causes a temporary
/// token list to be allocated. Instead you should use this property, which
/// returns the token value without allocating memory. Of course, if this
/// property is called on a non-trivia node, it simply returns
/// <c>Args[0].Value</c>.
/// </remarks>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public virtual object TriviaValue
{
get {
VList<LNode> args = Args;
if (args.Count >= 1)
return args[0].Value;
return NoValue.Value;
}
}
public override string ToString()
{
return Print();
}
/// <summary>Compares two lists of nodes for structural equality.</summary>
/// <param name="mode">Whether to pay attention to <see cref="Style"/> and trivia attributes</param>
/// <remarks>Position information is not compared.</remarks>
public static bool Equals(VList<LNode> a, VList<LNode> b, CompareMode mode = CompareMode.Normal)
{
if (a.Count != b.Count)
return false;
while (!a.IsEmpty)
if (!Equals(a.Pop(), b.Pop(), mode))
return false;
return true;
}
/// <inheritdoc cref="Equals(ILNode, CompareMode)"/>
public static bool Equals(ILNode a, ILNode b, CompareMode mode = CompareMode.Normal)
{
if ((object)a == b)
return true;
if (a == null || b == null)
return false;
int max = a.Max, min;
if (max != b.Max)
return false;
var kind = a.Kind;
if (kind != b.Kind)
return false;
if ((mode & CompareMode.Styles) != 0 && a.Style != b.Style)
return false;
if (a.Name != b.Name)
return false;
if (kind == LNodeKind.Literal && !object.Equals(a.Value, b.Value))
return false;
if ((mode & CompareMode.IgnoreTrivia) != 0) {
// TODO: unit test this
int ia, ib;
for (ia = a.Min, ib = b.Min; ia < -1 && ib < -1; ia++)
{
var attr_a = a[ia];
if (!attr_a.IsTrivia()) {
do {
var attr_b = b[ib];
ib++;
if (!attr_b.IsTrivia()) {
if (Equals(attr_a, attr_b, mode))
break;
else
return false;
}
} while (ib < -1);
}
}
while (ib < -1 && b[ib].IsTrivia())
ib++;
if (ia != ib)
return false;
min = -1;
} else if ((min = a.Min) != b.Min)
return false;
for (int i = min; i <= max; i++)
if (!Equals(a[i], b[i], mode))
return false;
return true;
}
[Flags]
public enum CompareMode { Normal = 0, Styles = 1, IgnoreTrivia = 2 }
/// <summary>Compares two nodes for structural equality. Two nodes are
/// considered equal if they have the same kind, the same name, the same
/// value, the same arguments, and the same attributes.</summary>
/// <param name="mode">Whether to pay attention to <see cref="Style"/> and trivia attributes</param>
/// <remarks>Position information (<see cref="Range"/>) is not compared.</remarks>
public virtual bool Equals(ILNode other, CompareMode mode) { return Equals(this, other, mode); }
public bool Equals(LNode other) { return Equals(this, other); }
public bool Equals(ILNode other) { return Equals(this, other); }
public override bool Equals(object other) { var b = other as LNode; return Equals(this, b); }
protected internal abstract int GetHashCode(int recurse, int styleMask);
/// <summary>Gets the hash code based on the structure of the tree.</summary>
/// <remarks>
/// If the tree is large, less than the entire tree is scanned to produce
/// the hashcode (in the absolute worst case, about 4000 nodes are examined,
/// but usually it is less than 100).
/// </remarks>
public override int GetHashCode() { return GetHashCode(3, 0); }
[DebuggerBrowsable(DebuggerBrowsableState.Never)] public int ArgCount { get { return Args.Count; } }
[DebuggerBrowsable(DebuggerBrowsableState.Never)] public int AttrCount { get { return Attrs.Count; } }
[DebuggerBrowsable(DebuggerBrowsableState.Never)] public bool HasAttrs { get { return Attrs.Count != 0; } }
public bool HasPAttrs()
{
var a = Attrs;
for (int i = 0, c = a.Count; i < c; i++)
if (!a[i].IsTrivia)
return true;
return false;
}
public VList<LNode> PAttrs()
{
return Attrs.SmartWhere(a => !a.IsTrivia);
}
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public bool IsTrivia
{
get { return CodeSymbols.IsTriviaSymbol(Name); }
}
public virtual bool Calls(Symbol name, int argCount) { Debug.Assert(!IsCall); return false; }
public virtual bool Calls(string name, int argCount) { Debug.Assert(!IsCall); return false; }
public virtual bool Calls(Symbol name) { Debug.Assert(!IsCall); return false; }
public virtual bool Calls(string name) { Debug.Assert(!IsCall); return false; }
/// <summary>Returns true if this is a call with the specified name and the specified minimum number of arguments.</summary>
public virtual bool CallsMin(Symbol name, int minArgCount) { Debug.Assert(!IsCall); return false; }
/// <summary>Returns true if this is a call with the specified name and the specified minimum number of arguments.</summary>
public virtual bool CallsMin(string name, int minArgCount) { Debug.Assert(!IsCall); return false; }
/// <summary>Returns true if this is not a call, or if the call's Target is an Id or a Literal.</summary>
public virtual bool HasSimpleHead() { Debug.Assert(!IsCall); return true; }
/// <summary>Returns true if this is not a call, or if the call's Target is an Id or a Literal, and the Target has only trivia attributes.</summary>
public virtual bool HasSimpleHeadWithoutPAttrs() { Debug.Assert(!IsCall); return true; }
public virtual LNode WithAttrs(Func<LNode, Maybe<LNode>> selector) {
Debug.Assert(AttrCount == 0);
return this;
}
public virtual LNode WithArgs(Func<LNode, Maybe<LNode>> selector) { Debug.Assert(!IsCall); return this; }
public virtual bool IsIdWithoutPAttrs() { Debug.Assert(!IsId); return false; }
public virtual bool IsIdWithoutPAttrs(Symbol name) { Debug.Assert(!IsId); return false; }
public virtual bool IsIdNamed(Symbol name) { Debug.Assert(!IsId); return false; }
public virtual bool IsIdNamed(string name) { Debug.Assert(!IsId); return false; }
/// <summary>Some <see cref="CallNode"/>s are used to represent lists. This
/// method merges two nodes, forming or appending a list (see remarks).</summary>
/// <param name="node1">First node, list, or null.</param>
/// <param name="node2">Second node, list, or null.</param>
/// <param name="listName">The <see cref="Name"/> used to detect whether a
/// node is a list (typically "#splice"). Any other name is considered a
/// normal call, not a list. If this method creates a list from two non-
/// lists, this parameter specifies the Name that the list will have.</param>
/// <returns>The merged list.</returns>
/// <remarks>
/// The order of the data is retained (i.e. the data in node1 is inserted
/// before the data in node2).
/// <ul>
/// <li>If either node1 or node2 is null, this method returns the other (node1 ?? node2).</li>
/// <li>If both node1 and node2 are lists, this method merges the list
/// into a single list by appending node2's arguments at the end of node1.
/// The attributes of node1 are kept and those of node2 are discarded.</li>
/// <li>If one of the nodes is a list and the other is not, the non-list
/// is inserted into the list's Args.</li>
/// <li>If neither node is a list, a list is created with both nodes as
/// its two Args.</li>
/// </ul>
/// </remarks>
/// <seealso cref="LNodeExt.WithSpliced"/>
public static LNode MergeLists(LNode node1, LNode node2, Symbol listName)
{
if (node1 == null)
return node2;
if (node2 == null)
return node1;
if (node1.Calls(listName))
return node1.WithSplicedArgs(node2, listName);
else if (node2.Calls(listName))
return node2.WithSplicedArgs(0, node1, listName);
else
return LNode.Call(listName, new VList<LNode>(node1, node2));
}
/// <summary>Combines two nodes using a binary operator or function.</summary>
/// <param name="node1">First node, list, or null.</param>
/// <param name="node2">Second node, list, or null.</param>
/// <param name="binaryOpName">Binary operator to use when the nodes are not null.</param>
/// <returns>If either node1 or node2 is null, this method returns the other node
/// (node1 ?? node2), otherwise the nodes are joined with the specified operator.</returns>
public static LNode MergeBinary(LNode node1, LNode node2, Symbol binaryOpName)
{
if (node1 == null) return node2;
if (node2 == null) return node1;
SourceRange r1 = node1.Range, r2 = node2.Range;
if (r2.Source == r1.Source) {
int start = System.Math.Min(r1.StartIndex, r2.StartIndex);
r1 = new SourceRange(r1.Source, start, System.Math.Max(r1.EndIndex, r2.EndIndex) - start);
}
return Call(binaryOpName, new VList<LNode>(node1, node2), r1);
}
public CallNode WithSplicedArgs(int index, LNode from, Symbol listName)
{
return WithArgs(LNodeExt.WithSpliced(Args, index, from, listName));
}
public CallNode WithSplicedArgs(LNode from, Symbol listName)
{
return WithArgs(LNodeExt.WithSpliced(Args, from, listName));
}
public LNode WithSplicedAttrs(int index, LNode from, Symbol listName)
{
return WithAttrs(LNodeExt.WithSpliced(Attrs, index, from, listName));
}
public LNode WithSplicedAttrs(LNode from, Symbol listName)
{
return WithAttrs(LNodeExt.WithSpliced(Attrs, from, listName));
}
public NestedEnumerable<DescendantsFrame, LNode> Descendants(NodeScanMode mode = NodeScanMode.YieldAllChildren)
{