Picocog is a tiny library for use in formatting text programatically (indents and placeholders) for the purposes of source code generation.
Picocog’s only purpose is to output indented text, don’t expect anything fancier than that. Picocog supports deferred writing, indentation and not much more.
The initial release is just 6K. Size doesn’t matter.
The basic example simply demonstrates indentation.
PicoWriter w = new PicoWriter();
w.writeln("int foo = calcFoo();");
w.writeln("");
w.writeln("// We shall dance here");
w.writeln_r("if (foo == 0) {");
w.writeln("sayHello();");
w.writeln_lr("} else if (foo < 100) {");
w.writeln("sayGoodbye();");
w.writeln_lr("} else {");
w.writeln("sayAnything();");
w.writeln_l("}");
System.out.println(w.toString());
int foo = calcFoo();
// We shall dance here
if (foo == 0) {
sayHello();
} else if (foo < 100) {
sayGoodbye();
} else {
sayAnything();
}
Note
|
Adding in comments, even silly ones, is extremely important when generating sourcecode. They act as markers so that it is easy to locate the corresponding code generator code. |
This example demonstrates out of sequence writing (deferrals).
A deferral can be placed within the write stream, and then can be used to insert code at the reservation point. The point at which the call to createDeferredWriter() is called is significant. Indentation levels are inherited.
This is useful where there are blocks of code in different parts of the source that need to be updated at the same time.
Although not demonstrated here, it is possible to create deferrals within deferrals using the same API.
// This is our top level source formatter
PicoWriter topWriter = new PicoWriter();
String myPackageName = "com.samplepackage";
String myClassName = "MyClass";
topWriter.writeln ("package " + myPackageName + ";");
topWriter.writeln ("");
topWriter.writeln_r ("public class "+myClassName+" {");
PicoWriter memvarWriter = topWriter.createDeferredWriter();
topWriter.writeln_r ("{");
PicoWriter indentedSection = topWriter.createDeferredWriter();
topWriter.writeln_l ("}");
topWriter.writeln("");
// Reserve a place at the current row
PicoWriter methodSection = topWriter.createDeferredWriter();
memvarWriter.writeln("String myString = null;" );
indentedSection.writeln("// Contents of the indented section (1)");
memvarWriter.writeln("String myString2 = null;" );
indentedSection.writeln("// Contents of the indented section (2)");
// Reserve a place at the current row
PicoWriter mainMethod = methodSection.createDeferredWriter();
mainMethod.writeln_r("public static void main(String[] args) {");
mainMethod.writeln_r("if (args.length == 0) {");
mainMethod.writeln("System.out.println(\"Require more than one argument\");");
mainMethod.writeln_lr("} else if (args.length == 1) {");
mainMethod.writeln("doSomething();");
mainMethod.writeln_lr("} else {");
mainMethod.writeln("System.out.println(\"Too many arguments\");");
mainMethod.writeln_l("}");
mainMethod.writeln_l("}");
mainMethod.writeln("");
topWriter.writeln_l ("}");
// To extract the source code, call .toString()
System.out.println(topWriter.toString());
package com.samplepackage;
public class MyClass {
String myString = null;
String myString2 = null;
{
// Contents of the indented section (1)
// Contents of the indented section (2)
}
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Require more than one argument");
} else if (args.length == 1) {
doSomething();
} else {
System.out.println("Too many arguments");
}
}
}
It’s highly subjective but generating sourcecode using Java code is much easier to debug imho. There is almost no learning curve, and anyone can debug.
Templates can be easier to read, but they can also be very complex, and involve reflection, something which makes debugging difficult.
With Java based code generation, you control everything, and you understand everything.
A Template (Velocity) is generally better for shallow complexity, programatic code generation (Picocog, Java Poet) is better for deep complexity.
It’s a slippery slope, so Picoclogs ships with no text escaping utility methods.
If you want to escape common text formats, the following library is useful:
Some alternative technologies.
Warning
|
Some of these products are over 16 Kilobytes in size. |
Picocog can come in useful for iterating over annotations on source code, and from those annotations, generating new source code.
Of course, you can do this without Picocog too, but Picocog is tailor-made for this kind of use-case.
See :
-
It helps to create a bunch of deferrals straight away.
-
Always match indents at point of writing. Never wait to match an indent.
-
Always generate distinctive comments in your generated source - these act as anchors for debugging your source code generator.
-
Always use the writeln_lr for '} else if (…) {' lines.
-
Be aware of the escaping requirements of the language for which you are generating sourcecode.
-
Store commonly use strings in member variables and/or constants.
-
If you have access to the API at code generation time for which you are generating source for, make use of the YourApiClass.class.getName() method call. This will make sure that your code generator will automatically cope with class name refactoring.
-
If you are nice to other people, they tend to act nicer towards you.
Email : c.b.ainsley@gmail.com
Follow me : @ainslec