LINUX.ORG.RU

Собственный DSL?


0

3

Привет, ЛОР!

Есть у меня такая задача - есть сишная структура:

struct foo_struct {
  int8_t f8;
  int32_t f32;
  int64_t arr64[10];
};

т.е. только примитивные типы и массивы из них. Далее пользователь может захотеть сделать для нее некое представление, в котором просит из одного или арифметического выражения над несколькими полями структуры составить новые:

view foo_view {
   name: "My field 1", type: string, from: f8, id: 500
   name: "My field 2", type: int64, from: f8 * f32, id: 900
   name: "Array field %i", type: string[], from: arr64, ids: 1000...   
};

нужно на основе этих данных написать генератор сишного кода, который бы сделал мне такие функции:

int foo_view_get_by_id(struct foo_struct* stct, int id, void* to);

/* foo_view_get_by_name("Array field 8") должен вернуть to_str(arr64[8]) */
int foo_view_get_by_name(struct foo_struct* stct, const char* name, void* to);

сейчас что-то из этого делает наколеночный скрипт на awk, но этот подход давно себя исчерпал. как писать такой генератор? flex + bison + самописный генератор в С или что-то готовое уже есть?

s/C/C++11 + boost/g.

PS: хотел попросить привести более развернутый пример, но обнаружил, что в убогой сишечке нет указателей на члены.

PPS: возможно не C++ + boost, а C#. Или если следовать ЛОРовским традициям, то на выбор: Common Lisp, Scheme, Ocaml, Haskell.

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 4)

Для скриптовых языков есть много готовых парсеров. Для Python - PLY, pyparsing, SPARK.

А flex + bison лично я не советую, если парсер не обязан быть на Си.

tailgunner ★★★★★
()

Кстати, в дополнение к совету tailgunner'а, если конкретный синтаксис DSL'я для тебя не принципиален, то можно сделать так, чтобы твоя спецификация была корректной программой, скажем на Python:

view('foo_view', 
  { 'name': "My field 1", 'type': string, 'from': 'f8', 'id': 500 },
  { 'name': "My field 2", 'type': int64, 'from': 'f8 * f32', 'id': 900 },
  { 'name': "Array field %i", 'type': arrayof(string), 'from': 'arr64', 'ids': 1000 })

В этом случае тебе ничего парсить не придётся, а только определить функции (view и т.д.), которые будут генерировать нужные структуры данных, и загружать спецификацию exec'ом.

Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от BreadFan

правда, арифметические операции надо тоже парсить будет. и в зависимости от типа аргументов еще и проверки разные делать при генерации. но для proof of concept пойдет, наверное.

BreadFan ★★
() автор топика
Ответ на: комментарий от BreadFan

В питоне можно перегружать операторы, так что «парсинг» арифметики можно за счёт него реализовать. Что-то в духе:

class Product:
    def __init__(self, x1, x2):
        self.x1 = x1
        self.x2 = x2

    def __repr__(self):
        return '#(Product of %s and %s)' % (self.x1, self.x2)

class Variable(object):
    def __init__(self, name):
        self.name = name

    def __rmul__(self, x):
        return Product(self, x)

    def __repr__(self):
        return '#(Variable %s)' % self.name
>>> f8 = Variable('f8')
>>> 2 * f8
#(Product of #(Variable f8) and 2)
Begemoth ★★★★★
()
Последнее исправление: Begemoth (всего исправлений: 1)
Ответ на: комментарий от Begemoth

В питоне можно перегружать операторы, так что «парсинг» арифметики можно за счёт него реализовать.

Не учи плохому. Парсить арифметические выражения несложно.

tailgunner ★★★★★
()
view foo_view {
   name: "My field 1", type: string, from: f8, id: 500
   name: "My field 2", type: int64, from: f8 * f32, id: 900
   name: "Array field %i", type: string[], from: arr64, ids: 1000...   
};

Да это же почти json? Бери готовый json-парсер

anonymous
()
Ответ на: комментарий от BreadFan

Кстати, для облегчения парсинга лучше завершать описание каждого поля ':' (если у тебя пробельные символы незначимы).

tailgunner ★★★★★
()
Ответ на: комментарий от mix_mix

Я не просто так про букварь сказал, виртуальные функции тут ни при чем. Указатель может быть на поле структуры, например, или на не виртуальную функцию-член.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

но обнаружил, что в убогой сишечке нет указателей на члены.

