LINUX.ORG.RU

Синтаксис и LISP'ы

 , ,


0

1

Часто можно наткнуться на мнение, что у лиспа(ов) нет синтаксиса. Хотелось бы понять, что конкретно тут имеется в виду.

Во-первых, синтаксическая запись s-expr'ов так же является некоторым языком, со своими синтаксическими правилами и возможностью описания посредством грамматики.

Во-вторых, различные диалекты лиспа вводят специальные дополнительные синтаксические конструкции, вроде [ и ] в clojure или typed racket.

Имеется в виду, что этот синтаксис прост и фактически без особых изменений определяет AST?


Ответ на: комментарий от monk

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

И что? Значит если ты можешь написать парсер, который имеет доступ к ф-ям языка, значит ты вводишь новый синтаксис? Глупость какая-то.

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

И не токенов, а лексем

Для особо одаренных, список токенов для этого кода будет '("(" «loop» «for» «i» «from» ... ")"). Это результат работы лексера (обрати внимание на то, что скобочки являются токенами). После лексера отрабатывает парсер, на выходе парсера - АСТ: '(loop for i from ...)

АСТ потом идет на вход компилятору. Что тебе тут не ясно, объясни?

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

Каждый элемент списка - лексема. (FORMAT T «~D, » (FIBONACCI I)) целиком для loop лексема, он её не смотрит внутрь, а использует как есть.

(FORMAT T «~D, » (FIBONACCI I)) ... лекема

Ребят, расходимся, этому барану ничего объяснить не выйдет.

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

если ты можешь написать парсер, который имеет доступ к ф-ям языка, значит ты вводишь новый синтаксис

Если ты можешь написать парсер, значит ты вводишь новый синтаксис.

Для сишки можно вспомнить первые компиляторы С++, которые компилировали исходник на C++ в C. И С++ по отношению к С — определённо, новый синтаксис.

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

Например, Honu реализуется исключительно штатными средствами Lisp и выглядит как

for x = 1 to 10 do {
 var y = x + 1;
 printf("x ~a y ~a\n", x, y)
}

Для не-лиспов для каждого синтаксиса приходится писать отдельную программу-компилятор.

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

Если ты можешь написать парсер, значит ты вводишь новый синтаксис.

Тогда получается в любой языке вводится новый синтаксис. В итоге это бессодержательное понятие.

Например, Honu реализуется исключительно штатными средствами Lisp и выглядит как

Ты опять путаешь макросы и изменение синтаксиса. Фикс ридера меняет синтаксис (т.к. меняет правила построения АСТ). Макросы - не меняют (т.к. не меняет правила построения АСТ). А синтаксис - это, по определению, правила построения АСТ.

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

Ты путаешь синтаксис языка программирования с «синтаксисом» твоего edsl, который ты реализовал тем или иным способом.

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

синтаксис - это, по определению, правила построения АСТ

Нет. Синтаксис — множество возможных форм АСТ в языке.

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

Ты Немерлё видел? А оно совсем не лишпег.

Видел. Хорошая попытка скрестить достоинства лиспа (настраиваемый компилятор) с синтаксисом Си. Ещё Scala есть.

Но что-то сложнее простой подстановки (например аналог iterate) делать намного сложнее, чем в Lisp

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

«Сложнее» это субъетивщина. Не-инопланетянам проще. Посмотри еще на Converge, TH, или тот же Katahdin. Не нужна педоиконность для макросов.

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

Откуда ты это взял? Синтаксис описывается грамматикой и как раз задает правила построения AST. А что ты потом будешь со своими AST делать(в том числе и перекурочивать их своими макросами) - дело десятое.

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

Синтаксис описывается грамматикой и как раз задает правила построения AST

Нет. AST — это всего лишь дерево синтаксических объектов (лексем).

А синтаксис описывает, что цикл for в Си обязан иметь ключевое слово for, затем в скобках три выражения разделённых ";", затем выражение.

Причём грамматика для Си как раз включает все ключевые слова и их синтаксис.

И поэтому синтаксический анализ программы на Лисп невозможен без частичного выполнения программы (выполнения раскрытия макросов).

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

Еще раз. Откуда ты это взял? Это противоречит той же книге дракона. Это противоречит википедии. Это противоречит всему, что показывают на первых страницах гугла.

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

А синтаксис описывает, что цикл for в Си обязан иметь ключевое слово for, затем в скобках три выражения разделённых ";", затем выражение.

Это и есть правила грамматики. Но помимо этого они задают приоритеты и прочие вещи, которые однозначно определяют AST. Результатом работы синтаксического анализатора является AST. В случае лиспа после этого ты имеешь все формы. Далее ты уже преобразовываешь AST - это семантический анализ. Семантический. Понимаешь? И раскрытие макросов, как функций над AST, является этапом семантического анализа. Никакого синтаксиса, никаких грамматических правил тут уже нет(кроме тех, что придумал макрос сам себе - но это его решение, работает-то он все равно с AST).

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

Не-инопланетянам проще. Посмотри еще на Converge, TH, или тот же Katahdin. Не нужна педоиконность для макросов.

Ну попробуй на любом из них сделать макрос, который будет понимать конструкцию вида «{ cout >> _1 >> _2; return _2}» с поправкой на синтаксис языка. Должен превращать эту конструкцию в определение функции с нужным количеством параметров и подставлять параметры в нужные места.

На лиспе пишется буквально в несколько строк.

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

Нет. Синтаксис — множество возможных форм АСТ в языке.

Которые описываются правилами построения АСТ (грамматикой) на практике.

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

«Сложнее» это субъетивщина.

Нет, объективщина. Любой нетривиальный макрос в языке с негомоиконным синтаксис превращается в адъ и содомию. И чем более сложен макрос - тем больше разница.

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

Он просит сделать компактный синтаксис для лямбд. Типа:

«{ cout >> _1 >> _2; return _2}» => "( lambda _1, _2: cout >> _1 >> _2; return _2 )"

ну я так понял

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

На лиспе пишется буквально в несколько строк.

Ну на самом деле не совсем так все просто - надо квазицитирование по-хорошему отдельно обрабатывать

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

Ну и вообще не совсем понятно что делать в следующих случаях:

(define-syntax-rule (yoba x) 0)
(define-syntax-rule (yoba2) _1)
{yoba _1}
{yoba2}
как должнаы раскрываться?

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

не совсем понятно что делать в следующих случаях

{...} раскрывается первым (как макрос внешнего уровня).

{yoba _1} -> (lambda (x) (yoba x)) -> (lambda (x) 0)
{yoba2} -> (lambda () (yoba2)) -> (lambda () _1)

Аналог без нумерации: http://srfi.schemers.org/srfi-26/srfi-26.html

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

а, ну так да, можно банальным кодеволкером

#lang racket
(require (for-syntax syntax/parse)
         syntax/parse/define)

(begin-for-syntax
  (define-syntax-class rec-expr
    (pattern ((~literal cut) e: expr ...)
             #:with (id ...) #'())
    (pattern (x:rec-expr ...)
             #:with (id ...) #'(x.id ... ...))
    (pattern y:id
             #:when (char=? #\_
                            (string-ref (symbol->string (syntax->datum #'y)) 0))
             #:with (id ...) #'(y))
    (pattern y
             #:with (id ...) #'())))

(define-simple-macro (cut e:rec-expr ...+)
  (λ (e.id ... ...) e ...))

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

просит сделать компактный синтаксис для лямбд

Верно. Как http://www.boost.org/doc/libs/1_56_0/doc/html/lambda/le_in_details.html

В boost для этого запилили огромную портянку с ручным перебором вроде до _9. А в языке с нормальными макросами можно делать с произвольным количеством и в несколько строк.

Вот пример на Common Lisp с полной документацией и обработкой ошибочных ситуаций: https://www.common-lisp.net/project/cl-syntax-sugar/darcs/cl-syntax-sugar/src...

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

Но, конечно, возможность сломать все еще есть - если определить (define-syntax non-cut (syntax-local-value cut)), а потом поместить вызов макроса non-cut внутрь cut (или наоборот) то внутренние переменные будут захвачены внутренним и внешним макросом.

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

можно банальным кодеволкером

Так вот я как раз про то, что в макросе на не-лиспе кодеволкер — совсем не банальная вещь.

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

и квазицитирование там, по-моему, не обрабатывается отдельно? Кстати из-за квазицитирования и того что можно (define-syntax-rule (yoba x) 'x) какбы фейл.

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

вариант с переопределением по схеме (define-syntax non-cut ...) в любом случае сломается если макросы не раскрывать, т.к. в отличии от теста по биндингам, когда мы rename-in/rename-out делаем, тут экспандер никак не сможет узнать, что cut=non-cut. опять же может быть (define-syntax-rule (yoba args ...) (cur args ...))

Чтобы это предотвратить надо полностью раскрывать все макры.

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

а, ну тут-то и кодеволкера нет, потому как имена аргументов передаются явно :hz:

а, там какой-то костыльный cut, который в большинстве случаев не работает.

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

Так вот я как раз про то, что в макросе на не-лиспе кодеволкер — совсем не банальная вещь.

Это зависит от представления АСТа уже. Если АСТ представляется юниморфно (node-type args ...) то проблем особо нет. Но не думаю что оно так. В том же TH, полагаю, придется перебирать все синтаксические формы, подряд.

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

В том же TH, полагаю, придется перебирать все синтаксические формы, подряд

Вот и я про то же. В Scala вообще кошмар. «x < 10» транслируется в

Apply(
  Select(Ident(newTermName("x")), newTermName("$less"),
  List(Literal(Constant(10)))))

По мне уж лучше '(< x 10).

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

Исходя из отсутствия ответа на этот простой вопрос заключаю, что это невозможно.

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

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

Исходя из отсутствия ответа на этот простой вопрос заключаю, что это невозможно.

Сделай так, хотя бы.

unless := method(
  if(call evalArgAt(0), call evalArgAt(2), call evalArgAt(1))
) # Это твой макрос, аргументы не вычисляются

function := method(
 args := call message arguments
 lst :=list(call evalArgAt(1)) append (args at(2)) append (args at(3))
 doMessage(performWithArgList(args at(0) code, lst))
) 

function(if, 1>2, 1 print, 2 print) # подаешь нативный if в качестве аргумента
function(unless, 1>2, 1 print, 2 print) # подаешь свой макрос в качестве аргумента.

# out:
# 21

Твой макрос может быть подан в качестве аргумента в любой момент, в рантайме. Дерзай.

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

Опять приползла поебень, путающая макросы с ленью. Иди, поебень, сделай на своих гомосяцких fexpr-ах эффективную реализацию регулярных выражений.

anonymous
()
Ответ на: комментарий от anonymous
(define-syntax-rule (yoba args ...)
  (args ...))

(yoba if (> 1 2 ) (display 1) (display 2))
(yoba unless (> 1 2 ) (display 1))

че сказать-то хотел?

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

То, что ты просил, я сделал. не вижу проблем

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