使用Spirit.Qi消除语法糖

时间:2012-07-03 21:09:06

标签: boost-spirit boost-spirit-qi boost-spirit-lex

我正在尝试解析类似于lisp的语言,它具有一些常用功能的语法糖。例如,加号函数可以写成(+ 1 2)或1 + 2.我认为在尝试解释语言之前消除语法糖将大大方便解释过程,因为那时,唯一的评估规则是需要实现的将是函数调用,并且所有中缀运算符都必须具有相应的内置函数。我以为我可以创建一个解析器来迭代从词法分析器收到的标记,并重新排序它们以将表达式放入前缀表示法。但是这需要解析器的输出也是令牌列表。这在Spirit.Qi有可能吗?据我所知,Spirit.Qi构建了一个层次结构,而不是一个线性的令牌列表。

1 个答案:

答案 0 :(得分:5)

当然,一切都是可能的。

那就是说,你为什么不操作AST,

  • 在解释时不要担心输入表示
  • 在担心lexing / parsing时不要担心解释

考虑一个AST representation like this(受一种简化的s表达式启发):

typedef boost::make_recursive_variant<
        std::string,
        std::vector< boost::recursive_variant_ >
   >::type expr_t;

您可以定义规则以接受s_exprbinobj 解析为相同的AST

qi::rule<It, std::vector<expr_t>(), Skipper> binop;
qi::rule<It, expr_t(), Skipper> expr, s_expr, name;

expr   = binop | s_expr;

binop = (s_expr >> (string("+") | string("-") | string("*") | string("/")) >> expr)
    [
        phx::push_back(_val, _2),
        phx::push_back(_val, _1),
        phx::push_back(_val, _3)
    ];

name = as_string [ lexeme [ +(graph - char_("()") ) ] ];

s_expr = ('(' > +expr > ')') | name;

完整演示

我在这里有一个完整的演示:https://gist.github.com/3047788


为了好玩,我投入了

test.cpp的演示主要内容:

int main()
{
    for (const auto input : std::list<std::string> {
         "",
         "(+ 1 3)",
         "(1 + 3)",
         "(1 + 4 / 2)",
         "(1 + (* 4 5 6) / 2)",
         "(avg   1 + (* 4 5 6) / 2)",
         "(trace 1 + (* 4 5 6) / 2 1 1 1 1 999)",
         "(avg   1 + (* 4 5 6) / 2 1 1 1 1 999)",
         "(avg   1 + (* 4 (unknown-function 5) 6) / 2 a b c)",
     })
    {
        std::cout << "----------------------\n";
        expr_t ast = doParse(input, qi::space);

        try 
        {
            std::cout << "eval<double>: " << eval<double>(ast) << std::endl;
            std::cout << "eval<int>   : " << eval<int>   (ast) << std::endl;
        } catch(std::exception const& e)
        {
            std::cerr << e.what() << '\n';
        }
    }
}

结果如下:

----------------------
parse failed: ''
warning: undefined '<no ast>'
eval<double>: 0
warning: undefined '<no ast>'
eval<int>   : 0
----------------------
parse success
input       : (+ 1 3)
ast parsed  : (+ 1 3)
eval<double>: 4
eval<int>   : 4
----------------------
parse success
input       : (1 + 3)
ast parsed  : ((+ 1 3))
eval<double>: 4
eval<int>   : 4
----------------------
parse success
input       : (1 + 4 / 2)
ast parsed  : ((+ 1 (/ 4 2)))
eval<double>: 3
eval<int>   : 3
----------------------
parse success
input       : (1 + (* 4 5 6) / 2)
ast parsed  : ((+ 1 (/ (* 4 5 6) 2)))
eval<double>: 61
eval<int>   : 61
----------------------
parse success
input       : (avg   1 + (* 4 5 6) / 2)
ast parsed  : (avg (+ 1 (/ (* 4 5 6) 2)))
eval<double>: 0
eval<int>   : 0
----------------------
parse success
input       : (trace 1 + (* 4 5 6) / 2 1 1 1 1 999)
ast parsed  : (trace (+ 1 (/ (* 4 5 6) 2)) 1 1 1 1 999)
trace( 61 1 1 1 1 999 )
eval<double>: 0
trace( 61 1 1 1 1 999 )
eval<int>   : 0
----------------------
parse success
input       : (avg   1 + (* 4 5 6) / 2 1 1 1 1 999)
ast parsed  : (avg (+ 1 (/ (* 4 5 6) 2)) 1 1 1 1 999)
eval<double>: 167.167
eval<int>   : 167
----------------------
parse success
input       : (avg   1 + (* 4 (unknown-function 5) 6) / 2 a b c)
ast parsed  : (avg (+ 1 (/ (* 4 (unknown-function 5) 6) 2)) a b c)
error: can't apply 'unknown-function' to 1 args (std::exception)

1 请记住,没有类型系统,这就是为什么你必须指定硬编码数据类型来解释所有值的原因(例如double result = eval<double>(ast))。也没有符号表,因此只有内置函数+-/* traceavg存在