A mini-language in Ruby

Our Metaphor

Our Metaphor

John Underwood

  • CS at UT
  • Love languages
  • Love helping people
  • Live coding

Motivation

  • Small, but growing in complexity
  • or
  • Needs to be consistent

The Parts

The Parts

[
    { field: "start_at", data: "= 01/12/2016 || = 01/27/2016" },
    { field: "amount",   data: "> 200" }
]
[
    { field: "start_at", data: "= 01/12/2016 || = 01/27/2016"},
    { field: "amount",   data: "> 200"}
]
[
    { field: "start_at", data: { or: [{ op: :eq, data: "01/12/2016"}, 
                                      { op: :eq, data: "01/27/2016"}] } } },
    { field: "amount",   data: { { op: :gt, data: "200"} }
]
[
    { field: "start_at", data: { or: [{ op: :eq, data: "01/12/2016"}, 
                                      { op: :eq, data: "01/27/2016"}] } },
    { field: "amount",   data: { { op: :gt, data: "200"} }
]
[
    { field: "start_at", data: { or: [{ op: :eq, data: "01/12/2016"}, 
                                      { op: :eq, data: "01/27/2016"}] } },
    { field: "amount",   data: { { op: :gt, data: "200"} }
]
SELECT * 
    FROM scholarships 
WHERE 
    (start_at = '2016-01-12' OR start_at = '2016-01-27') AND amount > 200
[
    { field: "start_at", data: { or: [{ op: :eq, data: "01/12/2016"}, 
                                      { op: :eq, data: "01/27/2016"}] } },
    { field: "amount",   data: { { op: :gt, data: "200"} }
]
table = Scholarship.arel_table

Scholarship.where(
    table.grouping(
        table[:start_at].eq('01/12/2016').or((table[:start_at].eq('01/27/2016'))
    ).and(table[:amount].gt(200))))
[
    { field: "start_at", data: { or: [{ op: :eq, data: "01/12/2016"}, 
                                      { op: :eq, data: "01/27/2016"}] } },
    { field: "amount",   data: { { op: :gt, data: "200"} }
]
{ and: [{ or: [{ from: "01/12/2016 06:00:00 UTC", to: "01/13/2016 05:59:59 UTC", 
                 include_lower: true, include_upper: true },
               { from: "01/27/2016 06:00:00 UTC", to: "01/28/2016 05:59:59 UTC", 
                 include_lower: true, include_upper: true } ] },
        { range: {"amount" => {gt: "200" } } } ] }

The Source Code

Source Code

|| - Logical OR
=  - Includes
<  - Less than
<= - Less than or equal to
>  - Greater than
>= - Greater than or equal to

The AST

{ op: :eq, data: '4'}

{ op: :gt, data: '01/01/2015' }

{ or: [
        { op: :eq, data: '4'},
        { op: :eq, data: '6'}
      ]
}

The Parser

The SQL Compiler

The AREL Compiler

The AREL API

class Scholarship < ActiveRecord::Base
end

1 arel_table = Scholarship.arel_table

2 Scholarship.where(start_at: Date.today) # Active Record

3 clause = arel_table[:start_at].eq(Date.today) # AREL

4 Scholarship.where(clause) # Using AREL

5 before_today_clause = arel_table[:start_at].lte(Date.today) # Less than

6 amount_clause = arel_table[:amount].gt(500) # Greater than

7 combined_clause = before_today_clause.or(amount_clause) # combine

8 Scholarship.where(combined_clause) # Using AREL

The ElasticSearch Compiler

The ElasticSearch API

{ "term" => { "amount" => "2000" } }

{ "term" => { "start_at" => { "gt" => "2016-01-13 06:00:00 UTC" } } }

{ "term" => { "amount" => { "lt" => "5000" } } }

{ "or" => [
    { "term" => { "amount" => { "lt" => "5000" } } },
    { "term" => { "start_at" => { "gt" => "2016-01-13 06:00:00 UTC" } } }
  ]
}

Tying it all together

Uh, now what

Where did we go from here?

Parser Grammer

expression ::= <comparison> (|| <comparison>)*
comparison ::= (<>|<|<=|>|>=|=)? <value>
value      ::= number | date

Parsers

  • rexical and racc
  • Treetop
  • parselet

Compiling

  • AREL uses visitor pattern
  • LLVM

Compilers

  • Create Your Own Programming Language
  • Compilers: Principles, Techniques, and Tools
  • PL (http://lisperator.net/pltut/) (in javascript)
  • MRI, rubinius, etc

The return of why

Thank you

Made with Slides.com