;;; Infix-to-Prefix --- Library for converting infix formula to Prefix expression ;;; Copyright © 2024 Masaya Tojo <masaya@tojo.tokyo> ;;; ;;; This file is part of Infix-to-Prefix. ;;; ;;; Infix-to-Prefix is free software: you can redistribute it and/or ;;; modify it under the terms of the GNU General Public License as ;;; published by the Free Software Foundation, either version 3 of the ;;; License, or (at your option) any later version. ;;; ;;; Infix-to-Prefix is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with Infix-to-Prefix. If not, see ;;; <https://www.gnu.org/licenses/>. (define-library (infix-to-prefix) (export infix->prefix prefix->infix current-operator-rule-set) (import (scheme base) (scheme case-lambda) (infix-to-prefix rule-set) (only (srfi 1) car+cdr fold break! reverse! append! append-map! append-reverse!) (only (srfi 26) cut cute)) (begin (define (make-default-operator-rule-set) (rule-set (list (operator '= 0) (operator '+ 1 (direction 'left #t) (identity 0)) (operator '- 1 (direction 'left) (identity 0 #t #t 3)) (operator '* 2 (direction 'left #t) (identity 1)) (operator '/ 2 (direction 'left) (identity 1 #t)) (operator '^ 4 (direction 'right) #f (prefix #t 'expt))))) (define current-operator-rule-set (make-parameter (make-default-operator-rule-set))) (define infix->prefix (case-lambda ((expr failure) (call/cc (lambda (return) (let ((rs (current-operator-rule-set))) (map-all-list (cute infix->prefix-1 <> rs (lambda (e) (return (failure e)))) expr))))) ((expr) (infix->prefix expr (lambda (e) #f))))) (define (infix->prefix-1 expr rs fail) (if (and (pair? expr) (null? (cdr expr))) (car expr) (let-values (((op unary?) (minimum-precedence expr rs))) (if op (let ->prefix ((expr (list-copy expr)) (op op) (unary? unary?)) (define (make-prefix left op-sym right) (define (->infix left op-sym right) (append left (cons op-sym right))) (let-values (((left-op left-unary?) (minimum-precedence left rs)) ((right-op right-unary?) (minimum-precedence right rs))) (let ((not-binary-only? (not (operator-prefix-binary-only? op)))) `(,(operator-prefix-symbol op) ,@(if (operator? left-op) (if (eqv? (operator-symbol op) (operator-symbol left-op)) (if not-binary-only? (if (and (eqv? op-sym (car left)) (pair? (cdr left)) (null? (cdr (cdr left)))) (list (->prefix left left-op left-unary?)) (cdr (->prefix left left-op left-unary?))) (if (operator-left? op) (list (->prefix left left-op left-unary?)) (fail expr))) (list (->prefix left left-op left-unary?))) (if (and (pair? left) (null? (cdr left))) (if (and not-binary-only? (or (operator-left? op) (operator-associative? op)) (pair? (car left)) (eqv? (operator-symbol op) (car (car left)))) (cdr (car left)) (list (car left))) (fail (->infix left op-sym right)))) ,@(if (operator? right-op) (if (eqv? (operator-symbol op) (operator-symbol right-op)) (if not-binary-only? (cdr (->prefix right right-op right-unary?)) (if (operator-right? op) (list (->prefix right right-op right-unary?)) (fail expr))) (list (->prefix right right-op right-unary?))) (if (and (pair? right) (null? (cdr right))) (if (and not-binary-only? (or (operator-right? op) (operator-associative? op)) (pair? (car right)) (eqv? (operator-symbol op) (car (car right)))) (cdr (car right)) (list (car right))) (fail (->infix left op-sym right)))))))) (cond (unary? (let-values (((lst op+rest) (break! (cute eqv? (operator-symbol op) <>) expr))) (let-values (((op-sym rest) (car+cdr op+rest))) (infix->prefix-1 (append lst (list (list op-sym (infix->prefix-1 rest rs fail)))) rs fail)))) ((operator-left? op) (let ((rev-expr (reverse! expr))) (let-values (((rev-lst op+rev-rest) (break! (cute eqv? (operator-symbol op) <>) rev-expr))) (let-values (((op-sym rev-rest) (car+cdr op+rev-rest))) (make-prefix (reverse! rev-rest) op-sym (reverse! rev-lst)))))) (else (let-values (((lst op+rest) (break! (cute eqv? (operator-symbol op) <>) expr))) (let-values (((op rest) (car+cdr op+rest))) (make-prefix lst op rest)))))) expr)))) (define prefix->infix (case-lambda ((expr failure) (let ((rs (current-operator-rule-set))) (call-with-current-continuation (lambda (return) (let-values (((result _precedence) (%prefix->infix expr rs (lambda (e) (return (failure e)))))) result))))) ((expr) (prefix->infix expr (lambda (e) #f))))) (define (%prefix->infix expr rs failure) (let ->infix ((expr expr)) (define (->infix-fst expr) (let-values (((x _) (->infix expr))) x)) (if (not (pair? expr)) (values expr -inf.0) (let-values (((op-prefix-sym args) (car+cdr expr))) (cond ((rule-set-prefix-ref rs op-prefix-sym) => (lambda (op) (let ((p (operator-precedence op)) (op-sym (operator-symbol op))) (cond ((null? args) (cond ((and (not (operator-identity-inv? op)) (operator-identity op)) => (lambda (u) (values (identity-value u) -inf.0))) (else (failure expr)))) ((null? (cdr args)) (let-values (((r-expr r-p) (->infix (car args)))) (cond ((operator-identity op) => (lambda (u) (if (identity-inv? u) (if (identity-unary? u) (values `(,op-sym ,r-expr) (or (operator-identity-unary-precedence op) +inf.0)) (values `(,(identity-value (operator-identity op)) ,op-sym ,@(if (or (operator-right? op) (operator-associative? op)) (wrap-when (< r-p p) r-expr) (wrap-when (<= r-p p) r-expr))) p)) (values r-expr r-p)))) (else (failure expr))))) ((null? (cdr (cdr args))) (let-values (((l-expr l-p) (->infix (car args))) ((r-expr r-p) (->infix (cadr args)))) (values `(,@(if (or (operator-left? op) (operator-associative? op)) (wrap-when (< l-p p) l-expr) (wrap-when (<= l-p p) l-expr)) ,op-sym ,@(if (or (operator-right? op) (operator-associative? op)) (wrap-when (< r-p p) r-expr) (wrap-when (<= r-p p) r-expr))) p))) (else (cond ((operator-associative? op) (values (cdr (append-map! (lambda (arg) (let-values (((x-expr x-p) (->infix arg))) (cons op-sym (wrap-when (< x-p p) x-expr)))) args)) p)) ((operator-left? op) (let-values (((l-expr l-p) (->infix (car args)))) (values (append! (wrap-when (< l-p p) l-expr) (append-map! (lambda (arg) (let-values (((l-expr l-p) (->infix arg))) (cons op-sym (wrap-when (<= l-p p) l-expr)))) (cdr args))) p))) ((operator-right? op) (let ((rev-args (reverse args))) (let-values (((r-expr r-p) (->infix (car rev-args)))) (values (reverse! (append-reverse! (wrap-when (< r-p p) r-expr) (append-map! (lambda (arg) (let-values (((r-expr r-p) (->infix arg))) (cons op-sym (wrap-when (<= r-p p) r-expr)))) (cdr rev-args)))) p)))) (else (values (cdr (append-map! (lambda (arg) (let-values (((x-expr x-p) (->infix arg))) (cons op-sym (wrap-when (<= x-p p) x-expr)))) args)) p)))))))) (else (values (map ->infix-fst expr) -inf.0))))))) (define (minimum-precedence expr rs) (let loop ((expr expr) (min #f) (min-precedence +inf.0) (min-unary? #f) (prev #t)) (if (null? expr) (values min min-unary?) (cond ((rule-set-infix-ref rs (car expr)) => (lambda (current) (let-values (((precedence current-unary?) (if (and (operator-identity-unary? current) prev) (values (operator-identity-unary-precedence current) #t) (values (operator-precedence current) #f)))) (if (<= precedence min-precedence) (loop (cdr expr) current precedence current-unary? current) (loop (cdr expr) min min-precedence min-unary? current))))) (else (loop (cdr expr) min min-precedence min-unary? #f)))))) (define (operator-identity-inv? x) (cond ((operator-identity x) => (cut identity-inv? <>)) (else #f))) (define (operator-identity-unary? op) (cond ((operator-identity op) => identity-unary?) (else #f))) (define (operator-identity-unary-precedence op) (cond ((operator-identity op) => (lambda (id) (cond ((identity-unary-precedence id) => (lambda (p) p)) (else (operator-precedence op))))) (else #f))) (define (operator-prefix-binary-only? op) (cond ((operator-prefix op) => prefix-binary-only?) (else #f))) (define (wrap-when b? x) (if b? (list x) x)) (define (map-all-list f expr) (f (map-cars f expr))) (define (map-cars f expr) (if (pair? expr) (if (pair? (car expr)) (cons (f (map-cars f (car expr))) (map-cars f (cdr expr))) (cons (car expr) (map-cars f (cdr expr)))) expr)) ))