kaiser-ruby 0.7.1 → 0.8
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -0
- data/.ruby-version +1 -1
- data/.travis.yml +5 -4
- data/CHANGELOG.md +34 -10
- data/Gemfile +4 -2
- data/Gemfile.lock +39 -24
- data/Rakefile +5 -3
- data/bin/console +7 -3
- data/exe/kaiser-ruby +8 -6
- data/kaiser-ruby.gemspec +19 -16
- data/lib/kaiser_ruby.rb +4 -6
- data/lib/kaiser_ruby/cli.rb +19 -7
- data/lib/kaiser_ruby/parser.rb +189 -144
- data/lib/kaiser_ruby/refinements.rb +409 -28
- data/lib/kaiser_ruby/transformer.rb +112 -60
- data/lib/kaiser_ruby/version.rb +3 -1
- metadata +39 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 040ea9398c1f1cb0499f3a5d21038b9ee52434a9370145ec8efd5e1217264431
|
4
|
+
data.tar.gz: b7eb871f93fb4f68570685d9376c75698ca23e16b12752e6157cd046acacc6ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bdd9abbab639f244d3a97799a25dc8c0ef5b5dfc582011a6b1d3cc02df804c7ca2d3ca8746214ba5b7df101af8967021e9fe20a010e86c8121bd122b68ea98f0
|
7
|
+
data.tar.gz: 3c04422053c184d14c59740bceb57212b4f8b1d3483c916ee8c131128af2315cf30a2040dbf0c431dd31c27100372b858580b3180739975f593a639e6f336945
|
data/.rubocop.yml
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.2
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,26 @@
|
|
1
|
+
# 0.8
|
2
|
+
|
3
|
+
Language implementation:
|
4
|
+
|
5
|
+
- [x] Add support for simple variables
|
6
|
+
- [x] Add support for rest of the pronouns
|
7
|
+
- [x] Return integers instead of floats where applicable so it will show `5` instead of `5.0` if there's no need for a Float
|
8
|
+
- [x] Introduce proper `mysterious` type to allow `nil` to be used more predictably
|
9
|
+
- [x] Update pronouns only on variable assignment, not on usage too
|
10
|
+
- [x] Fix double elses without preceding empty line to end the `if/else` block explicitly
|
11
|
+
- [x] Comments are properly ignored now (even multiline ones though they're a really bad idea anyway)
|
12
|
+
- [x] Math operators like `+` are also handled in addition to `plus`
|
13
|
+
- [x] Strings are no longer considered falsy if they contain a falsy alias (`lies`, `false` etc), they're always truthy
|
14
|
+
- [x] Support `let <variable> be <expression>`
|
15
|
+
- [x] Handle recursion
|
16
|
+
- [x] Handle nested functions and their scoping
|
17
|
+
|
18
|
+
Other changes:
|
19
|
+
|
20
|
+
- [x] Add new reference tests from main language repository
|
21
|
+
- [x] Fix else nesting
|
22
|
+
- [x] Fix older Ruby versions
|
23
|
+
|
1
24
|
# 0.7.1
|
2
25
|
|
3
26
|
- [x] Strips all lines while parsing, so you can indent the .rock file however you want
|
@@ -7,7 +30,7 @@
|
|
7
30
|
|
8
31
|
# 0.7
|
9
32
|
|
10
|
-
Language
|
33
|
+
Language implementation:
|
11
34
|
|
12
35
|
- [x] Full language implementation according to the spec
|
13
36
|
|
@@ -27,7 +50,7 @@ Language Implementation
|
|
27
50
|
- [x] Multiple increments/decrements with `build X up, up up` and `knock Y down, down, down`
|
28
51
|
- [x] Handle global variables if they're declared after function definition that uses them
|
29
52
|
|
30
|
-
Other
|
53
|
+
Other changes:
|
31
54
|
|
32
55
|
- [x] Replaced parsing with Parslet with a hand-written parser in plain Ruby
|
33
56
|
- [x] Implemented reference tests
|
@@ -38,26 +61,27 @@ Other
|
|
38
61
|
|
39
62
|
# 0.6 - unreleased
|
40
63
|
|
64
|
+
Other changes:
|
65
|
+
|
41
66
|
- [x] Fixed error in input from STDIN
|
42
67
|
- [x] The transpiler now throws a SyntaxError instead of Parslet exception
|
43
|
-
|
44
|
-
Test Suite
|
45
|
-
|
46
68
|
- [x] Refactored the test suite to make more sense
|
47
69
|
- [x] Added a ton of new negative and positive tests
|
48
70
|
|
49
71
|
# 0.5
|
50
72
|
|
73
|
+
Language implementation:
|
74
|
+
|
51
75
|
- [x] Fixed converting decimals so "Conversion is lovestruck. lovestruck and essential seasick" results in "conversion = 0.0397" as one would expect.
|
52
76
|
|
53
|
-
Other
|
77
|
+
Other changes:
|
54
78
|
|
55
79
|
- [x] Updated the REPL to work on Ruby versions earlier than 2.5 (2.3 is the minimum supported version)
|
56
80
|
- [x] Travis CI tests on all supported Ruby versions
|
57
81
|
|
58
82
|
# 0.4
|
59
83
|
|
60
|
-
Language
|
84
|
+
Language implementation:
|
61
85
|
|
62
86
|
- [x] Handle non-alpha values in quoted strings
|
63
87
|
- [x] Ignore comments in parentheses
|
@@ -67,7 +91,7 @@ Language Implementation:
|
|
67
91
|
- [x] Better handle input of integers from STDIN
|
68
92
|
- [x] Print can print returned values from functions
|
69
93
|
|
70
|
-
Other
|
94
|
+
Other changes:
|
71
95
|
|
72
96
|
- [x] Updated the FizzBuzz example
|
73
97
|
- [x] Catch exceptions in the REPL
|
@@ -77,7 +101,7 @@ Other stuff:
|
|
77
101
|
|
78
102
|
# 0.3
|
79
103
|
|
80
|
-
Language
|
104
|
+
Language implementation:
|
81
105
|
|
82
106
|
- [x] Handle null type differently - nil in Ruby isn't really comparable to 0
|
83
107
|
- [x] Handle mysterious type - probably this should be nil and what is now nil should be 0 instead
|
@@ -91,7 +115,7 @@ Language Implementation:
|
|
91
115
|
- [x] Handle function calls
|
92
116
|
- [x] Return can return math operations directly
|
93
117
|
|
94
|
-
Other
|
118
|
+
Other changes:
|
95
119
|
|
96
120
|
- [x] FizzBuzz example is working
|
97
121
|
- [x] Fibonacci example is working
|
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
source 'https://rubygems.org'
|
4
|
+
|
5
|
+
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
4
6
|
|
5
7
|
# Specify your gem's dependencies in kaiser-ruby.gemspec
|
6
8
|
gemspec
|
data/Gemfile.lock
CHANGED
@@ -1,45 +1,60 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
kaiser-ruby (0.
|
4
|
+
kaiser-ruby (0.8)
|
5
5
|
hashie
|
6
|
-
thor
|
6
|
+
thor
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
ast (2.4.0)
|
11
12
|
coderay (1.1.2)
|
12
13
|
diff-lcs (1.3)
|
13
14
|
hashie (3.6.0)
|
14
|
-
|
15
|
-
|
15
|
+
jaro_winkler (1.5.2)
|
16
|
+
method_source (0.9.2)
|
17
|
+
parallel (1.15.0)
|
18
|
+
parser (2.6.2.0)
|
19
|
+
ast (~> 2.4.0)
|
20
|
+
pry (0.12.2)
|
16
21
|
coderay (~> 1.1.0)
|
17
22
|
method_source (~> 0.9.0)
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
rspec-
|
23
|
-
|
24
|
-
rspec-
|
25
|
-
rspec-
|
23
|
+
psych (3.1.0)
|
24
|
+
rainbow (3.0.0)
|
25
|
+
rake (12.3.2)
|
26
|
+
rspec (3.8.0)
|
27
|
+
rspec-core (~> 3.8.0)
|
28
|
+
rspec-expectations (~> 3.8.0)
|
29
|
+
rspec-mocks (~> 3.8.0)
|
30
|
+
rspec-core (3.8.0)
|
31
|
+
rspec-support (~> 3.8.0)
|
32
|
+
rspec-expectations (3.8.2)
|
26
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
-
rspec-support (~> 3.
|
28
|
-
rspec-mocks (3.
|
34
|
+
rspec-support (~> 3.8.0)
|
35
|
+
rspec-mocks (3.8.0)
|
29
36
|
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
-
rspec-support (~> 3.
|
31
|
-
rspec-support (3.
|
32
|
-
|
37
|
+
rspec-support (~> 3.8.0)
|
38
|
+
rspec-support (3.8.0)
|
39
|
+
rubocop (0.66.0)
|
40
|
+
jaro_winkler (~> 1.5.1)
|
41
|
+
parallel (~> 1.10)
|
42
|
+
parser (>= 2.5, != 2.5.1.1)
|
43
|
+
psych (>= 3.1.0)
|
44
|
+
rainbow (>= 2.2.2, < 4.0)
|
45
|
+
ruby-progressbar (~> 1.7)
|
46
|
+
unicode-display_width (>= 1.4.0, < 1.6)
|
47
|
+
ruby-progressbar (1.10.0)
|
48
|
+
thor (0.20.3)
|
49
|
+
unicode-display_width (1.5.0)
|
33
50
|
|
34
51
|
PLATFORMS
|
35
52
|
ruby
|
36
53
|
|
37
54
|
DEPENDENCIES
|
38
|
-
bundler (~> 1.
|
55
|
+
bundler (~> 1.17.0)
|
39
56
|
kaiser-ruby!
|
40
|
-
pry
|
41
|
-
rake
|
42
|
-
rspec
|
43
|
-
|
44
|
-
BUNDLED WITH
|
45
|
-
1.16.2
|
57
|
+
pry
|
58
|
+
rake
|
59
|
+
rspec
|
60
|
+
rubocop
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/exe/kaiser-ruby
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "kaiser_ruby"
|
5
|
-
require "kaiser_ruby/cli"
|
6
|
-
require "kaiser_ruby/refinements"
|
2
|
+
# frozen_string_literal: true
|
7
3
|
|
8
|
-
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler/setup'
|
6
|
+
require 'kaiser_ruby'
|
7
|
+
require 'kaiser_ruby/cli'
|
8
|
+
require 'kaiser_ruby/refinements'
|
9
|
+
|
10
|
+
KaiserRuby::CLI.start(ARGV)
|
data/kaiser-ruby.gemspec
CHANGED
@@ -1,30 +1,33 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
2
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
5
|
+
require 'kaiser_ruby/version'
|
4
6
|
|
5
7
|
Gem::Specification.new do |spec|
|
6
|
-
spec.name =
|
8
|
+
spec.name = 'kaiser-ruby'
|
7
9
|
spec.version = KaiserRuby::VERSION
|
8
|
-
spec.authors = [
|
9
|
-
spec.email = [
|
10
|
+
spec.authors = ['Marcin Ruszkiewicz']
|
11
|
+
spec.email = ['marcin.ruszkiewicz@polcode.net']
|
10
12
|
|
11
|
-
spec.summary =
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
13
|
+
spec.summary = 'Transpiler of Rockstar language to Ruby'
|
14
|
+
spec.homepage = 'https://github.com/marcinruszkiewicz/kaiser-ruby'
|
15
|
+
spec.license = 'MIT'
|
14
16
|
|
15
17
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
16
18
|
f.match(%r{^(spec|examples)/})
|
17
19
|
end
|
18
|
-
spec.bindir =
|
20
|
+
spec.bindir = 'exe'
|
19
21
|
spec.executables = ['kaiser-ruby']
|
20
|
-
spec.require_paths = [
|
22
|
+
spec.require_paths = ['lib']
|
21
23
|
|
22
24
|
spec.required_ruby_version = '>= 2.3'
|
23
25
|
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.
|
29
|
-
spec.add_dependency
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.17.0'
|
27
|
+
spec.add_development_dependency 'pry'
|
28
|
+
spec.add_development_dependency 'rake'
|
29
|
+
spec.add_development_dependency 'rspec'
|
30
|
+
spec.add_development_dependency 'rubocop'
|
31
|
+
spec.add_dependency 'hashie'
|
32
|
+
spec.add_dependency 'thor'
|
30
33
|
end
|
data/lib/kaiser_ruby.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'hashie'
|
2
4
|
require 'kaiser_ruby/parser'
|
3
|
-
require 'kaiser_ruby/transformer'
|
4
5
|
require 'kaiser_ruby/refinements'
|
6
|
+
require 'kaiser_ruby/transformer'
|
5
7
|
require 'pry'
|
6
8
|
|
9
|
+
# Transpile Rockstar into Ruby code
|
7
10
|
module KaiserRuby
|
8
11
|
class RockstarSyntaxError < SyntaxError
|
9
12
|
end
|
10
13
|
|
11
14
|
def self.parse(input)
|
12
|
-
# eat comments since we don't care about them
|
13
|
-
input = input.gsub(/\n *\(.*?\) *\n/m, "\n\n")
|
14
|
-
input = input.gsub(/\(.*?\)\s*\n/m, "\n").gsub(/ +/, ' ')
|
15
15
|
parser = KaiserRuby::Parser.new(input)
|
16
16
|
parser.parse
|
17
17
|
end
|
@@ -29,8 +29,6 @@ module KaiserRuby
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
private
|
33
|
-
|
34
32
|
def self.with_captured_stdout
|
35
33
|
old_stdout = $stdout
|
36
34
|
$stdout = StringIO.new
|
data/lib/kaiser_ruby/cli.rb
CHANGED
@@ -1,20 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thor'
|
2
4
|
require_relative 'version'
|
3
5
|
|
4
6
|
module KaiserRuby
|
7
|
+
# command line interface for kaiser-ruby command
|
5
8
|
class CLI < Thor
|
6
9
|
package_name "Kaiser-Ruby v#{KaiserRuby::VERSION}"
|
7
10
|
|
8
|
-
desc
|
9
|
-
|
10
|
-
|
11
|
+
desc 'parse FILE', 'parse a .rock FILE and output the intermediate tree'
|
12
|
+
def parse(filename)
|
13
|
+
file = File.read filename
|
14
|
+
output = KaiserRuby.parse(file)
|
15
|
+
|
16
|
+
puts output
|
17
|
+
say
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'transpile FILE', 'transpile a .rock FILE and output the resulting Ruby code'
|
21
|
+
option 'show-source'.to_sym, type: :boolean, desc: 'prints out the source file along with the transpiled output'
|
22
|
+
option :save, desc: 'saves the transpiled output in SAVE'
|
11
23
|
def transpile(filename)
|
12
24
|
file = File.read filename
|
13
25
|
output = KaiserRuby.transpile(file)
|
14
26
|
|
15
27
|
if options['show-source'.to_sym]
|
16
28
|
say file
|
17
|
-
say
|
29
|
+
say '-' * 40, :green
|
18
30
|
end
|
19
31
|
|
20
32
|
if options[:save]
|
@@ -35,7 +47,7 @@ module KaiserRuby
|
|
35
47
|
|
36
48
|
using KaiserRuby::Refinements
|
37
49
|
|
38
|
-
desc
|
50
|
+
desc 'execute FILE', 'transpiles and runs a .rock FILE'
|
39
51
|
def execute(filename)
|
40
52
|
file = File.read filename
|
41
53
|
output = KaiserRuby.transpile(file)
|
@@ -43,8 +55,8 @@ module KaiserRuby
|
|
43
55
|
say
|
44
56
|
end
|
45
57
|
|
46
|
-
desc
|
47
|
-
option :debug, type: :boolean, desc:
|
58
|
+
desc 'rock', 'opens an interactive console that accepts and evaluates Rockstar code'
|
59
|
+
option :debug, type: :boolean, desc: 'also shows transpiled code'
|
48
60
|
def rock
|
49
61
|
say "Type 'exit' to exit the console. Otherwise, rock on!"
|
50
62
|
|
data/lib/kaiser_ruby/parser.rb
CHANGED
@@ -1,70 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module KaiserRuby
|
4
|
+
# Parser class goes through the Rockstar code provided and generates
|
5
|
+
# an intermediate tree from which we can output a proper Ruby program
|
2
6
|
class Parser
|
3
7
|
attr_reader :lines, :raw_input, :tree
|
4
8
|
|
5
|
-
POETIC_STRING_KEYWORDS = %w
|
6
|
-
POETIC_NUMBER_KEYWORDS = %w
|
7
|
-
POETIC_NUMBER_CONTRACTIONS = %w
|
8
|
-
POETIC_TYPE_KEYWORDS = %w
|
9
|
-
PRINT_KEYWORDS = %w
|
10
|
-
LISTEN_TO_KEYWORDS = ['listen to']
|
11
|
-
LISTEN_KEYWORDS = ['listen']
|
12
|
-
BREAK_KEYWORDS = ['break', 'break it down']
|
13
|
-
CONTINUE_KEYWORDS = ['continue', 'take it to the top']
|
14
|
-
RETURN_KEYWORDS = ['give back']
|
15
|
-
|
16
|
-
INCREMENT_FIRST_KEYWORDS = %w
|
17
|
-
INCREMENT_SECOND_KEYWORDS = %w
|
18
|
-
DECREMENT_FIRST_KEYWORDS = %w
|
19
|
-
DECREMENT_SECOND_KEYWORDS = %w
|
20
|
-
ASSIGNMENT_FIRST_KEYWORDS = %w
|
21
|
-
ASSIGNMENT_SECOND_KEYWORDS = %w
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
9
|
+
POETIC_STRING_KEYWORDS = %w[says].freeze
|
10
|
+
POETIC_NUMBER_KEYWORDS = %w[is was were are 's 're].freeze
|
11
|
+
POETIC_NUMBER_CONTRACTIONS = %w['s 're].freeze
|
12
|
+
POETIC_TYPE_KEYWORDS = %w[is].freeze
|
13
|
+
PRINT_KEYWORDS = %w[say whisper shout scream].freeze
|
14
|
+
LISTEN_TO_KEYWORDS = ['listen to'].freeze
|
15
|
+
LISTEN_KEYWORDS = ['listen'].freeze
|
16
|
+
BREAK_KEYWORDS = ['break', 'break it down'].freeze
|
17
|
+
CONTINUE_KEYWORDS = ['continue', 'take it to the top'].freeze
|
18
|
+
RETURN_KEYWORDS = ['give back'].freeze
|
19
|
+
|
20
|
+
INCREMENT_FIRST_KEYWORDS = %w[build].freeze
|
21
|
+
INCREMENT_SECOND_KEYWORDS = %w[up].freeze
|
22
|
+
DECREMENT_FIRST_KEYWORDS = %w[knock].freeze
|
23
|
+
DECREMENT_SECOND_KEYWORDS = %w[down].freeze
|
24
|
+
ASSIGNMENT_FIRST_KEYWORDS = %w[put].freeze
|
25
|
+
ASSIGNMENT_SECOND_KEYWORDS = %w[into].freeze
|
26
|
+
LET_ASSIGNMENT_FIRST_KEYWORDS = %w[let].freeze
|
27
|
+
LET_ASSIGNMENT_SECOND_KEYWORDS = %w[be].freeze
|
28
|
+
|
29
|
+
FUNCTION_KEYWORDS = %w[takes].freeze
|
30
|
+
FUNCTION_SEPARATORS = ['and', ', and', "'n'", '&', ','].freeze
|
31
|
+
FUNCTION_CALL_KEYWORDS = %w[taking].freeze
|
32
|
+
FUNCTION_CALL_SEPARATORS = [', and', "'n'", '&', ','].freeze
|
33
|
+
IF_KEYWORDS = %w[if].freeze
|
34
|
+
UNTIL_KEYWORDS = %w[until].freeze
|
35
|
+
WHILE_KEYWORDS = %w[while].freeze
|
36
|
+
ELSE_KEYWORDS = %w[else].freeze
|
37
|
+
|
38
|
+
NULL_TYPE = %w[null nothing nowhere nobody gone empty].freeze
|
39
|
+
TRUE_TYPE = %w[true yes ok right].freeze
|
40
|
+
FALSE_TYPE = %w[false no lies wrong].freeze
|
41
|
+
MYSTERIOUS_TYPE = %w[mysterious].freeze
|
42
|
+
POETIC_TYPE_LITERALS = MYSTERIOUS_TYPE + NULL_TYPE + TRUE_TYPE + FALSE_TYPE
|
43
|
+
|
44
|
+
COMMON_VARIABLE_KEYWORDS = %w[a an the my your].freeze
|
45
|
+
PRONOUN_KEYWORDS = %w[he him she her it its they them ze hir zie zir xe xem ve ver].freeze
|
46
|
+
|
47
|
+
ADDITION_KEYWORDS = %w[plus with +].freeze
|
48
|
+
SUBTRACTION_KEYWORDS = %w[minus without -].freeze
|
49
|
+
MULTIPLICATION_KEYWORDS = %w[times of *].freeze
|
50
|
+
DIVISION_KEYWORDS = %w[over /].freeze
|
45
51
|
MATH_OP_KEYWORDS = ADDITION_KEYWORDS + SUBTRACTION_KEYWORDS + MULTIPLICATION_KEYWORDS + DIVISION_KEYWORDS
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
MATH_TOKENS = %w[+ / * -].freeze
|
53
|
+
|
54
|
+
EQUALITY_KEYWORDS = %w[is].freeze
|
55
|
+
INEQUALITY_KEYWORDS = %w[isn't isnt ain't aint is\ not].freeze
|
56
|
+
GT_KEYWORDS = ['is higher than', 'is greater than', 'is bigger than', 'is stronger than'].freeze
|
57
|
+
GTE_KEYWORDS = ['is as high as', 'is as great as', 'is as big as', 'is as strong as'].freeze
|
58
|
+
LT_KEYWORDS = ['is lower than', 'is less than', 'is smaller than', 'is weaker than'].freeze
|
59
|
+
LTE_KEYWORDS = ['is as low as', 'is as little as', 'is as small as', 'is as weak as'].freeze
|
53
60
|
COMPARISON_KEYWORDS = EQUALITY_KEYWORDS + INEQUALITY_KEYWORDS + GT_KEYWORDS + GTE_KEYWORDS + LT_KEYWORDS + LTE_KEYWORDS
|
54
61
|
|
55
62
|
FUNCTION_RESTRICTED_KEYWORDS = MATH_OP_KEYWORDS + ['(?<!, )and', 'is', 'or', 'into', 'nor']
|
56
63
|
|
57
|
-
AND_KEYWORDS =
|
58
|
-
OR_KEYWORDS = %w
|
59
|
-
NOR_KEYWORDS = %w
|
60
|
-
NOT_KEYWORDS = ['(?<!is )not']
|
64
|
+
AND_KEYWORDS = ['(?<!, )and'].freeze
|
65
|
+
OR_KEYWORDS = %w[or].freeze
|
66
|
+
NOR_KEYWORDS = %w[nor].freeze
|
67
|
+
NOT_KEYWORDS = ['(?<!is )not'].freeze
|
61
68
|
LOGIC_KEYWORDS = AND_KEYWORDS + OR_KEYWORDS + NOT_KEYWORDS + NOR_KEYWORDS
|
62
69
|
|
63
70
|
RESERVED_KEYWORDS = LOGIC_KEYWORDS + MATH_OP_KEYWORDS + POETIC_TYPE_LITERALS
|
64
71
|
|
65
72
|
def initialize(input)
|
66
73
|
@raw_input = input
|
67
|
-
@lines = input.split
|
74
|
+
@lines = input.gsub(/\(\n.*?\)/m, "\n").split(/\n/) # eat multiline comments
|
68
75
|
end
|
69
76
|
|
70
77
|
def parse
|
@@ -73,7 +80,8 @@ module KaiserRuby
|
|
73
80
|
@tree.extend(Hashie::Extensions::DeepLocate)
|
74
81
|
@function_temp = []
|
75
82
|
@nesting = 0
|
76
|
-
@
|
83
|
+
@nesting_has_else = false
|
84
|
+
@current_scope = [nil]
|
77
85
|
@lnum = 0
|
78
86
|
|
79
87
|
# parse through lines to get the general structure (statements/flow control/functions/etc) out of it
|
@@ -94,14 +102,18 @@ module KaiserRuby
|
|
94
102
|
end
|
95
103
|
|
96
104
|
def parse_line(line)
|
97
|
-
|
105
|
+
# consume comments and extra spaces
|
106
|
+
line = line.gsub(/\(.*?\)/, '').strip
|
98
107
|
|
99
108
|
if line.empty?
|
100
|
-
if @nesting
|
109
|
+
if @nesting.positive?
|
110
|
+
@current_scope.pop unless @current_scope[@nesting].nil?
|
111
|
+
@nesting_has_else = false
|
101
112
|
@nesting -= 1
|
102
|
-
@nesting_start_line = nil
|
103
113
|
end
|
104
114
|
|
115
|
+
@current_scope.pop if @nesting.zero?
|
116
|
+
|
105
117
|
add_to_tree(parse_empty_line)
|
106
118
|
else
|
107
119
|
obj = parse_line_content(line)
|
@@ -111,14 +123,18 @@ module KaiserRuby
|
|
111
123
|
end
|
112
124
|
|
113
125
|
def update_nesting(object)
|
114
|
-
if %i
|
126
|
+
if %i[if function until while].include? object.keys.first
|
115
127
|
@nesting += 1
|
116
|
-
@
|
128
|
+
@nesting_has_else = false
|
129
|
+
end
|
130
|
+
|
131
|
+
if object.keys.first == :function
|
132
|
+
@current_scope.push object.deep_locate(:function_name).first.dig(:function_name)
|
117
133
|
end
|
118
134
|
end
|
119
135
|
|
120
136
|
def parse_line_content(line)
|
121
|
-
words = line.split
|
137
|
+
words = line.split(/\s/)
|
122
138
|
|
123
139
|
if matches_first?(words, IF_KEYWORDS)
|
124
140
|
return parse_if(line)
|
@@ -130,6 +146,8 @@ module KaiserRuby
|
|
130
146
|
return parse_until(line)
|
131
147
|
elsif matches_separate?(words, ASSIGNMENT_FIRST_KEYWORDS, ASSIGNMENT_SECOND_KEYWORDS)
|
132
148
|
return parse_assignment(line)
|
149
|
+
elsif matches_separate?(words, LET_ASSIGNMENT_FIRST_KEYWORDS, LET_ASSIGNMENT_SECOND_KEYWORDS)
|
150
|
+
return parse_let_assignment(line)
|
133
151
|
elsif matches_several_first?(line, RETURN_KEYWORDS)
|
134
152
|
return parse_return(line)
|
135
153
|
elsif matches_first?(words, PRINT_KEYWORDS)
|
@@ -146,21 +164,13 @@ module KaiserRuby
|
|
146
164
|
return(parse_continue) if matches_several_first?(line, CONTINUE_KEYWORDS)
|
147
165
|
return(parse_function_call(line)) if matches_any?(words, FUNCTION_CALL_KEYWORDS)
|
148
166
|
|
149
|
-
if matches_separate?(words, INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
if matches_separate?(words, DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
|
154
|
-
return parse_decrement(line)
|
155
|
-
end
|
156
|
-
|
157
|
-
if matches_any?(words, FUNCTION_KEYWORDS)
|
158
|
-
return(parse_function(line))
|
159
|
-
end
|
167
|
+
return parse_increment(line) if matches_separate?(words, INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
|
168
|
+
return parse_decrement(line) if matches_separate?(words, DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
|
169
|
+
return(parse_function(line)) if matches_any?(words, FUNCTION_KEYWORDS)
|
160
170
|
end
|
161
171
|
end
|
162
172
|
|
163
|
-
raise KaiserRuby::RockstarSyntaxError, "couldn't parse line: #{line}:#{@lnum+1}"
|
173
|
+
raise KaiserRuby::RockstarSyntaxError, "couldn't parse line: #{line}:#{@lnum + 1}"
|
164
174
|
end
|
165
175
|
|
166
176
|
# statements
|
@@ -176,6 +186,7 @@ module KaiserRuby
|
|
176
186
|
words = line.split prepared_regexp(LISTEN_TO_KEYWORDS)
|
177
187
|
arg = parse_variables(words.last.strip)
|
178
188
|
arg[:type] = :assignment
|
189
|
+
|
179
190
|
{ listen_to: arg }
|
180
191
|
end
|
181
192
|
|
@@ -187,6 +198,7 @@ module KaiserRuby
|
|
187
198
|
words = line.split prepared_regexp(RETURN_KEYWORDS)
|
188
199
|
arg = consume_function_calls(words.last.strip)
|
189
200
|
argument = parse_argument(arg)
|
201
|
+
|
190
202
|
{ return: argument }
|
191
203
|
end
|
192
204
|
|
@@ -194,8 +206,8 @@ module KaiserRuby
|
|
194
206
|
match_rxp = prepared_capture(INCREMENT_FIRST_KEYWORDS, INCREMENT_SECOND_KEYWORDS)
|
195
207
|
var = line.match(match_rxp).captures.first.strip
|
196
208
|
capture = parse_variables(var)
|
197
|
-
|
198
209
|
capture[:amount] = line.split(var).last.scan(/\bup\b/i).count
|
210
|
+
|
199
211
|
{ increment: capture }
|
200
212
|
end
|
201
213
|
|
@@ -203,12 +215,20 @@ module KaiserRuby
|
|
203
215
|
match_rxp = prepared_capture(DECREMENT_FIRST_KEYWORDS, DECREMENT_SECOND_KEYWORDS)
|
204
216
|
var = line.match(match_rxp).captures.first.strip
|
205
217
|
capture = parse_variables(var)
|
206
|
-
|
207
218
|
capture[:amount] = line.split(var).last.scan(/\bdown\b/i).count
|
219
|
+
|
208
220
|
{ decrement: capture }
|
209
221
|
end
|
210
222
|
|
211
223
|
def parse_else
|
224
|
+
if @nesting_has_else
|
225
|
+
@nesting -= 1
|
226
|
+
@nesting_has_else = false
|
227
|
+
add_to_tree parse_empty_line
|
228
|
+
else
|
229
|
+
@nesting_has_else = true
|
230
|
+
end
|
231
|
+
|
212
232
|
{ else: nil }
|
213
233
|
end
|
214
234
|
|
@@ -247,15 +267,14 @@ module KaiserRuby
|
|
247
267
|
end
|
248
268
|
|
249
269
|
def parse_function_call(line)
|
250
|
-
words = line.split
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
end
|
270
|
+
words = line.split(/\s/)
|
271
|
+
return false unless matches_any?(words, FUNCTION_CALL_KEYWORDS)
|
272
|
+
|
273
|
+
words = line.split prepared_regexp(FUNCTION_CALL_KEYWORDS)
|
274
|
+
left = parse_function_name(words.first.strip)
|
275
|
+
right = parse_function_call_arguments(words.last.strip)
|
276
|
+
|
277
|
+
{ function_call: { left: left, right: right } }
|
259
278
|
end
|
260
279
|
|
261
280
|
def parse_poetic_string(line)
|
@@ -263,6 +282,7 @@ module KaiserRuby
|
|
263
282
|
left = parse_variables(words.first.strip)
|
264
283
|
right = { string: "\"#{words.last.strip}\"" }
|
265
284
|
left[:type] = :assignment
|
285
|
+
|
266
286
|
{ poetic_string: { left: left, right: right } }
|
267
287
|
end
|
268
288
|
|
@@ -271,23 +291,28 @@ module KaiserRuby
|
|
271
291
|
left = parse_variables(words.first.strip)
|
272
292
|
right = parse_type_value(words.last.strip)
|
273
293
|
left[:type] = :assignment
|
294
|
+
|
274
295
|
{ poetic_type: { left: left, right: right } }
|
275
296
|
end
|
276
297
|
|
277
298
|
def parse_type_value(string)
|
278
|
-
words = string.split
|
299
|
+
words = string.split(/\s/)
|
279
300
|
|
280
|
-
if matches_first?(words,
|
281
|
-
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum+1}" if words.count > 1
|
282
|
-
|
301
|
+
if matches_first?(words, MYSTERIOUS_TYPE)
|
302
|
+
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1
|
303
|
+
|
304
|
+
{ type: 'mysterious' }
|
283
305
|
elsif matches_first?(words, NULL_TYPE)
|
284
|
-
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum+1}" if words.count > 1
|
306
|
+
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1
|
307
|
+
|
285
308
|
{ type: 'null' }
|
286
309
|
elsif matches_first?(words, TRUE_TYPE)
|
287
|
-
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum+1}" if words.count > 1
|
310
|
+
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1
|
311
|
+
|
288
312
|
{ type: 'true' }
|
289
313
|
elsif matches_first?(words, FALSE_TYPE)
|
290
|
-
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum+1}" if words.count > 1
|
314
|
+
raise KaiserRuby::RockstarSyntaxError, "extra words are not allowed after literal type keyword: #{string}:#{@lnum + 1}" if words.count > 1
|
315
|
+
|
291
316
|
{ type: 'false' }
|
292
317
|
elsif string.strip.start_with?('"') && string.strip.end_with?('"')
|
293
318
|
parse_literal_string(string)
|
@@ -297,11 +322,11 @@ module KaiserRuby
|
|
297
322
|
end
|
298
323
|
|
299
324
|
def parse_type_literal(string)
|
300
|
-
words = string.split
|
301
|
-
raise SyntaxError, "too many words in poetic type literal: #{string}:#{@lnum+1}" if words.size > 1
|
325
|
+
words = string.split(/\s/)
|
326
|
+
raise SyntaxError, "too many words in poetic type literal: #{string}:#{@lnum + 1}" if words.size > 1
|
302
327
|
|
303
|
-
if matches_first?(words,
|
304
|
-
{ type: '
|
328
|
+
if matches_first?(words, MYSTERIOUS_TYPE)
|
329
|
+
{ type: 'mysterious' }
|
305
330
|
elsif matches_first?(words, NULL_TYPE)
|
306
331
|
{ type: 'null' }
|
307
332
|
elsif matches_first?(words, TRUE_TYPE)
|
@@ -309,7 +334,7 @@ module KaiserRuby
|
|
309
334
|
elsif matches_first?(words, FALSE_TYPE)
|
310
335
|
{ type: 'false' }
|
311
336
|
else
|
312
|
-
raise SyntaxError, "unknown poetic type literal: #{string}:#{@lnum+1}"
|
337
|
+
raise SyntaxError, "unknown poetic type literal: #{string}:#{@lnum + 1}"
|
313
338
|
end
|
314
339
|
end
|
315
340
|
|
@@ -318,14 +343,33 @@ module KaiserRuby
|
|
318
343
|
right = parse_argument(line.match(match_rxp).captures.first.strip)
|
319
344
|
left = parse_variables(line.match(match_rxp).captures.last.strip)
|
320
345
|
left[:type] = :assignment
|
346
|
+
|
347
|
+
{ assignment: { left: left, right: right } }
|
348
|
+
end
|
349
|
+
|
350
|
+
def parse_let_assignment(line)
|
351
|
+
match_rxp = prepared_capture(LET_ASSIGNMENT_FIRST_KEYWORDS, LET_ASSIGNMENT_SECOND_KEYWORDS)
|
352
|
+
right = parse_argument(line.match(match_rxp).captures.last.strip)
|
353
|
+
left = parse_variables(line.match(match_rxp).captures.first.strip)
|
354
|
+
left[:type] = :assignment
|
355
|
+
|
356
|
+
# if the right is an expression and its variable name is empty
|
357
|
+
# then it's a compound assignment which we translate back to an explicit one
|
358
|
+
right.extend(Hashie::Extensions::DeepLocate)
|
359
|
+
unless right.deep_locate(:variable_name).empty?
|
360
|
+
if right.deep_locate(:variable_name).first[:variable_name].empty?
|
361
|
+
right.deep_locate(:variable_name).first[:variable_name] = left[:variable_name]
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
321
365
|
{ assignment: { left: left, right: right } }
|
322
366
|
end
|
323
367
|
|
324
368
|
def parse_if(line)
|
325
369
|
words = line.split prepared_regexp(IF_KEYWORDS)
|
326
|
-
|
327
370
|
arg = consume_function_calls(words.last.strip)
|
328
371
|
argument = parse_argument(arg)
|
372
|
+
|
329
373
|
{ if: { argument: argument } }
|
330
374
|
end
|
331
375
|
|
@@ -333,6 +377,7 @@ module KaiserRuby
|
|
333
377
|
words = line.split prepared_regexp(UNTIL_KEYWORDS)
|
334
378
|
arg = consume_function_calls(words.last.strip)
|
335
379
|
argument = parse_argument(arg)
|
380
|
+
|
336
381
|
{ until: { argument: argument } }
|
337
382
|
end
|
338
383
|
|
@@ -340,6 +385,7 @@ module KaiserRuby
|
|
340
385
|
words = line.split prepared_regexp(WHILE_KEYWORDS)
|
341
386
|
arg = consume_function_calls(words.last.strip)
|
342
387
|
argument = parse_argument(arg)
|
388
|
+
|
343
389
|
{ while: { argument: argument } }
|
344
390
|
end
|
345
391
|
|
@@ -347,13 +393,14 @@ module KaiserRuby
|
|
347
393
|
words = line.split prepared_regexp(FUNCTION_KEYWORDS)
|
348
394
|
funcname = parse_function_name(words.first.strip)
|
349
395
|
argument = parse_function_definition_arguments(words.last.strip)
|
396
|
+
|
350
397
|
{ function: { name: funcname, argument: argument } }
|
351
398
|
end
|
352
399
|
|
353
400
|
def consume_function_calls(string)
|
354
401
|
if string =~ prepared_regexp(FUNCTION_CALL_KEYWORDS)
|
355
402
|
words = string.split prepared_regexp(FUNCTION_RESTRICTED_KEYWORDS)
|
356
|
-
found_string = words.select { |w| w =~
|
403
|
+
found_string = words.select { |w| w =~ /\btaking\b/ }.first
|
357
404
|
@function_temp << found_string
|
358
405
|
string = string.gsub(found_string, " func_#{@function_temp.count - 1} ")
|
359
406
|
end
|
@@ -362,11 +409,9 @@ module KaiserRuby
|
|
362
409
|
end
|
363
410
|
|
364
411
|
def pass_function_calls(string)
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
return false
|
369
|
-
end
|
412
|
+
return false unless string.strip =~ /func_\d+\Z/
|
413
|
+
|
414
|
+
{ passed_function_call: string }
|
370
415
|
end
|
371
416
|
|
372
417
|
def parse_argument(string)
|
@@ -411,11 +456,9 @@ module KaiserRuby
|
|
411
456
|
|
412
457
|
def parse_poetic_number_value(string)
|
413
458
|
num = parse_literal_number(string)
|
414
|
-
if num
|
415
|
-
|
416
|
-
|
417
|
-
return { number_literal: string.strip }
|
418
|
-
end
|
459
|
+
return num if num
|
460
|
+
|
461
|
+
{ number_literal: string.strip }
|
419
462
|
end
|
420
463
|
|
421
464
|
def parse_logic_operation(string)
|
@@ -430,7 +473,7 @@ module KaiserRuby
|
|
430
473
|
return parse_nor(string)
|
431
474
|
end
|
432
475
|
|
433
|
-
|
476
|
+
false
|
434
477
|
end
|
435
478
|
|
436
479
|
def parse_and(string)
|
@@ -461,6 +504,7 @@ module KaiserRuby
|
|
461
504
|
|
462
505
|
def parse_not(string)
|
463
506
|
return false if string !~ /(?<!is )\bnot\b/i
|
507
|
+
|
464
508
|
words = string.split prepared_regexp(NOT_KEYWORDS)
|
465
509
|
argument = parse_argument(words.last.strip)
|
466
510
|
|
@@ -469,7 +513,6 @@ module KaiserRuby
|
|
469
513
|
|
470
514
|
def parse_comparison(string)
|
471
515
|
return false if string.strip.start_with?('"') && string.strip.strip.end_with?('"') && string.count('"') == 2
|
472
|
-
words = string.split(/\s/)
|
473
516
|
|
474
517
|
if string =~ prepared_regexp(GT_KEYWORDS)
|
475
518
|
return parse_gt(string)
|
@@ -485,7 +528,7 @@ module KaiserRuby
|
|
485
528
|
return parse_equality(string)
|
486
529
|
end
|
487
530
|
|
488
|
-
|
531
|
+
false
|
489
532
|
end
|
490
533
|
|
491
534
|
def parse_equality(string)
|
@@ -537,7 +580,7 @@ module KaiserRuby
|
|
537
580
|
end
|
538
581
|
|
539
582
|
def parse_variables(string)
|
540
|
-
words = string.split
|
583
|
+
words = string.split(/\s/)
|
541
584
|
words = words.map { |e| e.chars.select { |c| c =~ /[[:alnum:]]|\./ }.join }
|
542
585
|
string = words.join(' ')
|
543
586
|
|
@@ -547,9 +590,11 @@ module KaiserRuby
|
|
547
590
|
return parse_common_variable(string)
|
548
591
|
elsif matches_all?(words, /\A[[:upper:]]/) && string !~ prepared_regexp(RESERVED_KEYWORDS)
|
549
592
|
return parse_proper_variable(string)
|
593
|
+
elsif words.count == 1 && string !~ prepared_regexp(RESERVED_KEYWORDS)
|
594
|
+
return prase_simple_variable(string)
|
550
595
|
end
|
551
596
|
|
552
|
-
|
597
|
+
false
|
553
598
|
end
|
554
599
|
|
555
600
|
def parse_function_name(string)
|
@@ -561,14 +606,8 @@ module KaiserRuby
|
|
561
606
|
def parse_common_variable(string)
|
562
607
|
words = string.split(/\s/)
|
563
608
|
|
564
|
-
copied = words.dup
|
565
|
-
copied.shift
|
566
|
-
copied.each do |w|
|
567
|
-
raise SyntaxError, "invalid common variable name: #{string}:#{@lnum+1}" if w =~ /[[:upper:]]/
|
568
|
-
end
|
569
|
-
|
570
609
|
words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
|
571
|
-
{ variable_name: words.map
|
610
|
+
{ variable_name: words.map(&:downcase).join('_') }
|
572
611
|
end
|
573
612
|
|
574
613
|
def parse_proper_variable(string)
|
@@ -577,19 +616,24 @@ module KaiserRuby
|
|
577
616
|
copied = words.dup
|
578
617
|
copied.shift
|
579
618
|
copied.each do |w|
|
580
|
-
raise SyntaxError, "invalid proper variable name: #{string}:#{@lnum+1}" unless w =~ /\A[[:upper:]]/
|
619
|
+
raise SyntaxError, "invalid proper variable name: #{string}:#{@lnum + 1}" unless w =~ /\A[[:upper:]]/
|
581
620
|
end
|
582
621
|
|
583
622
|
words = words.map { |e| e.chars.select { |c| c =~ /[[:alpha:]]/ }.join }
|
584
|
-
{ variable_name: words.map
|
623
|
+
{ variable_name: words.map(&:downcase).join('_') }
|
585
624
|
end
|
586
625
|
|
587
626
|
def parse_pronoun
|
588
627
|
{ pronoun: nil }
|
589
628
|
end
|
590
629
|
|
630
|
+
def prase_simple_variable(string)
|
631
|
+
{ variable_name: string }
|
632
|
+
end
|
633
|
+
|
591
634
|
def parse_math_operations(string)
|
592
635
|
return false if string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2
|
636
|
+
|
593
637
|
words = string.split(/\s/)
|
594
638
|
|
595
639
|
if matches_any?(words, MULTIPLICATION_KEYWORDS)
|
@@ -602,7 +646,7 @@ module KaiserRuby
|
|
602
646
|
return parse_subtraction(string)
|
603
647
|
end
|
604
648
|
|
605
|
-
|
649
|
+
false
|
606
650
|
end
|
607
651
|
|
608
652
|
def parse_addition(string)
|
@@ -638,42 +682,37 @@ module KaiserRuby
|
|
638
682
|
end
|
639
683
|
|
640
684
|
def parse_literal_string(string)
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
return false
|
645
|
-
end
|
685
|
+
return false unless string.strip.start_with?('"') && string.strip.end_with?('"') && string.count('"') == 2
|
686
|
+
|
687
|
+
{ string: string }
|
646
688
|
end
|
647
689
|
|
648
690
|
def parse_literal_number(string)
|
649
691
|
num = Float(string) rescue string
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
return false
|
654
|
-
end
|
692
|
+
return false unless num.is_a?(Float)
|
693
|
+
|
694
|
+
{ number: num }
|
655
695
|
end
|
656
696
|
|
657
|
-
#private
|
697
|
+
# private
|
658
698
|
|
659
699
|
def add_to_tree(object)
|
660
700
|
object.extend(Hashie::Extensions::DeepLocate)
|
661
701
|
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
end
|
702
|
+
object[:current_scope] = @current_scope.last
|
703
|
+
object[:nesting] = @nesting
|
704
|
+
|
666
705
|
@tree << object
|
667
706
|
end
|
668
707
|
|
669
708
|
def prepared_regexp(array)
|
670
|
-
rxp = array.map { |a|
|
709
|
+
rxp = array.map { |a| tokenize_word(a) }.join('|')
|
671
710
|
Regexp.new(rxp, Regexp::IGNORECASE)
|
672
711
|
end
|
673
712
|
|
674
713
|
def prepared_capture(farr, sarr)
|
675
|
-
frxp = farr.map { |a|
|
676
|
-
srxp = sarr.map { |a|
|
714
|
+
frxp = farr.map { |a| tokenize_word(a) }.join('|')
|
715
|
+
srxp = sarr.map { |a| tokenize_word(a) }.join('|')
|
677
716
|
Regexp.new(frxp + '(.*?)' + srxp + '(.*)', Regexp::IGNORECASE)
|
678
717
|
end
|
679
718
|
|
@@ -691,22 +730,28 @@ module KaiserRuby
|
|
691
730
|
first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
|
692
731
|
second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }
|
693
732
|
|
694
|
-
second_idx
|
733
|
+
!second_idx.nil? && !first_idx.nil? && second_idx.to_i - first_idx.to_i == 1
|
695
734
|
end
|
696
735
|
|
697
736
|
def matches_first?(words, rxp)
|
698
|
-
words.index { |w| w =~ prepared_regexp(rxp) }
|
737
|
+
words.index { |w| w =~ prepared_regexp(rxp) }&.zero?
|
699
738
|
end
|
700
739
|
|
701
740
|
def matches_several_first?(line, rxp)
|
702
|
-
(line =~ prepared_regexp(rxp))
|
741
|
+
(line =~ prepared_regexp(rxp))&.zero?
|
703
742
|
end
|
704
743
|
|
705
744
|
def matches_separate?(words, first_rxp, second_rxp)
|
706
745
|
first_idx = words.index { |w| w =~ prepared_regexp(first_rxp) }
|
707
746
|
second_idx = words.index { |w| w =~ prepared_regexp(second_rxp) }
|
708
747
|
|
709
|
-
second_idx
|
748
|
+
!second_idx.nil? && !first_idx.nil? && second_idx.to_i > first_idx.to_i
|
749
|
+
end
|
750
|
+
|
751
|
+
def tokenize_word(word)
|
752
|
+
return '\B' + Regexp.escape(word) + '\B' if MATH_TOKENS.include?(word) # apparently ' + ' is not a word so word boundaries don't work
|
753
|
+
|
754
|
+
'\b' + word + '\b'
|
710
755
|
end
|
711
756
|
end
|
712
|
-
end
|
757
|
+
end
|