[Open source, Программирование, C++] Интерпретатор скрипта на С++
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Всем привет.
Написал простой интерпретатор, конечно не конкурент lua, но тоже может пригодиться.
Кому интересно прошу.
Сразу пример, что получилось:
stringstream ss;
ss << "$a = 5;"
"$b = 2;"
"while($a > 1){"
" $a -= 1;"
" $b = summ($b, $a);"
" if($a < 4){"
" break;"
" }"
"}"
"$b";
string res = ir.cmd(ss.str()); // 9
Что хотелось
Идея была в том, чтобы полностью отделить функционал (пользовательские операторы и функции) от самого языка скрипта, ограничиться минимумом ключевых слов (всем известных), ну и сделать интерпретатор и язык компактным и удобным для использования.
Что получилось
Скриптовый язык вышел простой и ограниченный конечно.
Состоит из трех компонентов — переменные, выражения и функции, и нескольких базовых ключевых слов. Тип значений для всех компонентов — строка.
Нет жестко забитых функций и операторов, все добавляет программист перед запуском скрипта.
Как это работает
Первый этап — парсинг скрипта. На этом этапе идет синтаксический разбор текста скрипта: выделяются ключевые слова, операторы, функции.
Явно АСД не создается, но косвенно появляется (можно сказать в плоскости массива) в виде очереди операций, которые должны быть выполнены последовательно, то есть другими словами, все встречающиеся сущности попадают в массив операций сразу в нужном порядке выполнения.
Все ошибки написания скрипта находятся на этом этапе.
Второй этап — выполнение скрипта. Здесь идет проход по массиву операций, с последовательным выполнением каждой.
Внутри все построено на рекурсивном вызове функций и проверках условий вызова.
Основные компоненты скрипта:
- Переменная. Любая последовательность символов в коде скрипта начинающаяся с '$', считается переменной.
Переменные используются для хранения промежуточных результатов вычислений, и для передачи параметров в функции. Имеют глобальную область видимости.
Объявляются и используются только в коде скрипта, сразу использовать без объявления можно (значение по умолчанию — пустая строка):
$c = 5 + 6;
summ($c, 6);
Ко всем переменным в скрипте можно обращаться (и изменять их при необходимости) из основного кода, например, в функции:
Intrerpreter ir;
ir.addFunction("summScriptVars", [&ir](const vector<string>& args) ->string {
int res = 0;
for (auto& v : ir.allVariables()) {
if (isNumber(v.second)) res += stoi(v.second);
}
return to_string(res);
});
- Выражение. Состоит из переменных, операторов и вызовов функций.
Обязательно должно заканчиваться символом ';'.
Может быть параметром функции, в этом случае его не нужно закрывать символом ';'.
В выражениях могут использоваться скобочки для повышения приоритета операций над переменными. О приоритете ниже.
$b = 4;
$c = 5 + $b + 3 - 7;
$a = $b * (3 + $c) + summ($a, $b, $c + 1);
- Функция. Любые функции создаются на уровне основного кода, в скрипте только используются. Функция принимает массив параметров, возвращает строку как результат работы.
Сначала функцию нужно определить и добавить в основном коде:
Interpreter ir;
ir.addFunction("summ", [](const vector<string>& args) ->string {
int res = 0;
for (auto& v : args) {
if (isNumber(v)) res += stoi(v);
}
return to_string(res);
});
В скрипте функция вызывается по имени, параметры передаются в скобочках, как обычно:
$b = summ($b, $a);
Функция может принимать другие функции и выражения:
$b = 1;
$c = summ($b, summ($b + 5, $b + $b - 1), 4);
$a = $c - summ($b, 3);
- Оператор. Любая последовательность символов в коде скрипта, заранее определенная в основном коде, считается оператором.
Сначала оператор нужно определить и добавить в основном коде:
Interpreter ir;
ir.addOperator("+", [](string& leftOpd, string& rightOpd) ->string {
if (isNumber(leftOpd) && isNumber(rightOpd))
return to_string(stoi(leftOpd) + stoi(rightOpd));
else
return leftOpd + rightOpd;
}, 1);
ir.addOperator("==", [](string& leftOpd, string& rightOpd) ->string {
return leftOpd == rightOpd? "1" : "0";
}, 2);
ir.addOperator("=", [](string& leftOpd, string& rightOpd) ->string {
leftOpd = rightOpd;
return leftOpd;
}, 17);
При создании оператора помимо определения нужно задать приоритет.
Приоритет работает так же как в С++: нулевой наивысший, далее чем больше значение приоритета, тем позже будет выполнен оператор. Порядок выполнения операторов с одинаковым приоритетом — слева направо.
Операторы используются в выражениях.
$c = 5 + 6;
$b = 2;
$a = $c + 5;
$c = summ($a + 5 / $b);
Теперь опишу остальные ключевые слова языка скрипта, в основном это управляющие конструкции.
- while(condition){body}. Выполняет циклически последовательность выражений (далее, тело цикла) в зависимости от результата выполнения условия.
Условие заключается в скобочки '()' и, как и в любом языке, рассчитывается на каждой итерации цикла.
Условие считается выполненным, если результат расчета условия для численного значения не равен 0, для строкового значения — не пустая строка (численное значение — значит, что строка может быть преобразована в целое число).
Тело цикла заключается в фигурные скобки '{}', и состоит из неограниченной последовательности выражений и управляющих конструкций (то есть в теле цикла могут быть другие циклы).
$c = 1;
$b = 4;
while($b > 0){
$c *= $b;
$b -= 1;
}
- if(condition){body}. Выполняет однократно последовательность выражений в зависимости от результата выполнения условия.
$c = 1;
$b = 4;
if(($b - 4) == 0){
$c = $b;
}
- else{body}. Выполняет однократно последовательность выражений, если не было выполнено предыдущее условие.
$c = 1;
$b = 4;
if(($b - 3) == 0){
$c = $b;
}
else{
$b = $c;
}
- elseif(condition){body}. Выполняет однократно последовательность выражений, если не было выполнено предыдущее условие и выполняется текущее условие.
$c = 1;
$b = 4;
if(($b = $b - 3) == 0){
$c = $b;
}
elseif($c == summ($b)){
$b = $c;
}
- break;. Выполняет прерывание текущего цикла.
continue;. Начинает заново текущий цикл.
$b = 4;
while($b > 0){
$b = rand(10);
if ($b == 3){
continue;
}
if ($b == 2){
break;
}
}
- #macro name{body}. Объявление макроса.
#name;. Вставка тела макроса далее в коде.
Под макросом здесь имеется в виду код, который повторяется много раз в скрипте, и можно его заменить именем.
#macro myMc{
$c = 1;
$b = 4;
};
$d = 5;
#myMc;
- goto l_name;. Перемещение на метку вверх или вниз по скрипту. Должен быть единственным оператором в выражении.
l_name:. Метка, на которую можно переместиться.
Метка обязательно должна начинаться с 'l_' (элл и нижнее подчеркивание) и заканчиваться ':'.
$a = 5;
while($a > 0){
$a -= 1;
if ($a == 2){
goto l_myLabel;
}
}
l_myLabel: $a;
На метку можно перемещаться из основного кода, например, в функции скрипта вызвать специальную функцию 'gotoOnLabel' (это конечно грязный хак, специально для месье, которые знают..):
Interpreter ir;
ir.addFunction("myJump", [&ir](const vector<string>& args) ->string {
if (!args.empty())
ir.gotoOnLabel(args[0]);
}
return "";
});
Как использовать и где может быть полезен
Предлагается использовать как код, то есть добавлять в свой проект файл исходного кода интерпретатора, он всего один. Заголовочный файл тоже единственный.
Может использоваться в простых случаях, когда не хочется подключать что-то внешнее, но нужно дать пользователю возможность интерактивно влиять на ход выполнения ПО.
Либо в случаях, когда не хочется давать пользователю в руки весь арсенал скриптовых языков, а ограничиться простым набором команд.
Еще можно попробовать построить RPC на его основе.
Что дальше, что планируется нового
Если коротко, то ничего.
Проект в ширину расти не будет, никаких новых ключевых слов, структур не планируется добавлять. Не хочу, чтобы он разбух и превратился в еще один птичий язык, в котором надо разбираться, что там наворочено, а здесь все пока прозрачно более-менее.
Только поддержка, правка багов, возможно, стоит добавить несколько заранее определенных пользовательских функций, отдельно.
Распространяется свободно, лицензия MIT
Спасибо.
P.S.:
Я писал его ранее когда-то давно, там получилось не очень. Тут после одного письма пользователя, решил все это дело переписать по нормальному.
Вот думал, публиковать-нет статью на эту поделку, по сути тривиальный баян в принципе, и есть уже мастодонты всякие в этой нише.
Нажал все-таки кнопочку, может еще кому-то пригодится когда.
===========
Источник:
habr.com
===========
Похожие новости:
- [C++] dynamic_cast на этапе компиляции
- [Программирование] Асинхронное взаимодействие. Брокеры сообщений. Apache Kafka
- [Open source, Виртуализация, Kubernetes, Openshift] 7 вещей, которые нужно проработать, прежде чем запускать OpenShift в продакшн
- [Разработка веб-сайтов, PHP, Drupal, Программирование] #lazy_builder (не путать с lazy load) в Drupal 8/9
- [PHP, Программирование] События в OpenCart
- [Программирование, C++, Проектирование и рефакторинг] Как легко и просто модернизировать код на C++ (перевод)
- [Программирование, Компиляторы, C, Разработка под Windows] Использование SEH в 32 разрядных приложениях Windows с компилятором Mingw-W64
- [Программирование, ReactJS] Создание React-компонентов с помощью Hygen (перевод)
- [Программирование, Разработка под iOS, Разработка мобильных приложений, Swift] Приложение на SwiftUI в AppStore – сложности разработки
- [Разработка веб-сайтов, Программирование, Исследования и прогнозы в IT, Облачные сервисы] Самые популярные языки программирования для бэкенда: для чего они подходят лучше всего и какие компании их используют (перевод)
Теги для поиска: #_open_source, #_programmirovanie (Программирование), #_c++, #_skript (скрипт), #_skriptovye_jazyki (скриптовые языки), #_open_source, #_programmirovanie (
Программирование
), #_c++
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:43
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Всем привет. Написал простой интерпретатор, конечно не конкурент lua, но тоже может пригодиться. Кому интересно прошу. Сразу пример, что получилось: stringstream ss;
ss << "$a = 5;" "$b = 2;" "while($a > 1){" " $a -= 1;" " $b = summ($b, $a);" " if($a < 4){" " break;" " }" "}" "$b"; string res = ir.cmd(ss.str()); // 9 Что хотелось Идея была в том, чтобы полностью отделить функционал (пользовательские операторы и функции) от самого языка скрипта, ограничиться минимумом ключевых слов (всем известных), ну и сделать интерпретатор и язык компактным и удобным для использования. Что получилось Скриптовый язык вышел простой и ограниченный конечно. Состоит из трех компонентов — переменные, выражения и функции, и нескольких базовых ключевых слов. Тип значений для всех компонентов — строка. Нет жестко забитых функций и операторов, все добавляет программист перед запуском скрипта. Как это работает Первый этап — парсинг скрипта. На этом этапе идет синтаксический разбор текста скрипта: выделяются ключевые слова, операторы, функции. Явно АСД не создается, но косвенно появляется (можно сказать в плоскости массива) в виде очереди операций, которые должны быть выполнены последовательно, то есть другими словами, все встречающиеся сущности попадают в массив операций сразу в нужном порядке выполнения. Все ошибки написания скрипта находятся на этом этапе. Второй этап — выполнение скрипта. Здесь идет проход по массиву операций, с последовательным выполнением каждой. Внутри все построено на рекурсивном вызове функций и проверках условий вызова. Основные компоненты скрипта:
Теперь опишу остальные ключевые слова языка скрипта, в основном это управляющие конструкции.
Как использовать и где может быть полезен Предлагается использовать как код, то есть добавлять в свой проект файл исходного кода интерпретатора, он всего один. Заголовочный файл тоже единственный. Может использоваться в простых случаях, когда не хочется подключать что-то внешнее, но нужно дать пользователю возможность интерактивно влиять на ход выполнения ПО. Либо в случаях, когда не хочется давать пользователю в руки весь арсенал скриптовых языков, а ограничиться простым набором команд. Еще можно попробовать построить RPC на его основе. Что дальше, что планируется нового Если коротко, то ничего. Проект в ширину расти не будет, никаких новых ключевых слов, структур не планируется добавлять. Не хочу, чтобы он разбух и превратился в еще один птичий язык, в котором надо разбираться, что там наворочено, а здесь все пока прозрачно более-менее. Только поддержка, правка багов, возможно, стоит добавить несколько заранее определенных пользовательских функций, отдельно. Распространяется свободно, лицензия MIT Спасибо. P.S.: Я писал его ранее когда-то давно, там получилось не очень. Тут после одного письма пользователя, решил все это дело переписать по нормальному. Вот думал, публиковать-нет статью на эту поделку, по сути тривиальный баян в принципе, и есть уже мастодонты всякие в этой нише. Нажал все-таки кнопочку, может еще кому-то пригодится когда. =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_c++ |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 16:43
Часовой пояс: UTC + 5