metaobject action_afti_dsa
package metaobjectTest | |
// @doc{+* is used instead of @doc{* because '{*' is used | |
// in the text attached to @doc | |
@doc{+* | |
This metaobject can add fields and methods to the current prototype, | |
add code before methods, and add code inside methods (if the annotation | |
is inside a method). This text is in language Markdown. Unlike other | |
metaobjects, this one demands the understanding of the Cyan Metaobject | |
Protocol to be used. To use an annotation of action_afti_dsa is | |
an alternative of building a whole metaobject class in Java. | |
The annotations take an attached DSL code of a language called *Myan* | |
which is interpreted Cyan with the possibility of declararing fields | |
and methods. | |
Each method should correspond to a method of a Java interface implemented by this | |
metaobject class: | |
IAction_dsa, IActionNewPrototypes_dsa, | |
afti, IActionNewPrototypes_afti, | |
IParseWithCyanCompiler_dpa, | |
ICommunicateInPrototype_afti_dsa | |
And it should not have parameters. For example, there | |
is a method `afti_addCodeTo` in the Java interface | |
`meta.afti`. However, this method takes two arguments and in the | |
Cyan code below it has none. To simplify the coding of metaobjects, the | |
formal parameters of the methods are implicitly declared. In the code | |
below we list which are the parameter names. Inside the methods, two | |
variables can be used: ``metaobject``, which is the metaobject that | |
received the message (it would be ``self``) and ``env``, the environment. | |
For those interface methods that have a ``compiler`` parameter, there is | |
a variable ``compiler`` available in the corresponding *Myan* method. | |
// field declarations | |
func afti_codeToAdd { | |
// two variables are accessible: | |
// ICompiler_afti compiler | |
// List<Tuple2<CyanMetaobjectAnnotation, | |
// List<ISlotInterfaceProgramUnit>>> infoList | |
// the return value has the Cyan type Tuple<String, String> | |
} | |
func dsa_codeToAdd { | |
// variable ICompiler_dsa compiler is accessible | |
// return value has the Cyan type String | |
} | |
func runUntilFixedPoint { | |
// either 'return true' or 'return false' | |
} | |
func afti_beforeMethodCodeList { | |
// variable ICompiler_afti compiler is accessible | |
// the return value has Cyan type | |
// Array<Tuple<String, String, Boolean>> | |
} | |
func afti_renameMethod { | |
// variable ICompiler_afti compiler is accessible | |
// the return value has the Cyan type | |
// Array<Tuple<String, Array<String>>> | |
} | |
func dsa_NewPrototypeList { | |
// variable ICompiler_dsa compiler is accessible | |
// return value has the Cyan type | |
// Array<Tuple<String, String>> | |
} | |
func afti_NewPrototypeList { | |
// variable ICompiler_afti compiler is accessible | |
// return value has the Cyan type | |
// Array<Tuple<String, String>> | |
} | |
func afti_dsa_shareInfoPrototype { | |
// the return value is Object | |
} | |
func afti_dsa_receiveInfoPrototype { | |
// variable annotationInfoSet has the Cyan type | |
// Set<Tuple<String, Int, Int, Dyn>> | |
} | |
Each Cyan method can also return a value. For example, | |
`afti_codeTo` should return a value of type | |
Tuple<String, String> | |
If there is no `return` statement inside the code, the Cyan | |
interpreter considers that the Java `null` value was returned. | |
All classes of the Cyan compiler and java.lang are implicitly | |
imported, as `meta.Tuple2` (Cyan compiler) and `StringBuffer` | |
(java.lang). So there is no need of importing them. If necessary, Java | |
packages (not classes) may be imported using the `import` keyword: | |
import ufscar.dcomp.lib | |
``import`` statements should come before any others *inside* a method. | |
A jar file with the package should be in the `--meta\meta` directory | |
of the current package (of the compilation unit in which the annotation | |
is used). Or in the Java path. Java classes with the full path can be | |
used if a jar file with them are in `--meta\meta`. | |
The workings of this metaobject class is simple: it declares a method | |
for every inherited interface method. This method just interprets the | |
method with the same name of the *Myan* code. The result is that | |
metaprogramming is made in interpreted Cyan, *Myan*. | |
In *Myan*, the ``self`` object has two important methods: ``runFile:`` | |
and ``call:``. The first takes at least a string with the name of a | |
file that should be in a directory ``--data`` of a package, with | |
extension ``myan``. | |
@action_afti_dsa{* | |
func dsa_codeToAdd { | |
runFile: #printProtoData_dsa; | |
} | |
*} | |
There should be a file ``printProtoData_dsa.myan`` in the ``--data`` | |
directory of the current package. To use this file in other package, | |
it is necessary to precede it with the package name. Import declarations | |
of the Cyan source code are not taken into consideration here. Parameters | |
can be passed to the file: | |
runFile: "checkProto", "test", 0; | |
There should be a file ``--data\checkProto(P,Q).myan`` in the current | |
package directory. Parameters ``P`` and ``Q`` can be anyone. But there | |
should be a file with **two** parameters since the ``runFile:`` method | |
has **three** parameters. ``P``and ``Q`` are textually replaced by the | |
parameters ``"test"`` and ``0``. But only when they are full words inside | |
the file (In the file, ``Proto`` is not changed to ``"test"roto). | |
Method ``call:`` calls an action metaobject. An *action metaobject* class | |
should inherit from class `CyanMetaobject` and implement interface | |
`IActionFunction`. A method | |
Object eval(Object arg) | |
should be defined. Inside *Myan* code, ``call:`` will call `eval` passing | |
as a parameter an object of | |
Tuple6<IAbstractCyanCompiler, | |
CyanMetaobjectWithAt, ArrayList<Object>, WrSymbol, | |
WrMethodDec, WrEnv> | |
The first tuple element is the `compiler`object passed as parameter to | |
several interface methods. It may be `null` because some interfaces do | |
not take a `compiler` parameter. The second tuple element is the | |
metaobject, the original one. The third is the list of parameters. | |
Using Cyan syntax, it would be | |
[ "run", "at: Int put: String", 0 ] | |
in | |
call: "checkNumberCalls", "at: Int put: String", 0 | |
The four element, of type `WrSymbol`, is a symbol that may be used | |
inside the *action metaobject* to issue errors. The fifth element is | |
the current method and the last one is the current environment. | |
By the way, the action metaobject ``checkNumberCalls`` does not exist. | |
But `shouldCallSuperMethod` does. It does not take parameters and | |
checks whether the method, the fifth tuple element, extends the | |
superprototype method. Its first statatement should be a call to the | |
superprototype method. Otherwise an error is issued. | |
*Action metaobjects* are used to tasks that Myan code cannot do. | |
Like visit AST nodes. It is necessary a Java object for that. Precisely, | |
a Java object of class `WrASTVisitor` is expected as parameter to method | |
`accept` of every AST class. Then the AST cannot be visited using Cyan | |
code. This job must be done using *action metaobjects*. A good project | |
is to create a *action metaobject* that calls Cyan code for each AST | |
class. The *action metaobject* would be called as | |
@action_afti_dsa{* | |
visit: """ | |
visit: WrMethodDec node, WrEnv env { | |
// visit a method | |
} | |
visit: WrStatementAssignmentList node, | |
WrEnv env { | |
// visit an assignment | |
} | |
"""; | |
*} | |
The Cyan code would be able to visit methods and statements of a | |
protototype, for example. | |
*+} | |
open | |
object Action_afti_dsa | |
var Int counter = 0; | |
func getCounter -> Int { | |
return counter; | |
} | |
func run { | |
var one = 0; | |
@action_afti_dsa{* | |
func dsa_codeToAdd { | |
// file printData_at_dsa.myan is at | |
// directory --data of the current package | |
// The code of this file will print a lot | |
// of information on this prototype at | |
// compile-time | |
runFile: #printData_at_dsa; | |
// 'one = 1;' will be added after | |
// the annotation | |
return "one = 1; "; | |
} | |
func afti_codeToAdd { | |
return [. """ | |
func getZero -> Int = 0; | |
""", "func getZero -> Int" .]; | |
} | |
func afti_beforeMethodCodeList { | |
return [ [. "getCounter", "counter = 5;", false .] ] | |
} | |
*} | |
// getCounter return counter that should be 0 but | |
// metaobject action_afti_dsa, method 'afti_beforeMethodCodeList', | |
// changed its value to 5 by adding code 'counter = 5;' before | |
// the first method statement | |
assert getCounter == 5; | |
// method getZero was added by method 'afti_codeToAdd' of | |
// action_afti_dsa | |
assert getZero == 0; | |
assert one == 1; | |
var String s = "aaa"; | |
@action_afti_dsa{* | |
func dsa_codeToAdd { | |
return """ s = "bbb";"""; | |
} | |
*} | |
assert s == "bbb"; | |
/* use the action metaobject 'fat', for demonstration only, | |
to calculate the factorial of 10 | |
*/ | |
var Long fat10 = 0L; | |
@action_afti_dsa{* | |
func dsa_codeToAdd { | |
let String s = call: #fat, 10; | |
return " fat10 = " ++ s ++ ";\n"; | |
} | |
*} | |
assert fat10 == 3628800L; | |
fat10 = 0L; | |
fat10 = @eval("cyan.lang", "Long"){* | |
let String s = call: #fat, 10; | |
return s; | |
*}; | |
assert fat10 == 3628800L; | |
} | |
/* methods communicateInPrototype_111 and communicateInPrototype_222 | |
test the communication among metaobjects of the same | |
prototype | |
*/ | |
func communicateInPrototype_111 { | |
@action_afti_dsa{* | |
func afti_dsa_afsa_shareInfoPrototype { | |
return "AAAA"; | |
} | |
func afti_dsa_afsa_receiveInfoPrototype { | |
var Array<Tuple<String, Int, Int, Dyn>> annotInfoArray = | |
annotationInfoSet asArray; | |
"The set of shared info is:" println; | |
for t in annotInfoArray { | |
Out println: "[. " ++ t f1 ++ ", " ++ t f2 ++ ", " ++ t f3 ++ | |
", " ++ t f4 ++ " .]"; | |
} | |
} | |
*} | |
} | |
func communicateInPrototype_222 { | |
@action_afti_dsa{* | |
func afti_dsa_afsa_shareInfoPrototype { | |
return "BBBB"; | |
} | |
func afti_dsa_afsa_receiveInfoPrototype { | |
Out println: "number of shared objects is " ++ annotationInfoSet size; | |
} | |
*} | |
} | |
func myprint: Int n { | |
} | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment