[BEEP] Как бы нам обустроить макросы
От: dmz Россия  
Дата: 04.03.09 17:49
Оценка:
Хочется странного, то есть макросов. Для начала — хотя бы самые примитивные, но хотелось бы честные — так что бы потом можно было потом извлечь из этого нечто большее.

По идее, для компилируемого языка макросы должны выглядеть как-то так:

1) В процессе разбора AST мы разбираем макрос — соответственно, у нас должны быть некие токены, которые определяют начало и конец макроса. Ну или хотя бы начало, конец может быть каким-то повторно используемым токеном.

2) Определение макроса может быть, наверное, распарсено тем же самым разборщиком, который разбирает и основной синтаксис.

3) Насколько я себе представляю, результатом разбора определения макроса является построение динамического разборщика

4) В месте, где макрос используется (вероятно, тоже надо отбить какими-то спец. токенами) — поток от начала макры до конца скармливается построенному разборщику, интерпретируется и результат интепретации применяется к некоему узлу AST.

Правильно ли это понимание, и есть ли какие-то более простые подходы? Серьезная засада мне тут видится с тем, что, например, сейчас очень многие проверки — вплоть до типизации некоторых моментов — делаются в парсере, совсем декларативно. Возможность делать произвольные преобразования над AST приведет к дополнительному анализу уже после раскрытия макросов, что может быть нетривиально, и вообще время компиляции никак не сократит.

Конкретно сейчас, хочется научиться определять именованные константы времени компиляции — но по идее, любых типов — так что, что бы делать константные, например, туплы — уже нужны честные макросы.

Где бы посмотреть? Кроме немерле есть что нибудь статическое, вменяемое и с макрами (не надо только про с++) ?
Re: [BEEP] Как бы нам обустроить макросы
От: z00n  
Дата: 04.03.09 23:07
Оценка: 46 (4)
Здравствуйте, dmz, Вы писали:


dmz>Хочется странного, то есть макросов. Для начала — хотя бы самые примитивные, но хотелось бы честные — так что бы потом можно было потом извлечь из этого нечто большее.


dmz>По идее, для компилируемого языка макросы должны выглядеть как-то так:

Composable and Compilable Macros

Лично мне последнее время плагинные расширители компиляторов нравятся больше.
Совершенно готовая к работе система с демонстрационным расширением языка С 22-мя плагинами.
Xoc, an Extension-Oriented Compiler.
Соответственный диссер: An Extension-Oriented Compiler.

И, конечно, OMETA.
Re: [BEEP] Как бы нам обустроить макросы
От: yumi  
Дата: 05.03.09 01:05
Оценка: 7 (1)
Здравствуйте, dmz, Вы писали:

dmz>2) Определение макроса может быть, наверное, распарсено тем же самым разборщиком, который разбирает и основной синтаксис.

dmz>3) Насколько я себе представляю, результатом разбора определения макроса является построение динамического разборщика

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

dmz>4) В месте, где макрос используется (вероятно, тоже надо отбить какими-то спец. токенами) — поток от начала макры до конца скармливается построенному разборщику, интерпретируется и результат интепретации применяется к некоему узлу AST.


Вы хотите многостадийную компиляцию сделать? Тогда смотрите на Meta ML, ИМХО, это потом выливается в жуткий геморрой.

dmz>Правильно ли это понимание, и есть ли какие-то более простые подходы? Серьезная засада мне тут видится с тем, что, например, сейчас очень многие проверки — вплоть до типизации некоторых моментов — делаются в парсере, совсем декларативно. Возможность делать произвольные преобразования над AST приведет к дополнительному анализу уже после раскрытия макросов, что может быть нетривиально, и вообще время компиляции никак не сократит.


Именно! Это и называется многостадийная компиляция со всеми вытекающими минусами.

dmz>Где бы посмотреть? Кроме немерле есть что нибудь статическое, вменяемое и с макрами (не надо только про с++) ?


Я как пример, смотрел на Meta ML, Template Haskell.
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org
Re[2]: [BEEP] Как бы нам обустроить макросы
От: dmz Россия  
Дата: 05.03.09 03:29
Оценка:
Y>Тут проще, основной разборщик должен быть расширяемым, тогда и динамический разборщик не нужен.

