123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- <?php
- /* Copyright (c)
- * - 2006-2013, Ivan Sagalaev (maniacsoftwaremaniacs.org), highlight.js
- * (original author)
- * - 2013-2019, Geert Bergman (geertscrivo.nl), highlight.php
- * - 2014 Daniel Lynge, highlight.php (contributor)
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. Neither the name of "highlight.js", "highlight.php", nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
- namespace Highlight;
- /**
- * @todo In highlight.php 10.x, replace the @final attribute with the `final` keyword.
- *
- * @final
- *
- * @internal
- *
- * // Backward compatibility properties
- *
- * @property \stdClass|Mode $mode (DEPRECATED) All properties traditionally inside of $mode are now available directly from this class.
- * @property bool $caseInsensitive (DEPRECATED) Due to compatibility requirements with highlight.js, use `case_insensitive` instead.
- */
- class Language extends Mode
- {
- private static $COMMON_KEYWORDS = array('of', 'and', 'for', 'in', 'not', 'or', 'if', 'then');
- /** @var string */
- public $name;
- /** @var \stdClass|Mode|null */
- private $mode = null;
- public function __construct($lang, $filePath)
- {
- $this->name = $lang;
- // We're loading the JSON definition file as an \stdClass object instead of an associative array. This is being
- // done to take advantage of objects being pass by reference automatically in PHP whereas arrays are pass by
- // value.
- $json = file_get_contents($filePath);
- $this->mode = json_decode($json);
- }
- public function __get($name)
- {
- if ($name === 'mode') {
- @trigger_error('The "mode" property will be removed in highlight.php 10.x', E_USER_DEPRECATED);
- return $this->mode;
- }
- if ($name === 'caseInsensitive') {
- @trigger_error('Due to compatibility requirements with highlight.js, use "case_insensitive" instead.', E_USER_DEPRECATED);
- if (isset($this->mode->case_insensitive)) {
- return $this->mode->case_insensitive;
- }
- return false;
- }
- if (isset($this->mode->{$name})) {
- return $this->mode->{$name};
- }
- return null;
- }
- /**
- * @param string $value
- * @param bool $global
- *
- * @return RegEx
- */
- private function langRe($value, $global = false)
- {
- return RegExUtils::langRe($value, $global, $this->case_insensitive);
- }
- /**
- * Performs a shallow merge of multiple objects into one.
- *
- * @param Mode|array ...$params The objects to merge.
- *
- * @return \stdClass
- */
- private function inherit()
- {
- $result = new \stdClass();
- $objects = func_get_args();
- $parent = array_shift($objects);
- foreach ($parent as $key => $value) {
- $result->{$key} = $value;
- }
- foreach ($objects as $object) {
- foreach ($object as $key => $value) {
- $result->{$key} = $value;
- }
- }
- return $result;
- }
- private function dependencyOnParent($mode)
- {
- if (!$mode) {
- return false;
- }
- if (isset($mode->endsWithParent) && $mode->endsWithParent) {
- return $mode->endsWithParent;
- }
- return $this->dependencyOnParent(isset($mode->starts) ? $mode->starts : null);
- }
- /**
- * @param Mode $mode
- *
- * @return Mode[]
- */
- private function expandOrCloneMode($mode)
- {
- if ($mode->variants && !$mode->cachedVariants) {
- $mode->cachedVariants = array();
- foreach ($mode->variants as $variant) {
- $mode->cachedVariants[] = $this->inherit($mode, array('variants' => null), $variant);
- }
- }
- // EXPAND
- // if we have variants then essentially "replace" the mode with the variants
- // this happens in compileMode, where this function is called from
- if ($mode->cachedVariants) {
- return $mode->cachedVariants;
- }
- // CLONE
- // if we have dependencies on parents then we need a unique
- // instance of ourselves, so we can be reused with many
- // different parents without issue
- if ($this->dependencyOnParent($mode)) {
- return array($this->inherit($mode, array(
- 'starts' => $mode->starts ? $this->inherit($mode->starts) : null,
- )));
- }
- // highlight.php does not have a concept freezing our Modes
- // no special dependency issues, just return ourselves
- return array($mode);
- }
- /**
- * @param \stdClass|Mode $mode
- * @param \stdClass|Mode|null $parent
- */
- private function compileMode($mode, $parent = null)
- {
- Mode::_normalize($mode);
- if ($mode->compiled) {
- return;
- }
- $mode->compiled = true;
- $mode->keywords = $mode->keywords ? $mode->keywords : $mode->beginKeywords;
- if ($mode->keywords) {
- $mode->keywords = $this->compileKeywords($mode->keywords, (bool) $this->case_insensitive);
- }
- $mode->lexemesRe = $this->langRe($mode->lexemes ? $mode->lexemes : "\w+", true);
- if ($parent) {
- if ($mode->beginKeywords) {
- $mode->begin = "\\b(" . implode("|", explode(" ", $mode->beginKeywords)) . ")\\b";
- }
- if (!$mode->begin) {
- $mode->begin = "\B|\b";
- }
- $mode->beginRe = $this->langRe($mode->begin);
- if ($mode->endSameAsBegin) {
- $mode->end = $mode->begin;
- }
- if (!$mode->end && !$mode->endsWithParent) {
- $mode->end = "\B|\b";
- }
- if ($mode->end) {
- $mode->endRe = $this->langRe($mode->end);
- }
- $mode->terminator_end = $mode->end;
- if ($mode->endsWithParent && $parent->terminator_end) {
- $mode->terminator_end .= ($mode->end ? "|" : "") . $parent->terminator_end;
- }
- }
- if ($mode->illegal) {
- $mode->illegalRe = $this->langRe($mode->illegal);
- }
- if ($mode->relevance === null) {
- $mode->relevance = 1;
- }
- if (!$mode->contains) {
- $mode->contains = array();
- }
- $expandedContains = array();
- foreach ($mode->contains as &$c) {
- if ($c instanceof \stdClass) {
- Mode::_normalize($c);
- }
- $expandedContains = array_merge($expandedContains, $this->expandOrCloneMode(
- $c === 'self' ? $mode : $c
- ));
- }
- $mode->contains = $expandedContains;
- foreach ($mode->contains as $contain) {
- $this->compileMode($contain, $mode);
- }
- if ($mode->starts) {
- $this->compileMode($mode->starts, $parent);
- }
- $terminators = new Terminators($this->case_insensitive);
- $mode->terminators = $terminators->_buildModeRegex($mode);
- Mode::_handleDeprecations($mode);
- }
- private function compileKeywords($rawKeywords, $caseSensitive)
- {
- $compiledKeywords = array();
- if (is_string($rawKeywords)) {
- $this->splitAndCompile("keyword", $rawKeywords, $compiledKeywords, $caseSensitive);
- } else {
- foreach ($rawKeywords as $className => $rawKeyword) {
- $this->splitAndCompile($className, $rawKeyword, $compiledKeywords, $caseSensitive);
- }
- }
- return $compiledKeywords;
- }
- private function splitAndCompile($className, $str, array &$compiledKeywords, $caseSensitive)
- {
- if ($caseSensitive) {
- $str = strtolower($str);
- }
- $keywords = explode(' ', $str);
- foreach ($keywords as $keyword) {
- $pair = explode('|', $keyword);
- $providedScore = isset($pair[1]) ? $pair[1] : null;
- $compiledKeywords[$pair[0]] = array($className, $this->scoreForKeyword($pair[0], $providedScore));
- }
- }
- private function scoreForKeyword($keyword, $providedScore)
- {
- if ($providedScore) {
- return (int) $providedScore;
- }
- return $this->commonKeyword($keyword) ? 0 : 1;
- }
- private function commonKeyword($word)
- {
- return in_array(strtolower($word), self::$COMMON_KEYWORDS);
- }
- /**
- * Compile the Language definition.
- *
- * @param bool $safeMode
- *
- * @since 9.17.1.0 The 'safeMode' parameter was added.
- */
- public function compile($safeMode)
- {
- if ($this->compiled) {
- return;
- }
- $jr = new JsonRef();
- $jr->decodeRef($this->mode);
- // self is not valid at the top-level
- if (isset($this->mode->contains) && !in_array("self", $this->mode->contains)) {
- if (!$safeMode) {
- throw new \LogicException("`self` is not supported at the top-level of a language.");
- }
- $this->mode->contains = array_filter($this->mode->contains, function ($mode) {
- return $mode !== "self";
- });
- }
- $this->compileMode($this->mode);
- }
- /**
- * @todo Remove in highlight.php 10.x
- *
- * @deprecated 9.16.0 This method should never have been exposed publicly as part of the API.
- *
- * @param \stdClass|null $e
- */
- public function complete(&$e)
- {
- Mode::_normalize($e);
- }
- }
|