Для дальнейшего запуска репозиторий необходимо клонировать в папку GOPATH/src
Папку с GOPATH можно узнать с помощью
go env GOPATH
Для запуска интерактивного выполните:
go run main.go
Чтобы выполнить заготовленный файл с кодом укажите путь до нужного файла:
go run main.go program.mlang resultfile
Тогда в resultfile
будут записаны результаты верхнеуровневых выражений.
go run main.go program.mlang
В этом случае будет выполнена программа, в stdout попадут ошибки и результаты вызова print
Для запуска тестов запустите tests.sh
Cами тесты располагаются в папках: lexer
, parser
, evaluator
в файлах с суффиксами _test
На выходе из лексера получаем следующий набор токенов(полный список можно посмотреть в файле token.go
), с которыми и работает парсер:
- Identifier - имя идентификатора начинающееся с буквы или символа
_
и не являющееся ключевым словом - Спец символ из
(){};,+=-*/<>!=
- знаки операторов - IntegerLiteral - целое неотрицательное 64 битное число
- BooleanLiteral -
true
илиfalse
- Ключевые слова - {
return
,if
,else
}
Видно что правила грамматики, относящиеся к выражениям(Expression) имеют похожую структуру:
ExpressionN-> Expression(N+1) InfixOperator ExpressionN | Expression(N+1)
.
Т.е правило с оператором меньшего приоритета сводится к аналогичному с большим. Для того, чтобы обеспечить расширяемость набора операторов будем спускаться на следующий уровень приоритета, если приоритет последнего связанного оператора строго меньше приоритета нового оператора. Использование строгого сравнения приоритетов также решает проблему правой асоциативности, т.к в таком случае левая часть выражения с одинаковыми приоритетами становится левым подвыражением следующего блока выражения. Понятнее написано в parser.go/parseExpression, там же находится полный список возможных операторов и их приоритетов(таблица precedence). Вызов функции может быть интерпретирован как инфиксный оператор (
c наивысшим приоритетом.
MLang поддерживает 3 типа данных с которыми может работать пользователь:
- integer - целые 64битные числа
- boolean - логический тип, в программе обозначается литералами true и false
- function - функции
- null - специальный тип null
Язык содержит 2 основные конструкции:
Условные выражения:
if (10 + 5) {
5 + 1;
} else {
6 + 2;
}
Результатом которых является последнее выполненное выражение вополненной ветки условия(6).
Функции:
func(x, y) {
x + y;
}
или
func(x,y) {
return x + y;
x - y;
}
В обоих случаях результатом вызова функции от 2 аргументов будет сумма этих аргументов.
В папке examples
расположены примеры программ для их запуска необходимо выполнить следующую команду
go run main.go ./examples/<filename>
В данных примерах используются стандартные функции print
- выводит значения переменных на экран и read
- считывает целое число с клавиатуры, возвращает null иначе.
Пример программы находящей сумму всех вводимых чисел до первого нуля
f = func(acc) {
a = read()
if (a) {
f(acc + a)
} else {
acc
}
}
print(f(0))
Интерпретатор языка состоит из 7 основных пакетов:
- token - Описание разрешенных токенов в языке
- lexer - Производит преобразование исходного кода на mlang в последовательность токенов для последующей обработки парсером
- ast - Описание абстрактного синтаксического дерева, задающего структуру выполнения программы
- parser - Преобразует последовательность токенов полученных из модуля лексера в абстрактное синтаксическое дерево
- evaluator - Производит разбор аст, выполняя описаннные в нем вычисления
- object - Описание внутренних объектов и типов языка
- repl - Собственно интерпретатор