aboutsummaryrefslogtreecommitdiff
;;; 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 rule-set)
  (export rule-set
          rule-set?
          rule-set-infix-ref
          rule-set-prefix-ref

          operator
          operator?
          operator-symbol
          operator-prefix-symbol
          operator-precedence
          operator-left?
          operator-right?
          operator-associative?
          operator-prefix
          operator-identity

          identity
          identity?
          identity-value
          identity-inv?
          identity-unary?
          identity-unary-precedence

          direction
          direction?
          direction-left?
          direction-associative?

          prefix
          prefix?
          prefix-symbol
          prefix-binary-only?)
  (import (scheme base)
          (scheme case-lambda)
          (only (srfi 128) make-eqv-comparator))
  (cond-expand
    ((library (srfi srfi-146 hash)) ;; for guile-srfi-146
     (import (only (srfi srfi-146 hash) hashmap-ref/default hashmap-unfold)))
    ((library (srfi 146 hash))
     (import (only (srfi 146 hash) hashmap-ref/default hashmap-unfold))))
  (begin
    (define-record-type <rule-set>
      (make-rule-set operator-hashmap prefix-hashmap)
      rule-set?
      (operator-hashmap rule-set-operator-hashmap)
      (prefix-hashmap rule-set-prefix-hashmap))

    (define (rule-set operator-list)
      (make-rule-set (list->operator-hashmap operator-list)
                     (list->prefix-hashmap operator-list)))

    (define (list->operator-hashmap ops)
      (hashmap-unfold null?
                      (lambda (ops)
                        (values (operator-symbol (car ops))
                                (car ops)))
                      cdr
                      ops
                      (make-eqv-comparator)))

    (define (list->prefix-hashmap ops)
      (hashmap-unfold null?
                      (lambda (ops)
                        (let ((op (car ops)))
                          (values (operator-prefix-symbol op)
                                  (car ops))))
                      cdr
                      ops
                      (make-eqv-comparator)))

    (define (rule-set-infix-ref rule-set key)
      (hashmap-ref/default (rule-set-operator-hashmap rule-set) key #f))

    (define (rule-set-prefix-ref rule-set key)
      (hashmap-ref/default (rule-set-prefix-hashmap rule-set) key #f))

    (define-record-type <operator>
      (make-operator symbol precedence dir identity prefix)
      operator?
      (symbol operator-symbol)
      (precedence operator-precedence)
      (dir operator-direction)
      (identity operator-identity)
      (prefix operator-prefix))

    (define operator
      (case-lambda
        ((symbol precedence)
         (operator symbol precedence #f))
        ((symbol precedence direction)
         (operator symbol precedence direction #f))
        ((symbol precedence direction identity)
         (operator symbol precedence direction identity #f))
        ((symbol precedence direction identity prefix)
         (when (and identity (identity-unary? identity)
                    (not (and direction (direction-left? direction))))
           (error "operator: unary operator must be left direction" symbol))
         (make-operator symbol precedence direction identity prefix))))

    (define-record-type <direction>
      (make-direction left? associative?)
      direction?
      (left? direction-left?)
      (associative? direction-associative?))

    (define (direction-right? assoc)
      (not (direction-left? assoc)))

    (define direction
      (case-lambda
        ((dir)
         (direction dir #f))
        ((dir associative?)
         (unless (or (eq? 'left dir)
                     (eq? 'right dir))
           (error "direction: The 1st argument must be 'left or 'right" dir))
         (make-direction (eq? 'left dir) associative?))))

    (define (operator-left? x)
      (cond ((operator-direction x) => direction-left?)
            (else #f)))

    (define (operator-right? x)
      (cond ((operator-direction x) => (lambda (a) (not (direction-left? a))))
            (else #f)))

    (define (operator-associative? x)
      (cond ((operator-direction x) => direction-associative?)
            (else #f)))

    (define (operator-prefix-symbol op)
      (cond ((operator-prefix op) => (lambda (p)
                                       (and (prefix-has-symbol? p)
                                            (prefix-symbol p))))
            (else (operator-symbol op))))

    (define-record-type <prefix>
      (make-prefix binary-only? has-symbol? symbol)
      prefix?
      (binary-only? prefix-binary-only?)
      (has-symbol? prefix-has-symbol?)
      (symbol prefix-symbol))

    (define prefix
      (case-lambda
        ((binary-only?)
         (make-prefix binary-only? #f #f))
        ((binary-only? symbol)
         (make-prefix binary-only?  #t symbol))))

    (define-record-type <identity>
      (make-identity value inv? unary? unary-precedence)
      identity?
      (value identity-value)
      (inv? identity-inv?)
      (unary? identity-unary?)
      (unary-precedence identity-unary-precedence))

    (define identity
      (case-lambda
        ((value) (identity value #f))
        ((value inv?) (identity value inv? #f))
        ((value inv? unary?) (identity value inv? unary? #f))
        ((value inv? unary? unary-precedence) (make-identity value inv? unary? unary-precedence))))))