public function testVisParser()
 {
     $p = new MC_Parser();
     $ident = $p->oneOf($p->word($p->alphas() . '_', $p->alphanums() . '_'), $p->quotedString('`'));
     $literal = $p->oneOf($p->number()->name('number'), $p->quotedString()->name('string'), $p->boolean('lower')->name('boolean'), $p->set($p->keyword('date', true), $p->quotedString())->name('date'), $p->set($p->keyword('timeofday', true), $p->quotedString())->name('time'), $p->set($p->oneOf($p->keyword('datetime', true), $p->keyword('timestamp', true)), $p->quotedString())->name('datetime'));
     $function = $p->set($p->oneOf($p->literal('min', true), $p->literal('max', true), $p->literal('count', true), $p->literal('avg', true), $p->literal('sum', true))->name('func_name'), $p->literal('(')->suppress(), $ident, $p->literal(')')->suppress())->name('function');
     $select = $p->set($p->keyword('select', true), $p->oneOf($p->keyword('*'), $p->delimitedList($p->oneOf($function, $ident))))->name('select');
     $from = $p->set($p->keyword('from', true), $ident)->name('from');
     $comparison = $p->oneOf($p->literal('<'), $p->literal('<='), $p->literal('>'), $p->literal('>='), $p->literal('='), $p->literal('!='), $p->literal('<>'))->name('operator');
     $expr = $p->recursive();
     $value = $p->oneOf($ident, $literal);
     $cond = $p->oneOf($p->set($value, $comparison, $value), $p->set($value, $p->keyword('is', true), $p->keyword('null', true)), $p->set($value, $p->keyword('is', true), $p->keyword('not', true), $p->keyword('null', true)), $p->set($p->literal('('), $expr, $p->literal(')')));
     $andor = $p->oneOf($p->keyword('and', true), $p->keyword('or', true));
     $expr->replace($p->set($cond, $p->zeroOrMore($p->set($andor, $expr))))->name('where_expr');
     $where = $p->set($p->keyword('where', true), $expr)->name('where');
     $groupby = $p->set($p->keyword('group', true), $p->keyword('by', true), $p->delimitedList($ident));
     $pivot = $p->set($p->keyword('pivot', true), $p->delimitedList($ident));
     $limit = $p->set($p->keyword('limit', true), $p->word($p->nums()))->name('limit');
     $offset = $p->set($p->keyword('offset', true), $p->word($p->nums()))->name('offset');
     $label = $p->set($p->keyword('label', true), $p->delimitedList($p->set($ident, $p->quotedString())));
     $format = $p->set($p->keyword('format', true), $p->delimitedList($p->set($ident, $p->quotedString())));
     $options = $p->set($p->keyword('options', true), $p->delimitedList($p->word($p->alphas() . '_')));
     $query = $p->set($p->optional($select), $p->optional($from), $p->optional($where), $p->optional($groupby), $p->optional($pivot), $p->optional($limit), $p->optional($offset), $p->optional($label), $p->optional($format), $p->optional($options));
     $result = $query->parse('');
     $this->assertEquals(array(), $result->getValues(), 'empty query');
     $result = $query->parse('select *');
     $this->assertEquals(array('select', '*'), $result->getValues(), 'simple select *');
     $result = $query->parse('from something');
     $this->assertEquals(array('from', 'something'), $result->getValues(), 'select-less from');
     $result = $query->parse('select one, two from table');
     $this->assertEquals(array('select', 'one', 'two', 'from', 'table'), $result->getValues(), 'select list');
     $result = $query->parse('select max(one) from table where (two>1.5 and (three < 4)) group by one');
     $this->assertEquals(array('select', 'max', 'one', 'from', 'table', 'where', '(', 'two', '>', '1.5', 'and', '(', 'three', '<', '4', ')', ')', 'group', 'by', 'one'), $result->getValues());
 }
 /**
  * Use MC_Parser to generate a grammar that matches the query language specified here: http://code.google.com/apis/visualization/documentation/querylanguage.html
  * @return MC_Parser_Def the grammar for the query language
  */
 public function getGrammar()
 {
     $p = new MC_Parser();
     $ident = $p->oneOf($p->word($p->alphas() . '_', $p->alphanums() . '_'), $p->quotedString('`'));
     $literal = $p->oneOf($p->number()->name('number'), $p->quotedString()->name('string'), $p->boolean('lower')->name('boolean'), $p->set($p->keyword('date', true), $p->quotedString())->name('date'), $p->set($p->keyword('timeofday', true), $p->quotedString())->name('time'), $p->set($p->oneOf($p->keyword('datetime', true), $p->keyword('timestamp', true)), $p->quotedString())->name('datetime'));
     $function = $p->set($p->oneOf($p->literal('min', true), $p->literal('max', true), $p->literal('count', true), $p->literal('avg', true), $p->literal('sum', true))->name('func_name'), $p->literal('(')->suppress(), $ident, $p->literal(')')->suppress())->name('function');
     $select = $p->set($p->keyword('select', true), $p->oneOf($p->keyword('*'), $p->delimitedList($p->oneOf($function, $ident))))->name('select');
     $from = $p->set($p->keyword('from', true), $ident)->name('from');
     $comparison = $p->oneOf($p->literal('<'), $p->literal('<='), $p->literal('>'), $p->literal('>='), $p->literal('='), $p->literal('!='), $p->literal('<>'))->name('operator');
     $expr = $p->recursive();
     $value = $p->oneOf($literal, $ident->name('where_field'));
     $cond = $p->oneOf($p->set($value, $comparison, $value), $p->set($value, $p->set($p->keyword('is', true), $p->literal('null', true))->name('isnull')), $p->set($value, $p->set($p->keyword('is', true), $p->keyword('not', true), $p->literal('null', true))->name('notnull')), $p->set($p->literal('(')->name('sep'), $expr, $p->literal(')')->name('sep')));
     $andor = $p->oneOf($p->keyword('and', true), $p->keyword('or', true))->name('andor_sep');
     $expr->replace($p->set($cond, $p->zeroOrMore($p->set($andor, $expr))));
     $where = $p->set($p->keyword('where', true), $expr)->name('where');
     $groupby = $p->set($p->keyword('group', true), $p->keyword('by', true), $p->delimitedList($ident))->name('groupby');
     $pivot = $p->set($p->keyword('pivot', true), $p->delimitedList($ident))->name('pivot');
     $orderby_clause = $p->set($ident, $p->optional($p->oneOf($p->literal('asc', true), $p->literal('desc', true))));
     $orderby = $p->set($p->keyword('order', true), $p->keyword('by', true), $p->delimitedList($orderby_clause))->name('orderby');
     $limit = $p->set($p->keyword('limit', true), $p->word($p->nums()))->name('limit');
     $offset = $p->set($p->keyword('offset', true), $p->word($p->nums()))->name('offset');
     $label = $p->set($p->keyword('label', true), $p->delimitedList($p->set($ident, $p->quotedString())))->name('label');
     $format = $p->set($p->keyword('format', true), $p->delimitedList($p->set($ident, $p->quotedString())))->name('format');
     $options = $p->set($p->keyword('options', true), $p->delimitedList($p->word($p->alphas() . '_')))->name('options');
     $query = $p->set($p->optional($select), $p->optional($from), $p->optional($where), $p->optional($groupby), $p->optional($pivot), $p->optional($orderby), $p->optional($limit), $p->optional($offset), $p->optional($label), $p->optional($format), $p->optional($options));
     return $query;
 }