Macros “的通用Lisp Read宏;惰性中缀或;解构关键字

Macros “的通用Lisp Read宏;惰性中缀或;解构关键字,macros,common-lisp,infix-notation,infix-operator,reader-macro,Macros,Common Lisp,Infix Notation,Infix Operator,Reader Macro,我有一个公共的Lisp reader宏来解析“or”关系的延迟/延迟声明,使用由管道chacaters(“|”)分隔的中缀语法以及标准列表括号和关键字文本。考虑表单(:A:By:C),它表示一个2部分元组,其中第一个元素是肯定的:A,第二个元素是:B或:C。例如,可以推断整个元组的有效形式是(:a:b)或(:a:c) 我已经有了函数封装逻辑来分解读取宏之后的这些元组列表表单。但在读取时,我需要解析一个表单,比如:a |:b |:c,并用移除的管道标记它,比如(:lazy或:a:b:c)。使用中缀

我有一个公共的Lisp reader宏来解析“or”关系的延迟/延迟声明,使用由管道chacaters(“|”)分隔的中缀语法以及标准列表括号和关键字文本。考虑表单(:A:By:C),它表示一个2部分元组,其中第一个元素是肯定的:A,第二个元素是:B或:C。例如,可以推断整个元组的有效形式是(:a:b)或(:a:c)

我已经有了函数封装逻辑来分解读取宏之后的这些元组列表表单。但在读取时,我需要解析一个表单,比如:a |:b |:c,并用移除的管道标记它,比如(:lazy或:a:b:c)。使用中缀语法纯粹是为了面向读者的表单;中缀形式是短暂的,在读取阶段会立即被丢弃,取而代之的是标记为:lazy或的等效合法lisp形式

