CLL в ISPA: Семантические действия просто и мощно

от автора

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

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% Cll0
0% Bison вставки0
0% ANTLR вставки0

Никто еще не голосовал. Воздержался 1 пользователь.

ссылка на оригинал статьи https://habr.com/ru/articles/923526/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *