;; ;; NEMO ;; ;; Copyright 2008-2012 Ivan Raikov and the Okinawa Institute of ;; Science and Technology. ;; ;; This program 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. ;; ;; This program 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. ;; ;; A full copy of the GPL license can be found at ;; . ;; (import files setup-api srfi-1 srfi-4 srfi-13 srfi-69) (define deployed? (make-parameter #f)) ;; support for deployment (let ((program-name (car (argv)))) (let ((program-dir (or (pathname-directory program-name) (let ((path (string-split (get-environment-variable "PATH") ":"))) (let recur ((path path)) (if (null? path) #f (let ((fpath (make-pathname (car path) program-name))) (if (file-exists? fpath) fpath (recur (cdr path))) ))) )))) (deployed? (not (file-exists? (make-pathname program-dir "csi")))) (if (deployed?) (repository-path program-dir)))) (require-extension nemo-core nemo-macros nemo-hh nemo-vclamp nemo-utils) (require-library iexpr) (require-extension datatype matchable lalr-driver ssax sxml-transforms sxpath sxpath-lolevel getopt-long) (import (prefix iexpr iexpr: )) (define-datatype nemo:model nemo:model? (ModelSource (source-path string?) (in-format symbol?) (name symbol?) (decls list?) (iexpr boolean?) (parse-expr procedure?)) (SingleModel (source-path string?) (name symbol?) (sys hash-table?) (decls list?) (iexpr boolean?) (parse-expr procedure?)) (ModelPart (source-path string?) (name symbol?) (part-name symbol?) (sys hash-table?) (decls list?) (iexpr boolean?) (parse-expr procedure?)) ) (define nemo-nmodl? (extension-information 'nemo-nmodl)) (define nemo-matlab? (extension-information 'nemo-matlab)) (define nemo-nest? (extension-information 'nemo-nest)) (define nemo-pyparams? (extension-information 'nemo-pyparams)) (define nemo-eqn? (extension-information 'nemo-eqn)) (if nemo-nmodl? (use nemo-nmodl)) (if nemo-matlab? (use nemo-matlab)) (if nemo-nest? (use nemo-nest)) (if nemo-pyparams? (use nemo-pyparams)) (if nemo-eqn? (use nemo-eqn)) (define (lookup-def k lst . rest) (let-optionals rest ((default #f)) (let ((kv (assoc k lst))) (if (not kv) default (match kv ((k v) v) (else (cdr kv))))))) (define ($ x) (and x (string->symbol (->string x)))) ;;; Procedures for string concatenation and pretty-printing (define (s+ . lst) (string-concatenate (map ->string lst))) (define (sw+ lst) (string-intersperse (filter-map (lambda (x) (and x (->string x))) lst) " ")) (define (s\ p . lst) (string-intersperse (map ->string lst) p)) (define (sl\ p lst) (string-intersperse (map ->string lst) p)) (define nl "\n") (define (warn port message . specialising-msgs) (print-error-message message (current-output-port) "Warning") (print (string-concatenate (map ->string specialising-msgs)))) ;;; Error procedure for the XML parser (define (parser-error port message . specialising-msgs) (error (string-append message (string-concatenate (map ->string specialising-msgs))))) (define ssax:warn warn) (define opt-defaults `( (nmodl-kinetic . all) )) (define (defopt x) (lookup-def x opt-defaults)) (define opt-grammar `( (input-format "specify input format (nemo, xml, ixml, sxml, s-exp)" (single-char #\i) (value (required FORMAT) (transformer ,string->symbol))) (partition "partition generated model code into individual parts for each current" (single-char #\p)) (surface-xml "write surface XML translation of input to file (default: .xml)" (value (optional FILENAME) )) (plain "write plain text output to file (default: .txt)" (value (optional FILENAME) )) (xml "write XML output to file (default: .xml)" (value (optional FILENAME) )) (sxml "write SXML output to file (default: .sxml)" (value (optional FILENAME) )) (hh-markov "convert HH rate equations to Markov chain form") ,@(if nemo-eqn? `( (eqn "write EQN output to the given file (default: .eqn)" (value (optional FILENAME))) ) `()) ,@(if nemo-nest? `( (nest "write NEST output files .cpp and .h in the given directory (default: .)" (value (optional DIRNAME))) ) `()) ,@(if nemo-matlab? `((matlab "write MATLAB output in the given directory (default: .)" (value (optional DIRNAME))) (octave "write Octave output to given file (default: .m)" (value (optional FILENAME))) (octave-method "specify Octave integration method (lsode or odepkg)" (value (required METHOD) (transformer ,string->symbol))) ) `()) ,@(if nemo-nmodl? `( (nmodl "write NMODL output to file (default: .mod)" (value (optional FILENAME))) (nmodl-kinetic ,(s+ "use NMODL kinetic equations for the given reactions " "(or for all reactions)") (value (optional STATES) (default ,(defopt 'nmodl-kinetic)) (transformer ,(lambda (x) (if (string=? x "all") 'all (map string->symbol (string-split x ","))))))) (nmodl-method "specify NMODL integration method" (value (required METHOD) (transformer ,string->symbol))) (nmodl-depend "specify DEPEND variables for NMODL interpolation tables" (value (required VARS) (transformer ,(lambda (x) (map string->symbol (string-split x ",")))))) ) `()) (vclamp-hoc "write voltage clamp scripts to HOC file (default: .(ses|hoc))" (value (optional FILENAME) )) (vclamp-octave "write voltage clamp script to Octave file (default: _vclamp.m)" (value (optional FILENAME) )) (debug "print additional debugging information") (help (single-char #\h)) )) ;; Use args:usage to generate a formatted list of options (from OPTS), ;; suitable for embedding into help text. (define (nemo:usage) (print "Usage: " (car (argv)) " [options...] ") (newline) (print "The following options are recognized: ") (newline) (print (parameterize ((indent 5) (width 30)) (usage opt-grammar))) (exit 1)) ;; Process arguments and collate options and arguments into OPTIONS ;; alist, and operands (filenames) into OPERANDS. You can handle ;; options as they are processed, or afterwards. (define opts (getopt-long (command-line-arguments) opt-grammar)) (define opt (make-option-dispatch opts opt-grammar)) (define (ncml:sxpath query doc) ((sxpath query '((ncml . "ncml"))) doc)) (define (ncml:car-sxpath query doc) (let ((lst ((sxpath query '((ncml . "ncml") )) doc))) (car lst))) (define (ncml:if-car-sxpath query doc) (let ((lst ((sxpath query '((ncml . "ncml") )) doc))) (and (not (null? lst)) (car lst)))) (define (ncml:if-sxpath query doc) (let ((lst ((sxpath query '((ncml . "ncml") )) doc))) (and (not (null? lst)) lst))) (define (ncml-binding->binding node) (match node (('ncml:bnd ('@ ('id id)) ('ncml:expr expr)) `(,($ id) ,(ncml-expr->expr expr))) (else (error 'ncml-binding->binding "invalid binding " node)))) (define (ncml-expr->expr node) (match node ((? number?) node) ((? string?) (sxml:number node)) (('ncml:id id) ($ id)) (('ncml:apply ('@ ('id id)) . args) (cons ($ id) (map ncml-expr->expr args))) (('ncml:let ('ncml:bnds . bnds) ('ncml:expr body)) `(let ,(map ncml-binding->binding bnds) ,(ncml-expr->expr body))) (((and op (? symbol?)) . args) (cons (ncml-op->op op) (map ncml-expr->expr args))) (else (error 'ncml-expr->expr "unknown expression " node)))) (define (ncml-op->op op) (case op ((ncml:sum) '+) ((ncml:sub) '-) ((ncml:mul) '*) ((ncml:div) '/) ((ncml:gt) '>) ((ncml:lt) '<) ((ncml:lte) '<=) ((ncml:gte) '>=) ((ncml:eq) '=) (else (match (string-split (->string op) ":") ((pre op) (string->symbol op)) (else (error 'ncml-op->op "invalid operator" op)))))) (define (nemo-constructor name declarations parse-expr) (let* ((nemo (make-nemo-core)) (sys ((nemo 'system) name)) (qs (eval-nemo-system-decls nemo name sys declarations parse-expr))) (list sys nemo qs))) (define (sexp->model-decls doc) (match doc ((or ('nemo-model model-name model-decls) ('nemo-model (model-name . model-decls))) (list model-name model-decls)) (else (error 'sexp->model "unknown model format")) )) (define (sexp->model options model-name model-decls parse-expr) (let* ((model+nemo (nemo-constructor model-name model-decls parse-expr)) (model (first model+nemo)) (nemo (second model+nemo))) (let ((model-1 (nemo:hh-transformer model (alist-ref 'hh-markov options) parse-expr))) (if (assoc 'depgraph options) (print "dependency graph: " ((nemo 'depgraph*) model-1))) (if (assoc 'exports options) (print "exports: " ((nemo 'exports) model-1))) (if (assoc 'imports options) (print "imports: " ((nemo 'imports) model-1))) (if (assoc 'components options) (for-each (lambda (x) (print "component " x ": " ((nemo 'component-exports) model-1 (second x))) (print "component " x " subcomponents: " ((nemo 'component-subcomps) model-1 (second x)))) ((nemo 'components) model-1))) model-1))) (define model->nmodl (if nemo-nmodl? (lambda (options model) (nemo:nmodl-translator model (lookup-def 'method options) (lookup-def 'kinetic options) )) (lambda (options model) (void)))) (define model->nest (if nemo-nest? (lambda (options model) (nemo:nest-translator model (lookup-def 'dirname options))) (lambda (options model) (void)))) (define model->eqn (if nemo-eqn? (lambda (options model) (nemo:eqn-translator model (lookup-def 'filename options))) (lambda (options model) (void)))) (define model->matlab (if nemo-matlab? (lambda (options model) (nemo:matlab-translator model #f (lookup-def 'dirname options))) (lambda (options model) (void)))) (define model->vclamp-hoc (lambda (options model) (nemo:vclamp-translator model 'hoc (lookup-def 'filename options)))) (define model->vclamp-octave (lambda (options model) (nemo:vclamp-translator model 'matlab (lookup-def 'filename options)))) (define model->octave (if nemo-matlab? (lambda (options model) (nemo:octave-translator model (lookup-def 'method options) (lookup-def 'filename options) (lookup-def 'dirname options))) (lambda (options model) (void)))) (define (transition->ncml-transition x) (match x (('-> src dst rate) `((ncml:transition (@ (src ,src) (dst ,dst)) (ncml:rate ,(expr->ncml-expr rate))))) ((src '-> dst rate) `((ncml:transition (@ (src ,src) (dst ,dst)) (ncml:rate ,(expr->ncml-expr rate))))) (('<-> src dst rate1 rate2) `((ncml:transition (@ (src ,src) (dst ,dst)) (ncml:rate ,(expr->ncml-expr rate1))) (ncml:transition (@ (src ,dst) (dst ,src)) (ncml:rate ,(expr->ncml-expr rate2))))) ((src '<-> dst rate1 rate2) `((ncml:transition (@ (src ,src) (dst ,dst)) (ncml:rate ,(expr->ncml-expr rate1))) (ncml:transition (@ (src ,dst) (dst ,src)) (ncml:rate ,(expr->ncml-expr rate2))))) (else (error 'transition->ncml-transition "invalid transition " x)))) (define (conseq->ncml-conseq parse-expr) (lambda (x) (match x (((and i (? integer?)) '= rhs) `(ncml:conseq (@ (val ,(->string i))) (ncml:expr ,(expr->ncml-expr (parse-expr rhs))))) (else (error 'conseq->ncml-conseq "invalid linear equation " x))))) (define builtin-fns `(+ - * / pow neg abs atan asin acos sin cos exp ln sqrt tan cosh sinh tanh hypot gamma lgamma log10 log2 log1p ldexp cube > < <= >= = and or round ceiling floor max min)) (define (binding->ncml-binding bnd) (match bnd ((id expr) `(ncml:bnd (@ (id ,id)) (ncml:expr ,(expr->ncml-expr expr)))) (else (error 'binding->ncml-binding "invalid binding " bnd)))) (define (expr->ncml-expr x) (match x ((? number?) x) ((? symbol?) `(ncml:id ,x)) (('let bnds expr) `(ncml:let (ncml:bnds . ,(map binding->ncml-binding bnds)) (ncml:expr ,(expr->ncml-expr expr)))) (((and op (? symbol?)) . args) (let ((ncml-expr (if (member op builtin-fns) (cons (op->ncml-op op) (map expr->ncml-expr args)) `(ncml:apply (@ (id ,op)) ,@(map expr->ncml-expr args))))) ncml-expr)) (else (error 'expr->ncml-expr "unknown expression " x)))) (define (op->ncml-op op) (case op ((+) 'ncml:sum) ((-) 'ncml:sub) ((*) 'ncml:mul) ((/) 'ncml:div) ((>) 'ncml:gt) ((<) 'ncml:lt) ((<=) 'ncml:lte) ((>=) 'ncml:gte) ((=) 'ncml:eq) (else (string->symbol (string-append "ncml:" (->string op)))))) (define (declaration->ncml parse-expr) (lambda (x) (match x (((or 'label 'LABEL) (and id (? symbol?)) '= (and v (? symbol?))) `(ncml:label (@ (id ,(->string id))) ,v)) (((or 'input 'INPUT) . lst) (map (lambda (x) (match x ((? symbol?) `(ncml:input (@ id ,(->string x)))) ((id1 (or 'as 'AS) x1) `(ncml:input (@ (id ,(->string id1)) (as ,(->string x1))))) ((id1 (or 'from 'FROM) n1) `(ncml:input (@ (id ,(->string id1)) (from ,(->string n1))))) ((id1 (or 'as 'AS) x1 (or 'from 'FROM) n1) `(ncml:input (@ (id ,(->string id1)) (as ,(->string x1)) (from ,(->string n1))))))) lst)) (((or 'output 'OUTPUT) . (and lst (? (lambda (x) (every symbol? x))))) (map (lambda (x) `(ncml:output (@ (id ,(->string x))))) lst)) (((or 'const 'CONST) (and id (? symbol?)) '= expr) `(ncml:const (@ (id ,(->string id))) (ncml:expr ,(expr->ncml-expr (parse-expr expr))))) (((or 'reaction 'REACTION) ((and id (? symbol?)) . alst) ) (let ((trs (lookup-def 'transitions alst)) (initial (lookup-def 'initial alst)) (open (lookup-def 'open alst)) (cons (lookup-def 'conserve alst)) (p (lookup-def 'power alst))) (let ((sxml-trs (append-map transition->ncml-transition trs))) `(ncml:reaction (@ (id ,(->string id))) (ncml:open ,open) ,(and initial `(ncml:initial ,(expr->ncml-expr (parse-expr initial))) ) ,(and cons `(ncml:conserve ,((conseq->ncml-conseq parse-expr) cons)) ) (ncml:transitions ,@sxml-trs) (ncml:power ,(expr->ncml-expr (parse-expr p))))))) (((or 'd 'D) ((and id (? symbol?))) '= expr . rest) (let ((initial (lookup-def 'initial rest))) `(ncml:rate (@ (id ,(->string id)) ) ,(and initial `(ncml:initial ,(expr->ncml-expr (parse-expr initial)))) (ncml:expr ,(expr->ncml-expr (parse-expr expr)))))) (((and id (? symbol?)) '= expr) `(ncml:asgn (@ (id ,id)) (ncml:expr ,(expr->ncml-expr (parse-expr expr))))) (((or 'defun 'DEFUN 'fun 'FUN 'rel 'REL) (and id (? symbol?)) (and idlist (? (lambda (x) (every symbol? x)))) expr) `(ncml:defun (@ (id ,x)) ,@(map (lambda (v) `(ncml:arg ,(->string v))) idlist) (ncml:body ,(expr->ncml-expr (parse-expr expr))))) (((or 'component 'COMPONENT) ((or 'type 'TYPE) typ) ((or 'name 'NAME) name) . lst) `(ncml:component (@ (name ,(->string name)) (type ,(->string typ))) ,@(map (declaration->ncml parse-expr) lst))) (((or 'component 'COMPONENT) ((or 'type 'TYPE) typ) . lst) `(ncml:component (@ (type ,(->string typ))) ,@(map (declaration->ncml parse-expr) lst))) (((or 'component 'COMPONENT) ((or 'name 'NAME) name) '= (and functor-name (? symbol?)) (and args (? list?))) `(ncml:component (@ (name ,(->string name)) (functor-name ,(->string functor-name))) ,@(map (declaration->ncml parse-expr) lst))) ))) (define (make-component->ncml dis model parse-expr) (lambda (x) (let ((en (hash-table-ref model x))) (cond ((procedure? en) (let ((fd (procedure-data en))) `(ncml:defun (@ (id ,x)) ,@(map (lambda (v) `(ncml:arg ,v)) (lookup-def 'vars fd)) (ncml:body ,(expr->ncml-expr (lookup-def 'body fd)))))) (else (match en (($ nemo:quantity 'LABEL v) `(ncml:label (@ (id ,name)) ,v)) (($ nemo:quantity 'EXTERNAL local-name name namespace) (if namespace `(ncml:input (@ (id ,name)) (as ,local-name) (from ,namespace)) `(ncml:input (@ (id ,name)) (as ,local-name)))) (($ nemo:quantity 'CONST name value) `(ncml:const (@ (id ,name)) (ncml:expr ,value))) (($ nemo:quantity 'ASGN name value rhs) (let ((expr (expr->ncml-expr rhs))) `(ncml:asgn (@ (id ,name)) (ncml:expr ,expr)))) (($ nemo:quantity 'RATE name initial rhs power) (let ((expr (expr->ncml-expr rhs)) (initial (and initial (expr->ncml-expr initial)))) `(ncml:rate (@ (id ,name)) ,(and initial `(ncml:initial ,initial)) (ncml:expr ,expr) (ncml:power ,(or (and power (expr->ncml-expr power)) (expr->ncml-expr 1.0))) ))) (($ nemo:quantity 'REACTION name initial open trs cons p) (let ((sxml-trs (append-map transition->ncml-transition trs))) `(ncml:reaction (@ (id ,name)) (ncml:open ,open) ,(and initial `(ncml:initial ,(expr->ncml-expr initial))) ,(and cons `(ncml:conserve ,(map (conseq->ncml-conseq identity) cons)) ) (ncml:transitions ,@sxml-trs) (ncml:power ,(expr->ncml-expr p))))) (($ nemo:quantity 'COMPONENT name type lst) (let ((component->ncml (make-component->ncml dis model parse-expr)) (component-exports ((dis 'component-exports) model x))) (case type ((toplevel) `(,@(map component->ncml lst) ,@(map (lambda (x) `(ncml:output (@ (id ,x)))) component-exports))) (else `(ncml:component (@ (name ,name) (type ,type)) ,@(filter-map component->ncml lst) ,@(map (lambda (x) `(ncml:output (@ (id ,x)))) component-exports) ))))) (($ nemo:quantity 'FUNCTOR name args type lst) (let ((component->ncml (make-component->ncml dis model parse-expr))) `(ncml:functor (@ (name ,name) (type ,type) (parameters ,(string-intersperse (map ->string args) ","))) ,@(filter-map (declaration->ncml parse-expr) lst) ))) (else #f))))))) (define (model->ncml model parse-expr) (match-let ((($ nemo:quantity 'DISPATCH dis) (hash-table-ref model (nemo-intern 'dispatch)))) (let ((sysname ((dis 'sysname) model)) (component->ncml (make-component->ncml dis model parse-expr))) `(ncml:model (@ (name ,sysname)) ,@(component->ncml (nemo-intern 'toplevel)))))) (define (transition->text-transition x) (match x (('-> src dst rate) `(-> ,src ,dst ,(expr->text-expr rate) )) ((src '-> dst rate) `(-> ,src ,dst ,(expr->text-expr rate) )) (('<-> src dst rate1 rate2) `(<-> ,src ,dst ,(expr->text-expr rate) )) (('src <-> dst rate1 rate2) `(<-> ,src ,dst ,(expr->text-expr rate) )) (else (error 'transition->text-transition "invalid transition " x)))) (define (conseq->text-conseq parse-expr) (lambda (x) (match x (((and i (? integer?)) '= rhs) `(,(->string i) = ,(expr->text-expr (parse-expr rhs)))) (else (error 'conseq->text-conseq "invalid linear equation " x))))) (define (binding->text-binding bnd) (match bnd ((id expr) `(,id = ,(expr->text-expr expr))) (else (error 'binding->text-binding "invalid binding " bnd)))) (define (expr->text-expr x) (match x ((? number?) x) ((? symbol?) x) (('let bnds expr) `(let (,(map binding->text-binding bnds)) ,(expr->text-expr expr))) (((and op (? symbol?)) . args) (let ((ncml-expr `(apply ,op ,@(map expr->text-expr args)))) ncml-expr)) (else (error 'expr->text-expr "unknown expression " x)))) (define (make-component->text dis model parse-expr) (lambda (x) (let ((en (hash-table-ref model x))) (cond ((procedure? en) (let ((fd (procedure-data en))) `(function ,x ,(lookup-def 'vars fd) = ,(expr->text-expr (lookup-def 'body fd))) )) (else (match en (($ nemo:quantity 'LABEL v) `(label ,name = ,v)) (($ nemo:quantity 'EXTERNAL local-name name namespace) (if namespace `(input ,name as ,local-name from ,namespace) `(input ,name as ,local-name))) (($ nemo:quantity 'CONST name value) `(const ,name = ,value)) (($ nemo:quantity 'ASGN name value rhs) (let ((expr (expr->text-expr rhs))) `(,name = ,expr))) (($ nemo:quantity 'RATE name initial rhs power) (let ((expr (expr->ncml-expr rhs)) (initial (and initial (expr->text-expr initial))) (power (or (and power (expr->text-expr power)) (expr->text-expr 1.0)))) `(d (,name) = (,expr) (initial: ,initial) (power: ,power)) )) (($ nemo:quantity 'REACTION name initial open trs cons p) (let ((sxml-trs (append-map transition->text-transition trs))) `(reaction ,name (open-state: ,open) (initial: ,(expr->text-expr initial)) (conserve: ,(map (conseq->text-conseq identity) cons)) (transitions: ,text-trs) (power: ,(expr->ncml-expr p)) ))) (($ nemo:quantity 'COMPONENT name type lst) (let ((component->text (make-component->text dis model parse-expr)) (component-exports ((dis 'component-exports) model x))) (case type ((toplevel) `(,@(map component->text lst) ,@(map (lambda (x) `(output ,x)) component-exports))) (else `(component ,name (type: ,(->string type) ) ,@(filter-map component->text lst) ,@(map (lambda (x) `(output ,x)) component-exports) ))))) (($ nemo:quantity 'FUNCTOR name args type lst) (let ((component->ncml (make-component->ncml dis model parse-expr))) `(functor ,name (type: ,(->string type) ) (parameters: ,(string-intersperse (map ->string args) ",")) ,@(filter-map (declaration->ncml parse-expr) lst) ))) (else #f))) )) )) (define (model->text model parse-expr) (match-let ((($ nemo:quantity 'DISPATCH dis) (hash-table-ref model (nemo-intern 'dispatch)))) (let ((sysname ((dis 'sysname) model)) (component->text (make-component->text dis model parse-expr))) `(model ,sysname ,@(component->text (nemo-intern 'toplevel))) ))) (include "expr-parser.scm") (include "SXML.scm") (include "SXML-to-XML.scm") (include "stx-engine.scm") (define null-template `(*default* ,(lambda (node bindings root env) (begin (warn "Unrecognized input element:" node) '())))) (define-syntax sxml:make-null-ss (syntax-rules () ((stx rule ...) (list ; default handler null-template ; handler for textual nodes (list '*text* (lambda (text) text)) rule ...)))) (define (ensure-xmlns doc) (let ((doc1 (sxml:add-attr doc '(xmlns:ncml "ncml")))) (sxml:add-attr doc1 '(xmlns ncml)))) ;; based on SRV:send-reply by Oleg Kiselyov (define (print-fragments b) (let loop ((fragments b) (result #f)) (cond ((null? fragments) result) ((not (car fragments)) (loop (cdr fragments) result)) ((null? (car fragments)) (loop (cdr fragments) result)) ((eq? #t (car fragments)) (loop (cdr fragments) #t)) ((pair? (car fragments)) (loop (cdr fragments) (loop (car fragments) result))) ((procedure? (car fragments)) ((car fragments)) (loop (cdr fragments) #t)) (else (display (car fragments)) (loop (cdr fragments) #t))))) (define (ncml->declarations ncml:model parse-expr) (letrec ((label-template (sxml:match 'ncml:label (lambda (node bindings root env) (let ((id (sxml:attr node 'id)) (v (or (sxml:attr node 'value) (sxml:text node)))) (if (not id) (error 'output-template "label declaration requires id attribute")) `(label ,($ id) = ,($ v)))))) (input-template (sxml:match 'ncml:input (lambda (node bindings root env) (let ((id (or (sxml:attr node 'id) (sxml:attr node 'name))) (from (sxml:kidn* 'ncml:from node)) (as (sxml:kidn* 'ncml:as node))) (if (not id) (error 'input-template "input declaration requires id attribute")) (cond ((and from as) `(input (,($ id) as ,($ (second as) ) from ,($ (second from)) ))) (from `(input (,($ id) from ,($ (second from))))) (as `(input (,($ id) as ,($ (second as))))) (else `(input ,($ id)))))))) (output-template (sxml:match 'ncml:output (lambda (node bindings root env) (let ((id (or (sxml:attr node 'id) (sxml:attr node 'name)))) (if (not id) (error 'output-template "output declaration requires id attribute")) `(output ,($ id)))))) (const-template (sxml:match 'ncml:const (lambda (node bindings root env) (let* ((id (or (sxml:attr node 'id) (sxml:attr node 'name))) (expr ((lambda (x) (if (not x) (error 'const-template "const declaration " id " requires expr element") (parse-expr (second x) id))) (or (sxml:kidn* 'ncml:expr node) (let ((vattr (sxml:attr node 'value))) (and vattr (list 'value vattr ))) (list 'value (sxml:text node)) ) ))) (if (not id) (error 'const-template "const declaration requires id attribute")) `(const ,($ id) = ,expr))))) (reaction-transition-template (sxml:match 'ncml:transition (lambda (node bindings root env) (let ((src (sxml:attr node 'src)) (dst (sxml:attr node 'dst)) (rate ((lambda (x) (if (not x) (error 'reaction-transition-template "reaction transition requires rate element") (parse-expr (second x)))) (sxml:kidn* 'ncml:rate node)))) (if (not src) (error 'reaction-transition-template "reaction transition requires src attribute")) (if (not dst) (error 'reaction-transition-template "reaction transition requires dst attribute")) `(-> ,($ src) ,($ dst) ,rate))))) (asgn-template (sxml:match 'ncml:asgn (lambda (node bindings root env) (let* ((id (or (sxml:attr node 'id) (sxml:attr node 'name))) (expr ((lambda (x) (if (not x) (error 'asgn-template "algebraic assignment requires expr element") (parse-expr (second x) id))) (or (sxml:kidn* 'ncml:expr node) (list 'expr (sxml:text node)) )) )) (if (not id) (error 'asgn-template "algebraic assignment requires id attribute")) `(,($ id) = ,expr))))) (rate-template (sxml:match 'ncml:rate (lambda (node bindings root env) (let* ((id (sxml:attr node 'id)) (rhs ((lambda (x) (if (not x) (error 'rate-template "rate equation requires expr element") (parse-expr (second x) id))) (sxml:kidn* 'ncml:expr node))) (initial ((lambda (x) (and x (parse-expr (second x) id))) (sxml:kidn* 'ncml:initial node))) (power ((lambda (x) (and x (parse-expr (second x) id))) (sxml:kidn* 'ncml:power node))) ) (if (not id) (error 'rate-template "rate equation requires id attribute")) `(d (,($ id)) = ,rhs ,(and initial `(initial ,initial) ) ,(and power `(power ,power) )))))) (conseq-template (sxml:match 'ncml:conseq (lambda (node bindings root env) (let ((val (string->number (->string (sxml:attr node 'val)))) (rhs ((lambda (x) (if (not x) (error 'conseq-template "conseq definition requires expr element") (parse-expr (second x)))) (sxml:kidn* 'ncml:expr node)))) `(,val = ,rhs))))) (reaction-template (sxml:match 'ncml:reaction (lambda (node bindings root env) (let* ((id ($ (or (sxml:attr node 'id) (sxml:attr node 'name)))) (initial ((lambda (x) (and x (parse-expr (second x) id))) (sxml:kidn* 'ncml:initial node))) (open ((lambda (x) (if (not x) (error 'reaction-template "reaction declaration requires open element") ($ (second x)))) (sxml:kidn* 'ncml:open node))) (conserve ((lambda (x) (and x (let ((tmpl (sxml:make-null-ss conseq-template))) (stx:apply-templates (cons 'ncml:conseq (cdr x)) tmpl root env)))) (sxml:kidn* 'ncml:conserve node))) (power ((lambda (x) (if (not x) (error 'reaction-template "reaction declaration requires open element") (parse-expr (second x) id))) (sxml:kidn* 'ncml:power node))) (transitions ((lambda (x) (if (not x) (error 'reaction-template "reaction declaration requires transitions element") (let ((tmpl (sxml:make-null-ss reaction-transition-template))) (stx:apply-templates (cdr x) tmpl root env)))) (sxml:kidn* 'ncml:transitions node))) ) (if (not id) (error 'reaction-template "reaction declaration requires id attribute")) `(reaction (,id (initial ,initial) (open ,open) (power ,power) ,(and conserve `(conserve ,conserve) ) (transitions ,@transitions))))))) (defun-template (sxml:match 'ncml:defun (lambda (node bindings root env) (let* ((id (or (sxml:attr node 'id) (sxml:attr node 'name))) (args ((lambda (x) (if (null? x) (error 'defun-template "function definition requires at least one arg element") (map (compose $ second) x))) (sxml:kidsn 'ncml:arg node))) (body ((lambda (x) (if (not x) (error 'defun-template "function definition requires body element") (parse-expr (second x) id))) (sxml:kidn* 'ncml:body node)))) (if (not id) (error 'defun-template "function definition requires id attribute")) `(defun ,($ id) ,args ,body))))) (component-template (sxml:match 'ncml:component (lambda (node bindings root env) (let ((name (sxml:attr node 'name)) (functor-name (or (sxml:attr node 'functor-name) (sxml:attr node 'functor))) (type (sxml:attr node 'type))) (if (and (not functor-name) (not type) ) (error 'component-template "component definition requires type attribute" name)) (if (and functor-name (not name) ) (error 'component-template "component definition requires name attribute")) (if functor-name `(component (name ,($ name)) = ,($ functor-name) ,(ncml->declarations (sxml:kids node) parse-expr)) (if name `(component (type ,($ type)) (name ,($ name)) ,@(ncml->declarations (sxml:kids node) parse-expr)) `(component (type ,($ type)) ,@(ncml->declarations (sxml:kids node) parse-expr)) )) )) )) (functor-template (sxml:match 'ncml:functor (lambda (node bindings root env) (let ((parameters (sxml:attr node 'parameters)) (name (sxml:attr node 'name)) (type (sxml:attr node 'type))) (if (not type) (error 'functor-template "functor definition requires type attribute")) (if (not name) (error 'functor-template "functor definition requires name attribute")) (if (not parameters) (error 'functor-template "functor definition requires parameters attribute")) `(functor (name ,($ name)) (type ,($ type)) ,(map string->symbol (string-split parameters ",")) = . ,(ncml->declarations (sxml:kids node) parse-expr)))))) (hh-template (sxml:match 'ncml:hh_ionic_gate (lambda (node bindings root env) (let* ( (id (or (sxml:attr node 'id) (sxml:attr node 'name))) (and-expr (lambda (x) (and x (parse-expr (second x) id)))) (initial_m (and-expr (sxml:kidn* 'ncml:initial_m node))) (initial_h (and-expr (sxml:kidn* 'ncml:initial_h node))) (m_power (and-expr (sxml:kidn* 'ncml:m_power node))) (h_power (and-expr (sxml:kidn* 'ncml:h_power node))) (m_alpha (and-expr (sxml:kidn* 'ncml:m_alpha node))) (m_beta (and-expr (sxml:kidn* 'ncml:m_beta node))) (h_alpha (and-expr (sxml:kidn* 'ncml:h_alpha node))) (h_beta (and-expr (sxml:kidn* 'ncml:h_beta node))) (m_tau (and-expr (sxml:kidn* 'ncml:m_tau node))) (m_inf (and-expr (sxml:kidn* 'ncml:m_inf node))) (h_tau (and-expr (sxml:kidn* 'ncml:h_tau node))) (h_inf (and-expr (sxml:kidn* 'ncml:h_inf node))) ) (if (not id) (error 'hh-template "hh ionic conductance definition requires id attribute")) `(hh-ionic-gate (,($ id) ,@(if initial_m `((initial-m ,initial_m)) `()) ,@(if initial_h `((initial-h ,initial_h)) `()) ,@(if m_power `((m-power ,m_power)) '()) ,@(if h_power `((h-power ,h_power)) '()) ,@(if m_alpha `((m-alpha ,m_alpha)) '()) ,@(if h_alpha `((h-alpha ,h_alpha)) '()) ,@(if m_beta `((m-beta ,m_beta)) '()) ,@(if h_beta `((h-beta ,h_beta)) '()) ,@(if m_inf `((m-inf ,m_inf)) '()) ,@(if h_inf `((h-inf ,h_inf)) '()) ,@(if m_tau `((m-tau ,m_tau)) '()) ,@(if h_tau `((h-tau ,h_tau)) '()) )))))) (decaying-pool-template (sxml:match 'ncml:decaying_pool (lambda (node bindings root env) (let* ((id (sxml:attr node 'id)) (and-expr (lambda (x) (and x (parse-expr (second x) id)))) (initial (and-expr (sxml:kidn* 'ncml:initial node))) (beta (and-expr (sxml:kidn* 'ncml:beta node))) (depth (and-expr (sxml:kidn* 'ncml:depth node))) (temp-adj (and-expr (sxml:kidn* 'ncml:temp_adj node)))) (if (not id) (error 'decaying-pool-template "decaying pool definition requires id attribute")) (if (not initial) (error 'decaying-pool-template "decaying pool definition requires initial value")) (if (not beta) (error 'decaying-pool-template "decaying pool definition requires beta parameter")) (if (not depth) (error 'decaying-pool-template "decaying pool definition requires depth parameter")) `(decaying-pool (,($ id) ,@(if temp_adj `((temp_adj ,temp_adj)) `()) (beta ,beta) (depth ,depth) (initial ,initial))))))) ) (stx:apply-templates ncml:model (sxml:make-null-ss label-template input-template output-template const-template asgn-template rate-template reaction-template defun-template component-template functor-template hh-template decaying-pool-template) ncml:model (list)))) (define (ncml->model-decls options doc) (let* ((parse-expr (or (lookup-def 'parse-expr options) identity)) (ncml:model ((lambda (x) (if (null? x) (error 'ncml->model "ncml:model element not found in input document") (car x))) (ncml:sxpath '(// ncml:model) `(*TOP* . ,doc)))) (model-name (or (sxml:attr ncml:model 'name) (gensym 'model))) (membraneprops (ncml:sxpath '(// cell biophysicalProperties membraneProperties) `(*TOP* . ,ncml:model))) (model-decls (ncml->declarations (if (null? membraneprops) (sxml:kids ncml:model) (sxml:kids membraneprops)) parse-expr)) ) (list model-name model-decls))) (define (ncml->model options model-name model-decls) (if (or (null? model-decls) (and (pair? model-decls) (every null? model-decls))) (error 'ncml->model "ncml declaration elements not found in input document")) (let* ((model+nemo (nemo-constructor model-name model-decls (lambda (x . rest) (identity x)))) (model (first model+nemo)) (nemo (second model+nemo))) (let ((model-1 (nemo:hh-transformer model (alist-ref 'hh-markov options) (lambda (x . rest) (identity x))))) (if (assoc 'depgraph options) (print "dependency graph: " ((nemo 'depgraph*) model-1))) (if (assoc 'exports options) (print "exports: " ((nemo 'exports) model-1))) (if (assoc 'imports options) (print "imports: " ((nemo 'imports) model-1))) (if (assoc 'components options) (for-each (lambda (x) (print "component " x ": " ((nemo 'component-exports) model-1 (second x))) (print "component " x " subcomponents: " ((nemo 'component-subcomps) model-1 (second x)))) ((nemo 'components) model-1))) model-1))) (define (entry->surface-xml x) (match x (('nemo-model name decls) (map entry->surface-xml decls)) (('output . names) (string-concatenate (map (lambda (name) (sprintf "~%" name)) names))) (('input . names) (string-concatenate (map (lambda (name) (match name ((and name (? symbol?)) (sprintf "~%" name)) ((name 'from ns) (sprintf "~%" name ns)) )) names))) (('const name '= value) (if (number? value) (sprintf "~%" name value) (sprintf "~%~A~%~%" name value))) (('defun name args body) (sprintf "~%~A~%~A~%~%" name (string-concatenate (map (lambda (x) (sprintf "~A" x)) args)) body)) ((name '= expr) (sprintf "~A~%~%" name expr)) (('d ( name ) '= expr) (sprintf "~A~%~%" name expr)) (('d ( name ) '= expr ('initial initial-expr)) (sprintf "~A~%~A~%~%" name expr initial-expr)) (('hh-ionic-gate (ion ('initial-m initial-m-expr) ('initial-h initial-h-expr) ('m-power m-power) ('h-power h-power) ('m-inf m-inf-expr) ('m-tau m-tau-expr) ('h-inf h-inf-expr) ('h-tau h-tau-expr) )) (sprintf "~%~A~%~A~%~A~%~A~%~A~%~A~%~A~%~A~%~%" ion initial-m-expr initial-h-expr m-power h-power m-inf-expr m-tau-expr h-inf-expr h-tau-expr)) (('hh-ionic-gate (ion ('initial-m initial-m-expr) ('m-power m-power) ('h-power h-power) ('m-inf m-inf-expr) ('m-tau m-tau-expr) )) (sprintf "~%~A~%~A~%~A~%~A~%~A~%~%" ion initial-m-expr m-power h-power m-inf-expr m-tau-expr)) (('hh-ionic-gate (ion ('initial-m initial-m-expr) ('m-power m-power) ('h-power h-power) ('m-tau m-tau-expr) ('m-inf m-inf-expr) )) (sprintf "~%~A~%~A~%~A~%~A~%~A~%~%" ion initial-m-expr m-power h-power m-inf-expr m-tau-expr)) (('hh-ionic-gate (ion ('initial-m initial-m-expr) ('initial-h initial-h-expr) ('m-power m-power) ('h-power h-power) ('m-alpha m-alpha-expr) ('m-beta m-beta-expr) ('h-alpha h-alpha-expr) ('h-beta h-beta-expr) )) (sprintf "~%~A~%~A~%~A~%~A~%~A~%~A~%~A~%~A~%~%" ion initial-m-expr initial-h-expr m-power h-power m-alpha-expr m-beta-expr h-alpha-expr h-beta-expr)) (('hh-ionic-gate (ion ('initial-m initial-m-expr) ('m-power m-power) ('h-power h-power) ('m-alpha m-alpha-expr) ('m-beta m-beta-expr) )) (sprintf "~%~A~%~A~%~A~%~A~%~A~%~%" ion initial-m-expr m-power h-power m-alpha-expr m-beta-expr)) (('component ('type ty) ('name name) . rest) (sprintf "~%~A~%" ty name (string-concatenate (map entry->surface-xml rest)))) (('component ('type ty) . rest) (sprintf "~%~A~%" ty (string-concatenate (map entry->surface-xml rest)))) (else (error 'nemo "unknown declaration" x)) )) (define (partition-model opt decls) (let recur ((bkts '()) (toplevel '()) (decls decls)) (if (null? decls) (list bkts (reverse toplevel)) (let ((decl (car decls))) (if (opt 'debug) (begin (print "partition-model: decl = ") (pp decl))) (match decl (((or 'component 'COMPONENT) ((or 'type 'TYPE) typ) ((or 'name 'NAME) name) . lst) (let ((bkt (alist-ref name bkts))) (if bkt (recur (alist-update name (cons decl bkt) bkts) toplevel (cdr decls)) (recur (alist-update name (list decl) bkts) toplevel (cdr decls))))) (else (recur bkts (cons decl toplevel) (cdr decls))))) ))) (define (process-model opt source-path prefix sys iexpr? parse-expr) (match-let ((($ nemo:quantity 'DISPATCH dis) (hash-table-ref sys (nemo-intern 'dispatch)))) (let* ( (sysname ((lambda (x) (or (and prefix ($ (s+ prefix "_" x))) x)) ((dis 'sysname) sys))) (dirname (pathname-directory source-path)) (eqn-fname (make-output-fname dirname sysname ".eqn" (opt 'eqn) )) (plain-fname (make-output-fname dirname sysname ".txt" (opt 'plain) )) (sxml-fname (make-output-fname dirname sysname ".sxml" (opt 'sxml) )) (surface-xml-fname (make-output-fname dirname sysname ".xml" (opt 'surface-xml) )) (xml-fname (make-output-fname dirname sysname ".xml" (opt 'xml) )) (mod-fname (make-output-fname dirname sysname ".mod" (opt 'nmodl) )) (vclamp-ses-fname (make-output-fname dirname sysname ".ses" (opt 'vclamp-hoc) )) (vclamp-octave-fname (make-output-fname dirname sysname "_vclamp.m" (opt 'vclamp-octave) )) (eqn (opt 'eqn)) (nest (opt 'nest)) (matlab (opt 'matlab)) (octave (opt 'octave)) (vclamp-hoc (opt 'vclamp-hoc)) (vclamp-octave (opt 'vclamp-octave)) (nmodl-depend (opt 'nmodl-depend)) (nmodl-method (let ((method ($ (opt 'nmodl-method) ))) (case method ((adams runge euler adeuler heun adrunge gear newton simplex simeq seidel sparse derivimplicit cnexp clsoda after_cvode cvode_t cvode_t_v expeuler #f) method) (else (error "unknown nmodl-method " method))))) (octave-method (let ((method ($ (opt 'octave-method) ))) (case method ((lsode odepkg #f) method) (else (error "unknown octave method " method))))) (in-format (cond ((opt 'input-format) => (lambda (x) (case ($ x) ((nemo) 'nemo) ((s-exp sexp) 'sexp) ((sxml) 'sxml) ((xml ixml) 'xml) (else (error 'nemo "unknown input format" x))))) (else (case ((lambda (x) (or (not x) ($ x))) (pathname-extension source-path)) ((s-exp sexp) 'sexp) ((sxml) 'sxml) ((xml) 'xml) (else 'nemo))))) (parse-expr (case in-format ((sxml xml) identity) ((sexp) identity) ((ixml) (lambda (x #!optional loc) (let ((xs (if (string? x) x (string-concatenate (map (lambda (el) (if (string? el) el (if (equal? el '(divide)) " / " (->string el)))) x))))) (nemo:parse-string-expr xs loc)))) ((nemo) (if iexpr? (lambda (x #!optional loc) (if (string? x) (nemo:parse-string-expr x loc) (nemo:parse-sym-expr x loc))) nemo:parse-sym-expr)) (else (error 'nemo "unknown input format" in-format)))) ) (if (and xml-fname surface-xml-fname) (error 'nemo "both --xml and --surface-xml options are not permitted")) (if plain-fname (with-output-to-file plain-fname (lambda () (pretty-print (model->text sys parse-expr))))) (if sxml-fname (with-output-to-file sxml-fname (lambda () (pretty-print (model->ncml sys parse-expr))))) (if xml-fname (let* ((doc (model->ncml sys parse-expr)) (doc1 (ensure-xmlns (cond ((eq? (car doc) '*TOP*) (assoc 'ncml:model (cdr doc))) (else doc))))) (with-output-to-file xml-fname (lambda () (print-fragments (generate-XML `(begin ,doc1))))))) (if surface-xml-fname (with-output-to-file surface-xml-fname (lambda () (print-fragments (entry->surface-xml model-decls))))) (if eqn (model->eqn `((filename . ,eqn-fname)) sys)) (if mod-fname (with-output-to-file mod-fname (lambda () (model->nmodl `((depend . ,nmodl-depend) (method . ,nmodl-method) (kinetic . ,(opt 'nmodl-kinetic))) sys)))) (if octave (model->octave `((filename . ,(or (and (string? octave) (pathname-file octave)) octave)) (dirname . ,(or (and (string? octave) (pathname-directory octave)) dirname)) (method . ,octave-method)) sys)) (if matlab (model->matlab `((dirname . ,(or (and (string? matlab) matlab) dirname))) sys)) (if nest (model->nest `((dirname . ,(or (and (string? nest) nest) dirname))) sys)) (if vclamp-hoc (model->vclamp-hoc `((filename . ,vclamp-ses-fname) ) sys)) (if vclamp-octave (model->vclamp-octave `((filename . ,vclamp-octave-fname) ) sys)) )) ) (define (main opt operands) (if (null? operands) (nemo:usage) (let* ( (model-sources (map (lambda (operand) (let* ((read-xml (lambda (name) (call-with-input-file name (lambda (port) (ssax:xml->sxml port '((ncml . "ncml")))) ))) (read-sexp (lambda (name) (call-with-input-file name read))) (read-iexpr (lambda (name) (call-with-input-file name (lambda (port) (let ((content (iexpr:tree->list (iexpr:parse operand port)))) (car content)))))) (in-format (cond ((opt 'input-format) => (lambda (x) (case ($ x) ((nemo) 'nemo) ((s-exp sexp) 'sexp) ((xml) 'xml) ((ixml) 'ixml) ((sxml) 'sxml) (else (error 'nemo "unknown input format" x))))) (else (case ((lambda (x) (or (not x) ($ x))) (pathname-extension operand)) ((s-exp sexp) 'sexp) ((sxml) 'sxml) ((xml) 'xml) (else 'nemo))))) (doc.iexpr (case in-format ((nemo) (let ((content (read-sexp operand))) (if (eq? content 'nemo-model) (cons (read-iexpr operand) #t) (cons content #f)))) ((sxml sexp) (cons (read-sexp operand) #f)) ((xml ixml) (cons (read-xml operand) #f)) (else (error 'nemo "unknown input format" in-format)))) (dd (if (opt 'debug) (pp (car doc.iexpr)))) (parse-expr (case in-format ((sxml sexp) identity) ((nemo) (if (cdr doc.iexpr) (lambda (x #!optional loc) (if (string? x) (nemo:parse-string-expr x loc) (nemo:parse-sym-expr x loc))) nemo:parse-sym-expr)) ((xml) (lambda (x #!optional loc) (ncml-expr->expr x))) ((ixml) (lambda (x #!optional loc) (nemo:parse-string-expr x loc))) (else (error 'nemo "unknown input format" in-format)))) (model-name.model-decls (case in-format ((sxml xml ixml) (ncml->model-decls `((parse-expr . ,parse-expr)) (car doc.iexpr))) ((sexp nemo) (sexp->model-decls (car doc.iexpr))) (else (error 'nemo "unknown input format" in-format)))) ) (ModelSource operand in-format (car model-name.model-decls) (cadr model-name.model-decls) (cdr doc.iexpr) parse-expr) )) operands)) (models (if (opt 'partition) (let recur ((srcs model-sources) (ax '())) (if (null? srcs) ax (let ((src (car srcs))) (cases nemo:model src (ModelSource (source-path in-format model-name model-decls iexpr parse-expr) (let ((pmodels (partition-model opt model-decls))) (if (opt 'debug) (begin (print "length pmodels = " (length pmodels)) (print "pmodels = " ) (pp pmodels))) (let ((model-parts (match-let (((bkts toplevel) pmodels)) (map (lambda (bkt) (let ((part-decls (append toplevel (cdr bkt))) (part-name (car bkt))) (case in-format ((sxml xml ixml) (ModelPart source-path model-name part-name (ncml->model `((hh-markov . ,(opt 'hh-markov)) (parse-expr . ,parse-expr)) ($ (s+ model-name "_" (car bkt))) part-decls) part-decls iexpr parse-expr) ) ((sexp nemo) (ModelPart source-path model-name part-name (sexp->model `((hh-markov . ,(opt 'hh-markov))) ($ (s+ model-name "_" (car bkt))) part-decls parse-expr) part-decls iexpr parse-expr) ) (else (error 'nemo "invalid input format" in-format)) ))) bkts)) )) (recur (cdr srcs) (append model-parts ax)) ))) (else (error 'nemo "invalid model source" src))) ))) (map (lambda (x) (cases nemo:model x (ModelSource (source-path in-format model-name model-decls iexpr parse-expr) (case in-format ((sxml xml ixml) (SingleModel source-path model-name (ncml->model `((hh-markov . ,(opt 'hh-markov)) (parse-expr . ,parse-expr)) model-name model-decls) model-decls iexpr parse-expr)) ((sexp nemo) (SingleModel source-path model-name (sexp->model `((hh-markov . ,(opt 'hh-markov))) model-name model-decls parse-expr) model-decls iexpr parse-expr)) (else (error 'nemo "invalid input format")) )) (else (error 'name "invalid model source" x)))) model-sources)) ) ) (for-each (lambda (model) (cases nemo:model model (SingleModel (source-path model-name sys model-decls iexpr? parse-expr) (process-model opt source-path #f sys iexpr? parse-expr)) (ModelPart (source-path model-name part-name sys model-decls iexpr? parse-expr) (process-model opt source-path #f sys iexpr? parse-expr)) (else (error 'nemo "invalid model" model)))) models) ))) (main opt (opt '@))