public static parse ( |
||
$parser | The parser that serves as context. | |
$list | The list of tokens that are being parsed. | |
$options | array | Parameters for parsing. |
return |
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return IntoKeyword */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new IntoKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ name ]----------------------> 1 * 0 ---------------------[ OUTFILE ]---------------------> 2 * * 1 ------------------------[ ( ]------------------------> (END) * * 2 ---------------------[ filename ]--------------------> 1 * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED) { if ($state === 0 && $token->value === 'OUTFILE') { $ret->type = 'OUTFILE'; $state = 2; continue; } // No other keyword is expected. break; } if ($state === 0) { $ret->dest = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $ret->columns = ArrayObj::parse($parser, $list)->values; ++$list->idx; } break; } elseif ($state === 2) { $ret->dest = $token->value; ++$list->idx; break; } } --$list->idx; return $ret; }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return Reference */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new Reference(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ table ]---------------------> 1 * * 1 ---------------------[ columns ]--------------------> 2 * * 2 ---------------------[ options ]--------------------> (END) * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { $ret->table = $token->value; $state = 1; } elseif ($state === 1) { $ret->columns = ArrayObj::parse($parser, $list)->values; $state = 2; } elseif ($state === 2) { $ret->options = OptionsArray::parse($parser, $list, static::$REFERENCES_OPTIONS); ++$list->idx; break; } } --$list->idx; return $ret; }
/** * @param Parser $parser The instance that requests parsing. * @param TokensList $list The list of tokens to be parsed. * * @return void */ public function parse(Parser $parser, TokensList $list) { ++$list->idx; // Skipping `CREATE`. // Parsing options. $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); ++$list->idx; // Skipping last option. // Parsing the field name. $this->name = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); if (!isset($this->name) || $this->name === '') { $parser->error(__('The name of the entity was expected.'), $list->tokens[$list->idx]); } else { ++$list->idx; // Skipping field. } if ($this->options->has('DATABASE')) { $this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS); } elseif ($this->options->has('TABLE')) { $this->fields = CreateDefinition::parse($parser, $list); if (empty($this->fields)) { $parser->error(__('At least one column definition was expected.'), $list->tokens[$list->idx]); } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS); /** * The field that is being filled (`partitionBy` or * `subpartitionBy`). * * @var string $field */ $field = null; /** * The number of brackets. `false` means no bracket was found * previously. At least one bracket is required to validate the * expression. * * @var int|bool $brackets */ $brackets = false; /* * Handles partitions. */ for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping comments. if ($token->type === Token::TYPE_COMMENT) { continue; } if ($token->type === Token::TYPE_KEYWORD && $token->value === 'PARTITION BY') { $field = 'partitionBy'; $brackets = false; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITION BY') { $field = 'subpartitionBy'; $brackets = false; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'PARTITIONS') { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->partitionsNum = $token->value; } elseif ($token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITIONS') { $token = $list->getNextOfType(Token::TYPE_NUMBER); --$list->idx; // `getNextOfType` also advances one position. $this->subpartitionsNum = $token->value; } elseif (!empty($field)) { /* * Handling the content of `PARTITION BY` and `SUBPARTITION BY`. */ // Counting brackets. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { // This is used instead of `++$brackets` because, // initially, `$brackets` is `false` cannot be // incremented. $brackets = $brackets + 1; } elseif ($token->type === Token::TYPE_OPERATOR && $token->value === ')') { --$brackets; } // Building the expression used for partitioning. $this->{$field} .= $token->type === Token::TYPE_WHITESPACE ? ' ' : $token->token; // Last bracket was read, the expression ended. // Comparing with `0` and not `false`, because `false` means // that no bracket was found and at least one must is // required. if ($brackets === 0) { $this->{$field} = trim($this->{$field}); $field = null; } } elseif ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { if (!empty($this->partitionBy)) { $this->partitions = ArrayObj::parse($parser, $list, array('type' => 'SqlParser\\Components\\PartitionDefinition')); } break; } } } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $this->parameters = ParameterDefinition::parse($parser, $list); if ($this->options->has('FUNCTION')) { $token = $list->getNextOfType(Token::TYPE_KEYWORD); if ($token->value !== 'RETURNS') { $parser->error(__('A "RETURNS" keyword was expected.'), $token); } else { ++$list->idx; $this->return = DataType::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS); ++$list->idx; for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } elseif ($this->options->has('VIEW')) { $token = $list->getNext(); // Skipping whitespaces and comments. // Parsing columns list. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { --$list->idx; // getNext() also goes forward one field. $this->fields = ArrayObj::parse($parser, $list); ++$list->idx; // Skipping last token from the array. $list->getNext(); } // Parsing the `AS` keyword. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } elseif ($this->options->has('TRIGGER')) { // Parsing the time and the event. $this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); ++$list->idx; // Skipping `ON`. // Parsing the name of the table. $this->table = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); ++$list->idx; // Skipping `FOR EACH ROW`. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } else { for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return FunctionCall */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new FunctionCall(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ name ]-----------------------> 1 * * 1 --------------------[ parameters ]-------------------> (END) * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { $ret->name = $token->value; $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $ret->parameters = ArrayObj::parse($parser, $list); } break; } } return $ret; }
/** * @param Parser $parser The instance that requests parsing. * @param TokensList $list The list of tokens to be parsed. * * @return void */ public function parse(Parser $parser, TokensList $list) { ++$list->idx; // Skipping `CREATE`. // Parsing options. $this->options = OptionsArray::parse($parser, $list, static::$OPTIONS); ++$list->idx; // Skipping last option. // Parsing the field name. $this->name = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; // Skipping field. if ($this->options->has('DATABASE')) { $this->entityOptions = OptionsArray::parse($parser, $list, static::$DB_OPTIONS); } elseif ($this->options->has('TABLE')) { $this->fields = FieldDefinition::parse($parser, $list); ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$TABLE_OPTIONS); } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { $this->parameters = ParameterDefinition::parse($parser, $list); if ($this->options->has('FUNCTION')) { $token = $list->getNextOfType(Token::TYPE_KEYWORD); if ($token->value !== 'RETURNS') { $parser->error('\'RETURNS\' keyword was expected.', $token); } else { ++$list->idx; $this->return = DataType::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsArray::parse($parser, $list, static::$FUNC_OPTIONS); ++$list->idx; for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } else { if ($this->options->has('VIEW')) { $token = $list->getNext(); // Skipping whitespaces and comments. // Parsing columns list. if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { --$list->idx; // getNext() also goes forward one field. $this->fields = ArrayObj::parse($parser, $list); ++$list->idx; // Skipping last token from the array. $list->getNext(); } // Parsing the `AS` keyword. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; if ($token->type === Token::TYPE_DELIMITER) { break; } $this->body[] = $token; } } else { if ($this->options->has('TRIGGER')) { // Parsing the time and the event. $this->entityOptions = OptionsArray::parse($parser, $list, static::$TRIGGER_OPTIONS); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'ON'); ++$list->idx; // Skipping `ON`. // Parsing the name of the table. $this->table = Expression::parse($parser, $list, array('noAlias' => true, 'noBrackets' => true, 'skipColumn' => true)); ++$list->idx; $list->getNextOfTypeAndValue(Token::TYPE_KEYWORD, 'FOR EACH ROW'); ++$list->idx; // Skipping `FOR EACH ROW`. for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; } } } } }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return JoinKeyword[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new JoinKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ JOIN ]----------------------> 1 * * 1 -----------------------[ expr ]----------------------> 2 * * 2 ------------------------[ ON ]-----------------------> 3 * 2 -----------------------[ USING ]---------------------> 4 * * 3 --------------------[ conditions ]-------------------> 0 * * 4 ----------------------[ columns ]--------------------> 0 * * @var int $state */ $state = 0; // By design, the parser will parse first token after the keyword. // In this case, the keyword must be analyzed too, in order to determine // the type of this join. if ($list->idx > 0) { --$list->idx; } for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { if ($token->type === Token::TYPE_KEYWORD && !empty(static::$JOINS[$token->value])) { $expr->type = static::$JOINS[$token->value]; $state = 1; } else { break; } } elseif ($state === 1) { $expr->expr = Expression::parse($parser, $list, array('field' => 'table')); $state = 2; } elseif ($state === 2) { if ($token->type === Token::TYPE_KEYWORD) { if ($token->value === 'ON') { $state = 3; } elseif ($token->value === 'USING') { $state = 4; } } } elseif ($state === 3) { $expr->on = Condition::parse($parser, $list); $ret[] = $expr; $expr = new JoinKeyword(); $state = 0; } elseif ($state === 4) { $expr->using = ArrayObj::parse($parser, $list); $ret[] = $expr; $expr = new JoinKeyword(); $state = 0; } } if (!empty($expr->type)) { $ret[] = $expr; } --$list->idx; return $ret; }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return ArrayObj[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); /** * The number of values in each set. * * @var int $count */ $count = -1; /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ array ]----------------------> 1 * * 1 ------------------------[ , ]------------------------> 0 * 1 -----------------------[ else ]----------------------> (END) * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } // No keyword is expected. if ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_RESERVED) { break; } if ($state === 0) { if ($token->value === '(') { $arr = ArrayObj::parse($parser, $list, $options); $arrCount = count($arr->values); if ($count === -1) { $count = $arrCount; } elseif ($arrCount != $count) { $parser->error(sprintf(__('%1$d values were expected, but found %2$d.'), $count, $arrCount), $token); } $ret[] = $arr; $state = 1; } else { break; } } elseif ($state === 1) { if ($token->value === ',') { $state = 0; } else { break; } } } if ($state === 0) { $parser->error(__('An opening bracket followed by a set of values was expected.'), $list->tokens[$list->idx]); } --$list->idx; return $ret; }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return DataType */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new DataType(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -------------------[ data type ]--------------------> 1 * * 1 ----------------[ size and options ]----------------> 2 * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { $ret->name = strtoupper($token->value); if ($token->type !== Token::TYPE_KEYWORD || !($token->flags & Token::FLAG_KEYWORD_DATA_TYPE)) { $parser->error(__('Unrecognized data type.'), $token); } $state = 1; } elseif ($state === 1) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $parameters = ArrayObj::parse($parser, $list); ++$list->idx; $ret->parameters = $ret->name === 'ENUM' || $ret->name === 'SET' ? $parameters->raw : $parameters->values; } $ret->options = OptionsArray::parse($parser, $list, static::$DATA_TYPE_OPTIONS); ++$list->idx; break; } } if (empty($ret->name)) { return null; } --$list->idx; return $ret; }
/** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * * @return PartitionDefinition */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new PartitionDefinition(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -------------[ PARTITION | SUBPARTITION ]------------> 1 * * 1 -----------------------[ name ]----------------------> 2 * * 2 ----------------------[ VALUES ]---------------------> 3 * * 3 ---------------------[ LESS THAN ]-------------------> 4 * 3 ------------------------[ IN ]-----------------------> 4 * * 4 -----------------------[ expr ]----------------------> 5 * * 5 ----------------------[ options ]--------------------> 6 * * 6 ------------------[ subpartitions ]------------------> (END) * * @var int $state */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * * @var Token $token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Skipping whitespaces and comments. if ($token->type === Token::TYPE_WHITESPACE || $token->type === Token::TYPE_COMMENT) { continue; } if ($state === 0) { $ret->isSubpartition = $token->type === Token::TYPE_KEYWORD && $token->value === 'SUBPARTITION'; $state = 1; } elseif ($state === 1) { $ret->name = $token->value; $state = $ret->isSubpartition ? 5 : 2; } elseif ($state === 2) { $state = 3; } elseif ($state === 3) { $ret->type = $token->value; $state = 4; } elseif ($state === 4) { if ($token->value === 'MAXVALUE') { $ret->expr = $token->value; } else { $ret->expr = Expression::parse($parser, $list, array('bracketsDelimited' => true, 'noAlias' => true)); } $state = 5; } elseif ($state === 5) { $ret->options = OptionsArray::parse($parser, $list, static::$OPTIONS); $state = 6; } elseif ($state === 6) { if ($token->type === Token::TYPE_OPERATOR && $token->value === '(') { $ret->subpartitions = ArrayObj::parse($parser, $list, array('type' => 'SqlParser\\Components\\PartitionDefinition')); ++$list->idx; } break; } } --$list->idx; return $ret; }
public function testParseType() { $components = ArrayObj::parse(new Parser(), $this->getTokensList('(1 + 2, 3 + 4)'), array('type' => 'SqlParser\\Components\\Expression', 'typeOptions' => array('noBrackets' => true))); $this->assertEquals($components[0]->expr, '1 + 2'); $this->assertEquals($components[1]->expr, '3 + 4'); }