| 1 | use v6; |
|---|
| 2 | use Test; |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | plan 3; |
|---|
| 6 | # Avoiding accidental multiple execution |
|---|
| 7 | # as occurs in C's defines and lisp macros |
|---|
| 8 | # This can also ensure a correct order of evaluation |
|---|
| 9 | |
|---|
| 10 | # This needs to be decided |
|---|
| 11 | # is CST (Concrete) |
|---|
| 12 | # is Thunk |
|---|
| 13 | # is Proxy |
|---|
| 14 | # is Reduced |
|---|
| 15 | # is Bound |
|---|
| 16 | # is Once |
|---|
| 17 | # is Evaluated |
|---|
| 18 | # is EvaluatedOnce |
|---|
| 19 | # is Eager |
|---|
| 20 | macro max ($x is CST, $y is CST) { |
|---|
| 21 | return quasi { ($x > $y) ?? $x !! $y }; |
|---|
| 22 | } |
|---|
| 23 | |
|---|
| 24 | # Alternatively, the default could be eval once and the special name |
|---|
| 25 | # is for the CallByName version |
|---|
| 26 | |
|---|
| 27 | my ($x, $y) = (1,2); |
|---|
| 28 | |
|---|
| 29 | my $got = max($x++, $y++); |
|---|
| 30 | |
|---|
| 31 | # say qc'$x = {$x} $y = {$y}'; |
|---|
| 32 | |
|---|
| 33 | is($x, 2, '$x incremented once', :todo<unspecced>); |
|---|
| 34 | is($y, 3, '$x incremented once', :todo<unspecced>); |
|---|
| 35 | is($got, 3, '$got incremented max', :todo<unspecced>); |
|---|
| 36 | |
|---|
| 37 | =begin pod |
|---|
| 38 | |
|---|
| 39 | =head1 quasi & AST splicing options |
|---|
| 40 | |
|---|
| 41 | =head2 Splicing Problem |
|---|
| 42 | |
|---|
| 43 | The contents of quasi blocks need to be able to refer to values |
|---|
| 44 | as any other closure does, but also define graft points for |
|---|
| 45 | other asts. |
|---|
| 46 | |
|---|
| 47 | =head2 Solution Space |
|---|
| 48 | |
|---|
| 49 | First principle, adding quasi before a block doesn't change things |
|---|
| 50 | - it's still a closure |
|---|
| 51 | - if you mention $var it means the $var in the surrounding lexical scope |
|---|
| 52 | (mentioning new vars may bind at the macro use) |
|---|
| 53 | - subs continue to be bound to the local definitions |
|---|
| 54 | - macros continue to be expanded at parse time (quasi parse time) |
|---|
| 55 | |
|---|
| 56 | Second principle, to do something special, say something special. |
|---|
| 57 | |
|---|
| 58 | Differences: possible variable/sub not defined errors may bind at macro uses |
|---|
| 59 | or are delayed. |
|---|
| 60 | |
|---|
| 61 | macro parameters may be ASTs, strings, or possibly a singly-evaluated-ASTs, |
|---|
| 62 | the point is to wrap or warp them into the returned AST|string. |
|---|
| 63 | We want an easy way to splice the input AST into the output AST |
|---|
| 64 | The output AST is usually a quasi block somehow using the macro arg asts: |
|---|
| 65 | |
|---|
| 66 | quasi { my $x; $x; $input_ast; $captured_var; $var_at_call } |
|---|
| 67 | |
|---|
| 68 | =head2 Some Suggested Solutions |
|---|
| 69 | |
|---|
| 70 | * Roles, anything that does QuasiQuote is spliced including ASTs, |
|---|
| 71 | ThunkishASTS and certain strings |
|---|
| 72 | literal($ast) macro to talk about an ast as a value |
|---|
| 73 | |
|---|
| 74 | * signature such as quasi ($ast) { $var; $ast } |
|---|
| 75 | (idea--: duplicates macro's param list, violates sig -> application w/ args |
|---|
| 76 | expectation with subs) |
|---|
| 77 | |
|---|
| 78 | * twigil used for interpolating AST/strings |
|---|
| 79 | |
|---|
| 80 | * escape meta-syntax (lisp's solution) |
|---|
| 81 | quasi { say "Exp (\qq[~$package]) = ", \qq[$package] } |
|---|
| 82 | |
|---|
| 83 | * set of escape macros which only apply in quasi blocks glue($ast) |
|---|
| 84 | |
|---|
| 85 | More complex restructuring of the input AST would require walking |
|---|
| 86 | the AST (or munging the string). Nothing spec'd for that yet. |
|---|
| 87 | We would likw to ensure only valid ASTs can be produced. |
|---|
| 88 | |
|---|
| 89 | Perl's rules may be able to scan the tree with L<S05/"Matching against non-strings"> |
|---|
| 90 | and just s:g/// to produce the output ast. |
|---|
| 91 | |
|---|
| 92 | If a tree grammar tool reaches the Perl 6 user space then that can be used |
|---|
| 93 | |
|---|
| 94 | =head2 Current Solution |
|---|
| 95 | |
|---|
| 96 | quasi { } is the quoter and {{{ }}} is the default antiquote |
|---|
| 97 | (the rule is actually the delimiter repeated thrice). |
|---|
| 98 | The use of {{{ ... }}} in a quasi is just like an inline macro. |
|---|
| 99 | Pretending that the body of you program is within a quasi{ } |
|---|
| 100 | then {{{ }}} can be used for inline macros, compile time |
|---|
| 101 | generation of ASTs which are spliced immediately. |
|---|
| 102 | |
|---|
| 103 | =head1 Recursive Macros |
|---|
| 104 | |
|---|
| 105 | We started thinking about these, but then realized we'd need to |
|---|
| 106 | know how they work, so we started thinking about these, ... |
|---|
| 107 | |
|---|
| 108 | These are banned in C but permitted in Lisp (and a source of |
|---|
| 109 | infinite loops during compilation). We could adopt either method |
|---|
| 110 | or try to reduce the likely pain. Lisp allows a recursive expander |
|---|
| 111 | function, but not a recursive expansion. |
|---|
| 112 | |
|---|
| 113 | It seems impossible to parse beyond a recursive macro call, |
|---|
| 114 | so we can ask Turing's oracle or give up. |
|---|
| 115 | |
|---|
| 116 | One remaining possibility to add the macro expansion to a todo list that |
|---|
| 117 | is run after the macro definition is completed. The effects of the |
|---|
| 118 | macro may then be different from the non-recursive case. |
|---|
| 119 | It is different to a mere sub call however, as it happens immediately |
|---|
| 120 | after the definition. |
|---|
| 121 | |
|---|
| 122 | Finally, a recursive call could merely be a subroutine call, allowing |
|---|
| 123 | recursion at runtime (which is compile time as far as the macro's caller |
|---|
| 124 | is concerned). This is perhaps close to C++ template expansion games. |
|---|
| 125 | |
|---|
| 126 | Recursive macros seem to be an unneeded feature, ast's equivalent |
|---|
| 127 | to those from a recursive macro can be contructed using normal runtime tools, |
|---|
| 128 | including subroutine recursion. |
|---|
| 129 | |
|---|
| 130 | =head2 Run Defined Macros Only |
|---|
| 131 | |
|---|
| 132 | Macros are only macros once |
|---|
| 133 | |
|---|
| 134 | |
|---|
| 135 | =head1 Quoting Declarations |
|---|
| 136 | |
|---|
| 137 | Can declarations and pragmas be quoted? |
|---|
| 138 | $Larry "I don't see why not" |
|---|
| 139 | |
|---|
| 140 | quasi { sub x { ... }; macro y { ... }; use force; } |
|---|
| 141 | |
|---|
| 142 | The AST needs to be rescanned anyway, so perform the appropriate |
|---|
| 143 | actions at that time in the correct scope. |
|---|
| 144 | |
|---|
| 145 | =end pod |
|---|
| 146 | # vim: syntax=perl6: |
|---|