【发布时间】:2020-04-22 08:38:45
【问题描述】:
我正在查看这两个资源(https://github.com/racket/parser-tools/blob/master/parser-tools-lib/parser-tools/examples/calc.rkt 和 https://gist.github.com/gcr/1318240),虽然我还不完全理解主 calc 函数的工作原理,但我想知道是否可以将其扩展为适用于类似 c 的简单程序只是没有功能?因此它将对 if、while 和 print 语句进行 lex、解析和评估。所以像(define-empty-tokens op-tokens ( newline = OC CC (open-curly/closed-curly for block statements) DEL PRINT WHILE (WHILE exp S) S IF S1 S2 (IF exp S1 S2) OP CP + - * / || % or && == != >= <= > < EOF ))
到目前为止,我将它(第一个链接的代码)扩展为也可以使用布尔值:
所以在 calcl 中我添加了这两行:
[ (:= 2 #\|) (token-||)]
[(:or "=" "+" "-" "*" "/" "%" "&&" "==" "!=" ">=" "<=" ">" "<") (string->symbol lexeme)]
然后:
(define calcp
(parser
(start start)
(end newline EOF)
(tokens value-tokens op-tokens)
(error (lambda (a b c) (void)))
(precs (right =)
(left ||)
(left &&)
(left == !=)
(left <= >= < >)
(left - +)
(left * / %)
)
(grammar
(start [() #f]
[(error start) $2]
[(exp) $1])
(exp [(NUM) $1]
[(VAR) (hash-ref vars $1 (lambda () 0))]
[(VAR = exp) (begin (hash-set! vars $1 $3)
$3)]
[(exp || exp) (if (not(and (equal? $1 0) (equal? $3 0) )) 1 0) ]
[(exp && exp) (and $1 $3)]
[(exp == exp) (equal? $1 $3)]
[(exp != exp) (not(equal? $1 $3))]
[(exp < exp) (< $1 $3)]
[(exp > exp) (> $1 $3)]
[(exp >= exp) (>= $1 $3)]
[(exp <= exp) (<= $1 $3)]
[(exp + exp) (+ $1 $3)]
[(exp - exp) (- $1 $3)]
[(exp * exp) (* $1 $3)]
[(exp / exp) (/ $1 $3)]
[(exp % exp) (remainder $1 $3)]
[(OP exp CP) $2]))))
但我很难理解上面的代码以及下面的代码。如果可能的话,我会更改它以便它也适用于 ifs 和 whiles 等?
(define (calc ip)
(port-count-lines! ip)
(letrec ((one-line
(lambda ()
(let ((result (calcp (lambda () (calcl ip)) )))
(when result (printf "~a\n" result) (one-line))
)
) ))
(one-line))
)
另外,这家伙似乎依赖换行符来标记语句的结尾。即你不能在一行上有超过 1 个语句。我希望程序能够识别一行中的两个语句,并通过某种方式向前看并检查是否有新的未声明变量、特殊关键字或开/闭括号等来分别评估它们。
更新:
我通过以下规则设法为 arith 表达式构建了一个 AST,但我如何摆脱除重要括号之外的所有内容,以便我可以评估它?
例如:输入列表:(list (token 'NUM 17) '+ (token 'NUM 1) '* (token 'NUM 3) '/ 'OP (token 'NUM 6) '- (token 'NUM 5) 'CP)
我回来了:
'(exp (((((factor 17)))) + (((((factor 1))) * ((factor 3))) / ((((((factor 6)))) - (((factor 5))))))))
这是我的规则:
exp : add
/add : add ('+' mul)+ | add ('-' mul)+ | mul
/mul : mul ('*' atom)+ | mul ('/' atom)+ | mul ('%' atom)+ | atom
/atom : /OP add /CP | factor
factor : NUM | ID
【问题讨论】:
-
更新:这是我目前所理解的: 1 - 单行是一个声明的函数,当被调用时执行一个 lambda: 2 - 为结果分配一个值,它将从中接收一个值计算。 3 - calcp 将通过执行它从一行接收到的 lambda 来产生这个值。 4 - 一个 lambda,它在执行时调用 calcl 从词法分析器传递它的“一个”值。 5 - 这样该值将被转发到 calcp。
-
6 - calcp 将做它的事情并传回设置为结果的值。 7 - 然后打印结果 8 - 再次递归调用一行 9 - 在函数结束时,调用单行以使事情在开始时正确运行 所以在 5) 是我所在的位置困惑,当 calcp 只从 calcl 收到“一个”标记时,它如何设法评估整个表达式?
-
calcp传递了一个函数,每次调用它都会返回一个令牌。所以calcp可以使用它来获取它认为必要的尽可能多的代币。 -
您可以通过更改语法中的归约操作让
calcp做任何您想做的事情。现在,这些动作评估并返回一个数字。要制作 AST,这些操作需要构建并返回一个 AST 节点(如果这是它们返回的,这就是它们将在$1中恢复的,...) -
您必须查看
calcp的定义才能看到它在哪里调用其参数,并且可能不容易看到定义,因为parser可能会生成函数.你真的应该阅读 SICP :-)
标签: parsing racket interpreter yacc lexer