如何在序言中编写解析器以输出解析树

时间:2019-01-25 16:57:43

标签: parsing prolog dcg

我正在序言中编写一个解析器,该解析器应该能够解析此数学公式:

a = 1 * 2 + (3 - 4) / 5;

并从中打印出一个解析树,该解析树应如下所示:

 PARSE TREE:
 assignment
    ident(a)
    assign_op
    expression
        term
            factor
                int(1)
            mult_op
            term
                factor
                    int(2)
        add_op
        expression
            term
                factor
                    left_paren
                    expression
                        term
                            factor
                                int(3)
                        sub_op
                        expression
                            term
                                factor
                                    int(4)
                    right_paren
                div_op
                term
                    factor
                        int(5)
    semicolon

我有此功能,当我运行代码run('program1.txt', 'myparsetree1.txt')时,将打印出解析树。它将从program1.txt文件中读取数学公式,并在myparsetree1.txt文件上打印出分析树。

到目前为止,我已经尝试为解析器编写此语法,但是由于我不断遇到letter_code和来自tokenizer的digit_code而不断出现存在和实例化错误,而prolog却无法正常工作抱怨其中的论点太少。

 /*Loads the tokenizer*/
:- [tokenizer].

parse(I) --> assign(I).
assign(assign(I,'=', Expr,';')) -->
        letter_code(I), '=', expr(Expr), ';'.
expr(expr(Term, add_op, Expr)) -->
        term(Term), add_op, expr(Expr).
expr(expr(Term, sub_op, Expr)) -->
        term(Term), sub_op, expr(Expr).
expr(expr(Term)) --> term(Term).        

term(term(Factor, mul_op, Term)) -->
        factor(Factor), mul_op, term(Term). 
term(term(Factor, div_op, Term)) -->
        factor(Factor), div_op, term(Term).
term(term(Factor)) --> factor(Factor).



factor(factor('(', Expr, ')')) --> '(', expr(Expr), ')'.

factor(factor(Digit)) --> digit_code(Digit).

add_op --> ['+'].       
sub_op --> ['-'].
mul_op --> ['*'].
div_op --> ['/'].

letter_code和digit_code是来自名为tokenizer.pl的单独文件的谓词

digit_code(Code):-
    Code >= 48, /* 48 = '0' 57 = '9' */
    Code =< 57.  

letter_code(Code):-
    Code >= 97, /* 97 = 'a' 122 = 'z' */
    Code =< 122. 

当我运行程序时,通常会出现存在错误:letter_code / 3,与数字代码相同,在其中抱怨theere不是带有3个参数的谓词。我尝试将谓词更改为具有三个参数,但随后却收到实例化错误。这就是我所做的事,它的结果是:

letter_code(Code, Xs, Xs):-
    Code >= 97,
    Code =< 122.


| ?- run('program1.txt','myparsetree1.txt').
! Existence error in user:letter_code/1
! procedure user:letter_code/1 does not exist
! goal:  user:letter_code(97)
//------------------------------------------------

letter_code(Code, Xs, Xs):-
    Code >= 97,
    Code =< 122.

letter_code(Code):-
    Code >= 97,
    Code =< 122.


| ?- run('program1.txt','myparsetree1.txt').
! Instantiation error in argument 1 of (>=)/2
! goal:  _293>=97

有人知道如何解决这个问题吗?我希望我比第一次提出这个问题时更清楚。

2 个答案:

答案 0 :(得分:0)

在DCG正文中,letter_code(C)将扩展为对letter_code/3的调用。具有三个参数的实现不会从列表中删除任何内容,因此我认为这不太可能实现所需的效果,您可能需要这样的东西:

letter_code(Code) --> 
    [Code], 
    {
        Code >= 97,
        Code =< 122
    }.

DCG规则扩展为带有两个额外参数的谓词;例如,listing(letter_code//1)将此显示为已解析的值:

?- listing(letter_code//1).
letter_code(A, [A|C], B) :-
    A>=97,
    A=<122,
    B=C.

true.

当您用letter_code/3重新定义letter_code(Code, Xs, Xs) :- ...时,您应该做出可以编译但在运行时失败的内容。因此,您无法找到已明确定义的谓词的问题就在其他地方。

答案 1 :(得分:0)

在DCG中,您将Prolog代码括在括号中:

parse(I) --> assign(I).
assign(assign(I,'=', Expr,';')) -->
        {letter_code(I)}, '=', expr(Expr), ';'.
...

那是