#lang scheme
(require parser-tools/yacc
parser-tools/lex
(prefix-in : parser-tools/lex-sre))
(define-tokens value-tokens (NUM VAR))
(define-empty-tokens op-tokens (OP CP + - * / ^ EOF NEG RAND RANK))
(define-lex-abbrevs
(lower-letter (:/ "a" "z"))
(upper-letter (:/ #\A #\Z))
(digit (:/ "0" "9")))
(define calcl
(lexer
[(eof) 'EOF]
[(:or #\tab #\space) (calcl input-port)]
[(:or "+" "-" "*" "/" "^") (string->symbol lexeme)]
["(" 'OP]
[")" 'CP]
["$rand" 'RAND]
["$rank" 'RANK]
[(:: "$" (:+ digit)) (token-VAR (string->number (substring lexeme 1)))]
[(:+ digit) (token-NUM (string->number lexeme))]
[(:: (:+ digit) #\. (:* digit)) (token-NUM (string->number lexeme))]))
(define (lift f)
(lambda xs
(if (andmap number? xs)
(apply f xs)
(lambda (rank params)
(apply f
(map (lambda (x)
(if (number? x) x
(x rank params)))
xs))))))
(define (unlift v)
(if (number? v)
(lambda (rank params) v)
v))
(define lifted-+ (lift +))
(define lifted-- (lift -))
(define lifted-* (lift *))
(define lifted-/ (lift /))
(define lifted-expt (lift expt))
(define calcp
(parser
(start exp)
(end EOF)
(tokens value-tokens op-tokens)
(error (lambda (a b c) (void)))
(precs (left - +)
(left * /)
(left NEG)
(right ^))
(grammar
(exp [(NUM) $1]
[(VAR) (lambda (rank params) (vector-ref params (sub1 $1)))]
[(RAND) (lambda (rank params) (random))]
[(RANK) (lambda (rank params) rank)]
[(exp + exp) (lifted-+ $1 $3)]
[(exp - exp) (lifted-- $1 $3)]
[(exp * exp) (lifted-* $1 $3)]
[(exp / exp) (lifted-/ $1 $3)]
[(- exp) (prec NEG) (lifted-- $2)]
[(exp ^ exp) (lifted-expt $1 $3)]
[(OP exp CP) $2]))))
(define (parse-expr s)
(define ip (open-input-string s))
(unlift (calcp (lambda () (calcl ip)))))
(provide/contract
[parse-expr (string? . -> . (number? (vectorof number?) . -> . number?))])