この SRFI では、プログラムを実行するのに必要となる Scheme のフィーチャや拡張機能の集合を記述するための設定言語について述べる。 絶対に必要なフィーチャのリストを記述するだけでなく、 特定のフィーチャやフィーチャの組み合わせが利用可能な場合にのみ実行する Scheme コードを記述することもできる。
この設定言語は Scheme 言語とはまったく異なるものである。 Scheme に埋め込む言語でもなく、Scheme をサブセットとして含む言語でもない。
設定言語を使用することにより、人間もプログラムも、プログラムの実行に必要なものを認識することが容易になる。 また、どのフィーチャやマクロがプログラム中で使用されているかを、マクロ展開によって確認する方法に起因する、 あいまいさを避けることも可能になる。
このような設定制御が必要となる論拠については、SRFI 0 を参照せよ。
<program> --> (program <program clause>+) <program clause> --> (requires <feature identifier>+) | (files <filename>*) | (code <Scheme expression, definition, or syntax definition>*) | (feature-cond <feature-cond clause>+) | (feature-cond <feature-cond clause>* (else <program clause>+)) <feature-cond clause> --> (<feature requirement> <program clause>+) <feature requirement> --> <feature identifier> | (and <feature requirement>*) | (or <feature requirement>*) | (not <feature requirement>) <feature identifier> --> a symbol which is the name of a SRFI
この設定言語は Scheme 言語とはまったく異なるものである。
利用可能なフィーチャの集合が与えられると、<program>
は
Scheme のコマンド、定義、構文定義のシーケンスに変換することができる。
この変換にはマクロの展開は必要ないし、Scheme コードを処理する必要もない。
この設定言語を実装するためには、プログラムを実行するための何らかの方法が必要になるだろう。
たとえば、(LOAD-PROGRAM <filename>)
関数や、
プログラムを実行ファイルにコンパイルするコンパイラが必要になるだろう。
<program>
内の各節の意味を以下で説明する。
<program>
内の各節の順序は、
生成される Scheme プログラムのフォームの順序となる。
<program>
内の REQUIRES
節および FEATURE-COND
節を処理する場合、利用可能または利用不可のフィーチャの集合を、一貫して使用しなければならない。
<program>
を解析することで、
必要なフィーチャの集合を決定するような実装を行うことは許されるが、
同じ <program>
内の節なのに、
異なるフィーチャ集合を使用するようなことがあってはならない。
(requires <feature-identifier>+)
(files <filename>*)
(code <body>)
(feature-cond <feature-cond clause>+)
FEATURE-COND
節の意味は、<implementation-requirement>
が満たされる最初の <feature-cond clause>
内の
<program-clause>
の意味になる。
もし ELSE
節が存在するなら、それより前の節の条件が満たされない場合に限り、
それが使用される。ELSE
節をもつ FEATURE-COND
は常に条件が満たされる。
どの節の条件も満たされない場合、<program>
を評価することはできません。
<implementation requirement>
の意味は次のとおり。
<feature identifier> |
フィーチャが存在すれば満たされる |
(and) |
常に満たされる |
(and x ...) |
すべての X が満たされれば満たされる |
(or) |
絶対に満たされない |
(or x ...) |
どれか 1 つの X でも満たされれば満たされる |
(not x) |
X が満たされなければ満たされる |
ここでは 2 つの実装を記載する。
最初の実装は PROCESS-PROGRAM
関数で、
これは S-式で表現された <program>
とフィーチャ識別子のリストを変換し、リスト式、定義、構文定義を返し、それがこれらのフィーチャのもとでの <program>
のソースとなる。
提供されるフィーチャを使用してそのプログラムを実行することができない場合、この関数は #F
を返す。
これは設定言語の完全な実装ではない。
PROCESS-PROGRAM
が返すフォームを評価するための (実装に固有の) 方法を与える必要がある。
(define (process-program program features) (call-with-current-continuation (lambda (exit) ; We exit early when an unsatisfiable clause is found. ; Process each clause in turn (define (process-clauses clauses) (if (null? clauses) '() (append (process-clause (car clauses)) (process-clauses (cdr clauses))))) ; Dispatch on the type of the clause. (define (process-clause clause) (case (car clause) ((requires) (if (all-satisfied? (cdr clause)) '() (exit #f))) ((code) (cdr clause)) ((files) (read-files (cdr clause))) ((feature-cond) (process-cond-clauses (cdr clause))))) ; Loop through CLAUSES finding the first that is satisfied. (define (process-cond-clauses clauses) (cond ((null? clauses) (exit #f)) ((or (and (eq? (caar clauses) 'else) (null? (cdr clauses))) (satisfied? (caar clauses))) (process-clauses (cdar clauses))) (else (process-cond-clauses (cdr clauses))))) ; Compound requirements are handled recursively, simple ones are tested. (define (satisfied? requirement) (if (pair? requirement) (case (car requirement) ((and) (all-satisfied? (cdr requirement))) ((or) (any-satisfied? (cdr requirement))) ((not) (not (satisfied? (cadr requirement))))) (memq requirement features))) ; True if every requirement in LIST is satisfied. (define (all-satisfied? list) (if (null? list) #t (and (satisfied? (car list)) (all-satisfied? (cdr list))))) ; True if any requirement in LIST is satisfied. (define (any-satisfied? list) (if (null? list) #f (or (satisfied? (car list)) (any-satisfied? (cdr list))))) ; Start by doing the whole program. (process-clauses (cdr program))))) ; Returns a list of the forms in the named files. (define (read-files filenames) (if (null? filenames) '() (append (call-with-input-file (car filenames) (lambda (in) (let label () (let ((next (read in))) (if (eof-object? next) '() (cons next (label))))))) (read-files (cdr filenames)))))
2 つめの実装は PROGRAM
マクロで、これは SRFI 0 の COND-EXPAND
構文を利用して設定言語を実装する。
この実装では、LOAD
は現在の評価環境を使用することが必要である。
(define-syntax program (syntax-rules (requires files code feature-cond) ((program) (begin)) ((program (requires feature-id ...) more ...) (begin (cond-expand ((and feature-id ...) 'okay)) (program more ...))) ((program (files filename ...) more ...) (begin (load filename) ... (program more ...))) ((program (code stuff ...) more ...) (begin stuff ... (program more ...))) ((program (feature-cond (requirement stuff ...) ...) more ...) (begin (cond-expand (requirement (program stuff ...)) ...) (program more ...)))))
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.