#include <stdio.h>
#include<stdlib.h>
struct name {
   int a;
   float b;
   char c[30];
};
int main(){
   struct name *ptr;
   int i,n;
   printf("Enter n: ");
   scanf("%d",&n);
   ptr=(struct name*)malloc(n*sizeof(struct name));
/* Above statement allocates the memory for n structures with pointer ptr pointing to base address */
   for(i=0;i<n;++i){
       printf("Enter string, integer and floating number  respectively:\n");
       scanf("%s%d%f",&(ptr+i)->c,&(ptr+i)->a,&(ptr+i)->b);
   }
   printf("Displaying Infromation:\n");
   for(i=0;i<n;++i)
       printf("%s\t%d\t%.2f\n",(ptr+i)->c,(ptr+i)->a,(ptr+i)->b);
   return 0;
}
comp00 ★★★★
()
Ответ на: комментарий от anonymous

Я знаю, но стоит ли натягивать задачу на питоний AST - ХЗ.

tailgunner ★★★★★
()
Ответ на: комментарий от comp00

Вот тебе пример:

#include <iostream>
#include <vector>

struct Foo
{
  double x;
  double y;
};

double sum(std::vector<Foo> const& foos, double Foo::*selector)
{
  double r = 0.0;
  for (auto f : foos) r += f.*selector;
  return r;
}

void main()
{
  std::vector<Foo> foos = { Foo{1,2}, Foo{3,4}, Foo{5.5,6} };
  std::cout << sum(foos, &Foo::x) << '\n';
  std::cout << sum(foos, &Foo::y) << '\n';
}

Изобрази фукнцию sum на С.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

Изобрази фукнцию sum на С.

Не смог удержаться:

#include <stdio.h>
#include <stddef.h>

typedef struct Foo
    {
    double x;
    double y;
    } Foo_t;

double sum(Foo_t const *foos, int count, size_t selector)
    {
    double r = 0.0;

    while(count > 0)
        {
        r += *(double *)((char *)foos + selector);
        ++foos;
        --count;
        }

    return r;
    }

int main(void)
    {
    Foo_t foos[] = { {1,2}, {3,4}, {5.5,6} };

    printf("%g\n", sum(foos, sizeof(foos) / sizeof(*foos), offsetof(Foo_t, x)));
    printf("%g\n", sum(foos, sizeof(foos) / sizeof(*foos), offsetof(Foo_t, y)));

    return 0;
    }
cdslow ★★
()
Ответ на: комментарий от Begemoth

Ну это пацанский С, а не просто С, но всё же:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

typedef struct {
  double x, y;
} foo_t;

//members
#define offsetof(type, member)  (void *)__builtin_offsetof(type, member)
#define get_member(obj, selector) ({*(typeof(selector))((uintptr_t)selector + (intptr_t)(&obj));})
//members_end
//vec
#define vec(type) type##_vec_t
#define reg_vec_type(type) typedef struct {typeof(type) * begin, * end;} * vec(type);typedef struct {typeof(type) * begin, * end;} type##_vec_np_t;

reg_vec_type(void);

#define static_vec_construcotor(...) (typeof(__VA_ARGS__)[]){__VA_ARGS__}
#define vec_consr(...) ({\
  void * begin = static_vec_construcotor(__VA_ARGS__);\
  void * end = begin + sizeof(static_vec_construcotor(__VA_ARGS__));\
  &(void_vec_np_t){begin, end};\
})

#define vec_foreach(type, iter, vec) for(type * __iterator = vec->begin, * __iterator_end = vec->end, iter = *__iterator;__iterator < __iterator_end; ++__iterator, iter = *__iterator)
//vec_end
reg_vec_type(foo_t);
double sum(vec(foo_t) vec, double * selector) {
  double r = 0.;
  vec_foreach(foo_t, f, vec)
    r += get_member(f, selector);
  return r;
}

int main(void) {
  foo_t_vec_t vec = vec_consr((foo_t){1, 2}, (foo_t){3, 4}, (foo_t){5.5, 6});
  fprintf(stderr, "%f\n", sum(vec, offsetof(foo_t, x)));
  fprintf(stderr, "%f\n", sum(vec, offsetof(foo_t, y)));
}

Это пруфонконцепт на скорую руку, вроде работает. В твоих плюсах оффсетофы и гетмембер подставляет конпелятор.

Carb_blog2
()
Ответ на: комментарий от Begemoth

Изобрази фукнцию sum на С

Функции уже написали (у царя как обычно зачетный трешак), буду оригинален и напишу макросом:

#define SUM(FOO_PTR, FOO_FIELD, N, R) \
do {\
        size_t i;\
        R = 0.0;\
        for (i=0; i<N; i++) {\
                R += FOO_PTR[i].FOO_FIELD;\
        }\
} while(0);

