hans.hu

夫天地者,万物之逆旅也;光阴者,百代之过客也。而浮生若梦,为欢几何?古人秉烛夜游,良有以也。况阳春召我以烟景,大块假我以文章。

循序渐进学Boo - 高级篇

经过前面两篇文章的介绍,相信大家对Boo的一些基本特性有所了解,本文着重介绍Boo深层次的知识以及其在AOP中的实现。

在开始之前先给大家做些准备:

一、Boo Parser

目前Boo的编译器使用C#实现,用ANTLR进行解析。
Compiler Steps 

Pipelines consist of a set of steps that the compiler passes the code through. The source code for all the steps is here.

The order of steps when you compile a script is like this:

1.First the Boo Parser (and lexer): Boo.Lang.Parser.BooParsingStep.
    1.The rules the Boo parser uses are defined in this ANTLR grammar file
    2.The boo lexer turns the text script into a set of tokens, and the boo parser converts the tokens into an abstract syntax tree (AST). I'll show examples of this later on. Each step after the parser is basically just working on this AST and transforming it. At the end, the AST is used to produce raw .NET assembly code (IL). The steps that happen after the parser step:
2.InitializeTypeSystemServices
3.PreErrorChecking
4.InitializeNameResolutionService
5.IntroduceGlobalNamespaces
6.TransformCallableDefinitions
7.BindTypeDefinitions
8.[BindNamespaces]
9.BindBaseTypes
10.BindAndApplyAttributes
11.ExpandMacros
12.IntroduceModuleClasses
13.NormalizeStatementModifiers
14.[NormalizeTypeAndMemberDefinitions]
15.BindTypeDefinitions
16.BindBaseTypes
17.ResolveTypeReferences
18.BindTypeMembers
19.[ProcessInheritedAbstractMembers]
20.ProcessMethodBodiesWithDuckTyping
21.[ProcessAssignmentsToValueTypeMembers]
22.[ExpandProperties]
23.[StricterErrorChecking]
24.[RemoveDeadCode]
25.NormalizeIterationStatements
26.ProcessSharedLocals
27.ProcessClosures
28.ProcessGenerators
29.InjectCallableConversions
30.[ImplementICallableOnCallableDefinitions]
31.[EmitAssembly]
32.and finally [SaveAssembly] or [RunAssembly] 

二、Boo.Lang.Compiler.AbstractAstAttribute与Boo.Lang.Compiler.AbstractAstMacro

继承体系如下:

AbstractAstAttribute 

AbstractAstMacro

public abstract class Node : ICloneable
{
    // Fields
    protected Node _parent;
    ......
    // Mothods
    public abstract void Accept(IAstVisitor visitor);
    public abstract object Clone();
    public Node CloneNode(); 

    public TAncestor GetAncestor<TAncestor>() where TAncestor: Node;
    public Node GetAncestor(NodeType ancestorType);
    public Node GetAncestor(NodeType ancestorType, int limitDepth);
    public IEnumerable<TAncestor> GetAncestors<TAncestor>() where TAncestor: Node; 

    public virtual bool Replace(Node existing, Node newNode);
    public int ReplaceNodes(Node pattern, Node template);
    public int ReplaceNodes(NodePredicate predicate, Node template);
    public string ToCodeString();
    public override string ToString();
    ......
    // Properties
    public abstract NodeType NodeType {get;}
    public Node ParentNode {get;}
    ......
} 

在前一篇文章我们看到:

class AssignFieldsMacro(AbstractAstMacro): 
    override def Expand(macro as MacroStatement):  
#        ctor = (macro.ParentNode.ParentNode as Constructor) 
        ctor = macro.GetAncestor(NodeType.Constructor) as Constructor
        b = Block()
        
        for param in ctor.Parameters: 
            assign = BinaryExpression(BinaryOperatorType.Assign,
                            ReferenceExpression("_" + param.Name),
                            ReferenceExpression(param.Name))
            b.Add(assign)
        
        return b
class Customer:
    [property(Name)]
    private _name as string

    def constructor(name as string):
        AssignFields
        #autoAssign

上述Macro会在编译阶段将Customer构造函数的代码扩展为:

def constructor(name as string):
    self._name = name

[注]:

目前在Boo 0.91下可以有更简洁的写法:

macro autoAssign: 
    ctor = autoAssign.GetAncestor(NodeType.Constructor) as Constructor
    b = Block()
    
    for param in ctor.Parameters: 
        assign = BinaryExpression(BinaryOperatorType.Assign,
                        ReferenceExpression("_" + param.Name),
                        ReferenceExpression(param.Name))
        b.Add(assign)
    
    return b

三、DepthFirstVisitor与DepthFirstTransformer

它们都继承于IAstVisitor。

1、DepthFirstVisitor访问AST中的所有节点,但是不会对AST进行修改操作。请看下面这个例子:

class CaptureReferences(DepthFirstVisitor):
    [getter(Found)]
    _found = []
    
    def constructor(node as Node):
        node.Accept(self)
    override def OnReferenceExpression(reference as ReferenceExpression):
        _found.Add(reference)

上例就是通过DepthFirstVisitor提供的OnReferenceExpression方法获取所用到的ReferenceExpression,具体使用如下:

expression = BinaryExpression( 
                    BinaryOperatorType.Division, 
                    BinaryExpression( 
                        BinaryOperatorType.Subtraction, 
                        ReferenceExpression("x"), 
                        IntegerLiteralExpression(3)), 
                    ReferenceExpression("t")) 
print expression.ToCodeString()    // (x-3)/t

references = CaptureReferences(expression).Found
print join(references)        // x t

2、DepthFirstTransformer访问AST中的所有节点,并可以对AST进行添加与删除操作。通常在自定义的Macro与Attribute中,会需要对当前的AST进行修改以根据要求产生相应的AST,这时就会用到DepthFirstTransformer提供的RemoveCurrentNode,ReplaceCurrentNode等方法。请看下面这个例子:

class WithMacro(AbstractAstMacro):

    private class NameExpander(DepthFirstTransformer):

        _inst as ReferenceExpression

        def constructor(inst as ReferenceExpression):
            _inst = inst

        override def OnReferenceExpression(node as ReferenceExpression):
            // if the name of the reference begins with '_'
            // then convert the reference to a member reference
            // of the provided instance
            if node.Name.StartsWith('_'):
                // create the new member reference and set it up
                mre = MemberReferenceExpression(node.LexicalInfo)
                mre.Name = node.Name[1:]
                mre.Target = _inst.CloneNode()

                // replace the original reference in the AST
                // with the new member-reference
                ReplaceCurrentNode(mre)

    override def Expand(macro as MacroStatement) as Statement:
        assert 1 == macro.Arguments.Count
        assert macro.Arguments[0] isa ReferenceExpression

        inst = macro.Arguments[0] as ReferenceExpression

        // convert all _<ref> to inst.<ref>
        block = macro.Body
        ne = NameExpander(inst)
        ne.Visit(block)
        return block
class Example:
    public Name as string
    public Content as string
    
    def DoSomething([default('hello')]cstr as string):
        print cstr.ToUpper()
ex = Example()
with ex:
    _Name = "hans"
    _Content = "test"
    _DoSomething('hello world')
print ex.Name,ex.Content

[注]:

目前在Boo 0.91下可以有更简洁的写法:

macro with(target, body as Expression*):
    for expression in body:
        match expression:
            case BinaryExpression(Left: mre = MemberReferenceExpression(Target: OmittedExpression())):
                mre.Target = target
            case MethodInvocationExpression(Target: mre = MemberReferenceExpression(Target: OmittedExpression())):
                 mre.Target = target
    yield
with ex:
    .Name = "hans"
    .Content = "test"
    .DoSomething('hello world')

可以看到上述的with使用方法更为大家所熟悉。

四、Boo与AOP

