Я пишу небольшой интерпретатор в Racket, используя parser-tools / lex и parser-tools / yacc. Как часть проекта я хочу визуализировать s-выражения, которые генерирует мой парсер. Вот код
(require parser-tools/lex
parser-tools/yacc
(prefix-in re: parser-tools/lex-sre)
(only-in pict cc-superimpose disk filled-rectangle text)
pict/tree-layout)
(provide parse draw)
(define-empty-tokens empty-tokens
(PLUS MINUS MULT DIV
LPAREN RPAREN COMMA
IF THEN ELSE END
FOR NEXT STEP TO
DEFINE
EQUALS SEP EOF
GT LT
NOT AND OR
TRUE FALSE
QUOTE SET))
(define-tokens value-tokens (INT ID))
(define basic-lexer
(lexer
(#\+ (token-PLUS))
(#\- (token-MINUS))
(#\* (token-MULT))
(#\/ (token-DIV))
(#\= (token-EQUALS))
(#\> (token-GT))
(#\< (token-LT))
(#\( (token-LPAREN))
(#\) (token-RPAREN))
(#\, (token-COMMA))
("var" (token-DEFINE))
("if" (token-IF))
("then" (token-THEN))
("else" (token-ELSE))
("end" (token-END))
("for" (token-FOR))
("to" (token-TO))
("next" (token-NEXT))
("step" (token-STEP))
("not" (token-NOT))
("and" (token-AND))
("or" (token-OR))
("true" (token-TRUE))
("false" (token-FALSE))
("literal" (token-QUOTE))
("set" (token-SET))
((re:or #\tab #\space) (basic-lexer input-port))
(#\newline (token-SEP))
((re:seq #\return #\newline) (token-SEP))
((re:+ numeric) (token-INT (string->number lexeme)))
((re:+ alphabetic) (token-ID (string->symbol lexeme)))
((eof) (token-EOF))))
(define basic-parser
(parser
(start start)
(end EOF)
(tokens value-tokens empty-tokens)
(error (lambda (ok? name value)
(printf "Couldn't parse: ~a\n" name)))
(grammar
(start
((sep-opt expr-list sep-opt) $2))
(expr-list
((expr) (list 'begin $1))
((expr-list sep expr) (append $1 (list $3))))
(expr
((or-expr) $1)
((var-def) $1)
((proc-call) $1)
((if-expr) $1)
((for-expr) $1)
((quote-expr) $1)
((assign-expr) $1))
(assign-expr
((SET ID EQUALS expr) (list 'set! $2 $4)))
(quote-expr
((QUOTE expr) (list 'quote $2)))
(for-expr
((FOR ID EQUALS or-expr TO or-expr
sep-opt expr-list
sep-opt NEXT) (list 'do (list (list $2 $4 (list '+ $2 1))) (list (list '> $2 $6)) $8)))
(if-expr
((IF expr THEN sep-opt
expr-list sep-opt END IF) (list 'if $2 $5))
((IF expr THEN sep-opt
expr-list sep-opt
ELSE sep-opt
expr-list sep-opt END IF) (list 'if $2 $5 $9)))
(proc-call
((ID LPAREN RPAREN) (list $1))
((ID LPAREN arg-list RPAREN) (cons $1 $3)))
(arg-list
((expr) (list $1))
((arg-list COMMA expr) (append $1 (list $3))))
(var-def
((DEFINE ID EQUALS expr) (list 'define $2 $4)))
(or-expr
((or-expr OR and-expr) (list 'or $1 $3))
((and-expr) $1))
(and-expr
((and-expr AND not-expr) (list 'and $1 $3))
((not-expr) $1))
(not-expr
((NOT not-expr) (list 'not $2))
((compare-expr) $1))
(compare-expr
((compare-expr EQUALS add-expr) (list '= $1 $3))
((compare-expr GT add-expr) (list '> $1 $3))
((compare-expr LT add-expr) (list '< $1 $3))
((add-expr) $1))
(add-expr
((add-expr PLUS mult-expr) (list '+ $1 $3))
((add-expr MINUS mult-expr) (list '- $1 $3))
((mult-expr) $1))
(mult-expr
((mult-expr MULT value) (list '* $1 $3))
((mult-expr DIV value) (list '/ $1 $3))
((value) $1))
(value
((INT) $1)
((ID) $1)
((TRUE) #t)
((FALSE) #f)
((LPAREN expr RPAREN ) $2))
(sep-opt
((sep) null)
(() null))
(sep
((sep SEP) null)
((SEP) null)))))
(define (parse str)
(let ((port (open-input-string str)))
(basic-parser
(lambda () (basic-lexer port)))))
;;; /11760164/vizualiziruite-proizvolnoe-derevo-v-racket-ispolzuya-maket-dereva
(define (draw tree)
(define (viz tree)
(cond
((null? tree) #f)
((not (pair? tree))
(tree-layout #:pict (cc-superimpose
(filled-rectangle 44 22 #:color "white")
(text (token->string tree)))))
((not (pair? (car tree)))
(apply tree-layout (map viz (cdr tree))
#:pict (cc-superimpose
(filled-rectangle 44 22 #:color "white")
(text (token->string (car tree))))))))
(if (null? tree)
#f
(naive-layered (viz tree))))
(define (token->string token)
(cond ((symbol? token) (symbol->string token))
((number? token) (number->string token))
((boolean? token) (if token "#t" "#f"))
((void? token) "void")
(else "")))
У меня есть небольшая функция для визуализации сгенерированных выражений с использованием макета pict / tree, который я получил из Stack Overflow. Это работает для простых выражений, но я приступил к разбору циклов. Разобранный текст -
(parse "for i = 1 to 10
display(i)
next")
Сгенерированное выражение -
'(begin (do ((i 1 (+ i 1))) ((> i 10)) (begin (display i))))
Но моя функция рисования выдает мне эту ошибку
tree-layout: contract violation
expected: (or/c tree-edge? tree-layout? #f)
given: #<void>
in: an element of
the rest argument of
(->*
()
(#:pict pict-convertible?)
#:rest
(listof (or/c tree-edge? tree-layout? #f))
tree-layout?)
contract from:
<pkgs>/pict-lib/pict/tree-layout.rkt
blaming: C:\Users\uros.calakovic\DODO-RKT\dodo.rkt
(assuming the contract is correct)
at: <pkgs>/pict-lib/pict/tree-layout.rkt:14.10
, которую, как мне кажется, я имею в виду, потому что у меня естьвложенные выражения. Я пытался поймать пустоту Ракета в функции
((void? token) "void")
Но я получаю ту же ошибку.