1

我正在尝试实现一种语言,并且我使用 Ragel 作为词法分析器(和野牛作为解析器)。我希望能够在我的语言中支持字符串插值,但我不确定如何做到这一点。

我的词法分析器对语言的主要部分使用如下扫描仪:

sstring = "'" ( ( any -- "'" ) | ( '\\' any ) )* "'";
# dstring = ?;
main := |*
   comment => {};
   '(' => { ADD_TOKEN(LPAREN); };
   ')' => { ADD_TOKEN(RPAREN); };
   # ...
   sstring => { ADD_TOKEN2(STRING); };
   # dstring => ?; 
*|;

我需要做什么才能像处理字符串插值一样"hello #{world}"

4

2 回答 2

0

大括号的内部内容可以是一个完整的表达式。插入的字符串将转换为连接操作。这对词法分析器来说太多了。您需要解析器的强大功能。因此,在每个插值字符串中识别三种不同类型的标记:

istring_start = "'" ( ( any -- "'" ) | ( '\\' any ) )* "#{";
istring_middle = "}" ( ( any -- "'" ) | ( '\\' any ) )* "#{";
istring_end = "}" ( ( any -- "'" ) | ( '\\' any ) )* "'";

在解析器中,您将拥有如下规则:

istring : istring_prefix expr ISTRING_END 
                ;
istring_prefix : ISTRING_START
               | istring_prefix expr ISTRING_MIDDLE 
               ;

这些规则构建语法树或编译字节码或您想要作为字符串连接操作的代码的任何内容。

于 2013-11-23T04:32:33.447 回答
0

我会通过与完成字符串令牌相关的操作来完成它。然后,在该操作中,您可以遍历 ts 到 te 并添加字符串插值的逻辑,并在操作中发出标记。

像这样的东西,可能只涵盖最基本的形式,可能会帮助您入门:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

enum token { STR, STR_I };

void emit(enum token tok, const char* start, const char* end) {
  if (start == end)
    return;
  switch(tok) {
    case STR:
      printf("STR(\"%.*s\") ", (int)(end - start), start); break;
    case STR_I:
      printf("STR_I(%.*s) ", (int)(end - start), start); break;
  }
}


%%{
machine interpolation;
write data;

action interpolate {
  // this is the data input without ""
  const char* data_start = ts + 1;
  const char* data_end = te - 1;

  // Use this to walk through the token to find interpolation points
  const char *tok_start = data_start;
  const char *tok_end = data_start;

  for (;tok_end <= data_end; tok_end++) {

    // Does it contain #{ ?
    if (strncmp(tok_end,"#{", 2) == 0) {
      emit(STR, tok_start, tok_end);
      tok_start = tok_end + 2;
      // fast-forward to } or end, whichever comes first
      while (tok_end < data_end && *tok_end != '}') {
        ++tok_end;
      }

      if (tok_end == data_end) {
        // we're at the end
        emit(STR, tok_start - 2, data_end);
        tok_start = tok_end;
        break;
      } else {
        // found matching }
        emit(STR_I, tok_start, tok_end);
        tok_start = tok_end + 1;
      }
    }
  }
  if (tok_start != data_end) {
    emit(STR, tok_start, data_end);
  }
}

not_dquote_or_escape = [^"\\];
escaped_something = /\\./;
string_constant     = '"' ( not_dquote_or_escape | escaped_something )* '"';

main := |*
  string_constant => interpolate;
  *|;

}%%

int main(int argc, char **argv) {
  //char text[] = "\"hello #{first_name} #{last_name}, how are you?\"";
  //char text[] = "\"#{first_name} is my name.\"";
  //char text[] = "\"#{first_name}#{last_name}\"";
  //char text[] = "\"#{ without finishing brace.\"";
  //char text[] = "\" no starting brace }.\"";

  char *p = &text[0];
  char *pe = (text + strlen(text));
  char *eof = pe;
  int act, cs;
  const char* ts;
  const char* te;

  %% write init;
  %% write exec;

  return 0;
}

我很确定您也可以使用 fgoto 等跳转到不同的状态,但我只将 Ragel 用于简单的扫描仪,因此无法真正帮助您。

于 2013-11-13T23:33:07.703 回答