diff options
Diffstat (limited to 'simetrix.scm')
-rw-r--r-- | simetrix.scm | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/simetrix.scm b/simetrix.scm new file mode 100644 index 0000000..3a3f16b --- /dev/null +++ b/simetrix.scm @@ -0,0 +1,246 @@ +;;;; "simetrix.scm" SI Metric Interchange Format for Scheme +;;; Copyright (C) 2000, 2001 Aubrey Jaffer +; +;Permission to copy this software, to modify it, to redistribute it, +;to distribute modified versions, and to use it for any purpose is +;granted, subject to the following restrictions and understandings. +; +;1. Any copy made of this software must include this copyright notice +;in full. +; +;2. I have made no warrantee or representation that the operation of +;this software will be error-free, and I am under no obligation to +;provide any services, by way of maintenance, update, or otherwise. +; +;3. In conjunction with products arising from the use of this +;material, there shall be no use of my name in any advertising, +;promotional, or sales literature without prior written consent in +;each case. + +;; Implements "Representation of numerical values and SI units in +;; character strings for information interchanges" +;; http://swissnet.ai.mit.edu/~jaffer/MIXF.html + +(require 'precedence-parse) + +;;; Combine alists +(define (SI:adjoin unitlst SIms) + (for-each (lambda (new) + (define pair (assoc (car new) SIms)) + (if pair + (set-cdr! pair (+ (cdr new) (cdr pair))) + (set! SIms (cons (cons (car new) (cdr new)) SIms)))) + unitlst) + SIms) + +;;; Combine unit-alists +(define (SI:product unit1 unit2) + (define nunits '()) + (set! unit1 (SI:expand-unit unit1)) + (set! unit2 (SI:expand-unit unit2)) + (cond ((and unit1 unit2) + (set! nunits (SI:adjoin unit1 nunits)) + (set! nunits (SI:adjoin unit2 nunits)) + nunits) + (else #f))) + +(define (SI:quotient unit1 . units) + (apply SI:product unit1 + (map (lambda (unit) (SI:pow unit -1)) units))) + +(define (SI:pow unit expon) + (define punit (SI:expand-unit unit)) + (and punit (number? expon) + (map (lambda (unit-pair) + (cons (car unit-pair) (* (cdr unit-pair) expon))) + punit))) + +;;; Parse helper functions. +(define (SI:solidus . args) + (if (and (= 2 (length args)) + (number? (car args)) + (number? (cadr args))) + (/ (car args) (cadr args)) + (apply SI:quotient args))) + +(define (SI:e arg1 arg2) + (cond ((and (number? arg1) (number? arg2) + (exact? arg2)) + (let ((expo (string->number + (string-append "1e" (number->string arg2))))) + (and expo (* arg1 expo)))) + (else (SI:product arg1 arg2)))) + +(define (SI:dot arg1 arg2) + (cond ((and (number? arg1) (number? arg2) + (exact? arg1) (exact? arg2) + (positive? arg2)) + (string->number + (string-append (number->string arg1) "." (number->string arg2)))) + (else (SI:product arg1 arg2)))) + +(define (SI:minus arg) (and (number? arg) (- arg))) + +(define (SI:identity . args) (and (= 1 (length args)) (car args))) + +;;; Binary prefixes are (zero? (modulo expo 10)) +(define SI:prefix-exponents + '(("Y" 24) ("Z" 21) ("E" 18) ("P" 15) + ("T" 12) ("G" 9) ("M" 6) ("k" 3) ("h" 2) ("da" 1) + ("d" -1) ("c" -2) ("m" -3) ("u" -6) ("n" -9) + ("p" -12) ("f" -15) ("a" -18) ("z" -21) ("y" -24) + + ("Ei" 60) ("Pi" 50) ("Ti" 40) ("Gi" 30) ("Mi" 20) ("Ki" 10) + )) + +(define SI:unit-infos + `( + ("s" all #f) + ("min" none "60.s") + ("h" none "3600.s") + ("d" none "86400.s") + ("Hz" all "s^-1") + ("Bd" pos "s^-1") + ("m" all #f) + ("L" neg "dm^3") + ("rad" neg #f) + ("sr" neg "rad^2") + ("r" pos ,(string-append (number->string (* 8 (atan 1))) ".rad")) + ("o" neg ,(string-append (number->string (/ 360)) ".r")) + ("bit" bin #f) + ("B" pin "8.b") + ("g" all #f) + ("t" pos "Mg") + ("u" none "1.66053873e-27.kg") + ("mol" all #f) + ("kat" all "mol/s") + ("K" all #f) + ("oC" neg #f) + ("cd" all #f) + ("lm" all "cd.sr") + ("lx" all "lm/m^2") + ("N" all "m.kg/s^2") + ("Pa" all "N/m^2") + ("J" all "N.m") + ("eV" all "1.602176462e-19.J") + ("W" all "J/s") + ("Np" neg #f) + ("dB" none ,(string-append (number->string (/ (log 10) 20)) ".Np")) + ("A" all #f) + ("C" all "A.s") + ("V" all "W/A") + ("F" all "C/V") + ("Ohm" all "V/A") + ("S" all "A/V") + ("Wb" all "V.s") + ("T" all "Wb/m^2") + ("H" all "Wb/A") + ("Bq" all "s^-1") + ("Gy" all "m^2.s^-2") + ("Sv" all "m^2.s^-2") + )) + +(define (SI:try-split preSI SIm) + (define expo (assoc preSI SI:prefix-exponents)) + (define stuff (assoc SIm SI:unit-infos)) + (if expo (set! expo (cadr expo))) + (if stuff (set! stuff (cdr stuff))) + (and expo stuff + (let ((equivalence (cadr stuff))) + (and (case (car stuff) ;restriction + ((all) (not (zero? (modulo expo 10)))) + ((pos) (and (positive? expo) (not (zero? (modulo expo 10))))) + ((bin) #t) + ((pin) (positive? expo)) + ((neg) (and (negative? expo) (not (zero? (modulo expo 10))))) + ((none) #f) + (else #f)) + (if (and (positive? expo) (zero? (modulo expo 10))) + (if equivalence + (let ((eqv (SI:expand-equivalence equivalence))) + (and eqv + (SI:adjoin (list (cons 1024 (quotient expo 10))) + eqv))) + (list (cons 1024 (quotient expo 10)) + (cons SIm 1))) + (if equivalence + (let ((eqv (SI:expand-equivalence equivalence))) + (and eqv (SI:adjoin (list (cons 10 expo)) eqv))) + (list (cons 10 expo) (cons SIm 1)))))))) + +(define (SI:try-simple SIm) + (define stuff (assoc SIm SI:unit-infos)) + (if stuff (set! stuff (cdr stuff))) + (and stuff (if (cadr stuff) + (SI:expand-equivalence (cadr stuff)) + (list (cons SIm 1))))) + +(define (SI:expand-unit str) + (if (symbol? str) (set! str (symbol->string str))) + (cond + ((pair? str) str) + ((number? str) (list (cons str 1))) + ((string? str) + (let ((len (string-length str))) + (let ((s1 (and (> len 1) + (SI:try-split (substring str 0 1) (substring str 1 len)))) + (s2 (and (> len 2) + (SI:try-split (substring str 0 2) (substring str 2 len)))) + (sn (and (SI:try-simple str)))) + (define cnt (+ (if s1 1 0) (if s2 1 0) (if sn 1 0))) + (if (> cnt 1) (slib:warn 'ambiguous s1 s2 sn)) + (or s1 s2 sn)))) + (else #f))) + +(define (SI:expand-equivalence str) + (call-with-input-string + str (lambda (sport) + (define result (prec:parse SI:grammar 'EOS sport)) + (cond ((eof-object? result) (list (cons 1 0))) + ((symbol? result) (SI:expand-unit result)) + (else result))))) + +;;;; advertised interface +(define (SI:conversion-factor to-unit from-unit) + (let ((funit (SI:expand-equivalence from-unit)) + (tunit (SI:expand-equivalence to-unit))) + (if (and funit tunit) + (let loop ((unit-pairs (SI:quotient funit tunit)) + (flactor 1)) + (cond ((null? unit-pairs) flactor) + ((zero? (round (* 2 (cdar unit-pairs)))) + (loop (cdr unit-pairs) flactor)) + ((number? (caar unit-pairs)) + (loop (cdr unit-pairs) + ((if (negative? (cdar unit-pairs)) / *) + flactor + (expt (caar unit-pairs) + (abs (cdar unit-pairs)))))) + (else 0))) + (+ (if tunit 0 -1) (if funit 0 -2))))) + +(define SI:grammar #f) + +;;;; The parse tables. +;;; Definitions accumulate in top-level variable *SYN-DEFS*. +;;(trace-all (in-vicinity (program-vicinity) "simetrix.scm")) + +;;; Character classes +(prec:define-grammar (tok:char-group 70 #\^ list->string)) +(prec:define-grammar (tok:char-group 49 #\. list->string)) +(prec:define-grammar (tok:char-group 50 #\/ list->string)) +(prec:define-grammar (tok:char-group 51 #\- list->string)) +(prec:define-grammar (tok:char-group 40 tok:decimal-digits + (lambda (l) (string->number (list->string l))))) +(prec:define-grammar (tok:char-group 44 + (string-append tok:upper-case tok:lower-case "@_") + list->string)) + +(prec:define-grammar (prec:prefix '- SI:minus 130)) +(prec:define-grammar (prec:infix "." SI:dot 120 120)) +(prec:define-grammar (prec:infix '("e" "E") SI:e 115 125)) +(prec:define-grammar (prec:infix '/ SI:solidus 100 150)) +(prec:define-grammar (prec:infix '^ SI:pow 160 140)) +(prec:define-grammar (prec:matchfix #\( SI:identity #f #\))) + +(set! SI:grammar *syn-defs*) |