変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

[javascript]binaryen.js

binaryen.jsはbinaryenのjs版。延々とビルドせずに使えるのが嬉しい。

binaryenの説明文に「 used from JavaScript. 」って書いてあるけど、その先はパスにwikiがあるので説明だけ。ググり続け、npmのリポジトリィを見つけた。

npm install binaryen

元ネタのリポジトリィは、github.com/AssemblyScript/binaryen.jsのindex.jsだそうだ。

ちなみにそこをクローンすると6GB以上あるのでnpmで済ませる。

パッケージ本体はTypeScriptで書かれていて配布用のindex.jsは圧縮されほぼ解読不能。

npmのページのサンプルコードを入力して実行すると

Uncaught Error Error [ERR_REQUIRE_ASYNC_MODULE]: require() cannot be used on an ESM graph with top-level await. Use import() instead. To see where the top-level await comes from, use --experimental-print-required-tla.

どうやらnpmのページのサンプルは5年前の書き方らしい。
var binaryen = require(“binaryen”);
を元ネタのページのサンプルの様に
import binaryen from “binaryen”;
に直すと動き出したが、直ぐエラってしまうので、※5年の間に色々変わり過ぎ
全部元ネタのページのサンプルに差し替えると最後まで動いた。
ちなみにこの記事を書いている時点でのモジュールのupdateは4時間前。※出来たてホヤホヤ

それにしても
jsのアロー関数とか、
wasmのテキストがS式とか、
大昔に卒論で(デバッグのために)いっぱい書いたから違和感はないけど
なんで大昔(半世紀くらい前)に流行ったフォーマットを使うんだろう?

謎(闇?)は深い。

binaryen.jsはTypeScriptベースのjavascriptソースで提供されている。

Node.jsでサンプルを実行する分にはnpmで作った設定ファイル等を参照してくれるのでパッケージ名だけ指定すればOK。

import binaryen from "binaryen";

ブラウザ(Chrome)で使用する場合はスクリプト・タグのタイプを”module”指定しimport文を使用可能にしてもnpmプロジェクトとか気に留めてないので直接node_modules/binaryenプロジェクトのpackage.jsonに”main”:”index.js”と書かれたファイルを相対パスで指定しないといけない。

chromeで表示するHTMLで<script type="module" src="./worker.js">とtypeでmodule指定しないとimportが使えない

import binaryen from "./node_modules/binaryen/index.js"; ※npm install binaryen したなら皆同じになるハズ

