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 +28 -5
- data/bin/opal +0 -3
- data/lib/opal.rb +8 -3
- data/lib/opal/builder.rb +10 -1
- data/lib/opal/bundle.rb +48 -0
- data/lib/opal/command.rb +18 -6
- data/lib/opal/context.rb +68 -44
- data/lib/opal/lexer.rb +702 -682
- data/lib/opal/nodes.rb +3 -1
- data/lib/opal/rake/bundle_task.rb +62 -0
- metadata +4 -5
- data/lib/opal/browserify.rb +0 -34
- data/lib/rbp.rb +0 -2
- data/lib/rbp/package.rb +0 -49
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
|
|
24
|
-
|
|
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
|
-
$
|
|
80
|
+
$ rbp install
|
|
58
81
|
```
|
|
59
82
|
|
|
60
|
-
This will put opaltest into `
|
|
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
|
-
$
|
|
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
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
|
-
|
|
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 +=
|
|
88
|
+
code += "var core_lib = #{parse core.join};"
|
|
80
89
|
|
|
81
90
|
code + File.read(File.join RUNTIME_PATH, 'post.js')
|
|
82
91
|
end
|
data/lib/opal/bundle.rb
ADDED
|
@@ -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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
47
|
-
bundle
|
|
48
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
-
|
|
27
|
+
def setup_load_paths
|
|
28
|
+
return if @loaded_paths
|
|
39
29
|
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
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 "=> #{
|
|
71
|
+
puts "=> #{eval line, '(opal)'}"
|
|
80
72
|
end
|
|
81
73
|
|
|
82
74
|
finish
|
|
83
75
|
end
|
|
84
76
|
|
|
85
|
-
def
|
|
77
|
+
def eval(content, file = nil, line = "")
|
|
86
78
|
begin
|
|
87
|
-
|
|
88
|
-
code = "opal.run(function() {var $rb = opal.runtime, self = $rb.top
|
|
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
|
|
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
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
57
|
+
def cond_push(n)
|
|
58
|
+
@cond = (@cond << 1) | (n & 1)
|
|
59
|
+
end
|
|
23
60
|
|
|
24
|
-
|
|
25
|
-
|
|
61
|
+
def cond_pop
|
|
62
|
+
@cond = @cond >> 1
|
|
63
|
+
end
|
|
26
64
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
65
|
+
def cond_lexpop
|
|
66
|
+
@cond = (@cond >> 1) | (@cond & 1)
|
|
67
|
+
end
|
|
30
68
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
t
|
|
35
|
-
end
|
|
69
|
+
def cond?
|
|
70
|
+
(@cond & 1) != 0
|
|
71
|
+
end
|
|
36
72
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
73
|
+
def cmdarg_push(n)
|
|
74
|
+
@cmdarg = (@cmdarg << 1) | (n & 1)
|
|
75
|
+
end
|
|
40
76
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
77
|
+
def cmdarg_pop
|
|
78
|
+
@cmdarg = @cmdarg >> 1
|
|
79
|
+
end
|
|
44
80
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
81
|
+
def cmdarg_lexpop
|
|
82
|
+
@cmdarg = (@cmdarg >> 1) | (@cmdarg & 1)
|
|
83
|
+
end
|
|
48
84
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
85
|
+
def cmdarg?
|
|
86
|
+
(@cmdarg & 1) != 0
|
|
87
|
+
end
|
|
52
88
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
89
|
+
def push_string_parse(hash)
|
|
90
|
+
@string_parse_stack << hash
|
|
91
|
+
end
|
|
56
92
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
def pop_string_parse
|
|
94
|
+
@string_parse_stack.pop
|
|
95
|
+
end
|
|
60
96
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
97
|
+
def current_string_parse
|
|
98
|
+
@string_parse_stack.last
|
|
99
|
+
end
|
|
64
100
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
74
|
-
@string_parse_stack.pop
|
|
75
|
-
end
|
|
110
|
+
words = ['w', 'W'].include? str_parse[:beg]
|
|
76
111
|
|
|
77
|
-
|
|
78
|
-
@string_parse_stack.last
|
|
79
|
-
end
|
|
112
|
+
space = true if ['w', 'W'].include?(str_parse[:beg]) and scanner.scan(/\s+/)
|
|
80
113
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
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
|
-
|
|
130
|
+
elsif str_parse[:beg] == '`'
|
|
131
|
+
@lex_state = :expr_end
|
|
132
|
+
return :STRING_END, scanner.matched
|
|
91
133
|
|
|
92
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
|
|
145
|
+
return :SPACE, ' ' if space
|
|
126
146
|
|
|
127
|
-
|
|
128
|
-
|
|
147
|
+
# not end of string, so we must be parsing contents
|
|
148
|
+
str_buffer = []
|
|
129
149
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
186
|
+
until scanner.eos?
|
|
187
|
+
c = nil
|
|
188
|
+
handled = true
|
|
165
189
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
190
|
+
if scanner.check end_str_re
|
|
191
|
+
# eos
|
|
192
|
+
break
|
|
169
193
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
elsif words && scanner.scan(/\s/)
|
|
195
|
+
scanner.pos -= 1
|
|
196
|
+
break
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
break
|
|
198
|
+
elsif interpolate && scanner.check(/#(?=[\@\{])/)
|
|
199
|
+
break
|
|
177
200
|
|
|
178
|
-
|
|
179
|
-
|
|
201
|
+
elsif scanner.scan(/\\\\/)
|
|
202
|
+
c = scanner.matched
|
|
180
203
|
|
|
181
|
-
|
|
182
|
-
|
|
204
|
+
elsif scanner.scan(/\\/)
|
|
205
|
+
c = scanner.matched
|
|
206
|
+
c += scanner.matched if scanner.scan end_str_re
|
|
183
207
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
208
|
+
else
|
|
209
|
+
handled = false
|
|
210
|
+
end
|
|
187
211
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
scanner.scan reg
|
|
195
|
-
c = scanner.matched
|
|
218
|
+
c ||= scanner.matched
|
|
219
|
+
str_buffer << c
|
|
196
220
|
end
|
|
197
221
|
|
|
198
|
-
|
|
199
|
-
str_buffer << c
|
|
222
|
+
raise "reached EOF while in string" if scanner.eos?
|
|
200
223
|
end
|
|
201
224
|
|
|
202
|
-
|
|
203
|
-
|
|
225
|
+
def get_next_token
|
|
226
|
+
string_scanner = current_string_parse
|
|
204
227
|
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
if string_scanner && string_scanner[:content]
|
|
229
|
+
return next_string_token
|
|
230
|
+
end
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
243
|
+
elsif scanner.scan(/(\n|#)/)
|
|
244
|
+
c = scanner.matched
|
|
245
|
+
if c == '#' then scanner.scan(/(.*)/) else @line_number += 1; end
|
|
282
246
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return '%', '%'
|
|
247
|
+
scanner.scan(/(\n+)/)
|
|
248
|
+
@line_number += scanner.matched.length if scanner.matched
|
|
286
249
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
|
|
296
|
-
|
|
297
|
-
|
|
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
|
-
|
|
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
|
-
|
|
302
|
-
cond_lexpop
|
|
303
|
-
cmdarg_lexpop
|
|
304
|
-
@lex_state = :expr_end
|
|
305
|
-
return ')', scanner.matched
|
|
301
|
+
return '/', '/'
|
|
306
302
|
|
|
307
|
-
|
|
308
|
-
|
|
303
|
+
elsif scanner.scan(/\%/)
|
|
304
|
+
@lex_state = @lex_state == :expr_fname ? :expr_end : :expr_beg
|
|
305
|
+
return '%', '%'
|
|
309
306
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
if
|
|
313
|
-
|
|
314
|
-
elsif
|
|
315
|
-
|
|
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
|
-
|
|
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
|
-
|
|
338
|
-
cond_lexpop
|
|
339
|
-
cmdarg_lexpop
|
|
340
|
-
@lex_state = :expr_end
|
|
319
|
+
return result, scanner.matched
|
|
341
320
|
|
|
342
|
-
|
|
343
|
-
|
|
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
|
-
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
elsif scanner.scan(/\]/)
|
|
352
|
+
cond_lexpop
|
|
353
|
+
cmdarg_lexpop
|
|
354
|
+
@lex_state = :expr_end
|
|
355
|
+
return ']', scanner.matched
|
|
348
356
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
357
|
+
elsif scanner.scan(/\}/)
|
|
358
|
+
cond_lexpop
|
|
359
|
+
cmdarg_lexpop
|
|
360
|
+
@lex_state = :expr_end
|
|
352
361
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
return '.', scanner.matched
|
|
362
|
+
current_string_parse[:content] = true if current_string_parse
|
|
363
|
+
return '}', scanner.matched
|
|
356
364
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
365
|
+
elsif scanner.scan(/\.\.\./)
|
|
366
|
+
@lex_state = :expr_beg
|
|
367
|
+
return '...', scanner.matched
|
|
360
368
|
|
|
361
|
-
|
|
362
|
-
|
|
369
|
+
elsif scanner.scan(/\.\./)
|
|
370
|
+
@lex_state = :expr_beg
|
|
371
|
+
return '..', scanner.matched
|
|
363
372
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
373
|
+
elsif scanner.scan(/\./)
|
|
374
|
+
@lex_state = :expr_dot unless @lex_state == :expr_fname
|
|
375
|
+
return '.', scanner.matched
|
|
367
376
|
|
|
368
|
-
|
|
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 :
|
|
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
|
-
|
|
385
|
-
|
|
381
|
+
elsif scanner.scan(/\*\*/)
|
|
382
|
+
return '**', '**'
|
|
383
|
+
|
|
384
|
+
elsif scanner.scan(/\*\=/)
|
|
386
385
|
@lex_state = :expr_beg
|
|
387
|
-
return '
|
|
388
|
-
end
|
|
386
|
+
return :OP_ASGN, '*'
|
|
389
387
|
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
394
|
-
|
|
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
|
-
|
|
411
|
-
|
|
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
|
-
|
|
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 '
|
|
427
|
-
else
|
|
428
|
-
@lex_state = :expr_beg
|
|
429
|
-
return '|', scanner.matched
|
|
456
|
+
return '^', scanner.matched
|
|
430
457
|
end
|
|
431
|
-
end
|
|
432
458
|
|
|
433
|
-
|
|
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
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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
|
-
|
|
483
|
-
elsif scanner.scan(
|
|
484
|
-
|
|
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
|
-
|
|
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
|
-
|
|
490
|
-
|
|
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
|
-
|
|
494
|
-
@lex_state = :expr_beg
|
|
568
|
+
return '===', '==='
|
|
495
569
|
end
|
|
496
|
-
|
|
497
|
-
|
|
570
|
+
@lex_state = :expr_beg
|
|
571
|
+
return '===', '==='
|
|
498
572
|
|
|
499
|
-
|
|
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
|
-
|
|
508
|
-
@lex_state = :expr_beg
|
|
576
|
+
return '==', '=='
|
|
509
577
|
end
|
|
510
|
-
|
|
511
|
-
|
|
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
|
-
|
|
515
|
-
@lex_state = :expr_beg
|
|
584
|
+
return '=~', '=~'
|
|
516
585
|
end
|
|
517
|
-
|
|
518
|
-
|
|
586
|
+
@lex_state = :expr_beg
|
|
587
|
+
return '=~', '=~'
|
|
519
588
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
589
|
+
elsif scanner.scan(/\=\>/)
|
|
590
|
+
@lex_state = :expr_beg
|
|
591
|
+
return '=>', '=>'
|
|
523
592
|
|
|
524
|
-
|
|
525
|
-
@lex_state = :
|
|
526
|
-
return
|
|
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
|
-
|
|
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
|
|
536
|
-
end
|
|
603
|
+
return '!=', '!='
|
|
537
604
|
|
|
538
|
-
|
|
539
|
-
|
|
605
|
+
elsif scanner.scan(/\!\~/)
|
|
606
|
+
@lex_state = :expr_beg
|
|
607
|
+
return '!~', '!~'
|
|
540
608
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
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
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
return :
|
|
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
|
-
|
|
694
|
-
return :DO, scanner.matched
|
|
668
|
+
raise "Lexing error on numeric type: `#{scanner.peek 5}`"
|
|
695
669
|
end
|
|
696
670
|
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
-
|
|
703
|
-
|
|
704
|
-
|
|
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
|
-
|
|
707
|
-
|
|
722
|
+
when 'unless'
|
|
723
|
+
return :UNLESS, scanner.matched if @lex_state == :expr_beg
|
|
724
|
+
return :UNLESS_MOD, scanner.matched
|
|
708
725
|
|
|
709
|
-
|
|
710
|
-
|
|
726
|
+
when 'else'
|
|
727
|
+
return :ELSE, scanner.matched
|
|
711
728
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
return :SELF, scanner.matched
|
|
729
|
+
when 'elsif'
|
|
730
|
+
return :ELSIF, scanner.matched
|
|
715
731
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
732
|
+
when 'self'
|
|
733
|
+
@lex_state = :expr_end unless @lex_state == :expr_fname
|
|
734
|
+
return :SELF, scanner.matched
|
|
719
735
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
736
|
+
when 'true'
|
|
737
|
+
@lex_state = :expr_end
|
|
738
|
+
return :TRUE, scanner.matched
|
|
723
739
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
740
|
+
when 'false'
|
|
741
|
+
@lex_state = :expr_end
|
|
742
|
+
return :FALSE, scanner.matched
|
|
727
743
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
744
|
+
when 'nil'
|
|
745
|
+
@lex_state = :expr_end
|
|
746
|
+
return :NIL, scanner.matched
|
|
731
747
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
748
|
+
when 'undefined'
|
|
749
|
+
@lex_state = :expr_end
|
|
750
|
+
return :UNDEFINED, scanner.matched
|
|
735
751
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
752
|
+
when 'null'
|
|
753
|
+
@lex_state = :expr_end
|
|
754
|
+
return :NULL, scanner.matched
|
|
739
755
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
756
|
+
when '__LINE__'
|
|
757
|
+
@lex_state = :expr_end
|
|
758
|
+
return :LINE, @line_number.to_s
|
|
743
759
|
|
|
744
|
-
|
|
745
|
-
if [:expr_dot, :expr_fname].include? @lex_state
|
|
760
|
+
when '__FILE__'
|
|
746
761
|
@lex_state = :expr_end
|
|
747
|
-
return :
|
|
748
|
-
end
|
|
749
|
-
@lex_state = :expr_beg
|
|
750
|
-
return :BEGIN, scanner.matched
|
|
762
|
+
return :FILE, scanner.matched
|
|
751
763
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
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
|
-
|
|
763
|
-
|
|
764
|
-
|
|
778
|
+
when 'ensure'
|
|
779
|
+
@lex_state = :expr_beg
|
|
780
|
+
return :ENSURE, scanner.matched
|
|
765
781
|
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
782
|
+
when 'case'
|
|
783
|
+
@lex_state = :expr_beg
|
|
784
|
+
return :CASE, scanner.matched
|
|
769
785
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
786
|
+
when 'when'
|
|
787
|
+
@lex_state = :expr_beg
|
|
788
|
+
return :WHEN, scanner.matched
|
|
773
789
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
790
|
+
when 'or'
|
|
791
|
+
@lex_state = :expr_beg
|
|
792
|
+
return :OR, scanner.matched
|
|
777
793
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
794
|
+
when 'and'
|
|
795
|
+
@lex_state = :expr_beg
|
|
796
|
+
return :AND, scanner.matched
|
|
781
797
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
798
|
+
when 'not'
|
|
799
|
+
@lex_state = :expr_beg
|
|
800
|
+
return :NOT, scanner.matched
|
|
785
801
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
return :IDENTIFIER, scanner.matched
|
|
790
|
-
end
|
|
802
|
+
when 'return'
|
|
803
|
+
@lex_state = :expr_mid
|
|
804
|
+
return :RETURN, scanner.matched
|
|
791
805
|
|
|
792
|
-
|
|
793
|
-
|
|
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
|
-
|
|
796
|
-
|
|
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
|
-
|
|
802
|
-
|
|
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
|
-
|
|
805
|
-
|
|
806
|
-
return :BREAK, scanner.matched
|
|
821
|
+
@lex_state = :expr_mid
|
|
822
|
+
return :REDO, scanner.matched
|
|
807
823
|
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
824
|
+
when 'break'
|
|
825
|
+
@lex_state = :expr_mid
|
|
826
|
+
return :BREAK, scanner.matched
|
|
811
827
|
|
|
812
|
-
|
|
813
|
-
|
|
828
|
+
when 'super'
|
|
829
|
+
@lex_state = :expr_arg
|
|
830
|
+
return :SUPER, scanner.matched
|
|
814
831
|
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
@lex_state = :expr_beg
|
|
818
|
-
return :WHILE_MOD, scanner.matched
|
|
832
|
+
when 'then'
|
|
833
|
+
return :THEN, scanner.matched
|
|
819
834
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
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
|
-
|
|
825
|
-
|
|
826
|
-
|
|
840
|
+
when 'for'
|
|
841
|
+
@lex_state = :expr_beg
|
|
842
|
+
return :FOR, scanner.matched
|
|
827
843
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
return :UNTIL_MOD, scanner.matched
|
|
844
|
+
when 'in'
|
|
845
|
+
@lex_state = :expr_beg
|
|
846
|
+
return :IN, scanner.matched
|
|
832
847
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
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
|
-
|
|
839
|
-
|
|
853
|
+
when 'yield'
|
|
854
|
+
@lex_state = :expr_arg
|
|
855
|
+
return :YIELD, scanner.matched
|
|
856
|
+
end
|
|
840
857
|
|
|
841
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|