2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
1

C++ vs Common Lisp: кодогенерация, метапрограммирование

25.03.2017, 11:14. Показов 4858. Ответов 64

Дано описание произвольного математических выражений на XML вида:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<calc>
  <mul>
    <var>k</var>
    <plus>
      <var>foo</var>
      <var>bar</var>
      <var>baz</var>
      <mul>
        <raw>100</raw>
        <var>xyz</var>
      </mul>
    </plus>
  </mul>
</calc>
Для простоты положим, что у нас есть только сложение и умножение. var - символьное название переменной, raw - литерал целого числа.

Задача: в compile time сгенерировать функцию, аргументами которой будут все var имена переменных из xml. Функция должна вычислять выражение описанное в xml.
Такой простой eDSL.

У нас есть 2 стула: Common Lisp и "всемогущий" C++. Я сажусь на первый стул.

Беру первый попавшийся xml parser - xmls.
Долблю на коленке кодогенератор.
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(eval-when (:compile-toplevel :load-toplevel :execute)
  (defparameter *op-table*
    '(("mul"  . *)
      ("plus" . +)
      ("raw"  . raw)
      ("var"  . var)))
 
  (defparameter *var-names* nil)
  
  (defun genexpr (x)
    (typecase x
      (cons
       (let ((op (cdr (assoc (car x) *op-table* :test 'string=))))
         (case op
           (raw (parse-integer (third x)))
           (var (let ((s (intern (string-upcase (third x)))))
                  (push s *var-names*)
                  s))
           (otherwise `(,op ,@(mapcar #'genexpr (cddr x)))))))
      (string
       (parse-integer x)))))
Пишу макрос define-xml-function на вход принимающий xml строчный литерал.
Lisp
1
2
3
4
5
(defmacro define-xml-function (name xml-string)
  (let (*var-names*)
    (let ((body (genexpr (third (xmls:parse xml-string)))))
      `(defun ,name (&key ,@*var-names*)
         ,body))))
Всё. Десяток строк, пол часа времени с перекурами - eDSL готов.

Использую.
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(define-xml-function foo
 "
<calc>
  <mul>
    <var>k</var>
    <plus>
      <var>foo</var>
      <var>bar</var>
      <var>baz</var>
      <mul>
        <raw>100</raw>
        <var>xyz</var>
      </mul>
    </plus>
  </mul>
</calc>
 ")
 
(foo :k 2 :foo 100 :bar 200 :baz 300 :xyz 4)
;; 2000
Приведеный пример define-xml-function раскрывается в:
Lisp
1
2
(DEFUN FOO (&KEY XYZ BAZ BAR FOO K)
  (* K (+ FOO BAR BAZ (* 100 XYZ))))
define-xml-function в compile time генерирует lisp функцию, которая в свою очередь компилируется в native.

Но подождите? Ведь есть второй кошерный стул - кресты. Темплейты, туда сюда. Что же мне делать? Как решить задачу?

Добавлено через 2 минуты
hoggy, есть чё на лурке по этому поводу?

Добавлено через 5 часов 28 минут
Цитата Сообщение от nullxdth Посмотреть сообщение
Долблю на коленке кодогенератор
Фикс. Лишка написал.
Lisp
1
2
3
4
5
6
7
8
9
(defun genexpr (x)
  (when x
    (let ((op (cdr (assoc (car x) *op-table* :test 'string=))))
      (case op
        (raw (parse-integer (third x)))
        (var (let ((s (intern (string-upcase (third x)))))
               (push s *var-names*)
               s))
        (otherwise `(,op ,@(mapcar #'genexpr (cddr x))))))))
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
25.03.2017, 11:14
Ответы с готовыми решениями:

Common Lisp vs D
Может быть, попробуем затеять холиварчик. Чтобы было менее честно, можно противопоставлять языку D...

Common Lisp vs D (2)
Продолжение темы.

Книги или другой источник, где описана история версий Lisp и Common Lisp
Доброго времени суток.Такой вопрос,знаете какой-нибудь источник,где описана история версий Lisp и...

Common Lisp
Подскажите пожалуйста, а может Common Lisp формировать списки из функций, и (если вдруг может) как?

64
Игогошка!
1801 / 708 / 44
Регистрация: 19.08.2012
Сообщений: 1,367
25.03.2017, 16:49 2
nullxdth, отлично, давай чуть обобщим задачку на то, как бы она выглядела в реальном мире.

Например - значения некоторых переменных лежат в Cassandra, а xml содержит алиасы вложенных объектов-выражений, которые приходят в очереди кластера RabbitMQ (ну или Kafka). Естественно, нужно поддерживать все адекватные фичи, относящиеся как минимум к высокодоступности и отказоустойчивости. Ну и например, если выражение большое, мы еще должны спрашивать по каким-то параметрам (фазы луны?) у внешнего сервиса (который использует grpc на http2 и protobuf3), нужно ли нам это действительно считать.
Код писать не надо, просто изменится ли принципиально текущий код, а также какие либы нужно будет заюзать и почему именно их.
0
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
25.03.2017, 20:07  [ТС] 3
Цитата Сообщение от ct0r Посмотреть сообщение
отлично, давай чуть обобщим задачку на то, как бы она выглядела в реальном мире
Да без проблем. Но прежде чем строить космический корабль, следует убедиться, что мы справляемся с элементарными вещами. Есть чётко поставленная простая задача на кодогенерацию, как только появится альтернативная реализация на С++, так сразу и будем расширять условия и ставить новые задачи. Если кто-то хочет написать на чём-то другом, это только приветствуется.

Добавлено через 1 минуту
Цитата Сообщение от ct0r Посмотреть сообщение
Например - значения некоторых переменных лежат в Cassandra, а xml содержит алиасы вложенных объектов-выражений, которые приходят в очереди кластера RabbitMQ (ну или Kafka). Естественно, нужно поддерживать все адекватные фичи, относящиеся как минимум к высокодоступности и отказоустойчивости. Ну и например, если выражение большое, мы еще должны спрашивать по каким-то параметрам (фазы луны?) у внешнего сервиса (который использует grpc на http2 и protobuf3), нужно ли нам это действительно считать.
Код писать не надо, просто изменится ли принципиально текущий код, а также какие либы нужно будет заюзать и почему именно их.
Это всё очень круто. Но я пока не вижу решения поставленной элементарной задачи.
0
710 / 282 / 16
Регистрация: 31.03.2013
Сообщений: 1,340
25.03.2017, 20:20 4
Цитата Сообщение от nullxdth Посмотреть сообщение
Ведь есть второй кошерный стул - кресты. Темплейты, туда сюда. Что же мне делать? Как решить задачу?
Да вообще элементарно, по xml представлению можно прям в рантайме нагенерить машинный код функции ( asmjit, gccjit, luajit, вот это все ).
0
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
25.03.2017, 20:40  [ТС] 5
Цитата Сообщение от Voivoid Посмотреть сообщение
Да вообще элементарно, по xml представлению можно прям в рантайме нагенерить машинный код функции ( asmjit, gccjit, luajit, вот это все ).
Давай код.

Добавлено через 11 минут
Цитата Сообщение от Voivoid Посмотреть сообщение
Да вообще элементарно, по xml представлению можно прям в рантайме нагенерить машинный код функции ( asmjit, gccjit, luajit, вот это все ).
И кстати, какое это отношение вообще к крестам имеет? Но да фиг с ним, напиши примерчик решающий задачу, если это так просто. Чёрт с ним, что если даже там крестов не будет чуть более чем полностью.
0
710 / 282 / 16
Регистрация: 31.03.2013
Сообщений: 1,340
26.03.2017, 00:22 6
Цитата Сообщение от nullxdth Посмотреть сообщение
И кстати, какое это отношение вообще к крестам имеет?
Дык, это плюсовые библиотеки

Цитата Сообщение от nullxdth Посмотреть сообщение
Давай код.
Писну, если не поленюсь

Добавлено через 2 часа 11 минут
Цитата Сообщение от nullxdth Посмотреть сообщение
Давай код.
А, хотя тебе compile-time надо, ну тогда легче на haskell'е написать генератор плюсового кода Но если очень хочется, то на плюсовых constexpr'ах можно че-нить написать, но это конечно нетривиально будет
0
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
26.03.2017, 00:35  [ТС] 7
Цитата Сообщение от Voivoid Посмотреть сообщение
А, хотя тебе compile-time
Цитата Сообщение от Voivoid Посмотреть сообщение
по xml представлению можно прям в рантайме нагенерить машинный код функции
Дык, если ты нагенеришь и сможешь её вызывать, пусть даже генерация будет происходить в runtime, она будет происходить один раз, на сколько я понимаю, поэтому это тоже считается за ответ.
0
Эксперт Java
3994 / 2599 / 470
Регистрация: 28.04.2012
Сообщений: 8,381
26.03.2017, 01:42 8
Цитата Сообщение от Voivoid Посмотреть сообщение
Но если очень хочется, то на плюсовых constexpr'ах можно че-нить написать
Там разве можно в IO?
0
4814 / 2275 / 287
Регистрация: 01.03.2013
Сообщений: 5,933
Записей в блоге: 26
26.03.2017, 02:00 9
Цитата Сообщение от nullxdth Посмотреть сообщение
Если кто-то хочет написать на чём-то другом, это только приветствуется.
Цитата Сообщение от nullxdth Посмотреть сообщение
Дык, если ты нагенеришь и сможешь её вызывать, пусть даже генерация будет происходить в runtime, она будет происходить один раз, на сколько я понимаю, поэтому это тоже считается за ответ.
Чё, правда?
Тогда я тоже приму участие в регате - вручную, без единого гвоздя и безо всяких парсеров и библиотек. Для начала тройка утилитарных однострочных макросов/функций, с тривиальной семантикой:
Lisp
1
2
3
4
5
(defn is-open-tag? (t) eq? "<" (java (++ t) "substring" 0 1))
 
(defn close-tag (t) ++ "</" (java (++ t) "substring" 1))
 
(defn get-op (t) cond (eq? t '<mul>) '* (eq? t '<plus>) '+ nil)
Далее пишем простейший парсер из линейного списка токенов в иерархичный список = тело функции:
Lisp
1
2
3
4
5
6
7
8
9
10
(defn parse (l)
    cond (null? l) nil
         (is-open-tag? (car l))
             (
              (match (span (lambda (x) eq? x (close-tag (car l))) (cdr l)) '(a b))
              (def op (get-op (car l)))
              (cond (null? op) (parse a)
                    (cons (cons op (parse a)) (parse (cdr b)) ))
             )
         (cons (car l) (parse (cdr l))) )
Вызываем его на тестовом примере, смотрим результат:
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(def f '(
<calc>
  <mul>
    <var> k </var>
    <plus>
      <var> foo </var>
      <var> bar </var>
      <var> baz </var>
      <mul>
        <raw> 100 </raw>
        <var> xyz </var>
      </mul>
    </plus>
  </mul>
</calc>
))
=> OK
f
=> (<calc> <mul> <var> k </var> <plus> <var> foo </var> <var> bar </var> <var> baz </var> <mul> <raw> 100 </raw> <var> xyz </var> </mul> </plus> </mul> </calc>)
parse f
=> ((* k (+ foo bar baz (* 100 xyz))))
Тут конечно надо сказать, что в моем Лискрипте до сих пор мне не требовалась функция чтения строки в список, но ее тривиально добавить в ядро интерпретатора. А уж заменить тэги, чтобы знаки < и > отделялись пробелами от рядом стоящих токенов - это и сейчас однострочник с вызовом джаво-стрингового метода. Но вернемся к нашим баранам - у нас уже есть готовое тело функции. Осталось только выделить из него символы - выпрямим тело в линейный список (flatten), уберем из него дубли (nub) - чтобы многократные вхождения имен были в списке аргументов только единожды, и мы получим список аргументов. Составим список - особая форма lambda, список аргументов и тело функции - и вычислим этот список (eval):
Lisp
1
2
3
(defn make-f (f)
    (def p (car (parse f)))
    (eval (cons lambda (filter symbol? (nub (flatten p))) p) ))
Вот и все! Проверим:
Lisp
1
2
3
4
5
6
7
8
def func (make-f f)
=> OK
func
=> (lambda (k foo bar baz xyz) (* k (+ foo bar baz (* 100 xyz))))
func 2 100 200 300 4
=> 2000
func 3 10 20 30 4
=> 1380
Собственно, все Безо всяких библиотек, парсеров/шмарсеров и т.п. Правда, для Кассандры/Кролика/Кафки придется джавалибы подключать.
0
906 / 663 / 318
Регистрация: 23.10.2016
Сообщений: 1,538
26.03.2017, 03:04 10
Ни на чё не претендую, просто интересно было реализовать
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
string xml = @"
  <mul>
    <var>k</var>
    <plus>
      <var>foo</var>
      <var>bar</var>
      <var>baz</var>
      <mul>
        <raw>100</raw>
        <var>xyz</var>
      </mul>
    </plus>
  </mul>
";
 
class XmlCompiler
{
    private List<ParameterExpression> _params;
    private Delegate _delegate;
    
    private Expression Raw(XNode[] nodes)
    {
        return Expression.Constant(double.Parse(nodes.Cast<XText>().Single().Value));
    }
    
    private Expression Var(XNode[] nodes)
    {
        string name = nodes.Cast<XText>().Single().Value;
        
        ParameterExpression existing = _params.FirstOrDefault(p => p.Name == name);
        
        if (existing != null)
            return existing;
            
        _params.Add(Expression.Parameter(typeof(double), name));
        
        return _params.Last();
    }
    
    private Expression Plus(XNode[] nodes)
    {
        return nodes
            .Select(Parse)
            .Aggregate((res, e) => Expression.Add(res, e));
    }
    
    private Expression Mul(XNode[] nodes)
    {
        return nodes
            .Select(Parse)
            .Aggregate((res, e) => Expression.Multiply(res, e));
    }
 
    private Expression Parse(XNode root)
    {
        string name = ((XElement)root).Name.LocalName;
        
        var builder = this
            .GetType()
            .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
            .First(mi => mi.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
            
        var nodes = ((XElement)root)
            .Nodes()
            .ToArray();
 
        return (Expression)builder.Invoke(this, new object[] { nodes });
    }
 
    public XmlCompiler(string xml)
    {
        _params = new List<ParameterExpression>();
        _delegate = Expression.Lambda(Parse(XElement.Parse(xml)), _params).Compile();
    }
    
    public IEnumerable<string> Params => _params.Select(p => p.Name);
    
    public Delegate Delegate => _delegate;
}
 
void Main()
{
    new XmlCompiler(xml).Delegate.DynamicInvoke(new object[] { 2, 2, 3, 4, 5 }).Dump(); // 1018
}
0
4814 / 2275 / 287
Регистрация: 01.03.2013
Сообщений: 5,933
Записей в блоге: 26
26.03.2017, 05:23 11
Продолжение регаты - накостылил чтение из строки в конс-список (тип данных моего языка) через джавовские методы, осталось впилить в ядро полезную функцию чтения типа из строки - и все будет работать еще проще Причем, все эти методы просятся в стандартную библиотеку, так что в финальном коде будет всего несколько строчек.
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
(def str "
<calc>
  <mul>
    <var>k</var>
    <plus>
      <var>foo</var>
      <var>bar</var>
      <var>baz</var>
      <mul>
        <raw>100</raw>
        <var>xyz</var>
      </mul>
    </plus>
  </mul>
</calc>
")
 
(defn replace-tokens (t)
    cond (eq? t "") ""
         (eq? t "<mul>") "(*"
         (eq? t "</mul>") ")"
         (eq? t "<plus>") "(+"
         (eq? t "</plus>") ")"
         (eq? "<" (java (++ t) "substring" 0 1)) " "
         t)
 
(defn array-to-cons-list (a)
    (def l (java (class "java.util.LinkedList") "new"
        (java (class "java.util.Arrays") "asList" a) ) size (java l "size"))
    (defn go (i) cond (>= i size) nil (cons (java l "get" i) (go (+ 1 i))))
    (go 0))
 
(defn string-split (s) array-to-cons-list (java (++ s) "split" "\s+"))
 
(defn prepare (s) java (java s "replaceAll" ">" "> ") "replaceAll" "<" " <")
 
(foldr ++ "" (map replace-tokens (string-split (prepare str))))
 
=>  (* k (+ foo  bar  baz (* 100  xyz )))
Добавлено через 19 минут
ЗЫ кстати, раз уж пошла такая регата, а в эту тему могут заглядывать знатоки лиспов - как мне назвать встроенную функцию языка, которая принимает строку, а возвращает распарсенное значение? Я бы назвал ее read, но у меня уже есть такая функция, и она делает ровно то же самое, только берет входную строку не из переданного параметра, а из блокирующего пользовательского ввода, и уже есть некоторая кодовая база, которую хочется оставить работоспособной. Я конечно понимаю, что с самого начала не заботился вопросами совместимости с распространенными диалектами Лиспов, и теперь уже поздно пить Боржоми (хотя какой-никакой транслятор в Scheme все-таки накостылен) Что насчет read-string?

Добавлено через 53 минуты
Собственно, зафигачено Теперь все работает полностью как надо безо всяких натяжек. И теперь можно признаться, что в первой версии ручного велосипедного парсера есть принципиальная ошибка, но чем ее устранять, проще воспользоваться стандартным парсером ядра
Lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(defn make-f (s)
    (def p (read-string (foldr ++ "" (map replace-tokens (string-split (prepare s))))))
    (eval (cons lambda (filter symbol? (nub (flatten p))) p) ))
 
(def func (make-f
"
<calc>
  <mul>
    <var>k</var>
    <plus>
      <var>foo</var>
      <var>bar</var>
      <var>baz</var>
      <mul>
        <raw>100</raw>
        <var>xyz</var>
      </mul>
    </plus>
  </mul>
</calc>
"))
=> OK
func
=> (lambda (k foo bar baz xyz) (* k (+ foo bar baz (* 100 xyz))))
func 2 100 200 300 4
=> 2000
func 3 10 20 30 4
=> 1380
0
117 / 53 / 2
Регистрация: 12.02.2017
Сообщений: 194
26.03.2017, 10:18 12
Цитата Сообщение от _Ivana Посмотреть сообщение
Тут конечно надо сказать, что в моем Лискрипте до сих пор мне не требовалась функция чтения строки в список, но ее тривиально добавить в ядро интерпретатора
imho, не стоит захламлять фичами сомнительной необходимости.
0
Эксперт Java
3994 / 2599 / 470
Регистрация: 28.04.2012
Сообщений: 8,381
26.03.2017, 11:18 13
Цитата Сообщение от _Ivana Посмотреть сообщение
ЗЫ кстати, раз уж пошла такая регата, а в эту тему могут заглядывать знатоки лиспов - как мне назвать встроенную функцию языка, которая принимает строку, а возвращает распарсенное значение? Я бы назвал ее read, но у меня уже есть такая функция, и она делает ровно то же самое, только берет входную строку не из переданного параметра, а из блокирующего пользовательского ввода, и уже есть некоторая кодовая база, которую хочется оставить работоспособной. Я конечно понимаю, что с самого начала не заботился вопросами совместимости с распространенными диалектами Лиспов, и теперь уже поздно пить Боржоми (хотя какой-никакой транслятор в Scheme все-таки накостылен) Что насчет read-string?
И почему было просто не использовать ByteArrayInputStream?
0
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
26.03.2017, 11:44  [ТС] 14
Цитата Сообщение от _Ivana Посмотреть сообщение
Что насчет read-string?
Название больше говорит о том, что мы читаем строку (читаем из потока объект с проверкой на строчный литарал, например), а не из строки. Можно назвать как в CL: read-from-string. Ну это дело такое, личное
0
710 / 282 / 16
Регистрация: 31.03.2013
Сообщений: 1,340
26.03.2017, 17:09 15
Цитата Сообщение от nullxdth Посмотреть сообщение
Дык, если ты нагенеришь и сможешь её вызывать, пусть даже генерация будет происходить в runtime, она будет происходить один раз, на сколько я понимаю, поэтому это тоже считается за ответ.
Без JIT'а:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <numeric>
#include <cassert>
 
#include <xml/parser> // [url]http://www.codesynthesis.com/projects/libstudxml/[/url]
 
using ValType = int;
using VarType = std::string;
using OpType = std::string;
using Env = std::map<VarType, ValType>;
using Op = std::function<ValType(ValType, ValType)>;
using MathExpr = std::function <ValType(const Env&)>;
 
constexpr const char* CalcElement = "calc";
constexpr const char* MulElement = "mul";
constexpr const char* PlusElement = "plus";
constexpr const char* VarElement = "var";
constexpr const char* RawElement = "raw";
 
static const std::map<OpType, Op> MathOps = {
    {PlusElement, std::plus<ValType>()},
    {MulElement, std::multiplies<ValType>()}
};
 
 
MathExpr makeRaw(const ValType val) {
    return [val](const Env&) {
        return val;
    };
}
 
MathExpr makeVar(const VarType& var)
{
    return [var](const Env& env) {
        return env.at(var);
    };
}
 
MathExpr makeOp(const OpType& opStr, std::vector<MathExpr> exprs)
{
    assert(exprs.size() >= 2);
 
    const auto& op = MathOps.at(opStr);
 
    return [&op, exprs = std::move(exprs)](const Env& env) {
        const auto init = exprs[0](env);
        return std::accumulate(exprs.cbegin() + 1, exprs.cend(), init, [&op, &env](int acc, const auto& f){
            return op(acc, f(env));
        });
    };
}
 
MathExpr parse(xml::parser& parser)
{
    MathExpr f;
 
    parser.next_expect(xml::parser::start_element);
 
    if(parser.name() == RawElement)
    {
        parser.next_expect(xml::parser::characters);
        f = makeRaw(std::stoi(parser.value()));
    }
    else if(parser.name() == VarElement)
    {
        parser.next_expect(xml::parser::characters);
        f = makeVar(parser.value());
    }
    else
    {
        parser.content(xml::content::complex);
        const auto op = parser.name();
 
        std::vector<MathExpr> exprs;
        while(parser.peek() != xml::parser::end_element)
        {
            exprs.push_back(parse(parser));
        }
 
        f = makeOp(op, std::move(exprs));
    }
 
    parser.next_expect(xml::parser::end_element);
 
    return f;
}
 
MathExpr parse(const std::string& xml)
{
    xml::parser parser(xml.data(), xml.size(), "FuncParser");
    parser.next_expect(xml::parser::start_element, CalcElement, xml::content::complex);
 
    auto f = parse(parser);
 
    parser.next_expect(xml::parser::end_element, CalcElement);
    return f;
}
 
int main() try {
    const std::string xml = R"(
        <calc>
            <mul>
                <var>k</var>
                <plus>
                    <var>foo</var>
                    <var>bar</var>
                    <var>baz</var>
                    <mul>
                        <raw>100</raw>
                        <var>xyz</var>
                    </mul>
                </plus>
            </mul>
        </calc>
    )";
 
    const auto f = parse(xml);
 
    const auto result = f({
        {"k", 2},
        {"foo", 100},
        {"bar", 200},
        {"baz", 300},
        {"xyz", 4}
    });
 
    assert(result == 2000);
 
    return 0;
}
catch(const std::exception& ex)
{
    std::cout << "Unhandled exception: " << ex.what();
}
catch(...)
{
    std::cout << "Unhandled unknown exception";
}
C JIT'ом тоже потом напишу

Добавлено через 14 минут
0
4814 / 2275 / 287
Регистрация: 01.03.2013
Сообщений: 5,933
Записей в блоге: 26
26.03.2017, 17:48 16
Спасибо всем ответившим.
Цитата Сообщение от _JohnSmith Посмотреть сообщение
imho, не стоит захламлять фичами сомнительной необходимости.
Ну как сомнительными - до сих пор во всех ранее встреченных задачах мне это не требовалось. А конкретно в этой оказалось будет очень удобно, к тому же имхо полезная возможность - вытащить парсер на уровень языка без запроса ввода. Можно любые строки собирать/клеить и потом в объекты языка переводить.

korvin_, наверное можно было бы, но я пока не представляю как Сейчас у меня в вычислитель передается объект, реализующий интерфейс InOutable, с методами In и Out, через которые вычислитель общается с текстовым внешним миром. А как конкретный объект реализует эти методы - его личное дело. Консольный вариант - System.in / out, гуевый - по-своему, ботовый - по-своему. А чтение-парсинг объекта из строки не привязано к вводу/выводу, и живет внутри эвалюатора.

nullxdth, то-что-надо, я подозревал, что в каких-то диалектах обязательно должна быть такая функция, значит ввод у меня будет отчасти по Common-Lisp-овому Тем более что с read из потока я угадал. Только вычислять длину строки и передавать аргументами позиции начала и конца парсинга - вот это уже я думаю будут именно сомнительные фичи, как говорил оратор выше. Это я и чисто строковыми функциями (с подкапотным вызовом джавовских методов) могу предварительно и отдельно делать.

Добавлено через 5 минут
ЗЫ у меня сейчас эта функция чтения уже сделана полиарной - если ей передана одна строка, то возвращается один прочитанный объект, если несколько - то она читает каждую и возвращает список прочитанных объектов. Это продолжает выбранную философию моих call-conventions, когда все особые формы, какие можно логично и непротиворечиво сделать полиарными (начиная с def / cons и заканчивая read / print), сделаны полиарными.
0
Эксперт Java
3994 / 2599 / 470
Регистрация: 28.04.2012
Сообщений: 8,381
26.03.2017, 18:29 17
Цитата Сообщение от _Ivana Посмотреть сообщение
Сейчас у меня в вычислитель передается объект, реализующий интерфейс InOutable, с методами In и Out
Зачем Out для чтения? Всегда делается два отдельных интерфейса для ввода и вывода (чтения и записи), и объединяющий интерфейс, если вдруг оно понадобится.

Вот и реализуй для строки Inable.
0
4814 / 2275 / 287
Регистрация: 01.03.2013
Сообщений: 5,933
Записей в блоге: 26
26.03.2017, 18:44 18
Цитата Сообщение от korvin_ Посмотреть сообщение
Вот и реализуй для строки Inable.
Спасибо, понял идею Вообще-то можно и общий интерфейс для строки реализовать, а в качестве Out просто конкатенировать в хвост. Но придется немного переделывать вызов - сейчас у меня нет в качестве входящего параметра print/read "дескриптора объекта чтения/записи", он берется по умолчанию один на текущее вычисление. А если сделать его явно указываемым, тогда можно будет выбирать куда писать/откуда читать в каждом конкретном вызове - клавиатура/экран, бот чат-канала, файл на диске или собственно строка. Не знаю, буду ли в ближайшее время это пилить, но фича интересная, да.
0
2304 / 1063 / 77
Регистрация: 12.03.2013
Сообщений: 4,987
27.03.2017, 11:31  [ТС] 19
Цитата Сообщение от Voivoid Посмотреть сообщение
C++
1
2
3
4
5
6
7
const auto result = f({
    {"k", 2},
    {"foo", 100},
    {"bar", 200},
    {"baz", 300},
    {"xyz", 4}
});
А можно, что бы вычисление f происходило в runtime?

Добавлено через 1 минуту
TopLayer, спасибо. Попробую разобраться.
0
710 / 282 / 16
Регистрация: 31.03.2013
Сообщений: 1,340
27.03.2017, 12:36 20
Цитата Сообщение от nullxdth Посмотреть сообщение
А можно, что бы вычисление f происходило в runtime?
Так оно и происходит в runtime
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
27.03.2017, 12:36
Помогаю со студенческими работами здесь

Common lisp
Есть фрагмент программы: (defun game(x y) (cond (( &lt; x y)(print &quot;menshe&quot;)(setq y (read))) (( &gt;...

Common lisp
кто поможет решить эти задачи? Перечитайте правила форума. Один вопрос - одна тема. Заголовок...

ООП Common Lisp
Помогите пожалуйста! Реализовать класс: Программа (название, тип, сфера применения, язык...

Вопрос по common lisp
Подскажите пожалуйста как выполнить лисп-программу из txt или какого другого файла?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru