Displaying articles with tag

Conditional Compilation of Ruby Code

Posted by rue, Tue Apr 01 23:31:00 UTC 2008

It was one of those silly ideas-nights and I decided to add the ability to have blocks of code that can be omitted from the bytecode depending on a condition as a proof-of-concept. Once the parser is in, we can start working on ‘real,’ more generic sexp and AST manipulators towards something approaching Lisp’s facilities.

I figured it would be a fairly short task and for a change I was correct&emdash;and a good portion of the thanks goes to Rubinius’ already-excellent facilities: I was able to modify the sexp with the full power of Ruby. It took me a bit to figure out the best way to set this up but in the end there are literally about 11 lines of implementation code plus the specs. The end result is a block construct for a special form that allows conditionalising. Currently I only set it up to process global variables but it is trivial to add support for the rest—what may not be trivial is for the user to get other types of conditions set up without confusion over compile-time versus runtime. The invocation is pretty simple:
1
2
3
4
5
6

  # ...

  Rubinius.compile_if($DEBUG) { Logger.debug "Blah blah: #{some_variable}" } 

  # ...
If $DEBUG is false at the time this section goes through the compiler, that entire line disappears from the first R to the last }. If it evaluates to true, the block contents are processed normally&emdash;but we still remove the now-extra wrapper block around it both to avoid the overhead as well as ensure that the variable scope etc. are consistent.

This should be useful for at least removing those generally-unnecessary debug statements etc.; the if $DEBUG does not cost much by itself but it adds up over time. The other easy target are platform/library/etc. specific sections that can be omitted in some cases. It is general-purpose in that the only limiting factor is the condition variable (or possibly expression in the future.)

The commit is here.

0 comments | Filed Under: rubinius | Tags:

Compiler Walkthrough Illustration

Posted by rue, Fri Nov 09 19:49:00 UTC 2007

Note: this is a sort of a draft. See the real document over in the repo.
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

# Start
[:block, 
  [:newline, 1, "(eval)", 
    [:defn, :foo, 
      [:scope, 
        [:block, 
          [:args, [:a, :b], [], nil, nil], 
          [:newline, 1, "(eval)", 
            [:yield, 
              [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
            ]
          ]
        ], 
        [:a, :b]
      ]
    ]
  ]
]

# into_script
[:script,  
 [:block, 
   [:newline, 1, "(eval)", 
    [:defn, :foo, 
      [:scope, 
        [:block, 
          [:args, [:a, :b], [], nil, nil], 
          [:newline, 1, "(eval)", 
            [:yield, 
              [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
            ]
          ]
        ], 
        [:a, :b]
      ]
    ]
   ]
  ]
]

# Script.create strips :script
[
 [:block, 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
]
# out = [<script node>]


# Script.consume
# convert sexp.first ->
# Block.create comp, sexp
 [:block, 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
# out = [<block node>]

# Block#consume sexp.shift
 [ 
   [:newline, 1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]
 ]
# out = [<newline node>]

# Compiler#convert_sexp first
# -> Newline.create
# Newline.new.consume sexp.shift
   [1, "(eval)", 
     [:defn, :foo, 
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
   ]

# << out = [1, "eval", <defn node>]
# compiler.convert <defn node>

# Define.create
# Define.new.consume sexp.shift
# name = sexp[0] (:foo), body = sexp[1..-1]
# scope = super(body)
# ClosedScope#consume body
     [
       [:scope, 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]
     ]
# out = []

# Scope.new.consume sexp.shift
# convert args.first, 
# out = [<converted>, locals]
       [ 
         [:block, 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]
         ], 
         [:a, :b]
       ]

# Block.new.consume sexp.shift
# :args => convert
# :newline => convert

         [ 
           [:args, [:a, :b], [], nil, nil], 
           [:newline, 1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
           ]


# out = [<args node>, <newline node>] 

# Arguments.new.consume sexp.shift
[[:a, :b], [], nil, nil]

# Newline.new.consume sexp.shift
            [1, "(eval)", 
             [:yield, 
               [:call, [:lvar, :a, 0], :+, [:array, [:lvar, :b, 0]]], false
             ]
            ] 

0 comments | Filed Under: rubinius | Tags: