opal 0.3.9 → 0.3.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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