Skip to content

Instantly share code, notes, and snippets.

@kevinswiber
Last active August 29, 2015 14:08
Show Gist options
  • Select an option

  • Save kevinswiber/50d0169bd2eaed82e7e8 to your computer and use it in GitHub Desktop.

Select an option

Save kevinswiber/50d0169bd2eaed82e7e8 to your computer and use it in GitHub Desktop.
Generate Web version of CaQL
number [+-]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?
name [A-Za-z0-9_-][\.A-Za-z0-9_-]*
escaped_name \[(?:(?!\])[^\\]|\\.)*\]
%options case-insensitive
%x sel fltr asgn loc ordby
%%
<INITIAL,sel,fltr,asgn,loc,ordby>\s+ /* skip whitespace */
"select" {
this.begin('sel');
return 'SELECT';
}
<sel,fltr,loc,ordby>"," return ','
<sel>"*" return '*'
<sel>"as" return 'AS'
<INITIAL,sel>"where" {
this.popState(); /* in: INITIAL */
this.begin('fltr');
return 'WHERE';
}
<sel,fltr>"order by" {
this.popState(); /* in: INITIAL */
this.begin('ordby');
return 'ORDERBY';
}
<sel>{name} return 'NAME'
<sel>{escaped_name} {
yytext = yytext.slice(1, -1);
return 'NAME';
}
<fltr>"and" return 'AND'
<fltr>"or" return 'OR'
<fltr>"not" return 'NOT'
<fltr>"&&" return 'AND'
<fltr>"||" return 'OR'
<sel,fltr,ordby><<EOF>> {
this.popState(); /* in: INITIAL */
return 'EOF';
}
<fltr>{name} {
this.begin('asgn');
return 'NAME';
}
<fltr>{escaped_name} {
this.begin('asgn');
yytext = yytext.slice(1, -1);
return 'NAME';
}
<fltr>"(" return '('
<fltr>")" return ')'
<asgn>"eq"|"gt"|"lt"|"gte"|"lte" return 'COMPARISON'
<asgn>">="|"<=" {
yytext = letterify(yytext);
return 'COMPARISON';
}
<asgn>"="|">"|"<" {
yytext = letterify(yytext);
return 'COMPARISON';
}
<asgn>"not" return 'NOT'
<asgn>"contains" return 'CONTAINS'
<asgn>"like" return 'LIKE'
<asgn>"within" {
this.begin('loc');
return 'WITHIN';
}
<asgn>(\@\w+) {
this.popState(); /* in fltr */
return 'PARAM';
}
<asgn>(["'])(?:(?!\1)[^\\]|\\.)*\1 {
this.popState(); /* in: fltr */
return 'STRING';
}
<asgn>{number} {
this.popState(); /* in: fltr */
return 'NUMBER';
}
<asgn>"true" {
this.popState(); /* in fltr */
yytext = true;
return 'TRUE';
}
<asgn>"false" {
this.popState(); /* in fltr */
yytext = false;
return 'FALSE';
}
<asgn>"null" {
this.popState(); /* in fltr */
yytext = null;
return 'NULL';
}
<loc>{number} return 'NUMBER'
<loc>"of" return 'OF'
<loc>"and" {
this.popState(); /* in: asgn */
this.popState(); /* in: fltr */
return 'AND';
}
<loc>"or" {
this.popState(); /* in: asgn */
this.popState(); /* in: fltr */
return 'OR';
}
<loc>"order by" {
this.popState(); /* in: asgn */
this.popState(); /* in: fltr */
this.begin('ordby');
return 'ORDERBY';
}
<ordby>"ASC" return 'ASC'
<ordby>"DESC" return 'DESC'
<ordby>{name} return 'NAME'
<ordby>{escaped_name} {
yytext = yytext.slice(1, -1);
return 'NAME';
}
<INITIAL,loc><<EOF>> return 'EOF'
%%
function letterify(op) {
switch(op) {
case '=' : op = 'eq'; break;
case '>' : op = 'gt'; break;
case '<' : op = 'lt'; break;
case '>=' : op = 'gte'; break;
case '<=' : op = 'lte'; break;
}
return op;
}
%left OR
%left AND
%left NOT
%start root
%%
root
: select_statement EOF
{ return $1; }
;
select_statement
: SELECT fields where_optional orderby_optional
{ $$ = new yy.SelectStatementNode($2, $3, $4); }
| where_clause orderby_optional
{ $$ = new yy.SelectStatementNode(new yy.FieldListNode('*'), $1, $2); }
;
fields
: column_list
| '*'
{ $$ = new yy.FieldListNode(new yy.ColumnNode('*')); }
;
column_list
: column_field
{ $$ = new yy.FieldListNode($1); }
| column_list ',' column_field
{ $1.push($3); $$ = $1; }
;
column_field
: NAME
{ $$ = new yy.ColumnNode($1) }
| NAME AS NAME
{ $$ = new yy.ColumnNode($1, $3) }
;
column
: NAME
;
where_optional
: /* empty */
| where_clause
;
where_clause
: WHERE filter
{ $$ = new yy.FilterNode($2); }
;
conjunction
: filter AND filter
{ $$ = new yy.ConjunctionNode($1, $3); }
;
disjunction
: filter OR filter
{ $$ = new yy.DisjunctionNode($1, $3); }
;
filter
: predicate
| conjunction
| disjunction
| '(' filter ')'
{ $$ = $2 }
;
predicate
: comparison_predicate
| contains_predicate
| location_predicate
| like_predicate
;
comparison_predicate
: column COMPARISON literal
{ $$ = new yy.ComparisonPredicateNode($1, $2, $3); }
| NOT comparison_predicate
{ $$ = $2.negate(); }
;
contains_predicate
: column CONTAINS STRING
{ $$ = new yy.ContainsPredicateNode($1, $3); }
| column CONTAINS PARAM
{ $$ = new yy.ContainsPredicateNode($1, $3); }
| NOT contains_predicate
{ $$ = $2.negate(); }
;
like_predicate
: column LIKE STRING
{ $$ = new yy.LikePredicateNode($1, $3); }
| column LIKE PARAM
{ $$ = new yy.LikePredicateNode($1, $3); }
| column NOT LIKE STRING
{ $$ = new yy.LikePredicateNode($1, $4).negate(); }
| column NOT LIKE PARAM
{ $$ = new yy.LikePredicateNode($1, $4).negate(); }
;
location_predicate
: column WITHIN location
{ $$ = new yy.LocationPredicateNode($1, $3); }
| NOT location_predicate
{ $$ = $2.negate(); }
;
location
: NUMBER OF coordinates
{ $$ = new yy.LocationNode($1, $3); }
;
coordinates
: NUMBER ',' NUMBER
{ $$ = new yy.CoordinatesNode($1, $3); }
;
orderby_optional
: /* empty */
| orderby_clause
;
orderby_clause
: ORDERBY sort_list
{ $$ = new yy.OrderByNode($2); }
;
sort_list
: sort_expression
{ $$ = new yy.SortListNode($1); }
| sort_list ',' sort_expression
{ $1.push($3); $$ = $1; }
;
sort_expression
: NAME direction
{ $$ = new yy.SortNode($1, $2); }
;
direction
: /* empty */
| ASC
| DESC
;
boolean
: TRUE
| FALSE
;
literal
: NUMBER
| STRING
| PARAM
| boolean
| NULL
;
var file = require('read-file');
var ebnfParser = require('ebnf-parser');
var lexParser = require('lex-parser');
var Generator = require('jison').Generator;
var grammarFile = '../calypso.yy';
var lexFile = '../calypso.l';
var grammar = ebnfParser.parse(file.readFileSync(grammarFile));
grammar.lex = lexParser.parse(file.readFileSync(lexFile));
var opts = {
moduleName: 'caql',
moduleType: 'js'
};
var generator = new Generator(grammar, opts);
var parserSource = generator.generate(opts);
process.stdout.write(parserSource);
@kevinswiber
Copy link
Copy Markdown
Author

Set:

caql.yy = CaqlAst;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment