尝试按 OR 运算符对术语进行分组
Trying to group terms by OR operator
我正在尝试解析一个字符串,以便我可以轻松识别由“OR”分隔的术语。
我目前有以下规则和解析器 class 设置:
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word.maybe >> or_op >> word).repeat(1).as(:or_terms) }
rule(:clause) { (or_terms | term).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
目前我可以这样做:
Parser.parse_tree_for('wow -bob')
=> {:query=>[{:clause=>{:term=>"wow"@0}}]}
Parser.parse_tree_for('wow OR lol')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol"@0}}]}
Parser.parse_tree_for('wow OR lol OR omg')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol OR omg"@0}}]}
哪个工作正常,但理想情况下我想要一些可以单独给我这些条款但带有 or 标志的东西:{:query=>[{:clause=>{:term=>"wow",:or=>true}},{:clause=>{:term=>"lol",:or=>true},{:clause=>{:term=>"omg",:or=>true}}]}
这是应该用变压器来完成的事情吗?比如,只需在转换器中设置一个规则来执行 split(' OR ')
或者是否有更好的方法来设置我的规则?
您需要在每个要明确捕获的事物上添加一个 as
。
您的 or_term
逻辑有点古怪。总是把必需的东西放在第一位,然后是可选的东西。
试试这个...
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1).as(:word) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word >> (or_op >> word).repeat(0)).as(:or_terms) }
rule(:clause) { (term | or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
puts Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>[{:clause=>{:or_terms=>[{:word=>"wow"@0}, {:word=>"lol"@7}, {:word=>"omg"@14}]}}]}
puts Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:term=>{:word=>"wow"@0}}}]}
我在 word 中添加了 as
,因此它们总是被明确捕获。
最好先捕捉比你想要的更多的东西,然后再用变压器把它弄平。
假设您要将其扩展到涵盖 AND 以及……您会发现使 AND 和 OR 表达式成为必需将使运算符优先级更容易处理。
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (and_terms >> (or_op >> and_terms).repeat(0)).as(:or_terms) }
rule(:and_terms) { (term >> (and_op >> term).repeat()).as(:and_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
pp Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"@0}},
# {:and_terms=>{:term=>"lol"@7}},
# {:and_terms=>{:term=>"omg"@14}}]}}]}
pp Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:or_terms=>{:and_terms=>{:term=>"wow"@0}}}}]}
pp Parser.parse_tree_for('wow OR lol AND omg OR bob')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"@0}},
# {:and_terms=>[{:term=>"lol"@7}, {:term=>"omg"@15}]},
# {:and_terms=>{:term=>"bob"@22}}]}}]}
回答您的完整问题...在变形金刚中,您必须一次匹配整个哈希。要解决这个问题,您可以匹配 'subtree' 但这通常是一种 hack。
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (term >> (or_op >> term).repeat(0)).as(:or_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
class MyTransform < Parslet::Transform
rule(:term => simple(:t)) {t}
rule(:or_terms => sequence(:terms)){
terms.map{|t| {term:{word:t, or:true}}}
}
rule(:or_terms => simple(:cs)){ [{term:{word:cs}}] } # so a single hash looks like a list.
rule(:query => subtree(:cs)){ {:query => cs.map{|c| c[:clause]}.flatten.map{|c| {clause:c}}}}
end
pp MyTransform.new.apply(Parser.parse_tree_for('foo bar OR baz'))
这个例子输出:
{:query=>
[{:clause=>{:term=>{:word=>"foo"@0}}},
{:clause=>{:term=>{:word=>"bar"@4, :or=>true}}},
{:clause=>{:term=>{:word=>"baz"@11, :or=>true}}}]}
我正在使用所有表达式都是 or_terms 的事实......并捕捉到只有一个术语未将 or
设置为 true
的情况。
我还使用 or_terms 匹配来使单个术语也像一个集合一样……因此所有子句都映射到一个列表。然后在匹配子树时,我可以展平列表以获取所有术语并将它们再次包装在 'clause' 哈希中...... Yuk! ;)
我正在尝试解析一个字符串,以便我可以轻松识别由“OR”分隔的术语。
我目前有以下规则和解析器 class 设置:
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word.maybe >> or_op >> word).repeat(1).as(:or_terms) }
rule(:clause) { (or_terms | term).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
目前我可以这样做:
Parser.parse_tree_for('wow -bob')
=> {:query=>[{:clause=>{:term=>"wow"@0}}]}
Parser.parse_tree_for('wow OR lol')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol"@0}}]}
Parser.parse_tree_for('wow OR lol OR omg')
=> {:query=>[{:clause=>{:or_terms=>"wow OR lol OR omg"@0}}]}
哪个工作正常,但理想情况下我想要一些可以单独给我这些条款但带有 or 标志的东西:{:query=>[{:clause=>{:term=>"wow",:or=>true}},{:clause=>{:term=>"lol",:or=>true},{:clause=>{:term=>"omg",:or=>true}}]}
这是应该用变压器来完成的事情吗?比如,只需在转换器中设置一个规则来执行 split(' OR ')
或者是否有更好的方法来设置我的规则?
您需要在每个要明确捕获的事物上添加一个 as
。
您的 or_term
逻辑有点古怪。总是把必需的东西放在第一位,然后是可选的东西。
试试这个...
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1).as(:word) }
rule(:or_op) { space >> str('OR') >> space }
rule(:term) { word.as(:term) >> or_op.absent? }
rule(:or_terms) { (word >> (or_op >> word).repeat(0)).as(:or_terms) }
rule(:clause) { (term | or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
puts Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>[{:clause=>{:or_terms=>[{:word=>"wow"@0}, {:word=>"lol"@7}, {:word=>"omg"@14}]}}]}
puts Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:term=>{:word=>"wow"@0}}}]}
我在 word 中添加了 as
,因此它们总是被明确捕获。
最好先捕捉比你想要的更多的东西,然后再用变压器把它弄平。
假设您要将其扩展到涵盖 AND 以及……您会发现使 AND 和 OR 表达式成为必需将使运算符优先级更容易处理。
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (and_terms >> (or_op >> and_terms).repeat(0)).as(:or_terms) }
rule(:and_terms) { (term >> (and_op >> term).repeat()).as(:and_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
pp Parser.parse_tree_for('wow OR lol OR omg')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"@0}},
# {:and_terms=>{:term=>"lol"@7}},
# {:and_terms=>{:term=>"omg"@14}}]}}]}
pp Parser.parse_tree_for('wow')
# {:query=>[{:clause=>{:or_terms=>{:and_terms=>{:term=>"wow"@0}}}}]}
pp Parser.parse_tree_for('wow OR lol AND omg OR bob')
# {:query=>
# [{:clause=>
# {:or_terms=>
# [{:and_terms=>{:term=>"wow"@0}},
# {:and_terms=>[{:term=>"lol"@7}, {:term=>"omg"@15}]},
# {:and_terms=>{:term=>"bob"@22}}]}}]}
回答您的完整问题...在变形金刚中,您必须一次匹配整个哈希。要解决这个问题,您可以匹配 'subtree' 但这通常是一种 hack。
require 'parslet'
class Parser < Parslet::Parser
rule(:space) { str(' ').repeat(1) }
rule(:word) { match['^\s"'].repeat(1) }
rule(:or_op) { space >> str('OR') >> space }
rule(:and_op) { space >> str('AND') >> space }
rule(:term) { word.as(:term) }
rule(:or_terms) { (term >> (or_op >> term).repeat(0)).as(:or_terms) }
rule(:clause) { (or_terms).as(:clause) }
rule(:query) { (clause >> space.maybe).repeat.as(:query) }
root(:query)
def self.parse_tree_for(query)
new.parse(query)
end
end
class MyTransform < Parslet::Transform
rule(:term => simple(:t)) {t}
rule(:or_terms => sequence(:terms)){
terms.map{|t| {term:{word:t, or:true}}}
}
rule(:or_terms => simple(:cs)){ [{term:{word:cs}}] } # so a single hash looks like a list.
rule(:query => subtree(:cs)){ {:query => cs.map{|c| c[:clause]}.flatten.map{|c| {clause:c}}}}
end
pp MyTransform.new.apply(Parser.parse_tree_for('foo bar OR baz'))
这个例子输出:
{:query=>
[{:clause=>{:term=>{:word=>"foo"@0}}},
{:clause=>{:term=>{:word=>"bar"@4, :or=>true}}},
{:clause=>{:term=>{:word=>"baz"@11, :or=>true}}}]}
我正在使用所有表达式都是 or_terms 的事实......并捕捉到只有一个术语未将 or
设置为 true
的情况。
我还使用 or_terms 匹配来使单个术语也像一个集合一样……因此所有子句都映射到一个列表。然后在匹配子树时,我可以展平列表以获取所有术语并将它们再次包装在 'clause' 哈希中...... Yuk! ;)