Rumah > hujung hadapan web > tutorial js > Bina Bahasa Serasi JavaScript Anda Sendiri: Menguasai Reka Bentuk Pengkompil

Bina Bahasa Serasi JavaScript Anda Sendiri: Menguasai Reka Bentuk Pengkompil

DDD
Lepaskan: 2024-11-24 10:24:15
asal
819 orang telah melayarinya

Build Your Own JavaScript-Compatible Language: Mastering Compiler Design

Mencipta bahasa pengaturcaraan anda sendiri yang menyusun kepada JavaScript adalah satu perjalanan yang menarik. Ia adalah projek yang akan meningkatkan kemahiran anda ke had dan memberi anda pemahaman yang lebih mendalam tentang cara bahasa berfungsi di bawah hud.

Mari kita mulakan dengan perkara asas. Pengkompil untuk bahasa tersuai kepada JavaScript biasanya melibatkan tiga peringkat utama: analisis leksikal, penghuraian dan penjanaan kod.

Analisis leksikal ialah langkah pertama. Di sini, kami memecahkan kod sumber kami kepada token. Ini adalah unit makna terkecil dalam bahasa kita. Contohnya, dalam pernyataan "biar x = 5;", kita akan mempunyai token untuk "biar", "x", "=", "5", dan ";".

Berikut ialah lexer mudah dalam JavaScript:

function lexer(input) {
    let tokens = [];
    let current = 0;

    while (current < input.length) {
        let char = input[current];

        if (char === '=' || char === ';') {
            tokens.push({ type: 'operator', value: char });
            current++;
            continue;
        }

        if (/\s/.test(char)) {
            current++;
            continue;
        }

        if (/[a-z]/i.test(char)) {
            let value = '';
            while (/[a-z]/i.test(char)) {
                value += char;
                char = input[++current];
            }
            tokens.push({ type: 'identifier', value });
            continue;
        }

        if (/\d/.test(char)) {
            let value = '';
            while (/\d/.test(char)) {
                value += char;
                char = input[++current];
            }
            tokens.push({ type: 'number', value });
            continue;
        }

        throw new Error('Unknown character: ' + char);
    }

    return tokens;
}
Salin selepas log masuk
Salin selepas log masuk

Lexer ini boleh mengendalikan tugasan mudah seperti "biar x = 5;". Ia asas, tetapi ia memberi anda idea tentang cara analisis leksikal berfungsi.

Seterusnya ialah penghuraian. Di sinilah kami mengambil aliran token kami dan membina Pokok Sintaks Abstrak (AST). AST mewakili struktur program kami.

Berikut ialah penghurai mudah untuk bahasa kita:

function parser(tokens) {
    let current = 0;

    function walk() {
        let token = tokens[current];

        if (token.type === 'identifier' && token.value === 'let') {
            let node = {
                type: 'VariableDeclaration',
                name: tokens[++current].value,
                value: null
            };

            current += 2; // Skip the '='
            node.value = walk();

            return node;
        }

        if (token.type === 'number') {
            current++;
            return { type: 'NumberLiteral', value: token.value };
        }

        throw new TypeError(token.type);
    }

    let ast = {
        type: 'Program',
        body: []
    };

    while (current < tokens.length) {
        ast.body.push(walk());
    }

    return ast;
}
Salin selepas log masuk

Penghuraikan ini boleh mengendalikan pengisytiharan pembolehubah mudah. Ia tidak begitu mantap, tetapi ia menggambarkan konsepnya.

Langkah terakhir ialah penjanaan kod. Di sinilah kami mengambil AST kami dan mengubahnya menjadi kod JavaScript. Berikut ialah penjana kod ringkas:

function codeGenerator(node) {
    switch (node.type) {
        case 'Program':
            return node.body.map(codeGenerator).join('\n');

        case 'VariableDeclaration':
            return 'let ' + node.name + ' = ' + codeGenerator(node.value) + ';';

        case 'NumberLiteral':
            return node.value;

        default:
            throw new TypeError(node.type);
    }
}
Salin selepas log masuk

Kini kita boleh menyusun semuanya:

function compile(input) {
    let tokens = lexer(input);
    let ast = parser(tokens);
    let output = codeGenerator(ast);
    return output;
}

console.log(compile('let x = 5;'));
// Outputs: let x = 5;
Salin selepas log masuk

Ini hanya menconteng permukaan. Pengkompil bahasa sebenar perlu mengendalikan lebih banyak lagi: fungsi, struktur kawalan, pengendali dan sebagainya. Tetapi ini memberi anda rasa tentang perkara yang terlibat.

Sambil kami mengembangkan bahasa kami, kami perlu menambahkan lebih banyak jenis token pada lexer kami, lebih banyak jenis nod pada penghurai kami dan lebih banyak kes pada penjana kod kami. Kami juga mungkin ingin menambah peringkat perwakilan pertengahan (IR) antara penghuraian dan penjanaan kod, yang boleh memudahkan anda melakukan pengoptimuman.

Mari tambah sokongan untuk ungkapan aritmetik mudah:

// Add to lexer
if (char === '+' || char === '-' || char === '*' || char === '/') {
    tokens.push({ type: 'operator', value: char });
    current++;
    continue;
}

// Add to parser
if (token.type === 'number' || token.type === 'identifier') {
    let node = { type: token.type, value: token.value };
    current++;

    if (tokens[current] && tokens[current].type === 'operator') {
        node = {
            type: 'BinaryExpression',
            operator: tokens[current].value,
            left: node,
            right: walk()
        };
        current++;
    }

    return node;
}

