shunting_yard 0.1.0

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.
@@ -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: []