parenthetcl/quick-reference.txt _______________________________________________________________________________ * Requires Tcl 8.4 (or later?) You will need the "tcl-devel" RPM (or equivalent) installed. ActiveTcl distributions work fine. Only tested on Linux on i386 32-bit model (specifically, Fedora Core) using gcc. To build: sh configure.sh --prefix=/tmp make make test make install * Compile with p7c. Your source goes in a .p7 file. Options to gcc go at the end of the command: p7c mysource.p7 -g -O2 -march=athlon ===> produces mysource.so To enable ParentheTcl's own debugging, use -g in front: p7c -g mysource.p7 -g -O2 -march=athlon ===> produces mysource_g.so To use from Tcl, load ./mysource.so or load ./mysource_g.so Your ParentheTcl p7proc functions will appear as commands in Tcl's global namespace. NOTE: p7c actually evaluates your .p7 file as a Tcl script, in a "safe subinterpreter", with just four added Tcl commands: p7class classname list_of_data_members p7proc (type)name list_of_parameters function_body p7global (type)varname p7source filename.p7i ?key value ...? Feel free to "metaprogram" by writing Tcl code to call these commands. Example: tests/fancyclass.p7i * Write ParentheTcl functions with p7proc: p7proc (int)twice { (int)x } { set (int)y ( 2 * x ) ; return $y } The name of the function being defined is "twice". Notice the return type of the function "(int)" and the type of the variables "(int)x" and "(int)y" are written in parentheses in front of the variable declaration (with no white space). * ParentheTcl functions look like Tcl procs -- with some limitations -- with all variables typed (but (tcl) is Tcl's flexible string type) -- with some syntax extensions, usually marked by parentheses: -- () always around type names -- () around parenthesized expressions -- () for indexing vectors, (tcl) lists, (dict) objects, and members of objects * Parenthesized expressions Use parentheses around arithmetic expressions, rather than an "expr" command. set n ( 2 * n ) --- rather than set n [expr { 2 * n }] Favor C-like or Java-like syntax within parenthesized expressions: -- You can omit the $ before variable names. -- You can use [ ] instead of ( ) to index vectors -- You can use . to access data members of classes -- You can use character constants like 'a' (of type (int)) You may still embed Tcl or ParentheTcl commands inside []: set (int)xLenPlus1 ( 1 + [len aVectorX] ) set (int)yLenPlus1 ( 1 + [::length aTclListY] ) * Strongly typed variables, functions, expressions: Data type names are always written inside parens (with no white space) * Primative Types (int) -- signed int in C (double) -- double floating-point number in C -- not well tested. (byt) -- unsigned 8-bit in C, for byte arrays (byt*) (utf) -- signed 8-bit in C, for UTF-8 char arrays (utf*) (uni) -- unsigned 16-bit in C, for unicode char arrays (uni*), like Java char (tcl) -- the string type of Tcl, like Tcl_Obj* (dict) -- a hashtable mapping (tcl) strings to (tcl) strings * vectors -- Add * to element type to create typename -- Created by the "new" command, with type and size: set (Foo*)temp [ new (Foo*) 32 ] -- Reference counted and deleted automatically -- Size is fixed at creation time -- Ask size with "len" if ( [len $temp] == 32 ) { ... } -- Indexed by "($index)" (or in parenthesized expressions by "[index]") set temp($i) $temp($i+1) -- Convert to and from (tcl) by COPYING: -- (byt*) (utf*) and (uni*) are special, converting naturally into Tcl strings -- all other vector types convert to and from Tcl Lists -- zero-length vectors are also considered "null" -- Example code: set (int*)v [new (int*) 100] set v(20) ( v(10) + v[11] ) -- use either () or [] when in parentheses if ( [len v] != 100 ) { error "oh no" } set (tcl)q $v -- copies to a list if ( [::llength $q] != 100 ) { error "oh no" } lappend q 42 set v $q -- copies list to a new vector if ( [len v] != 101 ) { error "oh no" } -- Using negative or out-of-bounds indices causes a runtime error (as in Java) * special character vectors -- (byt*) converts by copying to & from Tcl's internal binary byte array type -- (utf*) converts by copying to & from Tcl's internal UTF-8 unicode string type -- (uni*) converts by copying to & from Tcl's internal 16-bit unicode string type * classes -- Declared only in global scope -- Forward and circular type references are no problem -- Contain only public data members -- No inheritance -- Instances created with "[new (Classname)]" -- Reference counted and deleted automatically -- Convert to (tcl) via a special Tcl_Object type that contains reference to the parenthetcl class instance -- That is, object identity is preserved even in and out of Tcl (unlike vectors, which are copied in and out of Tcl) -- However class instances are opaque to Tcl: Tcl can only copy them around. -- Runtime method binding is possible, based on type of first argument to a p7 proc -- Use Tcl's "array variable" syntax to access members of a class instance: p7class IntLink { (int)left (IntLink)right } ... set (IntLink)p [ new (IntLink) ] set p(left) 100 set p(right) null -- In parenthesized expressions, and after "set", "incr", "append", or "lappend", you may also use "." notation (as in Java): set p.left ( 2 * p.left * p.right.left ) set p.right ( p.right ? p.right : [new (IntLink)] ) -- Dereferencing a null instance pointer causes a runtime error (as in Java) * strings -- use the (tcl) type -- literals either in "dquotes" or {braces}, as in Tcl -- character literals 'x' are type (int) -- command and variable substitution work, as in Tcl: set (tcl)a I${verb}I$linkingVerb-an-[what I $wishFor] set (tcl)b "Please sing: I${verb}I$linkingVerb-an-[what I $wishFor]" -- BUGS: These don't work: -- multi-line string literals -- backslash escapes in literals -- control characters in literals -- In fact, C-injection bugs probably present (but only at compile-time!) * null -- The literal expression "null" (as in Java) -- references to class instances may be null (as in Java) -- zero-length vectors are also considered == null -- empty (tcl) strings are also considered == null -- numeric types (int) (byt) (utf) (uni) cannot be null * (void) -- functions and expressions that return no value are said to return (void) * (dict) -- NEEDS TO BE DOCUMENTED -- See "tests/dict.p7" for current functionality -- BUG: if you try to get a key that does not exist, it makes the key -- Like Vectors, (dict) passes by REFERENCE to (dict), but by VALUE to (tcl), expanding into a { Key Value... } list, like "Tcl 8.5 dict" * boolean values -- any data type may be used in boolean expressions ( for "if", "while", "&&", "||", and "!" -- with numeric types, 0 is false -- with vectors, length 0 (a.k.a. null) is false -- with class instances, null is false -- with (tcl), Tcl's rules apply -- conditional operators return 0 or 1 * relational operators -- These compare numbers or identity of objects or vectors: == != < <= > >= They cannot be used on (tcl) values. -- These compare (tcl) strings: eq ne lt le gt ge Their operands are automatically converted to (tcl). Tcl_UtfNcmp() is used to do the comparisons. * cast -- use "[set (type) value]" to cast "value" to (type) * pass by reference -- within Parenthetcl p7procs, vectors and dict and class instances passed by reference -- Between Parenthetcl and Tcl, only class instances passed by reference -- As in Tcl, the (tcl) types is never passed by reference, always by value. * convert -- All parenthetcl data types convert automatically to and from (tcl) when needed. -- They do that especially when data goes between Tcl and Parenthetcl commands. -- Numeric types convert automatically to each other, without explicit casts, even when data is lost due to conversion to a shorter data type, or changing between signed & unsigned types. * runtime error -- "error" and "catch" work as in Tcl -- Tcl's error throwing and catching mechanims are used, so ParentheTcl and Tcl can throw and catch each other's errors * Namespaces are not supported. All parenthetcl commands appear in Tcl in the global namespace. * Modules are not supported. Limitation: You may only load one parenthetcl .so binary in any Tcl process. * Floating point numbers are not supported. Workaround: Drop into Tcl: set (tcl)pi 3.1415926536 set (tcl)twoPi ::eval { 2.0 * $pi } * Use >>> for unsigned shift right, and >> for signed shift right (as in Java). * Statements new (ClassType) -- constructs new object new (elementType*) size -- must provide size for vectors set (type)name value -- declares variable 'name', initializes to value set (type)name -- declares variable 'name', initializes to null or 0 new (ClassType)name -- shortcut for set (ClassType)name [new (ClassType)] new (elementType*)name size -- shortcut for set (elementType*)name [new (elementType*) size] set name value -- sets variable set name -- gets variable set (type) value -- explicitly casts value to (type) unset d($key) -- Limitation: d must be a local variable of type (dict) -- cannot be member of "this" or anything else. len $vector -- returns length of $vector len $dict -- returns count of $dict keys names $dict -- returns (tcl) list of $dict keys -- BUT probably should be (tcl*) incr var amount -- var must be type (int) append var string -- var must be type (tcl) lappend var element -- var must be type (tcl) return value break continue error "what" catch { commands } var if ( cond ) { ... } elseif ( cond2 ) { ... } else { ... } while ( cond ) { ... } for {init} (cond) {incr} {body} loop (int)var numberOfPasses { body } -- shortcut for for {set (int)var 0} ($var < $numberOfPasses) {incr var} { body } k -- evaluates to null k arg1 -- evaluates to arg1 (this is the "k combinator") k arg1 arg2... -- evaluates to arg1 (after evaluating all args in order) command ?arg...? -- call Parenthetcl p7proc with args ::command ?arg...? -- call ::command in Tcl with args lambda (type)verb { (type)parameters... } { body } -- see below * ParentheTcl p7proc produces Tcl commands, but not Tcl procs. That is, ParentheTcl p7procs do not produce new local name scopes for the "uplevel", "upvar", and "info level" commands. Beware! If you say p7proc SmashX {} { ::set X 99 } and call SmashX from a Tcl proc, it will alter X in the Tcl proc's scope. * Overloading p7proc functions on number of parameters p7proc functions with different number of parameters are completely separate. You may overload any way you like as long as the number of parameters is different. * Overloding p7proc functions on compile-time type of first parameters When number of args is the same (greater than zero), -- type of first parameter must be different -- types of other parameters don't matter * Runtime dispatch on run-time type of first argument when its compile-time type is (tcl) -- This is hard to explain: p7proc functions with the same name, same number of parameters, same return type, and same parameter types (except for first parameter type, which is either (tcl) or a p7class type) form a runtime dispatch group. If it is called with a compile-time type of (tcl), runtime dispatch will occur, choosing the one whose compile-time object type of first argument matches the object type of the value at runtime, if one exists, and falling back to the function taking (tcl), if it exist, if a object type match is not made. This runtime dispatch works whether calling from Tcl or ParentheTcl. -- See tests/dispatch.{p7,test} for example. * p7global (type)GlobalVarName ?{initializationFunctionBody}? Visible to all p7procs, like global variables in C. If initializationFunctionBody is provided, it is treated as the body of a p7proc returning the same type as the GlobalVarName. These initilizers are executed in order appearance. Not visible to Tcl. * p7source filename.p7i ?key value ...? Not only for including definitions, it also provides a generic mechanism. See tests/generic*.p7i Calling a second time with same filename and keys and values does nothing -- so includes can p7source includes safely. FUTURE: p7source filename.p7 imports a module, if .p7 instead of .p7i * _INIT Functions Any p7proc whose name ends in _INIT, takes no arguments, and returns void, will be called when the module is loaded. * Lambda expressions (You can skip this section if you like.) lambda (type)verb { (type)parameters... } { body } -- produce a result, e.g. $functionObject, that can be called later, from P7 or from Tcl, preceded by the same verb name and followed by arguments to the given parameters, like this: verb $function parameters... It is the presence of "verb" in both the lambda specification and application that is unusual. However it gives P7 a way to specify the types of the parameters and result value, for execution speed. Conventionally, the verb should be "call" when all parameters are type (tcl) and so is the return value. Also it makes Tcl happy: if $function were the first word, it would shimmer into a Tcl Command if used in Tcl, and would therefore disappear (due to reference counting) before it could even be used. END.