AӪk߭M̭uX v9+DaD#WO$*71549jvv§{<.[ZV~H<o XE:9<p,$zUֻ%"pyj[L>"76͇ڐs& كm<QwUo0S)? -a6JZJn٨]%sđ˻)&_)tыOHk:'qs}n}kί%]LΒ>C:pwva(oBWmL(Gsgu5Lf?PԜ{Z-N"lq6H5Ns?#J=ԎX$;B}3NzdqF?ʎvAi>Xyʯucv OIr5{sx\1v}3L܇!B(+JғReH+0;:dKەqz$IA"\gsQ8=>%j $?P>\,bvOƨS|peg)(ysrʅszZEvhyjF}x=F8i4*iyT]l3\WۘGW8ʁ@o3@4e\Y_WƏ/QZ)[dȮ=ey
7tBO]i620?eYH\J|`0w1+8xAF3MKb^CQ+Vm0	d DP$eO'镫,zL?h<YȘYzc4XauLEL@Fy>4^PEꗐtTa	MEFYϓjNc1*ƫ^vv Ne 䶠hޑfD5non.j|
')s7OQ*֐%GcrLRaU㼬˜bx=S@/s0*' c'#{/AhAht;+QňE*Nɶ1 !R.iHZ54nmOY1rݦ"jUKNۂ;EX!q)MEpCj-`WݪZa!B)fbRwh]e<;B	AIA6c~ a108ǵMޙH(ޠT~F+x+U)f	i hH8ۼ_	 <6(+(:^$sh:4s7#rKax*+${-t2/Ne! Zb$<)5~X#[x<gDRfO2ẋoIlr0?j<6w\aJ@~@soYwW̵nkoeOënY?$[koHg]Q1ubUNCuqh[_EMqmk(3M;˰R~\jAHY΢7ub̠׋;!C{3Nᾣ1#ɎzI.@GշpJnY󚆰R6u"Ca3Nᾣ1#Ɏz-:v_w	u?$3Nᾣ1#ɎzЭɅg?ۘ|ONzarϭ*; -3Nᾣ1#Ɏzu	_Mv ){h2mbkZfx9\}3Nᾣ1#ɎzS_E$\Dji	Ia763!3Nᾣ1#Ɏz ~5D;G޵{NHM+ӮghHQWʙA/sݼ)QxK@H6C{gR%vOXPI;ccVeV9,"Ezf@g[~+b!D-%qSmC{gR%Mq_a١pZz|xCBE u4Kr4}ŰVqJAGYv3p>@Vm4$I7ni
,q~ a%P0'ʶL
&ħh;n!
7:´dQ_,yYo9QJJ7ii4B]@63Ƭ E1XwxUI8~JUm'{r(;2r0ol(*iwb;
z@b\63Ͳs(7XMv,Cp٩0,W<+i@&hN<.lK5Mrp|XTB
`_ve72{k>yt賂/l69˝XbHEzf@ga,ucmƧTٶ:|'뿺VqB]sE6k~9@gaDsM~a8U:g-sxi>6t#ŐNnb#z#T\ #Z2Y
SE׆6ozKa-WJ6Dx1թ]È3T_dZ+2#
36`GNMi2}?\R<?6\:8Y^8:tbʼEAG{w/x@g(90FE'ۇ,P5Z khʍ$G}ԝ-F`&Ȓ'+X'Oxԥs[d
:Xe	r`I5|9cLํ⼽z=kq]Nۡ܁9͍mP%,KU#ecl:Xxlԛ+EZ_P 7]n>vvk2uQgٰKhcp8|j
>Q;Srjb I;ǒGWMՏ,C)kk雰)8iY?Nuh8OFCo
SdW)ls0/"Hseb40Z@5b[jHHnC~[(" %J߃NDFl0jʀU-̀}G
DgQnm>	=yݐX{=Ċ Ge#nI %J߃NDFlֲ0=w9>SI8p(Pey	،gRJiy,'x<^0QY k1m!wz++6ߝ'I2%ruփiC>icIm15LlK낟5j#:V˪3#DuW(\Sɚ\@:FHF#<N ,Ѷ^kYN]Ƶ0Oכ|AsfY`J{"d1`~c#)DLa&lE*!M3z&QKLÇS]\J)YpCE}#4j[SDl2mƒ;FIԊJ6vso^ykt^ԁ+N@tm%4~QgA	q4y|[M+]!m
;Wng/BQLbL@y.R֧RiD<cOPabRvSqTb`\^ĭU10}d5(urw>}a}ϣ΍8Ftӎ<IT`UՖNQN~N(ãhNƅZ[]h~&	ډ$Bޜ*,!L'JKޕsD\]dxG,s){OgWG.ÂQ Nu]----------------> 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 ------------------------[ ) ]-----------------------> 6 (-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;
                } else {
                    $parser->error(
                        __('An opening bracket was expected.'),
                        $token
                    );
                    break;
                }
            } 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 = Key::parse($parser, $list);
                    $state = 4;
                } elseif ($token->type === Token::TYPE_SYMBOL || $token->type === Token::TYPE_NONE) {
                    $expr->name = $token->value;
                    if (!$expr->isConstraint) {
                        $state = 2;
                    }
                } elseif ($token->type === Token::TYPE_KEYWORD) {
                    if ($token->flags & Token::FLAG_KEYWORD_RESERVED) {
                        // Reserved keywords can't be used
                        // as field names without backquotes
                        $parser->error(
                            __('A symbol name was expected! '
                                . 'A reserved keyword can not be used '
                                . 'as a column name without backquotes.'
                            ),
                            $token
                        );

                        return $ret;
                    } else {
                        // Non-reserved keywords are allowed without backquotes
                        $expr->name = $token->value;
                        $state = 2;
                    }
                } else {
                    $parser->error(
                        __('A symbol name was expected!'),
                        $token
                    );

                    return $ret;
                }
            } elseif ($state === 2) {
                $expr->type = DataType::parse($parser, $list);
                $state = 3;
            } elseif ($state === 3) {
                $expr->options = OptionsArray::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 = Reference::parse($parser, $list);
                } else {
                    --$list->idx;
                }
                $state = 5;
            } elseif ($state === 5) {
                if ((!empty($expr->type)) || (!empty($expr->key))) {
                    $ret[] = $expr;
                }
                $expr = new self();
                if ($token->value === ',') {
                    $state = 1;
                } elseif ($token->value === ')') {
                    $state = 6;
                    ++$list->idx;
                    break;
                } else {
                    $parser->error(
                        __('A comma or a closing bracket was expected.'),
                        $token
                    );
                    $state = 0;
                    break;
                }
            }
        }

        // Last iteration was not saved.
        if ((!empty($expr->type)) || (!empty($expr->key))) {
            $ret[] = $expr;
        }

        if (($state !== 0) && ($state !== 6)) {
            $parser->error(
                __('A closing bracket was expected.'),
                $list->tokens[$list->idx - 1]
            );
        }

        --$list->idx;

        return $ret;
    }

    /**
     * @param CreateDefinition|CreateDefinition[] $component the component to be built
     * @param array                               $options   parameters for building
     *
     * @return string
     */
    public static function build($component, array $options = array())
    {
        if (is_array($component)) {
            return "(\n  " . implode(",\n  ", $component) . "\n)";
        } else {
            $tmp = '';

            if ($component->isConstraint) {
                $tmp .= 'CONSTRAINT ';
            }

            if ((isset($component->name)) && ($component->name !== '')) {
                $tmp .= Context::escape($component->name) . ' ';
            }

            if (!empty($component->type)) {
                $tmp .= DataType::build(
                    $component->type,
                    array('lowercase' => true)
                ) . ' ';
            }

            if (!empty($component->key)) {
                $tmp .= $component->key . ' ';
            }

            if (!empty($component->references)) {
                $tmp .= 'REFERENCES ' . $component->references . ' ';
            }

            $tmp .= $component->options;

            return trim($tmp);
        }
    }
}
