/** * Extracts the name of affected column. * * @param Parser $parser The instance that requests parsing. * @param TokensList $list The list of tokens to be parsed. * @param Token $token The token that is being parsed. * * @return void */ public function after(Parser $parser, TokensList $list, Token $token) { // Parsing operation. ++$list->idx; $this->options->merge(OptionsFragment::parse($parser, $list, static::$OPTIONS)); // Parsing affected field. ++$list->idx; $this->altered = FieldFragment::parse($parser, $list); // parent::after($parser, $list, $token); }
/** * Parses the additional options fragment at the end. * * @param Parser $parser The instance that requests parsing. * @param TokensList $list The list of tokens to be parsed. * @param Token $token The token that is being parsed. * * @return void */ public function after(Parser $parser, TokensList $list, Token $token) { // [some options] is going to be parsed first. // // There is a parser specified in `Parser::$KEYWORD_PARSERS` // which parses the name of the tables. // // Finally, we parse here [some more options] and that's all. ++$list->idx; $this->options->merge(OptionsFragment::parse($parser, $list, static::$OPTIONS)); }
/** * @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 ReferencesKeyword */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new ReferencesKeyword(); /** * The state of the parser. * * Below are the states of the parser. * * 0 ----------------------[ table ]---------------------> 1 * * 1 ---------------------[ columns ]--------------------> 2 * * 2 ---------------------[ options ]--------------------> -1 * * @var int */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * @var 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; } else { if ($state === 1) { $ret->columns = ArrayFragment::parse($parser, $list)->values; $state = 2; } else { if ($state === 2) { $ret->options = OptionsFragment::parse($parser, $list, static::$REFERENCES_OPTIONS); ++$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 DataTypeFragment[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = new DataTypeFragment(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -------------------[ data type ]--------------------> 1 * * 1 ----------------[ size and options ]----------------> 2 * * @var int */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * @var 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 === '(') { $size = ArrayFragment::parse($parser, $list); ++$list->idx; $ret->parameters = $ret->name === 'ENUM' || $ret->name === 'SET' ? $size->raw : $size->values; } $ret->options = OptionsFragment::parse($parser, $list, static::$DATA_TYPE_OPTIONS); ++$list->idx; break; } } if (empty($ret->name)) { return null; } --$list->idx; return $ret; }
/** * Parses the statements defined by the tokens list. * * @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) { $this->first = $list->idx; /** * Whether options were parsed or not. * For statements that do not have any options this is set to `true` by * default. * @var bool */ $parsedOptions = isset(static::$OPTIONS) ? false : true; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * @var Token */ $token = $list->tokens[$list->idx]; // End of statement. if ($token->type === Token::TYPE_DELIMITER) { break; } // Only keywords are relevant here. Other parts of the query are // processed in the functions below. if ($token->type !== Token::TYPE_KEYWORD) { continue; } /** * The name of the class that is used for parsing. * @var string */ $class = null; /** * The name of the field where the result of the parsing is stored. * @var string */ $field = null; /** * Parser's options. * @var array */ $options = array(); if (!empty(Parser::$KEYWORD_PARSERS[$token->value])) { $class = Parser::$KEYWORD_PARSERS[$token->value]['class']; $field = Parser::$KEYWORD_PARSERS[$token->value]['field']; if (!empty(Parser::$KEYWORD_PARSERS[$token->value]['options'])) { $options = Parser::$KEYWORD_PARSERS[$token->value]['options']; } } if (!empty(Parser::$STATEMENT_PARSERS[$token->value])) { if (!$parsedOptions) { ++$list->idx; // Skipping keyword. $this->options = OptionsFragment::parse($parser, $list, static::$OPTIONS); $parsedOptions = true; } } else { if ($class === null) { // There is no parser for this keyword and isn't the beggining // of a statement (so no options) either. $parser->error('Unrecognized keyword "' . $token->value . '".', $token); continue; } } $this->before($parser, $list, $token); // Parsing this keyword. if ($class !== null) { ++$list->idx; // Skipping keyword. $this->{$field} = $class::parse($parser, $list, $options); } $this->after($parser, $list, $token); } $this->last = $list->idx--; // Go back to last used token. }
/** * Parsing the `CREATE` statement. * * @param Parser $parser The instance that requests parsing. * @param TokensList $list The list of tokens to be parsed. * @param Token $token The token that is being parsed. * * @return void */ public function before(Parser $parser, TokensList $list, Token $token) { ++$list->idx; $this->name = CreateDefFragment::parse($parser, $list); if ($this->options->has('TABLE')) { ++$list->idx; $this->fields = FieldDefFragment::parse($parser, $list); ++$list->idx; $this->entityOptions = OptionsFragment::parse($parser, $list, CreateDefFragment::$TABLE_OPTIONS); } elseif ($this->options->has('PROCEDURE') || $this->options->has('FUNCTION')) { ++$list->idx; $this->parameters = ParamDefFragment::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 = DataTypeFragment::parse($parser, $list); } } ++$list->idx; $this->entityOptions = OptionsFragment::parse($parser, $list, CreateDefFragment::$FUNC_OPTIONS); ++$list->idx; $this->body = array(); for (; $list->idx < $list->count; ++$list->idx) { $token = $list->tokens[$list->idx]; $this->body[] = $token; if ($token->type === Token::TYPE_KEYWORD && $token->value === 'END') { break; } } } }
/** * @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 FieldDefFragment[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); $expr = new FieldDefFragment(); /** * The state of the parser. * * Below are the states of the parser. * * 0 -----------------------[ ( ]------------------------> 1 * * 1 --------------------[ CONSTRAINT ]------------------> 1 * 1 -----------------------[ key ]----------------------> 2 * 1 -------------[ constraint / column name ]-----------> 2 * * 2 --------------------[ data type ]-------------------> 3 * * 3 ---------------------[ options ]--------------------> 4 * * 4 --------------------[ REFERENCES ]------------------> 4 * * 5 ------------------------[ , ]-----------------------> 1 * 5 ------------------------[ ) ]-----------------------> -1 * * @var int */ $state = 0; for (; $list->idx < $list->count; ++$list->idx) { /** * Token parsed at this moment. * @var 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_OPERATOR && $token->value === '(') { $state = 1; } } elseif ($state === 1) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'CONSTRAINT') { $expr->isConstraint = true; } elseif ($token->type === Token::TYPE_KEYWORD && $token->flags & Token::FLAG_KEYWORD_KEY) { $expr->key = KeyFragment::parse($parser, $list); $state = 4; } else { $expr->name = $token->value; if (!$expr->isConstraint) { $state = 2; } } } elseif ($state === 2) { $expr->type = DataTypeFragment::parse($parser, $list); $state = 3; } elseif ($state === 3) { $expr->options = OptionsFragment::parse($parser, $list, static::$FIELD_OPTIONS); $state = 4; } elseif ($state === 4) { if ($token->type === Token::TYPE_KEYWORD && $token->value === 'REFERENCES') { ++$list->idx; // Skipping keyword 'REFERENCES'. $expr->references = ReferencesKeyword::parse($parser, $list); } else { --$list->idx; } $state = 5; } else { if ($state === 5) { if (!empty($expr->type) || !empty($expr->key)) { $ret[] = $expr; } $expr = new FieldDefFragment(); if ($token->value === ',') { $state = 1; continue; } elseif ($token->value === ')') { ++$list->idx; break; } } } } // Last iteration was not saved. if (!empty($expr->type) || !empty($expr->key)) { $ret[] = $expr; } --$list->idx; return $ret; }