Created
March 4, 2018 23:52
-
-
Save jonatas/0f2d1740ad520212f6a02ce0f1f89b9c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'json' | |
require 'pp' | |
Query = Struct.new(:attributes, :conditions) | |
Condition = Struct.new(:query, :operator, :arg) | |
class Parser | |
attr_reader :query | |
def initialize(sql) | |
@tokens = sql.downcase.scan(/[><,]|\w+|\d+\.\d+|\d+/) | |
@query = Query.new | |
@query.attributes = [] | |
@query.conditions = [] | |
end | |
def parse | |
case token = @tokens.shift | |
when 'select', ',' | |
@query.attributes << parse_until(',') until @tokens.empty? || @tokens.first == 'where' | |
parse | |
when 'where' | |
@query.conditions << parse_condition until @tokens.empty? || @tokens.first == 'order' | |
parse | |
when /\d+\.\d+/ | |
token.to_f | |
when /\d+/ | |
token.to_i | |
else | |
token | |
end | |
end | |
def parse_until(*stoppers, shift: true) | |
a = [] | |
a.push(parse) until @tokens.empty? || stoppers.include?(@tokens.first) | |
@tokens.shift if shift | |
a.compact | |
end | |
def parse_condition | |
Condition.new( | |
parse_until(*%w(> < =), shift: false), | |
@tokens.shift, | |
parse_until('order', 'and', shift: false).first | |
) | |
end | |
def filter(data) | |
data.select do |row| | |
@query.conditions.all? do |condition| | |
row.slice(*condition.query).send(condition.operator, condition.arg) | |
end | |
end | |
end | |
def select(json, from: @query.attributes) | |
if json.is_a?(Hash) | |
from.map{|attribute|json.slice(*attribute)} | |
else | |
json.map(&method(:select)) | |
end | |
end | |
def fetch(json) | |
filter(json).map(&method(:select)) | |
end | |
end | |
class Hash | |
def slice(*keys) | |
if keys.size == 1 | |
self[keys.first] | |
else | |
key, *deep = keys | |
unless has_key?(key) | |
fail "undefined #{key}. keys are: #{keys}" | |
end | |
self[key].slice(*deep) | |
end | |
end | |
end | |
# https://think.cs.vt.edu/corgis/json/airlines/airlines.json | |
data = JSON.parse(IO.read('airlines.json')) | |
query = <<~JSONQL | |
SELECT | |
airport.name, | |
statistics.flights.cancelled | |
WHERE | |
statistics.flights.cancelled > 1000 | |
JSONQL | |
parser = Parser.new(query) | |
parser.parse | |
pp parser.query | |
pp parser.fetch(data) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment