llvm.js
llvm.js

Введение

В данной статье мы рассмотрим мощный проект - llvm.js и расскажем, что он представляет из себя. Также мы научимся создавать компилируемый язык программирования на основе JavaScript. Хотя в этой статье мы сфокусируемся именно на JavaScript, ознакомившись с процессом, вы сможете создавать свои собственные компилируемые языки программирования с использованием llvm.js.

Назначение и применение

Назначение и применение данного проекта не подлежат сомнению – он служит настоящим мощным инструментом для создания трансляторов, интерпретаторов и компиляторов. Если вы хотите разработать свой собственный язык программирования или преобразовать имеющийся язык в компилируемый формат, то этот проект, безусловно, является вашим незаменимым союзником.

Не утруждайтесь тратить ценное время на создание базового функционала с нуля – ведь с нашим инструментом вы уже обладаете им! Он предоставляет вам удобные и готовые решения для реализации базовых компонентов, экономя вам значительное количество времени и усилий.

Важно отметить, что в рамках этого проекта содержатся несколько компонентов, каждый из которых имеет четко определенную задачу. Вы сможете использовать эти компоненты самостоятельно или в комбинации друг с другом, чтобы достичь желаемого результата.

Компонент expression, входящий в состав проекта, предоставляет возможность выполнения математических и сложных выражений, а также возвращает результаты их выполнения.

Компонент exceptions позволяет эффективно обрабатывать и выводить ошибки при работе с llvm.js. Классы исключений, предоставляемые этим компонентом, помогут вам управлять потоком программы и обрабатывать различные типы ошибок.

Однако самыми важными компонентами проекта являются llvm и codegen. llvm - это лексер, конфигурация характеристик языка программирования и грамматика. Он также обладает функцией REPL (read-eval-print loop), что делает его еще более гибким и удобным для разработчиков. Codegen же отвечает за создание машинного кода и обеспечивает оптимизацию, что повышает эффективность программы.

Проект llvm.js стоит применять всем разработчикам, стремящимся сэкономить время и ресурсы при создании собственных языков программирования или превращении существующих языков в компилируемые. Благодаря этому инновационному инструменту, вы сможете добиться впечатляющих результатов в кратчайшие сроки и привнести новую эру в мир программирования.

Вместе с llvm.js вы сможете создавать языки программирования, которые будут легко восприниматься человеком и обладать высокой производительностью благодаря использованию мощного компилятора. Я уверен, что наш проект станет революционным прорывом в сфере разработки программного обеспечения и поможет изменить мир к лучшему. Доверьтесь llvm.js и прокачайте свои навыки программирования до нового уровня!

Старт

Давайте создадим папку для проекта и назовем ее "js-compiler". Внутри папки мы будем разрабатывать наш проект. Прежде чем приступить к разработке, мы должны скачать и установить llvm.js. Версия, доступная на момент написания данной статьи (llvm.js@1.0.0). Чтобы установить llvm.js, выполните следующую команду:

npm i llvm.js

Вы также можете скачать проект с GitHub, используя команду:

git clone https://github.com/llvm-js/llvm-project.git

После настройки проекта у нас будет следующая структура:
- js-compiler/
- grammar/
- grammar.json - файл, содержащий описание грамматики языка JavaScript
- lang/
- test.js - пример файла JavaScript, который мы будем компилировать
- language.js - основной файл компилятора
- compiler.js - файл, содержащий компилятор llvm.js

Напишем в начале файла language.js:

const fs = require('fs');

const llvm = require('llvm.js/llvm');
const Expression = require('llvm.js/expression');
const Compiler = require('./compiler');

Для более эффективной работы над проектом мы создадим класс Language, который будет отвечать за конфигурацию языка программирования. Внутри этого класса мы настроим лексер, а позднее также напишем компилятор.

class Language {
    run(src) {
        if (fs.existsSync(src)) { // (1)
            llvm.Config.setCommentLine('//'); // (2)
            let file_c = fs.readFileSync(src).toString('utf8').split('\n');
        
            const lexer = new llvm.Lexer();
            let ast = lexer.lexer(file_c); // (3)
            ast = ast.filter(tree => !['WHITESPACE', 'COMMENT'].includes(tree.type)); // (4)

            const compiler = new Compiler();
            compiler.run(ast); // (5)
        }
    }
}

Несмотря на то, что нам понадобится меньше 100 строк кода, наш проект оказывается достаточно объемным. Теперь давайте подробнее рассмотрим, что происходит в каждой части кода.

  • (1) - В начале проверяем, существует ли путь к файлу в первой строке. Если путь существует, мы переходим к следующему шагу.

  • (2) - настраиваем параметры комментариев с использованием функции setCommentLine(). Такое название функции делает код более интуитивно понятным.

  • (3) - Седьмая строка представляет создание лексера, а на восьмой строке мы вызываем его, передавая содержимое файла для токенизации и последующей обработки.

  • (4) - Лексер предоставляет нам все токены проекта, но нам нужно получить АСТ без комментариев и пробельных символов.

  • (5) - в одиннадцатой строке мы создаем компилятор, который будет обрабатывать наш АСТ. На двенадцатой строке мы вызываем компилятор, передавая фильтрованный АСТ в качестве аргумента.

Все достаточно просто и понятно! Такое упрощение работы над проектом lllvm.js позволяет нам более эффективно и продуктивно разрабатывать эту потрясающую технологию.

пишем грамматику JS

Перед тем, как приступить к написанию компилятора, необходимо разработать грамматику JavaScript, которую мы будем использовать в процессе проверки компилятором. Если грамматика соответствует нашим правилам, результатом выполнения функции llvm.Grammar.verifyGrammarNoStrict() будет объект. Этот объект включает информацию о пройденных этапах (steps) и текущем элементе AST, а также количество токенов, которые мы должны взять из AST (siliceSize).

Давайте напишем грамматику для создания переменной или константы. В файле grammar.json добавляем следующий код:

{
    "VariableDeclaration": [
        { "token": "IDENTIFER" },
        { "token": "IDENTIFER" },
        { "token": "EQUAL" },

        {
            "or": [
                { "token": "NUMBER" },
                { "token": "STRING" }
            ] 
        }
    ]
}

В данном случае, мы указываем типы токенов. Если встретится объект с полем "or", это означает, что в данной грамматике и месте токен может быть одним из перечисленных типов. В нашем случае мы указали, что токен должен быть либо строкой, либо числом.

Теперь напишем грамматику вызова функций. Для примера, рассмотрим вызов функции console.log():

    "CallExpression": [
        { "token": "IDENTIFER" },
        { "token": "DOT" },
        { "token": "IDENTIFER" },
        { "token": "OPEN_PAREN" },

        {
            "or": [
                { "token": "NUMBER" },
                { "token": "STRING" }
            ] 
        },

        { "token": "CLOSE_PAREN" }
    ]

Заметьте, что в этой грамматике мы используем верхний регистр для именования токенов. Токен "DOT" представляет собой точку. Грамматика позволяет вызывать любую функцию, где первый токен может быть не только "console", а третий токен может быть любым именем функции. Компилятор будет проверять соответствие этой грамматике.

grammar.json
{
    "VariableDeclaration": [
        { "token": "IDENTIFER" },
        { "token": "IDENTIFER" },
        { "token": "EQUAL" },

        {
            "or": [
                { "token": "NUMBER" },
                { "token": "STRING" }
            ] 
        }
    ],


    "CallExpression": [
        { "token": "IDENTIFER" },
        { "token": "DOT" },
        { "token": "IDENTIFER" },
        { "token": "OPEN_PAREN" },

        {
            "or": [
                { "token": "NUMBER" },
                { "token": "STRING" }
            ] 
        },

        { "token": "CLOSE_PAREN" }
    ]
}

Пишем компилятор

Для начала работы с компилятором нам необходимо импортировать необходимые модули и файлы. Мы импортируем модуль fs для работы с файловой системой, модуль grammar для получения правил грамматики из файла grammar.json, модуль llvm для работы с языком программирования, модуль codeGen для генерации кода, и модуль exceptions для обработки ошибок. В файлеcompiler.js добавляем следующий код:

const fs = require("fs");
const grammar = require('./grammar/grammar.json');
const llvm = require("llvm.js/llvm"); 
const codeGen = require('llvm.js/codegen');
const exceptions = require('llvm.js/exceptions');

Напишем метод run( ) внутри класса Compiler. Так как поскольку есть две переменные minast который изменяется во время итерации внутри while, только есть грамматика соответствует правилам которые мы писали ранее в файле grammar.json. isGrammar изменяется если грамматика соответствует правилам и возвращает ответ что мы писали выше.

class Compiler {
    current = 0;

    run(ast) {
        this.ast = ast;
        let isGrammar, miniast;

        // code

         while (this.current < this.ast.length) {
           // code
         }
      
        fs.existsSync('output.bc-asmx') ? fs.rmSync('output.bc-asmx') : fs.writeFileSync('output.bc-asmx', '');
        codeGen.codegen('output');
    }
}

module.exports = Compiler;

Давайте теперь разберём что же делает код:

  • В цикле while мы работаем с каждым элементом абстрактного синтаксического дерева (this.ast). Внутри цикла можно выполнять дополнительные операции, связанные с компиляцией.

  • После завершения цикла, на 14 строке мы проверяем, существует ли уже скомпилированный файл output.bc-asmx. Если файл существует, то мы его удаляем. Иначе, если файл не существует, создаем пустой файл с именем output.bc-asmx.

  • На 15 строке вызываем функцию codeGen.codegen('output'), которая генерирует код на основе абстрактного синтаксического дерева и сохраняет его в файл с именем output.bc-asmx. Аргумент 'output' указывает имя выходного файла, можно задать любое другое имя.

  • В конце файла экспортируем класс Compiler, чтобы иметь возможность использовать его в других модулях или файлах.

Теперь внутри метода run( ) на 8 строке давайте напишем функции которые выполняют свои задачи.

const getMiniAst = () => {
    // Получаем часть AST, указанную в переменной isGrammar.sliceSize,
    // и фильтруем ее, чтобы исключить токены типа 'SPACE'
    miniast = ast.slice(...isGrammar.sliceSize).filter(t => t.type !== 'SPACE');
}

const endIterator = () => {
    // Переключаемся на следующий токен в следующей итерации
    // путем изменения значения this.current
    this.current = isGrammar.sliceSize[1];
    // Обнуляем переменную isGrammar для следующей итерации
    isGrammar = null;
}

const exit = () => {
    // Завершаем процесс выполнения программы
    process.exit();
}

const exceptionInvalidToken = (token) => {
    // Вызываем исключение TokenException с сообщением 'Invalid token'
    // и переданным токеном в качестве аргумента
    new exceptions.TokenException('Invalid token', token);
    // Завершаем выполнение программы
    exit();
}

const exception = (msg, token) => {
    // Вызываем исключение TokenException с переданным сообщением
    // и токеном в качестве аргументов, указав false для показа токена
    new exceptions.TokenException(msg, token, false);
    // Завершаем выполнение программы
    exit();
}

Анализируя каждую функцию, можем привести следующие комментарии:

  • Функция getMiniAst() выполняет следующие действия:

    • В переменную miniast сохраняется часть AST, определенная в переменной isGrammar.sliceSize.

    • Далее происходит фильтрация полученного AST, чтобы исключить токены типа 'SPACE'.

  • Функция endIterator() вызывается в конце итерации, если у переменной isGrammar есть значение.

    • Она изменяет значение this.current так, чтобы в следующей итерации можно было переключиться на следующий токен.

    • Затем переменная isGrammar устанавливается в null для очистки, чтобы быть готовой для следующей итерации.

  • Функция exit() просто завершает выполнение программы.

    • Эта функция будет вызвана, если возникнет необходимость выбросить ошибку.

  • Функция exceptionInvalidToken(token) вызывает исключение с сообщением 'Invalid token' и переданным токеном в качестве аргумента.

    • Для этого она создает новое исключение типа TokenException и передает ему соответствующие аргументы.

    • После вызова исключения, выполнение программы будет завершено.

  • Функция exception(msg, token) также вызывает исключение типа TokenException, но с переданным сообщением и токеном в качестве аргументов.

    • Кроме того, третьим аргументом указывается false, чтобы сообщение ошибки не показывало токен.

    • Затем, как и в предыдущей функции, выполнение программы будет завершено.

код без комментариев.
const getMiniAst = () => miniast = ast.slice(...isGrammar.sliceSize).filter(t => t.type !== 'SPACE');
const endIterator = () => { this.current = isGrammar.sliceSize[1]; isGrammar = null; };
const exit = () => process.exit();
const exceptionInvalidToken = (token) => { new exceptions.TokenException('Invalid token', token); exit(); };
const exception = (msg, token) => { new exceptions.TokenException(msg, token, false); exit(); };

Внутри while цикла мы реализуем логику увеличения переменной this.current, если текущий токен представляет собой лексему ";". Если тип текущего токена является "EOF" (End Of Line), то мы завершаем цикл с помощью оператора break.

while (this.current < this.ast.length) {
  if (ast[this.current].lexem == ';') this.current++;

  if (ast[this.current].type == 'EOF') {
      break;
  }
}

Теперь перейдем к задачам, которые мы хотим компилировать JavaScript, проверяя синтаксис и генерируя код. Давайте сначала напишем генерацию кода для создания константы и переменной.

else if ((isGrammar = llvm.Grammar.verifyGrammarNoStrict(this.current, this.ast, grammar.VariableDeclaration))) {
    getMiniAst();

    if (miniast[0].lexem == 'let') {
        codeGen.variableDeclaration(miniast[1], miniast[miniast.length - 1]);
    } else if (miniast[0].lexem == 'const') {
        codeGen.constDeclaration(miniast[1], miniast[miniast.length - 1]);
    } else {
        exceptionInvalidToken(this.ast[this.current]);
    }

    endIterator();
} 
  • Как видно из приведенного выше кода, на первой строке мы проверяем синтаксис токенов в нестрогом режиме, что указывает функция llvm.Grammar.verifyGrammarNoStrict(). Затем мы используем VariableDeclaration из предварительно импортированной грамматики для проверки синтаксиса.

  • На 4-й строке мы проверяем, что первый токен является "let" для создания переменной, а на 6-й строке - для создания константы.

  • После проверки синтаксиса мы генерируем соответствующий код. Функции codeGen.variableDeclaration() и codeGen.constDeclaration() принимают два аргумента: имя переменной и значение переменной. Обратите внимание, что аргументы должны быть токенами.

  • Если первый токен не соответствует условиям, то мы выбрасываем ошибку с помощью exceptionInvalidToken(this.ast[this.current]).

В начале стоит проверить грамматику CallExpression. В данном контексте, мы создаём три константы для обозначения лексических токенов: объекта, имени функции и аргументов.

else if ((isGrammar = llvm.Grammar.verifyGrammarNoStrict(this.current, this.ast, grammar.CallExpression))) {
    getMiniAst();
    const [object, property, args] = [miniast[0], miniast[2], miniast[miniast.length - 2]].map(t => t?.lexem);
    // code
    endIterator();
}

После этого, мы проверяем, является ли объект console. В случае, если это так, мы проверяем, является ли имя функции log. Если же имя не соответствует log, то мы выбрасываем ошибку, указывая на токен имени функции. Если же имя функции log, то мы вызываем стандартную языковую функцию (SLF) print и передаем в нее аргументы для вывода.

if (object == 'console') {
    if (property == 'log') {
        codeGen.genSLFunction('print');
        let args_t = miniast[miniast.length - 2];
        codeGen.callFunction('print', args_t.lexem);
    } else {
        exception(`${object}.${property}(${args}) is not a function`, miniast[2]);
    }
} else {
    exception(`${object} is not defined`, this.ast[this.current]);
}

Наконец, добавим обработку ошибок для всех возможных проверок грамматики на токен.

else {
   exceptionInvalidToken(this.ast[this.current]);
}
полный код compiler.js
const fs = require("fs");
const llvm = require("llvm.js/llvm");
const grammar = require('./grammar/grammar.json');
const codeGen = require('llvm.js/codegen');
const exceptions = require('llvm.js/exceptions');

class Compiler {
    current = 0;

    run(ast) {
        this.ast = ast;
        let isGrammar, miniast;

        const getMiniAst = () => miniast = ast.slice(...isGrammar.sliceSize).filter(t => t.type !== 'SPACE');
        const endIterator = () => { this.current = isGrammar.sliceSize[1]; isGrammar = null; };
        const exit = () => process.exit();
        const exceptionInvalidToken = (token) => { new exceptions.TokenException('Invalid token', token); exit(); };
        const exception = (msg, token) => { new exceptions.TokenException(msg, token, false); exit(); };

        while (this.current < this.ast.length) {
            if (ast[this.current].lexem == ';') this.current++;

            if (ast[this.current].type == 'EOF') {
                break;
            } else if ((isGrammar = llvm.Grammar.verifyGrammarNoStrict(this.current, this.ast, grammar.VariableDeclaration))) {
                getMiniAst();

                if (miniast[0].lexem == 'let') {
                    codeGen.variableDeclaration(miniast[1], miniast[miniast.length - 1]);
                } else if (miniast[0].lexem == 'const') {
                    codeGen.constDeclaration(miniast[1], miniast[miniast.length - 1]);
                } else {
                    exceptionInvalidToken(this.ast[this.current]);
                }

                endIterator();
            } else if ((isGrammar = llvm.Grammar.verifyGrammarNoStrict(this.current, this.ast, grammar.CallExpression))) {
                getMiniAst();
                const [object, property, args] = [miniast[0], miniast[2], miniast[miniast.length - 2]].map(t => t?.lexem);
                
                if (object == 'console') {
                    if (property == 'log') {
                        codeGen.genSLFunction('print');
                        let args_t = miniast[miniast.length - 2];
                        codeGen.callFunction('print', args_t.lexem);
                    } else {
                        exception(`${object}.${property}(${args}) is not a function`, miniast[2]);
                    }
                } else {
                    exception(`${object} is not defined`, this.ast[this.current]);
                }

                endIterator();
            } else {
                exceptionInvalidToken(this.ast[this.current]);
            }
        }

        fs.existsSync('output.bc-asmx') ? fs.rmSync('output.bc-asmx') : fs.writeFileSync('output.bc-asmx', '');
        codeGen.codegen('output');
    }
}

module.exports = Compiler;

Заключение

В заключение, можно сказать, что llvm.js представляет собой новую технологию, разработанную на JavaScript, которая имеет широкие перспективы в области построения языков программирования и создания интерпретаторов/компиляторов. Одним из главных преимуществ llvm.js является наличие всех базовых компонентов и функций, которые уже реализованы в его основе, что позволяет избежать необходимости писать их с нуля при создании нового языка программирования.

  • В ходе статьи мы познакомились с различными компонентами и функциями llvm.js. Например, мы рассмотрели компоненты exceptions, expression и codegen. Компонент exceptions служит для обработки исключений, expression - для выполнения математических выражений, а codegen - для генерации кода в языке AsmX. Также мы рассмотрели компоненты llvm, который включает в себя конфигурацию и лексер, грамматику и типы токенов, а также создание REPL - среды для чтения, выполнения и вывода результатов кода.

  • В завершение статьи мы провели простую компиляцию JavaScript кода с использованием llvm.js. Мы создали компилятор и успешно скомпилировали JavaScript код в AsmX. Этот пример продемонстрировал мощь и гибкость llvm.js и его способность к работе с разными языками программирования.

  • В целом, llvm.js представляет собой мощное средство для разработчиков, которые работают в области создания языков программирования и интерпретаторов/компиляторов. Он предоставляет широкий набор компонентов и функций, которые значительно упрощают процесс создания новых языков и обеспечивают быстрое развертывание и выполнение кода на различных языках. И хотя llvm.js не связан непосредственно с LLVM, они оба основаны на тех же принципах и позволяют разработчикам использовать их мощь для создания эффективных и производительных приложений.

Исходный код js компилятора на GitHub.

