This chapter defines the syntax and semantics of the CamlTk description language, as well as some technical details of the protocol.
A source file is simply a collection of entries, each entry describing an element of the interface. The tkcompiler provided in this distribution takes the source file and produces Caml Light modules forming the interface library.
The syntax for describing translations respecting tokens is called a template. A template is a list of tokens, each token being
A type is given by a set of value constructors, as in Caml Light. For each constructor, one must give the Caml Light name, and its template.
type State { Normal ["normal"] Active ["active"] Disabled ["disabled"] }defines the Caml Light type
type State = Disabled | Active | Normal ;;and the conversion function
let cCAMLtoTKState = function Disabled -> TkToken"disabled" | Active -> TkToken"active" | Normal -> TkToken"normal" ;;
The parser can be generated only in restricted cases: zeroary constructors, and at most one anonymous int constructor and one anonymous string constructor (anonymous means that the template is composed only with this token). Otherwise, the compiler will produce a warning message, and you have to write the parser yourself.
If a type requires subtyping, then one should not declare the type itself, but instead define each of its subtypes. The compiler will consider the whole set of constructors for the definition of the type. Note also that the definition of a constructor may be omitted if it has already been declared.
subtype option(standard) { ActiveBackground ["-activebackground"; Color] ActiveBorderWidth ["-activeborderwidth"; Units] ActiveForeground ["-activeforeground"; Color] Anchor ["-anchor"; Anchor] ... } subtype option (bitmap) { Anchor Background ... }defines two subtypes (standard and bitmap) of the type option.
Subtypes may be referred to (in argument types of functions) with the same syntax: type(subtype).
function () delete [widget(listbox); "delete"; Index(listbox); Index(listbox)]
Options descriptions follow the same syntax as value constructors in type or subtype declarations, except for the presence of the option keyword. Actually, the options declared inside a widget description form a subtype (with the name of the class) of the option type. option is not really hardwired in the compiler, in that it obeys the same rules for subtyping as user-defined types. However the option type is implicitly used when producing the widget creation functions.
Function declarations are formed of
The Caml Light function will have as many arguments as types appearing in the template, in the same order.
widget message { option Anchor option Background option Borderwidth option Cursor option Font option Foreground option PadX option PadY option Relief option Text option TextVariable option Width option Aspect ["-aspect"; int] option Justify ["-justify"; Justification] function () configure [widget(message); "configure"; option(message) list] function (string) configure_get [widget(message); "configure"] }Note that the first options are given implicitly, since they belong to ``standard'' options, defined elsewhere. The two others are given in their full form.
Each widget class is compiled into a separate module (bearing the name of the widget class). Besides the functions described in the entry, the compiler produces two creation functions
value create : Widget -> option list -> Widget ;; value create_named : Widget -> string -> option list -> Widget ;;with, in fact, run-time verification of options, who must belong to the subtype option(widgetname). The first argument of create is the parent of the created widget. The second argument of create_named is the new path component of the widget.
module selection { function () clear ["selection"; "clear"; widget] function (string) get ["selection"; "get"] }
type Units external # builtin_GetPixelFor each builtin type foo, one should provide a cCAMLtoTKfoo function, of type foo -> protocol__TkArgs, respecting the tokenization. If data of this type is to be returned by a function, one should also write a parser cTKtoCAMLfoo. For example, the file builtin_GetPixel.ml contains the Units type, used extensively in Tk for specifying distances or coordinates. It requires both a pretty-printer and a parser (which emulates the tkGetPixel function in the Tk library).
$ tkcompiler filename(the file compiled defaults to Widgets.src if filename is omitted). The compiler requires the existence of a lib subdirectory, where it will produce tkgen.ml, modules, and various Caml Light files corresponding to entries in the interface description source.
The compiler will report the following errors
Producing the library requires the presence of several files in the lib directory. For more details, check the Makefile in lib.
value default_toplevel_widget : Widget (* The default toplevel widget is ".", of type "toplevel" *) ;; value widget_atom : Widget -> string -> Widget ;;
And some functions used internally are
value widget_name : Widget -> string (* Return the name (tk "path") of a widget *) and widget_class : Widget-> string (* Returns the class of a widget *) ;; value new_widget_atom : string -> Widget -> Widget and new_named_widget : string -> Widget -> string -> Widget (* Abstract creation functions *) ;;
type Widget = Untyped of string | Typed of string * string ;;We also maintain a table associating widget paths to Widgets. The reason for this table is that we may have to dynamically retype a widget path returned by a Tcl function.
Tcl has been extended with a new command camlcb to call Caml Light (or more precisely a given Caml Light function). camlcb relies on the callback primitive of the Caml Light runtime. Currently, the Caml Light runtime does not allow storage of Caml Light values in C global variables, because global variables cannot be roots for the garbage collector. The implementation of camlcb uses the same trick as signal handling.
In order to avoid space leaks due to storing callback closures in the table, we implement a memo-mechanism associating callbacks to widgets. Except for class wide callbacks, each callback is defined for a given widget. When this widget is destroyed, the callback cannot be invoked any more, and we can remove it from the table. We thus provide a general binding for all widgets, on event Destroy, that will remove all callbacks associated to a widget being destroyed. If a user bindings masks the default binding for the Destroy event, its callback should call the function protocol__remove_callbacks with argument the widget being destroyed.