/** * Parse a string, cleaning up whitespace when we're done * @param string $str * @param integer $loc * @return array A two-item array of the string location where parsing stopped, and the MC_Token instance that matches the grammar conditions */ public function parsePart($str, $loc) { list($loc, $tok) = $this->_parse($str, $loc); $char = $str[$loc++]; while ($char && MC_Parser::isWhitespace($char)) { $char = $str[$loc++]; } --$loc; return array($loc, $tok); }
public function _parse($str, $loc) { if (!$this->caseless) { $match = strpos($str, $this->search, $loc); } else { $match = stripos($str, $this->search, $loc); } if ($match !== $loc) { throw new MC_Parser_ParseError('Expected: ' . $this->search, $str, $loc); } $loc += strlen($this->search); if ($this->fullword && $loc < strlen($str) && !MC_Parser::isWhitespace($str[$loc])) { throw new MC_Parser_ParseError('Expected: ' . $this->search, $str, $loc); } return array($loc, $this->token($this->search)); }
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; }