Это мне придется выкинуть ocamlyacc. Наверное, я не готов прямо сейчас на это пойти. К тому же, я не уверен, что camlp4, который я вижу как замену, может динамически строить правила, надо еще посмотреть.

dmz>>4) В месте, где макрос используется (вероятно, тоже надо отбить какими-то спец. токенами) — поток от начала макры до конца скармливается построенному разборщику, интерпретируется и результат интепретации применяется к некоему узлу AST.


Y>Вы хотите многостадийную компиляцию сделать? Тогда смотрите на Meta ML, ИМХО, это потом выливается в жуткий геморрой.


Не, хочется делать в один проход как раз — вроде как раз получается, если парсер, наткнувшись на некий макрос, возвращает результат его разбора другим парсером. Второй вариант — сильно ограничить возможности макросов, так, что бы их раскрытие было заведомо корректное, а возможные макросы описывались бы правилами того-же самого парсера N1, но приводили бы не созданию самой конструкции, а обернутой — т.е. при определении макроса происходило бы не

X -> Y


а

X -> MacroDef(Y)


а при использовании — подставлялся бы из словаря, соответственно, Y. В принципе, если ограничить множество возможных X, а так же, контексты, где возможна макроподстановка, то можно обойтись малой кровью, наверное.


Y>Именно! Это и называется многостадийная компиляция со всеми вытекающими минусами.

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


Y>Я как пример, смотрел на Meta ML, Template Haskell.


MetaML — на ML? Насколько понятен там код?
Re[3]: [BEEP] Как бы нам обустроить макросы
От: dmz Россия  
Дата: 05.03.09 07:59
Оценка:
dmz>а при использовании — подставлялся бы из словаря, соответственно, Y. В принципе, если ограничить множество возможных X, а так же, контексты, где возможна макроподстановка, то можно обойтись малой кровью, наверное.

Ну, в общем, прямо при парсинге раскрывать макры, естесственно, не получится, так как спуск рекурсивный, придется раскрывать на уровне AST чем-то вроде унификации. Но унификация, в общем, работает достаточно быстро. Ну или скорость ее работы падает достаточно медленно. В общем, если принять за аксиому то, что макросы могут раскрыться только в элемент AST (а можно было сразу в код, например — так как есть statement EMIT и в принципе ничего не мешает раскрываться туда, но опасно) — а они бывают, грубо, либо Statement либо Expression, и если сделать макры двух видов — MacroStmt и MaxroExpr — то их раскрытие должно происходить ровно в тех контекстах, в которых может быть Expression или Statement. Таким образом, никаких дополнительных проверок не надо, и получается что-то вроде следующего:

@literal zabor  "ZABOR";
@literal golova "GOLOVA";
@literal thirty_five 35;

