mtg_search_parser 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +1 -0
- data/lib/mtg_search_parser.rb +92 -0
- data/lib/mtg_search_parser/lexer.rb +80 -0
- data/lib/mtg_search_parser/nodes/and.rb +6 -0
- data/lib/mtg_search_parser/nodes/left_paren.rb +6 -0
- data/lib/mtg_search_parser/nodes/node.rb +13 -0
- data/lib/mtg_search_parser/nodes/not.rb +6 -0
- data/lib/mtg_search_parser/nodes/or.rb +6 -0
- data/lib/mtg_search_parser/nodes/query.rb +19 -0
- data/lib/mtg_search_parser/nodes/right_paren.rb +6 -0
- data/lib/mtg_search_parser/not_group.rb +13 -0
- data/lib/mtg_search_parser/or_group.rb +13 -0
- data/lib/mtg_search_parser/parsed/any_color.rb +7 -0
- data/lib/mtg_search_parser/parsed/artist.rb +7 -0
- data/lib/mtg_search_parser/parsed/banned.rb +7 -0
- data/lib/mtg_search_parser/parsed/base.rb +15 -0
- data/lib/mtg_search_parser/parsed/card_type.rb +7 -0
- data/lib/mtg_search_parser/parsed/color_identity.rb +7 -0
- data/lib/mtg_search_parser/parsed/edition.rb +7 -0
- data/lib/mtg_search_parser/parsed/exact_color.rb +7 -0
- data/lib/mtg_search_parser/parsed/exact_name.rb +7 -0
- data/lib/mtg_search_parser/parsed/language.rb +7 -0
- data/lib/mtg_search_parser/parsed/legal.rb +7 -0
- data/lib/mtg_search_parser/parsed/mana_cost.rb +18 -0
- data/lib/mtg_search_parser/parsed/name.rb +7 -0
- data/lib/mtg_search_parser/parsed/power_tough_compare.rb +20 -0
- data/lib/mtg_search_parser/parsed/quality.rb +7 -0
- data/lib/mtg_search_parser/parsed/rarity.rb +7 -0
- data/lib/mtg_search_parser/parsed/release_year.rb +18 -0
- data/lib/mtg_search_parser/parsed/rules_text.rb +7 -0
- data/lib/mtg_search_parser/parser.rb +60 -0
- data/lib/mtg_search_parser/version.rb +3 -0
- data/mtg_search_parser.gemspec +23 -0
- data/spec/lexer_spec.rb +94 -0
- data/spec/mtg_search_parser_spec.rb +62 -0
- data/spec/parser_spec.rb +256 -0
- data/spec/spec_helper.rb +80 -0
- metadata +132 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module MtgSearchParser
|
2
|
+
module Parsed
|
3
|
+
class ManaCost < Base
|
4
|
+
attr_reader :operator, :mana_cost
|
5
|
+
|
6
|
+
def initialize(operator, mana_cost)
|
7
|
+
@operator = operator
|
8
|
+
@mana_cost = mana_cost
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(o)
|
12
|
+
o.class == self.class &&
|
13
|
+
o.operator == operator &&
|
14
|
+
o.mana_cost == mana_cost
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MtgSearchParser
|
2
|
+
module Parsed
|
3
|
+
class PowerToughCompare < Base
|
4
|
+
attr_reader :left, :op, :right
|
5
|
+
|
6
|
+
def initialize(left, op, right)
|
7
|
+
@left = left
|
8
|
+
@op = op
|
9
|
+
@right = right
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(o)
|
13
|
+
o.class == self.class &&
|
14
|
+
o.left == left &&
|
15
|
+
o.op == op &&
|
16
|
+
o.right == right
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MtgSearchParser
|
2
|
+
module Parsed
|
3
|
+
class ReleaseYear < Base
|
4
|
+
attr_reader :operator, :year
|
5
|
+
|
6
|
+
def initialize(operator, year)
|
7
|
+
@operator = operator
|
8
|
+
@year = year
|
9
|
+
end
|
10
|
+
|
11
|
+
def ==(o)
|
12
|
+
o.class == self.class &&
|
13
|
+
o.operator == operator &&
|
14
|
+
o.year == year
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module MtgSearchParser
|
2
|
+
class Parser
|
3
|
+
def parse(contents)
|
4
|
+
case contents
|
5
|
+
|
6
|
+
when /\Ac!([wubrgmlc]+)\z/i
|
7
|
+
MtgSearchParser::Parsed::ExactColor.new($1)
|
8
|
+
|
9
|
+
when /\Ac:([wubrgmlc]+)\z/i
|
10
|
+
MtgSearchParser::Parsed::AnyColor.new($1)
|
11
|
+
|
12
|
+
when /\Aci:([wubrgmlc]+)\z/i
|
13
|
+
MtgSearchParser::Parsed::ColorIdentity.new($1)
|
14
|
+
|
15
|
+
when /\At:"([^"]+)"\z/i, /\At:(.+)\z/i
|
16
|
+
MtgSearchParser::Parsed::CardType.new($1)
|
17
|
+
|
18
|
+
when /\Ao:"?([^"]+)"?\z/i
|
19
|
+
MtgSearchParser::Parsed::RulesText.new($1)
|
20
|
+
|
21
|
+
when /\Amana(=|>=|<=|>|<)(.+)\z/i
|
22
|
+
MtgSearchParser::Parsed::ManaCost.new($1, $2)
|
23
|
+
|
24
|
+
when /\A(pow|tou|cmc)(=|>=|<=|>|<)(pow|tou|cmc|\*|[0-9]+)\z/i
|
25
|
+
MtgSearchParser::Parsed::PowerToughCompare.new($1, $2, $3)
|
26
|
+
|
27
|
+
when /\Ar:(mythic|uncommon|common|rare|promo)\z/i
|
28
|
+
MtgSearchParser::Parsed::Rarity.new($1)
|
29
|
+
|
30
|
+
when /\Aa:"(.+)"\z/i, /\Aa:(.+)\z/i
|
31
|
+
MtgSearchParser::Parsed::Artist.new($1)
|
32
|
+
|
33
|
+
when /\Af:(standard|block|extended|vintage|classic|legacy|modern|commander)\z/i
|
34
|
+
MtgSearchParser::Parsed::Legal.new($1)
|
35
|
+
|
36
|
+
when /\Abanned:(standard|block|extended|vintage|classic|legacy|modern|commander)\z/i
|
37
|
+
MtgSearchParser::Parsed::Banned.new($1)
|
38
|
+
|
39
|
+
when /\Ae:(.+)\z/i
|
40
|
+
MtgSearchParser::Parsed::Edition.new($1)
|
41
|
+
|
42
|
+
when /\Al:(.+)\z/i
|
43
|
+
MtgSearchParser::Parsed::Language.new($1)
|
44
|
+
|
45
|
+
when /\Ayear(=|>=|<=|>|<)(.+)\z/i
|
46
|
+
MtgSearchParser::Parsed::ReleaseYear.new($1, $2)
|
47
|
+
|
48
|
+
when /\Ais:(.+)\z/i
|
49
|
+
MtgSearchParser::Parsed::Quality.new($1)
|
50
|
+
|
51
|
+
when /\A!(.+)\z/
|
52
|
+
MtgSearchParser::Parsed::ExactName.new($1)
|
53
|
+
when /\A"([^"]+)"\z/
|
54
|
+
MtgSearchParser::Parsed::Name.new($1)
|
55
|
+
else
|
56
|
+
MtgSearchParser::Parsed::Name.new(contents)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mtg_search_parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "mtg_search_parser"
|
8
|
+
spec.version = MtgSearchParser::VERSION
|
9
|
+
spec.authors = ["Donald Plummer"]
|
10
|
+
spec.email = ["donald.plummer@gmail.com"]
|
11
|
+
spec.summary = %q{A ruby implementation of a MTG search string parser.}
|
12
|
+
spec.homepage = "https://github.com/dplummer/mtg_search_parser"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
end
|
data/spec/lexer_spec.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module MtgSearchParser
|
4
|
+
describe Lexer do
|
5
|
+
it "lexes a word" do
|
6
|
+
expect(subject.lex("word")).to eq([Nodes::Query.new("word")])
|
7
|
+
end
|
8
|
+
|
9
|
+
it "splits two words" do
|
10
|
+
expect(subject.lex("word foo")).
|
11
|
+
to eq([Nodes::Query.new("word"), Nodes::Query.new("foo")])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "keeps characters in words" do
|
15
|
+
expect(subject.lex("foo-bar cat")).
|
16
|
+
to eq([Nodes::Query.new("foo-bar"), Nodes::Query.new("cat")])
|
17
|
+
end
|
18
|
+
|
19
|
+
it "allows apostrophes" do
|
20
|
+
expect(subject.lex("foo's bar")).
|
21
|
+
to eq([Nodes::Query.new("foo's"), Nodes::Query.new("bar")])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "keeps : in the word" do
|
25
|
+
expect(subject.lex("foo:bar cat")).
|
26
|
+
to eq([Nodes::Query.new("foo:bar"), Nodes::Query.new("cat")])
|
27
|
+
end
|
28
|
+
|
29
|
+
it "keeps ! in the word" do
|
30
|
+
expect(subject.lex("foo!bar cat")).
|
31
|
+
to eq([Nodes::Query.new("foo!bar"), Nodes::Query.new("cat")])
|
32
|
+
end
|
33
|
+
|
34
|
+
it "keeps it all together if the word starts with a bang" do
|
35
|
+
expect(subject.lex("!foo bar baz")).
|
36
|
+
to eq([Nodes::Query.new("!foo bar baz")])
|
37
|
+
end
|
38
|
+
|
39
|
+
it "keeps tokens together with quotes" do
|
40
|
+
expect(subject.lex('"foo bar" baz')).
|
41
|
+
to eq([Nodes::Query.new('"foo bar"'), Nodes::Query.new("baz")])
|
42
|
+
end
|
43
|
+
|
44
|
+
it "allows quotes in the middle of the term" do
|
45
|
+
expect(subject.lex('o:"foo bar" baz')).
|
46
|
+
to eq([Nodes::Query.new('o:"foo bar"'), Nodes::Query.new("baz")])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "recognizes left and right parens" do
|
50
|
+
expect(subject.lex("(foo)")).
|
51
|
+
to eq([Nodes::LeftParen.new, Nodes::Query.new("foo"), Nodes::RightParen.new])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "recognizes left and right parens ignoring spaces" do
|
55
|
+
expect(subject.lex(" ( foo ) ")).
|
56
|
+
to eq([Nodes::LeftParen.new, Nodes::Query.new("foo"), Nodes::RightParen.new])
|
57
|
+
end
|
58
|
+
|
59
|
+
it "parses AND as an AndNode" do
|
60
|
+
expect(subject.lex("foo AND bar")).
|
61
|
+
to eq([Nodes::Query.new("foo"), Nodes::And.new, Nodes::Query.new("bar")])
|
62
|
+
end
|
63
|
+
|
64
|
+
it "parses and as an AndNode" do
|
65
|
+
expect(subject.lex("foo and bar")).
|
66
|
+
to eq([Nodes::Query.new("foo"), Nodes::And.new, Nodes::Query.new("bar")])
|
67
|
+
end
|
68
|
+
|
69
|
+
it "parses OR as an OrNode" do
|
70
|
+
expect(subject.lex("foo OR bar")).
|
71
|
+
to eq([Nodes::Query.new("foo"), Nodes::Or.new, Nodes::Query.new("bar")])
|
72
|
+
end
|
73
|
+
|
74
|
+
it "parses or as an OrNode" do
|
75
|
+
expect(subject.lex("foo or bar")).
|
76
|
+
to eq([Nodes::Query.new("foo"), Nodes::Or.new, Nodes::Query.new("bar")])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "parses NOT as an NotNode" do
|
80
|
+
expect(subject.lex("foo NOT bar")).
|
81
|
+
to eq([Nodes::Query.new("foo"), Nodes::Not.new, Nodes::Query.new("bar")])
|
82
|
+
end
|
83
|
+
|
84
|
+
it "parses not as an NotNode" do
|
85
|
+
expect(subject.lex("foo not bar")).
|
86
|
+
to eq([Nodes::Query.new("foo"), Nodes::Not.new, Nodes::Query.new("bar")])
|
87
|
+
end
|
88
|
+
|
89
|
+
it "parses - as an NotNode" do
|
90
|
+
expect(subject.lex("foo -bar")).
|
91
|
+
to eq([Nodes::Query.new("foo"), Nodes::Not.new, Nodes::Query.new("bar")])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MtgSearchParser do
|
4
|
+
describe ".parse" do
|
5
|
+
it "parses a big long string of lots of tokens" do
|
6
|
+
expect(MtgSearchParser.parse("\"Foo Bar\" o:Rule t:Type c:WU mana>=3G "\
|
7
|
+
"pow<=10 tou=5 cmc>2 f:Modern a:\"Hank Hill\" "\
|
8
|
+
"e:avr year=1999")).
|
9
|
+
to eq([
|
10
|
+
MtgSearchParser::Parsed::Name.new("Foo Bar"),
|
11
|
+
MtgSearchParser::Parsed::RulesText.new("Rule"),
|
12
|
+
MtgSearchParser::Parsed::CardType.new("Type"),
|
13
|
+
MtgSearchParser::Parsed::AnyColor.new("WU"),
|
14
|
+
MtgSearchParser::Parsed::ManaCost.new(">=", "3G"),
|
15
|
+
MtgSearchParser::Parsed::PowerToughCompare.new("pow", "<=", "10"),
|
16
|
+
MtgSearchParser::Parsed::PowerToughCompare.new("tou", "=", "5"),
|
17
|
+
MtgSearchParser::Parsed::PowerToughCompare.new("cmc", ">", "2"),
|
18
|
+
MtgSearchParser::Parsed::Legal.new("Modern"),
|
19
|
+
MtgSearchParser::Parsed::Artist.new("Hank Hill"),
|
20
|
+
MtgSearchParser::Parsed::Edition.new("avr"),
|
21
|
+
MtgSearchParser::Parsed::ReleaseYear.new("=", "1999"),
|
22
|
+
])
|
23
|
+
end
|
24
|
+
|
25
|
+
it "groups items OR'd" do
|
26
|
+
expect(MtgSearchParser.parse("t:angel OR t:demon")).
|
27
|
+
to eq([
|
28
|
+
MtgSearchParser::OrGroup.new([
|
29
|
+
MtgSearchParser::Parsed::CardType.new("angel"),
|
30
|
+
MtgSearchParser::Parsed::CardType.new("demon"),
|
31
|
+
])
|
32
|
+
])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "not" do
|
36
|
+
expect(MtgSearchParser.parse("NOT t:angel -t:demon")).
|
37
|
+
to eq([
|
38
|
+
MtgSearchParser::NotGroup.new(
|
39
|
+
[MtgSearchParser::Parsed::CardType.new("angel")]),
|
40
|
+
MtgSearchParser::NotGroup.new(
|
41
|
+
[MtgSearchParser::Parsed::CardType.new("demon")])
|
42
|
+
])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "parenthesis" do
|
46
|
+
expect(MtgSearchParser.parse("t:demon NOT (t:legendary t:artifact)")).
|
47
|
+
to eq([
|
48
|
+
MtgSearchParser::Parsed::CardType.new("demon"),
|
49
|
+
MtgSearchParser::NotGroup.new([MtgSearchParser::Parsed::CardType.new("legendary"),
|
50
|
+
MtgSearchParser::Parsed::CardType.new("artifact")]),
|
51
|
+
])
|
52
|
+
end
|
53
|
+
|
54
|
+
it "strips unneccessary parenthesis" do
|
55
|
+
expect(MtgSearchParser.parse("(((t:legendary t:artifact)))")).
|
56
|
+
to eq([
|
57
|
+
MtgSearchParser::Parsed::CardType.new("legendary"),
|
58
|
+
MtgSearchParser::Parsed::CardType.new("artifact")
|
59
|
+
])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|