Описание всех функций генератора

CLL - Common Language Logic.

Что это

При создании парсера семантические действия часто важны для правильной конструкции AST. CLL - это возможность вставлять семантические действия в парсер генераторе. Отличается удобным синтаксисом, достаточной абстракцией и конвертацией в большинство, если не во все языки.

Принцип роботы

CLL объявляеться с помощью $. В дальнейшем планирую сделать опциональным. После $ идет объявление переменной, условие, цикл и т.д.

Объявление переменных

num floating = 1.4;
num integral = 1;
str string = "Hello, World!";
bool boolean = true;
arr<str> array = ["a", "b", "c"];
obj<str, bool> object1 = { a: true, b: false, c: true };
obj<num, bool> object2 = { 1: true, 2: false, 3: true };
var any_type = "Hello, World!";
any_type = 45;

Типы:
num - номер с плавающей запятой
str - строка
bool - булиновое значение
arr<type> - массив определенного типа
obj<typen type> - объект (ключ-значение)
Token - тип токена.
Rule - тип правила
var - переменная любого типа

Пример перевода в С++:

double floating = 1.4;
double integral = 1;
std::string string = "Hello, World!";
bool boolean = true;
std::vector<std::string> array = {"a", "b", "c"};
std::unordered_map<std::string, bool> object1 = {
  {"a", true},
  {"b", false},
  {"c": true}
};
std::unordered_map<long long, bool> object2 = {
  {1, true},
  {2, false},
  {3, true}
};
std::any any_type = "Hello, World!";
any_type = 45;

Объявление условий

NUMBER:

  @( @ [+-]? @ ( [0-9]+ ) @ ([.,] [0-9]+)? )
  @{full, sign, main, dec}
;
simple_expr:
  &a NUMBER &sign PLUS | MINUS | DIVIDE | MULTIPLE &b NUMBER
  $if (sign.name() == :PLUS) {
    $return a.full.to_double() + b.full.to_double();
  } else if (sign.name() == :MINUS) {
    $return a.full.to_double() - b.full.to_double();
  } // ...
;

&a NUMBER - объявление переменной "а" с значением результата матчинга токена NUMBER
sign.name() - для правил или токенов доступны методы, этот возвращает имя токена/правила
:PLUS - имя токена или правила. Для вложеных правил используеться "."
TO_DOUBLE - встроенная функция конвертирования

Пример перевода C++:

template<typename IT>
Rule_res simple_expr(IT pos) {
  Token a;
  Token sign;
  Tokne b;
  if (pos->name() != Tokens::NUMBER) {
     // throw an error
  }
  a = *pos++;
  // ...
  if (sign.name() == Tokens::PLUS) {
    return {true, Rule(/*...*/, stod(a.data().full) + stod(b.data().full))};
  } else {
    if (sign.name() == Tokens::MINUS) {
      return {true, Rule(/*...*/, stod(a.data().full) - stod(b.data().full))};
    }
  }
  return {true, {}};
}

Объявление циклов

object:
  &key NUMBER | STRING ':' &val rvalue (&keys NUMBER | STRING ':' &values rvalue)*

  $obj<num, var> ints;
  $obj<str, var> strings;
  $if (key.name() == :NUMBER) {
    ints[key.full.to_int()] = rvalue;
  } else {
    strings[key] = rvalue;
  }
  $for (num i = 0; i < keys.size(); i++) {
    $if (key.name() == :NUMBER) {
      ints[keys[i].full.to_int()] = values[i];
    } else {
      strings[keys[i]] = values[i];
    }
  }
  // create AST node
  {
    ints: ints
    strings: strings
  }
;

Пример перевода в C++:

std::unordered_map<int, std::any> ints;
std::unordered_map<std::string, std::any> strings;
if (key.name() == Tokens::NUMBER) {
  ints[stoi(key.data())] = rvalue;
} else {
  strings[key.data()] = rvalue
}
for (double i = 0; i < keys.size(); i++) {
  if (key.name() == Tokens::NUMBER) {
    ints[stoi(key.data())] = rvalue;
  } else {
    strings[key.data()] = rvalue
  }
}
object_data data;
data.ints = ints;
data.strings = strings;
return {true, Rule(/*...*/, data)};

Поддерживаеться также while, синтаксис как в других языках:

while(cond) { statement }

Встроенные методы

Этот список может изменяться
num - to_str
str - find, slice, substr, insert, erase, to_double, to_int
arr - find, slice, insert, erase, push, pop
obj - keys, values
Token, Rule - startpos, endpos, clear, empty, line, column, length, name, data

Объявление типов и функций

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

extern type uint8_t;
extern void log(str message);

Сравнение с ANTRL, Bison

  1. Вставка кода и абстракция.

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

    В CLL код конвертируеться в любой язык, давая возможность создавать общие структуры как переменные, циклы

  2. Типизация

    ANTRL использует типизацию целевого языка
    В Bison типизация может быть частичной через %type, но сложной в управлении.
    CLL имеет встроенные типы, ошибки проверяються во время компиляции

  3. Читаемость

    Семантические действия мешают читаемости в ANTRL, Bison, но не так существенно с CLL

Итог

CLL это мощный инструмент для добавления различных семантических действий в грамматику. Он позовляет создавать переменные, условия, циклы не делая эти добавления специфичными для языка. Его абстракция упрощает взаимодействие с разпарсенными данными, делая этот инструмент нужным для правильной конструкции AST.

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