미니 프로그래밍 언어를 만들어 봅시다!

이 자습서에서는 미니 프로그래밍 언어를 만듭니다. 컴파일

center,
left 100,
color blue,
top 10,
right 100,


에게

_center();
_draw();
_left(100);
_draw();
_setColor("blue");
_draw();
_top(10);
_draw();
_right(100);
_draw();


HTMLcanvas에서 흥미로운 내용을 그리는 데 사용할 것입니다.



그럼 시작하겠습니다!

기본적인 것들



코드를 작성하기 전에 논리를 알아야 합니다.

렉싱



먼저 코드를 분석합니다. 코드를 토큰으로 분할하고 불필요한 문자를 제거하는 과정입니다.



확인 중



그런 다음 토큰이 유효한지 확인하여 코드의 유효성을 검사합니다.

컴파일



그런 다음 코드를 JavaScript로 컴파일합니다.

프로그래밍 부분



미니 언어의 이름을 지정합니다drawlang . 구문은 다음과 같습니다.
  • center , left , top , right , bottom , color 는 키워드입니다.
  • , 역시 그리는 키워드입니다.
  • red , blue , green , black , white , ... 등과 같은 색상은 색상입니다.
  • 모든 숫자는 숫자입니다.

  • 기본 사항 설정



    먼저 index.js 파일을 생성하고 이 코드를 추가하겠습니다.

    const fs = require("fs"); // File system
    
    const code = `
    center,
    left 100,
    color blue,
    top 10,
    right 100,
    `; // Sample code to test during the tutorial
    


    파일을 읽기 위해 fs 모듈을 가져옵니다.

    어휘분석기 만들기


    tokenize라는 이름의 함수를 만들고 문자열을 인수로 받습니다.

    function tokenize(code) // ...
    


    이제 변수를 유지하여 토큰을 저장하고 현재 위치를 추적하십시오.

    const tokens = [];
    let i = 0;
    


    그리고 tokens 배열에 토큰을 추가하는 유틸리티 함수입니다.

    function addToken(type, value) {
        tokens.push({
            type,
            value,
        });
    }
    


    이제 코드 토큰화를 시작할 수 있습니다. while 루프를 사용하여 코드를 반복합니다.

    while (i < code.length) // ...
    


    그런 다음 현재 문자를 얻습니다.

    const char = code[i];
    


    우리는 토큰의 유형을 결정하기 위해 switch 문을 사용할 것입니다.

    switch (
        char
        // ...
    ) {
    }
    


    우리는 모든 공백 문자를 무시합니다.

    case " ":
    case "\t":
    case "\n":
    case "\r":
        i++;
        break;
    


    쉼표인 경우 COMMA 토큰을 추가합니다.

    case ",":
        addToken("COMMA", char);
        i++;
        break;
    


    그렇지 않으면 숫자인지 키워드인지 확인합니다.

    default:
        const isDigit = /\d/.test(char); // Returns true if it's a digit
        const isLetter = /[a-z]/i.test(char); // Returns true if it's a letter
    
        if (isDigit) {
            let number = ""; // Stores the number
            while (i < code.length && /\d/.test(code[i])) { // While the current character is a digit
                number += code[i];
                i++;
            }
            addToken("NUMBER", number); // Finally, we add the token
        } else if (isLetter) {
            let name = ""; // Stores the name
            while (i < code.length && /[a-z]/i.test(code[i])) { // While the current character is a letter
                name += code[i];
                i++;
            }
            addToken("NAME", name); // Finally, we add the token
        } else {
            throw new Error(`Unknown character: ${char}`); // 🤬 Error
        }
        break;
    


    그리고 우리는 마침내 while 루프를 탈출하고 토큰을 반환합니다.

    return tokens;
    


    이제 이 코드를 추가하고 실행node index.js하고 출력을 확인하십시오.

    const tokens = tokenize(code);
    console.log(tokens);
    




    멋진! 우리는 렉서/토큰나이저를 만들었습니다.

    유효성 검사 및 JavaScript로 컴파일



    토큰 배열을 인수로 받아들이는 compile라는 함수를 생성합니다.

    function compile(tokens) {
        // ...
    }
    


    그런 다음 몇 가지 변수를 추가하여 컴파일된 코드를 저장하고 현재 위치를 추적합니다.

    let i = 0;
    let out = "";
    
    let addCode = (code) => {
        out += code + "\n";
    };
    


    다시, 우리는 토큰을 반복하기 위해 while 루프를 사용합니다.

    while (i < tokens.length) {
        // ...
        i++;
    }
    


    이번에는 현재 토큰을 가져오는 함수를 만듭니다.

    const token = () => tokens[i];
    


    그리고 expect라는 이름의 함수는 다음 토큰이 예상되는 토큰인지 확인하고 그렇지 않은 경우 오류를 발생시킵니다.

    function expect(type) {
        if (tokens[++i].type !== type) {
            throw new Error(`Expected ${type}, got ${tokens[i].type}`);
        }
    }
    


    그런 다음 switch 문을 사용하여 토큰 유형을 결정합니다.

    switch (token().type) // ...
    

    COMMA 이면 컴파일된 코드에 _draw() 를 추가합니다.

    case "COMMA":
        addCode("_draw()");
        break;
    


    그렇지 않으면 NAME 토큰인지 확인합니다.

    case "NAME":
    /**
     * If the name is center, execute the center function
     */
    if (token().value === "center") {
        addCode(`_center();`);
    } else if (token().value === "color") {
        /**
         * If the name is color, expect a name and set the color
         */
        expect("NAME");
        addCode(`_setColor("${token().value}");`);
    } else if (
        /**
         * If the name is left/right/top/bottom, expect a number and execute the corresponding function
         */
        token().value === "left" ||
        token().value === "right" ||
        token().value === "top" ||
        token().value === "bottom"
    ) {
        expect("NUMBER");
        const value = parseInt(token().value);
    
        addCode(`_${tokens[i - 1].value}(${value});`); // We get the token before the current one and use it to determine the function to call
    } else {
        throw new Error(`Unknown name: ${token().value}`); // 🤬 Error
    }
    break;
    


    마침내 우리는 while 루프를 탈출하고 컴파일된 코드를 반환합니다.

    return out;
    


    이제 이 코드를 추가하고 실행node index.js하고 출력을 확인하십시오.

    const tokens = tokenize(code);
    const compiled = compile(tokens);
    console.log(compiled);
    


    올바르게 수행했다면 다음과 같은 결과가 표시됩니다.



    브라우저에서 실행할 수 있도록 out.js라는 파일에 코드를 작성합니다.

    const tokens = tokenize(code);
    const compiled = compile(tokens);
    
    console.log("Compiled code successfully!");
    fs.writeFileSync("out.js", compiled);
    


    멋진! 이제 프레임워크에 대한 코드를 포함할 framework.js 파일을 생성합니다.

    const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
    
    canvas.width = 900;
    canvas.height = 900;
    
    let x, y, lastX, lastY, color;
    x = y = lastX = lastY = 0;
    color = "transparent";
    
    function setX(newX) {
        lastX = x;
        x = newX;
    }
    
    function setY(newY) {
        lastY = y;
        y = newY;
    }
    
    function _center() {
        setX(canvas.width / 2);
        setY(canvas.height / 2);
    }
    
    ctx.beginPath();
    
    function _draw() {
        ctx.moveTo(lastX, lastY);
        ctx.lineTo(x, y);
        ctx.strokeStyle = color;
        ctx.stroke();
    }
    
    function _left(distance) {
        setX(x - distance);
    }
    
    function _right(distance) {
        setX(x + distance);
    }
    
    function _top(distance) {
        setY(y - distance);
    }
    
    function _bottom(distance) {
        setY(y + distance);
    }
    
    function _setColor(newColor) {
        color = newColor;
    }
    

    HTML 파일에서 캔버스와 스크립트 태그를 추가합니다.

    <!DOCTYPE html>
    <html lang="en">
        <head>
            <meta charset="UTF-8" />
            <meta http-equiv="X-UA-Compatible" content="IE=edge" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <title>Document</title>
        </head>
    
        <body>
            <canvas id="canvas"></canvas>
    
            <script src="framework.js"></script>
            <script src="out.js"></script>
        </body>
    </html>
    


    이제 node index.js를 실행하고 브라우저에서 index.html 파일을 엽니다.



    이 코드는 이것을 보여줍니다

    center,
    left 100,
    color blue,
    top 10,
    bottom 100,
    right 100,
    top 200,
    right 100,
    






    코드가 필요한 경우 - here
    날 따라와
    팔로우Github

    좋은 웹페이지 즐겨찾기