因此,我制作了一个read宏,它几乎可以按我所希望的方式工作,但目前需要在第一个或form关键字元素之前使用一个额外的管道作为一种读取器符号(我希望这根本不是必需的),并且它目前无法使用嵌套括号或拼接符号作为等效符号来推断类似的形式(就像在算术中,
2+(3*4)
是相同的运算顺序,一种等价形式,如
2+3*4

宏(源于此处的“斜杠读取器:):

预期目标是在读取时应用/替换此重写规则: (宏扩展“(:a(:b |:c))->(:a(:lazy或:b:c)) 或者,对于不包含在括号中的变体形式也一样(一种默认的操作顺序;空格不会有什么区别): (宏扩展“(:a:b |:c))->(:a(:lazy或:b:c)) (宏扩展“(:a:b |:c))->(:a(:lazy或:b:c)) 嵌套表单应以直观、递归的方式呈现: (宏扩展“(:a(:b |(:c |:d)))->(:a(:lazy或:b(:lazy或:c:d)))

注意,在基本形式的预期重写规则--(宏扩展’(:a(:b |:c))->(:a(:lazy或:b:c))中——:a不会在或形式中加入,因为它没有中缀管道操作符将其加入其中;它与或形式的结果一起以一种元组的形式存在。如前所述,这是这样的,在进一步计算惰性形式时,元组可以产生(:a:b)或(:a:c)-上述内容意味着这两种形式都是有效的,可以在以后进行分解

我很接近

问题是我无法完全理解,只有以下内容(使用上述宏): (宏扩展“(:a |:b |:c))->(:a(:LAZY-OR:b:c)) (宏扩展“(:a:b:d:e:f:g))->(:a:b’(:LAZY-OR:d:e:f:g)) 它实际上完成了我想做的大部分工作,这是一个功能性的基本解决方案:稍微修改执行规则,允许在读取时在中缀或窗体的开头添加一个额外的管道,而不将窗体连接到管道的左侧,因此它可以制作:b到:c(第一种形式),或:d到:g(第二种形式),放入为:lazy或form列出的有效案例中,并使该内部列表本身与非变量值a和:b一起成为外部列表/元组的成员。 很接近我想要的: (宏扩展“(:a:b:d |:e |:f |:g))->应该,不->(:a:b’(:LAZY-OR:d:e:f:g))

按重要性顺序,有3个错误:

  • 需要额外的“前缀”管道

    在读取的每个或窗体开始时,我希望不使用额外的开口管道。我希望切割该管道,以确保清洁度和可读性,并且仅在该读取宏的完全中缀位置使用该管道

  • 递归解析时向嵌套表单添加的额外括号

    它为嵌套表单添加了额外的括号,尽管它可以递归地处理嵌套表单

  • 空格不能任意接受

    它不接受管道之间的空格。我尝试使用保留读取的空格而不是读取,但在这里没有效果。它应该接受管道和关键字表单之间的空格,就像它们不存在一样,因为表单
    :a |:b
    :a |:b
    是等效的

  • read宏主要封装工作逻辑,擅长递归和嵌套形式: (宏扩展“(:a |)(:b |:c)(:e |:f))->收益率->(:a(:LAZY-OR(:LAZY-OR:b:c))(:LAZY-OR:e:f))
    (几乎完美,递归地完全按照预期展开,除了需要打开管道外,此表单中唯一的问题是围绕final生成的双括号:lazy或forms)。 因此,将从该表单生成一个相同的表单(当前为“不平衡”读取括号错误): (宏扩展“(:a(:b |:c)(:e |:f))->应该,不产生->(:a(:LAZY-OR(:LAZY-OR:b:c)(:LAZY-OR:e:f)))

    除了read宏添加的额外括号,以及它不能在中缀形式中使用空格之外,真正关键的错误是,如果不包括第一个非中缀管道(顺便说一下,前缀),就无法将or形式写入中缀管道形式。我在尝试匹配流时遇到了困难,而不需要使用第一个管道字符作为读取解析器的信号。也许对其中一个“peek”函数进行额外调用或两个“peek”函数会给我提供一个更专门的形式来匹配,但我还没有弄清楚如何解析它

    我着眼于围绕现有和全面的解决方案(如NKF(“definfix”宏)和CMU中缀()构建此功能,但由于这些是更通用和更大的代码基,我不认为我需要为更多类型的表单/运算符重用中缀逻辑,就这一种。从我对一个相当小的宏的了解来看,我肯定更喜欢用一个小而简洁的解决方案来解决它,只要它仍然是递归的,不容易出错


    如果您能从任何角度更有效地使用read宏,我将不胜感激。

    我将尝试不同的方法和使用方法
     (defun pipe-reader (stream char)                                                                   
       (declare (ignore char))                                                                          
       `(:lazy-or .  ,(loop for dir = (read stream t nil t)                       
                       then (progn (read-char stream t nil t)                                           
                                   (read stream t nil t))                         
                       collect dir                                                                      
                       while (eql (peek-char nil stream nil nil t) #\|))))                              
    (set-macro-character #\| #'pipe-reader)  
    
    ;; a bar is now the symbol 'or
    (set-macro-character #\| (constantly 'or))
    
    ;; read a list of forms for alternative forms
    (set-macro-character #\[
                         (lambda (stream char)
                           (declare (ignore char))
                           `(lazy-or-parse
                             ,@(read-delimited-list #\] stream t))))
    
    ;; this one is required too so that e.g. `a]` is not read as a single symbol.
    (set-macro-character #\]
                         (lambda (stream char)
                           (declare (ignore char))
                           (error "Unmatched closing bracket")))
    
    [:a|:b|:c]
    
    (LAZY-OR-PARSE :A OR :B OR :C)
    
    (read-from-string "[a b (c|d)]")
    (LAZY-OR-PARSE A B (C OR D))
    11
    
    (modify-syntax-entry ?\[ "(]" lisp-mode-syntax-table)
    (modify-syntax-entry ?\] ")[" lisp-mode-syntax-table)
    (modify-syntax-entry ?| "_" lisp-mode-syntax-table)
    
    '[ a | (c | d)]
    => (LAZY-OR-PARSE A OR (C OR D))