一个Scheme中的Yield语句实现

一个Scheme中Data和Code的统一的例子

ee.zsy posted @ 2011年4月19日 00:06 in 未分类 , 1580 阅读

SXML是S-expressions形式的XML(Extensible Markup Language,可扩展置标语言),好处是可以用单一的语言来实现的XML的各种扩展机制。

而用Scheme实现的DSSSL(Document Style Semantics and Specification Language),拥有比XML更长久的历史。
以下内容是关于标记语言的书写的转换,将不涉及到具体的语言标准。
 
 
 
例如现在定义一种标记语言,其格式依照S-expression书写,并有如下的类型要求。
操作符car的位置是tag,操作数cdr是children,如果第一个操作数是@开头的list则用来表示attributes。
其中类型tag为symbol,children是symbol或number或string或S-expression或为空,attributes是(symbol string)构成的关联列表。
 
 
 
不考虑类型错误的检测的话,可以写出这样的转换代码:
(define (to-xml sexp)
  (define (to-attrs alist)
    (if (null? alist)
        ""
        (let ((attr (car alist)))
          (string-append 
           " " (symbol->string (car attr)) 
           "=\"" (cadr attr) "\"" (to-attrs (cdr alist))))))
  (cond
    ((pair? sexp)
     (let* ((tag (symbol->string (car sexp)))
            (has-attrs? 
             (and (pair? (cdr sexp)) (pair? (cadr sexp)) (eq? (caadr sexp) '@)))
            (attrs (if has-attrs? (to-attrs (cdadr sexp)) ""))
            (children (cdr (if has-attrs? (cdr sexp) sexp)))
            (has-child? (not (null? children))))
       (apply
        string-append
        (append
         (list "<" tag attrs (if has-child? "" "/") ">")
         (map to-xml children)
         (if has-child? 
             (list "</" tag ">")
             (list))))))
    ((string? sexp) sexp)
    ((number? sexp) (number->string sexp))
    ((symbol?) (symbol->string sexp))
    (else "error")))
(display (to-xml '(p "go " (a (@ (href "/")) "home") (br))))
得到的输出是:
<p>go <a href="/">home</a><br/></p>
这里同时进行了解析和生成两个步骤,将一种S-exp的形式的形式表现的额数据转化为了XML格式。
如果想依据条件生成不同的文本,可以用加入条件语句,可以用quasiquote来生成list。
 
 
 
另一方面,我们可以为不同的标签定义单独的解析规则。
例如html中的strong标记:
(define (strong . texts) (to-xml (cons 'strong texts)))
(strong "hello") ;=> <b>hello</b>
和上面的不同在于,这里的strong是定义为一个过程的。
写下来的SXML可以eval的代码,而不是供解析list数据。
这里再次强调一下,这里的SXML已经变成了实际的代码了。
上面的一段用到的例子可以变为执行这样的一段表达式:
(p "go " (a (@  '(href "/")) "home") (br))
并在其中用到了p、a、@、br这四个过程,执行后返回一样的结果。
如果想依据条件生成不同的文本,可以直接使用Scheme的if语句。
 
 
 
如果觉得交给scheme执行不安全的话,可以自行定义eval:
(define (eval-sxml sexp)
  (define tags
    `((strong
      ,(lambda texts (apply string-append (append '("<b>") texts '("</b>")))))))
  (cond
    ((pair? sexp)
     (apply 
      (cadr (assoc (car sexp) tags)) 
            (map eval-sxml (cdr sexp))) )
    ((string? sexp) sexp)
    (else "error")))
(eval-sxml '(strong "hello"))
;=> "<b>hello</b>"
这里定义的不完整,可以继续扩充,定义更多的tag或者实现更多的语句。
现在可以看到,SXML不仅仅是一种数据格式,而是实实在在的一种标记语言。
在上面这个实例中可以说一组tags被看作看作了scheme的一个库,比如全部html标记就是一个库。
并这里的eval虽然和最上面直接解析是同样的作用,但是增加了tag的灵活性。
 
 
 
由于变换的规则是可以定义的,我们就可以定义一组tags,然后定义不同的规则以转化到XML、TeX、Text和更多的格式。
以上写法在有类型的OCaml中也是可行的,例如下面两行都是合法的表达式:
type txsml=Tag of string|Str of string|Exp of Tag*txml list;;Exp("strong",[Str "hello world"])
let strong text="<b>"~text~"</b>";;strong "hello world";;
前者定义了类型并用以此定义了数据,而后者则定义了可供执行的过程并写出了代码。
Avatar_small
seo service london 说:
2024年1月14日 02:36

After reading your article I was amazed. I know that you explain it very well. And I hope that other readers will also experience how I feel after reading your article


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter