я лексил раздельно дабл-квот, раздельно содержимое строки, раздельно скобки интерполяции, но для этого в лексере нужен контект в виде списка натов https://gist.github.com/kana-sama/f90ec78b342a014f606e86836b0a3128
Начало интерполированной строки, середина интерполированной строки, конец интерполированной строки
Хмм... а что такое "обычно" (это же зависит от возможностей, предоставляемых такой интерполяцией — начиная от "как обычные", через lexer modes (см. http://www.oilshell.org/blog/2017/12/17.html) / start conditions ( http://westes.github.io/flex/manual/Start-Conditions.html ) и до "рекурсивно вызывается [другой] интерпретатор")?
Зависит от умности лексера и от сложности интерполяции. Вот как сделано в парсере Котлина в Antlr, например (несколько состояний) https://github.com/antlr/grammars-v4/blob/master/kotlin/kotlin/KotlinLexer.g4#L174 Мы забирали строку в АСТ и разбирали позже.
Вот кстати совсем недавно попался пример того, как интерполированные строки делать не надо: http://phpsadness.com/sad/54
То есть, вы полностью разбираете выражение прямо в первом парсере! А там встроенное (интерполированное ?) выражение может быть любым Котлиновским выражением, или их язык слегка лимитирован?
К сожалению, не скажу, как оно по факту в Котлине. Я лишь показал, что это один из вариантов разбора интерированных строк. Заносить все в одно дерево - облегчить работу typechecker у, но усложнить парсер и дерево. Сохранить "просто строки" и разобрать попозже - оставить пространство для маневра в архитектуре, локализовать обработку, но частично завязать стадии компиляции друг на друга. You decide. Есть еще вариант не использовать интерированные строки, но, боюсь, меня совсем тапками закидают :) Я дважды подумаю о семантике строк перед добавлением такой фичи в язык.
Предлагаю вашему вниманию схему заработка на крипте. Все что нужно - быть программистом. Подробности в ЛС. Заработок до 1млн чистыми в месяц.
https://github.com/GaijinEntertainment/daScript/blob/master/src/parser/ds_parser.ypp - конкретно character_sequence : STRING_CHARACTER[char] { $$ = new string(); *$$ += $char; } | STRING_CHARACTER_ESC { $$ = new string(); *$$ += "\\\\"; } | character_sequence[elem] STRING_CHARACTER[char] { $$ = $elem; *$elem += $char; } | character_sequence[elem] STRING_CHARACTER_ESC { $$ = $elem; *$elem += "\\\\"; } ; string_constant : BEGIN_STRING character_sequence[seq] END_STRING { $$ = $seq; } ; string_builder_body : { $$ = new ExprStringBuilder(); $$->at = LineInfo(yyextra->g_FileAccessStack.back(), yylloc.first_column,yylloc.first_line,yylloc.last_column,yylloc.last_line); } | string_builder_body[sb] character_sequence[sconst] { bool err; auto esconst = unescapeString(*$sconst,&err); if ( err ) das_yyerror(scanner,"invalid escape sequence",tokAt(scanner,@sb), CompilationError::invalid_escape_sequence); auto sc = make_smart<ExprConstString>(tokAt(scanner,@sconst),esconst); delete $sconst; static_cast<ExprStringBuilder *>($sb)->elements.push_back(sc); $$ = $sb; } | string_builder_body[sb] BEGIN_STRING_EXPR expr[subexpr] END_STRING_EXPR { auto se = ExpressionPtr($subexpr); static_cast<ExprStringBuilder *>($sb)->elements.push_back(se); $$ = $sb; } ; string_builder : BEGIN_STRING[sfrm] string_builder_body[sb] END_STRING[sto] { auto strb = static_cast<ExprStringBuilder *>($sb); if ( strb->elements.size()==0 ) { $$ = new ExprConstString(tokRangeAt(scanner,@sfrm,@sto),""); delete $sb; } else if ( strb->elements.size()==1 && strb->elements[0]->rtti_isStringConstant() ) { auto sconst = static_pointer_cast<ExprConstString>(strb->elements[0]); $$ = new ExprConstString(tokRangeAt(scanner,@sfrm,@sto),sconst->text); delete $sb; } else { $$ = $sb; } } ;
Обсуждают сегодня