Skip to content

MrGoldSky/IScript

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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