为了建立松散耦合的、可扩展的企业系统,AOP应用到的横切技术,通常分为动态横切和静态横切两种类型。

静态横切和动态横切的区别在于它不修改一个给定对象的执行行为。相反,它允许通过引入附加的方法字段和属性来修改对象的结构。
此外,静态横切可以把扩展和实现附加到对象的基本结构中。

通过前面的介绍,我们知道了Boo有着强大的编译器扩展功能,可以通过AbstractAstAttribute与AbstractAstMacro很容易进行AOP的静态横切。

1、<在.Net中关于AOP的实现>中的”设计一个计算器,它能提供加法和减法功能。”例子可以简单通过如下的Boo代码实现:

[AttributeUsage(AttributeTargets.Method)]
class LogAttribute(AbstractAstAttribute):
    def constructor():
        pass
       
    override def Apply(node as Node):
        method as Method = node
        // Before
        expression = cast(ReturnStatement, method.Body.Statements[-1]).Expression
        match expression:
            case [| $left+$right |]:
                code = [| print "${$(method.Name)}(${$left},${$right})" |]
            case [| $left-$right |]:
                code = [| print "${$(method.Name)}(${$left},${$right})" |]
            otherwise:
                code = [| print "${$(method.Name)}" |]        
        // Body
        tmp = AstUtil.CreateReferenceExpression("__${method.Name}__")
        tbody = [|
            block:
                $tmp = $expression
                // After
                print "Result is ${$tmp}"
                print "After ${$(method.Name)} at ${date.Now}"
                return $tmp
        |].Body
        method.Replace(method.Body,tbody)
        method.Body.Insert(0, code)        
class Calculator:
"""
Description of Calculator
核心关注点是加法和减法,而通用业务-横切关注点则是日志功能.
"""
    def constructor():
        pass
    [log]
    def Add(x as int, y as int):
        return x+y
    [log]
    def Substract(x as int, y as int):
        return x-y

calc = Calculator()
calc.Add(2,10)

2、<在.Net中关于AOP的实现(补遗)>中“一个已经实现了收发邮件的类Mail。然而它并没有实现地址验证的功能。”例子,利用Boo可以将IValidatable接口织入到原有的Mail类中,这是一种非常形象的introduce功能:

[AttributeUsage(AttributeTargets.Class)]
class ValidatableAttribute(AbstractAstAttribute):
    def constructor():
        pass
       
    override def Apply(node as Node):
        classDef as ClassDefinition = node
        classDef.BaseTypes.Add(SimpleTypeReference("Examples.IValidatable"))
        AddMethod(classDef)
        
    def AddMethod(classDef as ClassDefinition):
        validator = Method()
#        fields = FindField(classDef)
#        field as Field = fields[0]
        validator.Name = "ValidateAddress"
        code = [|
            if string.IsNullOrEmpty($(ReferenceExpression('_mail'))):
                return false
            return true
        |]
        validator.Body.Add(code)
        classDef.Members.Add(validator)
[validatable]
class Mail:
"""Description of Mail"""
    [property(Email)]
    _mail as string
    
    def constructor():
        pass
    def constructor(mail as string):
        _mail = mail
        
    def Send():
        pass
    def Receive():
        pass

interface IValidatable:
    def ValidateAddress() as bool:
        pass

mail = Mail("test@gmail.com")
print mail.Email
mail
.ValidateAddress()

当然,上面的两个例子还相当不完善,只是向大家展示了Boo在AOP方面的优势,更好的实现需要大家自己去学习了解。

参考:

http://boo.codehaus.org/Type+System
http://boo.codehaus.org/Compiler+Steps
http://tore.vestues.no/2008/12/27/the-boo-extensibility-tutorials/

http://www.ibm.com/developerworks/cn/java/j-aopsc/
http://www.cnblogs.com/wayfarer/articles/233212.html

posted on 2009-07-25 11:24  hans.hu  阅读(2314)  评论(1编辑  收藏  举报

导航