Class: Opal::Compiler
Constant Summary
- INDENT =
Generated code gets indented with two spaces on each scope
' '
- COMPARE =
All compare method nodes - used to optimize performance of math comparisons
%w[< > <= >=]
Instance Attribute Summary collapse
-
#case_stmt ⇒ Object
readonly
Current case_stmt.
-
#eof_content ⇒ Object
readonly
Any content in END special construct.
-
#fragments ⇒ Object
readonly
Returns the value of attribute fragments.
-
#result ⇒ Object
readonly
Returns the value of attribute result.
-
#scope ⇒ Object
Current scope.
Class Method Summary collapse
-
.compiler_option(name, default_value, mid = nil) ⇒ Object
defines a compiler option, also creating method of form 'name?'.
Instance Method Summary collapse
-
#compile(source, options = {}) ⇒ Object
Compile some ruby code to a string.
-
#error(msg, line = nil) ⇒ Object
This is called when a parsing/processing error occurs.
- #fragment(str, sexp = nil) ⇒ Object
- #handle_block_given_call(sexp) ⇒ Object
- #handlers ⇒ Object
-
#helper(name) ⇒ Object
Use the given helper.
-
#helpers ⇒ Object
Any helpers required by this file.
- #in_case ⇒ Object
-
#in_while ⇒ Object
Used when we enter a while statement.
-
#in_while? ⇒ Boolean
Returns true if the parser is curently handling a while sexp, false otherwise.
-
#indent(&block) ⇒ Object
To keep code blocks nicely indented, this will yield a block after adding an extra layer of indent, and then returning the resulting code after reverting the indent.
-
#initialize ⇒ Compiler
constructor
A new instance of Compiler.
-
#method_calls ⇒ Object
Method calls made in this file.
-
#parser_indent ⇒ Object
Instances of
Scope
can use this to determine the current scope indent. -
#process(sexp, level = :expr) ⇒ Object
Process the given sexp by creating a node instance, based on its type, and compiling it to fragments.
-
#requires ⇒ Object
An array of requires used in this file.
-
#returns(sexp) ⇒ Object
The last sexps in method bodies, for example, need to be returned in the compiled javascript.
-
#s(*parts) ⇒ Object
Create a new sexp using the given parts.
- #source_map(source_file = nil) ⇒ Object
-
#unique_temp ⇒ Object
Used to generate a unique id name per file.
-
#warning(msg, line = nil) ⇒ Object
This is called when a parsing/processing warning occurs.
-
#with_temp(&block) ⇒ Object
Temporary varibales will be needed from time to time in the generated code, and this method will assign (or reuse) on while the block is yielding, and queue it back up once it is finished.
Constructor Details
#initialize ⇒ Compiler
Returns a new instance of Compiler
55 56 57 58 59 |
# File 'opal/lib/opal/compiler.rb', line 55 def initialize @indent = '' @unique = 0 @options = {} end |
Instance Attribute Details
#case_stmt ⇒ Object (readonly)
Current case_stmt
50 51 52 |
# File 'opal/lib/opal/compiler.rb', line 50 def case_stmt @case_stmt end |
#eof_content ⇒ Object (readonly)
Any content in END special construct
53 54 55 |
# File 'opal/lib/opal/compiler.rb', line 53 def eof_content @eof_content end |
#fragments ⇒ Object (readonly)
Returns the value of attribute fragments
44 45 46 |
# File 'opal/lib/opal/compiler.rb', line 44 def fragments @fragments end |
#result ⇒ Object (readonly)
Returns the value of attribute result
44 45 46 |
# File 'opal/lib/opal/compiler.rb', line 44 def result @result end |
#scope ⇒ Object
Current scope
47 48 49 |
# File 'opal/lib/opal/compiler.rb', line 47 def scope @scope end |
Class Method Details
.compiler_option(name, default_value, mid = nil) ⇒ Object
defines a compiler option, also creating method of form 'name?'
20 21 22 23 24 |
# File 'opal/lib/opal/compiler.rb', line 20 def self.compiler_option(name, default_value, mid = nil) define_method(mid || name) do @options.fetch(name) { default_value } end end |
Instance Method Details
#compile(source, options = {}) ⇒ Object
Compile some ruby code to a string.
62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'opal/lib/opal/compiler.rb', line 62 def compile(source, = {}) @source = source @options.update @parser = Parser.new @sexp = s(:top, @parser.parse(@source, self.file) || s(:nil)) @eof_content = @parser.lexer.eof_content @fragments = process(@sexp).flatten @result = @fragments.map(&:code).join('') end |
#error(msg, line = nil) ⇒ Object
This is called when a parsing/processing error occurs. This method simply appends the filename and curent line number onto the message and raises it.
92 93 94 |
# File 'opal/lib/opal/compiler.rb', line 92 def error(msg, line = nil) raise SyntaxError, "#{msg} :#{file}:#{line}" end |
#fragment(str, sexp = nil) ⇒ Object
117 118 119 |
# File 'opal/lib/opal/compiler.rb', line 117 def fragment(str, sexp = nil) Fragment.new(str, sexp) end |
#handle_block_given_call(sexp) ⇒ Object
277 278 279 280 281 282 283 284 285 286 |
# File 'opal/lib/opal/compiler.rb', line 277 def handle_block_given_call(sexp) @scope.uses_block! if @scope.block_name fragment("(#{@scope.block_name} !== nil)", sexp) elsif scope = @scope.find_parent_def and scope.block_name fragment("(#{scope.block_name} !== nil)", sexp) else fragment("false", sexp) end end |
#handlers ⇒ Object
192 193 194 |
# File 'opal/lib/opal/compiler.rb', line 192 def handlers @handlers ||= Opal::Nodes::Base.handlers end |
#helper(name) ⇒ Object
Use the given helper
128 129 130 |
# File 'opal/lib/opal/compiler.rb', line 128 def helper(name) self.helpers << name end |
#helpers ⇒ Object
Any helpers required by this file
80 81 82 |
# File 'opal/lib/opal/compiler.rb', line 80 def helpers @helpers ||= Set.new([:breaker, :slice]) end |
#in_case ⇒ Object
168 169 170 171 172 173 174 |
# File 'opal/lib/opal/compiler.rb', line 168 def in_case return unless block_given? old = @case_stmt @case_stmt = {} yield @case_stmt = old end |
#in_while ⇒ Object
Used when we enter a while statement. This pushes onto the current scope's while stack so we know how to handle break, next etc.
159 160 161 162 163 164 165 166 |
# File 'opal/lib/opal/compiler.rb', line 159 def in_while return unless block_given? @while_loop = @scope.push_while result = yield @scope.pop_while result end |
#in_while? ⇒ Boolean
Returns true if the parser is curently handling a while sexp, false otherwise.
178 179 180 |
# File 'opal/lib/opal/compiler.rb', line 178 def in_while? @scope.in_while? end |
#indent(&block) ⇒ Object
To keep code blocks nicely indented, this will yield a block after adding an extra layer of indent, and then returning the resulting code after reverting the indent.
135 136 137 138 139 140 141 142 143 |
# File 'opal/lib/opal/compiler.rb', line 135 def indent(&block) indent = @indent @indent += INDENT @space = "\n#@indent" res = yield @indent = indent @space = "\n#@indent" res end |
#method_calls ⇒ Object
Method calls made in this file
85 86 87 |
# File 'opal/lib/opal/compiler.rb', line 85 def method_calls @method_calls ||= Set.new end |
#parser_indent ⇒ Object
Instances of Scope
can use this to determine the current
scope indent. The indent is used to keep generated code easily
readable.
106 107 108 |
# File 'opal/lib/opal/compiler.rb', line 106 def parser_indent @indent end |
#process(sexp, level = :expr) ⇒ Object
Process the given sexp by creating a node instance, based on its type, and compiling it to fragments.
184 185 186 187 188 189 190 |
# File 'opal/lib/opal/compiler.rb', line 184 def process(sexp, level = :expr) if handler = handlers[sexp.type] return handler.new(sexp, level, self).compile_to_fragments else raise "Unsupported sexp: #{sexp.type}" end end |
#requires ⇒ Object
An array of requires used in this file
197 198 199 |
# File 'opal/lib/opal/compiler.rb', line 197 def requires @requires ||= [] end |
#returns(sexp) ⇒ Object
The last sexps in method bodies, for example, need to be returned
in the compiled javascript. Due to syntax differences between
javascript any ruby, some sexps need to be handled specially. For
example, if
statemented cannot be returned in javascript, so
instead the "truthy" and "falsy" parts of the if statement both
need to be returned instead.
Sexps that need to be returned are passed to this method, and the alterned/new sexps are returned and should be used instead. Most sexps can just be added into a s(:return) sexp, so that is the default action if no special case is required.
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 |
# File 'opal/lib/opal/compiler.rb', line 212 def returns(sexp) return returns s(:nil) unless sexp case sexp.type when :break, :next, :redo sexp when :yield sexp[0] = :returnable_yield sexp when :scope sexp[1] = returns sexp[1] sexp when :block if sexp.length > 1 sexp[-1] = returns sexp[-1] else sexp << returns(s(:nil)) end sexp when :when sexp[2] = returns(sexp[2]) sexp when :rescue sexp[1] = returns sexp[1] if sexp[2] and sexp[2][0] == :resbody if sexp[2][2] sexp[2][2] = returns sexp[2][2] else sexp[2][2] = returns s(:nil) end end sexp when :ensure sexp[1] = returns sexp[1] sexp when :begin sexp[1] = returns sexp[1] sexp when :rescue_mod sexp[1] = returns sexp[1] sexp[2] = returns sexp[2] sexp when :while # sexp[2] = returns(sexp[2]) sexp when :return, :js_return sexp when :xstr sexp[1] = "return #{sexp[1]};" unless /return|;/ =~ sexp[1] sexp when :dxstr sexp[1] = "return #{sexp[1]}" unless /return|;|\n/ =~ sexp[1] sexp when :if sexp[2] = returns(sexp[2] || s(:nil)) sexp[3] = returns(sexp[3] || s(:nil)) sexp else s(:js_return, sexp).tap { |s| s.source = sexp.source } end end |
#s(*parts) ⇒ Object
Create a new sexp using the given parts. Even though this just returns an array, it must be used incase the internal structure of sexps does change.
113 114 115 |
# File 'opal/lib/opal/compiler.rb', line 113 def s(*parts) Sexp.new(parts) end |
#source_map(source_file = nil) ⇒ Object
75 76 77 |
# File 'opal/lib/opal/compiler.rb', line 75 def source_map(source_file = nil) Opal::SourceMap.new(@fragments, source_file || self.file) end |
#unique_temp ⇒ Object
Used to generate a unique id name per file. These are used mainly to name method bodies for methods that use blocks.
123 124 125 |
# File 'opal/lib/opal/compiler.rb', line 123 def unique_temp "TMP_#{@unique += 1}" end |
#warning(msg, line = nil) ⇒ Object
This is called when a parsing/processing warning occurs. This method simply appends the filename and curent line number onto the message and issues a warning.
99 100 101 |
# File 'opal/lib/opal/compiler.rb', line 99 def warning(msg, line = nil) warn "#{msg} :#{file}:#{line}" end |
#with_temp(&block) ⇒ Object
Temporary varibales will be needed from time to time in the generated code, and this method will assign (or reuse) on while the block is yielding, and queue it back up once it is finished. Variables are queued once finished with to save the numbers of variables needed at runtime.
150 151 152 153 154 155 |
# File 'opal/lib/opal/compiler.rb', line 150 def with_temp(&block) tmp = @scope.new_temp res = yield tmp @scope.queue_temp tmp res end |