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) | kind | Description | Usage example |
---|---|---|---|
doc | document code | add the attached DSL to the list of documents of the declaration | @doc{* This is the documentation of the 'run' method *} func run { } |
addBeforeMethod | add code | add code before a method. A demonstration metaobject. | @addBeforeMethod(Proto, "run", "\"run called\" println;") |
addCodeFromMetaobject | add code | when 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 |
addCodeToGenericPrototype | add code | Used with addCodeFromMetaobject. See The Cyan Language Metaobject Protocol for more information. | @addCodeToGenericPrototype(String){* code *} @addCodeToGenericPrototype(Id){* code to be added*} |
addMethodTo | add code | add a method to a prototype. A demonstration metaobject. | @addMethodTo(Proto, "run", "func run { }") |
addToSet | add info | add a pair (varName, value) to a map associated to the project. The pair can be accessed by other metaobjects | @addToSet(secure, "yes") |
annot | add info | add information to instance variable, method, prototype, package, or program | @annot("correct this") |
assert | check | check if the boolean expression is true. If not, a warning message is output. | assert ok; assert n == 0; |
range | check | check if variable hold only values in the range | Int@range(1, 12) Char@range('a', 'z') |
regex | check | check if a String variable only holds strings that match a regular expression | String@regex("[A-Z]+") |
tainted and untainted | check | check 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) |
type | check | similar C-language typedef, strong types for any type | Int@type(inBytes) |
callTestMethods | add code | add code to call all methods of the current prototype ending with 'Test' | @callTestMethods |
callUnaryMethods | add code | add code to call unary methods of the current prototype whose name match the pattern that is parameter | @callUnaryMethods(".*Test") |
changeFunctionForMethod | replace message passing, use in Cyan libraries only | use only in prototype Any. Create a anonymous function that represents a method | @changeFunctionForMethod func functionForMethod: String |
checkCatchParameter | check | whether 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 Code | add methods, fields,and statements | @action_afterResTypes_semAn{* .... *} |
addFieldInfo | add Code | add fields with information on fields and methods | @addFieldInfo object Program ... end |
letter | check | check whether a letter is assigned to a Char variable | var Char@letter ch; ch = 'a'; |
restrictTo | check | check the values assigned to a variable | var Int@restrictTo{* self >= 0 *} age; |
checkIsA | check | check if the argument to a call to method isA: of Any is a prototype | @checkIsA final func isA: (Any proto) -> Boolean |
checkMethodEqualEqual | check | check 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 | check | a demonstration metaobject that checks the style of variables, fields, methods, and prototypes | @checkStyle object Person ... end |
codeAfter | check | a 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; |
fsmDSLMethods | add code/check | check if method calls obey a Finite State Machine | @fsmDSLMethods{* ... *} object FSMTest ... end |
@columnNumber | add code | replaced by the column number of '@' of the annotation | var cn = @columnNumber; |
compilationInfo | add code | give information according to the argument | var lineNumber = @compilationInfo("lineNumber"); |
concept See also GroupWork and IntGroupPlus | check | check restrictions of generic prototype parameters. See The Cyan Language Metaobject Protocol for more information. | @concept{* T has [ func Boolean ] *} |
countFieldAccess | add code | count how many times a field was accessed | @countFieldAccess var String s = "0"; |
countNew | add code | count how many objects of a prototype were created (if attached to all init or init: methods) | @countNew func init { ... } |
createArrayMethods | add code | add methods 'sort' and 'sortDescending' to a generic array. Used only in prototype cyan.lang.Array | @createArrayMethods object Array ... end |
createCatchExit | add code | Used only in cyan.lang.CatchExit | @createCatchExitobject CatchExit end |
createCatchIgnore | add code | Used only in cyan.lang.CatchIgnore | object CatchIgnore @createCatchIgnore end |
createCatchMethodsForFunctionNil | add code | Used only in cyan.lang.Function | abstract object Function @createCatchMethodsForFunctionNil ... end |
createCatchWarning | add code | Used only in cyan.lang.CatchWarning | object CatchWarning @createCatchWarning end |
createExceptionConverter | add code | Used only in cyan.lang.ExceptionConverter | object ExceptionConverter @createExceptionConverter end |
createExceptionEncapsulator | add code | Used only in cyan.lang.ExceptionEncapsulator | object ExceptionEncapsulator @createExceptionEncapsulator end |
createFieldIfAccessed | intercept field access | create a field in a hash table | @createFieldIfAccessed object Test ... end |
createFunction | add code | Used only in cyan.lang.Function | abstract object Function @createFunction end |
createPrototype | create prototype | create 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" ) |
createTuple | add code | Used only in cyan.lang.createTuple | object Tuple @createTuple end |
deprecated | check | Issue an error message whenever the attached method can be called | @deprecated("Method 'outdatedMethod:' was replaced by 'newMethod:'") func outdatedMethod: String s { } |
eval | add code | evaluate and return the value of the attached interpreted Cyan code | var s = @eval("cyan.lang", "Int"){* var k = 0; for n in 1..10 { k = k + n } return k *}; assert s == 55; |
extract | add code | transform an identifier into a string. It allows to pretend that generic prototype instantiations accept Int values | object MyVector<T> var Array end |
feature | add information | add information to a declaration that can be retrieved by other metaobjects, the compiler, or at runtime. | @feature(test "Test always") object MyTest end |
genericPrototypeInstantiationInfo | add information | This 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 information | get the value associated to a package key, transformed into a string | var String value = @getPackageValueFromKey(test); |
getProgramValueFromKey. See also getPackageValueFromKey and setVariable | get information | get the value associated to a program key, transformed into a string | var String value = @getProgramValueFromKey(debug); |
grammarMethod | create DSL | create 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 ... } |
immutable | check | check if a prototype is immutable | @immutable object Person ... end |
init | add code | create constructors based on field names | object Init @init(max) @init(name, lastLetter, max) ... let String name = "init"; var Char lastLetter = 'z'; var Int max end |
insertCode | add code | add 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; |
error | cause error | issue a compilation error with the message that is parameter | @error("This code is not working yet. Fix it") |
keepValue | add code | save 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); } |
lineNumber | add code | the annotation is an expression with the value of the current line number | var nextLine = @lineNumber; |
Literal string 'r' or 'R' | add code | r"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 code | the string is unescaped | assert n"\n" == 2; |
log | add code | log if the annotated method or methods of the annotated prototype were called | @log(mylog) object Test ... end |
Number_base | add code | a literal number in base 2 till 36 | assert 0FF_FF_BASE16 == 65535; assert 11_base_8 == 9; |
Number_bin | add code | a literal number in base 2 | assert 101bin == 5; assert 10BIN == 2; assert 111_111Bin == 63; |
OnDeclaration_afterSemAn | check | check 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_afterResTypes_semAn_afterSemAn | add code and check | add 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 check | add 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; |
onFieldMissing | add code and check | add 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_afterSemAn | check | check a message passing | @onMessageSend_afsa{* func afsa_checkKeywordMessageSend { ... } *} func at: Int n, String s { } |
OnMessageSend_semAn | add code | replace 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 code | replace 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_afterResTypes_semAn_afterSemAn | add code and check | add 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 { } |
onOverride | check | Cyan 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_afterResTypes_semAn_afterSemAn | add code and check | add 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 |
onSubprototype | check | Cyan statements are executed whenever the attached prototype is inherited | @onSubprototype{* Out println: "Do not forget of defining a 'subtest' method" *} open object Test ... end |
onVariableDeclaration | add code and check | add 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 SubOverrideTest | create test | create 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 } |
parametersToString | add code | A demonstration metaobject that converts all of the annotation parameters to a string | Out println: @parametersToString( 0, 'a', "Newton", Newton, // identifier, counts as string [ 0, 1, 2 ], [ "zero" -> 0, "one" -> 1 ], [. "Newton", 1643 .] *}; |
property | add code | create get and set methods for 'var' fields and get methods for 'let' fields | @property var Int total = 0; |
readOnly | check | demands that no field, shared or non-shared, is assigned to in the attached method | @readOnly func at: Int n { ... } |
renameMethod | rename method and add code | A demonstration metaobject. It renames a method and add another method with the old name. | see the example |
replaceCallBy | add code | replace a message passing by code | @replaceCallBy{* 3*n *} func threeTimes: Int n -> Int = n; |
restrictImplementation. See also ImplementRestrictImplementation | check | Restrict the prototypes allowed to implement the annotated interface | @restrictImplementation("ImplementRestrictImplementation") interface RestrictImplementation func myprint end |
restrictOverrideTo. See also SubRestrictOverrideTo | check | Restrict the prototypes allowed to override the annotated method | @restrictOverrideTo(main.SubRestrictOverrideTo, main.SubOtherRestrictOverrideTo) func myprint { } |
rpn | add code | At compile-time, evaluates a RPN expression | let value = @rpn{* 1 2 3 * + *}; |
runFile. See also afti_dsa_test(MetSig,UMS,Ret).myan | add code and check | run file with myan methods that is in a file. Similar to action_afti_dsa | @runFile("afti_dsa_test", "with:1 do:1", "unary", 10) |
runPastCode | add code | The 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 value | associate 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 | check | This is an action metaobject. It checks whether a method calls the superprototype method as its first statement | @onOverride{* call: #shouldCallSuperMethod; *} func run { } |
shout | add code | A demonstration metaobject that changes all alphabetic characters in literal strings of a method to uppercase letters | @shout func shoutTest { ... } |
symbolToString | add code | convert the parameter to the annotation into a string. Used mainly in generic prototypes | var String s = @symbolToString(Int); |
cep | check | Warn 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 code | prints the expression that follows the macro, as a string, and its value | printexpr n + 1; |