MrShoor
> Считаю надо переименовать язык пока еще не поздно. Вот пару примеров красивых
> названий языка:
> "Ъ"
> "Ы"
Я бы свой язык назвал так:
"Ь".
При том что нельзя этот язык обзывать "Мягкий знак", т.к. по документам он "Ь".
Sbtrn. Devil
> Угловые скобки не создают проблемы, если их ставить не после имени, а перед.
Угадай, где здесь обращение к шаблону, а где вызов оператора сравнения:
A<B> + C
C + <B>A
MrShoor
> Считаю надо переименовать язык пока еще не поздно. Вот пару примеров красивых
> названий языка:
> "Ъ"
> "Ы"
Хаус
> Я бы свой язык назвал так:
>
> "Ь".
Сначала напишите свой язык, а потом уже выпендривайтесь с его названием.
А мне и Ü по-нарву.
Panzerschrek[CN]
> Сначала напишите свой язык, а потом уже выпендривайтесь с его названием.
> А мне и Ü по-нарву.
Да, свой язык повышает ЧСВ всё-таки.
Panzerschrek[CN]
А такой вариант?
A::<B> + C A::[B] + C A::(B) + C
Или вот такие варианты:
Point`f32
Pair`A`B
Array`(Point`f32)
Array`(Pair`A`B)
Map`String`Foo
Map`String`(Array`u32)
Point`f32
Pair`[A, B]
Array`Point`f32
Array`Pair`[A, B]
Map`[String, Foo]
Map`[String, Array`u32]
Panzerschrek[CN]
> C + <B>A
И в чём проблема?
ATOM ::= id | number | ... | SUBEXPR TEMPLATE ::= '<' POLYNOM [',' POLYNOM]* '>' BASIC ::= [ TEMPLATE ]? ATOM OP_UNARY ::= '+' | '-' | '!' | '~' FACTOR ::= [ OP_UNARY ]* BASIC OP_MUL ::= '*' | '/' | '&' TERM ::= FACTOR [ OP_MUL FACTOR ]* OP_SUM ::= '+' | '-' | '|' | '^' POLYNOM ::= TERM [ OP_SUM TERM ]* OP_CMP ::= '<' | '>' | '<=' | '>=' | '==' | '!=' PREDICATE ::= POLYNOM [ OP_CMP POLYNOM ]* SUBEXPR ::= '(' PREDICATE [',' PREDICATE]* ')'
Sbtrn. Devil
> И в чём проблема?
очевидно вот тут:
> PREDICATE ::= POLYNOM [ OP_CMP POLYNOM ]*
OP_CMP имеет выше приоритет чем "[ TEMPLATE ]?" из BASIC
следовательно "C + <В>A" всегда будет парситься без шаблонов.
upd:
ошибся
desss
> A::<B> + C
Фигота, непонятно когда зыкрывать скобку шаблона. Вдруг там предполагалось A::< (B > C) > ?
> A::[B] + C
> A::(B) + C
Вот с этим не вижу проблем. Но тут всё равно 4 символа на инстанцирования шаблона, так же, как у меня сейчас - </ />. Так что профит от такого варианта не очевиден.
> Point`f32
Фигота. Вдруг захочется Point</ Point</ f32 /> /> ? В таком варианте будет Point'Point'f32, что нефига не однозначно.
Sbtrn. Devil
> И в чём проблема?
Ну тебе внизу уже объяснили, в чём. Если не веришь - попробуй запилить синтаксический анализатор для такой грамматики.
Panzerschrek[CN]
> Вдруг захочется Point</ Point</ f32 /> /> ? В таком варианте будет Point'Point'f32, что нефига не однозначно.
В первом варианте апостроф это начало и разделитель аргументов типов, и внутренние дженерики оборачиваются в скобки.
Т.е. сколько апострофов, то столько и типов аргументов к шаблонным типам.
Point`(Point`f32).
Pair`(Point`(Point`f32))`(Point`(Point`f32))
Во втором апостроф это признак начала аргументов типов, опционально оборачиваются в скобки когда один, когда несколько, то обязательно.
Т.е. сколько апострофов, то столько и шаблонных типов.
Point`Point`f32
Pair`[Point`Point`f32, Point`Point`f32]
И там и там однозначно, но по-разному.
Adler
> OP_CMP имеет выше приобретет чем "[ TEMPLATE ]?" из BASIC
Што-што?
Panzerschrek[CN]
> Если не веришь - попробуй запилить синтаксический анализатор для такой
> грамматики.
Да на здоровьице. Становись вот сюда: https://pegjs.org/online
забивай в грамматику следующее:
{ var whitespace = {}; function joinComponents(c1, c2) { var result = new Array(); if (c1 != null) result.push(c1); if (c2 != null) for (var i in c2) for (var j in c2[i]) if (c2[i][j] != null && c2[i][j] != whitespace) result.push(c2[i][j]); return result; } } EXPRESSION = PREDICATE _ "whitespace" = [ \t\n\r]* { return whitespace; } number "number" = _ [0-9]+ { return parseInt(text(), 10); } id "id" = _ [A-Za-z_][A-Za-z_0-9]* { return text(); } ATOM = id / number / SUBEXPR TEMPLATE = "<" _ C1:POLYNOM C2:( _ "," _ POLYNOM )* _ ">" { return {tplParams: joinComponents(C1, C2) } } BASIC = tpl:TEMPLATE? _ atom:ATOM { return { tpl: typeof(tpl)=="undefined"? null : tpl, atom: atom }; } OP_UNARY = "+" / "-" / "!" / "~" FACTOR = C:(_ OP_UNARY)* _ basic:BASIC { return { opsUnary: joinComponents(null, C), basic: basic }; } OP_MUL = "*" / "/" / "&" TERM = C1:FACTOR C2:( _ OP_MUL _ FACTOR )* { return { terms: joinComponents(C1, C2) }; } OP_SUM = "+" / "-" / "|" / "^" POLYNOM = C1:TERM C2:( _ OP_SUM _ TERM )* { return joinComponents(C1, C2); } OP_CMP = "<" / ">" / "<=" / ">=" / "==" / "!=" PREDICATE = C1:POLYNOM C2:( _ OP_CMP _ POLYNOM )* { return { predicate: joinComponents(C1, C2) }; } SUBEXPR = "(" _ C1:EXPRESSION C2:(_ "," _ EXPRESSION)* _ ")" { return { predicates: joinComponents(C1, C2) }; }
<B>A+C A+<B>C A<B>C A<+B>C A+<+B>C
Sbtrn. Devil
Внимание, вопрос:
Что есть вот это:
< B > C > A
Инстанцирование шаблона "C" с параметрами "B" больше "A". Или же инстанцирование шаблона "A" с параметрами "B > C"?
Это я всё к чему: даже если ты сочинил грамматику, которая переваривает такие выражения, человеку читать такое тяжело. Поэтому я выбрал особые скобки.
PS: дальнейшие обсуждения синтаксиса шаблонов предлагаю приостановить и пообсуждать в теме какие-нибудь другие аспекты языка.
Запиливаю импорт:
import "std/vector.ü" import "std/map.ü" fn Foo() { var std::vector</ f32 /> v; var std::map</ f32 /> m; }
В отличии от крестового include, import не включает голый текст файла. Вместо этого происходит компиляция файла, и последующее слияние таблиц символов с целевым файлом. При этом, каждый файл в дереве импорта компилируемого корневого файла компилируется ровно один раз, никаких костылей вроде pragma once для этого не нужно.
В отличии от подхода с включением текстов не может произойти возможного влияния на импортируемый файл предыдущих импотрируемых файлов.
При реализации столкнулся с такой особеностью:
Пришлось обязать файлы с forward-declaration для классов и прототипами функций обязательно импортировать в файлы с реализациями. Иначе происходит такая фигня, что llvm типы и функции различаются и компилятор ломается.
Panzerschrek[CN]
> Пришлось обязать файлы с forward-declaration для классов и прототипами функций
> обязательно импортировать в файлы с реализациями. Иначе происходит такая фигня,
> что llvm типы и функции различаются и компилятор ломается.
Ты что-тро не то делаешь, предполагаю что ты 2 раза вызываешь llvm::Function::Create с одинаковым именем, это так не работает и функции будут разные. Тебе надо поддерживать свою мапу name->function или использовать готовую:
Module::getOrInsertFunction для функций
Module::getTypeByName для типов
return [](){};
> Ты что-тро не то делаешь, предполагаю что ты 2 раза вызываешь
> llvm::Function::Create
Так и есть:
a.ü:
fn Foo();
b.ü:
fn Foo(){}
root.ü:
import "a.ü" import "b.ü"
Сначала компилируется a.ü, потом b.ü. Таблицы символов у них совершенно разные, из b.ü не видно символов из a.ü, поэтому llvm-функции создаются дважды.
> тебе надо поддерживать свою мапу name->function или использовать готовую
Кажется нормальным вариантом. Надо попробовать.