| 1 | kp6 ruby backend |
|---|
| 2 | |
|---|
| 3 | MISSION |
|---|
| 4 | |
|---|
| 5 | As of January 2008, kp6 is the most-advanced actively-developed |
|---|
| 6 | non-parrot perl6 implementation. It is written in a subset of |
|---|
| 7 | perl6, and runs on the more limited, but faster, mp6. |
|---|
| 8 | |
|---|
| 9 | kp6 is designed to be capable of running on itself. But the |
|---|
| 10 | performance of kp6 on kp6 is currently too slow to be usable. |
|---|
| 11 | By a couple of orders of magnitude. |
|---|
| 12 | |
|---|
| 13 | This performance obstacle has severely hampered using bootstrapped |
|---|
| 14 | kp6 as a development path towards the implementation of perl6. |
|---|
| 15 | |
|---|
| 16 | The objective of this kp6 ruby backend is to provide sufficient |
|---|
| 17 | performance for bootstrapped kp6 to be usable, thus unblocking |
|---|
| 18 | the development path. |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | DEVELOPMENT TIPS |
|---|
| 22 | |
|---|
| 23 | perl script/kp6 -Cruby -e 'say 3' # show the emitted ruby code |
|---|
| 24 | perl script/kp6 -Bruby -e 'say 3' # run the emitted ruby code |
|---|
| 25 | |
|---|
| 26 | perl script/kp6 -Cast -e 'say 3' # show the AST |
|---|
| 27 | perl script/kp6 -Cperl5 -e 'say 3' # show the emitted perl5 |
|---|
| 28 | |
|---|
| 29 | ./script/run_tests.pl --backend=ruby |
|---|
| 30 | |
|---|
| 31 | |
|---|
| 32 | If you change Emit/Ruby.pm, |
|---|
| 33 | # get svn file timestamps correct |
|---|
| 34 | rm -rf v6-KindaPerl6/compiled |
|---|
| 35 | svn up |
|---|
| 36 | # make |
|---|
| 37 | perl Makefile.PL |
|---|
| 38 | make kp6_mp6 |
|---|
| 39 | |
|---|
| 40 | make kp6_mp6 runs |
|---|
| 41 | perl script/mp6.pl \ |
|---|
| 42 | -o compiled/perl5-kp6-mp6/lib/KindaPerl6/Visitor/Emit/Ruby.pm \ |
|---|
| 43 | src/KindaPerl6/Visitor/Emit/Ruby.pm |
|---|
| 44 | |
|---|
| 45 | |
|---|
| 46 | Files: |
|---|
| 47 | docs/ruby_backend.txt |
|---|
| 48 | src/KindaPerl6/Visitor/Emit/Ruby.pm |
|---|
| 49 | compiled/ruby-kp6-mp6/kp6_runtime.rb |
|---|
| 50 | compiled/ruby-kp6-mp6 |
|---|
| 51 | src/KindaPerl6/Runtime/Ruby |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | SCOPE OF OBJECTIVES |
|---|
| 55 | |
|---|
| 56 | Performance. Obj: kp6 on kp6 should run with at least the speed of |
|---|
| 57 | kp6 on mp6, and ideally of pugs. Non-obj: performance on anything |
|---|
| 58 | other than kp6, eg, numerics. |
|---|
| 59 | |
|---|
| 60 | Ability to run kp6 without significant mofication. |
|---|
| 61 | |
|---|
| 62 | Simplicity. Obj: simplicity for reducing development cost and risk. |
|---|
| 63 | Non-obj: simplicity for elegance, long-term maintainability, or |
|---|
| 64 | anything else. |
|---|
| 65 | |
|---|
| 66 | Correctness. Just enough correctness to make kp6 happy, and support |
|---|
| 67 | it's near term further development. While stronger correctness will |
|---|
| 68 | likely exist, it's not a project objective. |
|---|
| 69 | Non-obj: Unneeded p6 spec-wise correctness, either short or long term. |
|---|
| 70 | |
|---|
| 71 | Readable emitted ruby code. Obj: easily readable for debugging. |
|---|
| 72 | Non-obj: easily hand writiable. No hairy ruby games. |
|---|
| 73 | |
|---|
| 74 | |
|---|
| 75 | DESIGN |
|---|
| 76 | |
|---|
| 77 | Calling convention: |
|---|
| 78 | ->(cap){ |
|---|
| 79 | ->(the,formal,arguments) { |
|---|
| 80 | ...inline argment processing... |
|---|
| 81 | ...body... |
|---|
| 82 | }.(nil,nil,nil) |
|---|
| 83 | } |
|---|
| 84 | cap is a Ruddy::Capture. |
|---|
| 85 | |
|---|
| 86 | Name encoding |
|---|
| 87 | base type - a prefix. $x -> s_x, @%& -> a_ h_ c_ |
|---|
| 88 | characters - follow kp6. |
|---|
| 89 | categories - follow kp6? |
|---|
| 90 | but kp6's including <> in the encoding of infix:<x> seems unfortunate. |
|---|
| 91 | |
|---|
| 92 | Variables |
|---|
| 93 | Lookup is done using normal ruby lookup on zero-ary accessors which |
|---|
| 94 | return p6 Containers. |
|---|
| 95 | |
|---|
| 96 | $x |
|---|
| 97 | $x = v |
|---|
| 98 | $x := v |
|---|
| 99 | s_x |
|---|
| 100 | s_x._(v) |
|---|
| 101 | s_x = v # for binding lexical variables |
|---|
| 102 | self.s_x = v # for binding package variables |
|---|
| 103 | |
|---|
| 104 | f(...) |
|---|
| 105 | c_f.(...) |
|---|
| 106 | |
|---|
| 107 | Lexical vars |
|---|
| 108 | ->(x1,x2){...}.(v1,v2) === (let ((x1 v1) (x2 v2)) ...) |
|---|
| 109 | The absence of a real lambda in ruby 1.8 is what forces us to use 1.9. |
|---|
| 110 | |
|---|
| 111 | Package vars |
|---|
| 112 | Done with class vars. |
|---|
| 113 | |
|---|
| 114 | our $.x |
|---|
| 115 | === |
|---|
| 116 | our :s_x |
|---|
| 117 | ==> |
|---|
| 118 | def s_x; @@x ||= ...; end where ... is the optional default, or undef. |
|---|
| 119 | def s_x=(v); @@x = v; end |
|---|
| 120 | |
|---|
| 121 | OO |
|---|
| 122 | p6 classes are similarly named ruby classes. |
|---|
| 123 | p6 modules and packages are ruby modules.(?) |
|---|
| 124 | methods: |
|---|
| 125 | |
|---|
| 126 | $o.m() |
|---|
| 127 | s_o.c_m.(...) |
|---|
| 128 | |
|---|
| 129 | current_class.def_pkg_var(:c_m, ...) |
|---|
| 130 | where ... is |
|---|
| 131 | ->(c){ |
|---|
| 132 | ->(s_self,...){ |
|---|
| 133 | ... |
|---|
| 134 | } |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | |
|---|
| 138 | |
|---|
| 139 | Examples: |
|---|
| 140 | my sub f(x) { 42 * x } |
|---|
| 141 | ==> |
|---|
| 142 | ->(c_f){ |
|---|
| 143 | c_f = |
|---|
| 144 | ->(cap){ |
|---|
| 145 | ->(s_x){ |
|---|
| 146 | cap.mumble(1) |
|---|
| 147 | s_x._(cap.frotz(0)) |
|---|
| 148 | 42 * s_x |
|---|
| 149 | }.(Scalar.new) |
|---|
| 150 | } |
|---|
| 151 | }.() |
|---|
| 152 | }.(nil) |
|---|
| 153 | |
|---|
| 154 | sub g() { 42 } |
|---|
| 155 | ==> |
|---|
| 156 | our :c_g, ->(c){ |
|---|
| 157 | ... |
|---|
| 158 | } |
|---|
| 159 | |
|---|
| 160 | |
|---|
| 161 | |
|---|
| 162 | DESIGN DISCUSSION |
|---|
| 163 | |
|---|
| 164 | A high-level alternate approach would be to more closely mimic the |
|---|
| 165 | perl5 backend. Just do a transliteration. Speedup would come from |
|---|
| 166 | running more or less directly on fast ruby oo, instead of the p5 |
|---|
| 167 | backend's running on an oo layer (Moose) sitting on top of slow p5 oo. |
|---|
| 168 | This approach would in some ways be simpler. And the performance gain |
|---|
| 169 | may well be sufficient to achieve a usably fast bootstrapped kp6. I |
|---|
| 170 | didn't take this path because it required greater understanding of the |
|---|
| 171 | perl5 backend, and the assorted libraries it uses. Which seemed a |
|---|
| 172 | higher cost and risk approach, for me, than reusing the architecture |
|---|
| 173 | from redsix. Someone else might pursue it, if the current approach |
|---|
| 174 | bogs down, of if keeping the ruby and p5 backends synced becomes |
|---|
| 175 | burdensome at some future time. |
|---|
| 176 | |
|---|
| 177 | Regards signature architecture. The p6 signature language is, even |
|---|
| 178 | more so than usual, a non-trivial language. Options: |
|---|
| 179 | (1) pass to the runtime, a p6 sig string '$x,$y', then parse in the |
|---|
| 180 | runtime. Even if the parse/emit was used to cannonicallize the form, |
|---|
| 181 | there's still such richness as would be a pain to duplicate. |
|---|
| 182 | (2) pass to the runtime, an ast instead. |
|---|
| 183 | (2a) an ast composed of runtime objects, which are not p6 objects. |
|---|
| 184 | (2b) an ast composed of p6 objects. Facilitates p6 reflection. |
|---|
| 185 | (3) at compile-time, generate inline code for parameter processing. |
|---|
| 186 | Currently the backend is using (3), but there is unfinished code for (2a). |
|---|
| 187 | |
|---|
| 188 | Here's the unused (2a) convention: |
|---|
| 189 | sig = Ruddy::Signature.new(...) |
|---|
| 190 | ->(cap){ |
|---|
| 191 | ->(the,formal,arguments) { |
|---|
| 192 | sig.bind(cap,binding) |
|---|
| 193 | ...body... |
|---|
| 194 | }.(nil,nil,nil) |
|---|
| 195 | } |
|---|
| 196 | cap may be an array (positional args only), a hash (named args only), |
|---|
| 197 | or a Ruddy::Capture. |
|---|
| 198 | |
|---|
| 199 | my sub f(x) { 42 * x } |
|---|
| 200 | ==> |
|---|
| 201 | ->(c_f){ |
|---|
| 202 | c_f = ->(){ |
|---|
| 203 | sig = Ruddy::Signature.new(...) |
|---|
| 204 | ->(cap){ |
|---|
| 205 | ->(s_x){ |
|---|
| 206 | sig.bind(cap,binding) |
|---|
| 207 | 42 * s_x |
|---|
| 208 | }.(nil) |
|---|
| 209 | } |
|---|
| 210 | }.() |
|---|
| 211 | }.(nil) |
|---|
| 212 | |
|---|
| 213 | |
|---|
| 214 | |
|---|
| 215 | |
|---|
| 216 | Note to self: don't forget Method .receiver and .owner. :) |
|---|
| 217 | |
|---|
| 218 | |
|---|
| 219 | TODO: |
|---|
| 220 | - continue to work through failing tests |
|---|
| 221 | - try compilation of kp6 p6 files |
|---|
| 222 | |
|---|
| 223 | - start creating mp6_runtime.rb. |
|---|
| 224 | |
|---|
| 225 | - flesh out todos |
|---|
| 226 | |
|---|
| 227 | OLD FUZZY TODO LIST: |
|---|
| 228 | - walk though Emit/Ruby.pm, deciding, creating, and documenting runtime apis. |
|---|
| 229 | - do Runtime/Ruby. Should be much simpler and smaller than Runtime/Perl5/. |
|---|
| 230 | |
|---|
| 231 | LATER: |
|---|
| 232 | - create script/kp6.pl? a p6 version of the p5 script/kp6. |
|---|
| 233 | - rename ruby backend to ruddy (after acceptance). |
|---|