Ragel中的字符串插值

时间:2013-11-05 23:04:22

标签: c ragel

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

我的词法分析器使用类似以下内容的扫描程序作为语言的主要部分:

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

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

2 个答案:

答案 0 :(得分: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用于简单的扫描仪,因此无法真正帮助你。

答案 1 :(得分: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 
               ;

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