|
[boo]
Boo meta methods
[
bamboo
]
Boo meta methods are methods that take code trees as input and return code trees as output. In addition they must be marked with the MetaAttribute for the compiler to recognize them as so. The compiler invokes meta methods during the type resolution phase and replaces the code tree at the point of invocation with the code tree returned by the meta method. Multiple overloads can be specified in which case the types of the code tree arguments will be used for the purpose of overload resolution. An example, assertAssert can be invoked with one or two arguments: assert x is null // use the code as the assertion message
assert x is not null, "x shouldn't be null" // custom exception or string
One way of implementing assert would be: [meta] def assert(condition as Expression):
return [|
if not $condition:
raise AssertionFailedException($(condition.ToCodeString()))
|]
[meta] def assert(condition as Expression, exception as Expression):
return [|
if not $condition:
raise $exception
|]
Where [| |] are the quasi-quotation delimiters. A quasi-quote evaluates its body as a code tree expression.
$ is generally called the "splice" operator and it means "evaluate me at compilation time". [meta] def assert(*arguments as (Expression)):
condition = arguments[0]
if len(arguments) > 1:
// assuming 2 arguments
exception = arguments[1]
else:
exception = [| AssertionFailedException($(condition.ToCodeString())) |]
return [|
if not $condition:
raise $exception
|]
An interesting aspect of boo's splicing semantics is exemplified by the subexpression: [| AssertionFailedException($(condition.ToCodeString())) |]The splice application $(condition.ToCodeString()) automatically lifts the string value returned by ToCodeString to a proper StringLiteralExpression. Another example, using'using' provides for deterministic disposal of resources. The argument should implement the IDisposable interface to have its Dispose method called For instance, the following code: using socket=OpenConnection():
socket.Send("ping")
should be expanded to something equivalent to:
socket=OpenConnection()
try:
socket.Send("ping")
ensure:
if socket isa IDisposable: (socket as IDisposable).Dispose()
A possible implementation for 'using' follows:
[meta] def using(e as Expression, block as BlockExpression):
temp = uniqueName()
return [|
$temp = $e
try:
$(block.Body)
ensure:
if $temp isa IDisposable: ($temp as IDisposable).Dispose()
|]
More about quasi-quotationDue to the reuse of syntactic elements in different contexts the parser needs to follow some conventions in order to infer the meaning of a quasi-quote expression. Take the quasi-quote [| a as string |]. What does it mean? If we were to interpret it as an expression it would mean a try cast expression. If we were to interpret it as a type member it would mean a field definition. For the inline form the convention is to try to interpret it as either an expression or an expression pair or an import declaration or a namespace declaration. The block form is first probed for a type member definition then for a single statement, then for a block of statements.and then for a module. Example:
e = [| a as string |] It should be possible to specify the exact context of quasi-quotation by using some special syntax but the specific details are not clear at this point. Soon to reach a svn repository near you. Oh, yeah, and many thanks to the people behind Template Haskell! TrackBackPost a comment
|