opal 0.3.9 → 0.3.10

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -20,8 +20,9 @@ browsers - including older versions of IE and mobile devices.
20
20
  Using opal
21
21
  ----------
22
22
 
23
- Opal can currently be used in two ways: through a distributed ruby gem,
24
- or directly in the web browser.
23
+ Opal can currently be used in three ways: through a distributed ruby gem,
24
+ directly in the web browser or with rbp - a new package manager designed
25
+ for opal but usable for any ruby project.
25
26
 
26
27
  ### Using the gem
27
28
 
@@ -46,6 +47,28 @@ $ cd opal
46
47
  $ bin/opal
47
48
  ```
48
49
 
50
+ ### Using rbp
51
+
52
+ rbp installs dependencies locally to your project, so edit your
53
+ package.yml file to add the following dependency:
54
+
55
+ ```yaml
56
+ dev_dependencies:
57
+ opal: "0.3.9"
58
+ therubyracer: "0.9.4"
59
+ ```
60
+
61
+ Install them with:
62
+
63
+ ```
64
+ $ rbp install
65
+ ```
66
+
67
+ This will install them into vendor/ ready to use. therubyracer is only
68
+ needed if you want to run your ruby code, compiled into javascript,
69
+ directly on the commandline. If you are just compiling ruby then just
70
+ the opal package is sufficient.
71
+
49
72
  Running tests
50
73
  -------------
51
74
 
@@ -54,14 +77,14 @@ based on minitest. To get opaltest, run the following in the opal
54
77
  directory:
55
78
 
56
79
  ```
57
- $ rake vendor
80
+ $ rbp install
58
81
  ```
59
82
 
60
- This will put opaltest into `vendor/opaltest` so it will be available
83
+ This will put opaltest into `packages/opaltest` so it will be available
61
84
  for running. To test `array.rb` for example, run:
62
85
 
63
86
  ```
64
- $ bin/opal test/array.rb
87
+ $ rbp exec opal test/array.rb
65
88
  ```
66
89
 
67
90
  The results should be printed to the console.
data/bin/opal CHANGED
@@ -1,8 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- opal_lib = File.expand_path('../../lib', __FILE__)
4
- $:.unshift opal_lib unless $:.include? opal_lib
5
-
6
3
  require 'opal'
7
4
  require 'opal/command'
8
5
 
data/lib/opal.rb CHANGED
@@ -1,7 +1,12 @@
1
- require 'rbp'
2
-
3
1
  require 'opal/parser'
4
2
  require 'opal/builder'
5
3
  require 'opal/context'
6
- require 'opal/browserify'
4
+
5
+ # Opal is a set of build tools and runtime utilies for compiling ruby
6
+ # source code into javascript. Opal can use therubyracer to provide a
7
+ # ruby context for evaluating the generated javascript against the
8
+ # provided runtime.
9
+ module Opal
10
+ OPAL_DIR = File.expand_path('../..', __FILE__)
11
+ end
7
12
 
data/lib/opal/builder.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'fileutils'
2
+ require 'opal/parser'
2
3
 
3
4
  module Opal
4
5
 
@@ -15,6 +16,14 @@ module Opal
15
16
 
16
17
  CORE_PATH = File.join OPAL_PATH, 'corelib'
17
18
 
19
+ def initialize
20
+ @parser = Parser.new
21
+ end
22
+
23
+ def parse(source, options = {})
24
+ @parser.parse source, options
25
+ end
26
+
18
27
  # Returns the result of the compiled file ready for opal to load.
19
28
  #
20
29
  # `relative_path` is used for the name the built file should have.
@@ -76,7 +85,7 @@ module Opal
76
85
  File.read File.join(CORE_PATH, o + '.rb')
77
86
  end
78
87
 
79
- code += 'var core_lib = function($rb, self, __FILE__) { ' + Opal::Parser.new(core.join).parse!.generate_top + '};'
88
+ code += "var core_lib = #{parse core.join};"
80
89
 
81
90
  code + File.read(File.join RUNTIME_PATH, 'post.js')
82
91
  end
@@ -0,0 +1,48 @@
1
+ require 'opal/builder'
2
+
3
+ begin
4
+ require 'rbp/package'
5
+ rescue LoadError
6
+ abort "You need to install rbp. `gem install rbp`."
7
+ end
8
+
9
+ module Opal
10
+ # Takes a package and builds it ready for the browser
11
+ class Bundle
12
+ # @return [Rbp::Package] the package this is bundling
13
+ attr_reader :package
14
+
15
+ attr_accessor :options
16
+
17
+ def initialize(package)
18
+ @package = package
19
+ @builder = Builder.new
20
+ @options = {}
21
+ end
22
+
23
+ # Simple build - returns a string which can be written to a file
24
+ # FIXME: hardcoded lib directory to './lib'
25
+ def build
26
+ package_dir = @package.package_dir
27
+
28
+ lib_files = @package.relative_lib_files.map do |lib|
29
+ path = File.join package_dir, lib
30
+ code = @builder.parse File.read(path), options
31
+
32
+ "\"#{lib}\": #{code}"
33
+ end
34
+
35
+ bundle = []
36
+ bundle << %[opal.package({\n]
37
+ bundle << %[ name: "#{@package.name}",\n]
38
+ bundle << %[ version: "#{@package.version}",\n]
39
+ bundle << %[ libs: {\n]
40
+ bundle << %[ #{lib_files.join ",\n "}\n]
41
+ bundle << %[ }\n]
42
+ bundle << %[});\n]
43
+
44
+ bundle.join ''
45
+ end
46
+ end
47
+ end
48
+
data/lib/opal/command.rb CHANGED
@@ -3,7 +3,7 @@ module Opal
3
3
  class Command
4
4
 
5
5
  # Valid command line arguments
6
- COMMANDS = [:help, :irb, :compile, :bundle, :exec, :eval]
6
+ COMMANDS = [:help, :irb, :compile, :bundle, :exec, :eval, :install]
7
7
 
8
8
  def initialize(args)
9
9
  command = args.shift
@@ -40,12 +40,24 @@ module Opal
40
40
  puts Opal::Parser.new(File.read(path)).parse!.generate_top
41
41
  end
42
42
 
43
- # desc "bundle", "Bundle the gem in the given directory ready for browser"
44
- # method_options :out => :string
43
+ def install
44
+ install = RBP::Install.new
45
+ install.install
46
+ end
47
+
48
+ # Bundle the gem (browserify) ready for the browser
45
49
  def bundle
46
- opts = options
47
- bundle = Opal::Bundle.new(Opal::Gem.new(Dir.getwd))
48
- bundle.build opts
50
+ # lazy load incase user does not have rbp installed
51
+ require 'opal/bundle'
52
+
53
+ path = File.join Dir.getwd, 'package.yml'
54
+ package = Rbp::Package.load_path path
55
+ bundle = Bundle.new package
56
+
57
+ puts bundle
58
+ puts bundle.package
59
+
60
+ puts bundle.build
49
61
  end
50
62
  end
51
63
  end
data/lib/opal/context.rb CHANGED
@@ -4,64 +4,56 @@ module Opal
4
4
  def initialize(root_dir = Dir.getwd)
5
5
  @root_dir = root_dir
6
6
  @builder = Opal::Builder.new
7
+ @loaded_paths = false
8
+
9
+ # special case: if we are running in opal root, then we dont want
10
+ # setup.rb to load the opal lib itself, so we do some "magic"
11
+ if @root_dir == OPAL_DIR
12
+ def self.setup_load_paths
13
+ return if @loaded_paths
14
+ Dir['packages/*/package.yml'].map do |package|
15
+ path = File.expand_path File.join(File.dirname(package), 'lib')
16
+ @v8.eval "opal.loader.paths.push('#{path}')"
17
+ end
18
+ end
19
+ end
7
20
 
8
- @load_paths = resolve_load_paths
21
+ setup_v8
9
22
  end
10
23
 
24
+ ##
11
25
  # Looks through vendor/ directory and adds all relevant load paths
12
- def resolve_load_paths
13
- Dir['vendor/*/package.yml'].map do |package|
14
- File.expand_path File.join(File.dirname(package), 'lib')
15
- end
16
- end
17
-
18
- # Setup the context. This basically loads opal.js into our context, and
19
- # replace the loader etc with our custom loader for a Ruby environment. The
20
- # default "browser" loader cannot access files from disk.
21
- def setup_v8
22
- return if @v8
23
-
24
- begin
25
- require 'v8'
26
- rescue LoadError => e
27
- abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`"
28
- end
29
-
30
- @v8 = V8::Context.new
31
- @v8['console'] = Console.new
32
-
33
- eval @builder.build_core, '(opal)'
34
- opal = @v8['opal']
35
- opal['fs'] = FileSystem.new self
36
26
 
37
- # FIXME: we cant use a ruby array as a js array :(
38
- opal['loader'] = Loader.new self, eval("[]")
27
+ def setup_load_paths
28
+ return if @loaded_paths
39
29
 
40
- @load_paths.each do |path|
41
- eval "opal.loader.paths.push('#{path}')"
42
- end
30
+ setup = File.join @root_dir, 'packages', 'init.rb'
31
+ return [] unless File.exists? setup
43
32
 
33
+ @v8.eval "opal.run(function() {opal.require('#{setup}');});", setup
44
34
  end
45
35
 
46
- def eval(code, file = nil)
47
- @v8.eval code, file
48
- end
49
-
36
+ ##
50
37
  # Require the given id as if it was required in the context. This simply
51
38
  # passes the require through to the underlying context.
39
+
52
40
  def require_file(path)
53
41
  setup_v8
54
- eval "opal.run(function() {opal.require('#{path}');});", path
42
+ @v8.eval "opal.run(function() {opal.require('#{path}');});", path
55
43
  finish
56
44
  end
57
45
 
46
+ ##
58
47
  # Set ARGV for the context
48
+
59
49
  def argv=(args)
60
50
  puts "setting argv to #{args.inspect}"
61
- eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});"
51
+ @v8.eval "opal.runtime.cs(opal.runtime.Object, 'ARGV', #{args.inspect});"
62
52
  end
63
53
 
54
+ ##
64
55
  # Start normal js repl
56
+
65
57
  def start_repl
66
58
  require 'readline'
67
59
  setup_v8
@@ -76,38 +68,68 @@ module Opal
76
68
  break
77
69
  end
78
70
 
79
- puts "=> #{eval_ruby line, '(opal)'}"
71
+ puts "=> #{eval line, '(opal)'}"
80
72
  end
81
73
 
82
74
  finish
83
75
  end
84
76
 
85
- def eval_ruby(content, line = "")
77
+ def eval(content, file = nil, line = "")
86
78
  begin
87
- code = Opal::Parser.new(content).parse!.generate_top
88
- code = "opal.run(function() {var $rb = opal.runtime, self = $rb.top, __FILE__ = '(opal)';" + code + "});"
79
+ js = @builder.parse content
80
+ code = "opal.run(function() { var $rb = opal.runtime, self = $rb.top"
81
+ code += ", __FILE__ = '(opal)'; return (#{js})($rb, self, __FILE__); })"
89
82
  # puts code
90
- @v8['$opal_irb_result'] = eval code, line
91
- eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'"
83
+ @v8['$opal_irb_result'] = @v8.eval(code, file)
84
+ @v8.eval "!($opal_irb_result == null || !$opal_irb_result.m$inspect) ? $opal_irb_result.m$inspect() : '(Object does not support #inspect)'"
92
85
  rescue => e
93
86
  puts e
94
87
  puts("\t" + e.backtrace.join("\n\t"))
95
88
  end
96
89
  end
97
90
 
91
+ ##
98
92
  # Finishes the context, i.e. tidy everything up. This will cause
99
93
  # the opal runtime to do it's at_exit() calls (if applicable) and
100
94
  # then the v8 context will de removed. It can be reset by calling
101
95
  # #setup_v8
96
+
102
97
  def finish
103
98
  return unless @v8
104
- eval "opal.runtime.do_at_exit()", "(opal)"
99
+ @v8.eval "opal.runtime.do_at_exit()", "(opal)"
105
100
 
106
101
  @v8 = nil
107
102
  end
108
103
 
104
+ # Setup the context. This basically loads opal.js into our context, and
105
+ # replace the loader etc with our custom loader for a Ruby environment. The
106
+ # default "browser" loader cannot access files from disk.
107
+ def setup_v8
108
+ return if @v8
109
+
110
+ begin
111
+ require 'v8'
112
+ rescue LoadError => e
113
+ abort "therubyracer is required for running javascript. Install it with `gem install therubyracer`"
114
+ end
115
+
116
+ @v8 = V8::Context.new
117
+ @v8['console'] = Console.new
118
+
119
+ @v8.eval @builder.build_core, '(opal)'
120
+ opal = @v8['opal']
121
+ opal['fs'] = FileSystem.new self
122
+
123
+ # FIXME: we cant use a ruby array as a js array :(
124
+ opal['loader'] = Loader.new self, @v8.eval("[]")
125
+
126
+ setup_load_paths
127
+ end
128
+
129
+ ##
109
130
  # Console class is used to mimic the console object in web browsers
110
131
  # to allow simple debugging to the stdout.
132
+
111
133
  class Console
112
134
  def log(*str)
113
135
  puts str.join("\n")
@@ -115,9 +137,11 @@ module Opal
115
137
  end
116
138
  end
117
139
 
140
+ ##
118
141
  # FileSystem is used to interact with the file system from the ruby
119
142
  # version of opal. The methods on this class replace the default ones
120
143
  # made available in the web browser.
144
+
121
145
  class FileSystem
122
146
 
123
147
  def initialize(context)
@@ -182,7 +206,7 @@ module Opal
182
206
  end
183
207
 
184
208
  def ruby_file_contents(filename)
185
- Opal::Parser.new(File.read(filename)).parse!.generate_top
209
+ Opal::Parser.new.parse File.read(filename)
186
210
  end
187
211
 
188
212
  def wrap(content, filename)
data/lib/opal/lexer.rb CHANGED
@@ -1,863 +1,883 @@
1
-
2
1
  require 'opal/parser'
3
2
  require 'opal/nodes'
4
3
 
5
-
6
4
  require 'strscan'
7
5
 
8
6
  module Opal
7
+ # This class is used for parsing ruby content and then generating
8
+ # javascript content.
9
+ #
10
+ # Usage:
11
+ #
12
+ # parser = Opal::Parser.new
13
+ #
14
+ # parser.parse "self.do_something 1, 2, 3"
15
+ # # => "some ruby content"
9
16
  class Parser < Racc::Parser
10
17
 
11
- class RubyLexingError < StandardError
12
-
13
- end
14
-
15
- def initialize(source, options = {})
16
- @lex_state = :expr_beg
18
+ # Thrown on parsing error
19
+ class RubyLexingError < StandardError; end
20
+
21
+ # Parse the given ruby code in `source` and returns a string of
22
+ # compiled javascript. `options` may be passed as a hash, and
23
+ # include a set of various lexing and generator options.
24
+ #
25
+ # All options:
26
+ #
27
+ # `:method_missing` - dictates if the generated javascript has
28
+ # support for rubys method_missing. Defaults to `false`. It
29
+ # is recomended only to use method_missing in debug mode as
30
+ # it adds overhead to **every** method call.
31
+ #
32
+ # `:main_scope` - whether the code is to run in the main scope.
33
+ # `true` means that all local variables will be available
34
+ # in future bindings. `false` means its just a normal scope,
35
+ # i.e. top. This is only needed in [Context] for irb.
36
+ #
37
+ # @param [String] source ruby source code to parse
38
+ # @param [Hash] options parsing options to use
39
+ def parse(source, options = {})
40
+ @lex_state = :expr_beg
41
+ @cond = 0
42
+ @cmdarg = 0
43
+ @line_number = 1
44
+ @string_parse_stack = []
45
+ @scanner = StringScanner.new source
46
+ nodes = do_parse
47
+
48
+ return nodes.generate_top
49
+ end
17
50
 
18
- @cond = 0
19
- @cmdarg = 0
20
- @line_number = 1
51
+ def next_token
52
+ t = get_next_token
53
+ t[1] = { :value => t[1], :line => @line_number }
54
+ t
55
+ end
21
56
 
22
- @string_parse_stack = []
57
+ def cond_push(n)
58
+ @cond = (@cond << 1) | (n & 1)
59
+ end
23
60
 
24
- @scanner = StringScanner.new source
25
- end
61
+ def cond_pop
62
+ @cond = @cond >> 1
63
+ end
26
64
 
27
- def parse!
28
- do_parse
29
- end
65
+ def cond_lexpop
66
+ @cond = (@cond >> 1) | (@cond & 1)
67
+ end
30
68
 
31
- def next_token
32
- t = get_next_token
33
- t[1] = { :value => t[1], :line => @line_number }
34
- t
35
- end
69
+ def cond?
70
+ (@cond & 1) != 0
71
+ end
36
72
 
37
- def cond_push(n)
38
- @cond = (@cond << 1) | (n & 1)
39
- end
73
+ def cmdarg_push(n)
74
+ @cmdarg = (@cmdarg << 1) | (n & 1)
75
+ end
40
76
 
41
- def cond_pop
42
- @cond = @cond >> 1
43
- end
77
+ def cmdarg_pop
78
+ @cmdarg = @cmdarg >> 1
79
+ end
44
80
 
45
- def cond_lexpop
46
- @cond = (@cond >> 1) | (@cond & 1)
47
- end
81
+ def cmdarg_lexpop
82
+ @cmdarg = (@cmdarg >> 1) | (@cmdarg & 1)
83
+ end
48
84
 
49
- def cond?
50
- (@cond & 1) != 0
51
- end
85
+ def cmdarg?
86
+ (@cmdarg & 1) != 0
87
+ end
52
88
 
53
- def cmdarg_push(n)
54
- @cmdarg = (@cmdarg << 1) | (n & 1)
55
- end
89
+ def push_string_parse(hash)
90
+ @string_parse_stack << hash
91
+ end
56
92
 
