= Functions = [[TracNav]] [[TOC(inline)]] == Calling Functions == Within a Data Manipulation Language (DML) block, user-defined and built-in functions may be called. The pan language uses a typical syntax for calling a function: {{{ identifier(arg1, arg2, ...); }}} where 'identifier' is a valid pan identifier and the arguments are passed to the function as a comma-separated list. The arguments may be expressions, in which case those expressions will be completely evaluated before calling the function. All functions return a value, although it might be 'undef'. == User-Defined Functions == The pan language permits user-defined functions. These functions are essentially a DML block bound to an identifier. Only one DML block may be assigned to a given identifier. Attempts to redefine an existing function will cause the execution to be aborted. The syntax for defining a function is: {{{ function identifier = DML; }}} where identifier is a valid pan identifier and DML is the block to bind to this identifier. When the function is called, the DML will have the variables 'ARGC' and 'ARGV' defined. (The deprecated names 'argc' and 'argv' will also be defined.) The variable 'ARGC' contains the number of arguments passed to the function; 'ARGV' is a list containing the values of the arguments. The pan language does no automatic checking of the number or types of arguments. The DML block that defines the function must make all of these checks explicitly and use the error() function to emit an informative message in case of an error. Recursive calls to a function are permitted. However, the call depth is limited (by an option when the compiler is invoked) to avoid infinite recursion. Typically, the maximum is a small number like 10. The following example defines a function that checks if the number of arguments is even and are all numbers: {{{ function even_numbers = { if (ARGC%2 != 0) { error('number of arguments must be even'); }; foreach (k, v, ARGV) { if (! is_number(v)) { error('non-numeric argument found'); }; }; }; }}} == Built-In Functions == === base64_decode( arg:string ) : string === This function decodes the given string that must be Base64 (defined in RFC 2045) encoded. {{{ '/test' = '[' + base64_decode("aGVsbG8gd29ybGQ=") + ']'; # will be the string '[hello world]' }}} === base64_encode( arg:string ) : string === This function encodes the given string into its Base64 (defined in RFC 2045) form. === clone( arg:element ) : element === This function returns a clone (copy) of the given argument. === create( name:string, ... ) : nlist === This function returns the named list which is the result of the execution of the structure template identified by the given name; the optional extra parameters must be pairs of key and value and will add or modify the result accordingly (a bit like with nlist). {{{ # description of a CD mount entry (but the device is unknown) structure template mount_cdrom; "device" = undef; "path" = "/mnt/cdrom"; "type" = "iso9660"; "options" = list("noauto", "owner", "ro"); # our first mount entry is the CD coming from hdc "/system/mounts/0" = create("mount_cdrom", "device", "hdc"); # this is exactly equivalent to the following two lines "/system/mounts/0" = create("mount_cdrom"); "/system/mounts/0/device" = "hdc"; }}} === debug( arg:string ) : string === This function prints the given message on stdout when debugging is enabled with the DEBUG USER flag. The function returns the message. {{{ # print the value of x if it is positive if (x > 0) debug("x is positive: " + to_string(x)); }}} === delete( arg:variable ) : undef === This function deletes the element identified by the ”variable expression” (i.e. variable name with optional subscript such as foo or foo![123] or foo![123][”abc”]). {{{ # the following will put the list ("a", "c") at path "/x" "/x" = { x = list("a", "b", "c"); delete(x[1]); return(x); }; }}} === error( msg:string ) === This function prints the given message on stderr and aborts the compilation. {{{ # user function requiring one long argument function foo = { if (argc != 1) error("foo(): wrong number of arguments: " + to_string(argc)); if (!is_long(argv[0])) error("foo(): argument is not a long"); # normal processing... }; }}} === escape( arg:string ) : string === This function escape non alphanumeric characters in the given string so that it can be used inside paths, for instance as an named list key. {{{ "/test" = escape("1+1"); # will be the string "1_2b1" }}} === exists( arg:variable ) : boolean === This function checks if the ”variable expression” (see delete function) corresponds to an existing element. ''Note that this function processes its argument in a non-standard way. The function only checks to see if the element identified by the variable expression exists. It does not check the actual value of the element. Consequently, this function will return true for any element including 'null' and 'undef' values.'' === exists( path:string ) : boolean === This function checks if the path corresponds to an existing element. ''Note that if the argument is a variable expression it will be interpreted as a call to exists( arg:variable) and will not read the value of the variable.'' For example, the following DML code: {{{ v = '/some/absolute/path'; r = exists(v); }}} always gives the value 'true' to the variable r. If you want to test the path, do something like the following instead: {{{ v = '/some/absolute/path'; r = exists(v+''); }}} Here the value r will depend on whether the path '/some/absolute/path' actually exists. === exists( template:string ) : boolean === This function checks if the given template exists. ''Note that this function processes its arguments in a non-standard way. The argument must be a bare template name; the template must not be specified by a string.'' === first( arg:resource, key:identifier, value:identifier ) : boolean === This function resets the iterator associated with arg so that it points to its first child element; if there is one (i.e. if the resource is not empty), sets the variable key to the ”key” of this element (i.e. a long if arg is a list or a string if it is a nlist) and the variable value to the child element itself and return true; if key or value is undef, don’t assign it; if there is no such child element, simply return false. {{{ # compute the sum of the elements inside numlist numlist = list(1, 2, 4, 8); sum = 0; ok = first(numlist, k, v); while (ok) { sum = sum + v; ok = next(numlist, k, v); }; # sum will be 15 # put the list of all the keys of table inside keys table = nlist("a", 1, "b", 2, "c", 3); keys = list(); ok = first(table, k, v); while (ok) { keys[length(keys)] = k; ok = next(table, k, v); }; # keys will be ("a", "b", "c") }}} === hash( ... ) : nlist === This is a synonym for the {{{nlist}}} function. === index ( sub:string, arg:string, [ start:long ] ) : long === This function searches for the given substring inside the given string and returns its position from the beginning of the string or -1 if not found; if the third argument is given, starts only from that position. {{{ "/s1" = index("foo", "abcfoodefoobar"); # 3 "/s2" = index("f0o", "abcfoodefoobar"); # -1 "/s3" = index("foo", "abcfoodefoobar", 4); # 8 }}} === index ( sub:property, arg:list, [ start:long ] ) : long === This function searches for the given property inside the given list of properties and returns its position or -1 if not found; if the third argument is given, starts only from that position; it is an error if sub and arg’s children are not of the same type. {{{ # search in a list of strings "/l1" = index("foo", list("Foo", "FOO", "foo", "bar")); # will be 2 # search in a list of longs "/l2" = index(1, list(3, 1, 4, 1, 6), 2); # will be 3 }}} === index ( sub:property, arg:nlist, [ start:long ] ) : string === This function searches for the given property inside the given named list of properties and returns its name or the empty string if not found; if the third argument is given, skips that many matching children; it is an error if sub and arg’s children are not of the same type. {{{ # simple color table "/table" = nlist("red", 0xf00, "green", 0x0f0, "blue", 0x00f); "/name1" = index(0x0f0, value("/table")); # will be the string "green" "/name2" = index(0x0f0, value("/table"), 1); # will be the string "" }}} === index ( sub:nlist, arg:list, [ start:long ] ) : long === This function searches for the given named list inside the given list of named lists and returns its position or -1 if not found; the comparison is done by comparing all the children of sub, these children must all be properties; if the third argument is given, starts only from that position; it is an error if sub and arg’s children are not of the same type or if their common children don’t have the same type. {{{ # search a record in a list of records "/l1" = index(nlist("key", "foo"), list(nlist("key", "bar", "val", 101), nlist("key", "foo", "val", 102))); # will be 1 (i.e. the second nlist) }}} === index ( sub:nlist, arg:nlist, [ start:long ] ) : string === This function searches for the given named list inside the given named list of named lists and returns its name or the empty string if not found; if the third argument is given, skips that many matching children; it is an error if sub and arg’s children are not of the same type or if their common children don’t have the same type. === is_boolean( arg:element ) : boolean === This function checks if the given argument is a boolean. === is_defined( arg:element ) : boolean === This function checks if the given argument is defined (i.e. is anything but undef or null). === is_double( arg:element ) : boolean === This function checks if the given argument is a double. === is_hash( arg:element ) : boolean === This function checks if the given argument is a nlist. Synonym for the {{{is_nlist}}} function. === is_list( arg:element ) : boolean === This function checks if the given argument is a list. === is_long( arg:element ) : boolean === This function checks if the given argument is a long. === is_nlist( arg:element ) : boolean === This function checks if the given argument is a nlist. === is_null( arg:element ) : boolean === This function checks if the given argument has a null value (i.e. is anything but null). === is_number( arg:element ) : boolean === Returns true if the argument is either a long or double property. === is_property( arg:element ) : boolean === This function checks if the given argument is a property. === is_resource( arg:element ) : boolean === This function checks if the given argument is a resource. === is_string( arg:element ) : boolean === This function checks if the given argument is a string. === key( arg:nlist, index:long ) : string === This function returns the name of the child identified by its index, this can be used to iterate through all the children of a named list. {{{ "/table" = nlist("red", 0xf00, "green", 0x0f0, "blue", 0x00f); "/keys" = { tbl = value("/table"); res = ""; len = length(tbl); idx = 0; while (idx < len) { res = res + key(tbl, idx) + " "; idx = idx + 1; }; if (length(res) > 0) splice(res, -1, 1); return(res); }; # /keys will be the string "red green blue" }}} === length( arg:string ) : long === This function returns the length of the given string. === length( arg:resource ) : long === This function returns the number of children of the given resource. === list( ... ) : list === This function returns a list made of its arguments. {{{ # the list of mount entries is empty "/system/mounts = list(); # we define two DNS servers "/system/dns/servers" = list("137.138.16.5", "137.138.17.5"); # this machine has only one CPU "/hardware/cpus" = list(create("cpu_intel_p3_850")); }}} === match( arg:string, regexp:string ) : boolean === This function checks if the given string matches the regular expression. {{{ # device_t is a string that can only be "disk", "cd" or "net" type device_t = string with match(self, ’ˆ(disk|cd|net)$’); }}} === matches( arg:string, regexp:string ) : list === This function matches the given string against the regular expression and returns the list of captured substrings, the first one being the complete matched string (i.e. $& in Perl). {{{ # IPv4 address in dotted number notation type ipv4 = string with { result = matches(self, ’ˆ(\d+)\.(\d+)\.(\d+)\.(\d+)$’); if (length(result) == 0) return("bad string"); i = 1; while (i <= 4) { x = to_long(result[i]); if (x > 255) return("chunk " + to_string(i) + " too big: " + result[i]); i = i + 1; }; return(true); }; }}} === merge( ... ) : resource === This function returns the resource which is the merge of its arguments which must be of the same type: either all lists or all named lists; if several named lists have children of the same name, an error occurs. {{{ "/x" = list("a", "b", "c"); "/y" = list("d", "e"); "/z" = merge (value("/x"), value("/y")); # /z will contain the list "a", "b", "c", "d", "e" }}} === next( arg:resource, key:identifier, value:identifier ) : boolean === This function increments the iterator associated with arg so that it points to the next child element and then behaves like first. === nlist( ... ) : nlist === This function returns a named list made of its arguments which must be pairs of key (i.e. child’s name, a string) and value (i.e. child’s value, an element). {{{ # hda1 is our root filesystem "/system/mounts/0" = nlist("type", "ext2", "path", "/", "device", "hda1"); # hda2 is our /var filesystem "/system/mounts/1" = nlist( "type", "ext2", "path", "/var", "device", "hda2", ); }}} === return( arg:element ) : element === This function interrupts the processing of the current DML block and returns from it with the given value, this is often used in user defined functions. {{{ function facto = { if (argv[0] < 2) return(1); return(argv[0] * facto(argv[0] - 1)); }; }}} === splice ( arg:string, start:long, length:long, [ new:string ] ) : string === This function deletes the substring identified by start and length (see substr’s documentation for de- tails) and, if a fourth argument is given, replaces it with new. {{{ "/s1" = splice("abcde", 2, 0, "12"); # ab12cde "/s2" = splice("abcde", -2, 1); # abce "/s3" = splice("abcde", 2, 2, "XXX"); # abXXXe }}} === splice ( arg:list, start:long, length:long, [ new:list] ) : list === This function deletes the children of the given list identified by start and length (see substr’s docu- mentation for details) and, if a fourth argument is given, replaces them with the contents of new. {{{ "/l1" = splice(list("a","b","c","d","e"), 2, 0, list(1,2)); # will be the list "a", "b", 1, 2, "c", "d", "e" "/l2" = splice(list("a","b","c","d","e"), -2, 1); # will be the list "a", "b", "c", "e" "/l3" = splice(list("a","b","c","d","e"), 2, 2, list("XXX")); # will be the list "a", "b", "XXX", "e" }}} === substr ( arg:string, start:long, [ length:long ] ) : string === This function returns the part of the given string characterised by its start position (starting from 0) and its length (if omitted, returns everything to the end of the string); if start is negative, starts that far from the end of the string; if length is negative, leaves that many characters off the end of the string. {{{ "/s1" = substr("abcdef", 2); # cdef "/s2" = substr("abcdef", 1, 1); # b "/s3" = substr("abcdef", 1, -1); # bcde "/s4" = substr("abcdef", -4); # cdef "/s5" = substr("abcdef", -4, 1); # c "/s6" = substr("abcdef", -4, -1); # cde }}} === to_boolean( arg:property ) : boolean === This function converts the given property into a boolean; 0 and the empty strings are considered as false, everything else as true. === to_double( arg:property ) : double === This function converts the given property into a double; it is an error if the given string does not represent a double. === to_long( arg:property ) : long === This function converts the given property into a long; it is an error if the given string does not represent a long; if the argument is a double, it will be rounded to the nearest long. === to_string( arg:property ) : string === This function converts the given property into a string. === traceback( arg:string ) : string === Prints the argument and a traceback from the current execution point to the standard error. Value returned is the argument. This will throw an evaluation error (along with a traceback) if the argument is not a string. === unescape( arg:string ) : string === This function replace escaped characters in the given string to get back the original string, this is the inverse of the escape function. === value( path:string ) : element === This function returns the element identified by its path (which can be an external path), an error occurs if there is no such element. {{{ "/x" = 100; "/y" = 2 * value("/x"); # /y will be 200 # we add one DNS server to the current list # (we need to use list() because merge() requires lists) "/system/dns/servers" = merge(value("/system/dns/servers"), list("137.138.16.5")); # the RAM of this machine is the same as the machine foo "/hardware/memory/size" = value("//foo/hardware/memory/size"); }}}