Metaobject Examples

This table shows some metaobjects that are ready to use, with examples (download all the examples). For a more direct feeling of metaobjects, see action_afti_dsa and eval. For Codegs, the visual metaobjects, see this page.

Name
(Clique to Example)
kindDescriptionUsage example
doc document codeadd the attached DSL to the list of documents of the declaration @doc{*
This is the documentation of the 'run' method
*}
func run {
}
addBeforeMethodadd codeadd code before a method. A demonstration metaobject.@addBeforeMethod(Proto, "run", "\"run called\" println;")
addCodeFromMetaobjectadd codewhen attached to a generic prototype, an annotation addCodeFromMetaobject can ask to an annotation addCodeToGenericPrototype attached to a generic parameter to supply code to be inserted in the generic prototype.@addCodeFromMetaobject
addCodeToGenericPrototypeadd codeUsed with addCodeFromMetaobject. See The Cyan Language Metaobject Protocol for more information.@addCodeToGenericPrototype(String){* code *}

@addCodeToGenericPrototype(Id){* code to be added*}
addMethodToadd codeadd a method to a prototype. A demonstration metaobject.@addMethodTo(Proto, "run", "func run { }")
addToSetadd infoadd a pair (varName, value) to a map associated to the project. The pair can be accessed by other metaobjects@addToSet(secure, "yes")
annotadd infoadd information to instance variable, method, prototype, package, or program@annot("correct this")
assert
checkcheck if the boolean expression is true. If not, a warning message is output.assert ok;
assert n == 0;
rangecheckcheck if variable hold only values in the rangeInt@range(1, 12)
Char@range('a', 'z')
regexcheckcheck if a String variable only holds strings that match a regular expressionString@regex("[A-Z]+")
tainted and untaintedcheckcheck if a value of type tainted is assigned to a variable of type untainted. See the Checker Framework for more details. See also prototype TaintedToUntainted and the project file.String@tainted(sql) String@untainted(sql)
typechecksimilar C-language typedef, strong types for any typeInt@type(inBytes)
callTestMethodsadd codeadd code to call all methods of the current prototype ending with 'Test'@callTestMethods
callUnaryMethodsadd codeadd code to call unary methods of the current prototype whose name match the pattern that is parameter@callUnaryMethods(".*Test")
changeFunctionForMethodreplace message passing, use in Cyan libraries onlyuse only in prototype Any. Create a anonymous function that represents a method@changeFunctionForMethod
func functionForMethod: String
checkCatchParametercheckwhether each parameter to a catch: selector has at least one 'eval:'
method, each of them accepting one parameter whose type is subprototype of CyException
@checkCatchParameter
catch: CyException e
action_afti_dsa
add Codeadd methods, fields,and statements@action_afti_dsa{*
....
*}
addFieldInfoadd Codeadd fields with information on fields and methods@addFieldInfo
object Program ... end
letter
checkcheck whether a letter is assigned to a Char variablevar Char@letter ch;
ch = 'a';
restrictTo
checkcheck the values assigned to a variablevar Int@restrictTo{* self >= 0 *} age;
checkIsAcheckcheck if the argument to a call to method isA: of Any is a prototype@checkIsA
final func isA: (Any proto) -> Boolean
checkMethodEqualEqualcheckcheck if the argument to method == of Any is legal. It is illegal to compare values of types that will always result in false, like an Int and a Char. @checkMethodEqualEqual
func == (Dyn other) -> Boolean
checkStyle checka demonstration metaobject that checks the style of variables, fields, methods, and prototypes@checkStyle
object Person
...
end
codeAfter checka demonstration metaobject. It adds the code attached to the annotation after the local variable declaration the annotation is attached to.@codeAfter{*
sum = 1;
*}
var Int sum = 0;
assert sum == 1;
fsmDSLMethodsadd code/checkcheck if method calls obey a Finite State Machine@fsmDSLMethods{* ... *}
object FSMTest
...
end
@columnNumberadd codereplaced by the column number of '@' of the annotationvar cn = @columnNumber;
compilationInfoadd codegive information according to the argumentvar lineNumber = @compilationInfo("lineNumber");
concept
See also GroupWork and
IntGroupPlus
checkcheck restrictions of generic prototype parameters. See The Cyan Language Metaobject Protocol for more information.@concept{*
T has [ func Boolean ]
*}
countFieldAccessadd codecount how many times a field was accessed@countFieldAccess
var String s = "0";
countNewadd code count how many objects of a prototype were created (if attached to all init or init: methods)@countNew
func init { ... }
createArrayMethodsadd codeadd methods 'sort' and 'sortDescending' to a generic array. Used only in prototype cyan.lang.Arrayobject Array
...
@createArrayMethods
end
createCatchExit
add codeUsed only in cyan.lang.CatchExit. It adds the methods of this prototype. See the Cyan manual.object CatchExit
@createCatchExit
end
createCatchIgnoreadd codeUsed only in cyan.lang.CatchIgnore. It adds the methods of this prototype. See the Cyan manual.object CatchIgnore
@createCatchIgnore
end
createCatchMethodsForFunctionNiladd codeUsed only in cyan.lang.Function. Add methods for this prototypeabstract object Function

