コードの色
守矢諏訪子 <suwako@076.moe>、2026-11-10 18:21
コミット 71875eee4fa2932b2347d600f08c31bb1c8e7fc0
・親 9128438
・ファイルを見る
27個のファイル、+1646 -734
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | pre { |
| 2 | 2 | background: #232023; |
| 3 | - color: fcfcfc; | |
| 3 | + color: #fcfcfc; | |
| 4 | 4 | padding: 1rem; |
| 5 | 5 | border-radius: 6px; |
| 6 | 6 | overflow: auto; |
| @@ -9,11 +9,28 @@ | ||
| 9 | 9 | overflow-x: auto; |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | -/*pre > code { | |
| 13 | - color: #fcfcfc; | |
| 14 | -}*/ | |
| 12 | +pre > code.hl { | |
| 13 | + color: #fcfcfc; | |
| 14 | +} | |
| 15 | 15 | |
| 16 | 16 | code { |
| 17 | 17 | color: #b421f8; |
| 18 | 18 | line-height: 1.45; |
| 19 | 19 | } |
| 20 | + | |
| 21 | +.hl-comment { color: #2c980c; font-style: italic; } /* dark.green */ | |
| 22 | +.hl-keyword { color: #2687f7; } /* medium.blue */ | |
| 23 | +.hl-type { color: #10c074; } /* dark.lime */ | |
| 24 | +.hl-builtin { color: #ecea71; } /* light.yellow */ | |
| 25 | +.hl-function { color: #ecea71; } /* light.yellow */ | |
| 26 | +.hl-string { color: #f7a717; } /* medium.orange */ | |
| 27 | +.hl-number { color: #6cf344; } /* light.green */ | |
| 28 | +.hl-constant { color: #ea79d8; } /* light.pink */ | |
| 29 | +.hl-preprocessor { color: #ae6bdb; } /* light.purple */ | |
| 30 | +.hl-attribute { color: #f8c56a; } /* light.orange */ | |
| 31 | +.hl-variable { color: #29d3ff; } /* medium.cyan */ | |
| 32 | +/* アセンブリ */ | |
| 33 | +.hl-instruction { color: #2687f7; } /* medium.blue */ | |
| 34 | +.hl-register { color: #1cbcd0; } /* dark.cyan */ | |
| 35 | +.hl-directive { color: #f545f5; } /* medium.pink */ | |
| 36 | +.hl-label { color: #ecea71; } /* light.yellow */ |
| @@ -179,6 +179,7 @@ | ||
| 179 | 179 | $tmpl->addCss('news-article'); |
| 180 | 180 | $tmpl->addCss('search'); |
| 181 | 181 | $tmpl->addCss('blogtype'); |
| 182 | + $tmpl->addCss('code'); | |
| 182 | 183 | $tmpl->render('article'); |
| 183 | 184 | exit(); |
| 184 | 185 |
| @@ -121,6 +121,8 @@ | ||
| 121 | 121 | foreach ($lines as $line) { |
| 122 | 122 | $i++; |
| 123 | 123 | $line = rtrim($line, "\r\n"); |
| 124 | + // コードの行末の「\」(Cのマクロの継続等)は残す | |
| 125 | + $rawLine = $line; | |
| 124 | 126 | $hasBR = str_ends_with($line, '\\'); |
| 125 | 127 | $line = rtrim($line, "\\"); |
| 126 | 128 | |
| @@ -133,7 +135,8 @@ | ||
| 133 | 135 | // } |
| 134 | 136 | |
| 135 | 137 | // コードブロック |
| 136 | - if (preg_match('/^```(\w*)$/', $line, $matches)) { | |
| 138 | + // 言語名に「+」「#」「-」「.」も使える(```c++、```c#、```asm-x8664) | |
| 139 | + if (preg_match('/^```([\w+#.-]*)\s*$/', $rawLine, $matches)) { | |
| 137 | 140 | if (!$this->inCodeBlock) { |
| 138 | 141 | if (!empty($currentParagraph)) { |
| 139 | 142 | $this->html[] = " <p>\n ".implode("", $currentParagraph)."\n </p>"; |
| @@ -152,7 +155,7 @@ | ||
| 152 | 155 | } |
| 153 | 156 | |
| 154 | 157 | if ($this->inCodeBlock) { |
| 155 | - $this->codeBlockContent[] = $line; | |
| 158 | + $this->codeBlockContent[] = $rawLine; | |
| 156 | 159 | continue; |
| 157 | 160 | } |
| 158 | 161 | |
| @@ -517,14 +520,19 @@ | ||
| 517 | 520 | * @return string HTMLのコードブロック |
| 518 | 521 | */ |
| 519 | 522 | private function createCodeBlock(): string { |
| 520 | - $code = htmlspecialchars(implode("\n", $this->codeBlockContent)); | |
| 521 | - $class = $this->codeBlockLanguage ? " class=\"language-{$this->codeBlockLanguage}\"" : ''; | |
| 523 | + $raw = implode("\n", $this->codeBlockContent); | |
| 524 | + $lang = strtolower($this->codeBlockLanguage); | |
| 525 | + $attr = htmlspecialchars($lang, ENT_QUOTES, 'UTF-8'); | |
| 526 | + | |
| 527 | + // 対応している言語はハイライトする(Syntax::colorize()はエスケープ済みのHTMLを返す) | |
| 528 | + if ($lang !== '' && Syntax::isSupported($lang)) { | |
| 529 | + $code = (new Syntax($lang))->colorize($raw); | |
| 530 | + return " <p>\n <pre><code class=\"language-{$attr} hl\">{$code}</code></pre>"; | |
| 531 | + } | |
| 532 | + | |
| 533 | + $code = htmlspecialchars($raw); | |
| 534 | + $class = $lang !== '' ? " class=\"language-{$attr}\"" : ''; | |
| 522 | 535 | return " <p>\n <pre><code{$class}>{$code}</code></pre>"; |
| 523 | - // $raw = implode("\n", $this->codeBlockContent); | |
| 524 | - // $lang = $this->codeBlockLanguage ?: 'txt'; | |
| 525 | - // $class = $lang === 'txt' ? '' : " class=\"language-{$lang}\""; | |
| 526 | - // $highlighted = $this->highlightCode($raw, $lang); | |
| 527 | - // return "<pre><code{$class}>{$highlighted}</code></pre>"; | |
| 528 | 536 | } |
| 529 | 537 | |
| 530 | 538 | private function createBlockquote(array $content): string { |
| @@ -2,279 +2,332 @@ | ||
| 2 | 2 | namespace Std\Lib; |
| 3 | 3 | |
| 4 | 4 | use Std\Lib\Syntax\SyntaxInterface; |
| 5 | +use Std\Lib\Syntax\Asm; | |
| 6 | +use Std\Lib\Syntax\Asm6502; | |
| 7 | +use Std\Lib\Syntax\Asm68000; | |
| 5 | 8 | use Std\Lib\Syntax\AsmAarch32; |
| 6 | 9 | use Std\Lib\Syntax\AsmAarch64; |
| 10 | +use Std\Lib\Syntax\AsmMips; | |
| 11 | +use Std\Lib\Syntax\AsmPower; | |
| 12 | +use Std\Lib\Syntax\AsmRiscv; | |
| 7 | 13 | use Std\Lib\Syntax\AsmX8664; |
| 14 | +use Std\Lib\Syntax\AsmZ80; | |
| 8 | 15 | use Std\Lib\Syntax\C; |
| 9 | 16 | use Std\Lib\Syntax\Cpp; |
| 17 | +use Std\Lib\Syntax\CSharp; | |
| 10 | 18 | use Std\Lib\Syntax\Glsl; |
| 11 | 19 | use Std\Lib\Syntax\Go; |
| 20 | +use Std\Lib\Syntax\Hlsl; | |
| 21 | +use Std\Lib\Syntax\ObjC; | |
| 22 | +use Std\Lib\Syntax\Slang; | |
| 23 | +use Std\Lib\Syntax\Swift; | |
| 12 | 24 | |
| 25 | +/** | |
| 26 | + * コードブロックのシンタックスハイライト | |
| 27 | + * | |
| 28 | + * コードを先頭から1回だけ走査し、トークン毎に<span class="hl-種類">で囲む。 | |
| 29 | + * 色はstyle-code.cssで定義する。 | |
| 30 | + * | |
| 31 | + * 使い方: | |
| 32 | + * if (Syntax::isSupported($lang)) $html = (new Syntax($lang))->colorize($code); | |
| 33 | + */ | |
| 13 | 34 | class Syntax { |
| 14 | - private array $alt = [ | |
| 15 | - 'asm-aarch32' => ['asm-arm'], | |
| 16 | - 'asm-x8664' => ['asm', 'asm-x86', 'asm-x64'], | |
| 17 | - 'cpp' => ['cc', 'c++'], | |
| 18 | - 'css' => ['css3'], | |
| 19 | - 'go' => ['golang'], | |
| 20 | - ]; | |
| 21 | - | |
| 22 | - private array $include = []; | |
| 23 | - private string $lang; | |
| 24 | - private SyntaxInterface $handler; | |
| 25 | - | |
| 26 | - private array $supported = [ | |
| 35 | + private const SUPPORTED = [ | |
| 36 | + 'asm' => Asm::class, | |
| 37 | + 'asm-6502' => Asm6502::class, | |
| 38 | + 'asm-68000' => Asm68000::class, | |
| 27 | 39 | 'asm-aarch32' => AsmAarch32::class, |
| 28 | 40 | 'asm-aarch64' => AsmAarch64::class, |
| 41 | + 'asm-mips' => AsmMips::class, | |
| 42 | + 'asm-power' => AsmPower::class, | |
| 43 | + 'asm-riscv' => AsmRiscv::class, | |
| 29 | 44 | 'asm-x8664' => AsmX8664::class, |
| 45 | + 'asm-z80' => AsmZ80::class, | |
| 30 | 46 | 'c' => C::class, |
| 31 | 47 | 'cpp' => Cpp::class, |
| 48 | + 'csharp' => CSharp::class, | |
| 32 | 49 | 'glsl' => Glsl::class, |
| 33 | 50 | 'go' => Go::class, |
| 51 | + 'hlsl' => Hlsl::class, | |
| 52 | + 'objc' => ObjC::class, | |
| 53 | + 'slang' => Slang::class, | |
| 54 | + 'swift' => Swift::class, | |
| 34 | 55 | ]; |
| 35 | 56 | |
| 57 | + private const ALIASES = [ | |
| 58 | + 'asm-6502' => ['6502', 'asm6502', 'asm-65c02'], | |
| 59 | + 'asm-68000' => ['68000', '68k', 'm68k', 'asm-68k', 'asm-m68k'], | |
| 60 | + 'asm-aarch32' => ['asm-arm', 'asm-arm32', 'arm', 'arm32', 'aarch32', 'asm-thumb'], | |
| 61 | + 'asm-aarch64' => ['asm-arm64', 'arm64', 'aarch64'], | |
| 62 | + 'asm-mips' => ['mips', 'asm-mips64'], | |
| 63 | + 'asm-power' => ['asm-ppc', 'asm-powerpc', 'asm-ppc64', 'ppc', 'powerpc', 'power'], | |
| 64 | + 'asm-riscv' => ['riscv', 'asm-riscv64', 'asm-rv64', 'rv64', 'risc-v'], | |
| 65 | + 'asm-x8664' => ['asm-x86', 'asm-x64', 'asm-x86-64', 'asm-amd64', 'x86', 'x64', 'x86-64', 'nasm'], | |
| 66 | + 'asm-z80' => ['z80', 'asm-gbz80', 'gbz80'], | |
| 67 | + 'c' => ['h', 'c99', 'c11', 'c23'], | |
| 68 | + 'cpp' => ['c++', 'cc', 'cxx', 'hpp', 'hh', 'hxx', 'h++'], | |
| 69 | + 'csharp' => ['c#', 'cs'], | |
| 70 | + 'glsl' => ['vert', 'frag', 'geom', 'comp', 'tesc', 'tese'], | |
| 71 | + 'go' => ['golang'], | |
| 72 | + 'hlsl' => ['fx', 'fxh', 'hlsli'], | |
| 73 | + 'objc' => ['objective-c', 'obj-c', 'objectivec', 'm', 'mm', 'objc++'], | |
| 74 | + 'slang' => [], | |
| 75 | + 'swift' => [], | |
| 76 | + ]; | |
| 77 | + | |
| 78 | + private string $lang; | |
| 79 | + private SyntaxInterface $handler; | |
| 80 | + | |
| 81 | + /** @var array 規則の正規表現をキャッシュ(言語 => [行頭用, 通常用, 種類一覧]) */ | |
| 82 | + private static array $compiled = []; | |
| 83 | + | |
| 36 | 84 | public function __construct(string $lang) { |
| 37 | - $this->lang = $this->resolveLanguage($lang); | |
| 38 | - $this->handler = $this->createHandler($this->lang); | |
| 85 | + $resolved = self::resolve($lang); | |
| 86 | + if ($resolved === null) throw new \InvalidArgumentException("未対応の言語: {$lang}"); | |
| 87 | + $this->lang = $resolved; | |
| 88 | + $class = self::SUPPORTED[$resolved]; | |
| 89 | + $this->handler = new $class(); | |
| 39 | 90 | } |
| 40 | 91 | |
| 41 | - public function colorize(string $code): string { | |
| 42 | - $lines = explode("\n", $code); | |
| 43 | - foreach ($lines as $c) { | |
| 44 | - $c = str_replace('<', '<', $c); | |
| 45 | - $c = str_replace('>', '>', $c); | |
| 46 | - } | |
| 47 | - | |
| 48 | - switch ($this->lang) { | |
| 49 | - case 'asm-aarch': | |
| 50 | - break; | |
| 51 | - case 'asm-x8664': | |
| 52 | - $lines = $this->addString($lines); | |
| 53 | - $lines = $this->addNumber($lines); | |
| 54 | - $lines = $this->addComment($lines); | |
| 55 | - $lines = $this->add($lines, 'register'); | |
| 56 | - $lines = $this->add($lines, 'operator'); | |
| 57 | - $lines = $this->addLabel($lines); | |
| 58 | - $lines = $this->addSection($lines); | |
| 59 | - $lines = $this->addDirective($lines); | |
| 60 | - break; | |
| 61 | - case 'c': | |
| 62 | - $this->include = $this->handler->getInclude($lines); | |
| 63 | - $lines = $this->addString($lines); | |
| 64 | - $lines = $this->addNumber($lines); | |
| 65 | - $lines = $this->addComment($lines); | |
| 66 | - $lines = $this->add($lines, 'preprocessor'); | |
| 67 | - $lines = $this->add($lines, 'keyword'); | |
| 68 | - foreach ($this->include as $inc) { | |
| 69 | - $lines = $this->add($lines, 'typedef', $inc); | |
| 70 | - $lines = $this->add($lines, 'libc', $inc); | |
| 71 | - } | |
| 72 | - $lines = $this->addFunction($lines); | |
| 73 | - // kys($lines); | |
| 74 | - break; | |
| 75 | - case 'cpp': | |
| 76 | - break; | |
| 77 | - case 'glsl': | |
| 78 | - break; | |
| 79 | - case 'go': | |
| 80 | - break; | |
| 81 | - default: | |
| 82 | - break; | |
| 83 | - } | |
| 84 | - | |
| 85 | - return implode("\n", $lines); | |
| 92 | + /** | |
| 93 | + * 言語が対応しているか | |
| 94 | + * | |
| 95 | + * @param string $lang 言語名又は別名 | |
| 96 | + * @return bool | |
| 97 | + */ | |
| 98 | + public static function isSupported(string $lang): bool { | |
| 99 | + return self::resolve($lang) !== null; | |
| 86 | 100 | } |
| 87 | 101 | |
| 88 | - /////////////// | |
| 89 | - | |
| 90 | - private function resolveLanguage(string $lang): string { | |
| 91 | - if (isset($this->supported[$lang])) return $lang; | |
| 92 | - foreach ($this->alt as $k => $v) if (in_array($lang, $v, true)) return $k; | |
| 93 | - throw new \InvalidArgumentException("未対応の言語: {$lang}"); | |
| 102 | + /** | |
| 103 | + * 別名を正式な言語名にする | |
| 104 | + * | |
| 105 | + * @param string $lang 言語名又は別名 | |
| 106 | + * @return string|null 正式な言語名、未対応ならnull | |
| 107 | + */ | |
| 108 | + public static function resolve(string $lang): ?string { | |
| 109 | + $lang = strtolower(trim($lang)); | |
| 110 | + if (isset(self::SUPPORTED[$lang])) return $lang; | |
| 111 | + foreach (self::ALIASES as $k => $v) if (in_array($lang, $v, true)) return $k; | |
| 112 | + return null; | |
| 94 | 113 | } |
| 95 | 114 | |
| 96 | - private function createHandler(string $lang): SyntaxInterface { | |
| 97 | - $class = $this->supported[$lang] ?? null; | |
| 98 | - if (!$class) throw new \RuntimeException("言語のハンドルが存在しない: {$lang}"); | |
| 99 | - return new $class(); | |
| 115 | + public function getLanguage(): string { | |
| 116 | + return $this->lang; | |
| 100 | 117 | } |
| 101 | 118 | |
| 102 | - private function addComment(array $code): array { | |
| 103 | - global $colorPalette; | |
| 119 | + /** | |
| 120 | + * コードをハイライトしたHTMLにする(HTMLエスケープ済み) | |
| 121 | + * 失敗した場合はエスケープしただけのコードを返す | |
| 122 | + * | |
| 123 | + * @param string $code 生のコード | |
| 124 | + * @return string HTML | |
| 125 | + */ | |
| 126 | + public function colorize(string $code): string { | |
| 127 | + $code = str_replace(["\r\n", "\r"], "\n", $code); | |
| 128 | + if ($code === '' || preg_match('//u', $code) !== 1) return self::escape($code); | |
| 104 | 129 | |
| 105 | - $sc = $this->handler->getComment() ?? null; | |
| 106 | - $mc = $this->handler->getMultiline() ?? [null, null]; | |
| 107 | - $mcs = $mc[0]; | |
| 108 | - $mce = $mc[1]; | |
| 130 | + try { | |
| 131 | + return $this->tokenize($code); | |
| 132 | + } catch (\Throwable $e) { | |
| 133 | + return self::escape($code); | |
| 134 | + } | |
| 135 | + } | |
| 109 | 136 | |
| 110 | - $color = $this->handler->getColors()['comment'] ?? $colorPalette['dark']['green']; | |
| 137 | + /////////////// | |
| 111 | 138 | |
| 112 | - $isMulti = false; | |
| 113 | - $out = []; | |
| 139 | + private function tokenize(string $code): string { | |
| 140 | + $h = $this->handler; | |
| 141 | + $h->prepare($code); | |
| 142 | + [$reBol, $reAny, $classes] = $this->compile(); | |
| 143 | + $nested = $h->getNestedComment(); | |
| 144 | + $headers = $h->getHeaderDirectives(); | |
| 114 | 145 | |
| 115 | - foreach ($code as $c) { | |
| 116 | - $cur = $c; | |
| 146 | + $out = ''; | |
| 147 | + $plain = ''; | |
| 148 | + $pos = 0; | |
| 149 | + $len = strlen($code); | |
| 150 | + $bol = true; // 行頭から空白しか無い | |
| 151 | + $first = true; // この行でまだ単語が無い(アセンブリの命令判定用) | |
| 152 | + $prev = ''; // 直前の空白以外の文字 | |
| 153 | + $prevWord = ''; // この行の直前の単語 | |
| 154 | + $lineWord = ''; // この行の最初の単語 | |
| 155 | + $expectHeader = false; | |
| 117 | 156 | |
| 118 | - if ($isMulti || strpos($cur, $sc) !== false || ($mcs && strpos($cur, $mcs) !== false)) { | |
| 119 | - $cur = preg_replace('/<span[^>]*>(.*?)<\/span>/s', '$1', $cur); | |
| 157 | + $emit = function(string $text, ?string $class) use (&$out, &$plain): void { | |
| 158 | + if ($class === null) { | |
| 159 | + $plain .= $text; | |
| 160 | + return; | |
| 120 | 161 | } |
| 162 | + if ($plain !== '') { | |
| 163 | + $out .= self::escape($plain); | |
| 164 | + $plain = ''; | |
| 165 | + } | |
| 166 | + $out .= '<span class="hl-'.$class.'">'.self::escape($text).'</span>'; | |
| 167 | + }; | |
| 121 | 168 | |
| 122 | - if ($isMulti) { | |
| 123 | - $endPos = $mcs ? strpos($cur, $mce) : false; | |
| 169 | + while ($pos < $len) { | |
| 170 | + $ch = $code[$pos]; | |
| 124 | 171 | |
| 125 | - if ($endPos !== false) { | |
| 126 | - $before = substr($cur, 0, $endPos + strlen($mce)); | |
| 127 | - $after = substr($cur, $endPos + strlen($mce)); | |
| 172 | + if ($ch === "\n") { | |
| 173 | + $plain .= "\n"; | |
| 174 | + $pos++; | |
| 175 | + $bol = true; | |
| 176 | + $first = true; | |
| 177 | + $prevWord = ''; | |
| 178 | + $lineWord = ''; | |
| 179 | + $expectHeader = false; | |
| 180 | + continue; | |
| 181 | + } | |
| 128 | 182 | |
| 129 | - $cur = '<span style="color: '.$color.'; font-style: italics;">'.$before.'</span>'.$after; | |
| 130 | - $isMulti = false; | |
| 131 | - } else { | |
| 132 | - $cur = '<span style="color: '.$color.'; font-style: italics;">'.$cur.'</span>'; | |
| 133 | - } | |
| 134 | - } else { | |
| 135 | - if ($sc && ($pos = strpos($cur, $sc, 1)) !== false) { | |
| 136 | - $before = substr($cur, 0, $pos); | |
| 137 | - $rest = trim(substr($cur, $pos)); | |
| 138 | - $cur = $before.'<span style="color: '.$color.'; font-style: italics;">'.$rest.'</span>'; | |
| 139 | - } | |
| 183 | + if ($ch === ' ' || $ch === "\t") { | |
| 184 | + $plain .= $ch; | |
| 185 | + $pos++; | |
| 186 | + continue; | |
| 187 | + } | |
| 140 | 188 | |
| 141 | - if (!$isMulti && $mcs && $mce && str_contains($cur, $mcs)) { | |
| 142 | - $startPos = strpos($cur, $mcs); | |
| 143 | - $endPos = $mce ? strpos($cur, $mce) : false; | |
| 189 | + // 入れ子のコメント(/* /* */ */) | |
| 190 | + if ($nested && substr_compare($code, $nested[0], $pos, strlen($nested[0])) === 0) { | |
| 191 | + $end = $this->scanNested($code, $pos, $nested[0], $nested[1]); | |
| 192 | + $text = substr($code, $pos, $end - $pos); | |
| 193 | + $emit($text, 'comment'); | |
| 194 | + $pos = $end; | |
| 195 | + $bol = false; | |
| 196 | + if (str_contains($text, "\n")) $first = false; | |
| 197 | + continue; | |
| 198 | + } | |
| 144 | 199 | |
| 145 | - if ($endPos !== false && $startPos < $endPos) { | |
| 146 | - $before = substr($cur, 0, $startPos); | |
| 147 | - $part = substr($cur, $startPos, $endPos - $startPos + strlen($mce)); | |
| 148 | - $after = substr($cur, $endPos + strlen($mce)); | |
| 200 | + // #include <stdio.h> のファイル名 | |
| 201 | + if ($expectHeader && $ch === '<' && preg_match('~\G<[^>\n]*>~', $code, $m, 0, $pos)) { | |
| 202 | + $emit($m[0], 'string'); | |
| 203 | + $pos += strlen($m[0]); | |
| 204 | + $bol = false; | |
| 205 | + $expectHeader = false; | |
| 206 | + continue; | |
| 207 | + } | |
| 149 | 208 | |
| 150 | - $cur = $before.'<span style="color: '.$color.'; font-style: italics;">'.$part.'</span>'.$after; | |
| 151 | - } else { | |
| 152 | - $before = substr($cur, 0, $startPos); | |
| 153 | - $rest = substr($cur, $startPos); | |
| 209 | + if (preg_match($bol ? $reBol : $reAny, $code, $m, PREG_UNMATCHED_AS_NULL, $pos) === 1 && $m[0] !== '') { | |
| 210 | + $text = $m[0]; | |
| 211 | + $class = null; | |
| 212 | + foreach ($classes as $i => $c) { | |
| 213 | + if (isset($m["r{$i}"])) { | |
| 214 | + $class = $c; | |
| 215 | + break; | |
| 216 | + } | |
| 217 | + } | |
| 154 | 218 | |
| 155 | - $cur = $before.'<span style="color: '.$color.'; font-style: italics;">'.$rest; | |
| 156 | - $isMulti = true; | |
| 219 | + $after = $pos + strlen($text); | |
| 220 | + if ($class === 'ident') { | |
| 221 | + $class = $h->classify($text, $this->context($code, $after, $bol, $first, $prev, $prevWord, $lineWord)); | |
| 222 | + if ($class !== 'label') { | |
| 223 | + if ($first) $lineWord = $text; | |
| 224 | + $first = false; | |
| 157 | 225 | } |
| 226 | + $prevWord = $text; | |
| 227 | + } else if ($class === 'preprocessor') { | |
| 228 | + $name = strtolower(ltrim(substr($text, 1), " \t")); | |
| 229 | + if (in_array($name, $headers, true)) $expectHeader = true; | |
| 158 | 230 | } |
| 231 | + | |
| 232 | + $emit($text, $class); | |
| 233 | + $pos = $after; | |
| 234 | + $bol = false; | |
| 235 | + $prev = substr($text, -1); | |
| 236 | + if (str_contains($text, "\n")) $first = false; | |
| 237 | + continue; | |
| 159 | 238 | } |
| 160 | 239 | |
| 161 | - $out[] = $cur; | |
| 240 | + // どの規則にも合わない文字(演算子等) | |
| 241 | + preg_match('~\G.~su', $code, $m, 0, $pos); | |
| 242 | + $c = $m[0] ?? $ch; | |
| 243 | + $plain .= $c; | |
| 244 | + $pos += strlen($c); | |
| 245 | + $bol = false; | |
| 246 | + $prev = $c; | |
| 162 | 247 | } |
| 163 | 248 | |
| 249 | + if ($plain !== '') $out .= self::escape($plain); | |
| 164 | 250 | return $out; |
| 165 | 251 | } |
| 166 | 252 | |
| 167 | - private function addNumber(array $code): array { | |
| 168 | - $color = $this->handler->getColors()['number']; | |
| 169 | - $pattern = '/(?<![\w$@.])(?: # 関係ない | |
| 170 | - (?:0[xX][0-9a-fA-F]+) # 0xFF, 0XFF | |
| 171 | - |(?:0[bB][01]+) # 0b1010 | |
| 172 | - |(?:[0-9]+[hH]) # FFh, 0FFh | |
| 173 | - |(?:\$[0-9a-fA-F]+) # $FF | |
| 174 | - |(?:%[01]+) # %1010 | |
| 175 | - |(?:\d+(?:\.\d+)?(?:[eE][+-]?\d+)?) # 42, 3.14, 1e-5 | |
| 176 | - |(?:\d+) # 普通数字 | |
| 177 | - )(?![\w$@.])(?![a-zA-Z])/ix'; | |
| 253 | + /** | |
| 254 | + * 規則を2つの正規表現にまとめる(行頭用と通常用) | |
| 255 | + * | |
| 256 | + * @return array [行頭用, 通常用, 番号 => 種類] | |
| 257 | + */ | |
| 258 | + private function compile(): array { | |
| 259 | + if (isset(self::$compiled[$this->lang])) return self::$compiled[$this->lang]; | |
| 178 | 260 | |
| 179 | - return array_map(fn($c) => preg_replace_callback($pattern, fn($m) => '<span style="color:'.$color.'">'.$m[0].'</span>', $c), $code); | |
| 180 | - } | |
| 181 | - | |
| 182 | - private function addString(array $code): array { | |
| 183 | - $color = $this->handler->getColors()['string']; | |
| 184 | - $pattern = '/(?:"(?:[^"\\\\]|\\\\.)*"|\'(?:[^\'\\\\]|\\\\.)*\')/s'; | |
| 185 | - | |
| 186 | - return array_map( | |
| 187 | - function ($c) use ($pattern, $color) { | |
| 188 | - return preg_replace_callback($pattern, fn($m) => '<span style="color:'.$color.'">'.$m[0].'</span>', $c); | |
| 189 | - }, | |
| 190 | - $code | |
| 191 | - ); | |
| 192 | - } | |
| 193 | - | |
| 194 | - private function addLabel(array $code): array { | |
| 195 | - $color = $this->handler->getColors()['label']; | |
| 196 | - $out = []; | |
| 197 | - foreach ($code as $c) $out[] = preg_replace('/^\s*([\.a-zA-Z_][a-zA-Z0-9_]*)\s*:/m', '<span style="color: '.$color.';">$1</span>:', $c); | |
| 198 | - return $out; | |
| 199 | - } | |
| 200 | - | |
| 201 | - private function addSection(array $code): array { | |
| 202 | - $color1 = $this->handler->getColors()['label']; | |
| 203 | - $color2 = $this->handler->getColors()['register']; | |
| 204 | - $color3 = $this->handler->getColors()['number']; | |
| 205 | - $out = []; | |
| 206 | - | |
| 207 | - foreach ($code as $c) { | |
| 208 | - $cur = $c; | |
| 209 | - | |
| 210 | - $cur = preg_replace( | |
| 211 | - '/^(?:(section|default|segment|group)\s+)([\w.][\w.\-]*)(?:\s*(?:;.*)?)?$/im', | |
| 212 | - '<span style="color: '.$color1.';">$1</span> <span style="color: '.$color2.';">$2</span>', | |
| 213 | - $cur); | |
| 214 | - $cur = preg_replace( | |
| 215 | - '/^(bits)\s+(16|32|64)(?:\s*(?:;.*)?)?$/im', | |
| 216 | - '<span style="color: '.$color1.';">$1</span> <span style="color: '.$color3.';">$2</span>', | |
| 217 | - $cur); | |
| 218 | - | |
| 219 | - $out[] = $cur; | |
| 261 | + $rules = $this->handler->getRules(); | |
| 262 | + $bol = []; | |
| 263 | + $any = []; | |
| 264 | + $classes = []; | |
| 265 | + foreach ($rules as $i => $r) { | |
| 266 | + $classes[$i] = $r['class']; | |
| 267 | + $part = "(?<r{$i}>{$r['re']})"; | |
| 268 | + $bol[] = $part; | |
| 269 | + if (empty($r['bol'])) $any[] = $part; | |
| 220 | 270 | } |
| 221 | 271 | |
| 222 | - return $out; | |
| 272 | + $mk = fn(array $parts) => '~\G(?:'.implode('|', $parts).')~su'; | |
| 273 | + return self::$compiled[$this->lang] = [$mk($bol), $mk($any), $classes]; | |
| 223 | 274 | } |
| 224 | 275 | |
| 225 | - private function add(array $code, string $type, ?string $header = null): array { | |
| 226 | - $tok = null !== $header ? $this->handler->getTokens()[$type][$header] : $this->handler->getTokens()[$type]; | |
| 227 | - $color = $this->handler->getColors()[$type]; | |
| 228 | - $pattern = '/\b('.implode('|', array_map(fn($r) => preg_quote($r, '/'), $tok)).')\b/i'; | |
| 229 | - $so = '<span style="color: '.$color.';">'; | |
| 230 | - $sc = '</span>'; | |
| 231 | - $out = []; | |
| 232 | - foreach ($code as $c) { | |
| 233 | - $out[] = preg_replace_callback($pattern, function ($m) use ($so, $sc) { return $so.$m[0].$sc; }, $c); | |
| 234 | - } | |
| 235 | - return $out; | |
| 276 | + /** | |
| 277 | + * 識別子の分類に使う文脈を作る | |
| 278 | + * | |
| 279 | + * @param string $code コード全体 | |
| 280 | + * @param int $after 識別子の直後の位置 | |
| 281 | + * @param bool $bol 識別子が行頭か | |
| 282 | + * @param bool $first 行の最初の単語か | |
| 283 | + * @param string $prev 直前の空白以外の文字 | |
| 284 | + * @param string $prevWord この行の直前の単語 | |
| 285 | + * @param string $lineWord この行の最初の単語(ラベルを除く) | |
| 286 | + * @return array | |
| 287 | + */ | |
| 288 | + private function context(string $code, int $after, bool $bol, bool $first, string $prev, string $prevWord, string $lineWord): array { | |
| 289 | + preg_match('~\G[ \t]*([^ \t\n]?)~u', $code, $m, 0, $after); | |
| 290 | + $next = $m[1] ?? ''; | |
| 291 | + preg_match('~\G.~su', $code, $r, 0, $after); | |
| 292 | + preg_match('~\G[ \t]*:?[ \t]*([^\s,;:()]*)~u', $code, $w, 0, $after); | |
| 293 | + return [ | |
| 294 | + 'bol' => $bol, | |
| 295 | + 'first' => $first, | |
| 296 | + 'prev' => $prev, | |
| 297 | + 'prevWord' => $prevWord, | |
| 298 | + 'lineWord' => $lineWord, | |
| 299 | + 'next' => $next, | |
| 300 | + 'nextRaw' => $r[0] ?? '', | |
| 301 | + 'nextWord' => $w[1] ?? '', | |
| 302 | + ]; | |
| 236 | 303 | } |
| 237 | 304 | |
| 238 | - private function addFunction(array $code): array { | |
| 239 | - $color = $this->handler->getColors()['function']; | |
| 240 | - $out = []; | |
| 241 | - | |
| 242 | - foreach ($code as $c) { | |
| 243 | - $cur = $c; | |
| 244 | - $cur = preg_replace_callback("/\b(.*)\(\b/i", fn($m) => '<span style="color: '.$color.';">'.$m[0].'</span>', $cur); | |
| 245 | - $out[] = $cur; | |
| 305 | + /** | |
| 306 | + * 入れ子のコメントの終わりを探す | |
| 307 | + * | |
| 308 | + * @return int コメントの直後の位置(閉じていなければコードの最後) | |
| 309 | + */ | |
| 310 | + private function scanNested(string $code, int $pos, string $start, string $end): int { | |
| 311 | + $depth = 0; | |
| 312 | + $len = strlen($code); | |
| 313 | + $ls = strlen($start); | |
| 314 | + $le = strlen($end); | |
| 315 | + while ($pos < $len) { | |
| 316 | + if (substr_compare($code, $start, $pos, $ls) === 0) { | |
| 317 | + $depth++; | |
| 318 | + $pos += $ls; | |
| 319 | + } else if (substr_compare($code, $end, $pos, $le) === 0) { | |
| 320 | + $depth--; | |
| 321 | + $pos += $le; | |
| 322 | + if ($depth === 0) return $pos; | |
| 323 | + } else { | |
| 324 | + $pos++; | |
| 325 | + } | |
| 246 | 326 | } |
| 247 | - | |
| 248 | - return $out; | |
| 327 | + return $len; | |
| 249 | 328 | } |
| 250 | 329 | |
| 251 | - private function addDirective(array $code): array { | |
| 252 | - $tok = $this->handler->getTokens()['directive']; | |
| 253 | - $color1 = $this->handler->getColors()['directive']; | |
| 254 | - $color2 = $this->handler->getColors()['register']; | |
| 255 | - $pattern = implode('|', array_map(fn($r) => preg_quote($r, '/'), $tok)); | |
| 256 | - $out = []; | |
| 257 | - | |
| 258 | - foreach ($code as $c) { | |
| 259 | - $cur = $c; | |
| 260 | - $cur = preg_replace_callback("/\b({$pattern})\b/i", fn($m) => '<span style="color: '.$color1.';">'.$m[0].'</span>', $cur); | |
| 261 | - $cur = preg_replace_callback( | |
| 262 | - '/^\s*([a-zA-Z_][a-zA-Z0-9_$.@]*)\s+('.$pattern.')\b\s+(.+?)(?:\s*(?:;.*)?)?$/im', | |
| 263 | - function ($m) use ($color1, $color2) { | |
| 264 | - $l = $m[1]; | |
| 265 | - $d = $m[2]; | |
| 266 | - $v = $m[3]; | |
| 267 | - | |
| 268 | - // if (strcasecmp($d, 'equ') === 0) $v = preg_replace('/\$-[a-zA-Z_][a-zA-Z0-9_]*/i', '<span style="color: '.$color1.';">$o</span>', $v); | |
| 269 | - return sprintf( | |
| 270 | - '<span style="color: %s;">%s</span> '.'<span style="color: %s;">%s</span> %s', | |
| 271 | - $color1, $l, $color2, $v, $v | |
| 272 | - ); | |
| 273 | - }, $cur); | |
| 274 | - | |
| 275 | - $out[] = $cur; | |
| 276 | - } | |
| 277 | - | |
| 278 | - return $out; | |
| 330 | + private static function escape(string $s): string { | |
| 331 | + return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); | |
| 279 | 332 | } |
| 280 | -} | |
| 333 | +} |
| @@ -0,0 +1,45 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * 「```asm」用の汎用アセンブリ | |
| 6 | + * 1つの記事でx86-64・AArch64・RISC-V・POWER等を全部「asm」で書く事があるので、 | |
| 7 | + * 主なアーキテクチャのレジスター・命令をまとめて使う。 | |
| 8 | + * | |
| 9 | + * 「#」はコメント(RISC-V・POWER・GAS)、「#数字」は即値(ARM)として扱う。 | |
| 10 | + */ | |
| 11 | +class Asm extends Assembly { | |
| 12 | + protected array $comment = [';', '//', '#']; | |
| 13 | + protected ?array $multiline = ['/*', '*/']; | |
| 14 | + protected string $immediatePrefix = ''; | |
| 15 | + protected bool $percentBinary = false; | |
| 16 | + protected bool $percentMacros = true; | |
| 17 | + | |
| 18 | + protected array $rules = [ | |
| 19 | + // ARMの即値(#64, #-1, #0x10)はコメントより先に確認する | |
| 20 | + ['re' => '#[-+]?(?:0[xX][0-9A-Fa-f]+|0[bB][01]+|\d+(?:\.\d+)?)', 'class' => 'number'], | |
| 21 | + ]; | |
| 22 | + | |
| 23 | + /** @var string[] まとめるアーキテクチャ(8ビット機等は単一文字のレジスターが多いので含めない) */ | |
| 24 | + protected array $archs = [AsmX8664::class, AsmAarch64::class, AsmAarch32::class, AsmRiscv::class, AsmMips::class, AsmPower::class]; | |
| 25 | + | |
| 26 | + public function prepare(string $code): void { | |
| 27 | + $words = []; | |
| 28 | + $suffixes = []; | |
| 29 | + $prefixes = []; | |
| 30 | + foreach ($this->archs as $class) { | |
| 31 | + $a = new $class(); | |
| 32 | + $a->prepare(''); | |
| 33 | + [$w2, $s2, $p2] = $a->getTables(); | |
| 34 | + foreach ($w2 as $w => $c) { | |
| 35 | + // 命令の色を優先(「and」等が他のアーキテクチャでキーワードの場合) | |
| 36 | + if (!isset($words[$w]) || $c === 'instruction') $words[$w] = $c; | |
| 37 | + } | |
| 38 | + $suffixes = array_merge($suffixes, $s2); | |
| 39 | + $prefixes = array_merge($prefixes, $p2); | |
| 40 | + } | |
| 41 | + $this->words = $words; | |
| 42 | + $this->suffixes = array_values(array_unique($suffixes)); | |
| 43 | + $this->prefixes = array_values(array_unique($prefixes)); | |
| 44 | + } | |
| 45 | +} |
| @@ -1,40 +1,28 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * 6502・65C02 | |
| 6 | + */ | |
| 7 | +class Asm6502 extends Assembly { | |
| 8 | + protected string $identifier = '[.A-Za-z_@][\w.$]*'; | |
| 5 | 9 | |
| 6 | -class Asm6502 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 10 | + protected array $tokens = [ | |
| 12 | 11 | 'register' => [ |
| 13 | - // 16ビット | |
| 14 | - // 8ビット | |
| 12 | + 'a', 'x', 'y', 's', 'p', | |
| 15 | 13 | ], |
| 16 | - 'operator' => [ | |
| 14 | + 'instruction' => [ | |
| 15 | + 'adc', 'and', 'asl', 'bcc', 'bcs', 'beq', 'bit', 'bmi', 'bne', 'bpl', 'brk', 'bvc', 'bvs', 'clc', 'cld', 'cli', 'clv', | |
| 16 | + 'cmp', 'cpx', 'cpy', 'dec', 'dex', 'dey', 'eor', 'inc', 'inx', 'iny', 'jmp', 'jsr', 'lda', 'ldx', 'ldy', 'lsr', 'nop', | |
| 17 | + 'ora', 'pha', 'php', 'pla', 'plp', 'rol', 'ror', 'rti', 'rts', 'sbc', 'sec', 'sed', 'sei', 'sta', 'stx', 'sty', | |
| 18 | + 'tax', 'tay', 'tsx', 'txa', 'txs', 'tya', | |
| 19 | + // 65C02 | |
| 20 | + 'bra', 'phx', 'phy', 'plx', 'ply', 'stz', 'trb', 'tsb', 'stp', 'wai', | |
| 21 | + 'bbr0', 'bbr1', 'bbr2', 'bbr3', 'bbr4', 'bbr5', 'bbr6', 'bbr7', 'bbs0', 'bbs1', 'bbs2', 'bbs3', 'bbs4', 'bbs5', 'bbs6', 'bbs7', | |
| 22 | + 'rmb0', 'rmb1', 'rmb2', 'rmb3', 'rmb4', 'rmb5', 'rmb6', 'rmb7', 'smb0', 'smb1', 'smb2', 'smb3', 'smb4', 'smb5', 'smb6', 'smb7', | |
| 17 | 23 | ], |
| 18 | 24 | 'directive' => [ |
| 25 | + 'org', 'equ', 'db', 'dw', 'ds', 'byte', 'word', 'include', 'incbin', 'macro', 'endm', 'if', 'else', 'endif', | |
| 19 | 26 | ], |
| 20 | 27 | ]; |
| 21 | - | |
| 22 | - public function __construct() { | |
| 23 | - global $colorPalette; | |
| 24 | - | |
| 25 | - $this->colors = [ | |
| 26 | - 'comment' => $colorPalette['dark']['green'], | |
| 27 | - 'directive' => $colorPalette['dark']['pink'], | |
| 28 | - 'label' => $colorPalette['light']['yellow'], | |
| 29 | - 'number' => $colorPalette['light']['green'], | |
| 30 | - 'operator' => $colorPalette['dark']['blue'], | |
| 31 | - 'register' => $colorPalette['dark']['cyan'], | |
| 32 | - 'string' => $colorPalette['medium']['orange'], | |
| 33 | - ]; | |
| 34 | - } | |
| 35 | - | |
| 36 | - public function getColors(): array { return $this->colors; } | |
| 37 | - public function getComment(): ?string { return $this->comment; } | |
| 38 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 39 | - public function getTokens(): array { return $this->tokens; } | |
| 40 | -} | |
| 28 | +} |
| @@ -1,39 +1,44 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * Motorola 68000(68k) | |
| 6 | + */ | |
| 7 | +class Asm68000 extends Assembly { | |
| 8 | + protected string $identifier = '[.A-Za-z_][\w.$]*'; | |
| 9 | + // move.l → move、dc.w → dc | |
| 10 | + protected array $suffixes = ['/\.[bwlsdxp]$/']; | |
| 5 | 11 | |
| 6 | -class Asm68000 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 12 | + protected array $rules = [ | |
| 13 | + // 行頭の「*」もコメント | |
| 14 | + ['re' => '\*[^\n]*', 'class' => 'comment', 'bol' => true], | |
| 15 | + ]; | |
| 10 | 16 | |
| 11 | - private array $tokens = [ | |
| 17 | + protected array $tokens = [ | |
| 12 | 18 | 'register' => [ |
| 13 | - // 16ビット | |
| 19 | + 'sp', 'usp', 'ssp', 'pc', 'sr', 'ccr', 'vbr', 'sfc', 'dfc', | |
| 14 | 20 | ], |
| 15 | - 'operator' => [ | |
| 21 | + 'instruction' => [ | |
| 22 | + 'move', 'movea', 'moveq', 'movem', 'movep', 'lea', 'pea', 'exg', 'swap', 'ext', 'extb', 'clr', 'tst', 'tas', | |
| 23 | + 'add', 'adda', 'addi', 'addq', 'addx', 'sub', 'suba', 'subi', 'subq', 'subx', 'muls', 'mulu', 'divs', 'divu', 'neg', 'negx', | |
| 24 | + 'cmp', 'cmpa', 'cmpi', 'cmpm', 'chk', 'abcd', 'sbcd', 'nbcd', | |
| 25 | + 'and', 'andi', 'or', 'ori', 'eor', 'eori', 'not', 'lsl', 'lsr', 'asl', 'asr', 'rol', 'ror', 'roxl', 'roxr', | |
| 26 | + 'btst', 'bset', 'bclr', 'bchg', 'bra', 'bsr', 'jmp', 'jsr', 'rts', 'rte', 'rtr', 'trap', 'trapv', 'link', 'unlk', | |
| 27 | + 'nop', 'reset', 'stop', 'illegal', 'dbra', | |
| 16 | 28 | ], |
| 17 | 29 | 'directive' => [ |
| 30 | + 'dc', 'ds', 'dcb', 'even', 'odd', 'cnop', 'org', 'equ', 'set', 'rs', 'rsreset', 'rsset', 'section', 'text', 'data', 'bss', | |
| 31 | + 'xdef', 'xref', 'public', 'include', 'incbin', 'end', 'macro', 'endm', 'rept', 'endr', 'if', 'else', 'endc', 'endif', | |
| 32 | + 'ifd', 'ifnd', 'ifeq', 'ifne', 'opt', 'align', | |
| 18 | 33 | ], |
| 19 | 34 | ]; |
| 20 | 35 | |
| 21 | - public function __construct() { | |
| 22 | - global $colorPalette; | |
| 23 | - | |
| 24 | - $this->colors = [ | |
| 25 | - 'comment' => $colorPalette['dark']['green'], | |
| 26 | - 'directive' => $colorPalette['dark']['pink'], | |
| 27 | - 'label' => $colorPalette['light']['yellow'], | |
| 28 | - 'number' => $colorPalette['light']['green'], | |
| 29 | - 'operator' => $colorPalette['dark']['blue'], | |
| 30 | - 'register' => $colorPalette['dark']['cyan'], | |
| 31 | - 'string' => $colorPalette['medium']['orange'], | |
| 32 | - ]; | |
| 36 | + public function prepare(string $code): void { | |
| 37 | + $tokens = $this->tokens; | |
| 38 | + foreach (['d', 'a', 'fp'] as $p) foreach (self::numbered($p, 0, 7) as $r) $tokens['register'][] = $r; | |
| 39 | + // 条件付き命令(beq, dbne, sne, trapcc等) | |
| 40 | + $cc = ['t', 'f', 'hi', 'ls', 'cc', 'hs', 'cs', 'lo', 'ne', 'eq', 'vc', 'vs', 'pl', 'mi', 'ge', 'lt', 'gt', 'le']; | |
| 41 | + foreach ($cc as $c) array_push($tokens['instruction'], "b{$c}", "db{$c}", "s{$c}", "trap{$c}"); | |
| 42 | + $this->words = $this->buildWords($tokens); | |
| 33 | 43 | } |
| 34 | - | |
| 35 | - public function getColors(): array { return $this->colors; } | |
| 36 | - public function getComment(): ?string { return $this->comment; } | |
| 37 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 38 | - public function getTokens(): array { return $this->tokens; } | |
| 39 | -} | |
| 44 | +} |
| @@ -1,56 +1,52 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * AArch32(ARM・Thumb、GASのUAL記法) | |
| 6 | + */ | |
| 7 | +class AsmAarch32 extends Assembly { | |
| 8 | + protected array $comment = ['@', '//']; | |
| 9 | + protected ?array $multiline = ['/*', '*/']; | |
| 10 | + protected bool $dollarHex = false; | |
| 11 | + protected bool $percentBinary = false; | |
| 12 | + // 幅・型(.w, .f32)→ 条件コード(addeq)→ フラグ更新(adds)の順に外す | |
| 13 | + protected array $suffixes = [ | |
| 14 | + '/\.(?:w|n|[iusfp]?(?:8|16|32|64)|f16|f32|f64)$/', | |
| 15 | + '/(?:eq|ne|cs|hs|cc|lo|mi|pl|vs|vc|hi|ls|ge|lt|gt|le|al)$/', | |
| 16 | + '/s$/', | |
| 17 | + ]; | |
| 5 | 18 | |
| 6 | -class AsmAarch32 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 19 | + protected array $tokens = [ | |
| 12 | 20 | 'register' => [ |
| 13 | - // フロート、SIMD、ベクター | |
| 14 | - 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', // 64 | |
| 15 | - 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19', // 64 | |
| 16 | - 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', 'd31', // 64 | |
| 17 | - 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', // 32 | |
| 18 | - 's10', 's11', 's12', 's13', 's14', 's15', 's16', 's17', 's18', 's19', // 32 | |
| 19 | - 's20', 's21', 's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', // 32 | |
| 20 | - 'q0', 'q1', 'q2', 'q3', 'q4', 'q5', 'q6', 'q7', 'q8', 'q9', 'q10', 'q11', 'q12', 'q13', 'q14', 'q15', | |
| 21 | - // GPR | |
| 22 | - 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12', | |
| 23 | - 'r13' /*スタックポインター*/, 'r14' /*リンクレジスター*/, 'r15' /*プログラムカウンター*/, | |
| 21 | + 'sp', 'lr', 'pc', 'fp', 'ip', 'sl', 'sb', 'apsr', 'cpsr', 'spsr', 'fpscr', 'fpexc', | |
| 24 | 22 | ], |
| 25 | - 'operator' => [ | |
| 26 | - 'add', 'sub', 'mul', 'umul', 'mla', 'madd', 'msub', | |
| 27 | - 'and', 'orr', 'eor', 'bic', 'mvn', 'eon', 'ldr', 'bl', | |
| 28 | - 'ldm', 'stm', 'ldrd', 'strd', 'ldrex', 'strex', | |
| 29 | - 'vadd', 'vmul', 'vmla', 'blx', 'cmp', | |
| 23 | + 'instruction' => [ | |
| 24 | + // データ処理 | |
| 25 | + 'add', 'adc', 'sub', 'sbc', 'rsb', 'rsc', 'mul', 'mla', 'mls', 'umull', 'umlal', 'smull', 'smlal', 'sdiv', 'udiv', | |
| 26 | + 'and', 'orr', 'orn', 'eor', 'bic', 'mvn', 'mov', 'movw', 'movt', 'lsl', 'lsr', 'asr', 'ror', 'rrx', | |
| 27 | + 'cmp', 'cmn', 'tst', 'teq', 'clz', 'rev', 'rev16', 'rbit', 'ubfx', 'sbfx', 'bfi', 'bfc', 'uxtb', 'uxth', 'sxtb', 'sxth', 'usat', 'ssat', | |
| 28 | + 'qadd', 'qsub', 'uadd8', 'usub8', 'sel', | |
| 29 | + // 分岐 | |
| 30 | + 'b', 'bl', 'bx', 'blx', 'cbz', 'cbnz', 'tbb', 'tbh', 'it', 'ite', 'itt', 'itte', 'ittt', 'itee', | |
| 31 | + // ロード・ストア | |
| 32 | + 'ldr', 'ldrb', 'ldrh', 'ldrsb', 'ldrsh', 'ldrd', 'str', 'strb', 'strh', 'strd', 'ldm', 'ldmia', 'ldmdb', 'stm', 'stmia', 'stmdb', | |
| 33 | + 'push', 'pop', 'ldrex', 'strex', 'clrex', 'pld', 'adr', | |
| 34 | + // システム | |
| 35 | + 'svc', 'swi', 'bkpt', 'nop', 'wfi', 'wfe', 'sev', 'dmb', 'dsb', 'isb', 'mrs', 'msr', 'cpsie', 'cpsid', 'mrc', 'mcr', | |
| 36 | + // VFP・NEON | |
| 37 | + 'vmov', 'vadd', 'vsub', 'vmul', 'vdiv', 'vmla', 'vmls', 'vfma', 'vneg', 'vabs', 'vsqrt', 'vcmp', 'vcvt', 'vldr', 'vstr', 'vldm', 'vstm', | |
| 38 | + 'vpush', 'vpop', 'vmrs', 'vmsr', 'vld1', 'vst1', 'vdup', 'vand', 'vorr', 'veor', 'vmax', 'vmin', | |
| 30 | 39 | ], |
| 31 | - 'directive' => [ | |
| 32 | - '.arch armv8-a', 'arch_extension', '.cfi_', '.global', '.globl', '.section', | |
| 33 | - '.equ', '.set', '.byte', '.hword', '.word', '.quad', | |
| 34 | - '.arm', '.code 32', '.thumb', '.code 16', | |
| 40 | + 'keyword' => [ | |
| 41 | + 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs', 'vc', 'hi', 'ls', 'ge', 'lt', 'gt', 'le', 'al', | |
| 35 | 42 | ], |
| 36 | 43 | ]; |
| 37 | 44 | |
| 38 | - public function __construct() { | |
| 39 | - global $colorPalette; | |
| 40 | - | |
| 41 | - $this->colors = [ | |
| 42 | - 'comment' => $colorPalette['dark']['green'], | |
| 43 | - 'directive' => $colorPalette['dark']['pink'], | |
| 44 | - 'label' => $colorPalette['light']['yellow'], | |
| 45 | - 'number' => $colorPalette['light']['green'], | |
| 46 | - 'operator' => $colorPalette['dark']['blue'], | |
| 47 | - 'register' => $colorPalette['dark']['cyan'], | |
| 48 | - 'string' => $colorPalette['medium']['orange'], | |
| 49 | - ]; | |
| 45 | + public function prepare(string $code): void { | |
| 46 | + $tokens = $this->tokens; | |
| 47 | + foreach (self::numbered('r', 0, 15) as $r) $tokens['register'][] = $r; | |
| 48 | + foreach (['s', 'd'] as $p) foreach (self::numbered($p, 0, 31) as $r) $tokens['register'][] = $r; | |
| 49 | + foreach (self::numbered('q', 0, 15) as $r) $tokens['register'][] = $r; | |
| 50 | + $this->words = $this->buildWords($tokens); | |
| 50 | 51 | } |
| 51 | - | |
| 52 | - public function getColors(): array { return $this->colors; } | |
| 53 | - public function getComment(): ?string { return $this->comment; } | |
| 54 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 55 | - public function getTokens(): array { return $this->tokens; } | |
| 56 | -} | |
| 52 | +} |
| @@ -1,65 +1,56 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * AArch64(GAS。Appleのアセンブラの「;」コメントも対応) | |
| 6 | + */ | |
| 7 | +class AsmAarch64 extends Assembly { | |
| 8 | + protected array $comment = ['//', ';']; | |
| 9 | + protected ?array $multiline = ['/*', '*/']; | |
| 10 | + protected bool $dollarHex = false; | |
| 11 | + protected bool $percentBinary = false; | |
| 12 | + // b.eq → b、v0.4s → v0、ld1.16b等 | |
| 13 | + protected array $suffixes = ['/\.[a-z0-9]+$/']; | |
| 5 | 14 | |
| 6 | -class AsmAarch64 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 15 | + protected array $tokens = [ | |
| 12 | 16 | 'register' => [ |
| 13 | - // フロート、SIMD、ベクター | |
| 14 | - 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', // 64 | |
| 15 | - 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19', // 64 | |
| 16 | - 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', 'd31', // 64 | |
| 17 | - 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', // 32 | |
| 18 | - 's10', 's11', 's12', 's13', 's14', 's15', 's16', 's17', 's18', 's19', // 32 | |
| 19 | - 's20', 's21', 's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', // 32 | |
| 20 | - 'v0', 'v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7', 'v8', 'v9', | |
| 21 | - 'v10', 'v11', 'v12', 'v13', 'v14', 'v15', 'v16', 'v17', 'v18', 'v19', | |
| 22 | - 'v20', 'v21', 'v22', 'v23', 'v24', 'v25', 'v26', 'v27', 'v28', 'v29', 'v30', 'v31', | |
| 23 | - 'q16', 'q17', 'q18', 'q19', 'q20', 'q21', 'q22', 'q23', 'q24', 'q25', 'q26', 'q27', 'q28', 'q29', 'q30', 'q31', | |
| 24 | - // GPR | |
| 25 | - 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', | |
| 26 | - 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', | |
| 27 | - 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'x31' /*ゼロレジスター*/, | |
| 28 | - 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'w7', 'w8', 'w9', | |
| 29 | - 'w10', 'w11', 'w12', 'w13', 'w14', 'w15', 'w16', 'w17', 'w18', 'w19', | |
| 30 | - 'w20', 'w21', 'w22', 'w23', 'w24', 'w25', 'w26', 'w27', 'w28', 'w29', 'w30', | |
| 31 | - 'xzr' /*ゼロレジスター*/, 'wzr' /*ゼロレジスター*/, | |
| 32 | - 'sp' /*スタックポインター*/, 'pc' /*プログラムカウンター*/, | |
| 17 | + 'sp', 'wsp', 'xzr', 'wzr', 'lr', 'fp', 'pc', 'nzcv', 'fpcr', 'fpsr', 'tpidr_el0', | |
| 33 | 18 | ], |
| 34 | - 'operator' => [ | |
| 35 | - 'add', 'sub', 'mul', 'umul', 'mla', 'madd', 'msub', | |
| 36 | - 'and', 'orr', 'eor', 'bic', 'mvn', 'eon', 'ldr', 'bl', | |
| 37 | - 'csel', 'cinc', 'cset', 'addv', 'smull', 'fmla', 'blr', 'cbz', 'cbnz', 'tbz', 'tbnz', | |
| 38 | - 'adpr', 'movz', 'movk', 'ldp', 'stp', 'ldxr', 'stxr', 'ldadd', 'ldclr', 'ldset', | |
| 19 | + 'instruction' => [ | |
| 20 | + // データ処理 | |
| 21 | + 'add', 'adds', 'sub', 'subs', 'adc', 'adcs', 'sbc', 'sbcs', 'neg', 'negs', 'ngc', 'cmp', 'cmn', 'tst', | |
| 22 | + 'mul', 'madd', 'msub', 'mneg', 'smull', 'umull', 'smulh', 'umulh', 'smaddl', 'umaddl', 'sdiv', 'udiv', | |
| 23 | + 'and', 'ands', 'orr', 'orn', 'eor', 'eon', 'bic', 'bics', 'mvn', 'lsl', 'lsr', 'asr', 'ror', | |
| 24 | + 'mov', 'movz', 'movn', 'movk', 'adr', 'adrp', 'sxtb', 'sxth', 'sxtw', 'uxtb', 'uxth', 'ubfx', 'sbfx', 'ubfiz', 'sbfiz', 'bfi', 'bfxil', | |
| 25 | + 'ubfm', 'sbfm', 'bfm', 'extr', 'clz', 'cls', 'rbit', 'rev', 'rev16', 'rev32', | |
| 26 | + 'csel', 'csinc', 'csinv', 'csneg', 'cset', 'csetm', 'cinc', 'cinv', 'cneg', 'ccmp', 'ccmn', | |
| 27 | + // 分岐 | |
| 28 | + 'b', 'bl', 'br', 'blr', 'ret', 'cbz', 'cbnz', 'tbz', 'tbnz', 'braa', 'blraa', 'retaa', | |
| 29 | + // ロード・ストア | |
| 30 | + 'ldr', 'ldrb', 'ldrh', 'ldrsb', 'ldrsh', 'ldrsw', 'ldur', 'ldurb', 'ldurh', 'str', 'strb', 'strh', 'stur', 'sturb', 'sturh', | |
| 31 | + 'ldp', 'stp', 'ldnp', 'stnp', 'ldxr', 'stxr', 'ldaxr', 'stlxr', 'ldar', 'stlr', 'ldxp', 'stxp', 'prfm', | |
| 32 | + 'ldadd', 'ldadda', 'ldaddal', 'ldclr', 'ldset', 'ldeor', 'swp', 'swpal', 'cas', 'casal', | |
| 33 | + // システム | |
| 34 | + 'svc', 'hvc', 'smc', 'brk', 'hlt', 'nop', 'yield', 'wfe', 'wfi', 'sev', 'dmb', 'dsb', 'isb', 'mrs', 'msr', 'sys', 'paciasp', 'autiasp', 'bti', | |
| 35 | + // 浮動小数点・NEON | |
| 36 | + 'fmov', 'fadd', 'fsub', 'fmul', 'fdiv', 'fmadd', 'fmsub', 'fnmadd', 'fneg', 'fabs', 'fsqrt', 'fmin', 'fmax', 'fminnm', 'fmaxnm', 'fcmp', 'fcsel', | |
| 37 | + 'fcvt', 'fcvtzs', 'fcvtzu', 'fcvtns', 'scvtf', 'ucvtf', 'frintn', 'frintm', 'frintp', 'frintz', | |
| 38 | + 'fmla', 'fmls', 'addv', 'addp', 'faddp', 'umov', 'smov', 'ins', 'dup', 'movi', 'zip1', 'zip2', 'uzp1', 'uzp2', 'trn1', 'trn2', 'ext', 'tbl', | |
| 39 | + 'ld1', 'ld2', 'ld3', 'ld4', 'st1', 'st2', 'st3', 'st4', 'ld1r', 'cnt', 'uaddlv', 'saddlv', 'shl', 'sshr', 'ushr', 'xtn', 'sqxtn', 'uqxtn', | |
| 40 | + 'cmeq', 'cmgt', 'cmge', 'fcmeq', 'fcmgt', 'bsl', 'bit', 'bif', 'not', 'abs', 'mla', 'mls', 'smlal', 'umlal', 'sdot', 'udot', 'fmlal', | |
| 39 | 41 | ], |
| 40 | - 'directive' => [ | |
| 41 | - '.arch armv8-a', 'arch_extension', '.cfi_', '.global', '.globl', '.section', | |
| 42 | - '.equ', '.set', '.byte', '.hword', '.word', '.quad', | |
| 43 | - '.req', '.aarch64', | |
| 42 | + 'keyword' => [ | |
| 43 | + // 条件コード・シフト・拡張 | |
| 44 | + 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs', 'vc', 'hi', 'ls', 'ge', 'lt', 'gt', 'le', 'al', 'nv', | |
| 45 | + 'uxtw', 'sxtx', 'uxtx', 'msl', | |
| 44 | 46 | ], |
| 45 | 47 | ]; |
| 46 | 48 | |
| 47 | - public function __construct() { | |
| 48 | - global $colorPalette; | |
| 49 | - | |
| 50 | - $this->colors = [ | |
| 51 | - 'comment' => $colorPalette['dark']['green'], | |
| 52 | - 'directive' => $colorPalette['dark']['pink'], | |
| 53 | - 'label' => $colorPalette['light']['yellow'], | |
| 54 | - 'number' => $colorPalette['light']['green'], | |
| 55 | - 'operator' => $colorPalette['dark']['blue'], | |
| 56 | - 'register' => $colorPalette['dark']['cyan'], | |
| 57 | - 'string' => $colorPalette['medium']['orange'], | |
| 58 | - ]; | |
| 49 | + public function prepare(string $code): void { | |
| 50 | + $tokens = $this->tokens; | |
| 51 | + foreach (['x', 'w'] as $p) foreach (self::numbered($p, 0, 30) as $r) $tokens['register'][] = $r; | |
| 52 | + foreach (['v', 'q', 'd', 's', 'h', 'b', 'z'] as $p) foreach (self::numbered($p, 0, 31) as $r) $tokens['register'][] = $r; | |
| 53 | + foreach (self::numbered('p', 0, 15) as $r) $tokens['register'][] = $r; | |
| 54 | + $this->words = $this->buildWords($tokens); | |
| 59 | 55 | } |
| 60 | - | |
| 61 | - public function getColors(): array { return $this->colors; } | |
| 62 | - public function getComment(): ?string { return $this->comment; } | |
| 63 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 64 | - public function getTokens(): array { return $this->tokens; } | |
| 65 | -} | |
| 56 | +} |
| @@ -1,40 +1,41 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * MIPS(MIPS32/MIPS64、GAS) | |
| 6 | + */ | |
| 7 | +class AsmMips extends Assembly { | |
| 8 | + protected array $comment = ['#']; | |
| 9 | + protected ?array $multiline = ['/*', '*/']; | |
| 10 | + // 「$」はレジスター($t0、$31)なので16進数ではない | |
| 11 | + protected bool $dollarHex = false; | |
| 12 | + protected bool $percentBinary = false; | |
| 13 | + protected string $immediatePrefix = ''; | |
| 14 | + protected string $identifier = '\$\d+|[%$]?[.A-Za-z_][\w.$]*'; | |
| 15 | + // add.s → add、c.eq.d → c.eq → c | |
| 16 | + protected array $suffixes = ['/\.[a-z0-9]+$/', '/\.[a-z0-9]+$/']; | |
| 5 | 17 | |
| 6 | -class AsmMips implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 18 | + protected array $tokens = [ | |
| 12 | 19 | 'register' => [ |
| 13 | - // 64ビット | |
| 14 | - // 32ビット | |
| 20 | + 'zero', 'at', 'v0', 'v1', 'a0', 'a1', 'a2', 'a3', 'k0', 'k1', 'gp', 'sp', 'fp', 'ra', 'hi', 'lo', 'pc', | |
| 15 | 21 | ], |
| 16 | - 'operator' => [ | |
| 22 | + 'instruction' => [ | |
| 23 | + 'add', 'addu', 'addi', 'addiu', 'sub', 'subu', 'mult', 'multu', 'mul', 'madd', 'div', 'divu', 'mfhi', 'mflo', 'mthi', 'mtlo', | |
| 24 | + 'dadd', 'daddu', 'daddi', 'daddiu', 'dsub', 'dsubu', 'dmult', 'ddiv', | |
| 25 | + 'and', 'andi', 'or', 'ori', 'xor', 'xori', 'nor', 'sll', 'srl', 'sra', 'sllv', 'srlv', 'srav', 'dsll', 'dsrl', 'dsra', 'slt', 'sltu', 'slti', 'sltiu', | |
| 26 | + 'lui', 'lb', 'lbu', 'lh', 'lhu', 'lw', 'lwu', 'ld', 'lwl', 'lwr', 'll', 'sb', 'sh', 'sw', 'sd', 'swl', 'swr', 'sc', | |
| 27 | + 'beq', 'bne', 'bgez', 'bgtz', 'blez', 'bltz', 'bgezal', 'bltzal', 'j', 'jal', 'jr', 'jalr', 'syscall', 'break', 'eret', 'sync', 'nop', | |
| 28 | + 'mfc0', 'mtc0', 'mfc1', 'mtc1', 'lwc1', 'swc1', 'ldc1', 'sdc1', 'movn', 'movz', 'teq', 'ext', 'ins', 'seb', 'seh', 'clz', 'clo', | |
| 29 | + 'abs', 'cvt', 'c', 'bc1t', 'bc1f', 'mov', 'neg', 'sqrt', 'trunc', 'round', 'floor', 'ceil', | |
| 30 | + // 疑似命令 | |
| 31 | + 'li', 'la', 'move', 'b', 'bal', 'beqz', 'bnez', 'bge', 'bgt', 'ble', 'blt', 'not', 'negu', | |
| 17 | 32 | ], |
| 18 | - 'directive' => [ | |
| 19 | - ], | |
| 20 | 33 | ]; |
| 21 | 34 | |
| 22 | - public function __construct() { | |
| 23 | - global $colorPalette; | |
| 24 | - | |
| 25 | - $this->colors = [ | |
| 26 | - 'comment' => $colorPalette['dark']['green'], | |
| 27 | - 'directive' => $colorPalette['dark']['pink'], | |
| 28 | - 'label' => $colorPalette['light']['yellow'], | |
| 29 | - 'number' => $colorPalette['light']['green'], | |
| 30 | - 'operator' => $colorPalette['dark']['blue'], | |
| 31 | - 'register' => $colorPalette['dark']['cyan'], | |
| 32 | - 'string' => $colorPalette['medium']['orange'], | |
| 33 | - ]; | |
| 35 | + public function prepare(string $code): void { | |
| 36 | + $tokens = $this->tokens; | |
| 37 | + $names = array_merge(self::numbered('t', 0, 9), self::numbered('s', 0, 8), self::numbered('f', 0, 31), self::numbered('', 0, 31)); | |
| 38 | + foreach ($names as $r) $tokens['register'][] = $r; | |
| 39 | + $this->words = $this->buildWords($tokens); | |
| 34 | 40 | } |
| 35 | - | |
| 36 | - public function getColors(): array { return $this->colors; } | |
| 37 | - public function getComment(): ?string { return $this->comment; } | |
| 38 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 39 | - public function getTokens(): array { return $this->tokens; } | |
| 40 | -} | |
| 41 | +} |
| @@ -1,40 +1,50 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * POWER・PowerPC(GAS) | |
| 6 | + * GASではレジスターをただの数字で書く事が多い(ld 2, 24(12))ので、「r2」「%r2」の形だけを色付けする | |
| 7 | + */ | |
| 8 | +class AsmPower extends Assembly { | |
| 9 | + protected array $comment = ['#']; | |
| 10 | + protected ?array $multiline = ['/*', '*/']; | |
| 11 | + protected bool $dollarHex = false; | |
| 12 | + protected bool $percentBinary = false; | |
| 13 | + protected string $immediatePrefix = ''; | |
| 14 | + // add. → add(条件レジスターを更新する形) | |
| 15 | + protected array $suffixes = ['/\.$/']; | |
| 5 | 16 | |
| 6 | -class AsmPower implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 17 | + protected array $tokens = [ | |
| 12 | 18 | 'register' => [ |
| 13 | - // 64ビット | |
| 14 | - // 32ビット | |
| 19 | + 'lr', 'ctr', 'xer', 'msr', 'cr', 'fpscr', 'vrsave', 'vscr', 'sp', 'toc', 'rtoc', | |
| 15 | 20 | ], |
| 16 | - 'operator' => [ | |
| 21 | + 'instruction' => [ | |
| 22 | + 'li', 'lis', 'la', 'addi', 'addis', 'add', 'addc', 'adde', 'addze', 'addme', 'addic', 'subf', 'subfc', 'subfe', 'subfic', 'sub', 'subi', 'neg', | |
| 23 | + 'mullw', 'mulld', 'mulhw', 'mulhwu', 'mulhd', 'mulhdu', 'mulli', 'divw', 'divwu', 'divd', 'divdu', 'modsw', 'moduw', | |
| 24 | + 'and', 'andc', 'andi', 'andis', 'or', 'orc', 'ori', 'oris', 'xor', 'xori', 'xoris', 'nor', 'nand', 'eqv', 'not', 'mr', | |
| 25 | + 'slw', 'srw', 'sraw', 'srawi', 'sld', 'srd', 'srad', 'sradi', 'rlwinm', 'rlwimi', 'rlwnm', 'rldicl', 'rldicr', 'rldic', 'rldimi', | |
| 26 | + 'slwi', 'srwi', 'sldi', 'srdi', 'clrlwi', 'clrldi', 'extsb', 'extsh', 'extsw', 'cntlzw', 'cntlzd', 'popcntw', 'popcntd', | |
| 27 | + 'lbz', 'lbzu', 'lbzx', 'lhz', 'lhzu', 'lha', 'lhax', 'lwz', 'lwzu', 'lwzx', 'lwa', 'lwax', 'ld', 'ldu', 'ldx', 'lmw', 'lwarx', 'ldarx', | |
| 28 | + 'stb', 'stbu', 'stbx', 'sth', 'sthu', 'sthx', 'stw', 'stwu', 'stwx', 'std', 'stdu', 'stdx', 'stmw', 'stwcx', 'stdcx', | |
| 29 | + 'lfs', 'lfd', 'stfs', 'stfd', 'fadd', 'fadds', 'fsub', 'fmul', 'fmuls', 'fdiv', 'fmadd', 'fmr', 'fneg', 'fabs', 'fsqrt', 'fcmpu', 'fctiwz', 'fcfid', | |
| 30 | + 'cmpw', 'cmpwi', 'cmplw', 'cmplwi', 'cmpd', 'cmpdi', 'cmpld', 'cmpldi', 'cmp', 'cmpi', 'cmpl', 'cmpli', | |
| 31 | + 'b', 'ba', 'bl', 'bla', 'bc', 'bcl', 'blr', 'blrl', 'bctr', 'bctrl', 'beq', 'bne', 'blt', 'bgt', 'ble', 'bge', 'bso', 'bns', 'bdnz', 'bdz', | |
| 32 | + 'beqlr', 'bnelr', 'bltlr', 'bgtlr', 'blelr', 'bgelr', | |
| 33 | + 'mflr', 'mtlr', 'mfctr', 'mtctr', 'mfcr', 'mtcrf', 'mfxer', 'mtxer', 'mfspr', 'mtspr', 'mfmsr', 'mtmsr', | |
| 34 | + 'sc', 'nop', 'isync', 'sync', 'lwsync', 'eieio', 'dcbf', 'dcbst', 'icbi', 'trap', 'tw', 'twi', 'td', 'tdi', | |
| 35 | + 'lvx', 'stvx', 'vaddfp', 'vmaddfp', 'vperm', 'lxvd2x', 'stxvd2x', 'xvadddp', 'xvmuldp', | |
| 17 | 36 | ], |
| 18 | - 'directive' => [ | |
| 37 | + 'keyword' => [ | |
| 38 | + // シンボル修飾子(msg@toc、x@ha) | |
| 39 | + 'ha', 'got', 'plt', 'tocbase', 'localentry', | |
| 19 | 40 | ], |
| 20 | 41 | ]; |
| 21 | 42 | |
| 22 | - public function __construct() { | |
| 23 | - global $colorPalette; | |
| 24 | - | |
| 25 | - $this->colors = [ | |
| 26 | - 'comment' => $colorPalette['dark']['green'], | |
| 27 | - 'directive' => $colorPalette['dark']['pink'], | |
| 28 | - 'label' => $colorPalette['light']['yellow'], | |
| 29 | - 'number' => $colorPalette['light']['green'], | |
| 30 | - 'operator' => $colorPalette['dark']['blue'], | |
| 31 | - 'register' => $colorPalette['dark']['cyan'], | |
| 32 | - 'string' => $colorPalette['medium']['orange'], | |
| 33 | - ]; | |
| 43 | + public function prepare(string $code): void { | |
| 44 | + $tokens = $this->tokens; | |
| 45 | + foreach (['r', 'f', 'v'] as $p) foreach (self::numbered($p, 0, 31) as $r) $tokens['register'][] = $r; | |
| 46 | + foreach (self::numbered('vs', 0, 63) as $r) $tokens['register'][] = $r; | |
| 47 | + foreach (self::numbered('cr', 0, 7) as $r) $tokens['register'][] = $r; | |
| 48 | + $this->words = $this->buildWords($tokens); | |
| 34 | 49 | } |
| 35 | - | |
| 36 | - public function getColors(): array { return $this->colors; } | |
| 37 | - public function getComment(): ?string { return $this->comment; } | |
| 38 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 39 | - public function getTokens(): array { return $this->tokens; } | |
| 40 | -} | |
| 50 | +} |
| @@ -1,41 +1,53 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * RISC-V(RV32/RV64、GAS) | |
| 6 | + */ | |
| 7 | +class AsmRiscv extends Assembly { | |
| 8 | + protected array $comment = ['#']; | |
| 9 | + protected ?array $multiline = ['/*', '*/']; | |
| 10 | + protected bool $dollarHex = false; | |
| 11 | + protected bool $percentBinary = false; | |
| 12 | + protected string $immediatePrefix = ''; | |
| 13 | + // fcvt.w.s → fcvt.w → fcvt、lr.w.aq → lr、c.addi → addi | |
| 14 | + protected array $suffixes = ['/\.[a-z0-9]+$/', '/\.[a-z0-9]+$/', '/^c\./']; | |
| 5 | 15 | |
| 6 | -class AsmRiscV implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 16 | + protected array $tokens = [ | |
| 12 | 17 | 'register' => [ |
| 13 | - // 128ビット | |
| 14 | - // 64ビット | |
| 15 | - // 32ビット | |
| 18 | + 'zero', 'ra', 'sp', 'gp', 'tp', 'fp', 'pc', | |
| 16 | 19 | ], |
| 17 | - 'operator' => [ | |
| 20 | + 'instruction' => [ | |
| 21 | + // RV32I/RV64I | |
| 22 | + 'lui', 'auipc', 'jal', 'jalr', 'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu', 'lb', 'lh', 'lw', 'lbu', 'lhu', 'lwu', 'ld', 'sb', 'sh', 'sw', 'sd', | |
| 23 | + 'addi', 'slti', 'sltiu', 'xori', 'ori', 'andi', 'slli', 'srli', 'srai', 'add', 'sub', 'sll', 'slt', 'sltu', 'xor', 'srl', 'sra', 'or', 'and', | |
| 24 | + 'addiw', 'slliw', 'srliw', 'sraiw', 'addw', 'subw', 'sllw', 'srlw', 'sraw', 'fence', 'ecall', 'ebreak', | |
| 25 | + // M・A | |
| 26 | + 'mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu', 'mulw', 'divw', 'divuw', 'remw', 'remuw', | |
| 27 | + 'lr', 'sc', 'amoswap', 'amoadd', 'amoxor', 'amoand', 'amoor', 'amomin', 'amomax', 'amominu', 'amomaxu', | |
| 28 | + // F・D | |
| 29 | + 'flw', 'fsw', 'fld', 'fsd', 'fadd', 'fsub', 'fmul', 'fdiv', 'fsqrt', 'fmin', 'fmax', 'fmadd', 'fmsub', 'fnmadd', 'fnmsub', | |
| 30 | + 'fsgnj', 'fsgnjn', 'fsgnjx', 'fcvt', 'fmv', 'feq', 'flt', 'fle', 'fclass', | |
| 31 | + // Zicsr・特権 | |
| 32 | + 'csrrw', 'csrrs', 'csrrc', 'csrrwi', 'csrrsi', 'csrrci', 'csrr', 'csrw', 'csrs', 'csrc', 'mret', 'sret', 'wfi', 'sfence', | |
| 33 | + // B・V(よく使う物) | |
| 34 | + 'andn', 'orn', 'xnor', 'clz', 'ctz', 'cpop', 'min', 'max', 'minu', 'maxu', 'rol', 'ror', 'rev8', 'sext', 'zext', | |
| 35 | + 'vsetvli', 'vsetivli', 'vsetvl', 'vle8', 'vle16', 'vle32', 'vle64', 'vse8', 'vse16', 'vse32', 'vse64', 'vadd', 'vsub', 'vmul', 'vfadd', 'vfmul', | |
| 36 | + 'vfmacc', 'vredsum', 'vfredusum', 'vmv', | |
| 37 | + // 疑似命令 | |
| 38 | + 'li', 'la', 'lla', 'mv', 'not', 'neg', 'negw', 'nop', 'j', 'jr', 'ret', 'call', 'tail', 'seqz', 'snez', 'sltz', 'sgtz', | |
| 39 | + 'beqz', 'bnez', 'blez', 'bgez', 'bltz', 'bgtz', 'bgt', 'ble', 'bgtu', 'bleu', 'fabs', 'fneg', | |
| 18 | 40 | ], |
| 19 | - 'directive' => [ | |
| 20 | - ], | |
| 21 | 41 | ]; |
| 22 | 42 | |
| 23 | - public function __construct() { | |
| 24 | - global $colorPalette; | |
| 25 | - | |
| 26 | - $this->colors = [ | |
| 27 | - 'comment' => $colorPalette['dark']['green'], | |
| 28 | - 'directive' => $colorPalette['dark']['pink'], | |
| 29 | - 'label' => $colorPalette['light']['yellow'], | |
| 30 | - 'number' => $colorPalette['light']['green'], | |
| 31 | - 'operator' => $colorPalette['dark']['blue'], | |
| 32 | - 'register' => $colorPalette['dark']['cyan'], | |
| 33 | - 'string' => $colorPalette['medium']['orange'], | |
| 34 | - ]; | |
| 43 | + public function prepare(string $code): void { | |
| 44 | + $tokens = $this->tokens; | |
| 45 | + $names = array_merge( | |
| 46 | + self::numbered('x', 0, 31), self::numbered('f', 0, 31), self::numbered('v', 0, 31), | |
| 47 | + self::numbered('t', 0, 6), self::numbered('s', 0, 11), self::numbered('a', 0, 7), | |
| 48 | + self::numbered('ft', 0, 11), self::numbered('fs', 0, 11), self::numbered('fa', 0, 7), | |
| 49 | + ); | |
| 50 | + foreach ($names as $r) $tokens['register'][] = $r; | |
| 51 | + $this->words = $this->buildWords($tokens); | |
| 35 | 52 | } |
| 36 | - | |
| 37 | - public function getColors(): array { return $this->colors; } | |
| 38 | - public function getComment(): ?string { return $this->comment; } | |
| 39 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 40 | - public function getTokens(): array { return $this->tokens; } | |
| 41 | -} | |
| 53 | +} |
| @@ -1,56 +1,92 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * x86-64(NASMとGASのAT&T記法の両方) | |
| 6 | + */ | |
| 7 | +class AsmX8664 extends Assembly { | |
| 8 | + protected array $comment = [';', '#']; | |
| 9 | + protected string $immediatePrefix = ''; | |
| 10 | + protected bool $percentBinary = false; | |
| 11 | + protected bool $percentMacros = true; | |
| 12 | + protected array $prefixes = ['rep', 'repe', 'repne', 'repz', 'repnz', 'lock', 'xacquire', 'xrelease', 'bnd', 'notrack', 'data16', 'addr32', 'rex', 'rex.w']; | |
| 5 | 13 | |
| 6 | -class AsmX8664 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 14 | + protected array $tokens = [ | |
| 12 | 15 | 'register' => [ |
| 13 | - // 64ビット | |
| 14 | - 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'r8', 'r8d', 'r9', 'r9d', 'r10', 'r10d', 'r11', 'r11d', 'r12', 'r12d', 'r13', 'r13d', 'r14', 'r14d', 'r15', 'r15d', | |
| 15 | - // 32ビット | |
| 16 | - 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', | |
| 17 | - // 16ビット | |
| 18 | - 'al', 'bl', 'cl', 'dl', | |
| 19 | - 'ah', 'bh', 'ch', 'dh', | |
| 20 | - 'spl', 'bpl', 'sil', 'dil', | |
| 16 | + 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', | |
| 17 | + 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp', 'esp', | |
| 18 | + 'ax', 'bx', 'cx', 'dx', 'si', 'di', 'bp', 'sp', | |
| 19 | + 'al', 'bl', 'cl', 'dl', 'ah', 'bh', 'ch', 'dh', 'sil', 'dil', 'bpl', 'spl', | |
| 20 | + 'rip', 'eip', 'ip', 'rflags', 'eflags', 'flags', | |
| 21 | + 'cs', 'ds', 'es', 'fs', 'gs', 'ss', 'st', | |
| 21 | 22 | ], |
| 22 | - 'operator' => [ | |
| 23 | - 'mov', 'lea', | |
| 24 | - 'add', 'sub', 'div', 'imul', 'dec', 'inc', | |
| 25 | - 'xor', 'or', 'and', | |
| 26 | - 'js', 'jmp', 'jz', 'jnz', 'ja', 'jb', | |
| 27 | - 'pop', 'push', | |
| 28 | - 'call', 'ret', 'syscall', 'cmp', 'test', 'stosb', | |
| 23 | + 'instruction' => [ | |
| 24 | + // データ転送 | |
| 25 | + 'mov', 'movzx', 'movsx', 'movsxd', 'movabs', 'lea', 'xchg', 'cmpxchg', 'cmpxchg8b', 'cmpxchg16b', 'xadd', 'bswap', 'push', 'pop', | |
| 26 | + 'pushf', 'popf', 'pushfq', 'popfq', 'pusha', 'popa', 'cbw', 'cwde', 'cdqe', 'cwd', 'cdq', 'cqo', 'movbe', 'movnti', | |
| 27 | + // AT&T記法のサイズ付き | |
| 28 | + 'movb', 'movw', 'movl', 'movq', 'leaq', 'leal', 'addq', 'addl', 'subq', 'subl', 'pushq', 'popq', 'cmpq', 'cmpl', 'xorl', 'xorq', | |
| 29 | + 'andq', 'andl', 'orq', 'orl', 'incq', 'incl', 'decq', 'decl', 'imulq', 'imull', 'testq', 'testl', 'shlq', 'shrq', 'sarq', 'callq', 'retq', | |
| 30 | + 'movzbl', 'movzwl', 'movsbl', 'movswl', 'movslq', 'cltq', 'cqto', 'cltd', | |
| 31 | + // 算術・論理 | |
| 32 | + 'add', 'adc', 'sub', 'sbb', 'mul', 'imul', 'div', 'idiv', 'inc', 'dec', 'neg', 'cmp', 'test', 'and', 'or', 'xor', 'not', | |
| 33 | + 'shl', 'shr', 'sal', 'sar', 'rol', 'ror', 'rcl', 'rcr', 'shld', 'shrd', 'bt', 'bts', 'btr', 'btc', 'bsf', 'bsr', | |
| 34 | + 'lzcnt', 'tzcnt', 'popcnt', 'andn', 'bextr', 'blsi', 'blsmsk', 'blsr', 'bzhi', 'mulx', 'pdep', 'pext', 'rorx', 'sarx', 'shlx', 'shrx', | |
| 35 | + 'adcx', 'adox', 'crc32', | |
| 36 | + // 分岐 | |
| 37 | + 'jmp', 'call', 'ret', 'retf', 'iret', 'iretq', 'loop', 'loope', 'loopne', 'jcxz', 'jecxz', 'jrcxz', 'enter', 'leave', | |
| 38 | + 'int', 'int3', 'into', 'syscall', 'sysret', 'sysenter', 'sysexit', | |
| 39 | + // 文字列 | |
| 40 | + 'movs', 'movsb', 'movsw', 'movsq', 'stos', 'stosb', 'stosw', 'stosd', 'stosq', 'lods', 'lodsb', 'lodsw', 'lodsd', 'lodsq', | |
| 41 | + 'scas', 'scasb', 'scasw', 'scasd', 'scasq', 'cmps', 'cmpsb', 'cmpsw', 'cmpsq', | |
| 42 | + // フラグ・その他 | |
| 43 | + 'clc', 'stc', 'cmc', 'cld', 'std', 'cli', 'sti', 'lahf', 'sahf', 'nop', 'hlt', 'pause', 'ud2', 'cpuid', 'rdtsc', 'rdtscp', 'rdrand', 'rdseed', | |
| 44 | + 'xgetbv', 'lfence', 'sfence', 'mfence', 'prefetcht0', 'prefetcht1', 'prefetcht2', 'prefetchnta', 'clflush', 'endbr64', 'in', 'out', | |
| 45 | + // x87 | |
| 46 | + 'fld', 'fst', 'fstp', 'fild', 'fist', 'fistp', 'fadd', 'faddp', 'fsub', 'fsubp', 'fmul', 'fmulp', 'fdiv', 'fdivp', 'fsqrt', 'fsin', 'fcos', | |
| 47 | + 'fxch', 'fcomi', 'fcomip', 'fldz', 'fld1', 'fninit', 'fwait', | |
| 48 | + // SSE・AVX | |
| 49 | + 'movaps', 'movups', 'movapd', 'movupd', 'movss', 'movsd', 'movd', 'movdqa', 'movdqu', 'movhlps', 'movlhps', 'movmskps', | |
| 50 | + 'addss', 'addsd', 'addps', 'addpd', 'subss', 'subsd', 'subps', 'subpd', 'mulss', 'mulsd', 'mulps', 'mulpd', 'divss', 'divsd', 'divps', 'divpd', | |
| 51 | + 'sqrtss', 'sqrtsd', 'sqrtps', 'sqrtpd', 'rsqrtss', 'rsqrtps', 'rcpss', 'rcpps', 'minss', 'minsd', 'minps', 'minpd', 'maxss', 'maxsd', 'maxps', 'maxpd', | |
| 52 | + 'andps', 'andpd', 'andnps', 'andnpd', 'orps', 'orpd', 'xorps', 'xorpd', 'cmpps', 'cmpss', 'comiss', 'comisd', 'ucomiss', 'ucomisd', | |
| 53 | + 'shufps', 'shufpd', 'unpcklps', 'unpckhps', 'unpcklpd', 'unpckhpd', 'blendps', 'blendvps', 'dpps', 'roundss', 'roundps', | |
| 54 | + 'cvtsi2ss', 'cvtsi2sd', 'cvtss2si', 'cvtsd2si', 'cvttss2si', 'cvttsd2si', 'cvtss2sd', 'cvtsd2ss', 'cvtdq2ps', 'cvtps2dq', 'cvttps2dq', | |
| 55 | + 'paddb', 'paddw', 'paddd', 'paddq', 'psubb', 'psubw', 'psubd', 'psubq', 'pmullw', 'pmulld', 'pmuludq', 'pand', 'pandn', 'por', 'pxor', | |
| 56 | + 'pcmpeqb', 'pcmpeqd', 'pcmpgtd', 'pshufb', 'pshufd', 'pslld', 'psrld', 'psrad', 'psllq', 'psrlq', 'punpcklbw', 'punpckldq', 'packuswb', | |
| 57 | + 'pmovmskb', 'ptest', 'pminsd', 'pmaxsd', 'pextrd', 'pinsrd', 'pabsd', 'phaddd', | |
| 58 | + 'vmovaps', 'vmovups', 'vmovapd', 'vmovupd', 'vmovss', 'vmovsd', 'vmovdqa', 'vmovdqu', 'vmovdqa64', 'vmovdqu8', 'vmovdqu32', | |
| 59 | + 'vaddps', 'vaddpd', 'vaddss', 'vaddsd', 'vsubps', 'vsubpd', 'vmulps', 'vmulpd', 'vdivps', 'vdivpd', 'vsqrtps', 'vminps', 'vmaxps', | |
| 60 | + 'vandps', 'vorps', 'vxorps', 'vpxor', 'vpand', 'vpor', 'vpaddd', 'vpsubd', 'vpmulld', 'vpshufb', 'vpermd', 'vpermps', 'vperm2f128', | |
| 61 | + 'vbroadcastss', 'vbroadcastsd', 'vpbroadcastd', 'vinsertf128', 'vextractf128', 'vshufps', 'vblendvps', 'vgatherdps', 'vcvtps2ph', 'vcvtph2ps', | |
| 62 | + 'vfmadd132ps', 'vfmadd213ps', 'vfmadd231ps', 'vfmadd132ss', 'vfmadd213ss', 'vfmadd231ss', 'vfmadd231pd', 'vfnmadd231ps', 'vfmsub231ps', | |
| 63 | + 'vzeroupper', 'vzeroall', 'vptest', 'vcmpps', 'vhaddps', 'vdpps', 'vroundps', 'kmovw', 'kmovq', 'vpternlogd', 'vpcompressd', 'vpexpandd', | |
| 29 | 64 | ], |
| 65 | + 'keyword' => [ | |
| 66 | + // サイズ指定等(NASM) | |
| 67 | + 'byte', 'word', 'dword', 'qword', 'tword', 'oword', 'yword', 'zword', 'ptr', 'rel', 'abs', 'near', 'far', 'short', 'strict', 'wrt', 'seg', | |
| 68 | + 'rep', 'repe', 'repne', 'repz', 'repnz', 'lock', 'xacquire', 'xrelease', 'bnd', 'notrack', | |
| 69 | + ], | |
| 30 | 70 | 'directive' => [ |
| 31 | - 'db', 'dw', 'dd', 'dw', | |
| 32 | - 'equ', 'global', 'extern', | |
| 33 | - 'resb', 'resw', 'resd', 'resq', | |
| 71 | + // NASM | |
| 72 | + 'section', 'segment', 'global', 'extern', 'common', 'static', 'bits', 'default', 'db', 'dw', 'dd', 'dq', 'dt', 'do', 'dy', 'dz', | |
| 73 | + 'resb', 'resw', 'resd', 'resq', 'rest', 'reso', 'resy', 'resz', 'equ', 'times', 'incbin', 'align', 'alignb', 'struc', 'endstruc', | |
| 74 | + 'istruc', 'iend', 'at', 'org', 'cpu', 'use16', 'use32', 'use64', 'absolute', 'sectalign', | |
| 34 | 75 | ], |
| 35 | 76 | ]; |
| 36 | 77 | |
| 37 | - public function __construct() { | |
| 38 | - global $colorPalette; | |
| 39 | - | |
| 40 | - $this->colors = [ | |
| 41 | - 'comment' => $colorPalette['dark']['green'], | |
| 42 | - 'directive' => $colorPalette['dark']['pink'], | |
| 43 | - 'label' => $colorPalette['light']['yellow'], | |
| 44 | - 'number' => $colorPalette['light']['green'], | |
| 45 | - 'operator' => $colorPalette['dark']['blue'], | |
| 46 | - 'register' => $colorPalette['dark']['cyan'], | |
| 47 | - 'string' => $colorPalette['medium']['orange'], | |
| 48 | - ]; | |
| 78 | + public function prepare(string $code): void { | |
| 79 | + $tokens = $this->tokens; | |
| 80 | + // r8〜r15、各サイズ | |
| 81 | + foreach (self::numbered('r', 8, 15) as $r) array_push($tokens['register'], $r, "{$r}d", "{$r}w", "{$r}b", "{$r}l"); | |
| 82 | + // 制御・デバッグ・ベクター・マスク・MMX・x87 | |
| 83 | + foreach ([['cr', 0, 15], ['dr', 0, 15], ['xmm', 0, 31], ['ymm', 0, 31], ['zmm', 0, 31], ['k', 0, 7], ['mm', 0, 7], ['st', 0, 7]] as [$p, $a, $b]) { | |
| 84 | + foreach (self::numbered($p, $a, $b) as $r) $tokens['register'][] = $r; | |
| 85 | + } | |
| 86 | + // 条件付き命令(je, cmovne, setg等) | |
| 87 | + $cc = ['o', 'no', 'b', 'nb', 'c', 'nc', 'ae', 'nae', 'z', 'nz', 'e', 'ne', 'be', 'nbe', 'a', 'na', 's', 'ns', 'p', 'np', 'pe', 'po', | |
| 88 | + 'l', 'nl', 'ge', 'nge', 'le', 'nle', 'g', 'ng']; | |
| 89 | + foreach ($cc as $c) array_push($tokens['instruction'], "j{$c}", "cmov{$c}", "set{$c}"); | |
| 90 | + $this->words = $this->buildWords($tokens); | |
| 49 | 91 | } |
| 50 | - | |
| 51 | - public function getColors(): array { return $this->colors; } | |
| 52 | - public function getComment(): ?string { return $this->comment; } | |
| 53 | - public function getInclude(array $code): ?array { return null; } | |
| 54 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 55 | - public function getTokens(): array { return $this->tokens; } | |
| 56 | -} | |
| 92 | +} |
| @@ -1,66 +1,48 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +/** | |
| 5 | + * Z80・GBZ80・eZ80 | |
| 6 | + */ | |
| 7 | +class AsmZ80 extends Assembly { | |
| 8 | + // 「af'」の様な裏レジスターも1つの単語にする(「%」は2進数なので接頭辞にしない) | |
| 9 | + protected string $identifier = '[.A-Za-z_][\w.$]*\'?'; | |
| 5 | 10 | |
| 6 | -class AsmZ80 implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = ';'; | |
| 9 | - private ?array $multiline = null; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 11 | + protected array $tokens = [ | |
| 12 | 12 | 'register' => [ |
| 13 | 13 | // 16ビット |
| 14 | - "bc'", "de'", "hl'", | |
| 15 | - 'bc', 'de', 'hl', | |
| 16 | - 'ix' /*指標レジスター*/, 'iy' /*指標レジスター*/, | |
| 17 | - 'sp' /*スタックポインター*/, 'pc' /*プログラムカウンター*/, | |
| 14 | + "af'", "bc'", "de'", "hl'", 'af', 'bc', 'de', 'hl', 'ix', 'iy', 'ixh', 'ixl', 'iyh', 'iyl', 'sp', 'pc', | |
| 18 | 15 | // 8ビット |
| 19 | - 'a', 'b', 'd', 'h', 'f', 'c', 'e', 'l', | |
| 20 | - "a'", "b'", "d'", "h'", "f'", "c'", "e'", "l'", | |
| 21 | - 'a', 'b', 'd', 'h', 'f', 'c', 'e', 'l', | |
| 16 | + 'a', 'b', 'c', 'd', 'e', 'f', 'h', 'l', 'i', 'r', | |
| 17 | + "a'", "b'", "c'", "d'", "e'", "f'", "h'", "l'", | |
| 18 | + // eZ80 | |
| 19 | + 'mb', | |
| 22 | 20 | ], |
| 23 | - 'operator' => [ | |
| 21 | + 'instruction' => [ | |
| 24 | 22 | // Z80 + GBZ80 + eZ80 |
| 25 | - 'adc', 'add', 'and', 'bit', 'call', 'ccf', 'cp', 'daa', 'dec', 'di', 'djnz', 'ei', | |
| 26 | - 'halt', 'im0', 'im1', 'im2', 'im', 'inc', 'in', 'jp', 'jr', 'ldd', | |
| 23 | + 'adc', 'add', 'and', 'bit', 'call', 'ccf', 'cp', 'cpl', 'daa', 'dec', 'di', 'djnz', 'ei', | |
| 24 | + 'halt', 'im', 'inc', 'in', 'jp', 'jr', 'ld', 'ldd', 'ldh', 'ldi', | |
| 27 | 25 | 'nop', 'otir', 'otdr', 'or', 'pop', 'push', 'retn', 'reti', 'ret', 'res', |
| 28 | - 'rst', 'rrc', 'rr', 'rdc', 'rl', 'sub', 'srl', 'sra', 'sla', 'set', 'scf', 'sbc', 'xor', | |
| 26 | + 'rst', 'rrc', 'rrca', 'rr', 'rra', 'rlc', 'rlca', 'rl', 'rla', 'sub', 'srl', 'sra', 'sla', 'set', 'scf', 'sbc', 'xor', | |
| 29 | 27 | // Z80 + GBZ80のみ |
| 30 | - 'sll', 'ld', | |
| 28 | + 'sll', 'sl1', | |
| 31 | 29 | // eZ80のみ |
| 32 | - 'ind2', 'indmr', 'indrx', 'indm', 'inimr', 'inim', 'ini2r', 'ini2', 'lea', 'mlt', | |
| 33 | - 'otirx', 'otimr', 'otim', 'oti2r', 'otdrx', 'otdmr', 'otdm', 'otd2r', 'out0', 'outd2', | |
| 34 | - 'outi2', 'pea', 'rsmix', 'slp', 'stmix', 'tstio', 'tst', '.is', 'xxx.S', 'xxx.L', '.il', '.sis', '.lil', | |
| 30 | + 'ind2', 'ind2r', 'indm', 'indmr', 'indr2', 'indrx', 'ini2', 'ini2r', 'inim', 'inimr', 'inirx', 'lea', 'mlt', | |
| 31 | + 'otd2r', 'otdm', 'otdmr', 'otdrx', 'oti2r', 'otim', 'otimr', 'otirx', 'out0', 'in0', 'outd2', 'outi2', | |
| 32 | + 'pea', 'rsmix', 'slp', 'stmix', 'tst', 'tstio', | |
| 35 | 33 | // Z80のみ |
| 36 | - 'cpdr', 'cpir', 'cpd', 'cpi', 'exx', 'ex', 'outd', 'inir', 'ini', 'indr', 'ind', 'lddr', 'ldir', 'ldi', | |
| 37 | - 'neg', 'outi', 'outdi', 'out', 'rld', 'rrd', | |
| 34 | + 'cpdr', 'cpir', 'cpd', 'cpi', 'exx', 'ex', 'outd', 'inir', 'ini', 'indr', 'ind', 'lddr', 'ldir', | |
| 35 | + 'neg', 'outi', 'out', 'rld', 'rrd', | |
| 38 | 36 | // GBZ80のみ |
| 39 | - 'outd', 'stop', 'swap', | |
| 37 | + 'stop', 'swap', | |
| 40 | 38 | ], |
| 39 | + 'keyword' => [ | |
| 40 | + // 条件 | |
| 41 | + 'nz', 'z', 'nc', 'po', 'pe', 'p', 'm', | |
| 42 | + ], | |
| 41 | 43 | 'directive' => [ |
| 42 | - // 'db', 'dw', 'dd', 'dw', | |
| 43 | - // 'equ', 'global', 'extern', | |
| 44 | - // 'resb', 'resw', 'resd', 'resq', | |
| 44 | + 'org', 'equ', 'db', 'dw', 'ds', 'defb', 'defw', 'defs', 'defm', 'dm', 'include', 'incbin', 'end', 'macro', 'endm', | |
| 45 | + 'section', 'rb', 'rw', 'if', 'else', 'endc', 'endif', 'rept', 'endr', 'export', 'assert', 'align', 'charmap', | |
| 45 | 46 | ], |
| 46 | 47 | ]; |
| 47 | - | |
| 48 | - public function __construct() { | |
| 49 | - global $colorPalette; | |
| 50 | - | |
| 51 | - $this->colors = [ | |
| 52 | - 'comment' => $colorPalette['dark']['green'], | |
| 53 | - 'directive' => $colorPalette['dark']['pink'], | |
| 54 | - 'label' => $colorPalette['light']['yellow'], | |
| 55 | - 'number' => $colorPalette['light']['green'], | |
| 56 | - 'operator' => $colorPalette['dark']['blue'], | |
| 57 | - 'register' => $colorPalette['dark']['cyan'], | |
| 58 | - 'string' => $colorPalette['medium']['orange'], | |
| 59 | - ]; | |
| 60 | - } | |
| 61 | - | |
| 62 | - public function getColors(): array { return $this->colors; } | |
| 63 | - public function getComment(): ?string { return $this->comment; } | |
| 64 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 65 | - public function getTokens(): array { return $this->tokens; } | |
| 66 | -} | |
| 48 | +} |
| @@ -0,0 +1,127 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * アセンブリ言語の基本クラス | |
| 6 | + * | |
| 7 | + * 種類:instruction(命令)、register(レジスター)、directive(指令)、keyword(サイズ指定等)、label(ラベル) | |
| 8 | + * 行の最初の単語は、一覧に無くても命令として扱う(ラベルと指令を除く) | |
| 9 | + */ | |
| 10 | +abstract class Assembly extends Language { | |
| 11 | + protected array $comment = [';']; | |
| 12 | + protected ?array $multiline = null; | |
| 13 | + protected array $strings = ['"', "'"]; | |
| 14 | + protected bool $caseInsensitive = true; | |
| 15 | + protected bool $functionCalls = false; | |
| 16 | + protected string $digitSep = ''; | |
| 17 | + // 「%rax」(AT&T)や「$t0」(MIPS)の様な接頭辞付きも1つの単語にする | |
| 18 | + protected string $identifier = '[%$]?[.A-Za-z_][\w.$]*'; | |
| 19 | + | |
| 20 | + /** | |
| 21 | + * @var string[] 命令・レジスターの接尾辞を外す正規表現(ARMの条件コード等) | |
| 22 | + * 順番に適用し、途中の結果も全部候補にする | |
| 23 | + */ | |
| 24 | + protected array $suffixes = []; | |
| 25 | + | |
| 26 | + /** @var string 数字の接頭辞(ARMの「#」、6502の「#」等) */ | |
| 27 | + protected string $immediatePrefix = '#?'; | |
| 28 | + | |
| 29 | + /** @var bool 「$FF」を16進数とするか(MIPSでは「$」はレジスター) */ | |
| 30 | + protected bool $dollarHex = true; | |
| 31 | + | |
| 32 | + /** @var bool 「%1010」を2進数とするか(x86では「%」はレジスター・マクロ) */ | |
| 33 | + protected bool $percentBinary = true; | |
| 34 | + | |
| 35 | + /** @var bool 「%define」等をNASMのマクロとして扱うか */ | |
| 36 | + protected bool $percentMacros = false; | |
| 37 | + | |
| 38 | + /** @var string[] 命令の前に付く接頭辞(rep、lock等)。次の単語も命令として扱う */ | |
| 39 | + protected array $prefixes = []; | |
| 40 | + | |
| 41 | + protected function numberPattern(): string { | |
| 42 | + $alts = [ | |
| 43 | + '0[xX][0-9A-Fa-f]+', | |
| 44 | + '0[bB][01]+', | |
| 45 | + '0[oO][0-7]+', | |
| 46 | + '[0-9][0-9A-Fa-f]*[hH](?![\w.])', | |
| 47 | + '\d+(?:\.\d+)?(?:[eE][+-]?\d+)?', | |
| 48 | + ]; | |
| 49 | + if ($this->dollarHex) $alts[] = '\$[0-9A-Fa-f]+(?![\w.])'; | |
| 50 | + if ($this->percentBinary) $alts[] = '%[01]+(?![\w.])'; | |
| 51 | + return $this->immediatePrefix.'-?(?:'.implode('|', $alts).')'; | |
| 52 | + } | |
| 53 | + | |
| 54 | + public function classify(string $word, array $ctx): ?string { | |
| 55 | + if ($word === '.' || $word === '$') return null; | |
| 56 | + $key = strtolower($word); | |
| 57 | + | |
| 58 | + // ラベル(「名前:」、又は「名前 db …」「名前 = …」の様な定義) | |
| 59 | + if ($ctx['next'] === ':' && ($ctx['first'] || $ctx['bol'])) return 'label'; | |
| 60 | + if ($ctx['first'] && ($ctx['next'] === '=' || $this->lookup(strtolower($ctx['nextWord'])) === 'directive')) return 'label'; | |
| 61 | + | |
| 62 | + $atOpcode = $ctx['first'] || in_array(strtolower($ctx['prevWord']), $this->prefixes, true); | |
| 63 | + $class = $this->lookup($key); | |
| 64 | + if ($class !== null) { | |
| 65 | + // 命令の位置以外の命令名はシンボルの可能性が高い(例:「jmp add」) | |
| 66 | + if ($class === 'instruction' && !$atOpcode) return null; | |
| 67 | + return $class; | |
| 68 | + } | |
| 69 | + | |
| 70 | + if ($this->percentMacros && $key[0] === '%' && strlen($key) > 1) return 'preprocessor'; | |
| 71 | + if ($key[0] === '.') { | |
| 72 | + // 「.section .text」の様に指令の行なら指令、「jne .done」の様に命令の行ならローカルラベルの参照 | |
| 73 | + if ($atOpcode) return 'directive'; | |
| 74 | + $head = strtolower($ctx['lineWord']); | |
| 75 | + return ($head !== '' && ($head[0] === '.' || $this->lookup($head) === 'directive')) ? 'directive' : null; | |
| 76 | + } | |
| 77 | + if ($atOpcode) return 'instruction'; | |
| 78 | + return null; | |
| 79 | + } | |
| 80 | + | |
| 81 | + /** | |
| 82 | + * 単語表・接尾辞・接頭辞を返す(汎用のAsmがまとめる為) | |
| 83 | + * | |
| 84 | + * @return array [単語 => 種類, 接尾辞の正規表現, 接頭辞] | |
| 85 | + */ | |
| 86 | + public function getTables(): array { | |
| 87 | + return [$this->words, $this->suffixes, $this->prefixes]; | |
| 88 | + } | |
| 89 | + | |
| 90 | + /** | |
| 91 | + * 単語を一覧から探す(接頭辞「%」「$」と接尾辞を外した候補も試す) | |
| 92 | + * | |
| 93 | + * @param string $key 小文字の単語 | |
| 94 | + * @return string|null 種類 | |
| 95 | + */ | |
| 96 | + protected function lookup(string $key): ?string { | |
| 97 | + if ($key === '') return null; | |
| 98 | + $base = ltrim($key, '%$'); | |
| 99 | + foreach ($this->candidates($key) as $c) { | |
| 100 | + if (!isset($this->words[$c])) continue; | |
| 101 | + $class = $this->words[$c]; | |
| 102 | + // 接尾辞を外した候補は命令・指令だけ(「v0.4s」の様に「.」で区切った場合はレジスターも) | |
| 103 | + // そうしないと「hello」→「hel」+「lo」の様な誤判定が起きる | |
| 104 | + if ($c !== $key && $c !== $base && $class !== 'instruction' && $class !== 'directive' && !str_contains($key, '.')) continue; | |
| 105 | + return $class; | |
| 106 | + } | |
| 107 | + return null; | |
| 108 | + } | |
| 109 | + | |
| 110 | + /** | |
| 111 | + * 接頭辞・接尾辞を外した候補を作る | |
| 112 | + * | |
| 113 | + * @param string $key 小文字の単語 | |
| 114 | + * @return array 候補(元の単語が最初) | |
| 115 | + */ | |
| 116 | + protected function candidates(string $key): array { | |
| 117 | + $cands = [$key]; | |
| 118 | + if (strlen($key) > 1 && ($key[0] === '%' || $key[0] === '$')) $cands[] = substr($key, 1); | |
| 119 | + foreach ($this->suffixes as $re) { | |
| 120 | + foreach ($cands as $c) { | |
| 121 | + $s = preg_replace($re, '', $c); | |
| 122 | + if ($s !== null && $s !== '' && $s !== $c) $cands[] = $s; | |
| 123 | + } | |
| 124 | + } | |
| 125 | + return array_values(array_unique($cands)); | |
| 126 | + } | |
| 127 | +} |
| @@ -1,14 +1,11 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +class C extends Language { | |
| 5 | + protected bool $preprocessor = true; | |
| 6 | + protected array $headerDirectives = ['include', 'include_next', 'embed']; | |
| 5 | 7 | |
| 6 | -class C implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = '//'; | |
| 9 | - private array $multiline = ['/*', '*/']; | |
| 10 | - | |
| 11 | - private array $tokens = [ | |
| 8 | + protected array $tokens = [ | |
| 12 | 9 | 'keyword' => [ |
| 13 | 10 | // C23 |
| 14 | 11 | 'alignas', 'alignof', 'bool', 'constexpr', 'false', 'nullptr', 'static_assert', 'thread_local', 'true', 'typeof', 'typeof_unqual', |
| @@ -23,7 +20,7 @@ | ||
| 23 | 20 | // ISO |
| 24 | 21 | 'FILE', |
| 25 | 22 | ], |
| 26 | - 'libc' => [ | |
| 23 | + 'builtin' => [ | |
| 27 | 24 | // ISO C |
| 28 | 25 | 'assert.h' => [ // C89 |
| 29 | 26 | 'assert', |
| @@ -178,7 +175,7 @@ | ||
| 178 | 175 | // C89 |
| 179 | 176 | '#if', '#elif', '#else', '#endif', '#ifdef', '#ifndef', '#define', '#undef', '#include', '#line', '#error', '#pragma', '#defined', |
| 180 | 177 | ], |
| 181 | - 'typedef' => [ | |
| 178 | + 'type' => [ | |
| 182 | 179 | 'errno.h' => [ |
| 183 | 180 | 'errno_t', |
| 184 | 181 | ], |
| @@ -245,33 +242,31 @@ | ||
| 245 | 242 | ], |
| 246 | 243 | ]; |
| 247 | 244 | |
| 248 | - public function __construct() { | |
| 249 | - global $colorPalette; | |
| 245 | + /** @var array よく使う定数(ヘッダーに関係なく色を付ける) */ | |
| 246 | + protected array $constants = [ | |
| 247 | + 'NULL', 'EOF', 'stdin', 'stdout', 'stderr', 'EXIT_SUCCESS', 'EXIT_FAILURE', 'RAND_MAX', | |
| 248 | + 'CHAR_BIT', 'CHAR_MIN', 'CHAR_MAX', 'SCHAR_MIN', 'SCHAR_MAX', 'UCHAR_MAX', 'SHRT_MIN', 'SHRT_MAX', 'USHRT_MAX', | |
| 249 | + 'INT_MIN', 'INT_MAX', 'UINT_MAX', 'LONG_MIN', 'LONG_MAX', 'ULONG_MAX', 'LLONG_MIN', 'LLONG_MAX', 'ULLONG_MAX', | |
| 250 | + 'INT8_MIN', 'INT8_MAX', 'INT16_MIN', 'INT16_MAX', 'INT32_MIN', 'INT32_MAX', 'INT64_MIN', 'INT64_MAX', | |
| 251 | + 'UINT8_MAX', 'UINT16_MAX', 'UINT32_MAX', 'UINT64_MAX', 'SIZE_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', | |
| 252 | + 'FLT_MAX', 'FLT_MIN', 'FLT_EPSILON', 'DBL_MAX', 'DBL_MIN', 'DBL_EPSILON', 'HUGE_VAL', 'INFINITY', 'NAN', | |
| 253 | + 'M_PI', 'M_E', 'SEEK_SET', 'SEEK_CUR', 'SEEK_END', 'BUFSIZ', 'FILENAME_MAX', | |
| 254 | + '__FILE__', '__LINE__', '__DATE__', '__TIME__', '__func__', '__STDC__', '__STDC_VERSION__', | |
| 255 | + ]; | |
| 250 | 256 | |
| 251 | - $this->colors = [ | |
| 252 | - 'comment' => $colorPalette['dark']['green'], | |
| 253 | - 'keyword' => $colorPalette['dark']['blue'], | |
| 254 | - 'number' => $colorPalette['light']['green'], | |
| 255 | - 'preprocessor' => $colorPalette['medium']['purple'], | |
| 256 | - 'string' => $colorPalette['medium']['orange'], | |
| 257 | - 'typedef' => $colorPalette['dark']['lime'], | |
| 258 | - 'function' => $colorPalette['light']['yellow'], | |
| 259 | - 'libc' => $colorPalette['light']['yellow'], | |
| 260 | - ]; | |
| 261 | - } | |
| 262 | - | |
| 263 | - public function getColors(): array { return $this->colors; } | |
| 264 | - public function getComment(): ?string { return $this->comment; } | |
| 265 | - public function getInclude(array $code): ?array { | |
| 266 | - $headers = []; | |
| 267 | - foreach ($code as $c) { | |
| 268 | - $c = htmlspecialchars($c); | |
| 269 | - if (!str_starts_with($c, '#include')) continue; | |
| 270 | - $header = str_replace(['#include ', '<', '>', '"', '''], '', $c); | |
| 271 | - $headers[] = trim($header); | |
| 257 | + /** | |
| 258 | + * #includeされたヘッダーの関数と型だけに色を付ける | |
| 259 | + * #includeが1つも無い場合(ブログの抜粋等)は全部に色を付ける | |
| 260 | + */ | |
| 261 | + public function prepare(string $code): void { | |
| 262 | + $tokens = $this->tokens; | |
| 263 | + preg_match_all('~^[ \t]*#[ \t]*include[ \t]*[<"]([^>"\n]+)[>"]~m', $code, $m); | |
| 264 | + if (!empty($m[1])) { | |
| 265 | + $inc = array_flip(array_map('trim', $m[1])); | |
| 266 | + foreach (['builtin', 'type'] as $k) { | |
| 267 | + if (isset($tokens[$k])) $tokens[$k] = array_intersect_key($tokens[$k], $inc); | |
| 268 | + } | |
| 272 | 269 | } |
| 273 | - return $headers; | |
| 270 | + $this->words = $this->buildWords(['constant' => $this->constants] + $tokens); | |
| 274 | 271 | } |
| 275 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 276 | - public function getTokens(): array { return $this->tokens; } | |
| 277 | -} | |
| 272 | +} |
| @@ -0,0 +1,45 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +class CSharp extends Language { | |
| 5 | + protected bool $preprocessor = true; | |
| 6 | + protected string $digitSep = '_'; | |
| 7 | + // 「@class」の様な逐語的識別子 | |
| 8 | + protected string $identifier = '@?[\p{L}_][\p{L}\p{N}_]*'; | |
| 9 | + | |
| 10 | + protected array $rules = [ | |
| 11 | + // 生文字列 """…"""($"""…"""も) | |
| 12 | + ['re' => '\$*"""(?s:.*?)(?:"""|\z)', 'class' => 'string'], | |
| 13 | + // 逐語的文字列 @"…"(""でエスケープ、複数行可)、$@"…"、@$"…" | |
| 14 | + ['re' => '(?:\$@|@\$|@)"(?:[^"]|"")*"?', 'class' => 'string'], | |
| 15 | + // 補間文字列 $"…" | |
| 16 | + ['re' => '\$"(?:[^"\\\\\n]|\\\\.)*"?', 'class' => 'string'], | |
| 17 | + ]; | |
| 18 | + | |
| 19 | + protected array $tokens = [ | |
| 20 | + 'keyword' => [ | |
| 21 | + 'abstract', 'as', 'base', 'break', 'case', 'catch', 'checked', 'class', 'const', 'continue', 'default', 'delegate', 'do', 'else', | |
| 22 | + 'enum', 'event', 'explicit', 'extern', 'finally', 'fixed', 'for', 'foreach', 'goto', 'if', 'implicit', 'in', 'interface', 'internal', | |
| 23 | + 'is', 'lock', 'namespace', 'new', 'operator', 'out', 'override', 'params', 'private', 'protected', 'public', 'readonly', 'ref', | |
| 24 | + 'return', 'sealed', 'sizeof', 'stackalloc', 'static', 'struct', 'switch', 'this', 'throw', 'try', 'typeof', 'unchecked', 'unsafe', | |
| 25 | + 'using', 'virtual', 'volatile', 'while', | |
| 26 | + // 文脈依存(「value」「select」「file」等、変数名によく使う単語は除く) | |
| 27 | + 'alias', 'and', 'async', 'await', 'get', 'global', 'init', 'managed', 'nameof', 'not', 'notnull', 'or', 'partial', 'record', | |
| 28 | + 'required', 'scoped', 'set', 'unmanaged', 'var', 'when', 'where', 'with', 'yield', | |
| 29 | + ], | |
| 30 | + 'constant' => [ | |
| 31 | + 'true', 'false', 'null', | |
| 32 | + ], | |
| 33 | + 'type' => [ | |
| 34 | + 'bool', 'byte', 'sbyte', 'char', 'decimal', 'double', 'float', 'int', 'uint', 'nint', 'nuint', 'long', 'ulong', 'short', 'ushort', | |
| 35 | + 'object', 'string', 'void', 'dynamic', | |
| 36 | + 'Boolean', 'Byte', 'SByte', 'Char', 'Decimal', 'Double', 'Single', 'Int16', 'Int32', 'Int64', 'Int128', 'UInt16', 'UInt32', 'UInt64', | |
| 37 | + 'IntPtr', 'UIntPtr', 'Half', 'Object', 'String', 'Guid', 'DateTime', 'TimeSpan', 'Span', 'ReadOnlySpan', 'Memory', 'ReadOnlyMemory', | |
| 38 | + 'List', 'Dictionary', 'HashSet', 'Queue', 'Stack', 'LinkedList', 'SortedList', 'SortedDictionary', 'Array', 'Tuple', 'ValueTuple', | |
| 39 | + 'IEnumerable', 'IEnumerator', 'ICollection', 'IList', 'IDictionary', 'IReadOnlyList', 'IDisposable', 'IComparable', 'IEquatable', | |
| 40 | + 'Task', 'ValueTask', 'Action', 'Func', 'Predicate', 'EventHandler', 'Exception', 'ArgumentException', 'ArgumentNullException', | |
| 41 | + 'InvalidOperationException', 'NotImplementedException', 'Console', 'Math', 'MathF', 'Convert', 'Enumerable', 'StringBuilder', | |
| 42 | + 'Thread', 'CancellationToken', 'Stream', 'File', 'Path', 'Directory', 'Random', 'Vector2', 'Vector3', 'Vector4', 'Matrix4x4', 'Quaternion', | |
| 43 | + ], | |
| 44 | + ]; | |
| 45 | +} |
| @@ -1,66 +1,74 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +class Cpp extends C { | |
| 5 | + protected array $headerDirectives = ['include', 'include_next', 'embed', 'import']; | |
| 5 | 6 | |
| 6 | -class Cpp implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = '//'; | |
| 9 | - private array $multiline = ['/*', '*/']; | |
| 7 | + protected array $rules = [ | |
| 8 | + // 生文字列 R"区切り(…)区切り" | |
| 9 | + ['re' => '(?:u8|[uUL])?R"(?<rawdelim>[^()\\\\\s]{0,16})\((?s:.*?)\)\k<rawdelim>"', 'class' => 'string'], | |
| 10 | + // 属性 [[nodiscard]] | |
| 11 | + ['re' => '\[\[[^\]\n]*\]\]', 'class' => 'attribute'], | |
| 12 | + ]; | |
| 10 | 13 | |
| 11 | - private array $tokens = [ | |
| 14 | + /** @var array C++のトークン(Cの関数・型は prepare() で追加する) */ | |
| 15 | + protected array $cppTokens = [ | |
| 12 | 16 | 'keyword' => [ |
| 13 | 17 | // C++26 |
| 14 | - 'contract_assert', 'pre', 'post', 'trivially_reallocate_if_eligible', 'replaceable_if_eligible', | |
| 18 | + 'contract_assert', 'pre', 'post', 'trivially_relocatable_if_eligible', 'replaceable_if_eligible', | |
| 15 | 19 | // C++20 |
| 16 | 20 | 'char8_t', 'concept', 'consteval', 'constinit', 'co_await', 'co_return', 'co_yield', 'import', 'module', 'requires', |
| 17 | 21 | // C++11 |
| 18 | 22 | 'alignas', 'alignof', 'char16_t', 'char32_t', 'constexpr', 'decltype', 'final', 'noexcept', 'nullptr', 'override', 'static_assert', 'thread_local', |
| 19 | - // C++99 | |
| 23 | + // C++98 | |
| 20 | 24 | 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break', 'case', 'catch', 'char', 'class', 'compl', 'const', 'const_cast', 'continue', |
| 21 | 25 | 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend', 'goto', 'if', 'inline', |
| 22 | - 'int', 'long', 'mutable', 'namespace', 'new', 'not', 'not_eq', 'operator', 'or', 'or_eq', 'private', 'protected', 'public', 'reflexpr', 'register', | |
| 23 | - 'reinterpret_cast', 'return', 'short', 'signed', 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', 'throw', 'true', 'try', | |
| 26 | + 'int', 'long', 'mutable', 'namespace', 'new', 'not', 'not_eq', 'operator', 'or', 'or_eq', 'private', 'protected', 'public', 'register', | |
| 27 | + 'reinterpret_cast', 'return', 'short', 'signed', 'sizeof', 'static', 'static_cast', 'struct', 'switch', 'template', 'this', 'throw', 'true', 'try', | |
| 24 | 28 | 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq', |
| 25 | 29 | // TMTS |
| 26 | 30 | 'atomic_cancel', 'atomic_commit', 'atomic_noexcept', 'synchronized', 'transaction_safe', 'transaction_safe_dynamic', |
| 27 | 31 | ], |
| 28 | - 'preprocessor' => [ | |
| 29 | - // C++26 | |
| 30 | - '#embed', '__has_embed', | |
| 31 | - // C++23 | |
| 32 | - '#elifdef', '#elifndef', '#warning', | |
| 33 | - // C++20 | |
| 34 | - '#__has_cpp_attribute', '#export', '#import', '#module', | |
| 35 | - // C++17 | |
| 36 | - '#__has_include', | |
| 37 | - // C++11 | |
| 38 | - '#_Pragma', | |
| 39 | - // C++99 | |
| 40 | - '#if', '#elif', '#else', '#endif', '#ifdef', '#ifndef', '#define', '#undef', '#include', '#line', '#error', '#pragma', '#defined', | |
| 32 | + 'type' => [ | |
| 33 | + 'std', 'chrono', 'filesystem', 'ranges', 'views', 'this_thread', 'numbers', 'execution', 'pmr', | |
| 34 | + 'string', 'wstring', 'u8string', 'u16string', 'u32string', 'string_view', 'wstring_view', | |
| 35 | + 'vector', 'array', 'deque', 'list', 'forward_list', 'map', 'multimap', 'set', 'multiset', | |
| 36 | + 'unordered_map', 'unordered_multimap', 'unordered_set', 'unordered_multiset', 'flat_map', 'flat_set', | |
| 37 | + 'stack', 'queue', 'priority_queue', 'span', 'mdspan', 'bitset', 'valarray', | |
| 38 | + 'pair', 'tuple', 'optional', 'variant', 'any', 'expected', 'unexpected', 'monostate', 'nullopt_t', 'nullptr_t', 'byte', | |
| 39 | + 'unique_ptr', 'shared_ptr', 'weak_ptr', 'function', 'move_only_function', 'reference_wrapper', 'initializer_list', | |
| 40 | + 'thread', 'jthread', 'mutex', 'recursive_mutex', 'shared_mutex', 'lock_guard', 'unique_lock', 'scoped_lock', 'condition_variable', | |
| 41 | + 'atomic', 'atomic_flag', 'future', 'promise', 'stop_token', 'stop_source', | |
| 42 | + 'istream', 'ostream', 'iostream', 'ifstream', 'ofstream', 'fstream', 'stringstream', 'istringstream', 'ostringstream', | |
| 43 | + 'size_t', 'ptrdiff_t', 'intptr_t', 'uintptr_t', 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', | |
| 44 | + 'float16_t', 'float32_t', 'float64_t', 'float128_t', 'bfloat16_t', 'max_align_t', | |
| 45 | + 'exception', 'runtime_error', 'logic_error', 'invalid_argument', 'out_of_range', 'bad_alloc', 'error_code', | |
| 46 | + 'iterator', 'const_iterator', 'reverse_iterator', 'allocator', 'hash', 'less', 'greater', 'equal_to', | |
| 47 | + 'duration', 'time_point', 'steady_clock', 'system_clock', 'high_resolution_clock', 'path', 'regex', 'complex', 'ratio', | |
| 41 | 48 | ], |
| 42 | - 'typedef' => [ | |
| 43 | - 'int8_t', 'int16_t', 'int32_t', 'int64_t', | |
| 44 | - 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', | |
| 45 | - 'size_t', | |
| 49 | + 'builtin' => [ | |
| 50 | + 'cout', 'cin', 'cerr', 'clog', 'wcout', 'wcin', 'endl', 'flush', 'ws', 'getline', 'print', 'println', 'format', | |
| 51 | + 'move', 'forward', 'swap', 'exchange', 'make_unique', 'make_shared', 'make_pair', 'make_tuple', 'make_optional', 'tie', 'get', | |
| 52 | + 'static_pointer_cast', 'dynamic_pointer_cast', 'reinterpret_pointer_cast', 'addressof', 'launder', 'bit_cast', 'to_underlying', | |
| 53 | + 'sort', 'stable_sort', 'find', 'find_if', 'count', 'count_if', 'copy', 'copy_if', 'fill', 'transform', 'accumulate', 'reduce', | |
| 54 | + 'for_each', 'remove', 'remove_if', 'reverse', 'unique', 'min', 'max', 'clamp', 'min_element', 'max_element', 'lower_bound', 'upper_bound', | |
| 55 | + 'all_of', 'any_of', 'none_of', 'iota', 'to_string', 'stoi', 'stol', 'stof', 'stod', 'invoke', 'apply', 'visit', 'holds_alternative', | |
| 56 | + 'declval', 'is_same_v', 'is_integral_v', 'is_floating_point_v', 'enable_if_t', 'conditional_t', 'decay_t', 'remove_reference_t', | |
| 57 | + 'numeric_limits', 'begin', 'end', 'cbegin', 'cend', 'rbegin', 'rend', 'size', 'ssize', 'empty', 'data', 'unreachable', | |
| 46 | 58 | ], |
| 47 | 59 | ]; |
| 48 | 60 | |
| 49 | - public function __construct() { | |
| 50 | - global $colorPalette; | |
| 51 | - | |
| 52 | - $this->colors = [ | |
| 53 | - 'comment' => $colorPalette['dark']['green'], | |
| 54 | - 'keyword' => $colorPalette['dark']['blue'], | |
| 55 | - 'number' => $colorPalette['light']['green'], | |
| 56 | - 'preprocessor' => $colorPalette['medium']['purple'], | |
| 57 | - 'string' => $colorPalette['medium']['orange'], | |
| 58 | - 'typedef' => $colorPalette['dark']['lime'], | |
| 59 | - ]; | |
| 61 | + /** | |
| 62 | + * C++のトークンに、Cのライブラリ関数と型を加える(ヘッダーでは絞らない) | |
| 63 | + */ | |
| 64 | + public function prepare(string $code): void { | |
| 65 | + $tokens = $this->cppTokens; | |
| 66 | + foreach (['builtin', 'type'] as $k) { | |
| 67 | + foreach ($this->tokens[$k] ?? [] as $list) { | |
| 68 | + foreach ($list as $w) $tokens[$k][] = $w; | |
| 69 | + } | |
| 70 | + } | |
| 71 | + $tokens['preprocessor'] = $this->tokens['preprocessor'] ?? []; | |
| 72 | + $this->words = $this->buildWords(['constant' => $this->constants] + $tokens); | |
| 60 | 73 | } |
| 61 | - | |
| 62 | - public function getColors(): array { return $this->colors; } | |
| 63 | - public function getComment(): ?string { return $this->comment; } | |
| 64 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 65 | - public function getTokens(): array { return $this->tokens; } | |
| 66 | -} | |
| 74 | +} |
| @@ -1,65 +1,100 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +class Glsl extends Language { | |
| 5 | + protected bool $preprocessor = true; | |
| 6 | + protected array $headerDirectives = ['include']; | |
| 7 | + protected array $strings = ['"']; | |
| 8 | + protected string $digitSep = ''; | |
| 5 | 9 | |
| 6 | -class Glsl implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = '//'; | |
| 9 | - | |
| 10 | - private array $tokens = [ | |
| 11 | - 'function' => [ | |
| 12 | - 'asin', 'acos', 'atan', 'cos', 'degrees', 'radians', 'sin', 'tan', | |
| 13 | - 'exp', 'exp2', 'inversesqrt', 'log', 'log2', 'pow', 'sqrt', | |
| 14 | - 'abs', 'ceil', 'clamp', 'floor', 'fract', 'max', 'min', 'mix', 'mod', 'sign', 'smoothstep', 'step', | |
| 15 | - 'cross', 'distance', 'dot', 'faceforward', 'ftransform', 'length', 'normalize', 'reflect', 'refract', | |
| 16 | - 'dFdx', 'dFdy', 'fwidth', | |
| 17 | - 'matrixCompMulti', | |
| 18 | - 'all', 'any', 'equal', 'greaterThan', 'greaterThanEqual', 'lessThan', 'lessThanEqual', 'not', 'notEqual', | |
| 19 | - 'texture1D', 'texture1DProj', 'texture2D', 'texture2DProj', 'texture3D', 'texture3DProj', 'textureCube', | |
| 20 | - 'texture1DLod', 'texture1DProjLod', 'texture2DLod', 'texture2DProjLod', 'texture3DProjLod', | |
| 21 | - 'shadow1DLog', 'shadow1DProjLog', 'shadow2DLog', 'shadow2DProjLog', | |
| 22 | - 'noise1', 'noise2', 'noise3', 'noise4', | |
| 23 | - ], | |
| 10 | + protected array $tokens = [ | |
| 24 | 11 | 'keyword' => [ |
| 25 | - 'attribute', 'bool', 'bvec2', 'bvec3', 'bvec4', 'const', 'float', 'genType', 'in', 'int', 'inout', 'ivec2', 'ivec3', 'ivec4', 'mat2', 'mat3', 'mat4', 'out', | |
| 26 | - 'sampler1D', 'sampler1DShadow', 'sampler2D', 'sampler2DShadow', 'sampler3D', 'samplerCube', 'struct', 'uniform', 'varying', 'vec2', 'vec3', 'vec4', 'void', | |
| 12 | + 'attribute', 'const', 'uniform', 'varying', 'buffer', 'shared', 'layout', 'centroid', 'flat', 'smooth', 'noperspective', | |
| 13 | + 'patch', 'sample', 'break', 'continue', 'do', 'for', 'while', 'switch', 'case', 'default', 'if', 'else', 'subroutine', | |
| 14 | + 'in', 'out', 'inout', 'invariant', 'precise', 'discard', 'return', 'struct', 'precision', 'highp', 'mediump', 'lowp', | |
| 15 | + 'coherent', 'volatile', 'restrict', 'readonly', 'writeonly', 'demote', 'terminateInvocation', 'rayPayloadEXT', 'hitAttributeEXT', | |
| 16 | + 'callableDataEXT', 'taskPayloadSharedEXT', 'perprimitiveEXT', | |
| 17 | + // layout()の中(「offset」「points」等、変数名にも使う単語は除く) | |
| 18 | + 'location', 'binding', 'set', 'push_constant', 'std140', 'std430', 'local_size_x', 'local_size_y', 'local_size_z', | |
| 19 | + 'input_attachment_index', 'constant_id', 'row_major', 'column_major', 'early_fragment_tests', 'max_vertices', | |
| 27 | 20 | ], |
| 28 | - 'preprocessor' => [ | |
| 29 | - '#', '#define', '#elif', '#else', '#endif', '#error', '#extension', '#if', '#ifdef', '#ifndef', '#line', '#pragma', '#version', '#undef', | |
| 30 | - '__FILE__', '__LINE__', '__VERSION__', | |
| 21 | + 'constant' => [ | |
| 22 | + 'true', 'false', '__FILE__', '__LINE__', '__VERSION__', 'GL_ES', | |
| 31 | 23 | ], |
| 32 | - 'variables' => [ | |
| 33 | - // Vertex | |
| 34 | - 'gl_ClipVertex', 'gl_PointSize', 'gl_Position', 'gl_Color', 'gl_FogCoord', 'gl_Normal', 'gl_SecondaryColor', 'gl_Vertex', | |
| 35 | - 'gl_MultiTexCoord0', 'gl_MultiTexCoord1', 'gl_MultiTexCoord2', 'gl_MultiTexCoord3', 'gl_MultiTexCoord4', 'gl_MultiTexCoord5', 'gl_MultiTexCoord6', 'gl_MultiTexCoord7', | |
| 36 | - 'gl_BackColor', 'gl_BackSecondaryColor', 'gl_FogFragCoord', 'gl_FrontColor', 'gl_FrontSecondaryColor', 'gl_TexCoord', | |
| 37 | - // Fragment | |
| 38 | - 'gl_FragColor', 'gl_FragCoord', 'gl_FragData', 'gl_FragDepth', 'gl_FrontFacing', 'gl_MaxDrawBuffers', | |
| 39 | - // Constant | |
| 40 | - 'gl_MaxClipPlanes', 'gl_MaxCombinedTextureImageUnits', 'gl_MaxDrawBuffers', 'gl_MaxFragmentUniformComponents', 'gl_MaxLights', 'gl_MaxTextureCoords', | |
| 41 | - 'gl_MaxTextureImageUnits', 'gl_MaxTextureUnits', 'gl_MaxVaryingFloats', 'gl_MaxVertexAttribs', 'gl_MaxVertexUniformComponents', | |
| 42 | - // Uniform | |
| 43 | - 'gl_ClipPlane', 'gl_DepthRange', 'gl_DepthRangeParameters', 'gl_EyePlane', 'gl_Fog', 'gl_FogParameters', 'gl_LightModel', 'gl_LightModelParameters', | |
| 44 | - 'gl_LightModelProducts', 'gl_BackLightModelProduct', 'gl_FrontLightModelProduct', 'gl_LightProducts', 'gl_BackLightProduct', 'gl_FrontLightProduct', | |
| 45 | - 'gl_LightSource', 'gl_LightSourceParameters', 'gl_MaterialParameters', 'gl_BackMaterial', 'gl_FrontMaterial', | |
| 46 | - 'gl_ModelViewMatrix', 'gl_ModelViewMatrixInverse', 'gl_ModelViewMatrixTranspose', 'gl_ModelViewMatrixInverseTranspose', | |
| 47 | - 'gl_ModelViewProjectionMatrix', 'gl_ModelViewProjectionMatrixInverse', 'gl_ModelViewProjectionMatrixTranspose', 'gl_ModelViewProjectionMatrixInverseTranspose', | |
| 48 | - 'gl_ObjectPlane', 'gl_Point', 'gl_PointParameters', | |
| 49 | - 'gl_ProjectionMatrix', 'gl_ProjectionMatrixInverse', 'gl_ProjectionMatrixTranspose', 'gl_ProjectionMatrixInverseTranspose', 'gl_NormalMatrix', 'gl_NormalScale', | |
| 50 | - 'gl_TextureEnvColor', 'gl_TextureMatrix', 'gl_TextureMatrixInverse', 'gl_TextureMatrixTranspose', 'gl_TextureMatrixInverseTranspose', | |
| 24 | + 'type' => [ | |
| 25 | + 'void', 'bool', 'int', 'uint', 'float', 'double', 'atomic_uint', 'genType', 'genIType', 'genUType', 'genBType', 'genDType', | |
| 26 | + 'int8_t', 'int16_t', 'int32_t', 'int64_t', 'uint8_t', 'uint16_t', 'uint32_t', 'uint64_t', 'float16_t', 'float32_t', 'float64_t', | |
| 27 | + 'accelerationStructureEXT', 'rayQueryEXT', | |
| 28 | + 'subpassInput', 'subpassInputMS', 'isubpassInput', 'isubpassInputMS', 'usubpassInput', 'usubpassInputMS', 'sampler', 'samplerShadow', | |
| 51 | 29 | ], |
| 30 | + 'builtin' => [ | |
| 31 | + // 角度・三角関数 | |
| 32 | + 'radians', 'degrees', 'sin', 'cos', 'tan', 'asin', 'acos', 'atan', 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh', | |
| 33 | + // 指数 | |
| 34 | + 'pow', 'exp', 'log', 'exp2', 'log2', 'sqrt', 'inversesqrt', | |
| 35 | + // 共通 | |
| 36 | + 'abs', 'sign', 'floor', 'trunc', 'round', 'roundEven', 'ceil', 'fract', 'mod', 'modf', 'min', 'max', 'clamp', 'mix', 'step', 'smoothstep', | |
| 37 | + 'isnan', 'isinf', 'floatBitsToInt', 'floatBitsToUint', 'intBitsToFloat', 'uintBitsToFloat', 'fma', 'frexp', 'ldexp', | |
| 38 | + 'packUnorm2x16', 'packSnorm2x16', 'packUnorm4x8', 'packSnorm4x8', 'unpackUnorm2x16', 'unpackSnorm2x16', 'unpackUnorm4x8', 'unpackSnorm4x8', | |
| 39 | + 'packHalf2x16', 'unpackHalf2x16', 'packDouble2x32', 'unpackDouble2x32', | |
| 40 | + // 幾何 | |
| 41 | + 'length', 'distance', 'dot', 'cross', 'normalize', 'faceforward', 'reflect', 'refract', 'ftransform', | |
| 42 | + // 行列・ベクトル比較 | |
| 43 | + 'matrixCompMult', 'outerProduct', 'transpose', 'determinant', 'inverse', | |
| 44 | + 'lessThan', 'lessThanEqual', 'greaterThan', 'greaterThanEqual', 'equal', 'notEqual', 'any', 'all', 'not', | |
| 45 | + // 整数 | |
| 46 | + 'uaddCarry', 'usubBorrow', 'umulExtended', 'imulExtended', 'bitfieldExtract', 'bitfieldInsert', 'bitfieldReverse', 'bitCount', 'findLSB', 'findMSB', | |
| 47 | + // テクスチャ | |
| 48 | + 'textureSize', 'textureQueryLod', 'textureQueryLevels', 'textureSamples', 'texture', 'textureProj', 'textureLod', 'textureOffset', | |
| 49 | + 'texelFetch', 'texelFetchOffset', 'textureProjOffset', 'textureLodOffset', 'textureProjLod', 'textureProjLodOffset', | |
| 50 | + 'textureGrad', 'textureGradOffset', 'textureProjGrad', 'textureProjGradOffset', 'textureGather', 'textureGatherOffset', 'textureGatherOffsets', | |
| 51 | + 'texture1D', 'texture1DProj', 'texture1DLod', 'texture1DProjLod', 'texture2D', 'texture2DProj', 'texture2DLod', 'texture2DProjLod', | |
| 52 | + 'texture3D', 'texture3DProj', 'texture3DLod', 'texture3DProjLod', 'textureCube', 'textureCubeLod', | |
| 53 | + 'shadow1D', 'shadow1DProj', 'shadow1DLod', 'shadow1DProjLod', 'shadow2D', 'shadow2DProj', 'shadow2DLod', 'shadow2DProjLod', | |
| 54 | + // 原子操作・画像 | |
| 55 | + 'atomicCounterIncrement', 'atomicCounterDecrement', 'atomicCounter', 'atomicAdd', 'atomicMin', 'atomicMax', 'atomicAnd', 'atomicOr', | |
| 56 | + 'atomicXor', 'atomicExchange', 'atomicCompSwap', 'imageSize', 'imageSamples', 'imageLoad', 'imageStore', 'imageAtomicAdd', | |
| 57 | + 'imageAtomicMin', 'imageAtomicMax', 'imageAtomicAnd', 'imageAtomicOr', 'imageAtomicXor', 'imageAtomicExchange', 'imageAtomicCompSwap', | |
| 58 | + // 微分・補間・ジオメトリ・同期 | |
| 59 | + 'dFdx', 'dFdy', 'dFdxFine', 'dFdyFine', 'dFdxCoarse', 'dFdyCoarse', 'fwidth', 'fwidthFine', 'fwidthCoarse', | |
| 60 | + 'interpolateAtCentroid', 'interpolateAtSample', 'interpolateAtOffset', 'EmitStreamVertex', 'EndStreamPrimitive', 'EmitVertex', 'EndPrimitive', | |
| 61 | + 'barrier', 'memoryBarrier', 'memoryBarrierAtomicCounter', 'memoryBarrierBuffer', 'memoryBarrierShared', 'memoryBarrierImage', 'groupMemoryBarrier', | |
| 62 | + 'subpassLoad', 'noise1', 'noise2', 'noise3', 'noise4', | |
| 63 | + // サブグループ・レイトレーシング | |
| 64 | + 'subgroupBarrier', 'subgroupElect', 'subgroupAll', 'subgroupAny', 'subgroupBroadcast', 'subgroupBroadcastFirst', 'subgroupBallot', | |
| 65 | + 'subgroupAdd', 'subgroupMul', 'subgroupMin', 'subgroupMax', 'subgroupShuffle', 'subgroupShuffleXor', | |
| 66 | + 'traceRayEXT', 'reportIntersectionEXT', 'executeCallableEXT', 'ignoreIntersectionEXT', 'terminateRayEXT', 'EmitMeshTasksEXT', 'SetMeshOutputsEXT', | |
| 67 | + ], | |
| 52 | 68 | ]; |
| 53 | 69 | |
| 54 | - public function __construct() { | |
| 55 | - global $colorPalette; | |
| 56 | - | |
| 57 | - $this->colors = [ | |
| 58 | - ]; | |
| 70 | + public function prepare(string $code): void { | |
| 71 | + $tokens = $this->tokens; | |
| 72 | + // ベクトル・行列 | |
| 73 | + foreach (['vec', 'ivec', 'uvec', 'bvec', 'dvec', 'i8vec', 'u8vec', 'i16vec', 'u16vec', 'i64vec', 'u64vec', 'f16vec', 'f64vec'] as $v) { | |
| 74 | + for ($i = 2; $i <= 4; $i++) $tokens['type'][] = "{$v}{$i}"; | |
| 75 | + } | |
| 76 | + foreach (['mat', 'dmat', 'f16mat', 'f64mat'] as $m) { | |
| 77 | + for ($i = 2; $i <= 4; $i++) { | |
| 78 | + $tokens['type'][] = "{$m}{$i}"; | |
| 79 | + for ($j = 2; $j <= 4; $j++) $tokens['type'][] = "{$m}{$i}x{$j}"; | |
| 80 | + } | |
| 81 | + } | |
| 82 | + // サンプラー・テクスチャ・画像 | |
| 83 | + $dims = ['1D', '2D', '3D', 'Cube', '2DRect', '1DArray', '2DArray', 'CubeArray', 'Buffer', '2DMS', '2DMSArray']; | |
| 84 | + foreach (['', 'i', 'u'] as $p) { | |
| 85 | + foreach ($dims as $d) { | |
| 86 | + $tokens['type'][] = "{$p}sampler{$d}"; | |
| 87 | + $tokens['type'][] = "{$p}texture{$d}"; | |
| 88 | + $tokens['type'][] = "{$p}image{$d}"; | |
| 89 | + } | |
| 90 | + } | |
| 91 | + foreach (['1D', '2D', 'Cube', '2DRect', '1DArray', '2DArray', 'CubeArray'] as $d) $tokens['type'][] = "sampler{$d}Shadow"; | |
| 92 | + $this->words = $this->buildWords($tokens); | |
| 59 | 93 | } |
| 60 | 94 | |
| 61 | - public function getColors(): array { return $this->colors; } | |
| 62 | - public function getComment(): ?string { return $this->comment; } | |
| 63 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 64 | - public function getTokens(): array { return $this->tokens; } | |
| 65 | -} | |
| 95 | + public function classify(string $word, array $ctx): ?string { | |
| 96 | + // 組み込み変数(gl_Position, gl_GlobalInvocationID等) | |
| 97 | + if (str_starts_with($word, 'gl_')) return 'variable'; | |
| 98 | + return parent::classify($word, $ctx); | |
| 99 | + } | |
| 100 | +} |
| @@ -1,40 +1,28 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | -use Std\Lib\Syntax\SyntaxInterface; | |
| 4 | +class Go extends Language { | |
| 5 | + protected string $digitSep = '_'; | |
| 5 | 6 | |
| 6 | -class Go implements SyntaxInterface { | |
| 7 | - private array $colors = []; | |
| 8 | - private string $comment = '//'; | |
| 7 | + protected array $rules = [ | |
| 8 | + // 生文字列 `…`(複数行可) | |
| 9 | + ['re' => '`[^`]*`?', 'class' => 'string'], | |
| 10 | + ]; | |
| 9 | 11 | |
| 10 | - private array $tokens = [ | |
| 12 | + protected array $tokens = [ | |
| 11 | 13 | 'keyword' => [ |
| 12 | 14 | 'break', 'case', 'chan', 'const', 'continue', 'default', 'defer', 'else', 'fallthrough', 'for', 'func', 'go', 'goto', 'if', 'import', 'interface', 'map', |
| 13 | 15 | 'package', 'range', 'return', 'select', 'struct', 'switch', 'type', 'var', |
| 14 | - 'uint8', 'uint16', 'uint32', 'uint64', | |
| 15 | - 'int8', 'int16', 'int32', 'int64', | |
| 16 | - 'float32', 'float64', | |
| 17 | - 'complex64', 'complex128', | |
| 18 | - 'byte', 'rune', 'string', 'bool', | |
| 19 | - 'uint', 'int', 'uintptr', | |
| 20 | 16 | ], |
| 21 | - 'operator' => [ | |
| 22 | - '+', '-', '*', '/', '%', '&', '|', '^', '<<', '>>', '&^', '<', '>', '=', '!', '~', '...', ',', '.', ';', ':', | |
| 23 | - '{', '}', '(', ')', '[', ']', | |
| 24 | - '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', '&^=', '!=', '<=', '>=', ';=', | |
| 25 | - '&&', '||', '<-', '++', '--', '==', | |
| 17 | + 'constant' => [ | |
| 18 | + 'true', 'false', 'iota', 'nil', | |
| 26 | 19 | ], |
| 20 | + 'type' => [ | |
| 21 | + 'uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', 'int32', 'int64', 'float32', 'float64', 'complex64', 'complex128', | |
| 22 | + 'byte', 'rune', 'string', 'bool', 'uint', 'int', 'uintptr', 'error', 'any', 'comparable', | |
| 23 | + ], | |
| 24 | + 'builtin' => [ | |
| 25 | + 'append', 'cap', 'clear', 'close', 'complex', 'copy', 'delete', 'imag', 'len', 'make', 'max', 'min', 'new', 'panic', 'print', 'println', 'real', 'recover', | |
| 26 | + ], | |
| 27 | 27 | ]; |
| 28 | - | |
| 29 | - public function __construct() { | |
| 30 | - global $colorPalette; | |
| 31 | - | |
| 32 | - $this->colors = [ | |
| 33 | - ]; | |
| 34 | - } | |
| 35 | - | |
| 36 | - public function getColors(): array { return $this->colors; } | |
| 37 | - public function getComment(): ?string { return $this->comment; } | |
| 38 | - public function getMultiline(): ?array { return $this->multiline; } | |
| 39 | - public function getTokens(): array { return $this->tokens; } | |
| 40 | -} | |
| 28 | +} |
| @@ -0,0 +1,87 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +class Hlsl extends Language { | |
| 5 | + protected bool $preprocessor = true; | |
| 6 | + protected array $headerDirectives = ['include']; | |
| 7 | + protected array $strings = ['"']; | |
| 8 | + protected string $digitSep = ''; | |
| 9 | + | |
| 10 | + protected array $tokens = [ | |
| 11 | + 'keyword' => [ | |
| 12 | + 'if', 'else', 'for', 'while', 'do', 'break', 'continue', 'discard', 'return', 'switch', 'case', 'default', | |
| 13 | + 'struct', 'class', 'interface', 'namespace', 'typedef', 'cbuffer', 'tbuffer', 'register', 'packoffset', 'static', 'const', | |
| 14 | + 'uniform', 'volatile', 'extern', 'inline', 'export', 'in', 'out', 'inout', 'groupshared', 'nointerpolation', 'linear', | |
| 15 | + 'centroid', 'noperspective', 'sample', 'precise', 'row_major', 'column_major', 'snorm', 'unorm', 'globallycoherent', | |
| 16 | + 'shared', 'template', 'typename', 'using', 'sizeof', 'this', 'enum', 'operator', 'payload', 'vertices', 'indices', 'primitives', | |
| 17 | + ], | |
| 18 | + 'constant' => [ | |
| 19 | + 'true', 'false', | |
| 20 | + ], | |
| 21 | + 'type' => [ | |
| 22 | + 'void', 'vector', 'matrix', 'string', | |
| 23 | + 'Texture1D', 'Texture1DArray', 'Texture2D', 'Texture2DArray', 'Texture2DMS', 'Texture2DMSArray', 'Texture3D', 'TextureCube', 'TextureCubeArray', | |
| 24 | + 'RWTexture1D', 'RWTexture1DArray', 'RWTexture2D', 'RWTexture2DArray', 'RWTexture3D', 'RWTexture2DMS', 'RWTexture2DMSArray', | |
| 25 | + 'RasterizerOrderedTexture1D', 'RasterizerOrderedTexture2D', 'RasterizerOrderedTexture3D', 'RasterizerOrderedBuffer', | |
| 26 | + 'FeedbackTexture2D', 'FeedbackTexture2DArray', | |
| 27 | + 'Buffer', 'RWBuffer', 'StructuredBuffer', 'RWStructuredBuffer', 'ByteAddressBuffer', 'RWByteAddressBuffer', | |
| 28 | + 'AppendStructuredBuffer', 'ConsumeStructuredBuffer', 'ConstantBuffer', 'TextureBuffer', | |
| 29 | + 'SamplerState', 'SamplerComparisonState', 'sampler', 'sampler1D', 'sampler2D', 'sampler3D', 'samplerCUBE', | |
| 30 | + 'RaytracingAccelerationStructure', 'RayDesc', 'RayQuery', 'BuiltInTriangleIntersectionAttributes', | |
| 31 | + 'InputPatch', 'OutputPatch', 'PointStream', 'LineStream', 'TriangleStream', | |
| 32 | + ], | |
| 33 | + 'builtin' => [ | |
| 34 | + 'abort', 'abs', 'acos', 'all', 'AllMemoryBarrier', 'AllMemoryBarrierWithGroupSync', 'any', 'asdouble', 'asfloat', 'asfloat16', 'asin', 'asint', | |
| 35 | + 'asint16', 'asuint', 'asuint16', 'atan', 'atan2', 'ceil', 'CheckAccessFullyMapped', 'clamp', 'clip', 'cos', 'cosh', 'countbits', 'cross', | |
| 36 | + 'D3DCOLORtoUBYTE4', 'ddx', 'ddx_coarse', 'ddx_fine', 'ddy', 'ddy_coarse', 'ddy_fine', 'degrees', 'determinant', 'DeviceMemoryBarrier', | |
| 37 | + 'DeviceMemoryBarrierWithGroupSync', 'distance', 'dot', 'dst', 'errorf', 'EvaluateAttributeAtCentroid', 'EvaluateAttributeAtSample', | |
| 38 | + 'EvaluateAttributeSnapped', 'exp', 'exp2', 'f16tof32', 'f32tof16', 'faceforward', 'firstbithigh', 'firstbitlow', 'floor', 'fma', 'fmod', | |
| 39 | + 'frac', 'frexp', 'fwidth', 'GetRenderTargetSampleCount', 'GetRenderTargetSamplePosition', 'GroupMemoryBarrier', 'GroupMemoryBarrierWithGroupSync', | |
| 40 | + 'InterlockedAdd', 'InterlockedAnd', 'InterlockedCompareExchange', 'InterlockedCompareStore', 'InterlockedExchange', 'InterlockedMax', | |
| 41 | + 'InterlockedMin', 'InterlockedOr', 'InterlockedXor', 'isfinite', 'isinf', 'isnan', 'ldexp', 'length', 'lerp', 'lit', 'log', 'log10', 'log2', | |
| 42 | + 'mad', 'max', 'min', 'modf', 'msad4', 'mul', 'noise', 'normalize', 'pow', 'printf', 'radians', 'rcp', 'reflect', 'refract', 'reversebits', | |
| 43 | + 'round', 'rsqrt', 'saturate', 'sign', 'sin', 'sincos', 'sinh', 'smoothstep', 'sqrt', 'step', 'tan', 'tanh', 'tex1D', 'tex2D', 'tex3D', 'texCUBE', | |
| 44 | + 'transpose', 'trunc', 'NonUniformResourceIndex', 'select', 'and', 'or', | |
| 45 | + 'WaveActiveAllEqual', 'WaveActiveAllTrue', 'WaveActiveAnyTrue', 'WaveActiveBallot', 'WaveActiveBitAnd', 'WaveActiveBitOr', 'WaveActiveBitXor', | |
| 46 | + 'WaveActiveCountBits', 'WaveActiveMax', 'WaveActiveMin', 'WaveActiveProduct', 'WaveActiveSum', 'WaveGetLaneCount', 'WaveGetLaneIndex', | |
| 47 | + 'WaveIsFirstLane', 'WavePrefixCountBits', 'WavePrefixProduct', 'WavePrefixSum', 'WaveReadLaneAt', 'WaveReadLaneFirst', | |
| 48 | + 'QuadReadAcrossDiagonal', 'QuadReadAcrossX', 'QuadReadAcrossY', 'QuadReadLaneAt', | |
| 49 | + 'TraceRay', 'ReportHit', 'IgnoreHit', 'AcceptHitAndEndSearch', 'CallShader', 'DispatchRaysIndex', 'DispatchRaysDimensions', | |
| 50 | + 'WorldRayOrigin', 'WorldRayDirection', 'ObjectRayOrigin', 'ObjectRayDirection', 'RayTMin', 'RayTCurrent', 'RayFlags', 'InstanceIndex', | |
| 51 | + 'InstanceID', 'GeometryIndex', 'PrimitiveIndex', 'HitKind', 'ObjectToWorld3x4', 'ObjectToWorld4x3', 'WorldToObject3x4', 'WorldToObject4x3', | |
| 52 | + 'SetMeshOutputCounts', 'DispatchMesh', | |
| 53 | + // リソースのメソッド | |
| 54 | + 'Sample', 'SampleLevel', 'SampleGrad', 'SampleBias', 'SampleCmp', 'SampleCmpLevel', 'SampleCmpLevelZero', 'Load', 'Store', 'GetDimensions', | |
| 55 | + 'Gather', 'GatherRed', 'GatherGreen', 'GatherBlue', 'GatherAlpha', 'GatherCmp', 'CalculateLevelOfDetail', 'Append', 'Consume', | |
| 56 | + 'IncrementCounter', 'DecrementCounter', 'Load2', 'Load3', 'Load4', 'Store2', 'Store3', 'Store4', | |
| 57 | + ], | |
| 58 | + ]; | |
| 59 | + | |
| 60 | + /** @var string[] 「[」の直後だけ属性として扱う名前(「loop」「branch」等は普通の単語でもある) */ | |
| 61 | + protected array $attributes = [ | |
| 62 | + 'numthreads', 'unroll', 'loop', 'branch', 'flatten', 'fastopt', 'allow_uav_condition', 'call', 'forcecase', 'earlydepthstencil', | |
| 63 | + 'maxvertexcount', 'domain', 'partitioning', 'outputtopology', 'outputcontrolpoints', 'patchconstantfunc', 'maxtessfactor', | |
| 64 | + 'instance', 'RootSignature', 'shader', 'WaveSize', 'NodeLaunch', 'NodeDispatchGrid', 'NodeMaxDispatchGrid', 'NumThreads', 'vk', | |
| 65 | + ]; | |
| 66 | + | |
| 67 | + /** @var string[] スカラー型(ベクトル・行列型を作る元) */ | |
| 68 | + protected array $scalars = [ | |
| 69 | + 'bool', 'int', 'uint', 'dword', 'half', 'float', 'double', 'min16float', 'min10float', 'min16int', 'min12int', 'min16uint', | |
| 70 | + 'int16_t', 'uint16_t', 'int32_t', 'uint32_t', 'int64_t', 'uint64_t', 'float16_t', 'float32_t', 'float64_t', | |
| 71 | + ]; | |
| 72 | + | |
| 73 | + public function prepare(string $code): void { | |
| 74 | + $tokens = $this->tokens; | |
| 75 | + foreach (self::vectorTypes($this->scalars) as $t) $tokens['type'][] = $t; | |
| 76 | + $this->words = $this->buildWords($tokens); | |
| 77 | + } | |
| 78 | + | |
| 79 | + public function classify(string $word, array $ctx): ?string { | |
| 80 | + // セマンティクス(: SV_Position, : TEXCOORD0) | |
| 81 | + if (str_starts_with($word, 'SV_')) return 'variable'; | |
| 82 | + if ($ctx['prev'] === ':' && preg_match('/^[A-Z][A-Z0-9_]*$/', $word)) return 'variable'; | |
| 83 | + // 属性([numthreads(8, 8, 1)]) | |
| 84 | + if ($ctx['prev'] === '[' && in_array($word, $this->attributes, true)) return 'attribute'; | |
| 85 | + return parent::classify($word, $ctx); | |
| 86 | + } | |
| 87 | +} |
| @@ -0,0 +1,155 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * 言語定義の基本クラス | |
| 6 | + * C系の言語の既定値を持つので、各言語は違う所だけを上書きする | |
| 7 | + */ | |
| 8 | +abstract class Language implements SyntaxInterface { | |
| 9 | + /** @var string[] 一行コメントの開始 */ | |
| 10 | + protected array $comment = ['//']; | |
| 11 | + /** @var array|null 複数行コメントの[開始, 終了] */ | |
| 12 | + protected ?array $multiline = ['/*', '*/']; | |
| 13 | + /** 複数行コメントを入れ子に出来るか(Swift等) */ | |
| 14 | + protected bool $nestedComments = false; | |
| 15 | + /** @var string[] 文字列の引用符(「\」でエスケープ) */ | |
| 16 | + protected array $strings = ['"', "'"]; | |
| 17 | + /** 行頭の「#」をプリプロセッサとして扱うか */ | |
| 18 | + protected bool $preprocessor = false; | |
| 19 | + /** @var string[] 次の「<…>」をファイル名とするプリプロセッサ指令 */ | |
| 20 | + protected array $headerDirectives = []; | |
| 21 | + /** 大文字と小文字を区別しないか */ | |
| 22 | + protected bool $caseInsensitive = false; | |
| 23 | + /** 「名前(」を関数として扱うか */ | |
| 24 | + protected bool $functionCalls = true; | |
| 25 | + /** 数字の区切り文字(C++の「'」、Go等の「_」) */ | |
| 26 | + protected string $digitSep = "'"; | |
| 27 | + /** 識別子の正規表現 */ | |
| 28 | + protected string $identifier = '[\p{L}_][\p{L}\p{N}_]*'; | |
| 29 | + /** @var array 追加の規則(既定の規則より先に確認する) */ | |
| 30 | + protected array $rules = []; | |
| 31 | + /** | |
| 32 | + * @var array 種類 => 単語一覧 | |
| 33 | + * 先に書いた種類が優先される | |
| 34 | + */ | |
| 35 | + protected array $tokens = []; | |
| 36 | + | |
| 37 | + /** @var array 単語 => 種類 */ | |
| 38 | + protected array $words = []; | |
| 39 | + | |
| 40 | + public function prepare(string $code): void { | |
| 41 | + $this->words = $this->buildWords($this->tokens); | |
| 42 | + } | |
| 43 | + | |
| 44 | + public function getRules(): array { | |
| 45 | + $r = $this->rules; | |
| 46 | + | |
| 47 | + if ($this->multiline && !$this->nestedComments) { | |
| 48 | + [$s, $e] = $this->multiline; | |
| 49 | + $r[] = ['re' => preg_quote($s, '~').'.*?(?:'.preg_quote($e, '~').'|\z)', 'class' => 'comment']; | |
| 50 | + } | |
| 51 | + | |
| 52 | + if (!empty($this->comment)) { | |
| 53 | + $alts = implode('|', array_map(fn($c) => preg_quote($c, '~'), $this->comment)); | |
| 54 | + $r[] = ['re' => "(?:{$alts})[^\\n]*", 'class' => 'comment']; | |
| 55 | + } | |
| 56 | + | |
| 57 | + foreach ($this->strings as $q) { | |
| 58 | + $q = preg_quote($q, '~'); | |
| 59 | + $r[] = ['re' => "{$q}(?:[^{$q}\\\\\\n]|\\\\.)*{$q}?", 'class' => 'string']; | |
| 60 | + } | |
| 61 | + | |
| 62 | + if ($this->preprocessor) { | |
| 63 | + $r[] = ['re' => '#[ \t]*[A-Za-z_]\w*', 'class' => 'preprocessor', 'bol' => true]; | |
| 64 | + } | |
| 65 | + | |
| 66 | + $r[] = ['re' => $this->numberPattern(), 'class' => 'number']; | |
| 67 | + $r[] = ['re' => $this->identifier, 'class' => 'ident']; | |
| 68 | + | |
| 69 | + return $r; | |
| 70 | + } | |
| 71 | + | |
| 72 | + public function classify(string $word, array $ctx): ?string { | |
| 73 | + $key = $this->caseInsensitive ? strtolower($word) : $word; | |
| 74 | + if (isset($this->words[$key])) return $this->words[$key]; | |
| 75 | + if ($this->functionCalls && $ctx['next'] === '(') return 'function'; | |
| 76 | + return null; | |
| 77 | + } | |
| 78 | + | |
| 79 | + public function getNestedComment(): ?array { | |
| 80 | + return $this->nestedComments ? $this->multiline : null; | |
| 81 | + } | |
| 82 | + | |
| 83 | + public function getHeaderDirectives(): array { | |
| 84 | + return $this->headerDirectives; | |
| 85 | + } | |
| 86 | + | |
| 87 | + /** | |
| 88 | + * 種類 => 単語一覧から、単語 => 種類の表を作る | |
| 89 | + * 一覧が「ヘッダー名 => 単語一覧」の場合はそのまま全部を使う(C::prepare()で絞る) | |
| 90 | + * | |
| 91 | + * @param array $tokens 種類 => 単語一覧 | |
| 92 | + * @return array 単語 => 種類 | |
| 93 | + */ | |
| 94 | + protected function buildWords(array $tokens): array { | |
| 95 | + $words = []; | |
| 96 | + foreach ($tokens as $class => $list) { | |
| 97 | + foreach ($list as $k => $w) { | |
| 98 | + foreach (is_array($w) ? $w : [$w] as $word) { | |
| 99 | + $key = $this->caseInsensitive ? strtolower($word) : $word; | |
| 100 | + if (!isset($words[$key])) $words[$key] = $class; | |
| 101 | + } | |
| 102 | + } | |
| 103 | + } | |
| 104 | + return $words; | |
| 105 | + } | |
| 106 | + | |
| 107 | + /** | |
| 108 | + * C系の数字(16進数・2進数・8進数・浮動小数点・接尾辞) | |
| 109 | + * | |
| 110 | + * @return string 正規表現 | |
| 111 | + */ | |
| 112 | + protected function numberPattern(): string { | |
| 113 | + $s = $this->digitSep === '' ? '' : preg_quote($this->digitSep, '~').'?'; | |
| 114 | + $d = "\\d(?:{$s}\\d)*"; | |
| 115 | + $x = "[0-9A-Fa-f](?:{$s}[0-9A-Fa-f])*"; | |
| 116 | + return "(?:0[xX]{$x}(?:\\.(?:{$x})?)?(?:[pP][+-]?\\d+)?" | |
| 117 | + ."|0[bB][01](?:{$s}[01])*" | |
| 118 | + ."|0[oO][0-7](?:{$s}[0-7])*" | |
| 119 | + ."|(?:{$d}(?:\\.(?!\\.)(?:{$d})?)?|\\.\\d(?:{$s}\\d)*)(?:[eE][+-]?\\d+)?)" | |
| 120 | + ."(?:[A-Za-z_]\\w*)?"; | |
| 121 | + } | |
| 122 | + | |
| 123 | + /** | |
| 124 | + * ベクトル・行列型を作る(float → float2…float4, float2x2…float4x4) | |
| 125 | + * | |
| 126 | + * @param array $bases 基本の型 | |
| 127 | + * @param bool $matrix 行列型も作るか | |
| 128 | + * @return array 型の一覧 | |
| 129 | + */ | |
| 130 | + protected static function vectorTypes(array $bases, bool $matrix = true): array { | |
| 131 | + $out = []; | |
| 132 | + foreach ($bases as $b) { | |
| 133 | + $out[] = $b; | |
| 134 | + for ($i = 1; $i <= 4; $i++) { | |
| 135 | + $out[] = "{$b}{$i}"; | |
| 136 | + if ($matrix) for ($j = 1; $j <= 4; $j++) $out[] = "{$b}{$i}x{$j}"; | |
| 137 | + } | |
| 138 | + } | |
| 139 | + return $out; | |
| 140 | + } | |
| 141 | + | |
| 142 | + /** | |
| 143 | + * 「名前0」…「名前N」の一覧を作る(レジスター用) | |
| 144 | + * | |
| 145 | + * @param string|array $prefix 接頭辞 | |
| 146 | + * @param int $from 最初の番号 | |
| 147 | + * @param int $to 最後の番号 | |
| 148 | + * @return array | |
| 149 | + */ | |
| 150 | + protected static function numbered(string|array $prefix, int $from, int $to): array { | |
| 151 | + $out = []; | |
| 152 | + foreach ((array)$prefix as $p) for ($i = $from; $i <= $to; $i++) $out[] = "{$p}{$i}"; | |
| 153 | + return $out; | |
| 154 | + } | |
| 155 | +} |
| @@ -0,0 +1,62 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * Objective-C(Cの上位互換) | |
| 6 | + */ | |
| 7 | +class ObjC extends C { | |
| 8 | + protected array $headerDirectives = ['include', 'include_next', 'import']; | |
| 9 | + | |
| 10 | + protected array $rules = [ | |
| 11 | + // NSString @"…" | |
| 12 | + ['re' => '@"(?:[^"\\\\\n]|\\\\.)*"?', 'class' => 'string'], | |
| 13 | + // @interface, @property, @autoreleasepool等 | |
| 14 | + ['re' => '@[A-Za-z_]\w*', 'class' => 'keyword'], | |
| 15 | + // リテラル @[…] @{…} @(…) | |
| 16 | + ['re' => '@(?=[\[{(])', 'class' => 'keyword'], | |
| 17 | + ]; | |
| 18 | + | |
| 19 | + /** @var array Cに加えるObjective-Cのトークン */ | |
| 20 | + protected array $objcTokens = [ | |
| 21 | + 'keyword' => [ | |
| 22 | + 'self', 'super', 'id', 'instancetype', 'Class', 'SEL', 'IMP', 'BOOL', 'in', 'out', 'inout', 'oneway', 'bycopy', 'byref', | |
| 23 | + 'nonatomic', 'atomic', 'strong', 'weak', 'copy', 'assign', 'retain', 'readonly', 'readwrite', 'getter', 'setter', | |
| 24 | + 'nullable', 'nonnull', 'null_resettable', '_Nullable', '_Nonnull', '_Null_unspecified', '__nullable', '__nonnull', | |
| 25 | + '__bridge', '__bridge_transfer', '__bridge_retained', '__block', '__weak', '__strong', '__unsafe_unretained', '__autoreleasing', '__kindof', | |
| 26 | + ], | |
| 27 | + 'constant' => [ | |
| 28 | + 'YES', 'NO', 'nil', 'Nil', 'NULL', | |
| 29 | + ], | |
| 30 | + 'type' => [ | |
| 31 | + 'NSObject', 'NSString', 'NSMutableString', 'NSArray', 'NSMutableArray', 'NSDictionary', 'NSMutableDictionary', 'NSSet', 'NSMutableSet', | |
| 32 | + 'NSNumber', 'NSValue', 'NSData', 'NSMutableData', 'NSDate', 'NSURL', 'NSError', 'NSException', 'NSInteger', 'NSUInteger', 'NSTimeInterval', | |
| 33 | + 'NSRange', 'NSZone', 'NSNotification', 'NSNotificationCenter', 'NSUserDefaults', 'NSFileManager', 'NSBundle', 'NSThread', 'NSTimer', | |
| 34 | + 'NSRunLoop', 'NSAutoreleasePool', 'NSProcessInfo', 'NSApplication', 'NSWindow', 'NSView', 'NSViewController', 'NSColor', 'NSImage', | |
| 35 | + 'UIApplication', 'UIView', 'UIViewController', 'UIWindow', 'UIColor', 'UIImage', 'UILabel', 'UIButton', | |
| 36 | + 'CGFloat', 'CGRect', 'CGPoint', 'CGSize', 'CGContextRef', 'CFStringRef', 'CFTypeRef', 'dispatch_queue_t', 'dispatch_block_t', | |
| 37 | + 'MTLDevice', 'MTLCommandQueue', 'MTLCommandBuffer', 'MTLRenderPipelineState', 'MTLBuffer', 'MTLTexture', | |
| 38 | + ], | |
| 39 | + 'builtin' => [ | |
| 40 | + 'NSLog', 'NSStringFromClass', 'NSClassFromString', 'NSStringFromSelector', 'NSSelectorFromString', 'NSMakeRange', 'NSApplicationMain', | |
| 41 | + 'UIApplicationMain', 'CGRectMake', 'CGPointMake', 'CGSizeMake', 'objc_msgSend', 'objc_getClass', 'class_getName', 'sel_registerName', | |
| 42 | + 'dispatch_async', 'dispatch_sync', 'dispatch_get_main_queue', 'dispatch_get_global_queue', 'dispatch_once', 'dispatch_after', | |
| 43 | + 'MTLCreateSystemDefaultDevice', 'CFRelease', 'CFRetain', | |
| 44 | + 'NS_ENUM', 'NS_OPTIONS', 'NS_ASSUME_NONNULL_BEGIN', 'NS_ASSUME_NONNULL_END', 'NS_DESIGNATED_INITIALIZER', 'NS_UNAVAILABLE', | |
| 45 | + ], | |
| 46 | + ]; | |
| 47 | + | |
| 48 | + public function prepare(string $code): void { | |
| 49 | + parent::prepare($code); | |
| 50 | + foreach ($this->buildWords($this->objcTokens) as $w => $class) { | |
| 51 | + if (!isset($this->words[$w])) $this->words[$w] = $class; | |
| 52 | + } | |
| 53 | + } | |
| 54 | + | |
| 55 | + public function classify(string $word, array $ctx): ?string { | |
| 56 | + $class = parent::classify($word, $ctx); | |
| 57 | + if ($class !== null) return $class; | |
| 58 | + // メッセージのセレクター部分([obj setValue:x forKey:y])。「Foo : NSObject」の様に空白がある場合は除く | |
| 59 | + if ($ctx['nextRaw'] === ':') return 'function'; | |
| 60 | + return null; | |
| 61 | + } | |
| 62 | +} |
| @@ -0,0 +1,58 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +/** | |
| 5 | + * Slang(HLSLの上位互換) | |
| 6 | + */ | |
| 7 | +class Slang extends Hlsl { | |
| 8 | + protected array $rules = [ | |
| 9 | + // 属性 [[vk::binding(0, 0)]] | |
| 10 | + ['re' => '\[\[[^\]\n]*\]\]', 'class' => 'attribute'], | |
| 11 | + ]; | |
| 12 | + | |
| 13 | + /** @var array HLSLに加えるSlangのトークン */ | |
| 14 | + protected array $slangTokens = [ | |
| 15 | + 'keyword' => [ | |
| 16 | + 'import', 'module', 'implementing', '__include', '__exported', 'public', 'private', 'internal', 'extension', 'associatedtype', | |
| 17 | + 'where', 'let', 'var', 'func', 'property', 'get', 'set', '__init', '__subscript', 'This', 'typealias', 'throw', 'throws', | |
| 18 | + 'try', 'catch', 'defer', 'is', 'as', 'new', 'some', 'dyn', 'ref', '__ref', '__generic', '__target_switch', 'spirv_asm', | |
| 19 | + '__intrinsic_asm', 'constexpr', 'override', 'no_diff', 'fwd_diff', 'bwd_diff', 'alignof', 'countof', | |
| 20 | + ], | |
| 21 | + 'constant' => [ | |
| 22 | + 'none', 'nullptr', | |
| 23 | + ], | |
| 24 | + 'type' => [ | |
| 25 | + 'Ptr', 'Optional', 'Tuple', 'Array', 'DescriptorHandle', 'ParameterBlock', 'NativeString', 'String', 'int8_t', 'uint8_t', | |
| 26 | + 'Sampler1D', 'Sampler2D', 'Sampler3D', 'SamplerCube', 'Sampler1DArray', 'Sampler2DArray', 'SamplerCubeArray', 'Sampler2DShadow', | |
| 27 | + 'Sampler2DMS', 'Sampler2DMSArray', 'Texture2DShadow', 'TextureCubeShadow', 'RWTexture2DArray', 'Atomic', | |
| 28 | + 'IDifferentiable', 'IDifferentiablePtrType', 'DifferentialPair', 'IArithmetic', 'IFloat', 'IInteger', 'ILogical', 'IComparable', | |
| 29 | + 'IRangedValue', 'IArray', 'IRWArray', 'IBufferDataLayout', 'DefaultDataLayout', 'Std140DataLayout', 'Std430DataLayout', 'ScalarDataLayout', | |
| 30 | + ], | |
| 31 | + 'builtin' => [ | |
| 32 | + 'detach', 'diffPair', 'updatePrimal', 'updateDiff', 'getDiff', 'getPrimal', 'isDifferentialNull', | |
| 33 | + 'printf', 'bit_cast', 'reinterpret', 'createDynamicObject', 'getStringHash', 'static_assert', | |
| 34 | + ], | |
| 35 | + ]; | |
| 36 | + | |
| 37 | + protected array $attributes = [ | |
| 38 | + // HLSL | |
| 39 | + 'numthreads', 'unroll', 'loop', 'branch', 'flatten', 'fastopt', 'allow_uav_condition', 'call', 'forcecase', 'earlydepthstencil', | |
| 40 | + 'maxvertexcount', 'domain', 'partitioning', 'outputtopology', 'outputcontrolpoints', 'patchconstantfunc', 'maxtessfactor', | |
| 41 | + 'instance', 'RootSignature', 'shader', 'WaveSize', 'NumThreads', 'vk', | |
| 42 | + // Slang | |
| 43 | + 'Differentiable', 'ForwardDifferentiable', 'BackwardDifferentiable', 'BackwardDerivative', 'ForwardDerivative', 'PrimalSubstitute', | |
| 44 | + 'PreferRecompute', 'PreferCheckpoint', 'TreatAsDifferentiable', 'mutating', 'nonmutating', 'ForceInline', 'noinline', 'require', | |
| 45 | + 'SpecializationConstant', 'push_constant', 'Payload', 'raypayload', 'AutoPyBindCUDA', 'CudaKernel', 'TorchEntryPoint', | |
| 46 | + 'Format', 'OverloadRank', 'Deprecated', 'KnownBuiltin', 'anyValueSize', 'COM', 'Flags', 'UnscopedEnum', 'ForceUnroll', | |
| 47 | + ]; | |
| 48 | + | |
| 49 | + public function prepare(string $code): void { | |
| 50 | + $tokens = $this->tokens; | |
| 51 | + foreach ($this->slangTokens as $k => $list) { | |
| 52 | + foreach ($list as $w) $tokens[$k][] = $w; | |
| 53 | + } | |
| 54 | + foreach (self::vectorTypes($this->scalars) as $t) $tokens['type'][] = $t; | |
| 55 | + foreach (self::vectorTypes(['int8_t', 'uint8_t']) as $t) $tokens['type'][] = $t; | |
| 56 | + $this->words = $this->buildWords($tokens); | |
| 57 | + } | |
| 58 | +} |
| @@ -0,0 +1,55 @@ | ||
| 1 | +<?php | |
| 2 | +namespace Std\Lib\Syntax; | |
| 3 | + | |
| 4 | +class Swift extends Language { | |
| 5 | + protected bool $nestedComments = true; | |
| 6 | + protected array $strings = ['"']; | |
| 7 | + protected string $digitSep = '_'; | |
| 8 | + | |
| 9 | + protected array $rules = [ | |
| 10 | + // 生文字列 #"…"#(複数行の#"""…"""#も) | |
| 11 | + ['re' => '(?<rawhash>#+)"""(?s:.*?)"""\k<rawhash>|(?<rawhash2>#+)"(?:[^"\n]|"(?!\k<rawhash2>))*"\k<rawhash2>', 'class' => 'string'], | |
| 12 | + // 複数行の文字列 """…""" | |
| 13 | + ['re' => '"""(?s:.*?)(?:"""|\z)', 'class' => 'string'], | |
| 14 | + // コンパイラ指令(#if, #available, #selector等) | |
| 15 | + ['re' => '#[A-Za-z_]\w*', 'class' => 'preprocessor'], | |
| 16 | + // 属性(@main, @MainActor, @escaping等) | |
| 17 | + ['re' => '@[A-Za-z_]\w*', 'class' => 'attribute'], | |
| 18 | + // 予約語を名前にする `default` | |
| 19 | + ['re' => '`[^`\n]+`', 'class' => null], | |
| 20 | + ]; | |
| 21 | + | |
| 22 | + protected array $tokens = [ | |
| 23 | + 'keyword' => [ | |
| 24 | + // 宣言 | |
| 25 | + 'associatedtype', 'class', 'deinit', 'enum', 'extension', 'fileprivate', 'func', 'import', 'init', 'inout', 'internal', 'let', | |
| 26 | + 'open', 'operator', 'private', 'precedencegroup', 'protocol', 'public', 'rethrows', 'static', 'struct', 'subscript', 'typealias', | |
| 27 | + 'var', 'actor', 'macro', 'package', 'nonisolated', 'isolated', 'consuming', 'borrowing', 'sending', | |
| 28 | + // 文 | |
| 29 | + 'break', 'case', 'catch', 'continue', 'default', 'defer', 'do', 'else', 'fallthrough', 'for', 'guard', 'if', 'in', 'repeat', | |
| 30 | + 'return', 'throw', 'switch', 'where', 'while', | |
| 31 | + // 式・型 | |
| 32 | + 'as', 'await', 'is', 'self', 'Self', 'super', 'throws', 'try', 'async', 'some', 'any', 'Type', 'Protocol', | |
| 33 | + // 文脈依存 | |
| 34 | + 'convenience', 'dynamic', 'didSet', 'final', 'get', 'indirect', 'lazy', 'mutating', 'nonmutating', 'optional', 'override', | |
| 35 | + 'postfix', 'prefix', 'infix', 'required', 'set', 'unowned', 'weak', 'willSet', | |
| 36 | + ], | |
| 37 | + 'constant' => [ | |
| 38 | + 'true', 'false', 'nil', | |
| 39 | + ], | |
| 40 | + 'type' => [ | |
| 41 | + 'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'Int128', 'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'UInt128', | |
| 42 | + 'Float', 'Float16', 'Float32', 'Float64', 'Float80', 'Double', 'CGFloat', 'Bool', 'String', 'Substring', 'Character', | |
| 43 | + 'Array', 'ContiguousArray', 'Dictionary', 'Set', 'Optional', 'Result', 'Range', 'ClosedRange', 'Void', 'Never', 'Any', 'AnyObject', | |
| 44 | + 'Error', 'Equatable', 'Hashable', 'Comparable', 'Codable', 'Encodable', 'Decodable', 'Identifiable', 'Sendable', 'Sequence', | |
| 45 | + 'Collection', 'IteratorProtocol', 'CustomStringConvertible', 'Numeric', 'BinaryInteger', 'FloatingPoint', 'SIMD2', 'SIMD3', 'SIMD4', | |
| 46 | + 'UnsafePointer', 'UnsafeMutablePointer', 'UnsafeRawPointer', 'UnsafeMutableRawPointer', 'UnsafeBufferPointer', 'OpaquePointer', | |
| 47 | + 'Data', 'Date', 'URL', 'UUID', 'NSObject', 'Task', 'MainActor', 'View', 'some', | |
| 48 | + ], | |
| 49 | + 'builtin' => [ | |
| 50 | + 'print', 'debugPrint', 'dump', 'assert', 'assertionFailure', 'precondition', 'preconditionFailure', 'fatalError', | |
| 51 | + 'min', 'max', 'abs', 'stride', 'zip', 'sequence', 'repeatElement', 'swap', 'type', 'withUnsafePointer', | |
| 52 | + 'withUnsafeMutablePointer', 'withUnsafeBytes', 'withExtendedLifetime', 'MemoryLayout', 'readLine', 'numericCast', | |
| 53 | + ], | |
| 54 | + ]; | |
| 55 | +} |
| @@ -2,9 +2,43 @@ | ||
| 2 | 2 | namespace Std\Lib\Syntax; |
| 3 | 3 | |
| 4 | 4 | interface SyntaxInterface { |
| 5 | - public function getColors(): array; | |
| 6 | - public function getComment(): ?string; | |
| 7 | - public function getInclude(array $code): ?array; | |
| 8 | - public function getMultiline(): ?array; | |
| 9 | - public function getTokens(): array; | |
| 10 | -} | |
| 5 | + /** | |
| 6 | + * コードを解析する前に呼ばれる(単語表の作成等) | |
| 7 | + * | |
| 8 | + * @param string $code ハイライトするコード全体 | |
| 9 | + * @return void | |
| 10 | + */ | |
| 11 | + public function prepare(string $code): void; | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * トークンの規則を優先順に返す | |
| 15 | + * 各規則:['re' => 正規表現(区切り文字なし), 'class' => 種類, 'bol' => 行頭のみか] | |
| 16 | + * 種類が「ident」の場合、classify()で分類する | |
| 17 | + * | |
| 18 | + * @return array 規則の配列 | |
| 19 | + */ | |
| 20 | + public function getRules(): array; | |
| 21 | + | |
| 22 | + /** | |
| 23 | + * 識別子を分類する | |
| 24 | + * | |
| 25 | + * @param string $word 識別子 | |
| 26 | + * @param array $ctx 文脈(bol, first, prev, prevWord, lineWord, next, nextRaw, nextWord) | |
| 27 | + * @return string|null 種類、色を付けない場合はnull | |
| 28 | + */ | |
| 29 | + public function classify(string $word, array $ctx): ?string; | |
| 30 | + | |
| 31 | + /** | |
| 32 | + * 入れ子可能な複数行コメントの[開始, 終了](Swift等) | |
| 33 | + * | |
| 34 | + * @return array|null | |
| 35 | + */ | |
| 36 | + public function getNestedComment(): ?array; | |
| 37 | + | |
| 38 | + /** | |
| 39 | + * 次の「<…>」をファイル名として扱うプリプロセッサ指令(include等) | |
| 40 | + * | |
| 41 | + * @return array | |
| 42 | + */ | |
| 43 | + public function getHeaderDirectives(): array; | |
| 44 | +} |
| @@ -0,0 +1,118 @@ | ||
| 1 | +<?php | |
| 2 | +/************************************************************* | |
| 3 | +# 076 License | |
| 4 | + | |
| 5 | +Copyright (c) テクニカル諏訪子 | |
| 6 | + | |
| 7 | +Permission is hereby granted to any person obtaining a copy of the software | |
| 8 | +Little Beast (the "Software") to use, modify, merge, copy, publish, distribute, | |
| 9 | +sublicense, and/or sell copies of the Software, subject to the following conditions: | |
| 10 | + | |
| 11 | + 1. **Origin Attribution**: | |
| 12 | + - You must not misrepresent the origin of the Software; you must not claim | |
| 13 | + you created the original Software. | |
| 14 | + - If the Software is used in a product, you must either: | |
| 15 | + a. Provide clear attribution in the product's documentation, user interface, | |
| 16 | + or other visible areas, **OR** | |
| 17 | + b. Pay the original developers a fee they specify in writing. | |
| 18 | + 2. **Usage Restriction**: | |
| 19 | + - The Software, or any derivative works, dependencies, or libraries | |
| 20 | + incorporating it, must not be used for censorship or to suppress freedom of | |
| 21 | + speech, expression, or creativity. Prohibited uses include, but are not | |
| 22 | + limited to: | |
| 23 | + - Censorship of so-called "hate speech", visuals, non-mainstream opinions, | |
| 24 | + ideas, or objective reality. | |
| 25 | + - Tools or systems designed to restrict access to information or | |
| 26 | + artistic works. | |
| 27 | + 3. **Notice Preservation**: | |
| 28 | + - This license and the above copyright notice must remain intact in all copies | |
| 29 | + of the source code. | |
| 30 | + | |
| 31 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, | |
| 32 | +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A | |
| 33 | +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
| 34 | +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF | |
| 35 | +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | |
| 36 | +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 37 | + */ | |
| 38 | +namespace Std\Test; | |
| 39 | + | |
| 40 | +require_once __DIR__.'/../../../autoload.php'; | |
| 41 | + | |
| 42 | +use Std\Lib\Tester; | |
| 43 | +use Std\Lib\Syntax; | |
| 44 | + | |
| 45 | +$test = new Tester([ | |
| 46 | + 'colorOutput' => true, | |
| 47 | + 'verboseOutput' => true | |
| 48 | +]); | |
| 49 | + | |
| 50 | +// タグを外してデコードすると元のコードに戻るか | |
| 51 | +function syntaxRoundtrip(string $lang, string $code): bool { | |
| 52 | + $html = (new Syntax($lang))->colorize($code); | |
| 53 | + return html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8') === $code; | |
| 54 | +} | |
| 55 | + | |
| 56 | +$samples = [ | |
| 57 | + 'c' => "#include <stdio.h>\n/* a */ int main(void) { printf(\"%d\\n\", 0x1F); return 0; } // b", | |
| 58 | + 'cpp' => "auto s = R\"(raw \"x\")\"; [[nodiscard]] std::vector<int> v{1'000};", | |
| 59 | + 'glsl' => "layout(location = 0) out vec4 fragColor;\nvoid main() { fragColor = texture(tex, gl_FragCoord.xy); }", | |
| 60 | + 'go' => "func main() { s := `raw\nstring`; fmt.Println(len(s), nil) }", | |
| 61 | + 'hlsl' => "[numthreads(8, 8, 1)]\nvoid CSMain(uint3 id : SV_DispatchThreadID) { float4x4 m; }", | |
| 62 | + 'slang' => "[shader(\"compute\")]\nvoid main<T : IFloat>(uniform Ptr<T> p) { let x = detach(1.0); }", | |
| 63 | + 'swift' => "@main struct A { /* /* nested */ */ let s = #\"raw\"#; #if DEBUG\n print(\"\\(s)\") }", | |
| 64 | + 'objc' => "#import <Foundation/Foundation.h>\n@interface Foo : NSObject\n@end\nNSLog(@\"%@\", [obj valueForKey:@\"k\"]);", | |
| 65 | + 'csharp' => "var p = @\"C:\\dir\"; var m = $\"{p}\"; var r = \"\"\"\nraw\n\"\"\";", | |
| 66 | + 'asm' => "section .text\n_start: mov rax, 1 ; nasm\n mov x8, #64\n li a7, 93 # riscv", | |
| 67 | + 'asm-x8664' => "%define N 1\nrep movsb\nmovl \$1, %eax", | |
| 68 | + 'asm-aarch64' => "b.eq done\nadd v0.4s, v1.4s, v2.4s // neon", | |
| 69 | + 'asm-aarch32' => "addeq r0, r0, #1 @ arm", | |
| 70 | + 'asm-riscv' => "fcvt.w.s a0, fa0 # x", | |
| 71 | + 'asm-mips' => "addiu \$sp, \$sp, -8 # x", | |
| 72 | + 'asm-power' => "addi r3, r3, 1 # x", | |
| 73 | + 'asm-z80' => "ex af,af' ; x\nld a,'A'", | |
| 74 | + 'asm-6502' => "lda #\$FF ; x", | |
| 75 | + 'asm-68000' => "* comment\nmove.l #1,d0", | |
| 76 | +]; | |
| 77 | + | |
| 78 | +$test->describe('シンタックスハイライト', function($test) use ($samples): void { | |
| 79 | + $test->it('全ての言語で、タグを外すと元のコードに戻るはず', function($test) use ($samples): void { | |
| 80 | + foreach ($samples as $lang => $code) { | |
| 81 | + $test->assertTrue(syntaxRoundtrip($lang, $code), "{$lang}の往復に失敗"); | |
| 82 | + } | |
| 83 | + }); | |
| 84 | + | |
| 85 | + $test->it('HTMLをエスケープするはず', function($test): void { | |
| 86 | + $html = (new Syntax('c'))->colorize('"</code><script>" // <b>'); | |
| 87 | + $test->assertFalse(str_contains($html, '<script>')); | |
| 88 | + $test->assertStringContains('</code>', $html); | |
| 89 | + }); | |
| 90 | + | |
| 91 | + $test->it('トークンを正しく分類するはず', function($test): void { | |
| 92 | + $html = (new Syntax('c'))->colorize('int x = 42; // c'); | |
| 93 | + $test->assertStringContains('<span class="hl-keyword">int</span>', $html); | |
| 94 | + $test->assertStringContains('<span class="hl-number">42</span>', $html); | |
| 95 | + $test->assertStringContains('<span class="hl-comment">// c</span>', $html); | |
| 96 | + | |
| 97 | + $html = (new Syntax('hlsl'))->colorize('float4 p : SV_Position;'); | |
| 98 | + $test->assertStringContains('<span class="hl-variable">SV_Position</span>', $html); | |
| 99 | + | |
| 100 | + $html = (new Syntax('asm'))->colorize("loop:\n mov x8, #64"); | |
| 101 | + $test->assertStringContains('<span class="hl-label">loop</span>', $html); | |
| 102 | + $test->assertStringContains('<span class="hl-number">#64</span>', $html); | |
| 103 | + }); | |
| 104 | + | |
| 105 | + $test->it('文字列の中の数字やキーワードに色を付けないはず', function($test): void { | |
| 106 | + $html = (new Syntax('c'))->colorize('"int 42"'); | |
| 107 | + $test->assertEquals('<span class="hl-string">"int 42"</span>', $html); | |
| 108 | + }); | |
| 109 | + | |
| 110 | + $test->it('別名を解決するはず', function($test): void { | |
| 111 | + $test->assertEquals('cpp', Syntax::resolve('c++')); | |
| 112 | + $test->assertEquals('csharp', Syntax::resolve('C#')); | |
| 113 | + $test->assertEquals('asm-aarch64', Syntax::resolve('arm64')); | |
| 114 | + $test->assertFalse(Syntax::isSupported('sh')); | |
| 115 | + }); | |
| 116 | +}); | |
| 117 | + | |
| 118 | +$test->printSummary(); |