shiki-01
@shiki-01 (shiki)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

HTMLの拡張言語作成について

初めて質問するので、わかりにくい点があったらすみません。

やりたいこと

JavaScriptで、コンパイラもどきを作っています。
具体的な機能としては、HTMLの拡張言語的なもので、bodyとかdiv本文コンテナというような、タグを日本語で書くというものです。この文法を、実際にHTMLに直せるようなコンパイラを作成したいです。

現状

正直に話すと、一応できました。
ただ、一介の高校生が作ったようなプログラムでは不安があったり、もっと良い方法があるのでは?と思ったので、皆さんならどう作るかなと思い質問させていただきました。

以下は、現在私が作成したプログラムです。

JavaScript
function compile(input) {

    const tagMap = {
        '見出し': 'h1',
        '強調': 'strong'
    };
    const attrMap = {
        'クラス': 'class',
        'ソース': 'src'
    };

    let output = input.replace(/<\/?([^>]+)>/g, function (match, tag) {
        let isClosingTag = match.startsWith('</');
        let [tagName, ...attrs] = tag.split(' ');

        tagName = tagMap[tagName] || tagName;

        attrs = attrs.map(attr => {
            let [attrName, attrValue] = attr.split('=');
            attrName = attrMap[attrName] || attrName;
            return `${attrName}=${attrValue}`;
        });
        return `<${isClosingTag ? '/' : ''}${[tagName, ...attrs].join(' ')}>`;
    });
    return output;
}

const input = `<見出し クラス="main">これは見出し<強調>ここは強調</強調></見出し>`;
const result = compile(input);

//result = <h1 class="main">これは見出し<strong>ここは強調</strong></h1>

オブジェクトに、日本語タグと、それに対応する英語(?)タグを入れておくことで、字句解析をした後、対応する部分があれば変換することで、HTMLを出力すると言った具合です。

もし何か質問がありましたら答えます。皆さんならどうするか、こうした方がいいかもとかありましたら、ぜひご教授いただきたいです。よろしくお願いします。

1

コンパイラの結果を実際のHTMLとして表示する処理を作ってみるのはどうですか?

私が作ってみましたが、参考になると幸いです。

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
</body>
<script>
    const body = document.getElementsByTagName("body")[0] ;

    function compile(input, node) { // Tagを入れるNode追加

        try {

            if( !node.innerHTML ) throw new Error('Not Found innerHTML Property') ; // Tagの中でinnerHTMLがないと例外発生させる

            const tagMap = {
                '見出し': 'h1',
                '強調': 'strong'
            };
            const attrMap = {
                'クラス': 'class',
                'ソース': 'src'
            };

            let output = input.replace(/<\/?([^>]+)>/g, function (match, tag) {
                let isClosingTag = match.startsWith('</') ;
                let [ tagName, ...attrs ] = tag.split(' ') ; 

                tagName = tagMap[tagName] || tagName;

                attrs = attrs.map(attr => {
                    let [attrName, attrValue] = attr.split('=') ;
                    attrName = attrMap[attrName] || attrName ;
                    return `${attrName}=${attrValue}` ;
                });
                
                return `<${isClosingTag ? '/' : ''}${[tagName, ...attrs].join(' ')}>`;

            });

            node.innerHTML = output ; // outputを実際のTagとして表示する。
            
        } catch (error) {
            
            console.log(error.message) ;

        }
    }

    const input = `<見出し クラス="main">これは見出し<強調>ここは強調</強調></見出し>`;
    
    compile(input, body);

</script>
</html>

こうしたら、入力したテキストが実際のHTMLのTagとして表示されます。

3Like

@heesukim998
確かにその発想はなかったです…!

私が必要ないかなと思って書いてなかったのですが、一応このプログラムはVScodeの拡張機能として作っています。
なので、本来はコマンドとしてこのプログラムを呼び出す予定だったのですが、もしかしたらテンプレートとしてこのようなものを呼び出すってのもありかもしれませんね!

回答ありがとうございました!

1Like

今回の要件を満たすのには、正規表現で置換するのは自分は良いアイデアだと思いました。

プログラムに不安があるということなのであればテストを書いてみるのはいかがでしょうか?
いくつか気になった部分を書かせて頂きました。
重箱の隅を突くようなケースも多いので、全ては直さずに自分が目指したい要件を満たしたらそこで一旦リリースみたいな形をとってもいいのかなと思いました。
構文エラーの際はどんな挙動をするのか、みたいな細かいことも気になりました。

function test(input, expected) {
    const result = compile(input);
    console.log(input)
    console.log(result)
    console.log(expected)
    console.log(`結果 ${result === expected ? '成功' : '失敗'}`)
}

test(
    `<見出し クラス="main">これは見出し<強調>ここは強調</強調></見出し>`,
    `<h1 class="main">これは見出し<strong>ここは強調</strong></h1>`,
)
test(
    `<見出し     クラス="main">これは見出し<強調>ここは強調</強調></見出し>`,
    `<h1 class="main">これは見出し<strong>ここは強調</strong></h1>`,
)
test(
    `<見出し クラス="main test">これは見出し<強調>ここは強調</強調></見出し>`,
    `<h1 class="main test">これは見出し<strong>ここは強調</strong></h1>`,
)

test(
    `<見出し クラス="main" ソース="sub">これは見出し<強調>ここは強調</強調></見出し>`,
    `<h1 class="main" src="sub">これは見出し<strong>ここは強調</strong></h1>`,
)
test(
    `<見出し クラス="main>" ソース="sub">これは見出し<強調>ここは強調</強調></見出し>`,
    `<h1 class="main>" src="sub">これは見出し<strong>ここは強調</strong></h1>`,
)
1Like

@Ryo_Suzuki

こういうテストのサンプルは作ってる側としてはあまり思いつかなかったりする部分も多いので本当に感謝です!

正規表現で一個でもずれたらはじいちゃったほうがいいかなとも思ったのですが、こういう所謂「重箱の隅をつつく」タイプのやつは確かにどうにかしたほうがいい問題ですね…
現段階ではすぐにリリースではなく、身内に体験してもらって、エラーなりシステムの改善なりはする予定なので、作ってく中でテストしながらやっていきたいと思います!

ありがとうございました!

1Like

「もっと良い方法」ということであれば、パーサージェネレーターを使ってパーサーを作ってしまうというやり方もあります。

パーサージェネレーターとして一般的なのはYaccやBisonやTree-sitterですが、PEG.jsLezerなどJavaScript向けに作れるものもあります。バグの少ない字句解析や構文解析プログラムを作れるため、今後もし高度な構文を加えるのであれば検討する価値はあると考えます。

ただし「要素名や属性名を置換する」だけの機能で良いならば、これらを使うのはあまりにも大げさなのも事実です。今の時点であれば、正規表現による置換だけで十分と思います。

1Like

Your answer might help someone💌