@createCatchMethodsForFunctionNil
...
end
createCatchWarningadd codeUsed only in cyan.lang.CatchWarning. It adds the methods of this prototype. See the Cyan manual.object CatchWarning
@createCatchWarning
end
createExceptionConverteradd codeUsed only in cyan.lang.ExceptionConverter. It adds the methods of this prototype. See the Cyan manual.object ExceptionConverter
@createExceptionConverter
end
createExceptionEncapsulatoradd codeUsed only in cyan.lang.ExceptionEncapsulator. It adds the methods of this prototype. See the Cyan manual.object ExceptionEncapsulator
@createExceptionEncapsulator
end
createFieldIfAccessedintercept field accesscreate a field in a hash table@createFieldIfAccessed
object Test
...
end
createFunctionadd codeUsed only in cyan.lang.Function. It adds the methods of this prototype. See the Cyan manual.abstract object Function
@createFunction
end
createPrototype create prototypecreate one or two prototypes. A demonstration metaobject @createPrototype(
"Dragon",
"package main\nobject Dragon\n func get -> Int { return 0 }\n\nend",

"Elf",
"package main\nobject Elf\n func get -> String { return \"Elf !\" }\n\nend"
)
createTupleadd codeUsed only in cyan.lang.createTuple. It adds the methods of this prototype. See the Cyan manual.object Tuple
@createTuple
end
deprecated checkIssue an error message whenever the attached method can be called @deprecated("Method 'outdatedMethod:' was replaced by 'newMethod:'")
func outdatedMethod: String s { }
eval add codeevaluate and return the value of the attached interpreted Cyan codevar s = @eval("cyan.lang", "Int"){*
var k = 0;
for n in 1..10 { k = k + n }
return k
*};
assert s == 55;
extract add codetransform an identifier into a string. It allows to pretend that generic prototype instantiations accept Int valuesobject MyVector<T>

var Array array = Array new: @extract(V);

end
feature add informationadd information to a declaration that can be retrieved by other metaobjects, the compiler, or at runtime.@feature(test "Test always")
object MyTest
end
genericPrototypeInstantiationInfoadd informationThis metaobject is used only by the Cyan compiler. It adds information on every compiler-created generic prototype instantiation. Because of this, error messages on generic prototype instantiations are very clear in Cyan.@genericPrototypeInstantiationInfo("main", "Program", 538, 13)
object G1
...
end
getPackageValueFromKey. See also getProgramValueFromKey and setVariable
get informationget the value associated to a package key, transformed into a stringvar String value = @getPackageValueFromKey(test);
getProgramValueFromKey. See also getPackageValueFromKey and setVariable
get informationget the value associated to a program key, transformed into a stringvar String value = @getProgramValueFromKey(debug);
grammarMethod create DSLcreate a DSL using regular operators and message keywords. One of the biggest metaobjects. See The Cyan Language Metaobject Protocol for more information.@grammarMethod{*
(name: String (at: String)? (with: String)* )+
*}
func meet: Array, Array>> p {
...
}
immutable checkcheck if a prototype is immutable@immutable
object Person
...
end
init add codecreate constructors based on field namesobject Init

@init(max)
@init(name, lastLetter, max)
...
let String name = "init";
var Char lastLetter = 'z';
var Int max
end