57
- def cmdarg_pop
58
- @cmdarg = @cmdarg >> 1
59
- end
93
+ def pop_string_parse
94
+ @string_parse_stack.pop
95
+ end
60
96
 
61
- def cmdarg_lexpop
62
- @cmdarg = (@cmdarg >> 1) | (@cmdarg & 1)
63
- end
97
+ def current_string_parse
98
+ @string_parse_stack.last
99
+ end
64
100
 
65
- def cmdarg?
66
- (@cmdarg & 1) != 0
67
- end
101
+ def next_string_token
102
+ # str_parse, scanner = current_string_parse, @scanner
103
+ str_parse = current_string_parse
104
+ scanner = @scanner
105
+ space = false
68
106
 
69
- def push_string_parse(hash)
70
- @string_parse_stack << hash
71
- end
107
+ # everything bar single quote and lower case bare wrds can interpolate
108
+ interpolate = (str_parse[:beg] != "'" && str_parse[:beg] != 'w')
72
109
 
73
- def pop_string_parse
74
- @string_parse_stack.pop
75
- end
110
+ words = ['w', 'W'].include? str_parse[:beg]
76
111
 
77
- def current_string_parse
78
- @string_parse_stack.last
79
- end
112
+ space = true if ['w', 'W'].include?(str_parse[:beg]) and scanner.scan(/\s+/)
80
113
 
81
- def next_string_token
82
- # str_parse, scanner = current_string_parse, @scanner
83
- str_parse = current_string_parse
84
- scanner = @scanner
85
- space = false
114
+ # see if we can read end of string/xstring/regecp markers
115
+ # if scanner.scan /#{str_parse[:end]}/
116
+ if scanner.scan Regexp.new(Regexp.escape(str_parse[:end]))
117
+ if words && !str_parse[:done_last_space]#&& space
118
+ str_parse[:done_last_space] = true
119
+ scanner.pos -= 1
120
+ return :SPACE, ' '
121
+ end
122
+ pop_string_parse
86
123
 
87
- # everything bar single quote and lower case bare wrds can interpolate
88
- interpolate = (str_parse[:beg] != "'" && str_parse[:beg] != 'w')
124
+ # return :SPACE, ' ' if words && space
125
+
126
+ if ['"', "'"].include? str_parse[:beg]
127
+ @lex_state = :expr_end
128
+ return :STRING_END, scanner.matched
89
129
 
90
- words = ['w', 'W'].include? str_parse[:beg]
130
+ elsif str_parse[:beg] == '`'
131
+ @lex_state = :expr_end
132
+ return :STRING_END, scanner.matched
91
133
 
92
- space = true if ['w', 'W'].include?(str_parse[:beg]) and scanner.scan(/\s+/)
134
+ elsif str_parse[:beg] == '/'
135
+ result = scanner.matched if scanner.scan(/\w+/)
136
+ @lex_state = :expr_end
137
+ return :REGEXP_END, result
93
138
 
94
- # see if we can read end of string/xstring/regecp markers
95
- # if scanner.scan /#{str_parse[:end]}/
96
- if scanner.scan Regexp.new(Regexp.escape(str_parse[:end]))
97
- if words && !str_parse[:done_last_space]#&& space
98
- str_parse[:done_last_space] = true
99
- scanner.pos -= 1
100
- return :SPACE, ' '
101
- end
102
- pop_string_parse
103
-
104
- # return :SPACE, ' ' if words && space
105
-
106
- if ['"', "'"].include? str_parse[:beg]
107
- @lex_state = :expr_end
108
- return :STRING_END, scanner.matched
109
-
110
- elsif str_parse[:beg] == '`'
111
- @lex_state = :expr_end
112
- return :STRING_END, scanner.matched
113
-
114
- elsif str_parse[:beg] == '/'
115
- result = scanner.matched if scanner.scan(/\w+/)
116
- @lex_state = :expr_end
117
- return :REGEXP_END, result
118
-
119
- else
120
- @lex_state = :expr_end
121
- return :STRING_END, scanner.matched
139
+ else
140
+ @lex_state = :expr_end
141
+ return :STRING_END, scanner.matched
142
+ end
122
143
  end
123
- end
124
144
 
125
- return :SPACE, ' ' if space
145
+ return :SPACE, ' ' if space
126
146
 
127
- # not end of string, so we must be parsing contents
128
- str_buffer = []
147
+ # not end of string, so we must be parsing contents
148
+ str_buffer = []
129
149
 
