Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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,
IAction_ati, IActionNewPrototypes_ati,
IParseWithCyanCompiler_dpa,
ICommunicateInPrototype_ati_dsa
And it should not have parameters. For example, there
is a method `ati_codeToAddCurrentPrototype` in the Java interface
`meta.IAction_ati`. 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_codeToAddCurrentPrototype {
// two variables are accessible:
// ICompiler_afti compiler
// List<Tuple2<CyanMetaobjectAnnotation,
// List<ISlotInterfaceProgramUnit>>> infoList
// the return value has type Tuple2<StringBuffer, String>
// Cyan code
}
func dsa_codeToAdd {
// variable ICompiler_dsa compiler is accessible
// return value has type StringBuffer
// Cyan code
}
func runUntilFixedPoint {
// either 'return true' or 'return false'
}
func afti_beforeMethodCodeList {
// variable ICompiler_afti compiler is accessible
// the return value has type ArrayList<Tuple2<String, StringBuffer>>
// Cyan code
}
func afti_renameMethod {
// variable ICompiler_afti compiler is accessible
// the return value has type ArrayList<Tuple2<String, String []>>
// Cyan code
}
func dsa_NewPrototypeList {
// variable ICompiler_dsa compiler is accessible
// return value has type
// ArrayList<Tuple2<String, StringBuffer>>
}
func afti_NewPrototypeList {
// variable ICompiler_afti compiler is accessible
// return value has type
// ArrayList<Tuple2<String, StringBuffer>>
}
func afti_dsa_shareInfoPrototype {
// the return value is Object
}
func afti_dsa_receiveInfoPrototype {
// variable annotationInfoSet has type
// Set<Tuple4<String, Integer, Integer, Object>>
}
Each Cyan method can also return a value. For example,
`afti_codeToAddCurrentPrototype` should return a value of type
Tuple2<StringBuffer, 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. It is necessary
to import ``ArrayList`` from ``java.util``. 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;" .] ]
}
*}
// 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
You can’t perform that action at this time.