(module prefix-infix mzscheme
(provide prefix->infix
prefix->infix-xexpr generate-expression
equivalent-exprs?
)
(require (lib "plt-match.ss")
(lib "etc.ss"))
(define operator?
(lambda (s)
(member s '(expt + - * /))))
(define op>
(lambda (outer-op inner-op)
(or (eq? outer-op 'expt)
(and (member inner-op '(+ -))
(member outer-op '(* /))))))
(define (precedence> outer-op inner-op)
(op> outer-op inner-op))
(define (precedence=? op1 op2)
(or (and (memq op1 '(+ -))
(memq op2 '(+ -)))
(and (memq op1 '(* /))
(memq op2 '(* /)))))
(define (needs-parens? outer-op expr side)
(match expr
((? number? expr) (and (negative? expr)
(eq? outer-op 'expt)))
(`(,sub-op ,e1 ,e2)
(or (precedence> outer-op sub-op)
(precedence=? outer-op sub-op)))
(else #f)))
(define (equivalent-exprs? e1 e2)
(match e1
((? number? e1)
(and (number? e2) (= e1 e2)))
((? operator? e1)
(and (operator? e2) (eq? e1 e2)))
((? symbol? e1) (and (symbol? e2) (eq? e1 e2)))
(`(,e1-op ,e-sub1 ,e-sub2)
(match e2
(`(,e2-op ,e2-sub1 ,e2-sub2)
(and (eq? e1-op e2-op)
(case e1-op
((+ *) (or (and (equivalent-exprs? e-sub1 e2-sub1)
(equivalent-exprs? e-sub2 e2-sub2))
(and (equivalent-exprs? e-sub1 e2-sub2)
(equivalent-exprs? e-sub2 e2-sub1))))
(else
(and (equivalent-exprs? e-sub1 e2-sub1)
(equivalent-exprs? e-sub2 e2-sub2))))))
(`(sqr ,e2-sub1)
(and (eq? e1-op 'sqr)
(and (number? e-sub2) (= e-sub2 2))
(equivalent-exprs? e-sub1 e2-sub1)))
(else #f)))
(else
(error 'equivalent-exprs? "malformed expr: ~a" e1))))
(define prefix->infix
(lambda (exp)
(match exp
((? number? exp)
(format "~a" exp))
((? symbol? exp)
(format "~a" exp))
('(expt)
"^")
((? operator? exp)
(format "~a" exp))
(`(,op ,exp1 ,exp2)
(format "~a ~a ~a"
(if (needs-parens? op exp1 'left)
(format "(~a)" (prefix->infix exp1))
(prefix->infix exp1))
(prefix->infix op)
(if (needs-parens? op exp2 'right)
(format "(~a)" (prefix->infix exp2))
(prefix->infix exp2)))))))
(define (fix-parens-xexpr outer-op expr side)
(let ((expr-x (prefix->infix-xexpr expr)))
(if (needs-parens? outer-op expr side)
`(span ((class "expression"))
"(" ,expr-x ")")
expr-x)))
(define prefix->infix-xexpr
(lambda (exp)
(match exp
((? number? exp)
(format "~a" exp))
((? symbol? exp)
(format "~a" exp))
(`(expt ,exp1 ,exp2)
`(span ((class "expression"))
(span ((class "base"))
,(fix-parens-xexpr 'expt exp1 'left))
(sup ((class "exponent"))
,(fix-parens-xexpr 'expt exp2 'right))))
(`(,op ,exp1 ,exp2)
`(span ((class "expression"))
,(fix-parens-xexpr op exp1 'left)
nbsp
,(symbol->string op)
nbsp
,(fix-parens-xexpr op exp2 'right))))))
(define (random-op using-expt?)
(let ((ops (if using-expt? '(expt + - * /) '(+ - * /))))
(list-ref ops (random (length ops)))))
(define VARS '(m n x y z w p q r))
(define VAR-FREQUENCY 3) (define NEG-FREQUENCY 3) (define MAX-OPERAND-MAGNITUDE 30)
(define (random-operand using-vars? using-negatives?)
(let ((var? (and using-vars? (zero? (random VAR-FREQUENCY)))))
(if var?
(list-ref VARS (random (length VARS)))
(let ((n (add1 (random MAX-OPERAND-MAGNITUDE)))
(neg? (and using-negatives?
(zero? (random NEG-FREQUENCY)))))
(if neg? (- n) n)))))
(define generate-expression
(opt-lambda (n [using-vars? #f] [using-negatives? #f] [using-expt? #f])
(let loop ((n n))
(cond
((zero? n) (random-operand using-vars? using-negatives?))
(else
`(,(random-op using-expt?)
,(loop (random n))
,(loop (random n))))))))
)