yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

ArielG-NVClean up `natvis` and use fiddle to generate info needed for `.natvis` debugging (#8192)cfd08da10

master
52.0 KiB1950 linesraw
1// slang-fiddle-scrape.cpp
2#include "slang-fiddle-scrape.h"
3
4#include "core/slang-string-util.h"
5#include "slang-fiddle-script.h"
6
7namespace fiddle
8{
9
10// Parser
11
12struct Parser
13{
14private:
15    DiagnosticSink& _sink;
16    List<TokenWithTrivia> _tokens;
17
18    TokenWithTrivia const* _cursor = nullptr;
19    TokenWithTrivia const* _end = nullptr;
20
21    LogicalModule* _module = nullptr;
22
23    ContainerDecl* _currentParentDecl = nullptr;
24
25    struct WithParentDecl
26    {
27    public:
28        WithParentDecl(Parser* outer, ContainerDecl* decl)
29        {
30            _outer = outer;
31            _saved = outer->_currentParentDecl;
32
33            outer->_currentParentDecl = decl;
34        }
35
36        ~WithParentDecl() { _outer->_currentParentDecl = _saved; }
37
38    private:
39        Parser* _outer;
40        ContainerDecl* _saved;
41    };
42
43public:
44    Parser(DiagnosticSink& sink, List<TokenWithTrivia> const& tokens, LogicalModule* module)
45        : _sink(sink), _tokens(tokens), _module(module)
46    {
47        _cursor = tokens.begin();
48        _end = tokens.end() - 1;
49    }
50
51    bool _isRecovering = false;
52
53    TokenWithTrivia const& peek() { return *_cursor; }
54
55    SourceLoc const& peekLoc() { return peek().getLoc(); }
56
57    TokenType peekType() { return peek().getType(); }
58
59    TokenWithTrivia read()
60    {
61        _isRecovering = false;
62        if (peekType() != TokenType::EndOfFile)
63            return *_cursor++;
64        else
65            return *_cursor;
66    }
67
68    TokenWithTrivia expect(TokenType expected)
69    {
70        if (peekType() == expected)
71        {
72            return read();
73        }
74
75        if (!_isRecovering)
76        {
77            _sink.diagnose(peekLoc(), fiddle::Diagnostics::unexpected, peekType(), expected);
78        }
79        else
80        {
81            // TODO: need to skip until we see what we expected...
82            _sink.diagnose(SourceLoc(), fiddle::Diagnostics::internalError);
83        }
84
85        return TokenWithTrivia();
86    }
87
88    TokenWithTrivia expect(const char* expected)
89    {
90        if (peekType() == TokenType::Identifier)
91        {
92            if (peek().getContent() == expected)
93            {
94                return read();
95            }
96        }
97
98        if (!_isRecovering)
99        {
100            _sink.diagnose(peekLoc(), fiddle::Diagnostics::unexpected, peekType(), expected);
101        }
102        else
103        {
104            // TODO: need to skip until we see what we expected...
105            _sink.diagnose(SourceLoc(), fiddle::Diagnostics::internalError);
106        }
107
108        return TokenWithTrivia();
109    }
110
111    bool advanceIf(TokenType type)
112    {
113        if (peekType() == type)
114        {
115            read();
116            return true;
117        }
118
119        return false;
120    }
121
122    bool advanceIf(char const* name)
123    {
124        if (peekType() == TokenType::Identifier)
125        {
126            if (peek().getContent() == name)
127            {
128                read();
129                return true;
130            }
131        }
132
133        return false;
134    }
135
136    RefPtr<Expr> parseCppSimpleExpr()
137    {
138        switch (peekType())
139        {
140        case TokenType::Identifier:
141            {
142                auto nameToken = expect(TokenType::Identifier);
143                return new NameExpr(nameToken);
144            }
145            break;
146
147        case TokenType::IntegerLiteral:
148        case TokenType::StringLiteral:
149            {
150                auto token = read();
151                return new LiteralExpr(token);
152            }
153            break;
154
155        case TokenType::LParent:
156            {
157                expect(TokenType::LParent);
158                auto inner = parseCppExpr();
159                expect(TokenType::RParent);
160
161                // TODO: handle a cast, in the case that the lookahead
162                // implies we should parse one...
163                switch (peekType())
164                {
165                case TokenType::Identifier:
166                case TokenType::LParent:
167                    {
168                        auto arg = parseCppExpr();
169                        return inner;
170                    }
171                    break;
172
173                default:
174                    return inner;
175                }
176            }
177            break;
178
179        default:
180            expect(TokenType::Identifier);
181            _sink.diagnose(SourceLoc(), fiddle::Diagnostics::internalError);
182            return nullptr;
183        }
184        return nullptr;
185    }
186
187    RefPtr<Expr> parseCppExpr()
188    {
189        auto base = parseCppSimpleExpr();
190        for (;;)
191        {
192            switch (peekType())
193            {
194            default:
195                return base;
196
197            case TokenType::OpMul:
198                {
199                    expect(TokenType::OpMul);
200                    switch (peekType())
201                    {
202                    default:
203                        // treat as introducting a pointer type
204                        return base;
205                    }
206                }
207                break;
208
209            case TokenType::Scope:
210                {
211                    expect(TokenType::Scope);
212                    auto memberName = expect(TokenType::Identifier);
213                    base = new StaticMemberRef(base, memberName);
214                }
215                break;
216            case TokenType::LParent:
217                {
218                    // TODO: actually parse this!
219                    readBalanced();
220                }
221                break;
222
223            case TokenType::OpLess:
224                {
225                    auto specialize = RefPtr(new SpecializeExpr());
226                    specialize->base = base;
227
228                    // Okay, we have a template application here.
229                    expect(TokenType::OpLess);
230                    specialize->args = parseCppTemplateArgs();
231                    parseGenericCloser();
232
233                    base = specialize;
234                }
235                break;
236            }
237        }
238    }
239
240    RefPtr<Expr> parseCppSimpleTypeSpecififer()
241    {
242        while (advanceIf("const") || advanceIf("static"))
243            ;
244
245        switch (peekType())
246        {
247        case TokenType::Identifier:
248            {
249                auto nameToken = expect(TokenType::Identifier);
250                return new NameExpr(nameToken);
251            }
252            break;
253
254        default:
255            expect(TokenType::Identifier);
256            _sink.diagnose(SourceLoc(), fiddle::Diagnostics::internalError);
257            return nullptr;
258        }
259    }
260
261    List<RefPtr<Expr>> parseCppTemplateArgs()
262    {
263        List<RefPtr<Expr>> args;
264        for (;;)
265        {
266            switch (peekType())
267            {
268            case TokenType::OpGeq:
269            case TokenType::OpGreater:
270            case TokenType::OpRsh:
271            case TokenType::EndOfFile:
272                return args;
273            }
274
275            auto arg = parseCppExpr();
276            if (arg)
277                args.add(arg);
278
279            if (!advanceIf(TokenType::Comma))
280                return args;
281        }
282    }
283
284    void parseGenericCloser()
285    {
286        if (advanceIf(TokenType::OpGreater))
287            return;
288
289        if (peekType() == TokenType::OpRsh)
290        {
291            peek().setType(TokenType::OpGreater);
292
293            return;
294        }
295
296        expect(TokenType::OpGreater);
297    }
298
299    RefPtr<Expr> parseCppTypeSpecifier()
300    {
301        auto result = parseCppSimpleTypeSpecififer();
302        for (;;)
303        {
304            switch (peekType())
305            {
306            default:
307                return result;
308
309            case TokenType::Scope:
310                {
311                    expect(TokenType::Scope);
312                    auto memberName = expect(TokenType::Identifier);
313                    auto memberRef = RefPtr(new StaticMemberRef(result, memberName));
314                    result = memberRef;
315                }
316                break;
317
318            case TokenType::OpLess:
319                {
320                    auto specialize = RefPtr(new SpecializeExpr());
321                    specialize->base = result;
322
323                    // Okay, we have a template application here.
324                    expect(TokenType::OpLess);
325                    specialize->args = parseCppTemplateArgs();
326                    parseGenericCloser();
327
328                    result = specialize;
329                }
330                break;
331            }
332        }
333    }
334
335    struct UnwrappedDeclarator
336    {
337        RefPtr<Expr> type;
338        TokenWithTrivia nameToken;
339    };
340
341    UnwrappedDeclarator unwrapDeclarator(RefPtr<Declarator> declarator, RefPtr<Expr> type)
342    {
343        if (!declarator)
344        {
345            UnwrappedDeclarator result;
346            result.type = type;
347            return result;
348        }
349
350        if (auto ptrDeclarator = as<PtrDeclarator>(declarator))
351        {
352            return unwrapDeclarator(ptrDeclarator->base, new PtrType(type));
353        }
354        else if (auto nameDeclarator = as<NameDeclarator>(declarator))
355        {
356            UnwrappedDeclarator result;
357            result.type = type;
358            result.nameToken = nameDeclarator->nameToken;
359            return result;
360        }
361        else
362        {
363            _sink.diagnose(SourceLoc(), Diagnostics::unexpected, "declarator type", "known");
364            return UnwrappedDeclarator();
365        }
366    }
367
368    RefPtr<Expr> parseCppType()
369    {
370        auto typeSpecifier = parseCppTypeSpecifier();
371        auto declarator = parseCppDeclarator();
372        return unwrapDeclarator(declarator, typeSpecifier).type;
373    }
374
375    RefPtr<Expr> parseCppBase()
376    {
377        // TODO: allow `private` and `protected`
378        // TODO: insert a default `public` keyword, if one is missing...
379        advanceIf("public");
380        return parseCppType();
381    }
382
383    void parseCppAggTypeDecl(RefPtr<AggTypeDecl> decl)
384    {
385        decl->mode = Mode::Cpp;
386
387        // read the type name
388        decl->nameToken = expect(TokenType::Identifier);
389
390        // Read the bases clause.
391        //
392        // TODO: handle multiple bases...
393        //
394        if (advanceIf(TokenType::Colon))
395        {
396            decl->directBaseType = parseCppBase();
397        }
398
399        expect(TokenType::LBrace);
400        addDecl(decl);
401        WithParentDecl withParent(this, decl);
402
403        // We expect any `FIDDLE()`-marked aggregate type declaration to start
404        // with a `FIDDLE(...)` or `FIDDLE(myFunc(a,b,c))` invocation, so that
405        // there is a suitable insertion point for the expansion step or the
406        // user has specific a custom step
407        //
408        {
409            auto saved = _cursor;
410            bool found = peekFiddleEllipsisInvocation() || peekFiddleLuaCall();
411            _cursor = saved;
412            if (!found)
413            {
414                _sink.diagnose(
415                    peekLoc(),
416                    fiddle::Diagnostics::expectedFiddleEllipsisInvocation,
417                    decl->nameToken.getContent());
418            }
419        }
420
421        parseCppDecls(decl);
422        expect(TokenType::RBrace);
423    }
424
425    bool peekFiddleEllipsisInvocation()
426    {
427        if (!advanceIf("FIDDLE"))
428            return false;
429
430        if (!advanceIf(TokenType::LParent))
431            return false;
432
433        if (!advanceIf(TokenType::Ellipsis))
434            return false;
435
436        return true;
437    }
438
439    bool peekFiddleLuaCall()
440    {
441        auto saved = _cursor;
442        const bool found = advanceIf(TokenType::Identifier) &&
443                           (peekType() == TokenType::LParent || peekType() == TokenType::LBrace);
444        _cursor = saved;
445        return found;
446    }
447
448    RefPtr<Declarator> parseCppSimpleDeclarator()
449    {
450        switch (peekType())
451        {
452        case TokenType::Identifier:
453            {
454                auto nameToken = expect(TokenType::Identifier);
455                return RefPtr(new NameDeclarator(nameToken));
456            }
457
458        default:
459            return nullptr;
460        }
461    }
462
463    RefPtr<Declarator> parseCppPostfixDeclarator()
464    {
465        auto result = parseCppSimpleDeclarator();
466        for (;;)
467        {
468            switch (peekType())
469            {
470            default:
471                return result;
472
473            case TokenType::LBracket:
474                readBalanced();
475                return result;
476            }
477        }
478        return result;
479    }
480
481    RefPtr<Declarator> parseCppDeclarator()
482    {
483        while (advanceIf("const") || advanceIf("static"))
484            ;
485
486        if (advanceIf(TokenType::OpMul))
487        {
488            auto base = parseCppDeclarator();
489            return RefPtr(new PtrDeclarator(base));
490        }
491        else
492        {
493            return parseCppPostfixDeclarator();
494        }
495    }
496
497    void parseCppDeclaratorBasedDecl(List<RefPtr<ModifierNode>> const& fiddleModifiers)
498    {
499        auto typeSpecifier = parseCppTypeSpecifier();
500        auto declarator = parseCppDeclarator();
501
502        auto unwrapped = unwrapDeclarator(declarator, typeSpecifier);
503
504        auto varDecl = RefPtr(new VarDecl());
505        varDecl->nameToken = unwrapped.nameToken;
506        varDecl->type = unwrapped.type;
507        addDecl(varDecl);
508
509        if (advanceIf(TokenType::OpAssign))
510        {
511            varDecl->initExpr = parseCppExpr();
512        }
513        expect(TokenType::Semicolon);
514    }
515
516    void parseNativeDeclaration(List<RefPtr<ModifierNode>> const& fiddleModifiers)
517    {
518        auto keyword = peek();
519        if (advanceIf("namespace"))
520        {
521            RefPtr<PhysicalNamespaceDecl> namespaceDecl = new PhysicalNamespaceDecl();
522            namespaceDecl->modifiers = fiddleModifiers;
523
524
525            // read the namespace name
526            namespaceDecl->nameToken = expect(TokenType::Identifier);
527
528            expect(TokenType::LBrace);
529
530            addDecl(namespaceDecl);
531            WithParentDecl withNamespace(this, namespaceDecl);
532
533            parseCppDecls(namespaceDecl);
534
535            expect(TokenType::RBrace);
536        }
537        else if (advanceIf("class"))
538        {
539            auto decl = RefPtr(new ClassDecl());
540            decl->modifiers = fiddleModifiers;
541            parseCppAggTypeDecl(decl);
542        }
543        else if (advanceIf("struct"))
544        {
545            auto decl = RefPtr(new StructDecl());
546            decl->modifiers = fiddleModifiers;
547            parseCppAggTypeDecl(decl);
548        }
549        else if (peekType() == TokenType::Identifier)
550        {
551            // try to parse a declarator-based declaration
552            // (which for now is probably a field);
553            //
554            parseCppDeclaratorBasedDecl(fiddleModifiers);
555        }
556        else
557        {
558            _sink.diagnose(peekLoc(), fiddle::Diagnostics::unexpected, peekType(), "OTHER");
559            _sink.diagnose(SourceLoc(), fiddle::Diagnostics::internalError);
560        }
561    }
562
563    List<RefPtr<ModifierNode>> parseFiddleModifiers()
564    {
565        List<RefPtr<ModifierNode>> modifiers;
566
567        for (;;)
568        {
569            switch (peekType())
570            {
571            default:
572                return modifiers;
573
574            case TokenType::Identifier:
575                if (advanceIf("abstract"))
576                {
577                    modifiers.add(new AbstractModifier());
578                }
579                else if (advanceIf("hidden"))
580                {
581                    modifiers.add(new HiddenModifier());
582                }
583                else
584                {
585                    return modifiers;
586                }
587                break;
588
589            case TokenType::LBrace:
590                {
591                    const auto b = read();
592                    StringBuilder sb;
593                    sb << b.getContent();
594                    for (int i = 0; i < b.getSkipCount(); ++i)
595                        sb << read().getContent() << " ";
596                    modifiers.add(new TableModifier(std::move(sb)));
597                }
598                break;
599            }
600        }
601
602        return modifiers;
603    }
604
605    RefPtr<Expr> parseFiddlePrimaryExpr()
606    {
607        switch (peekType())
608        {
609        case TokenType::Identifier:
610            return new NameExpr(read());
611
612        case TokenType::LParent:
613            {
614                expect(TokenType::LParent);
615                auto expr = parseFiddleExpr();
616                expect(TokenType::RParent);
617                return expr;
618            }
619
620        default:
621            expect(TokenType::Identifier);
622            return nullptr;
623        }
624    }
625
626    List<RefPtr<Arg>> parseFiddleArgs()
627    {
628        List<RefPtr<Arg>> args;
629        for (;;)
630        {
631            switch (peekType())
632            {
633            case TokenType::RBrace:
634            case TokenType::RBracket:
635            case TokenType::RParent:
636            case TokenType::EndOfFile:
637                return args;
638
639            default:
640                break;
641            }
642
643            auto arg = parseFiddleExpr();
644            args.add(arg);
645
646            if (!advanceIf(TokenType::Comma))
647                return args;
648        }
649    }
650
651    RefPtr<Expr> parseFiddlePostifxExpr()
652    {
653        auto result = parseFiddlePrimaryExpr();
654
655        for (;;)
656        {
657            switch (peekType())
658            {
659            default:
660                return result;
661
662            case TokenType::Dot:
663                {
664                    expect(TokenType::Dot);
665                    auto memberName = expect(TokenType::Identifier);
666
667                    result = new MemberExpr(result, memberName);
668                }
669                break;
670
671            case TokenType::LParent:
672                {
673                    expect(TokenType::LParent);
674                    auto args = parseFiddleArgs();
675                    expect(TokenType::RParent);
676
677                    result = new CallExpr(result, args);
678                }
679                break;
680            }
681        }
682    }
683    RefPtr<Expr> parseFiddleExpr() { return parseFiddlePostifxExpr(); }
684
685    RefPtr<Expr> parseFiddleTypeExpr() { return parseFiddleExpr(); }
686
687    void parseFiddleAggTypeDecl(RefPtr<AggTypeDecl> decl)
688    {
689        decl->mode = Mode::Fiddle;
690
691        // read the type name
692        decl->nameToken = expect(TokenType::Identifier);
693
694        // Read the bases clause.
695        if (advanceIf(TokenType::Colon))
696        {
697            decl->directBaseType = parseFiddleTypeExpr();
698        }
699
700        addDecl(decl);
701        WithParentDecl withParent(this, decl);
702
703        if (advanceIf(TokenType::LBrace))
704        {
705            parseOptionalFiddleModeDecls();
706
707            expect(TokenType::RBrace);
708        }
709        else
710        {
711            expect(TokenType::Semicolon);
712        }
713    }
714
715    void parseFiddleModeDecl(List<RefPtr<ModifierNode>> modifiers)
716    {
717        if (advanceIf("class"))
718        {
719            auto decl = RefPtr(new ClassDecl());
720            decl->modifiers = modifiers;
721            parseFiddleAggTypeDecl(decl);
722        }
723        else
724        {
725            _sink.diagnose(
726                peekLoc(),
727                Diagnostics::unexpected,
728                peekType(),
729                "fiddle-mode declaration");
730        }
731    }
732
733    void parseFiddleModeDecl()
734    {
735        auto modifiers = parseFiddleModifiers();
736        parseFiddleModeDecl(modifiers);
737    }
738
739    void parseOptionalFiddleModeDecls()
740    {
741        for (;;)
742        {
743            switch (peekType())
744            {
745            case TokenType::RParent:
746            case TokenType::RBrace:
747            case TokenType::RBracket:
748            case TokenType::EndOfFile:
749                return;
750            }
751
752            parseFiddleModeDecl();
753        }
754    }
755
756    void parseFiddleModeDecls(List<RefPtr<ModifierNode>> modifiers)
757    {
758        parseFiddleModeDecl(modifiers);
759        parseOptionalFiddleModeDecls();
760    }
761
762    void parseFiddleNode()
763    {
764        auto fiddleToken = expect("FIDDLE");
765
766        // We will capture the token at this invocation site,
767        // because later on we will generate a macro that
768        // this invocation will expand into.
769        //
770        auto fiddleMacroInvocation = RefPtr(new FiddleMacroInvocation());
771        fiddleMacroInvocation->fiddleToken = fiddleToken;
772        addDecl(fiddleMacroInvocation);
773
774        // The `FIDDLE` keyword can be followed by parentheses around a bunch of
775        // fiddle-mode modifiers.
776        List<RefPtr<ModifierNode>> fiddleModifiers;
777        if (advanceIf(TokenType::LParent))
778        {
779            if (advanceIf(TokenType::Ellipsis))
780            {
781                // A `FIDDLE(...)` invocation is a hook for
782                // our expansion step to insert the generated
783                // declarations that go into the body of
784                // the parent declaration.
785
786                fiddleMacroInvocation->node = _currentParentDecl;
787
788                expect(TokenType::RParent);
789                return;
790            }
791
792
793            // We start off by parsing optional modifiers
794            fiddleModifiers = parseFiddleModifiers();
795
796            if (peekType() != TokenType::RParent)
797            {
798                if (peekFiddleLuaCall())
799                {
800                    StringBuilder sb;
801                    const auto f = expect(TokenType::Identifier);
802                    const auto b = read();
803                    sb << f.getContent() << b.getContent();
804                    for (int i = 0; i < b.getSkipCount(); ++i)
805                        sb << read().getContent() << " ";
806                    auto fiddleLuaCall = RefPtr(new FiddleLuaCallInvocation());
807                    fiddleLuaCall->fiddleToken = fiddleToken;
808                    fiddleLuaCall->parentDecl = _currentParentDecl;
809                    fiddleLuaCall->callString = std::move(sb);
810                    addDecl(fiddleLuaCall);
811
812                    expect(TokenType::RParent);
813                    return;
814                }
815                else
816                {
817
818                    // In this case we are expecting a fiddle-mode declaration
819                    // to appear, in which case we will allow any number of full
820                    // fiddle-mode declarations, but won't expect a C++-mode
821                    // declaration to follow.
822
823                    // TODO: We should associate these declarations
824                    // as children of the `FiddleMacroInvocation`,
825                    // so that they can be emitted as part of its
826                    // expansion (if we decide to make more use
827                    // of the `FIDDLE()` approach...).
828
829                    parseFiddleModeDecls(fiddleModifiers);
830                    expect(TokenType::RParent);
831                    return;
832                }
833            }
834            expect(TokenType::RParent);
835        }
836        else
837        {
838            // TODO: diagnose this!
839        }
840
841        // Any tokens from here on are expected to be in C++-mode
842
843        parseNativeDeclaration(fiddleModifiers);
844    }
845
846    void addDecl(ContainerDecl* parentDecl, Decl* memberDecl)
847    {
848        if (!memberDecl)
849            return;
850
851        parentDecl->members.add(memberDecl);
852
853        auto physicalParent = as<PhysicalContainerDecl>(parentDecl);
854        if (!physicalParent)
855            return;
856
857        auto logicalParent = physicalParent->logicalVersion;
858        if (!logicalParent)
859            return;
860
861        if (auto physicalNamespace = as<PhysicalNamespaceDecl>(memberDecl))
862        {
863            auto namespaceName = physicalNamespace->nameToken.getContent();
864            auto logicalNamespace = findDecl<LogicalNamespace>(logicalParent, namespaceName);
865            if (!logicalNamespace)
866            {
867                logicalNamespace = new LogicalNamespace();
868
869                logicalNamespace->nameToken = physicalNamespace->nameToken;
870
871                logicalParent->members.add(logicalNamespace);
872                logicalParent->mapNameToMember.add(namespaceName, logicalNamespace);
873            }
874            physicalNamespace->logicalVersion = logicalNamespace;
875        }
876        else
877        {
878            logicalParent->members.add(memberDecl);
879        }
880    }
881
882    void addDecl(RefPtr<Decl> decl) { addDecl(_currentParentDecl, decl); }
883
884    void parseCppDecls(RefPtr<ContainerDecl> parentDecl)
885    {
886        for (;;)
887        {
888            switch (peekType())
889            {
890            case TokenType::EndOfFile:
891            case TokenType::RBrace:
892            case TokenType::RBracket:
893            case TokenType::RParent:
894                return;
895
896            default:
897                break;
898            }
899
900            parseCppDecl();
901        }
902    }
903
904    void readBalanced()
905    {
906        Count skipCount = read().getSkipCount();
907        _cursor = _cursor + skipCount;
908    }
909
910    void parseCppDecl()
911    {
912        // We consume raw tokens until we see something
913        // that ought to start a reflected/extracted declaration.
914        //
915        for (;;)
916        {
917            switch (peekType())
918            {
919            default:
920                {
921                    readBalanced();
922                    continue;
923                }
924
925            case TokenType::RBrace:
926            case TokenType::RBracket:
927            case TokenType::RParent:
928            case TokenType::EndOfFile:
929                return;
930
931            case TokenType::Identifier:
932                break;
933
934            case TokenType::Pound:
935                // a `#` means we have run into a preprocessor directive
936                // (or, somehow, we are already *inside* one...).
937                //
938                // We don't want to try to intercept anything to do with
939                // these lines, so we will read until the next end-of-line.
940                //
941                read();
942                while (!(peek().getToken().flags & TokenFlag::AtStartOfLine))
943                {
944                    if (peekType() == TokenType::EndOfFile)
945                        break;
946                    read();
947                }
948                continue;
949            }
950
951            // Okay, we have an identifier, but is its name
952            // one that we want to pay attention to?
953            //
954            //
955            auto name = peek().getContent();
956            if (name == "FIDDLE")
957            {
958                // If the `FIDDLE` is the first token we are seeing, then we will
959                // start parsing a construct in fiddle-mode:
960                //
961                parseFiddleNode();
962            }
963            else
964            {
965                // If the name isn't one we recognize, then
966                // we are just reading raw tokens as usual.
967                //
968                readBalanced();
969                continue;
970            }
971        }
972    }
973
974    RefPtr<SourceUnit> parseSourceUnit()
975    {
976        RefPtr<SourceUnit> sourceUnit = new SourceUnit();
977        sourceUnit->logicalVersion = _module;
978
979        WithParentDecl withSourceUnit(this, sourceUnit);
980        while (_cursor != _end)
981        {
982            parseCppDecl();
983
984            switch (peekType())
985            {
986            default:
987                break;
988
989            case TokenType::RBrace:
990            case TokenType::RBracket:
991            case TokenType::RParent:
992            case TokenType::EndOfFile:
993                read();
994                break;
995            }
996        }
997        read();
998
999        return sourceUnit;
1000    }
1001};
1002
1003
1004// Check
1005
1006struct CheckContext
1007{
1008private:
1009    DiagnosticSink& sink;
1010
1011public:
1012    CheckContext(DiagnosticSink& sink)
1013        : sink(sink)
1014    {
1015    }
1016
1017    void checkModule(LogicalModule* module) { checkMemberDecls(module); }
1018
1019private:
1020    struct Scope
1021    {
1022    public:
1023        Scope(ContainerDecl* containerDecl, Scope* outer)
1024            : containerDecl(containerDecl), outer(outer)
1025        {
1026        }
1027
1028        ContainerDecl* containerDecl = nullptr;
1029        Scope* outer = nullptr;
1030    };
1031    Scope* currentScope = nullptr;
1032
1033    struct WithScope : Scope
1034    {
1035        WithScope(CheckContext* context, ContainerDecl* containerDecl)
1036            : Scope(containerDecl, context->currentScope)
1037            , _context(context)
1038            , _saved(context->currentScope)
1039        {
1040            context->currentScope = this;
1041        }
1042
1043        ~WithScope() { _context->currentScope = _saved; }
1044
1045    private:
1046        CheckContext* _context = nullptr;
1047        Scope* _saved = nullptr;
1048    };
1049
1050
1051    //
1052    void checkDecl(Decl* decl)
1053    {
1054        if (auto aggTypeDecl = as<AggTypeDecl>(decl))
1055        {
1056            checkTypeExprInPlace(aggTypeDecl->directBaseType);
1057
1058            if (auto baseType = aggTypeDecl->directBaseType)
1059            {
1060                if (auto baseDeclRef = as<DirectDeclRef>(baseType))
1061                {
1062                    auto baseDecl = baseDeclRef->decl;
1063                    if (auto baseAggTypeDecl = as<AggTypeDecl>(baseDecl))
1064                    {
1065                        baseAggTypeDecl->directSubTypeDecls.add(aggTypeDecl);
1066                    }
1067                }
1068            }
1069
1070            checkMemberDecls(aggTypeDecl);
1071        }
1072        else if (auto namespaceDecl = as<LogicalNamespace>(decl))
1073        {
1074            checkMemberDecls(namespaceDecl);
1075        }
1076        else if (auto varDecl = as<VarDecl>(decl))
1077        {
1078            // Note: for now we aren't trying to check the type
1079            // or the initial-value expression of a field.
1080        }
1081        else if (as<FiddleMacroInvocation>(decl))
1082        {
1083        }
1084        else if (as<FiddleLuaCallInvocation>(decl))
1085        {
1086        }
1087        else
1088        {
1089            sink.diagnose(SourceLoc(), Diagnostics::unexpected, "case in checkDecl", "known type");
1090        }
1091    }
1092
1093    void checkMemberDecls(ContainerDecl* containerDecl)
1094    {
1095        WithScope moduleScope(this, containerDecl);
1096        for (auto memberDecl : containerDecl->members)
1097        {
1098            checkDecl(memberDecl);
1099        }
1100    }
1101
1102    void checkTypeExprInPlace(RefPtr<Expr>& ioTypeExpr)
1103    {
1104        if (!ioTypeExpr)
1105            return;
1106        ioTypeExpr = checkTypeExpr(ioTypeExpr);
1107    }
1108
1109    RefPtr<Expr> checkTypeExpr(Expr* expr) { return checkExpr(expr); }
1110
1111    RefPtr<Expr> checkExpr(Expr* expr)
1112    {
1113        if (auto nameExpr = as<NameExpr>(expr))
1114        {
1115            return lookUp(nameExpr->nameToken.getContent());
1116        }
1117        else
1118        {
1119            sink.diagnose(SourceLoc(), Diagnostics::unexpected, "case in checkExpr", "known type");
1120            return nullptr;
1121        }
1122    }
1123
1124    RefPtr<Expr> lookUp(UnownedStringSlice const& name)
1125    {
1126        for (auto scope = currentScope; scope; scope = scope->outer)
1127        {
1128            auto containerDecl = scope->containerDecl;
1129            // TODO: accelerate lookup with a dictionary on the container...
1130            for (auto memberDecl : containerDecl->members)
1131            {
1132                if (memberDecl->nameToken.getContent() == name)
1133                {
1134                    return new DirectDeclRef(memberDecl);
1135                }
1136            }
1137        }
1138        sink.diagnose(SourceLoc(), Diagnostics::undefinedIdentifier, name);
1139        return nullptr;
1140    }
1141};
1142
1143void push(lua_State* L, Val* val);
1144
1145// Emit
1146
1147struct EmitContext
1148{
1149private:
1150    SourceManager& _sourceManager;
1151    RefPtr<LogicalModule> _module;
1152    StringBuilder& _builder;
1153    DiagnosticSink& _sink;
1154
1155public:
1156    EmitContext(
1157        StringBuilder& builder,
1158        DiagnosticSink& sink,
1159        SourceManager& sourceManager,
1160        LogicalModule* module)
1161        : _builder(builder), _sourceManager(sourceManager), _module(module), _sink(sink)
1162    {
1163    }
1164
1165    void emitMacrosRec(Decl* decl)
1166    {
1167        emitMacrosForDecl(decl);
1168        if (auto container = as<ContainerDecl>(decl))
1169        {
1170            for (auto member : container->members)
1171                emitMacrosRec(member);
1172        }
1173    }
1174
1175private:
1176    void emitMacrosForDecl(Decl* decl)
1177    {
1178        if (auto fiddleMacroInvocation = as<FiddleMacroInvocation>(decl))
1179        {
1180            emitMacroForFiddleInvocation(fiddleMacroInvocation);
1181        }
1182        else if (const auto fiddleLuaCallInvocation = as<FiddleLuaCallInvocation>(decl))
1183        {
1184            emitMacroForFiddleLuaCallInvocation(fiddleLuaCallInvocation);
1185        }
1186        else
1187        {
1188            // do nothing with most decls
1189        }
1190    }
1191
1192#define MACRO_LINE_ENDING " \\\n"
1193
1194    void emitMacroForFiddleInvocationPreamble(const TokenWithTrivia& fiddleToken)
1195    {
1196        const auto loc = fiddleToken.getLoc();
1197        const auto humaneLoc = _sourceManager.getHumaneLoc(loc);
1198        const auto lineNumber = humaneLoc.line;
1199        // Un-define the old `FIDDLE_#` macro for the
1200        // given line number, since this file might
1201        // be pulling in another generated header
1202        // via one of its dependencies.
1203        //
1204        _builder.append("#ifdef FIDDLE_");
1205        _builder.append(lineNumber);
1206        _builder.append("\n#undef FIDDLE_");
1207        _builder.append(lineNumber);
1208        _builder.append("\n#endif\n");
1209
1210        _builder.append("#define FIDDLE_");
1211        _builder.append(lineNumber);
1212        _builder.append("(...)");
1213        _builder.append(MACRO_LINE_ENDING);
1214    }
1215
1216    void emitMacroForFiddleInvocationPostamble() { _builder.append("/* end */\n\n"); }
1217
1218    void emitMacroForFiddleLuaCallInvocation(FiddleLuaCallInvocation* fiddleInvocation)
1219    {
1220        _builder.append("/*\n");
1221        _builder.append(fiddleInvocation->callString);
1222        _builder.append("\n*/\n");
1223
1224        emitMacroForFiddleInvocationPreamble(fiddleInvocation->fiddleToken);
1225
1226        const auto file =
1227            _sourceManager.getHumaneLoc(fiddleInvocation->fiddleToken.getLoc()).pathInfo.getName();
1228        StringBuilder sb;
1229        sb << "require(\"" << file << ".lua\")." << fiddleInvocation->callString;
1230
1231        // Create the fiddle table
1232        const auto L = getLuaState();
1233        lua_newtable(L);
1234        push(L, fiddleInvocation->parentDecl);
1235        lua_setfield(L, -2, "current_decl");
1236        lua_setglobal(L, "fiddle");
1237
1238        const auto output = evaluateLuaExpression(
1239            fiddleInvocation->fiddleToken.getLoc(),
1240            file,
1241            sb.produceString(),
1242            &_sink);
1243
1244        // Deregister the fiddle table
1245        lua_pushnil(L);
1246        lua_setglobal(L, "fiddle");
1247
1248        _builder.append(StringUtil::replaceAll(
1249            output.getUnownedSlice(),
1250            UnownedStringSlice("\n"),
1251            UnownedStringSlice(MACRO_LINE_ENDING)));
1252
1253        _builder.append(MACRO_LINE_ENDING);
1254        emitMacroForFiddleInvocationPostamble();
1255    }
1256
1257    void emitMacroForFiddleInvocation(FiddleMacroInvocation* fiddleInvocation)
1258    {
1259        emitMacroForFiddleInvocationPreamble(fiddleInvocation->fiddleToken);
1260
1261        auto decl = as<AggTypeDecl>(fiddleInvocation->node);
1262        if (decl)
1263        {
1264            if (auto base = decl->directBaseType)
1265            {
1266                _builder.append("private: typedef ");
1267                emitTypedDecl(base, "Super");
1268                _builder.append(";" MACRO_LINE_ENDING);
1269            }
1270
1271            if (decl->isSubTypeOf("NodeBase"))
1272            {
1273                _builder.append("friend class ::Slang::ASTBuilder;" MACRO_LINE_ENDING);
1274                _builder.append("friend struct ::Slang::SyntaxClassInfo;" MACRO_LINE_ENDING);
1275
1276                _builder.append("public: static const ::Slang::SyntaxClassInfo "
1277                                "kSyntaxClassInfo;" MACRO_LINE_ENDING);
1278
1279                _builder.append("public: static constexpr ASTNodeType kType = ASTNodeType::");
1280                _builder.append(decl->nameToken.getContent());
1281                _builder.append(";" MACRO_LINE_ENDING);
1282
1283                if (decl->findModifier<AbstractModifier>())
1284                {
1285                    _builder.append("protected: ");
1286                }
1287                else
1288                {
1289                    _builder.append("public: ");
1290                }
1291                _builder.append(decl->nameToken.getContent());
1292                _builder.append("() {}" MACRO_LINE_ENDING);
1293            }
1294            _builder.append("public:" MACRO_LINE_ENDING);
1295        }
1296        emitMacroForFiddleInvocationPostamble();
1297    }
1298
1299    void emitTypedDecl(Expr* expr, const char* name)
1300    {
1301        if (auto declRef = as<DirectDeclRef>(expr))
1302        {
1303            _builder.append(declRef->decl->nameToken.getContent());
1304            _builder.append(" ");
1305            _builder.append(name);
1306        }
1307    }
1308
1309#if 0
1310        void emitLineDirective(Token const& lexeme)
1311        {
1312            SourceLoc loc = lexeme.getLoc();
1313            auto humaneLoc = _sourceManager.getHumaneLoc(loc);
1314            _builder.append("\n#line ");
1315            _builder.append(humaneLoc.line);
1316            _builder.append(" \"");
1317            for (auto c : humaneLoc.pathInfo.getName())
1318            {
1319                if (c == '\\') _builder.append("\\\\");
1320                else _builder.append(c);
1321            }
1322            _builder.append("\"\n");
1323        }
1324
1325        void emitLineDirective(TokenWithTrivia const& token)
1326        {
1327            if (token.getLeadingTrivia().getCount() != 0)
1328                emitLineDirective(token.getLeadingTrivia()[0]);
1329            else
1330                emitLineDirective(token.getToken());
1331        }
1332
1333        void emitLineDirective(RawNode* node)
1334        {
1335            emitLineDirective(node->tokens[0]);
1336        }
1337
1338
1339        void emitTrivia(List<Token> const& trivia)
1340        {
1341            for (auto trivium : trivia)
1342                _builder.append(trivium.getContent());
1343        }
1344
1345        void emitRawNode(RawNode* rawNode)
1346        {
1347            for (auto token : rawNode->tokens)
1348            {
1349                emitTrivia(token.getLeadingTrivia());
1350                _builder.append(token.getContent());
1351                emitTrivia(token.getTrailingTrivia());
1352            }
1353        }
1354
1355        void emitTopLevelNode(Decl* node)
1356        {
1357            if (!node)
1358                return;
1359
1360            if (node->findModifier<HiddenModifier>())
1361                return;
1362
1363            if (auto rawNode = as<RawNode>(node))
1364            {
1365                // TODO: should emit a `#line` to point back to
1366                // the original source file...
1367                emitLineDirective(rawNode);
1368
1369                emitRawNode(rawNode);
1370            }
1371            else if (auto decl = as<PhysicalNamespaceDecl>(node))
1372            {
1373                for (auto child : decl->members)
1374                {
1375                    emitTopLevelNode(child);
1376                }
1377            }
1378            else if (auto decl = as<AggTypeDecl>(node))
1379            {
1380                emitExtraMembersForAggTypeDecl(decl);
1381
1382                for (auto child : decl->members)
1383                {
1384                    emitTopLevelNode(child);
1385                }
1386            }
1387            else if (auto varDecl = as<VarDecl>(node))
1388            {
1389                // Note: nothing to be done here...
1390            }
1391            else
1392            {
1393                _sink.diagnose(SourceLoc(), fiddle::Diagnostics::unexpected, "emitTopLevelNode", "unhandled case");
1394            }
1395        }
1396
1397        void emitSourceUnit(SourceUnit* sourceUnit)
1398        {
1399            for (auto node : sourceUnit->members)
1400            {
1401                emitTopLevelNode(node);
1402            }
1403        }
1404
1405    private:
1406#endif
1407};
1408
1409
1410Decl* findDecl_(ContainerDecl* outerDecl, UnownedStringSlice const& name)
1411{
1412    for (auto memberDecl : outerDecl->members)
1413    {
1414        if (memberDecl->nameToken.getContent() == name)
1415            return memberDecl;
1416    }
1417    return nullptr;
1418}
1419
1420bool AggTypeDecl::isSubTypeOf(char const* name)
1421{
1422    Decl* decl = this;
1423    while (decl)
1424    {
1425        if (decl->nameToken.getContent() == UnownedTerminatedStringSlice(name))
1426        {
1427            return true;
1428        }
1429
1430        auto aggType = as<AggTypeDecl>(decl);
1431        if (!aggType)
1432            break;
1433
1434        auto baseTypeExpr = aggType->directBaseType;
1435        if (!baseTypeExpr)
1436            break;
1437
1438        auto declRef = as<DirectDeclRef>(baseTypeExpr);
1439        if (!declRef)
1440            break;
1441
1442        decl = declRef->decl;
1443    }
1444    return false;
1445}
1446
1447bool isTrivia(TokenType lexemeType)
1448{
1449    switch (lexemeType)
1450    {
1451    default:
1452        return false;
1453
1454    case TokenType::LineComment:
1455    case TokenType::BlockComment:
1456    case TokenType::NewLine:
1457    case TokenType::WhiteSpace:
1458        return true;
1459    }
1460}
1461
1462List<TokenWithTrivia> collectTokensWithTrivia(TokenList const& lexemes)
1463{
1464    TokenReader reader(lexemes);
1465
1466    List<TokenWithTrivia> allTokensWithTrivia;
1467    for (;;)
1468    {
1469        RefPtr<TokenWithTriviaNode> currentTokenWithTriviaNode = new TokenWithTriviaNode();
1470        TokenWithTrivia currentTokenWithTrivia = currentTokenWithTriviaNode;
1471        allTokensWithTrivia.add(currentTokenWithTrivia);
1472
1473        while (isTrivia(reader.peekTokenType()))
1474        {
1475            auto trivia = reader.advanceToken();
1476            currentTokenWithTriviaNode->leadingTrivia.add(trivia);
1477        }
1478
1479        auto token = reader.advanceToken();
1480        currentTokenWithTriviaNode->token = token;
1481
1482        if (token.type == TokenType::EndOfFile)
1483            return allTokensWithTrivia;
1484
1485        while (isTrivia(reader.peekTokenType()))
1486        {
1487            auto trivia = reader.advanceToken();
1488            currentTokenWithTriviaNode->trailingTrivia.add(trivia);
1489
1490            if (trivia.type == TokenType::NewLine)
1491                break;
1492        }
1493    }
1494}
1495
1496void readTokenTree(List<TokenWithTrivia> const& tokens, Index& ioIndex);
1497
1498void readBalancedToken(List<TokenWithTrivia> const& tokens, Index& ioIndex, TokenType closeType)
1499{
1500    auto open = tokens[ioIndex++];
1501    auto openNode = (TokenWithTriviaNode*)open;
1502
1503    Index startIndex = ioIndex;
1504    for (;;)
1505    {
1506        auto token = tokens[ioIndex];
1507        if (token.getType() == closeType)
1508        {
1509            ioIndex++;
1510            break;
1511        }
1512
1513        switch (token.getType())
1514        {
1515        default:
1516            readTokenTree(tokens, ioIndex);
1517            continue;
1518
1519        case TokenType::RBrace:
1520        case TokenType::RBracket:
1521        case TokenType::RParent:
1522        case TokenType::EndOfFile:
1523            break;
1524        }
1525        break;
1526    }
1527    openNode->skipCount = ioIndex - startIndex;
1528}
1529
1530void readTokenTree(List<TokenWithTrivia> const& tokens, Index& ioIndex)
1531{
1532    switch (tokens[ioIndex].getType())
1533    {
1534    default:
1535        ioIndex++;
1536        return;
1537
1538    case TokenType::LBrace:
1539        return readBalancedToken(tokens, ioIndex, TokenType::RBrace);
1540
1541    case TokenType::LBracket:
1542        return readBalancedToken(tokens, ioIndex, TokenType::RBracket);
1543
1544    case TokenType::LParent:
1545        return readBalancedToken(tokens, ioIndex, TokenType::RParent);
1546    }
1547}
1548
1549void matchBalancedTokens(List<TokenWithTrivia> tokens)
1550{
1551    Index index = 0;
1552    for (;;)
1553    {
1554        auto& token = tokens[index];
1555        switch (token.getType())
1556        {
1557        case TokenType::EndOfFile:
1558            return;
1559
1560        default:
1561            readTokenTree(tokens, index);
1562            break;
1563
1564        case TokenType::RBrace:
1565        case TokenType::RBracket:
1566        case TokenType::RParent:
1567            // error!!!
1568            index++;
1569            break;
1570        }
1571    }
1572}
1573
1574bool findOutputFileIncludeDirective(List<TokenWithTrivia> tokens, String outputFileName)
1575{
1576    auto cursor = tokens.begin();
1577    auto end = tokens.end() - 1;
1578
1579    while (cursor != end)
1580    {
1581        if (cursor->getType() != TokenType::Pound)
1582        {
1583            cursor++;
1584            continue;
1585        }
1586        cursor++;
1587
1588        if (cursor->getContent() != "include")
1589            continue;
1590        cursor++;
1591
1592        if (cursor->getType() != TokenType::StringLiteral)
1593            continue;
1594
1595        auto includedFileName = getStringLiteralTokenValue(cursor->getToken());
1596        if (includedFileName == outputFileName)
1597            return true;
1598    }
1599    return false;
1600}
1601
1602RefPtr<SourceUnit> parseSourceUnit(
1603    SourceView* inputSourceView,
1604    LogicalModule* logicalModule,
1605    NamePool* namePool,
1606    DiagnosticSink* sink,
1607    SourceManager* sourceManager,
1608    String outputFileName)
1609{
1610    Lexer lexer;
1611
1612    // We suppress any diagnostics that might get emitted during lexing,
1613    // so that we can ignore any files we don't understand.
1614    //
1615    DiagnosticSink lexerSink;
1616    lexer.initialize(inputSourceView, &lexerSink, namePool, sourceManager->getMemoryArena());
1617
1618    auto inputTokens = lexer.lexAllTokens();
1619    auto tokensWithTrivia = collectTokensWithTrivia(inputTokens);
1620    matchBalancedTokens(tokensWithTrivia);
1621
1622    Parser parser(*sink, tokensWithTrivia, logicalModule);
1623    auto sourceUnit = parser.parseSourceUnit();
1624
1625    // As a quick validation check, if the source file had
1626    // any `FIDDLE()` invocations in it, then we check to
1627    // make sure it also has a `#include` of the corresponding
1628    // output file name...
1629    if (hasAnyFiddleInvocations(sourceUnit))
1630    {
1631        if (!findOutputFileIncludeDirective(tokensWithTrivia, outputFileName))
1632        {
1633            sink->diagnose(
1634                inputSourceView->getRange().begin,
1635                fiddle::Diagnostics::expectedIncludeOfOutputHeader,
1636                outputFileName);
1637        }
1638    }
1639
1640    return sourceUnit;
1641}
1642
1643void push(lua_State* L, Val* val);
1644
1645void push(lua_State* L, UnownedStringSlice const& text)
1646{
1647    lua_pushlstring(L, text.begin(), text.getLength());
1648}
1649
1650template<typename T>
1651void push(lua_State* L, List<T> const& values)
1652{
1653    // Note: Lua tables are naturally indexed starting at 1.
1654    Index nextIndex = 1;
1655    lua_newtable(L);
1656    for (auto value : values)
1657    {
1658        Index index = nextIndex++;
1659
1660        push(L, value);
1661        lua_seti(L, -2, index);
1662    }
1663}
1664
1665List<RefPtr<AggTypeDecl>> getDirectSubclasses(AggTypeDecl* decl)
1666{
1667    List<RefPtr<AggTypeDecl>> result;
1668    for (auto subclass : decl->directSubTypeDecls)
1669        result.add(subclass);
1670    return result;
1671}
1672
1673void getAllSubclasses(AggTypeDecl* decl, List<RefPtr<AggTypeDecl>>& ioSubclasses)
1674{
1675    ioSubclasses.add(decl);
1676    for (auto subclass : decl->directSubTypeDecls)
1677        getAllSubclasses(subclass, ioSubclasses);
1678}
1679
1680List<RefPtr<AggTypeDecl>> getAllSubclasses(AggTypeDecl* decl)
1681{
1682    List<RefPtr<AggTypeDecl>> result;
1683    getAllSubclasses(decl, result);
1684    return result;
1685}
1686
1687int _toStringVal(lua_State* L)
1688{
1689    Val* val = (Val*)lua_touserdata(L, 1);
1690
1691    if (auto directDeclRef = as<DirectDeclRef>(val))
1692    {
1693        val = directDeclRef->decl;
1694    }
1695
1696    if (auto decl = as<Decl>(val))
1697    {
1698        push(L, decl->nameToken.getContent());
1699        return 1;
1700    }
1701
1702    lua_pushfstring(L, "fiddle::Val @ 0x%p", val);
1703    return 1;
1704}
1705
1706int _indexVal(lua_State* L)
1707{
1708    Val* val = (Val*)lua_touserdata(L, 1);
1709    char const* name = lua_tostring(L, 2);
1710
1711    // If we have some user data attached to this declaration, index that
1712    if (auto decl = as<Decl>(val))
1713    {
1714        if (auto tableModifier = decl->findModifier<TableModifier>())
1715        {
1716            // Check if we have a cached table
1717            if (tableModifier->tableRef == LUA_NOREF)
1718            {
1719                // Evaluate the table string and cache it
1720                std::string tableCode =
1721                    "return " + std::string(tableModifier->tableSource.getBuffer());
1722
1723                if (luaL_dostring(L, tableCode.c_str()) == LUA_OK)
1724                {
1725                    // Store the table in the registry
1726                    tableModifier->tableRef = luaL_ref(L, LUA_REGISTRYINDEX);
1727                }
1728                else
1729                {
1730                    // Handle error - pop error message and continue
1731                    lua_pop(L, 1);
1732                }
1733            }
1734
1735            // If we have a cached table, try to index it
1736            if (tableModifier->tableRef != LUA_NOREF)
1737            {
1738                // Get the cached table from registry
1739                lua_rawgeti(L, LUA_REGISTRYINDEX, tableModifier->tableRef);
1740
1741                // Index the table with the requested name
1742                lua_pushstring(L, name);
1743                lua_gettable(L, -2);
1744
1745                // Remove the table from stack, leaving just the result
1746                lua_remove(L, -2);
1747
1748                // Check if we found something
1749                if (!lua_isnil(L, -1))
1750                {
1751                    return 1;
1752                }
1753                else
1754                {
1755                    lua_pop(L, 1); // Pop the nil
1756                    // Fall through to check other properties
1757                }
1758            }
1759        }
1760    }
1761
1762    if (auto containerDecl = as<ContainerDecl>(val))
1763    {
1764        for (auto m : containerDecl->members)
1765        {
1766            if (m->nameToken.getContent() == UnownedTerminatedStringSlice(name))
1767            {
1768                push(L, m);
1769                return 1;
1770            }
1771        }
1772    }
1773
1774    if (auto aggTypeDecl = as<AggTypeDecl>(val))
1775    {
1776        if (strcmp(name, "directSubclasses") == 0)
1777        {
1778            auto value = getDirectSubclasses(aggTypeDecl);
1779            push(L, value);
1780            return 1;
1781        }
1782
1783        if (strcmp(name, "subclasses") == 0)
1784        {
1785            auto value = getAllSubclasses(aggTypeDecl);
1786            push(L, value);
1787            return 1;
1788        }
1789
1790        if (strcmp(name, "directSuperClass") == 0)
1791        {
1792            push(L, aggTypeDecl->directBaseType);
1793            return 1;
1794        }
1795    }
1796
1797    if (auto aggTypeDecl = as<AggTypeDecl>(val))
1798    {
1799        if (strcmp(name, "directFields") == 0)
1800        {
1801            List<RefPtr<Decl>> fields;
1802            for (auto m : aggTypeDecl->members)
1803            {
1804                if (auto f = as<VarDecl>(m))
1805                    fields.add(f);
1806            }
1807            push(L, fields);
1808            return 1;
1809        }
1810    }
1811
1812    if (auto decl = as<Decl>(val))
1813    {
1814        if (strcmp(name, "isAbstract") == 0)
1815        {
1816            lua_pushboolean(L, decl->findModifier<AbstractModifier>() != nullptr);
1817            return 1;
1818        }
1819        if (strcmp(name, "getDebugVisType") == 0)
1820        {
1821            auto aggTypeDecl = as<AggTypeDecl>(decl);
1822            if (aggTypeDecl)
1823            {
1824                if (aggTypeDecl->isSubTypeOf("Decl"))
1825                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Decl");
1826                else if (aggTypeDecl->isSubTypeOf("Expr"))
1827                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Expr");
1828                else if (aggTypeDecl->isSubTypeOf("Modifier"))
1829                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Modifier");
1830                else if (aggTypeDecl->isSubTypeOf("Stmt"))
1831                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Stmt");
1832                else if (aggTypeDecl->isSubTypeOf("Val"))
1833                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Val");
1834                else if (aggTypeDecl->isSubTypeOf("Scope"))
1835                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Scope");
1836                else
1837                    lua_pushstring(L, "SyntaxClassInfoDebugVisType::Unknown");
1838            }
1839            else
1840                lua_pushstring(L, "SyntaxClassInfoDebugVisType::Unknown");
1841            return 1;
1842        }
1843    }
1844
1845    if (auto varDecl = as<VarDecl>(val))
1846    {
1847        if (strcmp(name, "initExpr") == 0)
1848        {
1849            // TODO: do any expression here
1850            if (const auto literalExpr = as<LiteralExpr>(varDecl->initExpr))
1851            {
1852                lua_pushlstring(
1853                    L,
1854                    literalExpr->token.getContent().begin(),
1855                    literalExpr->token.getContent().getLength());
1856                return 1;
1857            }
1858            return 0;
1859        }
1860    }
1861
1862    return 0;
1863}
1864
1865void push(lua_State* L, Val* val)
1866{
1867    if (!val)
1868    {
1869        lua_pushnil(L);
1870        return;
1871    }
1872
1873    lua_pushlightuserdata(L, val);
1874    if (luaL_newmetatable(L, "fiddle::Val"))
1875    {
1876        lua_pushcfunction(L, &_indexVal);
1877        lua_setfield(L, -2, "__index");
1878
1879        lua_pushcfunction(L, &_toStringVal);
1880        lua_setfield(L, -2, "__tostring");
1881    }
1882    lua_setmetatable(L, -2);
1883}
1884
1885void registerValWithScript(String name, Val* val)
1886{
1887    auto L = getLuaState();
1888
1889    push(L, val);
1890    lua_setglobal(L, name.getBuffer());
1891}
1892
1893
1894void registerScrapedStuffWithScript(LogicalModule* logicalModule)
1895{
1896    for (auto decl : logicalModule->members)
1897    {
1898        if (!decl->nameToken)
1899            continue;
1900
1901        registerValWithScript(decl->nameToken.getContent(), decl);
1902    }
1903}
1904
1905bool _hasAnyFiddleInvocationsRec(Decl* decl)
1906{
1907    if (as<FiddleMacroInvocation>(decl))
1908        return true;
1909
1910    if (auto container = as<ContainerDecl>(decl))
1911    {
1912        for (auto m : container->members)
1913        {
1914            if (_hasAnyFiddleInvocationsRec(m))
1915                return true;
1916        }
1917    }
1918    return false;
1919}
1920
1921bool hasAnyFiddleInvocations(SourceUnit* sourceUnit)
1922{
1923    return _hasAnyFiddleInvocationsRec(sourceUnit);
1924}
1925
1926void checkModule(LogicalModule* module, DiagnosticSink* sink)
1927{
1928    CheckContext context(*sink);
1929    context.checkModule(module);
1930}
1931
1932
1933void emitSourceUnitMacros(
1934    SourceUnit* sourceUnit,
1935    StringBuilder& builder,
1936    DiagnosticSink* sink,
1937    SourceManager* sourceManager,
1938    LogicalModule* logicalModule)
1939{
1940    // The basic task here is to find each of the
1941    // `FIDDLE()` macro invocations, and for each
1942    // of them produce a matching definition that
1943    // will be used as the expansion of that one
1944    //
1945
1946    EmitContext context(builder, *sink, *sourceManager, logicalModule);
1947    context.emitMacrosRec(sourceUnit);
1948}
1949
1950} // namespace fiddle