Комментарии (16)


  1. MAXH0
    09.10.2023 13:27
    +3

    скомпилировали JavaScript код в AsmX.

    Для чего приведенное в статье решение нужно, я узнал только в конце. Это как надо затянуть интригу, чтобы читатель до конца ломал голову...


  1. hello_my_name_is_dany
    09.10.2023 13:27
    +6

    Это самый ужасный кликбейт. JavaScript -> LLVM (не настоящий) -> AsmX (интерпретатор на Node.js). Столько рефлексии на js, это явно выше нашего понимания...


  1. osmanpasha
    09.10.2023 13:27
    +2

    Хотелось бы узнать, имеет ли описанный проект отношение к нормальному LLVM


    1. hello_my_name_is_dany
      09.10.2023 13:27
      +1

      Он имеет отношение только к AsmX


      1. osmanpasha
        09.10.2023 13:27
        +2

        Прочитал статью. Мдэ. Понятно, почему по запросам llvm.js и asmx гуглятся совсем другие проекты.


  1. VasiliiKirienko
    09.10.2023 13:27
    +4

    Если вы хотите разработать свой собственный язык программирования или преобразовать имеющийся язык в компилируемый формат, то этот проект, безусловно, является вашим незаменимым союзником.

    Вопрос 1: Как трансляция в интерпретируемый язык программирования, зависимый от js, связана с компиляцией?

    Вместе с llvm.js вы сможете создавать языки программирования, которые будут легко восприниматься человеком и обладать высокой производительностью благодаря использованию мощного компилятора.

    Вопрос 2: Где проверка скорости работы "скомпилированной" программы на javascript?

    Вопрос 3: В чём смысл этого проекта, когда есть настоящий LLVM, делающий тоже самое, только лучше, быстрее и с возможностью перевода в бинарный код, вместо недоязыка со скоростью в 72.2 триллиона раз медленнее C++.


    1. ColdPhoenix
      09.10.2023 13:27
      +2

      1)Никак, автор, к сожалению, не понимает и не хочет понять разницу.

      На любую критику реагирует что его "гения" не признают.


    1. includedlibrary
      09.10.2023 13:27

      Вопрос 1: Как трансляция в интерпретируемый язык программирования, зависимый от js, связана с компиляцией?

      Скажу в защиту автора, что это зависит от определения компиляции. В драконьей книге было дано следующее определение (не дословная цитата): "Компиляция - это перевод программы с одного языка на другой с сохранением семантики"


  1. sofa3376
    09.10.2023 13:27
    +1

    Теперь вы можете компилировать джаваскрипт в джаваскрипт, чтобы исполнять джаваскрипт во время исполнения джаваскрипта


  1. ColdPhoenix
    09.10.2023 13:27
    +1

    Очередная статья "непризнаного гения"(по показаниям автора) вокруг AsmX, интерпритатора некоего авмоподобного языка со средой исполнения в NodeJS(то есть мы интерпретируем другой язык внутри интерпритириумой среды).

    Остальные статьи автор снял с публикаций и/или удалил, после кучи минусов.

    Молчу уж про использование названия LLVM для кликбейта и в названии проекта/репозитория в том числе.(что кстати не очень хорошо)


    1. hello_my_name_is_dany
      09.10.2023 13:27

      Можно на npm отправить жалобу, что фишинг или может использоваться для него в будущих апдейтах


  1. includedlibrary
    09.10.2023 13:27
    +3

    Назначение и применение данного проекта не подлежат сомнению – он служит
    настоящим мощным инструментом для создания трансляторов,
    интерпретаторов и компиляторов. Если вы хотите разработать свой
    собственный язык программирования или преобразовать имеющийся язык в
    компилируемый формат, то этот проект, безусловно, является вашим
    незаменимым союзником.

    Что мне мешает использовать для этих целей настоящий LLVM? Он, в отличие от вашего самозванца, выдаёт оптимизированный нативный код, а не код на интерпретируемом языке. И сразу возникает вопрос, что же такое "компилируемый формат"? Зачем вы собственные никому не понятные термины придумываете?

    Codegen же отвечает за создание машинного кода и обеспечивает оптимизацию, что повышает эффективность программы.

    Зачем врать? Asmx - не машинный код.

    Давайте напишем грамматику для создания переменной или константы. В файле grammar.json добавляем следующий код:

    {
        "VariableDeclaration": [
            { "token": "IDENTIFER" },
            { "token": "IDENTIFER" },
            { "token": "EQUAL" },
    
            {
                "or": [
                    { "token": "NUMBER" },
                    { "token": "STRING" }
                ] 
            }
        ]
    }

    То есть форматы идентификаторов, чисел и строк вы выбрали сами за разработчика компилятора и он никак не может их поменять. А где же обещанная гибкость? Ещё возникает вопрос, а могу ли описать с помощью данной грамматики выражение, в котором у операций есть приоритеты, например, простейший случай:

    <expr> ::= <term>
           |   <expr> + <term>
           |   <expr> - <term>
    <term> ::= <fact>
           |   <term> * <fact>
           |   <term> / <fact>
    <fact> ::= <var>
           |   (<expr>)
    <var> ::= A | B | C ... | X | Y | Z

    Это язык, в котором переменная задаётся одной латинской буквой, есть сложение, вычитание, умножение и деление, причём последние две операции имеют приоритет. Покажите пример грамматики для llvm.js, которая разбирает данный язык.

    Проект llvm.js стоит применять всем разработчикам, стремящимся
    сэкономить время и ресурсы при создании собственных языков
    программирования или превращении существующих языков в компилируемые.
    Благодаря этому инновационному инструменту, вы сможете добиться
    впечатляющих результатов в кратчайшие сроки и привнести новую эру в мир
    программирования.

    Не понятно зачем? Можно взять почти для любого языка flex и bison или их аналоги и сделать лексер и парсер довольно быстро, а плюсом получить гибкость, которой вы лишили разработчика. Для быстрой разработки можно взять haskell или ocaml, так как на них намного легче разрабатывать компиляторы и интерпретаторы, и использовать биндинги к настоящему llvm.

    Выводы, конечно, поражают.

    В завершение статьи мы провели простую компиляцию JavaScript кода с использованием llvm.js.
    Мы создали компилятор и успешно скомпилировали JavaScript код в AsmX.
    Этот пример продемонстрировал мощь и гибкость llvm.js и его способность к
    работе с разными языками программирования.

    Где компилятор JavaScript? В статье был сделан "компилятор" языка, в котором можно присваивать переменным значения и выводить их в консоль, всё.

    В целом, llvm.js представляет собой мощное средство для разработчиков,
    которые работают в области создания языков программирования и
    интерпретаторов/компиляторов. Он предоставляет широкий набор компонентов
    и функций, которые значительно упрощают процесс создания новых языков и
    обеспечивают быстрое развертывание и выполнение кода на различных
    языках. И хотя llvm.js не связан непосредственно с LLVM, они оба
    основаны на тех же принципах и позволяют разработчикам использовать их
    мощь для создания эффективных и производительных приложений.

    По всем пунктам нет. Нативный код быстрее, чем интерпретируемый на Asmx. llvm.js не является мощным и гибким средством.

    Вы сделали неплохой хобби проект, но совершенно нигде не применимый. А описали его так, будто бы революцию по меньшей мере совершили.


  1. raspberry-porridge
    09.10.2023 13:27
    +1

    Можно вопрос ребром? Создал ли автор сих поделий хоть сколько нибудь объёмное продуктовое приложение, полезное хоть кому нибудь?


  1. mark_ablov
    09.10.2023 13:27
    +1

    Даже для такой странной цели, как трансляция чего-либо в пестуемый автором AsmX, есть куда более подходящие инструменты. Тот же chevrotain справится куда лучше.


    1. ColdPhoenix
      09.10.2023 13:27
      +1

      Так это чужой инструмент жеж.

      А автор написал свой llvm(любые совпадения с настоящим LLVM "случайны").


  1. FisHlaBsoMAN
    09.10.2023 13:27

    О, снова этот сумасшедший, который пытается продвигать своё барахло, воруя имена у чего то существующего, рассказывая небылицы о своем бесполезном недоязыке.
    Кто хочет отравить себе мозг - рекомендую к прочтению:
    https://web.archive.org/web/20230831045508/https://habr.com/ru/articles/757140/
    https://web.archive.org/web/20230831045637/https://habr.com/ru/articles/757140/comments/
    +
    https://web.archive.org/web/20230831073156/https://habr.com/ru/articles/757996/
    https://web.archive.org/web/20230831080256/https://habr.com/ru/articles/757996/comments/

    А так же фирменный стиль автора https://web.archive.org/web/20230831045809/https://habr.com/ru/users/ElonReeve/comments/

    У него вроде бы пара аккаунтов была. Все отхабреные в минусах за сотку. Первые пару раз было смешно, а потом уже стало как то грустно.

    И почему мне все время гугл-новостник рекомендует такой "кринж" контент..