-
Notifications
You must be signed in to change notification settings - Fork 45
Template Engine
In Greed, Templates
are used to generating code to test your program against the example test cases. Many other plugins, like FileEdit, supports templates, too. But in Greed, templates are much more powerful and completely flexible. You can involve in almost every step of the code generation and take full control of the generating process.
There're two templates in Greed, the source code template and the test code template.
We generate the code as follows:
-
Generate test code, bind it to the
${TestCode}
key in the same model. -
Use the source code, using the model with
${TestCode}
binded in 1.
To fully enable the power of template, we use a lightweight template engine jmte. It's efficient, simple, and enough powerful.
If you're interested in this engine, read its documentation.
But as in Configuration, I'll briefly describe the template language, although the documentation of jmte has done such a thing. Here for 2 reasons,
-
Our version of jmte is 3.0, which is the newest version, and not all features are mentioned in the current documentation of jmte.
-
To make the code more pretty, some slight changes are made to jmte. I will tell you what I did in the following sentences.
Very simple to use, here it comes!
Templates are surrounded by ${
and }
. Anything else will be directly copied to the rendering result.
Templates are translated based on a model. The model is a String->Object
mapping, in which Object
can be anything.
jmte provided built-in functional templates like ${if}
, ${foreach}
, described below:
-
${if X}...${else}...${end}
- if keyX
is found in the model, the...
is inserted, the${else}
is optional. -
${foreach values v , }...${end}
-values
is a iterable object, and the loop will bindv
to every value invalues
,v
will be put in the model while in the scope in the...
. A,
is inserted after every...
block, except the last one. -
${key;custom(format)}
-custom
is a named renderer programmatically added in the engine, the value ofkey
will be passed into the named renderer and return the rendering result,format
is the parameter for the custom renderer. Currently, a 'string' utility renderer is binded, for transforming strings, see here.
Recall the ${Contest.Name}
in pathPattern
? If you wanna, you can write ${Contest.Name}${if Contest.Div}/DIV ${Contest.Div}${end}
.
It'll produce folder path like SRM 257/DIV 2
if SRM 257
contest has a division, or just SRM 257
if not.
The config values and templates use the same engine to render.
One of the limitation of the template mechanism above, is that the template cannot do anything about the plain text outside the template. So if I write,
${foreach ...}
${end}
See? There're two blank lines after each tag, and will be inserted into the result. That's not good practice for a programming code. So what I did, is that, you can change the above to:
${<foreach ...}
${<end}
The <
is like an option to the tag token. It will remove the directly following newline. It will work on any tag.
In this way, we can get a better formatted code.
Remember those predefined variables introduced in configuration page? Well, that's just a portion of them. Here's the full list. Those not listed in configuration page can only be used in the templates.
Value Name | Example | Comment |
---|---|---|
Contest.Name |
SRM 257 |
Contest name |
Contest.Div |
1 |
Contest division number |
Problem.Name |
RabbitWorking |
Problem name |
Problem.Score |
1000 |
Problem score, in integer |
ClassName |
StangeDictionary2 |
Class name |
Method.Name |
getProbabilities |
Method name |
Method.Params |
vector<string> words |
Method parameter list, splitted by ,
|
Method.ReturnType |
vector<int> |
Method return type, will be binded to type identifier according to current language |
Method.ReturnType;ZeroValue |
vector<int>() |
A special renderer for a default return value. Make sure your code can be correctly compiled |
TestCode |
Too long to show... | The test code are generated first and binded here, in convenient for user to seperate the test code template from the source code template |
These values are enough for you to generate your code template.
What? You wanna DIY your test code template? OK, OK, read on.
Value Name | Comment |
---|---|
HasArray |
Set to true if any of the parameter or return type is an array |
ReturnsArray |
Set to true if the return type is an array, you may need a function to print it to diff the result |
RecordRuntime |
Set to true if the runtime record is turned on in the configuration |
RecordScore |
Set to true if the score record is turned on in the configuration |
CreateTime |
Set to the create system time of this template, used for calculating the score |
Examples |
Example object list, each of which is of type Testcase . Iterate over it using ${foreach Examples e} . |
e.Num |
the example number of e (a Testcase object above) |
e.Input |
A list of ParamValue represents the input, each for one parameter |
e.Output |
The output value of this example, let it be parVal below |
parVal.Param |
This is a Param object let it be p below |
p.Name |
The name of a parameter |
p.Index |
The index of a parameter (0-indexed) |
p.Type |
The type of a parameter, it is a type object(we call it t ) |
t.Primitive |
the primitive type of t , like String , int
|
t.Array |
Set to true if t represents an array |
t.RealNumer |
Set to true if the primitive type in t is double
|
t.String |
Set to true if the primitive type in t is String
|
t.LongInteger |
Set to true if the primitive type in t is long
|
parVal.Value |
the given value of this param |
parVal.ValueList |
The value list, if it's an array, can be iterated on via ${foreach}
|
NumOfExamples |
Number of example test cases. A workaround for the not-working ${Examples.length}
|
For a complete use-case, refer to the built-in templates like Template.cpp
and GreedTest.cpp
, with the greed.model
package in the source code of Greed.
The model are also hierarchical, just like the config keys. Sounds familiar, huh? Better show you with examples.
${e.Output.Param.Type.Array}
is rendered as follows:
- Looks for key
e
in the model, it's either a predefined variable, or a local variable in theforeach
tag. If not found, a empty string will be used as the result - If found, the value object
r
fore
is obtained. Based on its type, do the following - If
r
is aString
, no key forOutput
, empty string! - If
r
is another Map, goto 1 and repeat for keyReturnType
- Otherwise, see if object
r
has method calledgetOutput
, orisOutput
, then if it has a public field calledOutput
, if so, use the return value or field value.
When the final value v
are fetched for all hierarchical keys, if the value is not a string, A typed renderer will be looked up for rendering v
. If not found, v.toString()
is called.
These are not described in the documentation of jmte, in case you're interested.