public function parse($str_gql, $arr_named_params = [])
    {
        // Record our input params
        foreach($arr_named_params as $obj_param) {
            if($obj_param->hasValue()) {
                $this->arr_named_params[$obj_param->getName()] = $obj_param->getValue();
            } else if ($obj_param->hasCursor()) {
                $this->arr_named_params[$obj_param->getName()] = $obj_param->getCursor();
            }
        }

        // Cleanup before we begin...
        $str_gql = trim($str_gql);

        // Ensure it's a 'SELECT *' query
        if(!preg_match('/^SELECT\s+\*\s+FROM\s+(.*)/i', $str_gql)) {
            throw new GQL("Sorry, only 'SELECT *' (full Entity) queries are currently supported by php-gds");
        }

        // Tokenize quoted items ** MUST BE FIRST **
        $str_gql = preg_replace_callback("/([`\"'])(?<quoted>.*?)(\\1)/", [$this, 'tokenizeQuoted'], $str_gql);

        // Kind
        $str_gql = preg_replace_callback('/^SELECT\s+\*\s+FROM\s+(?<kind>[^\s]*)/i', [$this, 'recordKind'], $str_gql, 1);

        // Offset
        $str_gql = preg_replace_callback('/OFFSET\s+(?<offset>[^\s]*)/i', [$this, 'recordOffset'], $str_gql, 1);

        // Limit
        $str_gql = preg_replace_callback('/LIMIT\s+(?<limit>[^\s]*)/i', [$this, 'recordLimit'], $str_gql, 1);

        // Order
        $str_gql = preg_replace_callback('/ORDER\s+BY\s+(?<order>.*)/i', [$this, 'recordOrder'], $str_gql, 1);

        // Where
        $str_gql = preg_replace_callback('/WHERE\s+(?<where>.*)/i', [$this, 'recordWhere'], $str_gql, 1);

        // Check we're done
        $str_gql = trim($str_gql);
        if(strlen($str_gql) > 0) {
            throw new GQL("Failed to parse entire query, remainder: [{$str_gql}]");
        }
    }