/*...*/

/* main */
        n = sizeof(foos) / sizeof(foos[0]);

        SUM(foos, x, n, r)
        printf("x sum: %f\n", r);

        SUM(foos, y, n, r)
        printf("y sum: %f\n", r);
/*...*/
anonymous
()
Ответ на: комментарий от tailgunner

Это как раз хорошее, т.к. позволит использовать не только арифметические выражения, но и вообще ф-и произвольного вида.

anonymous
()
Ответ на: комментарий от Begemoth

view('foo_view',
{ 'name': «My field 1»,

Жесть как она есть. Ну и кто там вякал, что ему в питоне достаточно однострочных лямбд?

hint={
  "name1":function(a1,an){return(result1);}
  "name2":function(a1,an){return(result2);}
  "nameN":function(a1,an){return(resultN);}
}

for(['name2','nameN','name1'] in name){
  print hint[name](a1,a2);
}
anonymous
()
Ответ на: комментарий от anonymous

позволит использовать не только арифметические выражения, но и вообще ф-и произвольного вида.

Для задачи это и близко не нужно.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Ну и кто там вякал, что ему в питоне достаточно однострочных лямбд?

А зачем тут лямбды вообще и почему ты сделал именно однострочные? %)

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

возможно и понадобится какой-нить sqrt(foo*foo + bar*bar). но вопрос был скорее не про то, как распарсить, это не бог весть как сложно. хотелось узнать можно ли, описав грамматику языка, получить код на С каким-нибудь из имеющихся инструментов.

BreadFan ★★
() автор топика
Ответ на: комментарий от tailgunner

А зачем тут лямбды вообще

там в последней строчке написано зачем

ты сделал именно однострочные?

воу, стрелок пошутил, всем смеятся полчаса

anonymous
()
Ответ на: комментарий от anonymous

А зачем тут лямбды вообще

там в последней строчке написано зачем

В последней строчке там закрывающая скобка.

ты сделал именно однострочные?

воу, стрелок пошутил

Не-а, пошутил ты. А я немного посмеялся.

tailgunner ★★★★★
()
Ответ на: комментарий от BreadFan

хотелось узнать можно ли, описав грамматику языка, получить код на С каким-нибудь из имеющихся инструментов.

Абстрактный «код на Си» - можно, foo_view_get_by_{id,name} - нет, конечно. Их придется генерировать самому.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

есть ли какие-нибудь примеры того, как какой-то DSL превращается в С? знаю про protobuf, есть ли менее проблемно-ориентированное?

BreadFan ★★
() автор топика
Ответ на: комментарий от BreadFan

есть ли какие-нибудь примеры того, как какой-то DSL превращается в С?

Готового примера под рукой нет, а вообще - обход AST и печать Си-кода (который, возможно, обращается к какой-то базовой библиотеке).

Сходил в гугл, нарыл http://szelei.me/code-generator/ Для твоей задачи это жестокий оверкилл, но всё равно прикольно.

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 2)
Ответ на: комментарий от tailgunner

В последней строчке там закрывающая скобка.

а в предпоследней «anonymous (10.06.2014 12:13:49)»?

Не-а, пошутил ты. А я немного посмеялся.

Рад, что ты по прежнему сохраняешь здоровую самоиронию.

anonymous
()
Ответ на: комментарий от anonymous

А зачем тут лямбды вообще

там в последней строчке написано зачем

В последней строчке там закрывающая скобка

а в предпоследней «anonymous (10.06.2014 12:13:49)»?

Это всё объясняет!

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Это всё объясняет!

Ну наконец. Всего-то 6 лет тормозил. Ведь можешь, когда захочешь!

anonymous
()
Ответ на: комментарий от Begemoth

Тем, что в С нет такого понятия как «указатель на поле foo структуры bar» (&bar::foo).

Ты никогда не слышал про offsetof?

mix_mix ★★★★★
()
Ответ на: комментарий от Begemoth

А что не так с типобезопасностью? Ты знаешь sizeof структуры, ты знаешь sizeof типа и его offsetof относительно начала структуры, больше ничего не нужно.

mix_mix ★★★★★
()
Ответ на: комментарий от mix_mix

Значит у нас разное понимание типобезопасности. Типобезопасность включает проверку компилятором, а не принятие им на веру твоих действий. Если ты накосячишь со смещениями или типами полей, компилятор не поможет тебе найти ошибку. В приведенном же мною коде на С++ это не так.

Begemoth ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.