insertCode add codeadd fields, methods, and statements. The code is produced by interpreted Cyan at compile-time var Int fat12;
@insertCode{*
var p = 2;
for n in 3..12 {
p = p*n
}
insert: " fat12 = " ++ p ++ ";" ++ '\n';
*}
"The factorial of 12 is $fat12" println;
errorcause errorissue a compilation error with the message that is parameter@error("This code is not working yet. Fix it")
keepValue
add codesave the last five values of a variable in a file. A demonstration metaobject 7 times: {
var Int r = Math randomInt;
// record the last five random numbers in the file
@keepValue(r);
}
lineNumberadd codethe annotation is an expression with the value of the current line numbervar nextLine =
@lineNumber;
Literal string 'r' or 'R'add coder"a*b+" is replaced by an object of RegExpr, that represents a literal expression var ok = "aabc" ~= r"a*b*c";
assert ok;
Literal string 'n' or 'N'
add codethe string is unescapedassert n"\n" == 2;
log
add codelog if the annotated method or methods of the annotated prototype were called@log(mylog)
object Test
...
end
Number_base
add codea literal number in base 2 till 36assert 0FF_FF_BASE16 == 65535;
assert 11_base_8 == 9;
Number_bin
add codea literal number in base 2assert 101bin == 5;
assert 10BIN == 2;
assert 111_111Bin == 63;
onDeclaration_afsa
checkcheck any declaration like method, prototype, and field@onDeclaration_afsa{*
var Int size = compiler getMethodDecList size;
if size < 10 {
metaobject addError:
"This prototype should have at least ten methods";
}
*}
object Test
...
end
onDeclaration_afti_dsa_afsaadd code and checkadd code to the current prototype and check a declaration like a prototype, method, or field@onDeclaration_afti_dsa_afsa{*
func afti_codeToAdd {
var annotation = metaobject getMetaobjectAnnotation;
var String name = annotation getDeclaration getName;
var String typeName = annotation getDeclaration getType getName;
return [. "func get_" ++ name ++ " -> " ++ typeName ++
" = " ++ name ++ ";", "func get_" ++ name ++ " -> Int" .]
}
*}
var Int age = 0;
onFieldAccess
add code and checkadd fields and methods to the current prototype and check accesses to a field/* Even if this field is set to another value,
its value will always be 0
*/
@onFieldAccess{*
func dsa_replaceGetField {
return "0";
}
*}
var Int alwaysZero = 9;
onFieldMissingadd code and checkadd fields and methods to the current prototype, can introduce virtual fields in a prototype@onFieldMissing{*
func dsa_replaceGetMissingField {
// replace 'read' accesses to 'cyan' by 0
var String name = fieldToGet asString;
if name startsWith: "self." { name = name substring: 5 }
var Int n = -1;
if name == "self.cyan" {
return [. "cyan.lang", "Int", "0" .];
}
// otherwise, return null and there will be
// a compilation error "field not found"
}
*}
object Test
...
end
onMessageSend_afsa

check check a message passing@onMessageSend_afsa{*
func afsa_checkKeywordMessageSend {
...
}
*}
func at: Int n, String s {
}
onMessageSend_dsa

add codereplace a message passing by any code /*
message passings
x mult: n, other
are replaced by, simply, 0.
*/
@onMessageSend_dsa{*
func dsa_analyzeReplaceKeywordMessage {
return [. "0", "cyan.lang", "Int" .]

}
*}
func mult: Int n to: Int other -> Int {
return n*other
}
onMethodMissing

add codereplace a message passing for which no method is adequate by any code@onMethodMissing{*
// any unary message for which there
// is no method is replaced by ""
func dsa_analyzeReplaceUnaryMessage {
return [. "", "cyan.lang", "String" .];
}
*}
object Test
...
end
onOverride_afti_dsa_afsaadd code and checkadd fields, methods, and statements. Check when a method is overridden.@onOverride_afti_dsa_afsa{*
// only prototypes whose names start with 'Sub' can inherit
// this prototype. A message is printed, but no error is issued
func afsa_checkOverride {
let String subProtoName = overridedMethod getDeclaringObject getName;
let String methodName = overridedMethod getName
if !(subProtoName startsWith: "Sub") {
Out println: "I am really sorry, but method " ++
methodName ++ " can only be overridden in prototypes whose name " ++
"start with 'Sub'";
}
}
*}
func draw {
}
onOverridecheckCyan statements are executed whenever the attached method is overridden@onOverride{*
// the overridden method should call
// this method in its first statement
// shouldCallSuperMethod is an action function
// of package cyan.lang
call: #shouldCallSuperMethod;
*}
func draw {
}
onSubprototype_afti_dsa_afsaadd code and checkadd fields, methods, and statements. Check when a prototype is inherited@onSubprototype_afti_dsa_afsa{*
func afsa_checkSubprototype {
let WrEnv env = compiler getEnv;
let WrProgramUnit attachedProgramUnit =
metaobject getAttachedDeclaration;
if attachedProgramUnit getPackageName: env != subPrototype getPackageName: env {
Out println:
"Prototype " ++ attachedProgramUnit getFullName ++
" can only be inherited in its own package. It is " ++
"being inherited by " ++ subPrototype getFullName;
}
}
*}
open
object Test
...
end
onSubprototypecheckCyan statements are executed whenever the attached prototype is inherited@onSubprototype{*
Out println: "Do not forget of defining a 'subtest' method"
*}
open
object Test
...
end
onVariableDeclarationadd code and checkadd fields and methods to the current prototype, add statements after a variable declaration@onVariableDeclaration{*
func dsa_codeToAddAfter {
return """ webpage = complexInitialization;"""

}
*}
var Int webpage;
overrideTest. See also SubOverrideTestcreate testcreate a test prototype whenever a method is overridden in a subprototype @overrideTest{*
func test {
var (%SUBPROTOTYPE%) subproto = (%SUBPROTOTYPE%) new;
var localTotal = subproto getTotal;
subproto add: 100;
subproto withdraw: 100;
assert localTotal == subproto getTotal;
}
*}
func withdraw: Int amount {
total = total - amount
}
parametersToStringadd codeA demonstration metaobject that converts all of the annotation parameters to a stringOut println: @parametersToString(
0,
'a',
"Newton",
Newton, // identifier, counts as string
[ 0, 1, 2 ],
[ "zero" -> 0, "one" -> 1 ],
[. "Newton", 1643 .]
*};
property
add codecreate get and set methods for 'var' fields and get methods for 'let' fields @property var Int total = 0;
prototypeCallOnlycheckA method annotated with prototypeCallOnly can only be called on a prototype @prototypeCallOnly
final func unary { value = 0; }
readOnly
checkdemands that no field, shared or non-shared, is assigned to in the attached method@readOnly
func at: Int n { ... }
renameMethodrename method and add codeA demonstration metaobject. It renames a method and add another method with the old name.see the example
replaceCallByadd codereplace a message passing by code@replaceCallBy{* 3*n *}
func threeTimes: Int n -> Int = n;
restrictImplementation. See also ImplementRestrictImplementation
checkRestrict the prototypes allowed to implement the annotated interface@restrictImplementation("ImplementRestrictImplementation")
interface RestrictImplementation

func myprint

end
restrictOverrideTo. See also SubRestrictOverrideTo
checkRestrict the prototypes allowed to override the annotated method@restrictOverrideTo(main.SubRestrictOverrideTo, main.SubOtherRestrictOverrideTo)
func myprint { }
rpn
add codeAt compile-time, evaluates a RPN expressionlet value = @rpn{* 1 2 3 * + *};
runFile. See also afti_dsa_test(MetSig,UMS,Ret).myanadd code and checkrun file with myan methods that is in a file. Similar to action_afti_dsa@runFile("afti_dsa_test", "with:1 do:1", "unary", 10)
runPastCodeadd codeThe attached Cyan code can use objects of the last time the program run.var RunPastCode past2 = self;
@runPastCode(true, past2){*
var Int fatorial5 = past2 fat: 5;
return "f5 = " ++ fatorial5 ++ ";";
*}
setVariable. See also getProgramValueFromKey and getPackageValueFromKey
associate a key to a valueassociate a key to a value in the program or in a package. Used inside the project file.@setVariable(debug, "yes")
@setVariable(author, "Jose")
// elided
program
// elided
@setVariable(test, create)
@setVariable(goal, "Test all metaobjects")
package metaobjectTest
// elided
shouldCallSuperMethod


checkThis is an action metaobject. It checks whether a method calls the superprototype method as its first statement@onOverride{*
call: #shouldCallSuperMethod;
*}
func run {
}
shout


add codeA demonstration metaobject that changes all alphabetic characters in literal strings of a method to uppercase letters@shout
func shoutTest { ... }
symbolToString


add codeconvert the parameter to the annotation into a string. Used mainly in generic prototypesvar String s =
@symbolToString(Int);
cep


checkWarn the compiler that it should expect a compilation error 'n' lines ahead with the message 'mss' if the annotation is
@cep(n, mss)
@cep(1, "Expression expected)
var k = 1 + ;
printexpr
add codeprints the expression that follows the macro, as a string, and its valueprintexpr n + 1;