Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

MrGoldSky/IScript

Repository files navigation

IScript Interpreter

Личный проект: интерпретатор скриптового языка IScript, реализованный на C++23.


Описание

IScript — легковесный интерпретируемый язык программирования с динамической типизацией, вдохновлённый идеями языков высокого уровня (Python, Lua). Этот проект представляет собой полноценный интерпретатор IScript: он считывает файл с расширением .is, разбирает исходный код, строит абстрактное синтаксическое дерево (AST) и выполняет программы «на лету».

Главная цель проекта — продемонстрировать навыки разработки компилятора/интерпретатора: реализовать лексер, парсер, AST, систему значений и окружений, встроенную стандартную библиотеку функций и механизм обработки ошибок.


Основные возможности

  1. Типы данных

    • Числа: double (числа с плавающей точкой двойной точности), включая поддержку экспоненциальной нотации (например, 1.23e-4).
    • Булевы значения: литералы true и false.
    • Строки: литералы в двойных кавычках, поддержка escape-последовательностей (\n, \t, \", \\ и т. д.).
    • Списки: динамические массивы, литералы в квадратных скобках ([1, 2, 3]), с нулевой индексацией, поддержка срезов (list[1:4], str[:3]).
    • Функции как объекты первого класса: можно присваивать переменным, передавать в качестве аргументов, создавать «анонимные» функции.
  2. Операторы

    • Арифметические: +, -, *, /, % (остаток), ^ (возведение в степень); унарные + и -.
    • Сравнения: ==, !=, <, >, <=, >=.
    • Логические: and, or, not.
    • Присваивания: простое (=) и составные (+=, -=, *=, /=, %=, ^=).
    • Постфиксные и префиксные инкремент/декремент: x++, --y.
    • Индексация и срезы: expr[index], expr[start:end].
  3. Управляющие конструкции

    • Условные операторы
      if условие then
       ... 
      else if условие then
       ...
      else
       ...
      end if
      
    • Циклы
      • while условие ... end while
      • for переменная in последовательность ... end for
    • Операторы прерывания: break, continue.
  4. Функции

    • Определяются через ключевое слово function, возвращают значение через оператор return.
    • Неограниченное число параметров, возможность вложенного объявления функций (без замыканий).
    • Лексическая область видимости, передача контекста (лексические окружения) для каждой функции.
  5. Стандартная библиотека

    • Числовые функции: abs(x), ceil(x), floor(x), round(x), sqrt(x), rnd([min,] max), max(...), min(...), parse_num(s), to_string(x).
    • Строковые функции: len(s), lower(s), upper(s), split(s, delim), join(list, delim), replace(s, old, new).
    • Функции для работы со списками: range(start, end[, step]), push(list, x), pop(list), insert(list, index, x), remove(list, index), sort(list).
    • Системные функции: print(...), println(...), read(), stacktrace().
  6. Модель выполнения

    • Динамическая типизация: все проверки типов происходят во время выполнения.
    • Автоматическое управление памятью: списки, строки и функции хранятся в std::shared_ptr. Примитивные типы копируются по значению.
    • Лексическая область видимости: переменные видны внутри того блока, где объявлены; вложенные функции получают копию окружения родителя, но без поддержки замыканий.
    • Обработка ошибок: ошибки лексики, синтаксиса и выполнения (деление на ноль, выход за границы и т. д.) перехватываются и выводятся в поток ошибок.

Архитектура и структура проекта

Проект разбит на несколько ключевых модулей:

├── lexer.h — определение класса Lexer
├── lexer.cpp — реализация лексического анализатора
├── token.h — перечисление типов токенов и структура Token
├── keywords.h — таблица ключевых слов языка
│
├── parser.h — интерфейс парсера, прототипы функций разбора
├── parser.cpp — реализация синтаксического анализатора (рекурсивный спуск)
├── AST.h — описание узлов абстрактного синтаксического дерева (AST)
│
├── value.h — класс Value (вариантное значение), FunctionValue, базовые операции
├── value.cpp — реализация арифметических и логических операций, toString, typeName
│
├── environment.h — класс Environment для хранения переменных (с поддержкой родительского окружения)
│
├── interpreter.h — прототип главной функции `interpret`
├── interpreter.cpp — инициализация окружения, регистрация встроенных функций, запуск интерпретации
│
└── README.md — документация проекта
  • Lexer (lexer.*) проходит по исходному потоку символов, разбивая его на токены: числа, идентификаторы, строки, операторы, разделители и комментарии (// ... до конца строки).
  • Parser (parser.*) реализует рекурсивный спуск для разбора первичных выражений, бинарных операторов, условных конструкций, циклов, функций и блоков.
  • AST (AST.h) описывает все узлы дерева: литералы (числа, строки, булевы), переменные, бинарные/унарные операции, вызовы функций, индексацию/срезы, условные выражения, циклы, присваивания, блоки и операторы управления (break/continue/return).
  • Value (value.*) инкапсулирует «все возможные» значения IScript: от nil до double, bool, std::string, std::vector<Value> и функций (FunctionValue). Здесь же определены перегруженные операторы: +, -, *, /, %, ^, сравнения, логические &&/||/!, доступ по индексу и срезы.
  • Environment (environment.h) хранит отображение имя → Value, включает ссылку на родительское окружение.
  • Interpreter (interpreter.*)
    1. Создаёт Lexer и Parser, строит список всех функций (включая «топ-левел» выражения) вектором std::vector<std::unique_ptr<FunctionAST>>.
    2. Инициализирует глобальное окружение: регистрирует встроенные функции (print, println, математические, строковые, список-функции и т. д.).
    3. Сохраняет все определённые пользователем функции в глобальном окружении.
    4. Исполняет «анонимные» топ-левел выражения (код вне функций).
    5. Обрабатывает исключения (std::runtime_error, ReturnException, BreakException, ContinueException) и выводит сообщения об ошибках.

Запуск и примеры

Исполняемый файл iscript_interpreter читает код из стандартного ввода:

./iscript_interpreter < script.is

Примеры

Ниже несколько классических задач, продемонстрированных на IScript. Сохраните каждую в отдельный файл с расширением .is.


1. Фибоначчи (fibonacci.is)

function fib(n)
 if n < 2 then
 return n
 end if
 return fib(n-1) + fib(n-2)
end function
for i in range(0, 10)
 println(fib(i))
end for

Что происходит:

  • Рекурсивная функция fib вычисляет число Фибоначчи.
  • Цикл for ... in итерируется по списку, возвращаемому range(0, 10).
  • Каждый результат выводится на новой строке.

2. Максимум в списке (maximum.is)

function maximum(lst)
 if len(lst) == 0 then
 return nil
 end if
 max_val = lst[0]
 for x in lst
 if x > max_val then
 max_val = x
 end if
 end for
 return max_val
end function
nums = [3, 42, 7, 19, 0, -5, 100, 23]
println("Maximum is: " + to_string(maximum(nums)))

3. FizzBuzz (fizzbuzz.is)

function fizzbuzz(n)
 for i in range(1, n + 1)
 if i % 15 == 0 then
 println("FizzBuzz")
 else if i % 3 == 0 then
 println("Fizz")
 else if i % 5 == 0 then
 println("Buzz")
 else
 println(to_string(i))
 end if
 end for
end function
fizzbuzz(30)

Структура проекта (файлы)

  • lexer.h/.cpp
    Лексический анализатор: преобразует поток символов в последовательность токенов (Token).

  • token.h
    Описание enum class TokenType, структура Token (тип, лексема, значение, номер строки).

  • keywords.h
    Таблица соответствия строковых ключевых слов (например, "if", "while") и их типов TokenType.

  • parser.h/.cpp
    Синтаксический анализ: рекурсивный спуск для разбора выражений, условных конструкций, циклов, функций и блоков.

  • AST.h
    Иерархия классов для узлов AST: литералы (NumberExprAST, StringExprAST, BooleanExprAST), переменные (VariableExprAST), бинарные/унарные операции (BinaryExprAST, UnaryExprAST), вызовы функций (CallExprAST), присваивания, циклы (WhileExprAST, ForExprAST), условные (IfExprAST), блоки (BlockExprAST), управляющие исключения (BreakExprAST, ContinueExprAST, ReturnExprAST).

  • value.h/.cpp
    Класс Value — контейнер для любого значения IScript. Поддерживает арифметику, сравнения, логику, индексацию и срезы. Также хранит FunctionValue для встроенных и пользовательских функций.

  • environment.h
    Класс Environment с отображением имя переменной → Value, включает ссылку на родительское окружение.

  • interpreter.h/.cpp
    Функция interpret запускает лексер и парсер, затем создаёт глобальное окружение, регистрирует встроенные функции, сохраняет в нём все пользовательские функции и выполняет «анонимные» выражения. Обрабатывает исключения и выводит ошибки.

  • README.md
    Документация проекта (этот файл).


Стандартная библиотека

Числовые функции

  • abs(x)
    Возвращает абсолютное значение числа x.

  • ceil(x)
    Округляет число x вверх до ближайшего целого.

  • floor(x)
    Округляет число x вниз до ближайшего целого.

  • round(x)
    Округляет x до ближайшего целого по математическим правилам.

  • sqrt(x)
    Вычисляет квадратный корень из числа x.

  • rnd(max) или rnd(min, max)
    Возвращает случайное число (вещественное) из диапазона [0, max) или [min, max).

  • max(a, b, c, ...)
    Возвращает максимальное из переданных числовых аргументов.

  • min(a, b, c, ...)
    Возвращает минимальное из переданных числовых аргументов.

  • parse_num(s)
    Преобразует строку s в число double.

  • to_string(x)
    Преобразует число или логическое значение x в строку.

Строковые функции

  • len(s)
    Возвращает длину строки s.

  • lower(s)
    Преобразует все символы строки s в нижний регистр.

  • upper(s)
    Преобразует все символы строки s в верхний регистр.

  • split(s, delim)
    Разбивает строку s по разделителю delim и возвращает список строк.

  • join(list, delim)
    Объединяет элементы списка строк list, вставляя между ними разделитель delim.

  • replace(s, old, new)
    Заменяет все вхождения подстроки old в строке s на new.

Функции для работы со списками

  • range(end)
    Возвращает список чисел от 0 до end - 1 с шагом 1.

  • range(start, end)
    Возвращает список чисел от start до end - 1 с шагом 1.

  • range(start, end, step)
    Возвращает список чисел от start до тех пор, пока

    • v < end, если step > 0
    • v > end, если step < 0.
  • push(list, x)
    Добавляет элемент x в конец списка list.

  • pop(list)
    Удаляет и возвращает последний элемент списка list.

  • insert(list, index, x)
    Вставляет элемент x в список list по индексу index.

  • remove(list, index)
    Удаляет и возвращает элемент по индексу index из списка list.

  • sort(list)
    Сортирует список list «на месте» по возрастанию.

Системные функции

  • print(...)
    Выводит аргументы без переноса строки.

  • println(...)
    Выводит аргументы с последующим переходом на новую строку.

  • read()
    Считывает строку из стандартного ввода и возвращает её.

  • stacktrace()
    Возвращает строку с трассировкой стека в момент вызова функции.


Тестирование

Важно! Этот проект предусматривает модульное тестирование для проверки корректности работы интерпретатора. Ниже приведены рекомендации по организации и запуску тестов.

Запуск тестов

  • После сборки перейдите в директорию build и выполните:
    ctest --output-on-failure
  • Все тесты будут запущены автоматически. В случае неудачных тестов выводятся подробные сообщения.

Лицензия

Проект распространяется под лицензией MIT — см. файл LICENSE.

About

Lightweight IScript interpreter in C++23 (lexer, parser, AST, Environment)

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

AltStyle によって変換されたページ (->オリジナル) /