使用 lexyacc 在 racket 中实现一个类似 C 语言的程序。

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 lexyacc 在 racket 中实现一个类似 C 语言的程序。相关的知识,希望对你有一定的参考价值。

我在看这两个资源(https:/github.comracketparser-toolsblobmasterparser-tools-libparser-toolssexamplescalc.rkt。https:/gist.github.comgcr1318240。),虽然我还不完全理解主计算函数的工作原理,但我想知道是否有可能将其扩展到一个简单的类似于c的程序中,只是不使用函数?因此,它将会对if、whiles和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 ))

下面是我目前对它的扩展(第一个链接的代码),使其也能与booleans一起工作。

所以在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))
  )

另外,这个家伙似乎是依靠换行符来标记语句的结束,也就是说,你不能在一行上有多于一条语句。我希望程序能够识别一行中的两个语句,并通过某种方式向前看并检查是否有新的未声明的变量、特殊关键字或开闭括号等来分别评估它们。

更新。

我用下面的规则在brag中建立了一个算术表达式的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
答案

你不能轻易地使用基于即时评价的评价器来实现带有条件和循环构造的语言。

至少对于循环来说,这一点应该很清楚。如果你有这样的东西(使用超简化的语法)。

repeat 3 { i = i + 1 }

如果你在解析过程中进行评价, i = i + 1 将被精确地评估一次,因为字符串被精确地解析一次。为了让它被多次评估,解析器需要将其转换为 i = i + 1 变成可以多次评估的东西,当。repeat 被评估。

这个东西通常是一个 抽象语法树(AST)或可能是一个清单 虚拟机 操作。在Scheme中,你也可以直接把被解析的表达式变成函数。

所有这些都是完全实用的,甚至不是特别困难,但你确实需要准备好做一些阅读,包括关于解析和关于生成可执行文件。对于后者,我强烈推荐经典的 计算机程序的结构和解释 (Abelson & Sussman).

以上是关于使用 lexyacc 在 racket 中实现一个类似 C 语言的程序。的主要内容,如果未能解决你的问题,请参考以下文章

在 Racket 中,列表相对于向量的优势是啥?

使用 VIM 的编程方案(Racket) - 如何开始

我需要安装 PLT Racket 的帮助,我将 Racket 文件夹移到 Applications 但我不知道如何在终端中使用 raco 命令

Racket里的方括号

如何确定一个数字是不是是列表中的最大元素 - 使用 Racket

sicp 中的lisp的语法(使用racket编译)