// Add to code generator
case 'BinaryExpression':
    return codeGenerator(node.left) + ' ' + node.operator + ' ' + codeGenerator(node.right);

case 'identifier':
    return node.value;
Salin selepas log masuk

Kini pengkompil kami boleh mengendalikan ungkapan seperti "biar x = 5 3;".

Sambil kami terus membina bahasa kami, kami akan menghadapi cabaran yang menarik. Bagaimanakah kita mengendalikan keutamaan pengendali? Bagaimanakah kita melaksanakan struktur kawalan seperti pernyataan if dan gelung? Bagaimanakah kita menangani fungsi dan skop pembolehubah?

Soalan ini membawa kita ke topik yang lebih lanjut. Kami mungkin melaksanakan jadual simbol untuk menjejaki pembolehubah dan skopnya. Kami boleh menambah pemeriksaan jenis untuk menangkap ralat sebelum masa jalan. Malah kami mungkin melaksanakan persekitaran masa jalan kami sendiri.

Satu bidang yang sangat menarik ialah pengoptimuman. Sebaik sahaja kami mempunyai AST kami, kami boleh menganalisis dan mengubahnya untuk menjadikan kod yang terhasil lebih cekap. Sebagai contoh, kita boleh melaksanakan lipatan berterusan, di mana kita menilai ungkapan malar pada masa penyusunan:

function lexer(input) {
    let tokens = [];
    let current = 0;

    while (current < input.length) {
        let char = input[current];

        if (char === '=' || char === ';') {
            tokens.push({ type: 'operator', value: char });
            current++;
            continue;
        }

        if (/\s/.test(char)) {
            current++;
            continue;
        }

        if (/[a-z]/i.test(char)) {
            let value = '';
            while (/[a-z]/i.test(char)) {
                value += char;
                char = input[++current];
            }
            tokens.push({ type: 'identifier', value });
            continue;
        }

        if (/\d/.test(char)) {
            let value = '';
            while (/\d/.test(char)) {
                value += char;
                char = input[++current];
            }
            tokens.push({ type: 'number', value });
            continue;
        }

        throw new Error('Unknown character: ' + char);
    }

    return tokens;
}
Salin selepas log masuk
Salin selepas log masuk

Kita boleh memanggil fungsi ini pada setiap nod semasa fasa penjanaan kod.

Satu lagi topik lanjutan ialah penjanaan peta sumber. Peta sumber membenarkan penyahpepijat memetakan antara JavaScript yang dijana dan kod sumber asal kami, menjadikan penyahpepijatan lebih mudah.

Apabila kami mendalami reka bentuk bahasa, kami mula menghargai nuansa dan pertukaran yang terlibat. Patutkah bahasa kita ditaip dengan kuat atau ditaip secara dinamik? Bagaimanakah kita mengimbangi ekspresif dengan keselamatan? Apakah sintaks yang akan menjadikan bahasa kita intuitif dan mudah digunakan?

Membina bahasa yang menyusun kepada JavaScript juga memberi kita perspektif unik tentang JavaScript itu sendiri. Kami mula melihat sebab keputusan reka bentuk tertentu dibuat dan kami mendapat penghargaan yang lebih mendalam untuk ciri dan ciri bahasa itu.

Selain itu, projek ini dapat meningkatkan pemahaman kita tentang bahasa dan alatan lain dengan ketara. Banyak konsep yang kami temui - skop leksikal, sistem jenis, pengumpulan sampah - adalah asas kepada reka bentuk dan pelaksanaan bahasa pengaturcaraan.

Perlu diambil perhatian bahawa semasa kami menyusun ke JavaScript, banyak prinsip ini juga digunakan pada bahasa sasaran yang lain. Setelah anda memahami asasnya, anda boleh menyesuaikan pengkompil anda untuk mengeluarkan kod Python, Java atau pun mesin.

Semasa kami mengakhiri, jelas bahawa membina transpiler bahasa bukanlah tugas yang kecil. Ia adalah projek yang boleh berkembang bersama anda, sentiasa menawarkan cabaran baharu dan peluang pembelajaran. Sama ada anda ingin mencipta bahasa khusus domain untuk masalah tertentu, atau anda hanya ingin tahu tentang cara bahasa berfungsi, projek ini merupakan cara terbaik untuk mendalami pengetahuan pengaturcaraan anda.

Ingat, matlamatnya tidak semestinya untuk mencipta bahasa pengaturcaraan besar seterusnya. Nilai sebenar adalah dalam perjalanan - pemahaman yang anda peroleh, masalah yang anda selesaikan, dan cara pemikiran baharu yang anda kembangkan. Oleh itu, jangan takut untuk bereksperimen, membuat kesilapan, dan menolak sempadan apa yang anda fikir mungkin. Selamat mengekod!


Ciptaan Kami

Pastikan anda melihat ciptaan kami:

Pusat Pelabur | Hidup Pintar | Epos & Gema | Misteri Membingungkan | Hindutva | Pembangunan Elit | Sekolah JS


Kami berada di Medium

Tech Koala Insights | Dunia Epok & Gema | Medium Pusat Pelabur | Medium Misteri Membingungkan | Sains & Zaman Sederhana | Hindutva Moden

Atas ialah kandungan terperinci Bina Bahasa Serasi JavaScript Anda Sendiri: Menguasai Reka Bentuk Pengkompil. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan