true, 'verboseOutput' => true ]); // タグを外してデコードすると元のコードに戻るか function syntaxRoundtrip(string $lang, string $code): bool { $html = (new Syntax($lang))->colorize($code); return html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8') === $code; } $samples = [ 'c' => "#include \n/* a */ int main(void) { printf(\"%d\\n\", 0x1F); return 0; } // b", 'cpp' => "auto s = R\"(raw \"x\")\"; [[nodiscard]] std::vector v{1'000};", 'glsl' => "layout(location = 0) out vec4 fragColor;\nvoid main() { fragColor = texture(tex, gl_FragCoord.xy); }", 'go' => "func main() { s := `raw\nstring`; fmt.Println(len(s), nil) }", 'hlsl' => "[numthreads(8, 8, 1)]\nvoid CSMain(uint3 id : SV_DispatchThreadID) { float4x4 m; }", 'slang' => "[shader(\"compute\")]\nvoid main(uniform Ptr p) { let x = detach(1.0); }", 'swift' => "@main struct A { /* /* nested */ */ let s = #\"raw\"#; #if DEBUG\n print(\"\\(s)\") }", 'objc' => "#import \n@interface Foo : NSObject\n@end\nNSLog(@\"%@\", [obj valueForKey:@\"k\"]);", 'csharp' => "var p = @\"C:\\dir\"; var m = $\"{p}\"; var r = \"\"\"\nraw\n\"\"\";", 'asm' => "section .text\n_start: mov rax, 1 ; nasm\n mov x8, #64\n li a7, 93 # riscv", 'asm-x8664' => "%define N 1\nrep movsb\nmovl \$1, %eax", 'asm-aarch64' => "b.eq done\nadd v0.4s, v1.4s, v2.4s // neon", 'asm-aarch32' => "addeq r0, r0, #1 @ arm", 'asm-riscv' => "fcvt.w.s a0, fa0 # x", 'asm-mips' => "addiu \$sp, \$sp, -8 # x", 'asm-power' => "addi r3, r3, 1 # x", 'asm-z80' => "ex af,af' ; x\nld a,'A'", 'asm-6502' => "lda #\$FF ; x", 'asm-68000' => "* comment\nmove.l #1,d0", ]; $test->describe('シンタックスハイライト', function($test) use ($samples): void { $test->it('全ての言語で、タグを外すと元のコードに戻るはず', function($test) use ($samples): void { foreach ($samples as $lang => $code) { $test->assertTrue(syntaxRoundtrip($lang, $code), "{$lang}の往復に失敗"); } }); $test->it('HTMLをエスケープするはず', function($test): void { $html = (new Syntax('c'))->colorize('"