/** * Recieves parse data from the context tree. Sends the * data on to the code parser, then to the renderer for * building the result string */ function addParseData($code, $context_name, array $data = array(), $complex = false) { // @todo [blocking 1.1.5] test this, esp. not passing back anything and passing back multiple // can use PHP code parser for this // @todo [blocking 1.1.9] since we are only looking for whitespace at start and end this can // be optimised if (GESHI_COMPLEX_NO == $complex) { $this->_addToParsedCode(array($code, $context_name, $data)); } elseif (GESHI_COMPLEX_PASSALL == $complex) { // Parse all at once $this->_addToParsedCode($this->_codeParser->parseToken($code, $context_name, $data)); } elseif (GESHI_COMPLEX_TOKENISE == $complex) { $matches = array(); preg_match_all('/^(\\s*)(.*?)(\\s*)$/si', $code, $matches); //echo 'START<br />'; //print_r($matches); if ($matches[1][0]) { $this->_addToParsedCode($this->_codeParser->parseToken($matches[1][0], $context_name, $data)); } if ('' != $matches[2][0]) { while ('' != $matches[2][0]) { $pos = geshi_get_position($matches[2][0], 'REGEX#(\\s+)#'); if (false !== $pos['pos']) { // Parse the token up to the whitespace //echo 'code: |' . substr($matches[2][0], 0, $pos['pos']) . '|<br />'; $this->_addToParsedCode($this->_codeParser->parseToken(substr($matches[2][0], 0, $pos['pos']), $context_name, $data)); // Parse the whitespace //echo 'ws: |' . substr($matches[2][0], $pos['pos'], $pos['len']) . '|<br />'; $this->_addToParsedCode($this->_codeParser->parseToken(substr($matches[2][0], $pos['pos'], $pos['len']), $context_name, $data)); // Trim what we just parsed $matches[2][0] = substr($matches[2][0], $pos['pos'] + $pos['len']); } else { // No more whitespace //echo 'no more whitespace: |' . $matches[2][0] . '<br />'; $this->_addToParsedCode($this->_codeParser->parseToken($matches[2][0], $context_name, $data)); break; } } } if ($matches[3][0]) { $this->_addToParsedCode($this->_codeParser->parseToken($matches[3][0], $context_name, $data)); } } // else wtf??? }
/** * Adds code detected as being in this context to the parse data */ function _addParseData($code, $first_char_of_next_context = '') { $parent_name = $this->_parentName; geshi_dbg('GeSHiPHPDoubleStringContext::_addParseData(' . substr($code, 0, 15) . '...)'); while (true) { $earliest_data = array('pos' => false, 'len' => 0); foreach ($this->_regexes as $regex) { $data = geshi_get_position($code, $regex, 0, false, true); // request table if (false != $data['pos'] && false === $earliest_data['pos'] || false !== $data['pos'] && ($data['pos'] < $earliest_data['pos'] || $data['pos'] == $earliest_data['pos'] && $data['len'] > $earliest_data['len'])) { $earliest_data = $data; } } if (false === $earliest_data['pos']) { // No more variables in this string break; } // bugfix: because we match a var, it might have been escaped. // so only do to -1 so we can catch slash if it has been $pos = $earliest_data['pos'] ? $earliest_data['pos'] - 1 : 0; $len = $earliest_data['pos'] ? $earliest_data['len'] + 1 : $earliest_data['len']; parent::_addParseData(substr($code, 0, $pos)); // Now the entire possible var is in: $possible_var = substr($code, $pos, $len); geshi_dbg('Found variable at position ' . $earliest_data['pos'] . '(' . $possible_var . ')'); // Check that the dollar sign that started this variable was not escaped //$first_part = str_replace('\\\\', '', substr($code, 0, $pos)); //if ('\\' == substr($first_part, -1)) { // If \\ before var and { is not next character after that... if ('\\' == substr($possible_var, 0, 1) && '{' != substr($possible_var, 1, 1)) { // This variable has been escaped, so add the escaped dollar sign // as the correct context, and the rest of the variable (recurse to catch // other variables inside this possible variable) geshi_dbg('Variable was escaped'); $this->_styler->addParseData(substr($possible_var, 0, 2), $parent_name . '/esc', $this->_getExtraParseData(), $this->_complexFlag); $this->_addParseData(substr($possible_var, 2)); } else { // Add first character that might have been a \\ but in fact isn't to the parent // but only do it if we had to modify the position if ('$' != substr($possible_var, 0, 1)) { parent::_addParseData(substr($possible_var, 0, 1)); $possible_var = substr($possible_var, 1); } // Many checks could go in here... // @todo [blocking 1.1.5] check for ${foo} variables: start { matched by end } // because at the moment ${foo is matched for example. if ('{' == substr($possible_var, 0, 1)) { if ('}' == substr($possible_var, -1)) { $start_brace = '{'; } else { $start_brace = ''; parent::_addParseData('{'); // remove brace from $possible_var. This will only be used // if the variable isn't an OO variable anyway... $possible_var = substr($possible_var, 1); } } else { $start_brace = ''; } if (isset($earliest_data['tab'][5])) { // Then we matched off the third regex - the one that does objects // The first { if there is one, and $this (which is in index 2 $this->_styler->addParseData($start_brace . $earliest_data['tab'][2], $parent_name . '/var', $this->_getExtraParseData(), $this->_complexFlag); // The -> with any whitespace around it $this->_styler->addParseData($earliest_data['tab'][3], $parent_name . '/symbol', $this->_getExtraParseData(), $this->_complexFlag); // The method name $this->_styler->addParseData($earliest_data['tab'][4], $parent_name . '/oodynamic', $this->_getExtraParseData(), $this->_complexFlag); // The closing }, if any if ($earliest_data['tab'][5]) { if ($start_brace) { $this->_styler->addParseData($earliest_data['tab'][5], $parent_name . '/var', $this->_getExtraParseData(), $this->_complexFlag); } else { parent::_addParseData('}'); } } } else { $this->_styler->addParseData($possible_var, $parent_name . '/var', $this->_getExtraParseData(), $this->_complexFlag); } } // Chop off what we have done $code = substr($code, $earliest_data['pos'] + $earliest_data['len']); } // Add the rest parent::_addParseData($code, $first_char_of_next_context); }
/** * Checks whether the character(s) at the start of the parameter string are * characters that should be escaped. * * @param string The string to check the beginning of for escape characters * @return int|false The length of the escape character sequence, else false */ function _shouldBeEscaped($code, $chars_to_escape) { geshi_dbg('Checking: ' . substr($code, 0, 15)); foreach ($chars_to_escape as $match) { if ('REGEX' != substr($match, 0, 5)) { geshi_dbg('Test: ' . $match); if (substr($code, 0, 1) == $match) { return 1; } } else { geshi_dbg(' Testing via regex: ' . $match . '... ', false); $data = geshi_get_position($code, $match, 0); if (0 === $data['pos']) { geshi_dbg('match, data = ' . print_r($data, true)); return $data['len']; } geshi_dbg('no match'); } } // No matches... return false; }
/** * GetContextEndData */ function _getContextEndData($code, $context_open_key, $context_opener, $beginning_of_context) { geshi_dbg('GeSHiContext::_getContextEndData(' . $this->_contextName . ', ' . $context_open_key . ', ' . $context_opener . ', ' . $beginning_of_context . ')'); $context_end_pos = false; $context_end_len = -1; $context_end_dlm = ''; $offset = 0; // Bail out if context open key tells us that there is no ender for this context if (-1 == $context_open_key) { geshi_dbg(' no opener so no ender'); return false; } // Balanced endings is handled here if (isset($this->_contextDelimiters[$context_open_key][3])) { $balance_opener = $this->_contextDelimiters[$context_open_key][3][0]; $balance_closer = $this->_contextDelimiters[$context_open_key][3][1]; // We get the first push for free // @todo [blocking 1.1.4] if what we are balancing against is not related // to the starter of the context then we have a problem... check $context_opener // for starter stuff instead of assuming $balance_count = 1; geshi_dbg('@w Begun balancing'); while ($balance_count > 0) { // Look for opener/closers. $opener_pos = geshi_get_position($code, $balance_opener, $offset); $closer_pos = geshi_get_position($code, $balance_closer, $offset); geshi_dbg(' opener pos = ' . print_r($opener_pos, true) . ', closer pos = ' . print_r($closer_pos, true)); // Check what we found if (false !== $opener_pos['pos']) { if (false !== $closer_pos['pos']) { // Opener and closer available if ($opener_pos['pos'] < $closer_pos['pos']) { // Opener is closer so inc. counter ++$balance_count; geshi_dbg(' opener is closer so inc. to ' . $balance_count); // Start searching from new pos just past where we found the opener $offset = $opener_pos['pos'] + 1; // @todo [blocking 1.1.4] could cache closer pos at this point? } else { // closer is closer (bad english heh) --$balance_count; $offset = $closer_pos['pos'] + 1; geshi_dbg(' closer is closer so dec. to ' . $balance_count); } } else { // No closer will ever be available yet we are still in this context... // use end of code as end pos // I've yet to test this case geshi_dbg('@w No closer but still in this context!'); return array('pos' => strlen($code), 'len' => 0, 'dlm' => ''); } } elseif (false !== $closer_pos['pos']) { // No opener but closer. Nothing wrong with this --$balance_count; $offset = $closer_pos['pos'] + 1; geshi_dbg(' only closer left, dec. to ' . $balance_count); } else { // No opener or closer // Assume that we end this context at the end of the code, with // no delimiter geshi_dbg('@w No opener or closer but still in this context!'); return array('pos' => strlen($code), 'len' => 0, 'dlm' => ''); } } // start looking for real end from the position where balancing ends // because we've found where balancing ends, but the end of the balancing // is likely to be the same as the end of the context --$offset; } foreach ($this->_contextDelimiters[$context_open_key][1] as $ender) { geshi_dbg(' Checking ender: ' . str_replace("\n", '\\n', $ender), false); $ender = $this->_substitutePlaceholders($ender); geshi_dbg(' converted to ' . $ender); // Use the offset we may have found when handling balancing of contexts (will // be zero if balancing not done). $position = geshi_get_position($code, $ender, $offset); geshi_dbg(' Ender ' . $ender . ': ' . print_r($position, true)); $length = $position['len']; $position = $position['pos']; // BUGFIX:skip around crap starters if (false === $position) { continue; } if (false === $context_end_pos || $position < $context_end_pos || $position == $context_end_pos && strlen($ender) > $context_end_len) { $context_end_pos = $position; $context_end_len = $length; $context_end_dlm = $ender; } } geshi_dbg('Context ' . $this->_contextName . ' can finish at position ' . $context_end_pos); if (false !== $context_end_pos) { return array('pos' => $context_end_pos, 'len' => $context_end_len, 'dlm' => $context_end_dlm); } else { return false; } }