130
- if scanner.scan(/#(\$\@)\w+/)
131
- if interpolate
132
- return :STRING_DVAR, scanner.matched.slice(2)
133
- else
134
- str_buffer << scanner.matched
135
- end
150
+ if scanner.scan(/#(\$\@)\w+/)
151
+ if interpolate
152
+ return :STRING_DVAR, scanner.matched.slice(2)
153
+ else
154
+ str_buffer << scanner.matched
155
+ end
136
156
 
137
- elsif scanner.scan(/#\{/)
138
- if interpolate
139
- # we are into ruby code, so stop parsing content (for now)
140
- str_parse[:content] = false
141
- return :STRING_DBEG, scanner.matched
142
- else
143
- str_buffer << scanner.matched
157
+ elsif scanner.scan(/#\{/)
158
+ if interpolate
159
+ # we are into ruby code, so stop parsing content (for now)
160
+ str_parse[:content] = false
161
+ return :STRING_DBEG, scanner.matched
162
+ else
163
+ str_buffer << scanner.matched
164
+ end
165
+
166
+ # causes error, so we will just collect it later on with other text
167
+ elsif scanner.scan(/\#/)
168
+ str_buffer << '#'
144
169
  end
145
170
 
146
- # causes error, so we will just collect it later on with other text
147
- elsif scanner.scan(/\#/)
148
- str_buffer << '#'
171
+ add_string_content str_buffer, str_parse
172
+ complete_str = str_buffer.join ''
173
+ return :STRING_CONTENT, complete_str
149
174
  end
150
175
 
151
- add_string_content str_buffer, str_parse
152
- complete_str = str_buffer.join ''
153
- return :STRING_CONTENT, complete_str
154
- end
176
+ def add_string_content(str_buffer, str_parse)
177
+ scanner = @scanner
178
+ # regexp for end of string/regexp
179
+ # end_str_re = /#{str_parse[:end]}/
180
+ end_str_re = Regexp.new(Regexp.escape(str_parse[:end]))
181
+ # can be interpolate
182
+ interpolate = ['"', 'W', '/', '`'].include? str_parse[:beg]
155
183
 
156
- def add_string_content(str_buffer, str_parse)
157
- scanner = @scanner
158
- # regexp for end of string/regexp
159
- # end_str_re = /#{str_parse[:end]}/
160
- end_str_re = Regexp.new(Regexp.escape(str_parse[:end]))
161
- # can be interpolate
162
- interpolate = ['"', 'W', '/', '`'].include? str_parse[:beg]
184
+ words = ['W', 'w'].include? str_parse[:beg]
163
185
 
164
- words = ['W', 'w'].include? str_parse[:beg]
186
+ until scanner.eos?
187
+ c = nil
188
+ handled = true
165
189
 
166
- until scanner.eos?
167
- c = nil
168
- handled = true
190
+ if scanner.check end_str_re
191
+ # eos
192
+ break
169
193
 
170
- if scanner.check end_str_re
171
- # eos
172
- break
194
+ elsif words && scanner.scan(/\s/)
195
+ scanner.pos -= 1
196
+ break
173
197
 
174
- elsif words && scanner.scan(/\s/)
175
- scanner.pos -= 1
176
- break
198
+ elsif interpolate && scanner.check(/#(?=[\@\{])/)
199
+ break
177
200
 
178
- elsif interpolate && scanner.check(/#(?=[\@\{])/)
179
- break
201
+ elsif scanner.scan(/\\\\/)
202
+ c = scanner.matched
180
203
 
181
- elsif scanner.scan(/\\\\/)
182
- c = scanner.matched
204
+ elsif scanner.scan(/\\/)
205
+ c = scanner.matched
206
+ c += scanner.matched if scanner.scan end_str_re
183
207
 
184
- elsif scanner.scan(/\\/)
185
- c = scanner.matched
186
- c += scanner.matched if scanner.scan end_str_re
208
+ else
209
+ handled = false
210
+ end
187
211
 
188
- else
189
- handled = false
190
- end
212
+ unless handled
213
+ reg = words ? Regexp.new("[^#{Regexp.escape str_parse[:end]}\#\0\n\ \\\\]+|.") : Regexp.new("[^#{Regexp.escape str_parse[:end]}\#\0\\\\]+|.")
214
+ scanner.scan reg
215
+ c = scanner.matched
216
+ end
191
217
 
192
- unless handled
193
- reg = words ? Regexp.new("[^#{Regexp.escape str_parse[:end]}\#\0\n\ \\\\]+|.") : Regexp.new("[^#{Regexp.escape str_parse[:end]}\#\0\\\\]+|.")
194
- scanner.scan reg
195
- c = scanner.matched
218
+ c ||= scanner.matched
219
+ str_buffer << c
196
220
  end
197
221
 
198
- c ||= scanner.matched
199
- str_buffer << c
222
+ raise "reached EOF while in string" if scanner.eos?
200
223
  end
201
224
 
202
- raise "reached EOF while in string" if scanner.eos?
203
- end
225
+ def get_next_token
226
+ string_scanner = current_string_parse
204
227
 
205
- def get_next_token
206
- string_scanner = current_string_parse
228
+ if string_scanner && string_scanner[:content]
229
+ return next_string_token
230
+ end
207
231
 
208
- if string_scanner && string_scanner[:content]
209
- return next_string_token
210
- end
232
+ # scanner, space_seen, cmd_start, c = @scanner, false, false, ''
233
+ scanner = @scanner
234
+ space_seen = false
235
+ cmd_start = false
236
+ c = ''
211
237
 
212
- # scanner, space_seen, cmd_start, c = @scanner, false, false, ''
213
- scanner = @scanner
214
- space_seen = false
215
- cmd_start = false
216
- c = ''
217
-
218
- while true
219
- if scanner.scan(/\ |\t|\r/)
220
- space_seen = true
221
- next
222
-
223
- elsif scanner.scan(/(\n|#)/)
224
- c = scanner.matched
225
- if c == '#' then scanner.scan(/(.*)/) else @line_number += 1; end
226
-
227
- scanner.scan(/(\n+)/)
228
- @line_number += scanner.matched.length if scanner.matched
229
-
230
- next if [:expr_beg, :expr_dot].include? @lex_state
231
-
232
- cmd_start = true
233
- @lex_state = :expr_beg
234
- return '\\n', '\\n'
235
-
236
- elsif scanner.scan(/\;/)
237
- @lex_state = :expr_beg
238
- return ';', ';'
239
-
240
- elsif scanner.scan(/\"/)
241
- push_string_parse :beg => '"', :content => true, :end => '"'
242
- return :STRING_BEG, scanner.matched
243
-
244
- elsif scanner.scan(/\'/)
245
- push_string_parse :beg => "'", :content => true, :end => "'"
246
- return :STRING_BEG, scanner.matched
247
-
248
- elsif scanner.scan(/\`/)
249
- push_string_parse :beg => "`", :content => true, :end => "`"
250
- return :XSTRING_BEG, scanner.matched
251
-
252
- elsif scanner.scan(/\%W/)
253
- start_word = scanner.scan(/./)
254
- end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
255
- push_string_parse :beg => 'W', :content => true, :end => end_word
256
- return :WORDS_BEG, scanner.matched
257
-
258
- elsif scanner.scan(/\%w/)
259
- start_word = scanner.scan(/./)
260
- end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
261
- push_string_parse :beg => 'w', :content => true, :end => end_word
262
- return :AWORDS_BEG, scanner.matched
263
-
264
- elsif scanner.scan(/\%[Qq]/)
265
- start_word = scanner.scan(/./)
266
- end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
267
- push_string_parse :beg => start_word, :content => true, :end => end_word
268
- return :STRING_BEG, scanner.matched
269
-
270
- elsif scanner.scan(/\//)
271
- if [:expr_beg, :expr_mid].include? @lex_state
272
- push_string_parse :beg => '/', :content => true, :end => '/'
273
- return :REGEXP_BEG, scanner.matched
274
- elsif scanner.scan(/\=/)
275
- @lex_state = :expr_beg
276
- return :OP_ASGN, '/'
277
- elsif @lex_state == :expr_fname
278
- @lex_state = :expr_end
279
- end
238
+ while true
239
+ if scanner.scan(/\ |\t|\r/)
240
+ space_seen = true
241
+ next
280
242
 
281
- return '/', '/'
243
+ elsif scanner.scan(/(\n|#)/)
244
+ c = scanner.matched
245
+ if c == '#' then scanner.scan(/(.*)/) else @line_number += 1; end
282
246
 
283
- elsif scanner.scan(/\%/)
284
- @lex_state = @lex_state == :expr_fname ? :expr_end : :expr_beg
285
- return '%', '%'
247
+ scanner.scan(/(\n+)/)
248
+ @line_number += scanner.matched.length if scanner.matched
286
249
 
287
- elsif scanner.scan(/\(/)
288
- result = scanner.matched
289
- if [:expr_beg, :expr_mid].include? @lex_state
290
- result = :PAREN_BEG
291
- elsif space_seen
292
- result = '('
293
- end
250
+ next if [:expr_beg, :expr_dot].include? @lex_state
251
+
252
+ cmd_start = true
253
+ @lex_state = :expr_beg
254
+ return '\\n', '\\n'
294
255
 
295
- @lex_state = :expr_beg
296
- cond_push 0
297
- cmdarg_push 0
256
+ elsif scanner.scan(/\;/)
257
+ @lex_state = :expr_beg
258
+ return ';', ';'
259
+
260
+ elsif scanner.scan(/\"/)
261
+ push_string_parse :beg => '"', :content => true, :end => '"'
262
+ return :STRING_BEG, scanner.matched
298
263
 
299
- return result, scanner.matched
264
+ elsif scanner.scan(/\'/)
265
+ push_string_parse :beg => "'", :content => true, :end => "'"
266
+ return :STRING_BEG, scanner.matched
267
+
268
+ elsif scanner.scan(/\`/)
269
+ push_string_parse :beg => "`", :content => true, :end => "`"
270
+ return :XSTRING_BEG, scanner.matched
271
+
272
+ elsif scanner.scan(/\%W/)
273
+ start_word = scanner.scan(/./)
274
+ end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
275
+ push_string_parse :beg => 'W', :content => true, :end => end_word
276
+ return :WORDS_BEG, scanner.matched
277
+
278
+ elsif scanner.scan(/\%w/)
279
+ start_word = scanner.scan(/./)
280
+ end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
281
+ push_string_parse :beg => 'w', :content => true, :end => end_word
282
+ return :AWORDS_BEG, scanner.matched
283
+
284
+ elsif scanner.scan(/\%[Qq]/)
285
+ start_word = scanner.scan(/./)
286
+ end_word = { '(' => ')', '[' => ']', '{' => '}' }[start_word] || start_word
287
+ push_string_parse :beg => start_word, :content => true, :end => end_word
288
+ return :STRING_BEG, scanner.matched
289
+
290
+ elsif scanner.scan(/\//)
291
+ if [:expr_beg, :expr_mid].include? @lex_state
292
+ push_string_parse :beg => '/', :content => true, :end => '/'
293
+ return :REGEXP_BEG, scanner.matched
294
+ elsif scanner.scan(/\=/)
295
+ @lex_state = :expr_beg
296
+ return :OP_ASGN, '/'
297
+ elsif @lex_state == :expr_fname
298
+ @lex_state = :expr_end
299
+ end
300
300
 
301
- elsif scanner.scan(/\)/)
302
- cond_lexpop
303
- cmdarg_lexpop
304
- @lex_state = :expr_end
305
- return ')', scanner.matched
301
+ return '/', '/'
306
302
 
307
- elsif scanner.scan(/\[/)
308
- result = scanner.matched
303
+ elsif scanner.scan(/\%/)
304
+ @lex_state = @lex_state == :expr_fname ? :expr_end : :expr_beg
305
+ return '%', '%'
309
306
 
310
- if [:expr_fname, :expr_dot].include? @lex_state
311
- @lex_state = :expr_arg
312
- if scanner.scan(/\]=/)
313
- return '[]=', '[]='
314
- elsif scanner.scan(/\]/)
315
- return '[]', '[]'
316
- else
317
- raise "Unexpected '[' token"
307
+ elsif scanner.scan(/\(/)
308
+ result = scanner.matched
309
+ if [:expr_beg, :expr_mid].include? @lex_state
310
+ result = :PAREN_BEG
311
+ elsif space_seen
312
+ result = '('
318
313
  end
319
- elsif [:expr_beg, :expr_mid].include?(@lex_state) || space_seen
320
- @lex_state = :expr_beg
321
- cond_push 0
322
- cmdarg_push 0
323
- return '[', scanner.matched
324
- else
314
+
325
315
  @lex_state = :expr_beg
326
316
  cond_push 0
327
317
  cmdarg_push 0
328
- return '[@', scanner.matched
329
- end
330
-
331
- elsif scanner.scan(/\]/)
332
- cond_lexpop
333
- cmdarg_lexpop
334
- @lex_state = :expr_end
335
- return ']', scanner.matched
336
318
 
337
- elsif scanner.scan(/\}/)
338
- cond_lexpop
339
- cmdarg_lexpop
340
- @lex_state = :expr_end
319
+ return result, scanner.matched
341
320
 
342
- current_string_parse[:content] = true if current_string_parse
343
- return '}', scanner.matched
321
+ elsif scanner.scan(/\)/)
322
+ cond_lexpop
323
+ cmdarg_lexpop
324
+ @lex_state = :expr_end
325
+ return ')', scanner.matched
326
+
327
+ elsif scanner.scan(/\[/)
328
+ result = scanner.matched
329
+
330
+ if [:expr_fname, :expr_dot].include? @lex_state
331
+ @lex_state = :expr_arg
332
+ if scanner.scan(/\]=/)
333
+ return '[]=', '[]='
334
+ elsif scanner.scan(/\]/)
335
+ return '[]', '[]'
336
+ else
337
+ raise "Unexpected '[' token"
338
+ end
339
+ elsif [:expr_beg, :expr_mid].include?(@lex_state) || space_seen
340
+ @lex_state = :expr_beg
341
+ cond_push 0
342
+ cmdarg_push 0
343
+ return '[', scanner.matched
344
+ else
345
+ @lex_state = :expr_beg
346
+ cond_push 0
347
+ cmdarg_push 0
348
+ return '[@', scanner.matched
349
+ end
344
350
 
345
- elsif scanner.scan(/\.\.\./)
346
- @lex_state = :expr_beg
347
- return '...', scanner.matched
351
+ elsif scanner.scan(/\]/)
352
+ cond_lexpop
353
+ cmdarg_lexpop
354
+ @lex_state = :expr_end
355
+ return ']', scanner.matched
348
356
 
349
- elsif scanner.scan(/\.\./)
350
- @lex_state = :expr_beg
351
- return '..', scanner.matched
357
+ elsif scanner.scan(/\}/)
358
+ cond_lexpop
359
+ cmdarg_lexpop
360
+ @lex_state = :expr_end
352
361
 
353
- elsif scanner.scan(/\./)
354
- @lex_state = :expr_dot unless @lex_state == :expr_fname
355
- return '.', scanner.matched
362
+ current_string_parse[:content] = true if current_string_parse
363
+ return '}', scanner.matched
356
364
 
357
- elsif scanner.scan(/\*\*\=/)
358
- @lex_state = :expr_beg
359
- return :OP_ASGN, '**'
365
+ elsif scanner.scan(/\.\.\./)
366
+ @lex_state = :expr_beg
367
+ return '...', scanner.matched
360
368
 
361
- elsif scanner.scan(/\*\*/)
362
- return '**', '**'
369
+ elsif scanner.scan(/\.\./)
370
+ @lex_state = :expr_beg
371
+ return '..', scanner.matched
363
372
 
364
- elsif scanner.scan(/\*\=/)
365
- @lex_state = :expr_beg
366
- return :OP_ASGN, '*'
373
+ elsif scanner.scan(/\./)
374
+ @lex_state = :expr_dot unless @lex_state == :expr_fname
375
+ return '.', scanner.matched
367
376
 
368
- elsif scanner.scan(/\*/)
369
- result = scanner.matched
370
- if @lex_state == :expr_fname
371
- @lex_state = :expr_end
372
- return '*', result
373
- elsif space_seen && scanner.check(/\S/)
377
+ elsif scanner.scan(/\*\*\=/)
374
378
  @lex_state = :expr_beg
375
- return :SPLAT, result
376
- elsif [:expr_beg, :expr_mid].include? @lex_state
377
- @lex_state = :expr_beg
378
- return :SPLAT, result
379
- else
380
- @lex_state = :expr_beg
381
- return '*', result
382
- end
379
+ return :OP_ASGN, '**'
383
380
 
384
- elsif scanner.scan(/\:\:/)
385
- if [:expr_beg, :expr_mid, :expr_class].include? @lex_state
381
+ elsif scanner.scan(/\*\*/)
382
+ return '**', '**'
383
+
384
+ elsif scanner.scan(/\*\=/)
386
385
  @lex_state = :expr_beg
387
- return '::@', scanner.matched
388
- end
386
+ return :OP_ASGN, '*'
389
387
 
390
- @lex_state = :expr_dot
391
- return '::', scanner.matched
388
+ elsif scanner.scan(/\*/)
389
+ result = scanner.matched
390
+ if @lex_state == :expr_fname
391
+ @lex_state = :expr_end
392
+ return '*', result
393
+ elsif space_seen && scanner.check(/\S/)
394
+ @lex_state = :expr_beg
395
+ return :SPLAT, result
396
+ elsif [:expr_beg, :expr_mid].include? @lex_state
397
+ @lex_state = :expr_beg
398
+ return :SPLAT, result
399
+ else
400
+ @lex_state = :expr_beg
401
+ return '*', result
402
+ end
392
403
 
393
- elsif scanner.scan(/\:/)
394
- if [:expr_end, :expr_endarg].include?(@lex_state) || scanner.check(/\s/)
395
- unless scanner.check(/\w/)
404
+ elsif scanner.scan(/\:\:/)
405
+ if [:expr_beg, :expr_mid, :expr_class].include? @lex_state
396
406
  @lex_state = :expr_beg
397
- return ':', ':'
407
+ return '::@', scanner.matched
408
+ end
409
+
410
+ @lex_state = :expr_dot
411
+ return '::', scanner.matched
412
+
413
+ elsif scanner.scan(/\:/)
414
+ if [:expr_end, :expr_endarg].include?(@lex_state) || scanner.check(/\s/)
415
+ unless scanner.check(/\w/)
416
+ @lex_state = :expr_beg
417
+ return ':', ':'
418
+ end
419
+
420
+ @lex_state = :expr_fname
421
+ return :SYMBOL_BEG, ':'
422
+ end
423
+
424
+ if scanner.scan(/\'/)
425
+ push_string_parse :beg => "'", :content => true, :end => "'"
426
+ elsif scanner.scan(/\"/)
427
+ push_string_parse :beg => '"', :content => true, :end => '"'
398
428
  end
399
429
 
400
430
  @lex_state = :expr_fname
401
431
  return :SYMBOL_BEG, ':'
402
- end
403
-
404
- if scanner.scan(/\'/)
405
- push_string_parse :beg => "'", :content => true, :end => "'"
406
- elsif scanner.scan(/\"/)
407
- push_string_parse :beg => '"', :content => true, :end => '"'
408
- end
409
432
 
410
- @lex_state = :expr_fname
411
- return :SYMBOL_BEG, ':'
433
+ elsif scanner.check(/\|/)
434
+ if scanner.scan(/\|\|\=/)
435
+ @lex_state = :expr_beg
436
+ return :OP_ASGN, '||'
437
+ elsif scanner.scan(/\|\|/)
438
+ @lex_state = :expr_beg
439
+ return '||', '||'
440
+ elsif scanner.scan(/\|\=/)
441
+ @lex_state = :expr_beg
442
+ return :OP_ASGN, '|'
443
+ elsif scanner.scan(/\|/)
444
+ if @lex_state == :expr_fname
445
+ @lex_state = :expr_end
446
+ return '|', scanner.matched
447
+ else
448
+ @lex_state = :expr_beg
449
+ return '|', scanner.matched
450
+ end
451
+ end
412
452
 
413
- elsif scanner.check(/\|/)
414
- if scanner.scan(/\|\|\=/)
415
- @lex_state = :expr_beg
416
- return :OP_ASGN, '||'
417
- elsif scanner.scan(/\|\|/)
418
- @lex_state = :expr_beg
419
- return '||', '||'
420
- elsif scanner.scan(/\|\=/)
421
- @lex_state = :expr_beg
422
- return :OP_ASGN, '|'
423
- elsif scanner.scan(/\|/)
453
+ elsif scanner.scan(/\^/)
424
454
  if @lex_state == :expr_fname
425
455
  @lex_state = :expr_end
426
- return '|', scanner.matched
427
- else
428
- @lex_state = :expr_beg
429
- return '|', scanner.matched
456
+ return '^', scanner.matched
430
457
  end
431
- end
432
458
 
433
- elsif scanner.scan(/\^/)
434
- if @lex_state == :expr_fname
435
- @lex_state = :expr_end
459
+ @lex_state = :expr_beg
436
460
  return '^', scanner.matched
437
- end
438
461
 
439
- @lex_state = :expr_beg
440
- return '^', scanner.matched
441
-
442
- elsif scanner.check(/\&/)
443
- if scanner.scan(/\&\&\=/)
444
- @lex_state = :expr_beg
445
- return :OP_ASGN, '&&'
446
- elsif scanner.scan(/\&\&/)
447
- @lex_state = :expr_beg
448
- return '&&', scanner.matched
449
- elsif scanner.scan(/\&\=/)
450
- @lex_state = :expr_beg
451
- return :OP_ASGN, '&'
452
- elsif scanner.scan(/\&/)
453
- if space_seen && !scanner.check(/\s/) && @lex_state == :expr_cmdarg
454
- return '&@', '&'
455
- elsif [:expr_beg, :expr_mid].include? @lex_state
456
- return '&@', '&'
457
- else
458
- return '&', '&'
462
+ elsif scanner.check(/\&/)
463
+ if scanner.scan(/\&\&\=/)
464
+ @lex_state = :expr_beg
465
+ return :OP_ASGN, '&&'
466
+ elsif scanner.scan(/\&\&/)
467
+ @lex_state = :expr_beg
468
+ return '&&', scanner.matched
469
+ elsif scanner.scan(/\&\=/)
470
+ @lex_state = :expr_beg
471
+ return :OP_ASGN, '&'
472
+ elsif scanner.scan(/\&/)
473
+ if space_seen && !scanner.check(/\s/) && @lex_state == :expr_cmdarg
474
+ return '&@', '&'
475
+ elsif [:expr_beg, :expr_mid].include? @lex_state
476
+ return '&@', '&'
477
+ else
478
+ return '&', '&'
479
+ end
459
480
  end
460
- end
461
481
 
462
- elsif scanner.check(/\</)
463
- if scanner.scan(/\<\<\=/)
464
- @lex_state = :expr_beg
465
- return :OP_ASGN, '<<'
466
- elsif scanner.scan(/\<\</)
467
- if @lex_state == :expr_fname
468
- @lex_state = :expr_end
469
- return '<<', '<<'
470
- elsif ![:expr_end, :expr_dot, :expr_endarg, :expr_class].include?(@lex_state) && space_seen
482
+ elsif scanner.check(/\</)
483
+ if scanner.scan(/\<\<\=/)
484
+ @lex_state = :expr_beg
485
+ return :OP_ASGN, '<<'
486
+ elsif scanner.scan(/\<\</)
487
+ if @lex_state == :expr_fname
488
+ @lex_state = :expr_end
489
+ return '<<', '<<'
490
+ elsif ![:expr_end, :expr_dot, :expr_endarg, :expr_class].include?(@lex_state) && space_seen
491
+ @lex_state = :expr_beg
492
+ return '<<', '<<'
493
+ end
471
494
  @lex_state = :expr_beg
472
495
  return '<<', '<<'
496
+ elsif scanner.scan(/\<\=\>/)
497
+ if @lex_state == :expr_fname
498
+ @lex_state = :expr_end
499
+ else
500
+ @lex_state = :expr_beg
501
+ end
502
+ return '<=>', '<=>'
503
+ elsif scanner.scan(/\<\=/)
504
+ if @lex_state == :expr_fname
505
+ @lex_state = :expr_end
506
+ else
507
+ @lex_state = :expr_beg
508
+ end
509
+ return '<=', '<='
510
+ elsif scanner.scan(/\</)
511
+ if @lex_state == :expr_fname
512
+ @lex_state = :expr_end
513
+ else
514
+ @lex_state = :expr_beg
515
+ end
516
+ return '<', '<'
473
517
  end
474
- @lex_state = :expr_beg
475
- return '<<', '<<'
476
- elsif scanner.scan(/\<\=\>/)
477
- if @lex_state == :expr_fname
478
- @lex_state = :expr_end
479
- else
480
- @lex_state = :expr_beg
518
+
519
+ elsif scanner.check(/\>/)
520
+ if scanner.scan(/\>\>\=/)
521
+ return :OP_ASGN, '>>'
522
+ elsif scanner.scan(/\>\>/)
523
+ return '>>', '>>'
524
+ elsif scanner.scan(/\>\=/)
525
+ if @lex_state == :expr_fname
526
+ @lex_state = :expr_end
527
+ else
528
+ @lex_state = :expr_beg
529
+ end
530
+ return '>=', scanner.matched
531
+ elsif scanner.scan(/\>/)
532
+ if @lex_state == :expr_fname
533
+ @lex_state = :expr_end
534
+ else
535
+ @lex_state = :expr_beg
536
+ end
537
+ return '>', '>'
481
538
  end
482
- return '<=>', '<=>'
483
- elsif scanner.scan(/\<\=/)
484
- if @lex_state == :expr_fname
539
+
540
+ elsif scanner.scan(/[+-]/)
541
+ result = scanner.matched
542
+ sign = result + '@'
543
+
544
+ if @lex_state == :expr_beg || @lex_state == :expr_mid
485
545
  @lex_state = :expr_end
486
- else
546
+ return [sign, sign]
547
+ elsif @lex_state == :expr_fname
548
+ @lex_state = :expr_end
549
+ return [:IDENTIFIER, result + scanner.matched] if scanner.scan(/@/)
550
+ return [result, result]
551
+ end
552
+
553
+ if scanner.scan(/\=/)
487
554
  @lex_state = :expr_beg
555
+ return [:OP_ASGN, result]
488
556
  end
489
- return '<=', '<='
490
- elsif scanner.scan(/\</)
557
+
558
+ @lex_state = :expr_beg
559
+ return [result, result]
560
+
561
+ elsif scanner.scan(/\?/)
562
+ @lex_state = :expr_beg if [:expr_end, :expr_endarg].include?(@lex_state)
563
+ return '?', scanner.matched
564
+
565
+ elsif scanner.scan(/\=\=\=/)
491
566
  if @lex_state == :expr_fname
492
567
  @lex_state = :expr_end
493
- else
494
- @lex_state = :expr_beg
568
+ return '===', '==='
495
569
  end
496
- return '<', '<'
497
- end
570
+ @lex_state = :expr_beg
571
+ return '===', '==='
498
572
 
499
- elsif scanner.check(/\>/)
500
- if scanner.scan(/\>\>\=/)
501
- return :OP_ASGN, '>>'
502
- elsif scanner.scan(/\>\>/)
503
- return '>>', '>>'
504
- elsif scanner.scan(/\>\=/)
573
+ elsif scanner.scan(/\=\=/)
505
574
  if @lex_state == :expr_fname
506
575
  @lex_state = :expr_end
507
- else
508
- @lex_state = :expr_beg
576
+ return '==', '=='
509
577
  end
510
- return '>=', scanner.matched
511
- elsif scanner.scan(/\>/)
578
+ @lex_state = :expr_beg
579
+ return '==', '=='
580
+
581
+ elsif scanner.scan(/\=\~/)
512
582
  if @lex_state == :expr_fname
513
583
  @lex_state = :expr_end
514
- else
515
- @lex_state = :expr_beg
584
+ return '=~', '=~'
516
585
  end
517
- return '>', '>'
518
- end
586
+ @lex_state = :expr_beg
587
+ return '=~', '=~'
519
588
 
520
- elsif scanner.scan(/[+-]/)
521
- result = scanner.matched
522
- sign = result + '@'
589
+ elsif scanner.scan(/\=\>/)
590
+ @lex_state = :expr_beg
591
+ return '=>', '=>'
523
592
 
524
- if @lex_state == :expr_beg || @lex_state == :expr_mid
525
- @lex_state = :expr_end
526
- return [sign, sign]
527
- elsif @lex_state == :expr_fname
528
- @lex_state = :expr_end
529
- return [:IDENTIFIER, result + scanner.matched] if scanner.scan(/@/)
530
- return [result, result]
531
- end
593
+ elsif scanner.scan(/\=/)
594
+ @lex_state = :expr_beg
595
+ return '=', '='
532
596
 
533
- if scanner.scan(/\=/)
597
+ elsif scanner.scan(/\!\=/)
598
+ if @lex_state == :expr_fname
599
+ @lex_state == :expr_end
600
+ return '!=', '!='
601
+ end
534
602
  @lex_state = :expr_beg
535
- return [:OP_ASGN, result]
536
- end
603
+ return '!=', '!='
537
604
 
538
- @lex_state = :expr_beg
539
- return [result, result]
605
+ elsif scanner.scan(/\!\~/)
606
+ @lex_state = :expr_beg
607
+ return '!~', '!~'
540
608
 
541
- elsif scanner.scan(/\?/)
542
- @lex_state = :expr_beg if [:expr_end, :expr_endarg].include?(@lex_state)
543
- return '?', scanner.matched
609
+ elsif scanner.scan(/\!/)
610
+ if @lex_state == :expr_fname
611
+ @lex_state = :expr_end
612
+ return '!', '!'
613
+ end
614
+ @lex_state = :expr_beg
615
+ return '!', '!'
544
616
 
545
- elsif scanner.scan(/\=\=\=/)
546
- if @lex_state == :expr_fname
547
- @lex_state = :expr_end
548
- return '===', '==='
549
- end
550
- @lex_state = :expr_beg
551
- return '===', '==='
617
+ elsif scanner.scan(/\~/)
618
+ if @lex_state == :expr_fname
619
+ @lex_state = :expr_end
620
+ return '~', '~'
621
+ end
622
+ @lex_state = :expr_beg
623
+ return '~', '~'
552
624
 
553
- elsif scanner.scan(/\=\=/)
554
- if @lex_state == :expr_fname
625
+ elsif scanner.scan(/\$[\+\'\`\&!@\"~*$?\/\\:;=.,<>_]/)
555
626
  @lex_state = :expr_end
556
- return '==', '=='
557
- end
558
- @lex_state = :expr_beg
559
- return '==', '=='
627
+ return :GVAR, scanner.matched
560
628
 
561
- elsif scanner.scan(/\=\~/)
562
- if @lex_state == :expr_fname
629
+ elsif scanner.scan(/\$\w+/)
563
630
  @lex_state = :expr_end
564
- return '=~', '=~'
565
- end
566
- @lex_state = :expr_beg
567
- return '=~', '=~'
568
-
569
- elsif scanner.scan(/\=\>/)
570
- @lex_state = :expr_beg
571
- return '=>', '=>'
631
+ return :GVAR, scanner.matched
572
632
 
573
- elsif scanner.scan(/\=/)
574
- @lex_state = :expr_beg
575
- return '=', '='
576
-
577
- elsif scanner.scan(/\!\=/)
578
- if @lex_state == :expr_fname
579
- @lex_state == :expr_end
580
- return '!=', '!='
581
- end
582
- @lex_state = :expr_beg
583
- return '!=', '!='
584
-
585
- elsif scanner.scan(/\!\~/)
586
- @lex_state = :expr_beg
587
- return '!~', '!~'
588
-
589
- elsif scanner.scan(/\!/)
590
- if @lex_state == :expr_fname
633
+ elsif scanner.scan(/\@\@\w*/)
591
634
  @lex_state = :expr_end
592
- return '!', '!'
593
- end
594
- @lex_state = :expr_beg
595
- return '!', '!'
635
+ return :CVAR, scanner.matched
596
636
 
597
- elsif scanner.scan(/\~/)
598
- if @lex_state == :expr_fname
637
+ elsif scanner.scan(/\@\w*/)
599
638
  @lex_state = :expr_end
600
- return '~', '~'
601
- end
602
- @lex_state = :expr_beg
603
- return '~', '~'
604
-
605
- elsif scanner.scan(/\$[\+\'\`\&!@\"~*$?\/\\:;=.,<>_]/)
606
- @lex_state = :expr_end
607
- return :GVAR, scanner.matched
608
-
609
- elsif scanner.scan(/\$\w+/)
610
- @lex_state = :expr_end
611
- return :GVAR, scanner.matched
612
-
613
- elsif scanner.scan(/\@\@\w*/)
614
- @lex_state = :expr_end
615
- return :CVAR, scanner.matched
616
-
617
- elsif scanner.scan(/\@\w*/)
618
- @lex_state = :expr_end
619
- return :IVAR, scanner.matched
620
-
621
- elsif scanner.scan(/\,/)
622
- @lex_state = :expr_beg
623
- return ',', scanner.matched
624
-
625
- elsif scanner.scan(/\{/)
626
- if [:expr_end, :expr_cmdarg].include? @lex_state
627
- result = '{@'
628
- elsif @lex_state == :expr_endarg
629
- result = 'LBRACE_ARG'
630
- else
631
- result = '{'
632
- end
639
+ return :IVAR, scanner.matched
633
640
 
634
- @lex_state = :expr_beg
635
- cond_push 0
636
- cmdarg_push 0
637
- return result, scanner.matched
638
-
639
- elsif scanner.check(/[0-9]/)
640
- @lex_state = :expr_end
641
- if scanner.scan(/[\d_]+\.[\d_]+\b/)
642
- return [:FLOAT, scanner.matched.gsub(/_/, '')]
643
- elsif scanner.scan(/[\d_]+\b/)
644
- return [:INTEGER, scanner.matched.gsub(/_/, '')]
645
- elsif scanner.scan(/0(x|X)(\d|[a-f]|[A-F])+/)
646
- return [:INTEGER, scanner.matched]
647
- else
648
- raise "Lexing error on numeric type: `#{scanner.peek 5}`"
649
- end
641
+ elsif scanner.scan(/\,/)
642
+ @lex_state = :expr_beg
643
+ return ',', scanner.matched
650
644
 
651
- elsif scanner.scan(/(\w)+[\?\!]?/)
652
- case scanner.matched
653
- when 'class'
654
- if @lex_state == :expr_dot
655
- @lex_state = :expr_end
656
- return :IDENTIFIER, scanner.matched
645
+ elsif scanner.scan(/\{/)
646
+ if [:expr_end, :expr_cmdarg].include? @lex_state
647
+ result = '{@'
648
+ elsif @lex_state == :expr_endarg
649
+ result = 'LBRACE_ARG'
650
+ else
651
+ result = '{'
657
652
  end
658
- @lex_state = :expr_class
659
- return :CLASS, scanner.matched
660
-
661
- when 'module'
662
- return :IDENTIFIER, scanner.matched if @lex_state == :expr_dot
663
- @lex_state = :expr_class
664
- return :MODULE, scanner.matched
665
653
 
666
- when 'def'
667
- @lex_state = :expr_fname
668
- return :DEF, scanner.matched
669
-
670
- when 'undef'
671
- @lex_state = :expr_fname
672
- return :UNDEF, scanner.matched
673
-
674
- when 'end'
675
- if [:expr_dot, :expr_fname].include? @lex_state
676
- @lex_state = :expr_end
677
- return :IDENTIFIER, scanner.matched
678
- end
654
+ @lex_state = :expr_beg
655
+ cond_push 0
656
+ cmdarg_push 0
657
+ return result, scanner.matched
679
658
 
659
+ elsif scanner.check(/[0-9]/)
680
660
  @lex_state = :expr_end
681
- return :END, scanner.matched
682
-
683
- when 'do'
684
- if cond?
685
- @lex_state = :expr_beg
686
- return :DO_COND, scanner.matched
687
- elsif cmdarg? && @lex_state != :expr_cmdarg
688
- @lex_state = :expr_beg
689
- return :DO_BLOCK, scanner.matched
690
- elsif @lex_state == :expr_endarg
691
- return :DO_BLOCK, scanner.matched
661
+ if scanner.scan(/[\d_]+\.[\d_]+\b/)
662
+ return [:FLOAT, scanner.matched.gsub(/_/, '')]
663
+ elsif scanner.scan(/[\d_]+\b/)
664
+ return [:INTEGER, scanner.matched.gsub(/_/, '')]
665
+ elsif scanner.scan(/0(x|X)(\d|[a-f]|[A-F])+/)
666
+ return [:INTEGER, scanner.matched]
692
667
  else
693
- @lex_state = :expr_beg
694
- return :DO, scanner.matched
668
+ raise "Lexing error on numeric type: `#{scanner.peek 5}`"
695
669
  end
696
670
 
697
- when 'if'
698
- return :IF, scanner.matched if @lex_state == :expr_beg
699
- @lex_state = :expr_beg
700
- return :IF_MOD, scanner.matched
671
+ elsif scanner.scan(/(\w)+[\?\!]?/)
672
+ case scanner.matched
673
+ when 'class'
674
+ if @lex_state == :expr_dot
675
+ @lex_state = :expr_end
676
+ return :IDENTIFIER, scanner.matched
677
+ end
678
+ @lex_state = :expr_class
679
+ return :CLASS, scanner.matched
680
+
681
+ when 'module'
682
+ return :IDENTIFIER, scanner.matched if @lex_state == :expr_dot
683
+ @lex_state = :expr_class
684
+ return :MODULE, scanner.matched
685
+
686
+ when 'def'
687
+ @lex_state = :expr_fname
688
+ return :DEF, scanner.matched
689
+
690
+ when 'undef'
691
+ @lex_state = :expr_fname
692
+ return :UNDEF, scanner.matched
693
+
694
+ when 'end'
695
+ if [:expr_dot, :expr_fname].include? @lex_state
696
+ @lex_state = :expr_end
697
+ return :IDENTIFIER, scanner.matched
698
+ end
701
699
 
702
- when 'unless'
703
- return :UNLESS, scanner.matched if @lex_state == :expr_beg
704
- return :UNLESS_MOD, scanner.matched
700
+ @lex_state = :expr_end
701
+ return :END, scanner.matched
702
+
703
+ when 'do'
704
+ if cond?
705
+ @lex_state = :expr_beg
706
+ return :DO_COND, scanner.matched
707
+ elsif cmdarg? && @lex_state != :expr_cmdarg
708
+ @lex_state = :expr_beg
709
+ return :DO_BLOCK, scanner.matched
710
+ elsif @lex_state == :expr_endarg
711
+ return :DO_BLOCK, scanner.matched
712
+ else
713
+ @lex_state = :expr_beg
714
+ return :DO, scanner.matched
715
+ end
716
+
717
+ when 'if'
718
+ return :IF, scanner.matched if @lex_state == :expr_beg
719
+ @lex_state = :expr_beg
720
+ return :IF_MOD, scanner.matched
705
721
 
706
- when 'else'
707
- return :ELSE, scanner.matched
722
+ when 'unless'
723
+ return :UNLESS, scanner.matched if @lex_state == :expr_beg
724
+ return :UNLESS_MOD, scanner.matched
708
725
 
709
- when 'elsif'
710
- return :ELSIF, scanner.matched
726
+ when 'else'
727
+ return :ELSE, scanner.matched
711
728
 
712
- when 'self'
713
- @lex_state = :expr_end unless @lex_state == :expr_fname
714
- return :SELF, scanner.matched
729
+ when 'elsif'
730
+ return :ELSIF, scanner.matched
715
731
 
716
- when 'true'
717
- @lex_state = :expr_end
718
- return :TRUE, scanner.matched
732
+ when 'self'
733
+ @lex_state = :expr_end unless @lex_state == :expr_fname
734
+ return :SELF, scanner.matched
719
735
 
720
- when 'false'
721
- @lex_state = :expr_end
722
- return :FALSE, scanner.matched
736
+ when 'true'
737
+ @lex_state = :expr_end
738
+ return :TRUE, scanner.matched
723
739
 
724
- when 'nil'
725
- @lex_state = :expr_end
726
- return :NIL, scanner.matched
740
+ when 'false'
741
+ @lex_state = :expr_end
742
+ return :FALSE, scanner.matched
727
743
 
728
- when 'undefined'
729
- @lex_state = :expr_end
730
- return :UNDEFINED, scanner.matched
744
+ when 'nil'
745
+ @lex_state = :expr_end
746
+ return :NIL, scanner.matched
731
747
 
732
- when 'null'
733
- @lex_state = :expr_end
734
- return :NULL, scanner.matched
748
+ when 'undefined'
749
+ @lex_state = :expr_end
750
+ return :UNDEFINED, scanner.matched
735
751
 
736
- when '__LINE__'
737
- @lex_state = :expr_end
738
- return :LINE, @line_number.to_s
752
+ when 'null'
753
+ @lex_state = :expr_end
754
+ return :NULL, scanner.matched
739
755
 
740
- when '__FILE__'
741
- @lex_state = :expr_end
742
- return :FILE, scanner.matched
756
+ when '__LINE__'
757
+ @lex_state = :expr_end
758
+ return :LINE, @line_number.to_s
743
759
 
744
- when 'begin'
745
- if [:expr_dot, :expr_fname].include? @lex_state
760
+ when '__FILE__'
746
761
  @lex_state = :expr_end
747
- return :IDENTIFIER, scanner.matched
748
- end
749
- @lex_state = :expr_beg
750
- return :BEGIN, scanner.matched
762
+ return :FILE, scanner.matched
751
763
 
752
- when 'rescue'
753
- return :IDENTIFIER, scanner.matched if [:expr_dot, :expr_fname].include? @lex_state
754
- return :RESCUE, scanner.matched if @lex_state == :expr_beg
755
- @lex_state = :expr_beg
756
- return :RESCUE_MOD, scanner.matched
764
+ when 'begin'
765
+ if [:expr_dot, :expr_fname].include? @lex_state
766
+ @lex_state = :expr_end
767
+ return :IDENTIFIER, scanner.matched
768
+ end
769
+ @lex_state = :expr_beg
770
+ return :BEGIN, scanner.matched
757
771
 
758
- when 'ensure'
759
- @lex_state = :expr_beg
760
- return :ENSURE, scanner.matched
772
+ when 'rescue'
773
+ return :IDENTIFIER, scanner.matched if [:expr_dot, :expr_fname].include? @lex_state
774
+ return :RESCUE, scanner.matched if @lex_state == :expr_beg
775
+ @lex_state = :expr_beg
776
+ return :RESCUE_MOD, scanner.matched
761
777
 
762
- when 'case'
763
- @lex_state = :expr_beg
764
- return :CASE, scanner.matched
778
+ when 'ensure'
779
+ @lex_state = :expr_beg
780
+ return :ENSURE, scanner.matched
765
781
 
766
- when 'when'
767
- @lex_state = :expr_beg
768
- return :WHEN, scanner.matched
782
+ when 'case'
783
+ @lex_state = :expr_beg
784
+ return :CASE, scanner.matched
769
785
 
770
- when 'or'
771
- @lex_state = :expr_beg
772
- return :OR, scanner.matched
786
+ when 'when'
787
+ @lex_state = :expr_beg
788
+ return :WHEN, scanner.matched
773
789
 
774
- when 'and'
775
- @lex_state = :expr_beg
776
- return :AND, scanner.matched
790
+ when 'or'
791
+ @lex_state = :expr_beg
792
+ return :OR, scanner.matched
777
793
 
778
- when 'not'
779
- @lex_state = :expr_beg
780
- return :NOT, scanner.matched
794
+ when 'and'
795
+ @lex_state = :expr_beg
796
+ return :AND, scanner.matched
781
797
 
782
- when 'return'
783
- @lex_state = :expr_mid
784
- return :RETURN, scanner.matched
798
+ when 'not'
799
+ @lex_state = :expr_beg
800
+ return :NOT, scanner.matched
785
801
 
786
- when 'next'
787
- if @lex_state == :expr_dot || @lex_state == :expr_fname
788
- @lex_state = :expr_end
789
- return :IDENTIFIER, scanner.matched
790
- end
802
+ when 'return'
803
+ @lex_state = :expr_mid
804
+ return :RETURN, scanner.matched
791
805
 
792
- @lex_state = :expr_mid
793
- return :NEXT, scanner.matched
806
+ when 'next'
807
+ if @lex_state == :expr_dot || @lex_state == :expr_fname
808
+ @lex_state = :expr_end
809
+ return :IDENTIFIER, scanner.matched
810
+ end
794
811
 
795
- when 'redo'
796
- if @lex_state == :expr_dot || @lex_state == :expr_fname
797
- @lex_state = :expr_end
798
- return :IDENTIFIER, scanner.matched
799
- end
812
+ @lex_state = :expr_mid
813
+ return :NEXT, scanner.matched
800
814
 
801
- @lex_state = :expr_mid
802
- return :REDO, scanner.matched
815
+ when 'redo'
816
+ if @lex_state == :expr_dot || @lex_state == :expr_fname
817
+ @lex_state = :expr_end
818
+ return :IDENTIFIER, scanner.matched
819
+ end
803
820
 
804
- when 'break'
805
- @lex_state = :expr_mid
806
- return :BREAK, scanner.matched
821
+ @lex_state = :expr_mid
822
+ return :REDO, scanner.matched
807
823
 
808
- when 'super'
809
- @lex_state = :expr_arg
810
- return :SUPER, scanner.matched
824
+ when 'break'
825
+ @lex_state = :expr_mid
826
+ return :BREAK, scanner.matched
811
827
 
812
- when 'then'
813
- return :THEN, scanner.matched
828
+ when 'super'
829
+ @lex_state = :expr_arg
830
+ return :SUPER, scanner.matched
814
831
 
815
- when 'while'
816
- return :WHILE, scanner.matched if @lex_state == :expr_beg
817
- @lex_state = :expr_beg
818
- return :WHILE_MOD, scanner.matched
832
+ when 'then'
833
+ return :THEN, scanner.matched
819
834
 
820
- when 'for'
821
- @lex_state = :expr_beg
822
- return :FOR, scanner.matched
835
+ when 'while'
836
+ return :WHILE, scanner.matched if @lex_state == :expr_beg
837
+ @lex_state = :expr_beg
838
+ return :WHILE_MOD, scanner.matched
823
839
 
824
- when 'in'
825
- @lex_state = :expr_beg
826
- return :IN, scanner.matched
840
+ when 'for'
841
+ @lex_state = :expr_beg
842
+ return :FOR, scanner.matched
827
843
 
828
- when 'until'
829
- return :WHILE, scanner.matched if @lex_state == :expr_beg
830
- @lex_state = :expr_beg
831
- return :UNTIL_MOD, scanner.matched
844
+ when 'in'
845
+ @lex_state = :expr_beg
846
+ return :IN, scanner.matched
832
847
 
833
- when 'yield'
834
- @lex_state = :expr_arg
835
- return :YIELD, scanner.matched
836
- end
848
+ when 'until'
849
+ return :WHILE, scanner.matched if @lex_state == :expr_beg
850
+ @lex_state = :expr_beg
851
+ return :UNTIL_MOD, scanner.matched
837
852
 
838
- matched = scanner.matched
839
- return :LABEL, matched if scanner.peek(2) != '::' && scanner.scan(/\:/)
853
+ when 'yield'
854
+ @lex_state = :expr_arg
855
+ return :YIELD, scanner.matched
856
+ end
840
857
 
841
- if @lex_state == :expr_fname
842
- if scanner.scan(/\=/)
858
+ matched = scanner.matched
859
+ return :LABEL, matched if scanner.peek(2) != '::' && scanner.scan(/\:/)
860
+
861
+ if @lex_state == :expr_fname
862
+ if scanner.scan(/\=/)
863
+ @lex_state = :expr_end
864
+ return :IDENTIFIER, matched + scanner.matched
865
+ end
866
+ end
867
+
868
+ if [:expr_beg, :expr_dot, :expr_mid, :expr_arg, :expr_cmdarg].include? @lex_state
869
+ @lex_state = :expr_cmdarg
870
+ else
843
871
  @lex_state = :expr_end
844
- return :IDENTIFIER, matched + scanner.matched
845
872
  end
846
- end
847
873
 
848
- if [:expr_beg, :expr_dot, :expr_mid, :expr_arg, :expr_cmdarg].include? @lex_state
849
- @lex_state = :expr_cmdarg
850
- else
851
- @lex_state = :expr_end
852
- end
874
+ return [matched =~ /[A-Z]/ ? :CONSTANT : :IDENTIFIER, matched]
853
875
 
854
- return [matched =~ /[A-Z]/ ? :CONSTANT : :IDENTIFIER, matched]
876
+ end
877
+ return [false, false] if scanner.eos?
855
878
 
879
+ raise RubyLexingError, "Unexpected content in parsing stream `#{scanner.peek 5}`"
856
880
  end
857
- return [false, false] if scanner.eos?
858
-
859
- raise RubyLexingError, "Unexpected content in parsing stream `#{scanner.peek 5}`"
860
881
  end
861
882
  end
862
883
  end
863
- end