※ローカルな環境(file:///)ではimportがアクセスエラーで使えないっぽく、何かのWebサービス(apache等)上にHTML等を配置する必要がある。

index.html:1 Access to script at 'file:///・・・/test/worker.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: chrome, chrome-extension, chrome-untrusted, data, http, https, isolated-app.
worker.js:1 
Failed to load resource: net::ERR_FAILED

さらに厄介なのが、HTMLやtype=”text/script”(デフォルト設定)のjavascriptからtype=”module”を指定するモジュールが見えない(DevToolでモジュール欄すら出ない)のでpostMessageを経由してイベントを通知しないとダメらしい。※moduleと指定してるのでworker.jsをワーカーとして実行しなくてよい。

window.addEventListener("load", () => {
    document.querySelector("#btn").addEventListener("click", test);
});
const test = () => {
    postMessage("test", "http://localhost:xxxxx"とか);
}

それより全スクリプトをtype=”module”にすればいい、モジュールにはwindow.addEventListener(“load”, の通知が来ないが、 モジュールにはdefer属性が自動付与されるから、即DOMにアクセスできる。

import binaryen from "./node_modules/binaryen/index.js";

//# とりあえず、urlからドメインとポートを取り出してポストメッセージのチェックに使用する
//# function getDomainPort() {
//#     const url = import.meta.url;
//#     const result = /^(http[s]?:\/\/([.0-9a-z]+)(:\d+)?)/.exec(url);
//#     return result[0];
//# }
document.querySelector("#btn").addEventListener("click", workerTest);※これで解決

// アロー関数にするとworkerTest実行時にChromeのDevToolの範囲のモジュールに
// 自分の関数名が出ないので再帰する時は困るかもしれない。
function workerTest(event) {
    try {
        //# if (event.origin !== getDomainPort()) {
        //#     return;
        //# }
        // Create a module with a single function
        myModule.addFunction("add",                                 // 以下、ボクの理解範囲
            binaryen.createType([binaryen.i32, binaryen.i32]),      // parames: [引数[0]の型, 引数[1]の型]
            binaryen.i32,                                           // result: 戻り値の型
            [binaryen.i32],                                         // vars: スタックを型付き配列風に宣言
            myModule.block(null, [
                myModule.local.set(2,                               // 結果を スタック[2] に格納
                    myModule.i32.add(                               //   加算(スタックの2つの変数を加算)
                        myModule.local.get(0, binaryen.i32),        //      引数[0]をスタックに積む
                        myModule.local.get(1, binaryen.i32)         //      引数[1]をスタックに積む
                    )
                ),
                myModule.return(                                    // 戻り値は下記の様にスタックされる
                    myModule.local.get(2, binaryen.i32)             //   スタック[2]をスタックに積む
                )
            ])
        );
        myModule.addFunctionExport("add", "add");                   // 上の"add"関数を"add"の名前で外部参照を宣言

        // Optimize the module using default passes and levels
        myModule.optimize();                                        // 最適化

        // Validate the module
        if (!myModule.validate())                                   // 計算でエラったら
            throw new Error("validation error");                    // "validation error"とスローする

        // Generate text format and binary
        var textData = myModule.emitText();                         // 上記の設定からテキスト(S式)で得る
        var wasmData = myModule.emitBinary();                       // 上記の設定からWebAssemblyコードで得る

        // Example usage with the WebAssembly API
        var compiled = new WebAssembly.Module(wasmData);            // WebAssemblyコードを含むモジュールを生成
                                      //  importのリンク情報等を設定?
        var instance = new WebAssembly.Instance(compiled, {});      // 実行可能なモジュールのインスタンスを生成
        const result = (instance.exports.add(41, 1));               // インスタンスを実行させてみる

        document.querySelector("#result").innerHTML = `textData:${textData}<br/>result:${result}<br/>`;
};
window.addEventListener("message", workerTest);

うっかりすると、localってローカルな変数かと思うけどスタックマシンなのでlocal=stack。

myModule.local.set(2, よりも myModule.local.set(0, の方が良いと思うけど、最適化(Module.optimize())でS式はいづれも同じになる。

(module
   (type $0 (func (param i32 i32) (result i32)))
     (export "add" (func $add))
       (func $add (param $0 i32) (param $1 i32) (result i32)
         (i32.add (local.get $0) (local.get $1) ) ) )    ※明示的にパラメータをスタックに積む様になってる

ここまで面倒になってるものの、以前はmodule側からは一切DOMにアクセスできなかったと思うが

document.querySelector(“#result”)が可能になってるのは助かる。

とりあえず、podmanの未公開なapacheにソースを配置してVScodeでリモートデバッグを心掛ければ支障は無さそう。

VScodeの拡張機能でLive ServerLive Previewを使うのも手軽かも。いづれもVScode上でのデバッグはできないが、

Live Serverは、HTMLエディタ上で右クリックして「Open with Live Server」でブラウザで開けるしDevToolでデバッグが可能。

Live Previewも同様に「Show Preview」でスグ見れる、見れるダケだけど。

LiveServerがどのポートを使うのか判らないのでgetDomainPort()みたいにLiveServerが割り当てたurlからドメインとポートが得られるようにした方が気楽。



[AlmaLinux]Ninja

wabtのtestスイートのmakeにはPython3が必須。

条件を満たしているのでビルドしてみると

$ make test
which: no emcc in (/home/****/.local/bin:/home/****/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin)
cd out/clang/Debug/ && cmake -G Ninja /home/****/wabt/ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug 
CMake Error: CMake was unable to find a build program corresponding to "Ninja".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.
-- Configuring incomplete, errors occurred!
make: *** [Makefile:156: out/clang/Debug/build.ninja] エラー 1

Ninjaが必要らしいが、dnfのepelリポジトリにも無い

ソースからビルドしてみる。詳細はgithub

# git clone https://github.com/ninja-build/ninja.git
Cloning into 'ninja'...
remote: Enumerating objects: 13476, done.
remote: Counting objects: 100% (369/369), done.
remote: Compressing objects: 100% (222/222), done.
remote: Total 13476 (delta 266), reused 147 (delta 147), pack-reused 13107 (from 6)
Receiving objects: 100% (13476/13476), 5.12 MiB | 1.82 MiB/s, done.
Resolving deltas: 100% (9425/9425), done.
# cmake -Bbuild-cmake ※ビルド事前設定のオプションらしい -DBUILD_TESTING=OFF付加でユニットテスト無効も可
-- The CXX compiler identification is GNU 11.5.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- IPO / LTO enabled
-- Performing Test flag_no_deprecated
-- Performing Test flag_no_deprecated - Success
-- Looking for ppoll
-- Looking for ppoll - found
CMake Warning at CMakeLists.txt:82 (message):
  re2c 2 or later was not found; changes to src/*.in.cc will not affect your
  build.


-- Looking for fork
-- Looking for fork - found
-- Looking for pipe
-- Looking for pipe - found
-- Could NOT find GTest (missing: GTEST_LIBRARY GTEST_INCLUDE_DIR GTEST_MAIN_LIBRARY) 
-- The C compiler identification is GNU 11.5.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Found Python: /usr/bin/python3.9 (found version "3.9.21") found components: Interpreter 
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success
-- Found Threads: TRUE  
-- Configuring done (10.9s)
-- Generating done (0.2s)
-- Build files have been written to: /root/ninja/build-cmake
# cmake --build build-cmake
[  1%] Building CXX object CMakeFiles/libninja-re2c.dir/src/depfile_parser.cc.o
[  2%] Building CXX object CMakeFiles/libninja-re2c.dir/src/lexer.cc.o
[  2%] Built target libninja-re2c
[  3%] Building CXX object CMakeFiles/libninja.dir/src/build_log.cc.o
[  5%] Building CXX object CMakeFiles/libninja.dir/src/build.cc.o
[  6%] Building CXX object CMakeFiles/libninja.dir/src/clean.cc.o
[  7%] Building CXX object CMakeFiles/libninja.dir/src/clparser.cc.o
[  8%] Building CXX object CMakeFiles/libninja.dir/src/dyndep.cc.o
[ 10%] Building CXX object CMakeFiles/libninja.dir/src/dyndep_parser.cc.o
[ 11%] Building CXX object CMakeFiles/libninja.dir/src/debug_flags.cc.o
[ 12%] Building CXX object CMakeFiles/libninja.dir/src/deps_log.cc.o
[ 14%] Building CXX object CMakeFiles/libninja.dir/src/disk_interface.cc.o
[ 15%] Building CXX object CMakeFiles/libninja.dir/src/edit_distance.cc.o
[ 16%] Building CXX object CMakeFiles/libninja.dir/src/elide_middle.cc.o
[ 17%] Building CXX object CMakeFiles/libninja.dir/src/eval_env.cc.o
[ 19%] Building CXX object CMakeFiles/libninja.dir/src/graph.cc.o
[ 20%] Building CXX object CMakeFiles/libninja.dir/src/graphviz.cc.o
[ 21%] Building CXX object CMakeFiles/libninja.dir/src/json.cc.o
[ 23%] Building CXX object CMakeFiles/libninja.dir/src/line_printer.cc.o
[ 24%] Building CXX object CMakeFiles/libninja.dir/src/manifest_parser.cc.o
[ 25%] Building CXX object CMakeFiles/libninja.dir/src/metrics.cc.o
[ 26%] Building CXX object CMakeFiles/libninja.dir/src/missing_deps.cc.o
[ 28%] Building CXX object CMakeFiles/libninja.dir/src/parser.cc.o
[ 29%] Building CXX object CMakeFiles/libninja.dir/src/real_command_runner.cc.o
[ 30%] Building CXX object CMakeFiles/libninja.dir/src/state.cc.o
[ 32%] Building CXX object CMakeFiles/libninja.dir/src/status_printer.cc.o
[ 33%] Building CXX object CMakeFiles/libninja.dir/src/string_piece_util.cc.o
[ 34%] Building CXX object CMakeFiles/libninja.dir/src/util.cc.o
[ 35%] Building CXX object CMakeFiles/libninja.dir/src/version.cc.o
[ 37%] Building CXX object CMakeFiles/libninja.dir/src/subprocess-posix.cc.o
[ 37%] Built target libninja
[ 38%] Generating build/browse_py.h
[ 39%] Building CXX object CMakeFiles/ninja.dir/src/ninja.cc.o
[ 41%] Building CXX object CMakeFiles/ninja.dir/src/browse.cc.o
[ 42%] Linking CXX executable ninja
[ 42%] Built target ninja
[ 43%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o
[ 44%] Linking CXX static library ../../../lib/libgtest.a
[ 44%] Built target gtest
[ 46%] Building CXX object CMakeFiles/ninja_test.dir/src/build_log_test.cc.o
[ 47%] Building CXX object CMakeFiles/ninja_test.dir/src/build_test.cc.o
[ 48%] Building CXX object CMakeFiles/ninja_test.dir/src/clean_test.cc.o
[ 50%] Building CXX object CMakeFiles/ninja_test.dir/src/clparser_test.cc.o
[ 51%] Building CXX object CMakeFiles/ninja_test.dir/src/depfile_parser_test.cc.o
[ 52%] Building CXX object CMakeFiles/ninja_test.dir/src/deps_log_test.cc.o
[ 53%] Building CXX object CMakeFiles/ninja_test.dir/src/disk_interface_test.cc.o
[ 55%] Building CXX object CMakeFiles/ninja_test.dir/src/dyndep_parser_test.cc.o
[ 56%] Building CXX object CMakeFiles/ninja_test.dir/src/edit_distance_test.cc.o
[ 57%] Building CXX object CMakeFiles/ninja_test.dir/src/elide_middle_test.cc.o
[ 58%] Building CXX object CMakeFiles/ninja_test.dir/src/explanations_test.cc.o
[ 60%] Building CXX object CMakeFiles/ninja_test.dir/src/graph_test.cc.o
[ 61%] Building CXX object CMakeFiles/ninja_test.dir/src/json_test.cc.o
[ 62%] Building CXX object CMakeFiles/ninja_test.dir/src/lexer_test.cc.o
[ 64%] Building CXX object CMakeFiles/ninja_test.dir/src/manifest_parser_test.cc.o
[ 65%] Building CXX object CMakeFiles/ninja_test.dir/src/missing_deps_test.cc.o
[ 66%] Building CXX object CMakeFiles/ninja_test.dir/src/ninja_test.cc.o
[ 67%] Building CXX object CMakeFiles/ninja_test.dir/src/state_test.cc.o
[ 69%] Building CXX object CMakeFiles/ninja_test.dir/src/string_piece_util_test.cc.o
[ 70%] Building CXX object CMakeFiles/ninja_test.dir/src/subprocess_test.cc.o
[ 71%] Building CXX object CMakeFiles/ninja_test.dir/src/test.cc.o
[ 73%] Building CXX object CMakeFiles/ninja_test.dir/src/util_test.cc.o
[ 74%] Linking CXX executable ninja_test
[ 74%] Built target ninja_test
[ 75%] Building CXX object CMakeFiles/build_log_perftest.dir/src/build_log_perftest.cc.o
[ 76%] Linking CXX executable build_log_perftest
[ 76%] Built target build_log_perftest
[ 78%] Building CXX object CMakeFiles/canon_perftest.dir/src/canon_perftest.cc.o
[ 79%] Linking CXX executable canon_perftest
[ 79%] Built target canon_perftest
[ 80%] Building CXX object CMakeFiles/clparser_perftest.dir/src/clparser_perftest.cc.o
[ 82%] Linking CXX executable clparser_perftest
[ 82%] Built target clparser_perftest
[ 83%] Building CXX object CMakeFiles/depfile_parser_perftest.dir/src/depfile_parser_perftest.cc.o
[ 84%] Linking CXX executable depfile_parser_perftest
[ 84%] Built target depfile_parser_perftest
[ 85%] Building CXX object CMakeFiles/elide_middle_perftest.dir/src/elide_middle_perftest.cc.o
[ 87%] Linking CXX executable elide_middle_perftest
[ 87%] Built target elide_middle_perftest
[ 88%] Building CXX object CMakeFiles/hash_collision_bench.dir/src/hash_collision_bench.cc.o
[ 89%] Linking CXX executable hash_collision_bench
[ 89%] Built target hash_collision_bench
[ 91%] Building CXX object CMakeFiles/manifest_parser_perftest.dir/src/manifest_parser_perftest.cc.o
[ 92%] Linking CXX executable manifest_parser_perftest
[ 92%] Built target manifest_parser_perftest
[ 93%] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o
[ 94%] Linking CXX static library ../../../lib/libgmock.a
[ 94%] Built target gmock
[ 96%] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o
[ 97%] Linking CXX static library ../../../lib/libgmock_main.a
[ 97%] Built target gmock_main
[ 98%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o
[100%] Linking CXX static library ../../../lib/libgtest_main.a
[100%] Built target gtest_main
#

うまくいったみたいなのでインスト

# cmake --install ./build-cmake/
-- Install configuration: ""
-- Up-to-date: /usr/local/include
-- Installing: /usr/local/include/gmock
-- Installing: /usr/local/include/gmock/gmock-actions.h
-- Installing: /usr/local/include/gmock/gmock-cardinalities.h
-- Installing: /usr/local/include/gmock/gmock-function-mocker.h
-- Installing: /usr/local/include/gmock/gmock-matchers.h
-- Installing: /usr/local/include/gmock/gmock-more-actions.h
-- Installing: /usr/local/include/gmock/gmock-more-matchers.h
-- Installing: /usr/local/include/gmock/gmock-nice-strict.h
-- Installing: /usr/local/include/gmock/gmock-spec-builders.h
-- Installing: /usr/local/include/gmock/gmock.h
-- Installing: /usr/local/include/gmock/internal
-- Installing: /usr/local/include/gmock/internal/custom
-- Installing: /usr/local/include/gmock/internal/custom/README.md
-- Installing: /usr/local/include/gmock/internal/custom/gmock-generated-actions.h
-- Installing: /usr/local/include/gmock/internal/custom/gmock-matchers.h
-- Installing: /usr/local/include/gmock/internal/custom/gmock-port.h
-- Installing: /usr/local/include/gmock/internal/gmock-internal-utils.h
-- Installing: /usr/local/include/gmock/internal/gmock-port.h
-- Installing: /usr/local/include/gmock/internal/gmock-pp.h
-- Installing: /usr/local/lib64/libgmock.a
-- Installing: /usr/local/lib64/libgmock_main.a
-- Installing: /usr/local/lib64/pkgconfig/gmock.pc
-- Installing: /usr/local/lib64/pkgconfig/gmock_main.pc
-- Installing: /usr/local/lib64/cmake/GTest/GTestTargets.cmake
-- Installing: /usr/local/lib64/cmake/GTest/GTestTargets-noconfig.cmake
-- Installing: /usr/local/lib64/cmake/GTest/GTestConfigVersion.cmake
-- Installing: /usr/local/lib64/cmake/GTest/GTestConfig.cmake
-- Up-to-date: /usr/local/include
-- Installing: /usr/local/include/gtest
-- Installing: /usr/local/include/gtest/gtest-assertion-result.h
-- Installing: /usr/local/include/gtest/gtest-death-test.h
-- Installing: /usr/local/include/gtest/gtest-matchers.h
-- Installing: /usr/local/include/gtest/gtest-message.h
-- Installing: /usr/local/include/gtest/gtest-param-test.h
-- Installing: /usr/local/include/gtest/gtest-printers.h
-- Installing: /usr/local/include/gtest/gtest-spi.h
-- Installing: /usr/local/include/gtest/gtest-test-part.h
-- Installing: /usr/local/include/gtest/gtest-typed-test.h
-- Installing: /usr/local/include/gtest/gtest.h
-- Installing: /usr/local/include/gtest/gtest_pred_impl.h
-- Installing: /usr/local/include/gtest/gtest_prod.h
-- Installing: /usr/local/include/gtest/internal
-- Installing: /usr/local/include/gtest/internal/custom
-- Installing: /usr/local/include/gtest/internal/custom/README.md
-- Installing: /usr/local/include/gtest/internal/custom/gtest-port.h
-- Installing: /usr/local/include/gtest/internal/custom/gtest-printers.h
-- Installing: /usr/local/include/gtest/internal/custom/gtest.h
-- Installing: /usr/local/include/gtest/internal/gtest-death-test-internal.h
-- Installing: /usr/local/include/gtest/internal/gtest-filepath.h
-- Installing: /usr/local/include/gtest/internal/gtest-internal.h
-- Installing: /usr/local/include/gtest/internal/gtest-param-util.h
-- Installing: /usr/local/include/gtest/internal/gtest-port-arch.h
-- Installing: /usr/local/include/gtest/internal/gtest-port.h
-- Installing: /usr/local/include/gtest/internal/gtest-string.h
-- Installing: /usr/local/include/gtest/internal/gtest-type-util.h
-- Installing: /usr/local/lib64/libgtest.a
-- Installing: /usr/local/lib64/libgtest_main.a
-- Installing: /usr/local/lib64/pkgconfig/gtest.pc
-- Installing: /usr/local/lib64/pkgconfig/gtest_main.pc
-- Installing: /usr/local/bin/ninja

とんでもない数

ninja --version
1.13.0.git

一応インストできたっぽい

ところが先のtestスイートの実行がfailする。あきらめてtest/run-tests.py



[javascript]正規表現を正規表現で表現できるかな

正規表現自体・・・

呪文

と呼ばれているので

下のテキストから正規表現の部分をパースしなさいとかは、難題

(* 文法   *)
        syntax      =   rule_list /\s*/ ;
        rule_list   =   { ( rule | special_effect | comment ) } + ;
        rule        =   identifier defining_symbol definitions ";" ;
        defining_symbol = "=" | "::=" ;                                     (* 終端記号 '=':EBNF, '::=':BNF *)
        (* 定義   *)
        definitions =   definition { "|" definition } ;                     (* 定義リスト *)
        definition  =   choice ;                                            (* 定義 *)
        (* リスト *)
        choice      =   sequence { "|" sequence } ;                         (* アレかコレかソレかのどれか *)
        sequence    =   exception { [ "," ] exception } ;          		    (* アレとコレとソレの一式。","は空白改行と同義  *)
        exception   =   words, { "-" words } ;          			        (* アレが良いが、コレとソレを除く。 *)
        (* 語彙   *)
        words       =   [ comments ] word [ comments ] ;
        special_effect =  (*$ NO SPACE SKIP $*) "(*$" /[^\\?]*/ "$*)" ;     (* 特殊効果 *)
        comments    =   ( special_effect | comment ) repeat_symbol;
        word        =   ( sq_string | wq_string | wraper | special_text | regexp | identifier ) ;
        comment     =   /(?:\(*))(?![*][/])(.*)(?:*\))/ ;                   (* 注意書き *)
        sq_string   =   /(?:')(?!\\\\)[^']*(?:')/ ;                         (* ' で括られたテキスト *)
        wq_string   =   /(?:")(?!\\\\)[^"]*(?:")/ ;                         (* " で括られたテキスト *)
        bq_string   =   /(?:\`)(?!\\\\)[^"]*(?:\`)/ ;                       (* " で括られたテキスト *)
        (* 囲み   *)
        wraper      =   ( group | option | repeate ) repeat_symbol ;
        group       =   "(" choice ")"  ;                                   (* choiceを明示的に括る表現 *)
        option      =   "[" choice "]"  ;                                   (* あれば尚良し *)
        repeate     =   "{" choice "}"  ;                                   (* 0回以上繰り返す *: 0回以上、+:1回以上 *)    (* TODO choiceを使うと exceptionで[undefined]が返る *)
        regexp      =   /.+/([a-z]*)  ;      
        identifier  =   /[A-Za-z][A-Z_a-z0-9\\x20]*[A-Z_a-z0-9]*/;          (* 識別子 *)
        special_text =  (*$ NO SPACE SKIP $*) "?" /[^\\?]*/ "?" ;           (* 特殊文字列 *)
        repeat_symbol=  [ "*" | "+" | '{' [ number ] ',' [ number ] '}'];   (* 繰返し記号 *)
        number      =   /\d+/ ;

なので

regexp  =  /.+/([a-z]*) ;

とか表記は誤魔化してたけど、

実際にはこれでパースするとテキストの終りの手前までヒットしてしまう。

とある記事で、エスケープ表現の文字を潜り抜けさせる方法が載ってた。

 (¥¥@)[^@]+   ※@は任意の文字

な感じで@のエスケープ表現の2文字@以外の文字のいづれかを繰り返す表現をしていた。

これを真似て

/ ( ( \ / ( ( \ \ / ) | ( [ \ / ) | [^\ / ] ) * \ / ) ( [ a – z ] * ) ) /

/ \ / ( ( \ / ( ( \ \ \ / ) | ( \ [ \ / ) | ( \ ( \ / ) | ( [ ^ / ] ) ) * \ / ) ( [ a – z ] * ) ) /  ※壊れてたので訂正 2025/5/6

※ps.2025/5/8 さっぱり通らなくなることはタマにある。が起きたので再訂正、正規表現デバッガもコピーボタンも2通りの書き方を出力するように変更(DevToolsを開きキャッシュクリア必須)。やはりコノPC何かが変なんだろうね?

const re1 = new RegExp("(\\/((\\\\\\/)|(\\[\\/)|(\\(\\/)|([^/]))*\\/)([a-z]*)", "gim");
const re2 = /(\/((\\\/)|(\[\/)|(\(\/)|([^/]))*\/)([a-z]*)/gim;

※崩れた顔文字ではありません、読みにくいので空白を挿入しました。

を作って試してみた結果は

うまくいってるみたいだが、ヒットした正規表現の半分は顔文字にしか見えない。

しかも、長い。長すぎる~。

ps.2025/5/9

MDNでreplace系で使う置換する関数に渡るパラメータは

replace(/(/{2}.*$|\/\*\/?([^/]|[^*]\/|\r|\n)*\*\/)/, replacer);
👇
function replacer(match, p1, p2, /* …, */ pN, offset, string ) {
  return replacement;
}

で、「名前付きのキャプチャグループ」がある場合はgroupsが付加されて

replace(/(?<COMMENT>/{2}.*$|\/\*\/?([^/]|[^*]\/|\r|\n)*\*\/)/, replacer);
👇
function replacer(match, p1, p2, /* …, */ pN, offset, string, groups) {
  return replacement;
}

となっていたので、名無しの正規表現でもそれなりに色付けできるように上書き修正

普通に

function replacer(match, p1, p2,  … pN, offset, string, groups) {
//  … pN 部分は全部pNに含まれる(だったらいいな的な
  return replacement;
}

みたいに…(残余引数)が使えたらいいけどね。

ps.2025/5/18

自身の表記が通らなかったので、

・・・|(\^\/\])|・・・

を追記したら改行が通ってしまうので

[^/] を
[^/\r\n]に変更

して

/(\/((\\\/)|(\^\/\])|(\[\/)|(\(\/)|([^/\r\n]))*\/)([dgimsuvy]*)/

になったけど、BNFのテキストの正規表現内に

\r\n

があるとダメなのは当然なので

\\r\\n

と表記しても通らない?

原因がよく解らないなぁと思ったら \が通らなくなっていた。

[^/\r\n]は、 / と¥とrとnを除外する意味と\r\nを除外する2つの意味を持ってしまっていた。

色々試してみた結果

x ([^/\r\n])
▲ ([^\r\n/])   \]っぽく処理されてダメだった
▲ ([^/]|[^\r\n]) 論理和だから条件が甘アマ
◎([^/\x0a\x0d]) 非推奨だけど

らしい(微妙

その後も「*/」のパターンが抜けてたり追記する度にエスケープさせるパターンが増える罠に遭遇し続けた結果がコレ

/(\/((\\\/)|(\^\/)|(\[\/)|(\(\/)|(\*\/)|([^/\x0a\x0d]))*\/)([dgimsuvy]*)/

自身はちゃんと判定できるものの、「*/」は末端の「/」と判定しないから、

「/.*/aaaaaaa/」は、「/.*/aaaaaaa/」な正規表現と判定される訳で

正規表現はパーサコンビネーションで解消するしかないっぽい。

そうなるとまた際限がないので、

正規表現を

  • / で始まり
  • 特殊な文字「( ) { } [] . + *等」か その他の文字 の繰り返し
  • / で終わる

の様な表現で括らないとダメな気がする。

/(\/)((\\/)|([^/\x0a\x0d]))+(\/)([dgimsuvy]*)/

のあたりで妥協して

EBNFで使うには文法上の利用方法を見直した方が良さそう。

でも、正規表現の文字クラスの中の文字クラスとかグループとか繰り返しとかを内包しないみたいなんで、[…]は/有無に関わらず通しても良さそう

/(\/)(((\\\/)|(\[.*?\])|([^/\r\n\s]))+)?(\/)([dgimsuvy]*)/gmuy

で、何とかならないかな?

2か所で使っている最短マッチング(x?y)が微妙で、chromeでもnode.jsでも

1つ目はグループを入れて見やすくしたり手を入れると、通常の最長マッチングになり

◎ (\[.*?\])
× ((\[.*)?(\])) xというか通常の最長マッチングになる場合がある

文末に近いコメントの中の ] までマッチングしてしまうが、

/(\\/)((\\\\/)|((\[.*)?(\]))|([^/]))+(\\/)(dgimsuvy*)/  ; (* 正規表現されたテキスト  + /.+/ "/" + { /[dgimsuvy]/ } *)

2つ目は(…)?(\/)とグループにして入れても支障は無い。

どうやら(…)で括ると \[ [ みたいに階層無しに思え最長マッチング(つまり通常のマッチング)してる気もするが([.*?])単体では最短マッチングするので、最短マッチングを階層の上と下で使う分には問題ないけど、その両方の?の両辺をグループにしてしまうとマッチングの位置(position)をうっかり共有してしまってる(VMの変数のネーミングが被ってるかVMのコンパイラには同じに見えている)気がする。

それに、VScodeのjsファイルのエディタで(\\\/)部分がダメで(\\\\/)で通る様にしたのが上の正規表現だったりするので、雑な正規表現は微妙な動きをするのは仕方が無いのかもしれない。



「小ネタ」簡単に計算する?

動画を見てたら、45×45を簡単に計算する方法ってのがあって

上一桁が同じで下一桁の合計が10になる場合は

(上桁の数字+1)× 上桁 + 下桁同士の掛け算

  45       (1+4)5           55              55
×45     ×      45        ×45       ×     45
                                                 2025

と計算するらしい。

AB x AC
この式を各桁の数字で分解すると
=(A10+B)(A10+C) となり分解すると = BC + A10C + AB10 + AA100・・・式A

先の上桁に+1して同じ桁同士の掛け算は
= (A+1)A100 + BC = AA100 + A100 + BC = BC + A100 + AA*100・・・・・・・・式B

式Aと式Bの値が一致する等号式は
BC + A10C + AB10 + AA100 = BC + A100 + AA100

両辺で同じ計算消し込むと

BC + A10C + AB10 + AA100 = BC + A100 + AA100

A10C + AB10 = A100

10C + B10 = 100

これから C+ B = 10 の場合に成立する。

よって、上桁が同じで、下桁の合計が10になる場合に成立する。動画で下桁の合計が10の条件を聞き忘れたらしい。

条件が成立する45x45では正解するけど

条件を満たさない44x44では下桁同士の掛け算は16で見るからに成立しない

(小話)

同じ桁同士を掛け算してテストで

0点を貰った子供が

「悔しく悔しく悔しく・・・」

でも~「・・・・・・・・」の場合は、

ちょっと1加えて同じ桁同士を掛け算するだけで

いいんだよ!

っていたのを教師が

この子は天才(天災の意味も込めて)

お話と広めた結果なんかな?



[javascript]XmlDocument?

XmlDocumentの構成とかjavascriptでの読み方とか

xmlData.getElementsByTagName("要素名")[0]
とか
xmlData.querySelect(セレクター)
とか

な感じで検索する記事はあるけど、ザーット表示する記事はなかった。

ま、ブラウザで表示すればいいからね。

でも自作のTreeViewもどきで作った

APIはまだ無いので自力でTreeViewにHTMLobjectを書き込まないといけない。

    <div class="tree_view">
        <!-- level 1 -->
        <div class="element open"><tag-name attr-name1="xxxxxx" attr-name2="xxxxxx" >
            <div class="contener">
                <!-- level 2 -->
                <div class="context">テキスト1</div>
                <div class="context">テキスト2</div>
                <div class="context">テキスト3</div>
                <div class="context">テキスト4</div>
                <div><tag-name2/></div>
            </div>
            <div class="element-end"></tag-name1></div>
        </div>
        <!-- level 1 -->

な感じでHTMLを作ると

な雰囲気で表示するところまで地味にcssを書いて

body {
    background-color: antiquewhite;
}
div.tree_view {
    background-color: burlywood;
    font-family: 'メイリオ', 'Meiryo', 'MS ゴシック', 'Hiragino Kaku Gothic ProN', 'ヒラギノ角ゴ ProN W3', sans-serif;
    font-size: 10pt;
    height: 100%; width: calc(100% - 20px);
    margin: 10px; border: solid black 1px; padding-left: 1em;    
}
div.marker {
    vertical-align: top;
    display: inline-block;
    width: 1em;
    border: solid blue 1px;
}
div.element {
    margin-right: 6px;
}
(省略)

点線も描いて

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
	<path d="M 0,0,0,9,14,9" fill="none" stroke="gray" stroke-dasharray="2" />
</svg>

後は、XMLファイルを読んで終了。

  ・・・
    /**
     * ファイルボタンのファイル選択イベント 
     * @param {*} event 
     */
    onSelectedFile = (event) => {
        // A file selection event occurred.
        console.log(`file selection event occurred. '${Array.from(event.target.files).map((a) => a.name).join(', ')}'`);
        // Get a file object.
        const fileObjects = event.target.files;
        // 過去歴をクリア
        this.xmlDocuments = [];
        clearnHtmlObjectTextContent(this.outputHtmlObject);
        // Load file 
        this.loadFiles(fileObjects, this.readXml, this.outputHtmlObject);
    };
    /**
     * load fileObject
     * @param {FileList} fileObjects 
     * @param {*} contextReader 
     * @param {*} outputHtmlObject
     */
    loadFiles = (fileObjects, contextReader, outputHtmlObject) => {
        Array.from(fileObjects).map((fileObject) => {
            const fr = new FileReader();
            fr.addEventListener('load', (event) => {
                // ファイルのテキストを取得
                const xmlContext = event.target.result;
                contextReader(xmlContext, outputHtmlObject);
            });
            fr.readAsText(fileObject);
        });
    }
  (省略)
// setup
window.addEventListener("load", () => {
    const xmlView = new TreeView(document.querySelector(`#file`), document.querySelector(`.tree_view`));
    // tree_view の▼と▶の切替
    window.addEventListener("click", (event) => {
        if (Array.from(event.target.classList).some((e) => e === 'element')) {
            const target = event.target; target.classList.toggle('open');
            const chLst = Array.from(target.childNodes).filter((ch) => {
                return ((!ch.classList) ? false : Array.from(ch.classList).some((cl) => cl === 'contener'));
            });
            (chLst).forEach(ch => ch.classList.toggle('hidden'));
        };
    });
});

のハズだったけど、

new DOMParser().parseFromString(xmlText, ‘application/xml’)で得られる

XmlDocumentのMDNの説明はDocument→Nodeクラスの派生型です。(説明終わり

仕方なく汎用XML解析ルーチンaddTreeListByXmlを作ることに

tagNameが無い場合はnodeNameが「#TEXT」なんで、テキストコンテキストノードっぽいのでtextContentのみ設定とかXMLを読んだ結果を見ながら行き当たりばったりで書いた。

配下のオブジェクトはchildListかなと思ったけど、childNodesの方だった。

属性はgetAttributeNames()で属性名を取得してgetAttribute(属性名)で値が判る。

そんな雰囲気なので作ってみた感想は目新しいメソッドも特に無いから

XmlDocumentクラスはDocument→Nodeクラスの派生です。

という説明で充分かもしれない。

TreeViewのAPIもWindowsのみたいに細かく作るよりJSON形式のデータを渡すとそれっぽく表示する程度で充分かな

ps.2025/4/22:カスタムエレメント化した。

<body>
    <label for="fileXmlDom">parseFromString(fileText)</label><input type="file" id="fileXmlDom" value="XmlDom"><br />
    <label for="fileXmlText">replaceAll(/x/g,xmlReplacer)</label><input type="file" id="fileXmlText" value="XmlText">
    <tree-view id="tree_view_sample" width="500px" height="600px" tabindex="1" resize="both" overflow="auto">
    </tree-view>
</body>

String.replaceAll({正規表現パターン、置換処理})の結果をinnerHTMLで展開

XMLParserのparseFromString({XMLテキスト})の結果(DOMツリー)を展開

いづれも長いコード書かないとツリービュー化できないでいる。

ReplaceAllの方はHTMLも読めなくもないが、末端に正しく(?)「/>」が入っていないと南京玉簾になる。

とりあえず、オブジェクトだったら配下のオブジェクトを調べる方法を画一化(childListとか)してみたい。



[javascript]コードハイライト(途中経過)

<body>
・・・
<input type="text" id="regexp_pattern" size="80" autocomplete="on" />
<input type="text" id="regexp_option" size="16" autocomplete="on" />
・・・
<input type="button" id="text_color_regexp_text" value="名前付きキャプチャグループ名で色付け" /></br>
結果:<div class="result" id="result"></div>
・・・
</body>
/**
 * サンプルコードのオブジェクトを作成
 */
const regExpSample = new class RegExpSample {
 ・・・
  /**
   * 初期化
   * @memberof RegExpSample
   */
  setup = () => {
    // イベントリスナー登録
    ・・・
    document.querySelector('#text_color_regexp_text').addEventListener("click", this.textColorFromClickButton);
  ・・・
  };
 ・・・
  /**
   * 【名前付きキャプチャグループ名で色付け】ボタンクリック処理
   * @memberof RegExpSample
   */
  textColorFromClickButton = () => {
    try {
      clearnHtmlObjectTextContent('result');
      // 結果を格納するdiv
      const divResult = document.querySelector('#result');
      // サンプル ※関数の引数の初期値に割り当てたら、画面初期表示時の空っぽだった件
      let text = document.querySelector('#regexp_text').value; //.replace('\r\n', '\n');
      // RegExpのパターンとオプションからRegExpクラスのオブジェクトを作成する
      const re = createRegexpPattern();
      // RegExpクラスのオブジェクトを渡しtextColorにお任せ
      let result = textColor(text, re);
      // 読み飛ばした箇所に背景色(黒)を付ける
      result = this.setBackColor(result);
      // textColorの結果を画面に出力
      result = result.replaceAll(/[\r\n]/g, '<br/>');
      divResult.innerHTML = result;
    } catch (ex) {
      console.error(`textColorFromClickButton : ${ex}`);
    }
  };
 ・・・
};
// 初期化
window.addEventListener("load", () => {
  regExpSample.setup();
});

ここまでは、そこそこ簡略化できてる(と思う

/**
 * 色名
 */
const colors = ['black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia', 'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua', 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure', 'beige', 'bisque', 'black', 'blanchedalmond', 'blue', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'gray', 'green', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'lime', 'limegreen', 'linen', 'magenta', 'maroon', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive', 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'rebeccapurple', 'red', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'teal', 'thistle', 'tomato', 'transparent', 'turquoise', 'violet', 'wheat', 'white', 'whitesmoke', 'yellow', 'yellowgreen',];
/**
 * textColorReplacer内で使用する初期色
 */
const defaultColor = new DefaultColor();
/**
 * テキストに色を付ける正規表現のリプレサー
 * replace関数のリプレサーのパラメータは以下の通り
 * @param {string} match   一致した部分文字列
 * @param {string} rs1     1番目のキャプチャグループの結果
 * @param {string} rs2     2番目のキャプチャグループの結果
 * ・・・
 * @param {string} rsN     N番目のキャプチャグループの結果
 * @param {integer} index  分析中の文字列の開始位置
 * @param {string} string  分析中の文字列全体
 * @param {object} groups  キャプチャグループ全体の抽出結果 {blue: undefined, red= 'false' }
 * @returns 置換テキスト
 */
const textColorReplacer = (...args) => {
  const match = args.shift(); // argsの先頭から取出す => 一致した部分文字列
  const groups = args.pop();  // argsの末尾から取出す => キャプチャグループ全体の抽出結果
  const string = args.pop();  // argsの末尾から取出す => 分析中の文字列全体
  const index = args.pop();  // argsの末尾から取出す => 分析中の文字列の開始位置
  // 残りのパラメータは、各キャプチャグループの結果(正規表現での記載順)
  const capGrpResList = args;  // キャプチャグループの結果(配列)
  /**
   * グループ名からテキストを色付け
   * @param {string} groupName 
   * @param {string} match 
   * @returns 色付けしたテキスト
   */
  const colorGroupText = (groupName, match) => {
    // グループ名を分割
    const re = groupName.split('_');
    let element_color = [], not_element_color = [], italic = undefined, underline = undefined, strikethrough = undefined, bold = undefined;
    // reを分析
    groupName = '';
    re.forEach((elm) => {
      if (elm === '') { // '' is undefined.
        element_color.push(undefined);
      } else if (isHex(elm)) { // hex is color code.
        element_color.push(`#${elm}`);
      } else if (colors.some(col => (col === elm))) {
        element_color.push(elm);
        if (element_color.length > 2) { element_color.shift(); /* 先頭を削除 */ }
      } else {
        switch (elm) {
          case 'italic': italic = true; break;
          case 'underline': underline = true; break;
          case 'strikethrough': strikethrough = true; break;
          case 'bold': bold = true; break;
          default:
            not_element_color.push(elm);
            break;
        }
      }
    });
    // 色情報抜きのグループ名からクラス名を作る
    const className = (not_element_color.length > 0) ? (not_element_color.join('_')) : element_color[0];
    // 設定内容からfcol,bcolを決定
    const fcol = (element_color.length > 0) ? (element_color[0] ? element_color[0] : defaultColor.get(match)) : defaultColor.get(match);
    const bcol = (element_color.length > 1) ? (element_color[1]) : 'burlywood';
    // fcol, bcol, italicからstyle属性を決定
    const style = `${(fcol) ? `color:${fcol}; ` : ''}${(bcol) ? `background-color:${bcol};` : ''}${(italic) ? "font-style:italic;" : ''}`;
    // pri, pro
    let pri = '', pro = '';
    // styleが有効ならspan化
    pri = `<span${(className) ? ` class="${className}"` : ''}${(style) ? ` style ="${style}"` : ''}>`; pro = '</span>';
    // 修飾名の場合、pri, proに修飾内容を追記
    if (underline) { pri += `<u>`; pro = '</u>' + pro; }
    if (strikethrough) { pri += `<s>`; pro = '</s>' + pro; }
    if (bold) { pri += `<b>`; pro = '</b>' + pro; };
    //
    return `${pri}${htmlEscape(match)}${pro}`;
  };
  // 内容が格納されているグループのみテキスト化
  const result = Object.keys(groups).reduce((o, groupName) => {
    if (groups[groupName]) { o += colorGroupText(groupName, groups[groupName]) }
    return o;
  }, '');
  return result;
}; // end of textColorReplacer.
/**
 * テキストに色を付ける
 * @param {any} text  テキスト(ハズ
 * @param {RegExp} regExp  色付け正規表現 ※未指定時は適当な正規表現を割り当てる
 * @return {string}      色付け後テキスト
 */
const textColor = (text, re = /(?<blue>true)|(?<red>false)|(?<green>\d+)/gi) => {
  text = `${text}`; // 一応テキスト化しておく
  let preTag = '', proTag = '';
  // 置換してみる
  const result = text.replace(re, textColorReplacer);
  return result;
}; // end of textColor.

colorGroupText関数正規表現をデバッグしやすくなると思った機能を入れすぎたかな



[javscript]RegExpよりリテラルの方がムズイ

あいかわらず、javascriptのソースコードをreplaceAll(/…/g, myReplacer)で色付けしているうちに

`・・・`の器用な使い方を発見

const date = () => {
    const date = new Date();
    return new Intl.DateTimeFormat("ja-JP-u-ca-japanese", {
        dateStyle: "full",        timeStyle: undefined,        timeZone: "Asia/Tokyo",
    }).format(date);
};
const time = () => {
    const date = new Date();
    return new Intl.DateTimeFormat("ja-JP-u-ca-japanese", {
        dateStyle: undefined,        timeStyle: "full",        timeZone: "Asia/Tokyo",
    }).format(date);
};
const test1 (type) => {
  let result = '';
    if(type==='date') { result = `${ date() }`;
    if(type==='time') { result = `${ time() }`};
  return result;
}
test1('date');
test1('time');

すこし粘ってみる

・・・
(type) => {
  let result = `
    ${(type==='date')? `${ date() }` : ''}
    ${(type==='time')? `${ time() }` : ''}
    `;
  return result;
}

で通るらしい。

つまり、

`...${...`.....`...}..${...`.....`...}....`

とバッククォータ・リテラルの中で${…}すると、その中で また ` …..` が作れてしまう。

これをRegExp.replaceAll(text)で字句解析するのは無理っぽいなぁ

RegExp.exec(text)で1トークンごとに読んで、(`)バッククォート来た!別のRegExpに差し替えて・・・末端までたどりついたら、元のRegExpに戻りindexを差替えて・・・かな。

spreadSheetのパーサコンビネーションみたいに、

複数のRegExpパターンでimports/exportsみたいなことをする方が無難かな

# exmport:BQLITERAL
BQLITERAL:  ( ` ( '${'  |  '}'  | '.*' )*  ` )
# exmport:.
# main
COMMENT:    (/{2}.*$|\/\*\/?([^/]|[^*]\/|\r|\n)*\*\/)
・・・
BQLITERAL:  (`) # import(BQLITERAL)
 👈多分この部分は RegExp.exec()の戻り値のgroupsにBQLITERALがあったら、サブルーチン呼び出しみたいな感じかな
# .

それもつらいなぁ

( ( ?<= $ { ] ) | ( ・・・ ) | (?= } ) )

で逃げ切れるかな?

とりあえず今はこの辺

ps.すこし粘ってみた

((\$\{)([^\}]*)(\}))

この${…}パターンを

BQLITERALパターンに

BQLITERAL:  (`(\\\\|\\`|[^`]|\r|\n)*`)

組み込んで

(new)BQLITERAL:  (`(((\${)([^\}]*)(\}))|(\\\\|\\`|[^`]|\r|\n))*`)

みた。

試してみる

---`aaa${222`333444555`666}aaaa$bbbb${222`333444555`666}`---------
---`aaa${222`333${222`333444555`666}444555`666}aaaa$bbbb${222`333444555`666}`---------

サンプル1では

・・・
[BQLITERAL] : '`aaa${222`'
[SYMBOLE] : '333444555'
[BQLITERAL] : '`666}aaaa$bbbb${222`'
[SYMBOLE] : '333444555'
[BQLITERAL] : '`666}`'
・・・
[BQLITERAL] : '`aaa${222`'
[SYMBOLE] : '333$'
[L_BRACE] : '{'
[SYMBOLE] : '222'
[BQLITERAL] : '`333444555`'
[SYMBOLE] : '666'
[R_BRACE] : '}'
[SYMBOLE] : '444555'
[BQLITERAL] : '`666}aaaa$bbbb${222`'
[SYMBOLE] : '333444555'
[BQLITERAL] : '`666}`'
・・・

から

・・・
[BQLITERAL] : '`aaa${222`333444555`666}aaaa$bbbb${222`333444555`666}`'
・・・
[BQLITERAL] : '`aaa${222`333${222`333444555`666}444555`'
・・・

と多少改善はできてる。

けど、サンプル2では

---`aaa${222`333444555`666}aaaa$bbbb${222`333444555`666}`---------
---`aaa${222`333${222`333${222`333444555`666}444555`666}444555`666}aaaa$bbbb${222`333444555`666}`---------
・・・
[BQLITERAL] : '`aaa${222`333444555`666}aaaa$bbbb${222`333444555`666}`'
・・・
[BQLITERAL] : '`aaa${222`333${222`333${222`333444555`666}444555`'
・・・
[BQLITERAL] : '`666}aaaa$bbbb${222`333444555`666}`'
・・・

つまり${…} のネストは3段目でかなり厳しい。

`...${...`...`...}...${...`...`...}...`
や
`...${...`...${...`...`...}...`...}...${...`...`...}...` ※旧よりはマシな結果
`...${...`...${...`...${...`...`...}...`...}...`...}...${...`...`...}...`

色付けで確認してみると

「BQLITERAL_white_black」と色指定の名前に変えて

【↓組み合わせ】ボタンで正規表現を更新して

【色付け】ボタンを押すと・・・

多少は、マシってだけ。



[html]Quirks Mode

DevToolsに

Page layout may be unexpected due to Quirks Mode

ちゃんと

<DOCTYPE html>

あるけど・・・

<!DOCTYPE html>

が正解だった。

あれ?正解だと目立たない配色に

で灰色一色だったコードを貼ってみる。

って参照パーツの後のマージンが本体サイズ。

WP6.8ってBUGが多すぎな気がする。


/**
 * JSON.stringifyのreplacer
 * @param {*} key 
 * @param {*} value 
 */
const stringifyReplacer = (key, value) => {
  if (key !== '' && !value) {
    value = 'undefined';
  }
  return value;
};
/**
 * オブジェクトのプロパティを列挙
 * @param {object} object オブジェクト
 */
const objectPropertyStringify = (object) => {
  let ar = {};
  // for(..in object)
  for (const id in object) { ar[id] = object[id]; }
  // Object.keys(object)
  const keys = Object.keys(object);
  keys.forEach((id) => { ar[id] = JSON.stringify(object[id], stringifyReplacer, 0); });
  // Object.getOwnPropertyNames(object)
  const props = Object.getOwnPropertyNames(object);
  props.forEach((id) => { ar[id] = JSON.stringify(object[id], stringifyReplacer, 0); });
  // Object.getOwnPropertyNames(obj.__proto__)
  const prototypeProps = Object.getOwnPropertyNames(object.__proto__);
  prototypeProps.forEach((id) => { ar[id] = JSON.stringify(object[id], stringifyReplacer, 0); });
  // 纏め
  const txts = [];
  for (const id in ar) {
    // typeof value !== 'function' で メソッドを排除
    if (typeof object[id] !== 'function') {
      txts.push(`${id}:${textColor(ar[id])}`);
    }
  }
  //
  return `<ul><li>${txts.join('</li><li>')}</li></ul>`.replace('<li></li>', '');
};

チャント配色(コードハイライト)してくれてる!

何かバグってたのか。

JSだけでもちゃんと字句解析できたら自作でコードハイライトさせてみるか?

ちゃんと字句解析できたら???

ps.2025/4/18:とりあえずJavaScriptだけはString.replaceAll(RegExp,replacer)で字句解析できる範囲でコードハイライトが出来そう正規表現ができたみたいだから、WordPressのプラグインの作り方ってどうなってるのかな? 出来はまだまだだった


  • カテゴリー:
  • HTML

[WordPress]6.8の現状

まだまだ未テスト状態のプラグインだらけ

それでも動くなら問題ないけど

残念な結果になったプラグインは・・・

  1. Lite Syntax Highlighting
    • 【有効化】したら
      • 「重大な何たらがエラー発生」してページが真っ白
        • 削除済(合掌
  2. My WP Customize
    • 更新が来たので【更新】すると終わらない
    • 諦めて【削除】すると終わらない
    • pluginsフォルダからmy-wpフォルダを削除(合掌

記事の読み書きに支障はないけど

クイック編集の「保存」すると終わらない

データベースプラグイン画面のテーブル一覧がなかなか表示しない(遅

以前は/etc/hostsに自分のサーバー名を書いて無かったせいで自分へのセルフループが失敗してたが、今回は設定済。

WordPress関連のファイルを一式別の仮想ディスクにコピーって稼働させたら、設定の保存で書込み失敗するプラグインがでた。ファイルの所有者がrootだったせいでapacheに変更。




top