Last active
August 30, 2022 08:14
-
-
Save joseoliv/609dc35bfb2a93c192ef44e3862fbf37 to your computer and use it in GitHub Desktop.
metaobject action_afti_dsa
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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_afterResTypes_semAn 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_semAn, IActionNewPrototypes_semAn, | |
IAction_afterResTypes, IActionNewPrototypes_afterResTypes, | |
IParseWithCyanCompiler_parsing, | |
ICommunicateInPrototype_afterResTypes_semAn | |
And it should not have parameters. For example, there | |
is a method `afterResTypes_addCodeTo` in the Java interface | |
`meta.IAction_afterResTypes`. 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 afterResTypes_codeToAdd { | |
// two variables are accessible: | |
// ICompiler_afterResTypes compiler | |
// List<Tuple2<CyanMetaobjectAnnotation, | |
// List<ISlotInterfaceProgramUnit>>> infoList | |
// the return value has the Cyan type Tuple<String, String> | |
} | |
func semAn_codeToAdd { | |
// variable ICompiler_semAn compiler is accessible | |
// return value has the Cyan type String | |
} | |
func runUntilFixedPoint { | |
// either 'return true' or 'return false' | |
} | |
func afterResTypes_beforeMethodCodeList { | |
// variable ICompiler_afterResTypes compiler is accessible | |
// the return value has Cyan type | |
// Array<Tuple<String, String, Boolean>> | |
} | |
func afterResTypes_renameMethod { | |
// variable ICompiler_afterResTypes compiler is accessible | |
// the return value has the Cyan type | |
// Array<Tuple<String, Array<String>>> | |
} | |
func semAn_NewPrototypeList { | |
// variable ICompiler_semAn compiler is accessible | |
// return value has the Cyan type | |
// Array<Tuple<String, String>> | |
} | |
func afterResTypes_NewPrototypeList { | |
// variable ICompiler_afterResTypes compiler is accessible | |
// return value has the Cyan type | |
// Array<Tuple<String, String>> | |
} | |
func afterResTypes_semAn_shareInfoPrototype { | |
// the return value is Object | |
} | |
func afterResTypes_semAn_receiveInfoPrototype { | |
// variable annotationInfoSet has the Cyan type | |
// Set<Tuple<String, Int, Int, Dyn>> | |
} | |
Each Cyan method can also return a value. For example, | |
`afterResTypes_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_afterResTypes_semAn{* | |
func semAn_codeToAdd { | |
runFile: #printProtoData_semAn; | |
} | |
*} | |
There should be a file ``printProtoData_semAn.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_afterResTypes_semAn{* | |
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_afterResTypes_semAn | |
var Int counter = 0; | |
func getCounter -> Int { | |
return counter; | |
} | |
func run { | |
var one = 0; | |
@action_afterResTypes_semAn{* | |
func semAn_codeToAdd { | |
// file printData_at_semAn.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_semAn; | |
// 'one = 1;' will be added after | |
// the annotation | |
return "one = 1; "; | |
} | |
func afterResTypes_codeToAdd { | |
return [. """ | |
func getZero -> Int = 0; | |
""", "func getZero -> Int" .]; | |
} | |
func afterResTypes_beforeMethodCodeList { | |
return [ [. "getCounter", "counter = 5;", false .] ] | |
} | |
*} | |
// getCounter return counter that should be 0 but | |
// metaobject action_afterResTypes_semAn, method 'afterResTypes_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 'afterResTypes_codeToAdd' of | |
// action_afterResTypes_semAn | |
assert getZero == 0; | |
assert one == 1; | |
var String s = "aaa"; | |
@action_afterResTypes_semAn{* | |
func semAn_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_afterResTypes_semAn{* | |
func semAn_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_afterResTypes_semAn{* | |
func afterResTypes_semAn_afterSemAn_shareInfoPrototype { | |
return "AAAA"; | |
} | |
func afterResTypes_semAn_afterSemAn_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_afterResTypes_semAn{* | |
func afterResTypes_semAn_afterSemAn_shareInfoPrototype { | |
return "BBBB"; | |
} | |
func afterResTypes_semAn_afterSemAn_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