def main()
{
    local eq   = `zabor == `golova;
    local eq2  = `zabor == "GOLOVA";
    local a = `thirty_five;
    local moo = a == `zabor; #BDYSH...
}


и выливается примерно в такое:

    let rec with_funcs f l = match l with
        | (FuncDef(fp,c))::ds -> (f (FuncDef(fp,c))) :: (with_funcs f ds)
        | d::ds               -> d :: (with_funcs f ds)
        | []                  -> []

    let expand_macros (Module({mod_defs=defs},c)) = 
        let ql = List.map (function MacroDef(MacroLiteral(name,e),c) -> (name,e)::[] | _ -> [])
        in let equots = defs |> ql |> List.fold_left (@) []
        in let rec exp_macro_expr e = match e with 
        | ENothing                 ->  ENothing
        | ELiteral(n,c)            -> (ELiteral(n,c))
        | EListNil(c)              -> (EListNil(c))
        | EIdent(n,c)              -> (EIdent(n,c)) 
        | EVoid(ECall((e,a),c))    -> (EVoid(ECall((exp_macro_expr e, List.map exp_macro_expr a),c)))
        | ECall((e,a),c)           -> (ECall((exp_macro_expr e, List.map exp_macro_expr a),c))
        | EVoid(x)                 -> (EVoid(x))
        | ERecord((n,ef),c)        -> (ERecord((n,List.map exp_macro_expr ef),c))
        | ERecordFieldInit((n,e),c)-> (ERecordFieldInit((n,exp_macro_expr e),c))
        | ERecordField(Rec(e),x)   -> (ERecordField(Rec(exp_macro_expr e),x))
        | EBoolUn(op, e, c)        -> (EBoolUn(op, exp_macro_expr e, c))
        | EAriphUn(op, e,c)        -> (EAriphUn(op, exp_macro_expr e,c))
        | EPair( (e1, e2), c )     -> (EPair( (exp_macro_expr e1, exp_macro_expr e2), c ))
        | EList( (e1, e2), c )     -> (EList( (exp_macro_expr e1, exp_macro_expr e2), c ))
        | ECmp(op, (e1,e2), c )    -> (ECmp(op, (exp_macro_expr e1, exp_macro_expr e2), c ))
        | EBoolBin(op, (e1,e2), c) -> (EBoolBin(op, (exp_macro_expr e1, exp_macro_expr e2), c))
        | EAriphBin(op, (e1,e2),c) -> (EAriphBin(op, (exp_macro_expr e1, exp_macro_expr e2),c))
        | EQuot(n,c)               -> List.assoc n equots  

        in let exp_macro_stmt stmt = match stmt with 
        | StArg _           | StLocal _ 
        | StEmpty _         | StBreak _ 
        | StContinue _      | StIf _  
        | StEmit _
        | StBranchElse _                  -> stmt 
        | StAssign(x,e)                   -> StAssign(x, exp_macro_expr e)    
        | StWhile((e,x),c)                -> StWhile((exp_macro_expr e,x),c)
        | StBranch((e,x),c)               -> StBranch((exp_macro_expr e,x),c) 
        | StCall(e,c)                     -> StCall(exp_macro_expr e,c)
        | StRet(e,c)                      -> StRet(exp_macro_expr e,c)
        in let exp_macro_code (Block({blk_code=code},c)) = 
           Block({blk_code=List.map exp_macro_stmt code},c)
        in let exp_macro_fun fn = match fn with 
            | FuncDef(fp,c) -> FuncDef({fp with func_code=exp_macro_code fp.func_code},c)
            | _             -> assert false
        in Module({mod_defs=with_funcs exp_macro_fun defs},c)



прикольно, на форуме есть подсветка ML.
Re[2]: [BEEP] Как бы нам обустроить макросы
От: dmz Россия  
Дата: 05.03.09 08:43
Оценка:
Y>Тут проще, основной разборщик должен быть расширяемым, тогда и динамический разборщик не нужен.

Да, пользовался бы хотя бы комбинатором парсеров — можно было уже сейчас сделать очень мощные макросы.
Re[3]: [BEEP] Как бы нам обустроить макросы
От: yumi  
Дата: 05.03.09 08:50
Оценка:
Здравствуйте, dmz, Вы писали:

Слушай, если хочешь, могу потом из дома выслать тебе на мыло материалы, которыми я пользовался (если найду конечно, довольно много времени прошло), там были какие-то работы по многостадийной компиляции и по макросам. А то я на самом деле много чего подзабыл, все помню смутно, могу много глупостей наговорить
Lisp is not dead. It’s just the URL that has changed:
http://clojure.org
Re[4]: [BEEP] Как бы нам обустроить макросы
От: dmz Россия  
Дата: 05.03.09 09:02
Оценка:
Y>Слушай, если хочешь, могу потом из дома выслать тебе на мыло материалы, которыми я пользовался (если найду конечно, довольно много времени прошло), там были какие-то работы по многостадийной компиляции и по макросам. А то я на самом деле много чего подзабыл, все помню смутно, могу много глупостей наговорить

Да на самом деле, вроде все несложно получается.

1) При определении макроса мы генерируем нечто, что может применяться к узлу или узлам AST
2) При раскрытии — таки да, применяем это к узлам и получаем раскрытый макрос
3) Если при описании макроса можно пользоваться только языковыми конструкциями, а не произвольными — то нам достаточно
иметь парсер, который может парсить поток по частям — т.е. просто есть способ отпарсить часть потока. Пока насколько я вижу, вполне хватает комбинатора парсеров. Если бы его взял — вот прямо сейчас бы мог сделать очень широкий класс макросов. А так не знаю, получится или нет. Впрочем, возможно что и ocamlyacc умеет парсить не от top_level, но это надо разбираться.

Насчет прислать — давай, если не жалко, посмотрю когда-нибудь.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.