Difference between revision 9 and current revision
No diff available.Table of Contents for Programming Languages: a survey
continued from [1]
http://stackoverflow.com/questions/6839737/are-there-any-c-to-bytecode-compilers
VM instructions (from cint-5.18.00/cint/src/bc_inst.h https://github.com/dawehner/root/blob/master/cint/cint/src/bc_inst.h ). In some cases i've written comments on what i think these instructions might mean, these are only guesses from skimming the source code very quickly, as i couldn't find any documentation (although some instructions had code comments, which i often copied into here):
void LD(G__value* pval);
void LD(int a); # push a copy of the value found at depth a in the data stack onto the data stack
void CL(void); # clear stack pointer; Take a possible breakpoint, and then clear the data stack, and the structure offset stack.
void OP2(int opr); # one of +,-,*,/,%,@,>>,<<,&,| (applied to the data stack)
int CNDJMP(int addr=0); # if TOS == 0, then jump to specified address
int JMP(int addr=0);
void POP(void);
void LD_FUNC(const char* fname,int hash,int paran,void* pfunc, # fname is the string name of a fn; without a longer look, i can't tell if this pushes a reference to a partially applied function onto the data stack, or if it actually calls the function
struct G__ifunc_table_internal* ifunc, int ifn);
void LD_FUNC_BC(struct G__ifunc_table* ifunc,int ifn,int paran,void *pfunc);
void LD_FUNC_VIRTUAL(struct G__ifunc_table* ifunc,int ifn,int paran,void *pfunc);
void RETURN(void); # half
void CAST(int type,int tagnum,int typenum,int reftype);
void CAST(G__TypeInfo& x);
void OP1(int opr); # one of +,-
void LETVVAL(void); # something about assigning and lvalues?
void ADDSTROS(int os); # ? adds 'os' (offset) to a variable named G__store_struct_offset; dunno what this is for
void LETPVAL(void); # ? i dunno, some kind of assignment? looks at the top two(?) items on the stack and pops the top one
void TOPNTR(void); # calls G__val2pointer on the TOS
void NOT(void); # applies NOT to TOS
void BOOL(void); # i think this coerces the TOS to bool
int ISDEFAULTPARA(int addr=0); # if the stack is not empty, then JMP to addr, otherwise continue
void LD_VAR(struct G__var_array* var,int ig15,int paran,int var_type); # i dont understand this at all, but i think this is about loading a variable, which may be an array, into TOS? It first consumes a quantity of 'paran' parameters from the stack, but afaict it then ignores these? Then in the call to G__getvariable, the first parameter, which looks like it is supposed to be a variable name, is always the empty string, which looks like it should cause G_getvariable, in https://github.com/dawehner/root/blob/ed4cde23329c21e940754a56a7195ed082273229/cint/tool/ifdef/get.c , to return the empty string? So i'm confused.
void ST_VAR(struct G__var_array* var,int ig15,int paran,int var_type); #
void LD_MSTR(struct G__var_array* var,int ig15,int paran,int var_type);
void ST_MSTR(struct G__var_array* var,int ig15,int paran,int var_type);
void LD_LVAR(struct G__var_array* var,int ig15,int paran,int var_type);
void ST_LVAR(struct G__var_array* var,int ig15,int paran,int var_type);
void CMP2(int operator2);
void PUSHSTROS(void);
void SETSTROS(void);
void POPSTROS(void);
void SETTEMP(void);
void FREETEMP(void);
void GETRSVD(const char* item);
void REWINDSTACK(int rewind);
int CND1JMP(int addr=0);
private:
void LD_IFUNC(struct G__ifunc_table* p_ifunc,int ifn,int hash,int paran,int funcmatch,int memfunc_flag);
public:
void NEWALLOC(int size,int isclass_array);
void SET_NEWALLOC(int tagnum,int var_type);
void SET_NEWALLOC(const G__TypeInfo& type);
void DELETEFREE(int isarray);
void SWAP();
void BASECONV(int formal_tagnum,int baseoffset);
void STORETEMP(void);
void ALLOCTEMP(int tagnum);
void POPTEMP(int tagnum);
void REORDER(int paran,int ig25);
void LD_THIS(int var_type);
void RTN_FUNC(int isreturn);
void SETMEMFUNCENV(void);
void RECMEMFUNCENV(void);
void ADDALLOCTABLE(void);
void DELALLOCTABLE(void);
void BASEDESTRUCT(int tagnum,int isarray);
void REDECL(struct G__var_array* var,int ig15);
void TOVALUE(G__value* pbuf);
void INIT_REF(struct G__var_array* var,int ig15,int paran,int var_type);
void PUSHCPY(void);
void LETNEWVAL(void);
void SETGVP(int pushpop);
void TOPVALUE(void);
void CTOR_SETGVP(struct G__var_array* var,int ig15,int mode);
int TRY(int first_catchblock=0,int endof_catchblock=0);
void TYPEMATCH(G__value* pbuf);
void ALLOCEXCEPTION(int tagnum);
void DESTROYEXCEPTION(void);
void THROW(void);
void CATCH(void);
void SETARYINDEX(int newauto);
void RESETARYINDEX(int newauto);
void GETARYINDEX(void);void PAUSE();
void NOP(void);
// new instructions void ENTERSCOPE(void); void EXITSCOPE(void); void PUTAUTOOBJ(struct G__var_array* var,int ig15); void CASE(void* x); /* void SETARYCTOR(int num); */ void MEMCPY(); void MEMSETINT(int mode,map<long,long>& x); int JMPIFVIRTUALOBJ(int offset,int addr=0); void VIRTUALADDSTROS(int tagnum,struct G__inheritance* baseclass,int basen); void cancel_VIRTUALADDSTROS();
see also https://github.com/dawehner/root/blob/master/cint/cint/src/bc_exec_asm.h for some code that executes (some of?) these (and other things generated by the optimizer?)
" CIL (for C Intermediate Language) sits in between GENERIC and GIMPLE: control structures are kept and side-effects are hoisted out of expression trees by introducing instructions. It is originally the intermediate language used in safe compiler CCured. While GENERIC is best suited for source code syntactic analyses and GIMPLE for optimization, CIL is ideally suited for source code semantic analyses. As such, it is the frontend of many analyses for C programs, ranging from type-safe compilation to symbolic evaluation and slicing. " -- Yannick Moy. Automatic Modular Static Safety Checking for C Programs. PhD thesis, Université Paris-Sud, January 2009, section 2.1, page 42
"CIL is both lower-level than abstract-syntax trees, by clarifying ambiguous constructs and removing redundant ones, and also higher-level than typical intermediate languages designed for compilation, by maintaining types and a close relationship with the source program. The main advantage of CIL is that it compiles all valid C programs into a few core constructs with a very clean semantics...CIL features a reduced number of syntactic and conceptual forms. For example, all looping constructs are reduced to a single form, all function bodies are given explicit return statements, syntactic sugar like "->" is eliminated and function arguments with array types become pointers. (For an extensive list of how CIL simplifies C programs, see Section 4.) This reduces the number of cases that must be considered when manipulating a C program. CIL also separates type declarations from code and flattens scopes within function bodies. This structures the program in a manner more amenable to rapid analysis and transformation. CIL computes the types of all program expressions, and makes all type promotions and casts explicit. CIL supports all GCC and MSVC extensions except for nested functions and complex numbers. Finally, CIL organizes C’s imperative features into expressions, instructions and statements based on the presence and absence of side-effects and control-flow. Every statement can be annotated with successor and predecessor information. Thus CIL provides an integrated program representation that can be used with routines that require an AST (e.g. type-based analyses and pretty-printers), as well as with routines that require a CFG (e.g., dataflow analyses). CIL also supports even lower-level representations (e.g., three-address code), see Section 8." [2]
Links:
" C is such a simple language, an IR is fairly easy to design. Why is C so "simple"?
Only base types are several varieties of ints and floats
No true arrays — e1[e2] just abbreviates *(e1+e2); all indicies start at zero, there's no bounds checking
All parameters passed by value, and in order
Block structure is very restricted: all functions on same level (no nesting); variables are either truly global or local to a top-level function; implementation drastically simplified because no static links are ever needed and functions can be easily passed as parameters without scoping trouble.
Structure copy is bitwise
Arrays aren't copied element by element
Language is small; nearly everything interesting (like I/O) is in a library Thus, the following tuples are sufficient:
x <- y x <- y[i]
x <- &y x[i] <- y
x <- *y goto L
*x <- y if x relop y goto L
x <- unaryop y param x
x <- y binaryop z call p, n unaryop is one of: +, -, !, ~, ...
binaryop is one of: +, -, *, /, %, &, |, ^, ., &&, ||, ...
relop is one of ==, !=, <, <=, >, >=
x[i] means i memory location x + i
call p,n means call p with n bytes of arguments" -- [3]"Newspeak [96] is at the same level as MSIL, while maintaining some control structures and expression trees. These design decisions notably facilitate source code analyses. It is the intermediate language used in static analyzer Penjili." -- Yannick Moy. Automatic Modular Static Safety Checking for C Programs. PhD thesis, Université Paris-Sud, January 2009, section 2.1, page 42
Links:
"The CompCert? project [22] for building a verified compiler presents a chain of intermediate languages for compilation of C programs, from Clight, a large subset of C, to plain assembly, through Cminor, which is similar to MSIL and Newspeak" -- Yannick Moy. Automatic Modular Static Safety Checking for C Programs. PhD thesis, Université Paris-Sud, January 2009, section 2.1, page 42
Links:
"C0 is a Pascal-like subset of C, similar to MISRA-C. It excludes the features of C that make full verification problematic: pointer arithmetic, unions, pointer casts. It is the intermediate language used in the Verisoft project for pervasive formal verification of computer systems.
Simpl is a very generic Sequential IMperative Programming Language. It offers high-level constructs like closures, pointers to procedures, dynamic method invocation, all above an abstract state that can be instantiated differently depending on the language analyzed. In his PhD? thesis, Schirmer presents a two-fold embedding of a programming language in HOL theorem prover through Simpl. On the one hand, Simpl statements are deeply embedded inside HOL, which allows one to formally verify correctness of the anal- ysis on statements. On the other hand, the abstract state is shallowly embedded inside HOL, for an easier application to program analysis of many different languages. Simpl is used as target language in the C0 compiler of the Verisoft project." -- Yannick Moy. Automatic Modular Static Safety Checking for C Programs. PhD thesis, Université Paris-Sud, January 2009, section 2.1, page 43
http://ruva.rubyforge.org/classes/Ruva/VM/Opcodes.html
http://www.gnu.org/software/guile/docs/master/guile.html/Tree_002dIL.html#Tree_002dIL
a selection, quoted and paraphrased from http://www.gnu.org/software/guile/docs/master/guile.html/Tree_002dIL.html#Tree_002dIL
Tree Intermediate Language (Tree-IL) is a structured intermediate language that is close in expressive power to Scheme. It is an expanded, pre-analyzed Scheme.
Tree-IL is a convenient compilation target from source languages. It can be convenient as a medium for optimization, though CPS is usually better. The strength of Tree-IL is that it does not fix order of evaluation, so it makes some code motion a bit easier.
Tree-IL is “structured” in the sense that its representation is based on records, not S-expressions. This ... ensures that compiling to a lower-level language only requires a limited set of transformations. For example, the Tree-IL type <const> is a record type with two fields, src and exp. Instances of this type are created via make-const. Fields of this type are accessed via the const-src and const-exp procedures. There is also a predicate, const?. See Records, for more information on records.
All Tree-IL types have a src slot, which holds source location information for the expression.
Although Tree-IL objects are represented internally using records, there is also an equivalent S-expression external representation for each kind of Tree-IL. For example, the S-expression representation of #<const src: #f exp: 3> expression would be:
(const 3)
Users may program with this format directly at the REPL:
scheme@(guile-user)> ,language tree-il Happy hacking with Tree Intermediate Language! To switch back, type `,L scheme'. tree-il@(guile-user)> (call (primitive +) (const 32) (const 10)) ⇒ 42
One may create Tree-IL objects from their external representations via calling parse-tree-il, the reader for Tree-IL.
from http://www.gnu.org/software/guile/docs/master/guile.html/The-Scheme-Compiler.html#The-Scheme-Compiler "The job of the Scheme compiler is to expand all macros and all of Scheme to its most primitive expressions. The definition of “primitive expression” is given by the inventory of constructs provided by Tree-IL, the target language of the Scheme compiler: procedure calls, conditionals, lexical references, and so on. This is described more fully in the next section.
The tricky and amusing thing about the Scheme-to-Tree-IL compiler is that it is completely implemented by the macro expander. Since the macro expander has to run over all of the source code already in order to expand macros, it might as well do the analysis at the same time, producing Tree-IL expressions directly. "
todo:
(void): An empty expression. In practice, equivalent to Scheme’s (if #f #f). (const exp): A constant. (primitive name): A reference to a “primitive”. (lexical name gensym): A reference to a lexically-bound variable. The name is the original name of the variable in the source program. gensym is a unique identifier for this variable. (set! (lexical name gensym) exp): Sets a lexically-bound variable. ( @ module name): A reference to a variable in a specific module. (set! (@ mod name) exp): Sets a variable in a specific module. (toplevel name): References a variable from the current procedure’s module. (set! (toplevel name) exp): Sets a variable in the current procedure’s module. (define (toplevel name) exp): Defines a new top-level variable in the current procedure’s module. (if test then else): A conditional. Note that else is not optional. (call proc . args): A procedure call. (primcall name . args): A call to a primitive. Equivalent to (call (primitive name) . args). This construct is often more convenient to generate and analyze than <call>. As part of the compilation process, instances of (call (primitive name) . args) are transformed into primcalls. (seq head tail): A sequence. The semantics is that head is evaluated first, and any resulting values are ignored. Then tail is evaluated, in tail position. (lambda meta body): A closure. meta is an association list of properties for the procedure. body is a single Tree-IL expression of type <lambda-case>. As the <lambda-case> clause can chain to an alternate clause, this makes Tree-IL’s <lambda> have the expressiveness of Scheme’s case-lambda. (lambda-case ((req opt rest kw inits gensyms) body) [alternate]): One clause of a case-lambda. A lambda expression in Scheme is treated as a case-lambda with one clause. req is a list of the procedure’s required arguments, as symbols. opt is a list of the optional arguments, or #f if there are no optional arguments. rest is the name of the rest argument, or #f. kw is a list of the form, (allow-other-keys? (keyword name var) ...), where keyword is the keyword corresponding to the argument named name, and whose corresponding gensym is var. inits are tree-il expressions corresponding to all of the optional and keyword arguments, evaluated to bind variables whose value is not supplied by the procedure caller. Each init expression is evaluated in the lexical context of previously bound variables, from left to right. gensyms is a list of gensyms corresponding to all arguments: first all of the required arguments, then the optional arguments if any, then the rest argument if any, then all of the keyword arguments. body is the body of the clause. If the procedure is called with an appropriate number of arguments, body is evaluated in tail position. Otherwise, if there is an alternate, it should be a <lambda-case> expression, representing the next clause to try. If there is no alternate, a wrong-number-of-arguments error is signaled. (let names gensyms vals exp): Lexical binding, like Scheme’s let. names are the original binding names, gensyms are gensyms corresponding to the names, and vals are Tree-IL expressions for the values. exp is a single Tree-IL expression.
(letrec names gensyms vals exp): A version of <let> that creates recursive bindings, like Scheme’s letrec (prompt escape-only? tag body handler): A dynamic prompt. Instates a prompt named tag, an expression, during the dynamic extent of the execution of body, also an expression. If an abort occurs to this prompt, control will be passed to handler, also an expression, which should be a procedure. The first argument to the handler procedure will be the captured continuation, followed by all of the values passed to the abort. If escape-only? is true, the handler should be a <lambda> with a single <lambda-case> body expression with no optional or keyword arguments, and no alternate, and whose first argument is unreferenced. See Prompts, for more information. (abort tag args tail): An abort to the nearest prompt with the name tag, an expression. args should be a list of expressions to pass to the prompt’s handler, and tail should be an expression that will evaluate to a list of additional arguments. An abort will save the partial continuation, which may later be reinstated, resulting in the <abort> expression evaluating to some number of values.
There are two Tree-IL constructs that are not normally produced by higher-level compilers, but instead are generated during the source-to-source optimization and analysis passes that the Tree-IL compiler does. Users should not generate these expressions directly, unless they feel very clever, as the default analysis pass will generate them as necessary.
(let-values names gensyms exp body): Like Scheme’s receive – binds the values returned by evaluating exp to the lambda-like bindings described by gensyms. That is to say, gensyms may be an improper list. <let-values> is an optimization of a <call> to the primitive, call-with-values. (fix names gensyms vals body): Like <letrec>, but only for vals that are unset lambda expressions. fix is an optimization of letrec (and let).
See http://www.gnu.org/software/guile/docs/master/guile.html/Tree_002dIL.html#Tree_002dIL for some details of some optimizations that Guile performs on Guile Tree IL.
Continuation-passing style (CPS) is Guile’s principal intermediate language, bridging the gap between languages for people and languages for machines. CPS gives a name to every part of a program: every control point, and every intermediate value. This makes it an excellent medium for reasoning about programs, which is the principal job of a compiler.
" Consider the following Scheme expression:
(begin (display "The sum of 32 and 10 is: ") (display 42) (newline))
Let us identify all of the sub-expressions in this expression, annotating them with unique labels:
(begin (display "The sum of 32 and 10 is: ") |k1 k2 k0 (display 42) |k4 k5 k3 (newline)) |k7 k6
Each of these labels identifies a point in a program. One label may be the continuation of another label. For example, the continuation of k7 is k6. This is because after evaluating the value of newline, performed by the expression labelled k7, we continue to apply it in k6.
Which expression has k0 as its continuation? It is either the expression labelled k1 or the expression labelled k2. Scheme does not have a fixed order of evaluation of arguments, though it does guarantee that they are evaluated in some order. Unlike general Scheme, continuation-passing style makes evaluation order explicit. In Guile, this choice is made by the higher-level language compilers.
Let us assume a left-to-right evaluation order. In that case the continuation of k1 is k2, and the continuation of k2 is k0.
With this example established, we are ready to give an example of CPS in Scheme:
(lambda (ktail)
(let ((k1 (lambda ()
(let ((k2 (lambda (proc)
(let ((k0 (lambda (arg0)
(proc k4 arg0))))
(k0 "The sum of 32 and 10 is: ")))))
(k2 display))))
(k4 (lambda _
(let ((k5 (lambda (proc)
(let ((k3 (lambda (arg0)
(proc k7 arg0))))
(k3 42)))))
(k5 display))))
(k7 (lambda _
(let ((k6 (lambda (proc)
(proc ktail))))
(k6 newline)))))
(k1))
Holy code explosion, Batman! What’s with all the lambdas? Indeed, CPS is by nature much more verbose than “direct-style” intermediate languages like Tree-IL. At the same time, CPS is simpler than full Scheme, because it makes things more explicit.
In the original program, the expression labelled k0 is in effect context. Any values it returns are ignored. In Scheme, this fact is implicit. In CPS, we can see it explicitly by noting that its continuation, k4, takes any number of values and ignores them. Compare this to k2, which takes a single value; in this way we can say that k1 is in a “value” context. Likewise k6 is in tail context with respect to the expression as a whole, because its continuation is the tail continuation, ktail. CPS makes these details manifest, and gives them names. "
from http://www.gnu.org/software/guile/docs/master/guile.html/CPS-in-Guile.html#CPS-in-Guile
todo
Guile’s CPS language is composed of terms, expressions, and continuations.
A term can either evaluate an expression and pass the resulting values to some continuation, or it can declare local continuations and contain a sub-term in the scope of those continuations.
$continue k src exp: Evaluate the expression exp and pass the resulting values (if any) to the continuation labelled k. The source information associated with the expression may be found in src, which is either an alist as in source-properties or is #f if there is no associated source.
$letk conts body: Bind conts, a list of continuations ($cont instances), in the scope of the sub-term body. The continuations are mutually recursive.
Additionally, the early stages of CPS allow for a set of mutually recursive functions to be declared as a term. This $letrec type is like Tree-IL’s <fix>. The contification pass will attempt to transform the functions declared in a $letrec into local continuations. Any remaining functions are later lowered to $fun expressions. $letrec names syms funs body: Declare the mutually recursive set of functions denoted by names, syms, and funs within the sub-term body. names and syms are lists of symbols, and funs is a list of $fun values. syms are globally unique.
Here is an inventory of the kinds of expressions in Guile’s CPS language. Recall that all expressions are wrapped in a $continue term which specifies their continuation.
$void: Continue with the unspecified value.
$const val: Continue with the constant value val.
$prim name: Continue with the procedure that implements the primitive operation named by name.
$fun src meta free body: Continue with a procedure. src identifies the source information for the procedure declaration, and meta is the metadata alist as described above in Tree-IL’s <lambda>. free is a list of free variables accessed by the procedure. Early CPS uses an empty list for free; only after closure conversion is it correctly populated. Finally, body is the $kentry $cont of the procedure entry.
$call proc args: Call proc with the arguments args, and pass all values to the continuation. proc and the elements of the args list should all be variable names. The continuation identified by the term’s k should be a $kreceive or a $ktail instance.
$callk label proc args: $callk is for the case where the call target is known to be in the same compilation unit. label should be some continuation label, though it need not be in scope. In this case the proc is simply an additional argument, since it is not used to determine the call target at run-time.
$primcall name args: Perform the primitive operation identified by name, a well-known symbol, passing it the arguments args, and pass all resulting values to the continuation. The set of available primitives includes all primitives known to Tree-IL and then some more; see the source code for details.
$values args: Pass the values named by the list args to the continuation.
$prompt escape? tag handler: Push a prompt on the stack identified by the variable name tag, which may be escape-only if escape? is true, and continue with zero values. If the body aborts to this prompt, control will proceed at the continuation labelled handler, which should be a $kreceive continuation. Prompts are later popped by pop-prompt primcalls.
The remaining element of the CPS language in Guile is the continuation. In CPS, all continuations have unique labels. Since this aspect is common to all continuation types, all continuations are contained in a $cont instance:
CPS $cont k cont: Declare a continuation labelled k. All references to the continuation will use this label.
$kargs names syms body: The most common kind of continuation binds some number of values, and then evaluates a sub-term. $kargs is this kind of simple lambda. Bind the incoming values to the variables syms, with original names names, and then evaluate the sub-term body.
Variable names (the names in the syms of a $kargs) should be globally unique, and also disjoint from continuation labels. To bind a value to a variable and then evaluate some term, you would continue with the value to a $kargs that declares one variable. The bound value would then be available for use within the body of the $kargs.
$kif kt kf: Receive one value. If it is true for the purposes of Scheme, branch to the continuation labelled kt, passing no values; otherwise, branch to kf. For internal reasons, only certain terms may continue to a $kif. Compiling $kif avoids allocating space for the test variable, so it needs to be preceded by expressions that can test-and-branch without temporary values. In practice this condition is true for $primcalls to null?, =, and similar primitives that have corresponding br-if-foo VM operations; see the source code for full details. When in doubt, bind the test expression to a variable, and continue to the $kif with a $values expression. The optimizer should elide the $values if it is not needed. Calls out to other functions need to be wrapped in a $kreceive continuation in order to adapt the returned values to their uses in the calling function, if any.
$kreceive arity k: Receive values on the stack. Parse them according to arity, and then proceed with the parsed values to the $kargs continuation labelled k. As a limitation specific to $kreceive, arity may only contain required and rest arguments. $arity is a helper data structure used by $kreceive and also by $kclause, described below.
CPS Data: $arity req opt rest kw allow-other-keys?: A data type declaring an arity. req and opt are lists of source names of required and optional arguments, respectively. rest is either the source name of the rest variable, or #f if this arity does not accept additional values. kw is a list of the form ((keyword name var) ...), describing the keyword arguments. allow-other-keys? is true if other keyword arguments are allowed and false otherwise. Note that all of these names with the exception of the vars in the kw list are source names, not unique variable names.
Additionally, there are three specific kinds of continuations that can only be declared at function entries.
$kentry self tail clauses: Declare a function entry. self is a variable bound to the procedure being called, and which may be used for self-references. tail declares the $cont wrapping the $ktail for this function, corresponding to the function’s tail continuation. clauses is a list of $kclause $cont instances.
$ktail: A tail continuation.
$kclause arity cont: A clause of a function with a given arity. Applications of a function with a compatible set of actual arguments will continue to cont, a $kargs $cont instance representing the clause body.
See http://www.gnu.org/software/guile/docs/master/guile.html/Compiling-CPS.html#Compiling-CPS for some details of some optimizations that Guile performs on Guile CPS.
http://www.gnu.org/software/guile/docs/master/guile.html/Instruction-Set.html#Instruction-Set
http://www.gnu.org/software/guile/docs/master/guile.html/Instruction-Set.html#Instruction-Set
todo
See also Guile section of proj-plbook-plChLispLangs, proj-plbook-plChImplementationCaseStudies, proj-plbook-plChMiscIntermedLangs, proj-plbook-plChTargetsImpl