2014-11-03 132 views
1

我想使用FsLex和FsYacc来分析一个简单的脚本语言,并且我在区分minus运算符和负数时遇到了问题。在F#lex/yacc分析器中区分'负'运算符和负数

如果我评估术语“1 - 2”,解析器将返回所需的AST:Minus(NumberLiteral(1.0),NumberLiteral(2.0))。但是,如果我评估术语“1-2”,词法分析器将产生数字1,然后是数字-2,这不是有效的输入。

我已经做了一个最小的程序来重现我的问题。 AST这样定义:

module Ast 

type Expression = 
    | NumberLiteral of double 
    | Minus of Expression * Expression 

词法分析器代码如下所示:

{ 
module Lexer 
open Microsoft.FSharp.Text.Lexing 
open Parser 
} 

let whitespace = ' ' 
let digit = ['0' - '9'] 
let number = '-'?digit+ 

rule token = parse 
    | whitespace* { token lexbuf } 
    | '-'   { MINUS } 
    | number  { lexbuf |> LexBuffer<_>.LexemeString |> System.Double.Parse |> NUMBER } 
    | eof   { EOF } 

解析器是这样的:

%{ 
open Ast 
%} 
%start start 
%token EOF MINUS 
%token <double> NUMBER 
%type <Expression> start 
%% 

start: 
    | expression EOF { $1 } 

expression: 
    | NUMBER   { NumberLiteral $1 } 
    | expression 
     MINUS expression { Minus($1, $3) } 

我最初的想法是不处理-的一部分在词法分析器中的数字,并让解析器确定MINUS标记是否应产生负运算符或负数。不幸的是,这也会导致输入“-2”被评估为负数,因为空白将被消耗。

但我认为这一定是一个普遍问题,必须有一个通用的解决方案。那么我该如何最好地处理这个问题?

回答

1

通常的解决方案是-2实际上是一个表达式。如果您觉得评估-2(或者您可以在生产MINUS expression中将其作为特例处理)效率太低,则可以“不断折叠” - 直接评估其参数为常量的表达式。