«Разве вы не видите, что вся цель новояза состоит в том, чтобы сузить круг мыслей? В конце концов мы сделаем мыслепреступление буквально невозможным, потому что не будет слов, чтобы выразить его». — «1984», Джордж Оруэлл
Предположим, вы хотите иметь возможность использовать ключевое слово fn вместо function в JavaScript, аналогично тому, как это работает в Rust. Как сделать это возможным?
Итак, начнем с желудя.
Крошечный, быстрый анализатор JavaScript, полностью написанный на JavaScript.
Инструменты, которые вы используете каждый день, такие как ESLint или Babel, основаны на Acorn. Это быстрый и простой для вас парсер ESTree.
Его можно использовать следующим образом:
const {Parser} = require("acorn") const MyParser = Parser.extend( require("acorn-jsx")(), require("acorn-bigint") ) console.log(MyParser.parse("// Some bigint + JSX code"))
И даже больше! Его можно расширить:
module.exports = function noisyReadToken(Parser) { return class extends Parser { readToken(code) { console.log("Reading a token!") super.readToken(code) } } }
И если вы хотите сделать возможным использование такого кода:
fn hello() { return 'world'; }
И разобрать его на
function hello() { return 'world'; }
Не осторожно! Это намного проще, чем вы думаете. Все, что нужно сделать, это: расширить анализатор Acorn.
Как расширить парсер желудей?
Ну, есть пара вещей, которые вам нужно знать, прежде чем мы начнем.
Все зарезервированные слова, которые мы используем в JavaScript, такие как: if, else, function и т. д., расположены в объекте Parser.acorn.keywordTypes, поэтому, если вы хотите добавить новое ключевое слово, вам нужно создать новый токен:
const {Parser, TokenType} = require('acorn'); function newSpeak(Parser) { Parser.acorn.keywordTypes['fn'] = new TokenType('fn', { keyword: 'fn', }); } const {parse} = Parser.extend( newSpeak, ); console.log(parse(` fn hello() { return 'world'; } `)); // here you will see the AST
newSpeek — это название нашего воображаемого языка.
Далее следует добавить ключевое слово fn в список ключевых слов, о которых знает Acorn:
function newSpeak(Parser) { Parser.acorn.keywordTypes['fn'] = new TokenType('fn', { keyword: 'fn', }); return class extends Parser { parse() { this.keywords = addKeyword('fn', this.keywords); return super.parse(); } } } function addKeyword(keyword, keywords) { const str = keywords .toString() .replace(')$', `|${keyword})$`) .slice(1, -1); return RegExp(str); }
keywords – этоодно большое регулярное выражение, вот как оно выглядит:
/^(?:break|case|catch|continue|debugger|default|do|else|finally|for|function|if|return|switch|throw|try|var|while|with|null|true|false|instanceof|typeof|void|delete|new|in|this|const|class|extends|export|import|super)$/
Так что да, нам нужно добавить наше ключевое слово fn
в этот список, чтобы все заработало.
После этого нам нужно переопределить parseStatement
и сказать acorn, что это всего лишь fn
это всего лишь function
.
function newSpeak(Parser) { Parser.acorn.keywordTypes['fn'] = new TokenType('fn', { keyword: 'fn', }); return class extends Parser { parse() { this.keywords = addKeyword('fn', this.keywords); return super.parse(); } parseStatement(context, topLevel, exports) { if (this.type == Parser.acorn.keywordTypes['fn']) { this.type = Parser.acorn.keywordTypes['function']; } return super.parseStatement(context, topLevel, exports); } } }
Вот и все! Вот весь код:
const {TokenType, Parser} = require('acorn'); function newSpeak(Parser) { Parser.acorn.keywordTypes['fn'] = new TokenType('fn', { keyword: 'fn', }); return class extends Parser { parse() { this.keywords = addKeyword('fn', this.keywords); return super.parse(); } parseStatement(context, topLevel, exports) { if (this.type == Parser.acorn.keywordTypes['fn']) { this.type = Parser.acorn.keywordTypes['function']; } return super.parseStatement(context, topLevel, exports); } } } function addKeyword(keyword, keywords) { const str = keywords .toString() .replace(')$', `|${keyword})$`) .slice(1, -1); return RegExp(str); } const MyParser = Parser.extend( newSpeak, ); console.log(MyParser.parse(` fn hello() { return 'world'; } `));
Теперь вы можете добавить любое ключевое слово, чтобы сделать язык своей мечты :)!