用perl将类似Lisp的语法编译成javascript

用perl将类似Lisp的语法编译成javascript,perl,compiler-construction,lisp,Perl,Compiler Construction,Lisp,作为学习perl的个人练习,我想在perl中为javascript编译器编写一个“类似lisp的语法” “类似于Lisp”,因此不是完整的Lisp实现,我希望最终的语法将是“上下文无关”语法(LALR),并且相对容易编译为本机Javascript 制作词法分析器应该没有问题(可能是使用Parse::Flex),但在选择语法分析器生成器方面需要帮助 在CPAN中找到3,需要帮助选择/阅读:我应该学习什么:)/完成上述任务 yacc/野牛家族-或 (或R3)或马尔帕群岛 或者其他我错过的东西

作为学习perl的个人练习,我想在perl中为javascript编译器编写一个“类似lisp的语法”

“类似于Lisp”,因此不是完整的Lisp实现,我希望最终的语法将是“上下文无关”语法(LALR),并且相对容易编译为本机Javascript

制作词法分析器应该没有问题(可能是使用Parse::Flex),但在选择语法分析器生成器方面需要帮助

在CPAN中找到3,需要帮助选择/阅读:我应该学习什么:)/完成上述任务

  • yacc/野牛家族-或
  • (或R3)或马尔帕群岛
  • 或者其他我错过的东西
问题是:

  • 什么最适合lisp类语言
  • 什么样的人学习曲线不那么陡峭(因此,有很多学习的例子)(例如,我发现只有很少的Marpa例子)

我想你可以使用yapp-lisp有简单的语法。检查。您应该检查CPAN中的“”和“”,您将发现:

  • 全新的CPAN模块—perl/lisp桥
  • 也许您应该检查一下(用C编写)和(用Javascript编写并编译成Javascript)关于您未来的“类似Lisp”语言的想法:)

如果您只想解析Lisp的一个子集(特别是Scheme的一个简单子集),您可以自己编写解析器,
m//gc
样式并使用堆栈:

sub parse {
  my $_ = shift;
  pos($_) = 0;
  my @stack = ([]);
  while (pos($_) < length($_)) {
    m/\G\s+/gc and next; # skip whitespace
    if (m/\G\(/gc) { # opening parens
      push @stack, [];
    } elsif (m/\G\)/gc) { # closing parens
      my $list = pop @stack;
      push @{ $stack[-1] }, $list;
    } elsif (m/([\w-.]+)/gc) { # identifiers, numbers
      push @{ $stack[-1] }, $1;
    } else {
      die "I'm at @{[pos($_)]} and I have no idea how to parse this";
    }
  }
  @stack == 1 or die "Closing parens expected at the end";
  return $stack[0];
}
这将需要终端符号
ParenL
ParenR
编号
标识符

在我们的
parse
子部分中,我们首先必须制作一个新的识别器

my $rec = Marpa::R2::Recognizer({ grammar => $grammar });
并修改标记器循环中的操作:

my ($type, $value);
if (m/\G\(/gc) {
  ($type, $value) = (ParenL => undef);
} elsif (m/\G\)/gc) {
  ($type, $value) = (ParenR => undef);
} elsif (m/\G([0-9]+(?:\.[0-9]+))/gc) {
  ($type, $value) = (Number => $1);
} elsif (m/\G([\w-]+)/gc) {
  ($type, $value) = (Identifier => $1);
} else {
  die ...;
}
unless (defined $rec->read($type, $value) {
  die "Error at position @{[pos($_)]}. Expecting any of\n",
       map " * $_\n", @{ $rec->terminals_expected };
}
我们可以通过

my $ref = $rec->value;
unless (defined $ref) {
  die "The input couldn't be parsed";
}
return $$ref;

在我们的例子中,解析树将是嵌套数组引用的一个bunc。但您可以提供自定义操作,以便生成更复杂的AST。例如,将树的每个节点赋给一个对象,然后在根节点上调用
compile
,这可能是一种策略。

Lisp系统本身不是使用解析器生成器构建的。公共Lisp完全使用读取表进行解析,读取表将输入字符分类为各种类别。某些字符属于触发基于一个或两个字符组合的注册函数的类别。一些字符类型被收集到标记中。血淋淋的细节在报告中。 这个易读的想法来自MacLisp(见第11页)

解析器生成技术(LALR(1)及其类似技术)是由计算机科学家在20世纪60年代发明的,他们一定感到受到新兴编程语言复杂语法的挑战。Lisp黑客们已经进入了语义学的绿色牧场,他们基本上拒绝了这一点,转而采取不发明不必要的问题(复杂的上下文无关语法,带有歧义)的立场,然后需要寻找复杂的解决方案(时间和空间高效的解析器,由机器生成)

所以,如果你用这样的东西来解析Lisp,你实际上是在犯异端邪说


注意,TeX中出现了与Lisp的readtables非常相似的东西:讽刺的是,软件是由发明LR解析器的同一个人发明的。

基本Lisp语法非常简单。我不会想得太多。您可以很容易地用perl编写自己的解析器。n、 b.,parse::recdescent是一个很好的解析器,但我觉得这太过分了。一年前,你评论了我的一个答案,在这个问题上,只有你把Mason改成了“类似Lisp的语言”。我敢打赌,你还是在玩弄这个想法,让编译器获得一种用于模板的语言,它将编译成javascript,然后使用javascript::V8执行。我喜欢这个想法,这也是我为其他无法回答且范围非常广泛的问题撰写答案的唯一原因。:)这里有一个。这不是一个真正的“Lisp”语法。它是s表达式的一种简单形式。
my $ref = $rec->value;
unless (defined $ref) {
  die "The input couldn't be parsed";
}
return $$ref;