shunting_yard 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 825936faa9f1c872f460b25d5e988e418abe6b98a2bd167af2c83fe13b25bf03
4
+ data.tar.gz: 2c91cb879600cc2dc260475ae03d3f1d763d0173f77a9060f43f1dd7609a9029
5
+ SHA512:
6
+ metadata.gz: d4484b313d0106bb9279290fab1f86413701a673d0e4d2f43521f8f1cb95bb5fb62feb773f1d6e2abc921fc0711939ebca3ae8b9e6f2225c0cbdfe4d9114c504
7
+ data.tar.gz: ad729d6a29a484d3c3c77ba36e9080a3bf90c7f64d4ef8c021112d7817efd3732f0ce7d264431187ea73528b5f035d3820962d3f50867575043febea69fc7743
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+
13
+ .byebug_history
14
+ .vscode
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in shunting_yard.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "rspec", "~> 3.0"
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shunting_yard (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (11.1.3)
10
+ coderay (1.1.2)
11
+ debase (0.2.4.1)
12
+ debase-ruby_core_source (>= 0.10.2)
13
+ debase-ruby_core_source (0.10.9)
14
+ diff-lcs (1.3)
15
+ method_source (1.0.0)
16
+ pry (0.13.1)
17
+ coderay (~> 1.1)
18
+ method_source (~> 1.0)
19
+ pry-byebug (3.9.0)
20
+ byebug (~> 11.0)
21
+ pry (~> 0.13.0)
22
+ rake (12.3.2)
23
+ rspec (3.9.0)
24
+ rspec-core (~> 3.9.0)
25
+ rspec-expectations (~> 3.9.0)
26
+ rspec-mocks (~> 3.9.0)
27
+ rspec-core (3.9.2)
28
+ rspec-support (~> 3.9.3)
29
+ rspec-expectations (3.9.2)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.9.0)
32
+ rspec-mocks (3.9.1)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.9.0)
35
+ rspec-support (3.9.3)
36
+ ruby-debug-ide (0.7.2)
37
+ rake (>= 0.8.1)
38
+
39
+ PLATFORMS
40
+ ruby
41
+
42
+ DEPENDENCIES
43
+ debase
44
+ pry
45
+ pry-byebug
46
+ rake (~> 12.0)
47
+ rspec (~> 3.0)
48
+ ruby-debug-ide
49
+ shunting_yard!
50
+
51
+ BUNDLED WITH
52
+ 2.1.2
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Artem Rashev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,147 @@
1
+ # ShuntingYard
2
+
3
+ ## TL;DR Usage
4
+
5
+ ```ruby
6
+ parser = ShuntingYard::Parser.new
7
+
8
+ parser.add_pattern :space, /\s+/, -> (_) { nil }
9
+ parser.add_pattern :argument_separator, /\,/
10
+ parser.add_pattern :operator, /[\+\-\*\/\^]/
11
+ parser.add_pattern :parenthesis, /[\(\)]/
12
+ parser.add_pattern :function, /(?:min|max)/
13
+ parser.add_pattern :operand, /d+/, -> (lexeme) { Integer(lexeme) }
14
+
15
+ parser.add_operator "+", 0, :left, -> (left, right) { left + right }
16
+ parser.add_operator "-", 0, :left, -> (left, right) { left - right }
17
+ parser.add_operator "*", 1, :left, -> (left, right) { left * right }
18
+ parser.add_operator "/", 1, :left, -> (left, right) { left / right }
19
+ parser.add_operator "^", 2, :right, -> (left, right) { left ** right }
20
+
21
+ parser.add_function "min", -> (left, right) { [left, right].min }
22
+ parser.add_function "max", -> (left, right) { [left, right].max }
23
+
24
+ input = "min(max(3, 4 / 2) * 2 ^ 3, 25)"
25
+
26
+ parser.to_rpn(input).to_s #=> 3 4 2 / max 2 3 ^ * 25 min
27
+ parser.evaluate(input) #=> 24
28
+ ```
29
+
30
+ ## Lexer
31
+
32
+ `ShuntingYard::Lexer` is responsible for splitting source string into tokens.
33
+ To recognize each possible token it needs to know corresponding patterns represented as regular expressions.
34
+
35
+ If substring is matched to multiple patterns, **the longest match** wil be used.
36
+
37
+ After matching a token, its lexeme is evaluated with provided function. If function is not provided - it returns the lexeme itself.
38
+
39
+ Tokens values evaluated to `nil` will not be included into output sequence.
40
+
41
+ ```ruby
42
+ lexer = ShuntingYard::Lexer.new
43
+
44
+ lexer.add_pattern :operator, /\+/
45
+ lexer.add_pattern :space, /\s+/, -> (_) { nil }
46
+ lexer.add_pattern :operand, /\d+/, -> (lexeme) { Integer(lexeme) }
47
+
48
+ puts lexer.tokenize("3 + 5").inspect
49
+ ```
50
+
51
+ ```
52
+ [
53
+ #<struct ShuntingYard::Token name=:operand, lexeme="3", value=3>,
54
+ #<struct ShuntingYard::Token name=:operator, lexeme="+", value="+">,
55
+ #<struct ShuntingYard::Token name=:operand, lexeme="5", value=5>
56
+ ]
57
+ ```
58
+
59
+ First argument in `#add_pattern` is a token name. It can have any value since `Lexer` doesn't make assumptions about names.
60
+
61
+ ## Interpterer
62
+
63
+ Once we have token list, it needs to be converted to [Revese Polish Notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation) before evalutation and here Shunting Yard algorithm comes in.
64
+
65
+ ```ruby
66
+ ...
67
+
68
+ interpreter = ShuntingYard::Interpreter.new
69
+ interpreter.add_operator "+", 0, :left, -> (left, right) { left + right }
70
+
71
+ tokens = lexer.tokenize("3 + 5")
72
+ puts interpreter.to_rpn(tokens).inspect
73
+ ```
74
+
75
+ ```
76
+ [
77
+ #<struct ShuntingYard::Operand value=3>,
78
+ #<struct ShuntingYard::Operand value=5>,
79
+ #<struct ShuntingYard::Operator value="+", precedence=0, associativity=:left, evaluator=#<Proc:...>>
80
+ ]
81
+ ```
82
+
83
+ Interpreter defines **strict names list that are accepted in tokens**:
84
+
85
+ * `:operand` - arbitrary value, passed as argument operators and functions
86
+ * `:parenthesis` - currently interpreter accepts only "(" and ")" parentheses
87
+ * `:operator` - token value must match to one of registered in interpreter operators
88
+ * `:function` - token value must match to one of registered in interpreter functions
89
+ * `:argument_separator` - pattern that defines argument separation in functions (usually comma)
90
+
91
+ All other token types will not be recognized and interpreter throws `ShuntingYard::UnknownTokenError`.
92
+
93
+ ### Registering functions
94
+
95
+ `#add_function(name, evaluator)`
96
+
97
+ * `name` - function name that must match to corresponding token value
98
+ * `evaluator` - a function that accepts fixed number of arguments and returns single value
99
+
100
+ ### Registering operations
101
+
102
+ `#add_operator(name, precedence, associativity, evaluator)`
103
+
104
+ * `name` - function name that must match to corresponding token value
105
+ * `precedence` - operator precedence, can be any integer value
106
+ * `associativity` - [operator associativity](https://en.wikipedia.org/wiki/Operator_associativity), can be either `:left` or `:right`
107
+ * `evaluator` - a function that accepts fixed number of arguments and returns single value
108
+
109
+
110
+ ### Evaluators
111
+
112
+ Evaluators for operators and functions can be defined as `proc` or `lambda` with at least one argument.
113
+ **Default arguments are not allowed** because interpreter uses `#arity` method to get number of operands required by function / operator.
114
+
115
+ ```ruby
116
+ # Expected format
117
+ interpreter.add_operator "%", 0, :left, -> (left, right) { left % right }
118
+ interpreter.add_operator "~", 0, :left, proc { |left, right| left % right }
119
+
120
+ # Will not work
121
+ interpreter.add_operator "%", 0, :left, -> (left, right = 5) { left % right }
122
+ interpreter.add_function "max", -> (*args) { args.max }
123
+ ```
124
+
125
+ ## Parser
126
+
127
+ `ShuntingYard::Parser` is a proxy class for Lexer and Interpreter.
128
+
129
+ When you need to recognize and evaluate an expression in one go, Parser object is a single entry point for all actions.
130
+
131
+ ```ruby
132
+ parser = ShuntingYard::Parser.new
133
+
134
+ # Add patterns to parser's lexer
135
+ parser.add_pattern :space, /\s+/, -> (_) { nil }
136
+ parser.add_pattern :operator, /[\+\-]/
137
+ parser.add_pattern :operand, /d+/, -> (lexeme) { Integer(lexeme) }
138
+
139
+ # Add operators to parser's interpreter
140
+ parser.add_operator "+", 0, :left, -> (left, right) { left + right }
141
+ parser.add_operator "-", 0, :left, -> (left, right) { left - right }
142
+
143
+ input = "5 + 3 - 2"
144
+
145
+ # Tokenize, convert to RPN and evaluate the expression
146
+ parser.evaluate(input) #=> 6
147
+ ```
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,10 @@
1
+ require "shunting_yard/structs"
2
+ require "shunting_yard/errors"
3
+ require "shunting_yard/reverse_polish_notation"
4
+ require "shunting_yard/interpreter"
5
+ require "shunting_yard/lexer"
6
+ require "shunting_yard/parser"
7
+ require "shunting_yard/version"
8
+
9
+ module ShuntingYard
10
+ end
@@ -0,0 +1,45 @@
1
+ module ShuntingYard
2
+ class Error < StandardError; end
3
+
4
+ class InvalidArgumentsCountError < Error
5
+ def initialize
6
+ super "Invalid arguments count passed to one of functions or operators"
7
+ end
8
+ end
9
+
10
+ class MismatchedParenthesesError < Error
11
+ def initialize
12
+ super "Mismatched parentheses"
13
+ end
14
+ end
15
+
16
+ class UnknownTokenError < Error
17
+ def initialize(token, position)
18
+ super "Unknown token '#{token}' at position #{position}"
19
+ end
20
+ end
21
+
22
+ class UnknownOperatorError < Error
23
+ def initialize(token)
24
+ super "Unknown operator '#{token.lexeme}'"
25
+ end
26
+ end
27
+
28
+ class UnknownFunctionError < Error
29
+ def initialize(token)
30
+ super "Unknown function '#{token}'"
31
+ end
32
+ end
33
+
34
+ class UnknownParenthesisError < Error
35
+ def initialize(token)
36
+ super "Token '#{token}' is not a parenthesis"
37
+ end
38
+ end
39
+
40
+ class UnknownTokenTypeError < Error
41
+ def initialize(name)
42
+ super "Token '#{name}' is not defined"
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,132 @@
1
+ module ShuntingYard
2
+ class Interpreter
3
+ attr_accessor :functions
4
+ attr_accessor :operators
5
+
6
+ def initialize
7
+ @functions = []
8
+ @operators = []
9
+ end
10
+
11
+ def add_function(*args)
12
+ functions << Function.new(*args)
13
+ end
14
+
15
+ def add_operator(*args)
16
+ operators << Operator.new(*args)
17
+ end
18
+
19
+ def to_rpn(source_tokens)
20
+ tokens = source_tokens.dup
21
+ output = ReversePolishNotation.new
22
+ op_stack = []
23
+
24
+ while tokens.any?
25
+ current = match_token(tokens.shift)
26
+
27
+ case current
28
+ when ArgumentSeparator
29
+ while op_stack.any? &&
30
+ op_stack.last.class != Parenthesis
31
+ output << op_stack.pop
32
+ end
33
+ when Function
34
+ op_stack << current
35
+ when Parenthesis
36
+ case current.side
37
+ when :left
38
+ op_stack << current
39
+ when :right
40
+ while op_stack.last.class != Parenthesis
41
+ raise MismatchedParenthesesError if op_stack.empty?
42
+
43
+ output << op_stack.pop
44
+ end
45
+
46
+ op_stack.pop
47
+ end
48
+ when Operand
49
+ output << current
50
+ when Operator
51
+ while op_stack.any? &&
52
+ op_stack.last.class != Parenthesis &&
53
+ (op_stack.last.class == Function ||
54
+ op_stack.last.precedence > current.precedence ||
55
+ op_stack.last.precedence == current.precedence && current.associativity == :left)
56
+ output << op_stack.pop
57
+ end
58
+
59
+ op_stack << current
60
+ end
61
+ end
62
+
63
+ while op_stack.any?
64
+ current = op_stack.pop
65
+ raise MismatchedParenthesesError if current.class == Parenthesis
66
+
67
+ output << current
68
+ end
69
+
70
+ output
71
+ end
72
+
73
+ def evaluate(rpn_tokens)
74
+ rpn = rpn_tokens.dup
75
+ stack = []
76
+
77
+ while rpn.any?
78
+ current = rpn.shift
79
+
80
+ case current
81
+ when Function, Operator
82
+ arity = current.evaluator.arity
83
+ raise InvalidArgumentsCountError if stack.size < arity
84
+
85
+ operands = stack.pop(arity)
86
+ stack << current.evaluator.(*operands)
87
+ when Operand
88
+ stack << current.value
89
+ end
90
+ end
91
+
92
+ raise InvalidArgumentsCountError if stack.size > 1
93
+
94
+ stack[0]
95
+ end
96
+
97
+ private
98
+
99
+ def match_token(token)
100
+ matched =
101
+ case token.name
102
+ when :argument_separator
103
+ ArgumentSeparator.new(token.value)
104
+ when :function
105
+ function = functions.find { |f| f.value == token.value }
106
+ raise UnknownFunctionError, token.lexeme unless function
107
+
108
+ function
109
+ when :parenthesis
110
+ case token.value
111
+ when "("
112
+ Parenthesis.new(:left)
113
+ when ")"
114
+ Parenthesis.new(:right)
115
+ else
116
+ raise UnknownParenthesisError, token.lexeme
117
+ end
118
+ when :operand
119
+ Operand.new(token.value)
120
+ when :operator
121
+ operator = operators.find { |op| binding.pry if op.kind_of?(Array); op.value == token.value }
122
+ raise UnknownOperatorError, token unless operator
123
+
124
+ operator
125
+ else
126
+ raise UnknownTokenTypeError, token.name
127
+ end
128
+
129
+ matched
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,58 @@
1
+ require "strscan"
2
+
3
+ module ShuntingYard
4
+ class Lexer
5
+ SPACE_OR_EOL = /(\s|$)/.freeze
6
+
7
+ attr_accessor :patterns
8
+ attr_accessor :separator_pattern
9
+
10
+ def initialize
11
+ @patterns = []
12
+ @separator_pattern = SPACE_OR_EOL
13
+ end
14
+
15
+ def add_pattern(name, regex, evaluator = -> (lexeme) { lexeme })
16
+ @patterns << [name, regex, evaluator]
17
+ end
18
+
19
+ def tokenize(input)
20
+ sc = StringScanner.new(input)
21
+ matches = []
22
+
23
+ until sc.eos?
24
+ match = nil
25
+ last_match = nil
26
+ longest_match_size = 0
27
+
28
+ @patterns.each do |name, regex, evaluator|
29
+ match = sc.check(regex)
30
+ next if match.nil?
31
+
32
+ longest_match_size = [longest_match_size, match.size].max
33
+
34
+ value = evaluator.(match)
35
+ next if value.nil?
36
+
37
+ last_match = [name, match, value] if last_match.nil? || last_match[1].size < match.size
38
+ end
39
+
40
+ if longest_match_size == 0
41
+ unknown_token = sc.check_until(separator_pattern).sub(separator_pattern, "")
42
+ raise UnknownTokenError.new(unknown_token, sc.pos + 1)
43
+ end
44
+
45
+ sc.pos += longest_match_size
46
+ matches << build_token(last_match) unless last_match.nil?
47
+ end
48
+
49
+ matches
50
+ end
51
+
52
+ private
53
+
54
+ def build_token(args)
55
+ Token.new(*args)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,28 @@
1
+ require "forwardable"
2
+
3
+ module ShuntingYard
4
+ class Parser
5
+ extend Forwardable
6
+
7
+ attr_accessor :lexer
8
+ attr_accessor :interpreter
9
+
10
+ def initialize(lexer: nil, interpreter: nil)
11
+ @lexer = lexer || Lexer.new
12
+ @interpreter = interpreter || Interpreter.new
13
+ end
14
+
15
+ def_delegators :@lexer, :add_pattern, :separator_pattern, :separator_pattern=, :tokenize
16
+ def_delegators :@interpreter, :add_function, :add_operator
17
+
18
+ def evaluate(input)
19
+ rpn_tokens = to_rpn(input)
20
+ interpreter.evaluate(rpn_tokens)
21
+ end
22
+
23
+ def to_rpn(input)
24
+ tokens = tokenize(input)
25
+ interpreter.to_rpn(tokens)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module ShuntingYard
2
+ class ReversePolishNotation < Array
3
+ def to_s
4
+ self.map(&:value).join(" ")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module ShuntingYard
2
+ Token = Struct.new(:name, :lexeme, :value)
3
+
4
+ # Matched tokens
5
+ ArgumentSeparator = Struct.new(:value)
6
+ Function = Struct.new(:value, :evaluator)
7
+ Operand = Struct.new(:value)
8
+ Operator = Struct.new(:value, :precedence, :associativity, :evaluator)
9
+ Parenthesis = Struct.new(:side)
10
+ end
@@ -0,0 +1,3 @@
1
+ module ShuntingYard
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'lib/shunting_yard/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "shunting_yard"
5
+ spec.version = ShuntingYard::VERSION
6
+ spec.authors = ["Donkey Kong"]
7
+ spec.email = ["584951-highaf@users.noreply.gitlab.com"]
8
+
9
+ spec.summary = "ShuntingYard algorithm implementation"
10
+ spec.description = ""
11
+ spec.homepage = "https://gitlab.com/hodlhodl-public/shunting_yard"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://gitlab.com/hodlhodl-public/shunting_yard"
19
+
20
+ # Specify which files should be added to the gem when it is released.
21
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ end
25
+
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "debase"
31
+ spec.add_development_dependency "pry"
32
+ spec.add_development_dependency "pry-byebug"
33
+ spec.add_development_dependency "ruby-debug-ide"
34
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shunting_yard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Donkey Kong
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-07-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: debase
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ruby-debug-ide
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: ''
70
+ email:
71
+ - 584951-highaf@users.noreply.gitlab.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - Gemfile
79
+ - Gemfile.lock
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - lib/shunting_yard.rb
84
+ - lib/shunting_yard/errors.rb
85
+ - lib/shunting_yard/interpreter.rb
86
+ - lib/shunting_yard/lexer.rb
87
+ - lib/shunting_yard/parser.rb
88
+ - lib/shunting_yard/reverse_polish_notation.rb
89
+ - lib/shunting_yard/structs.rb
90
+ - lib/shunting_yard/version.rb
91
+ - shunting_yard.gemspec
92
+ homepage: https://gitlab.com/hodlhodl-public/shunting_yard
93
+ licenses:
94
+ - MIT
95
+ metadata:
96
+ allowed_push_host: https://rubygems.org
97
+ homepage_uri: https://gitlab.com/hodlhodl-public/shunting_yard
98
+ source_code_uri: https://gitlab.com/hodlhodl-public/shunting_yard
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 2.3.0
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubygems_version: 3.1.2
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: ShuntingYard algorithm implementation
118
+ test_files: []