Skip to content

Instantly share code, notes, and snippets.

@jonatas
Created March 4, 2018 23:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonatas/0f2d1740ad520212f6a02ce0f1f89b9c to your computer and use it in GitHub Desktop.
Save jonatas/0f2d1740ad520212f6a02ce0f1f89b9c to your computer and use it in GitHub Desktop.
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