미니 프로그래밍 언어를 만들어 봅시다!
31316 단어 javascriptcompilersprogramming
center,
left 100,
color blue,
top 10,
right 100,
에게
_center();
_draw();
_left(100);
_draw();
_setColor("blue");
_draw();
_top(10);
_draw();
_right(100);
_draw();
HTML
canvas
에서 흥미로운 내용을 그리는 데 사용할 것입니다.그럼 시작하겠습니다!
기본적인 것들
코드를 작성하기 전에 논리를 알아야 합니다.
렉싱
먼저 코드를 분석합니다. 코드를 토큰으로 분할하고 불필요한 문자를 제거하는 과정입니다.
확인 중
그런 다음 토큰이 유효한지 확인하여 코드의 유효성을 검사합니다.
컴파일
그런 다음 코드를 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
Reference
이 문제에 관하여(미니 프로그래밍 언어를 만들어 봅시다!), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/posandu/lets-create-a-mini-programming-language-2426텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)