|
INTRODUCTIONThis manual page documents the language as understood by the KL-EL library. For documentation on how to embed the KL-EL compiler and interpreter in an application, see klelapi(3). For a tutorial on how to use the library, see kleltut(3).The language understood by KL-EL is an expression language. Everything in the language is an expression that has a value. The purpose of the library is to allow these expressions to be input at runtime and have the application make use of the resulting values in some fashion. There are two kinds of KL-EL expressions: value expressions (often referred to as simply "expressions") and guarded commands. Guarded commands are more complicated, but they are also very useful in certain situations. Because guarded commands are more complex than value expressions, they will be covered second. Expressions in KL-EL are not sensitive to whitespace; whitespace is only significant inside literal strings. A NOTE ON TYPESEvery value in KL-EL has one and only one type, and the type of a given expression never changes. That makes KL-EL strongly typed, and this typing discipline is possibly its most interesting feature. KL-EL was designed to be useful in applications where an expression is compiled only once but executed thousands or millions of times with different parameters, and possibly on ephemeral or forensic data. If typechecking were not performed until evaluation, it is possible that a failure could occur after millions of executions on some set of inputs, rendering the entire run (or a large portion of it) invalid. In the best case, the expression could be fixed and execution could pick up where it left off, but in the worst case, it may be impossible to recover the values used previously or begin execution again. Checking types at compile time also makes things faster for multiple executions of a given expression, since types do not need to be checked on each execution.KL-EL understands four types:
Functions are provided to transform values of one type to another type as necessary. In general, there is no concept of type promotion. Expressions like "123" + 5 don't work since addition is not defined for strings. There is a slight relaxation of this restriction for expressions involving a mixture of integers and reals -- integers are silently promoted to real values when they are used in arithmetic expressions involving reals. This is the only exception to the typing discipline. Note that the exception only applies to arithmetic expressions -- functions expecting real arguments will not accept integer arguments. NAMESAny top-level expression can be named, and this name can be extracted from the compiled expression at runtime. This is useful in situations where there may be many unrelated expressions (say, in a configuration file), and it would be useful to identify expressions in diagnostics or other output.To name an expression, simply prefix it with a name and a colon. A name in this case may consist of letters, numbers, and underscores, and must begin with a letter. For example, this gives an expression "1 + 1" the name "Example": Example : 1 + 1 Once again, note that only top-level expressions may be named. VALUE EXPRESSIONSValue expressions are simple expressions that, when evaluated, produce a value. There are seven different kinds of value expressions:
Each of these expression types is covered below. For more information on how to retrieve the value of an evaluated expression using the library, see klelapi(3). LITERAL BOOLEANSThere is no literal syntax for Boolean values. The standard variables "true" and "false" are defined and have their expected meanings.LITERAL NUMBERSNumbers can be written in octal, decimal, or hexadecimal. Octal numbers are prefixed with '0o', and hexadecimal numbers are prefixed with '0x'. Sequences of decimal digits with no prefix are assumed to be in decimal.Real numbers are written in decimal, with '.' as the decimal point. The decimal point and one digit after it are obligatory. An optional exponent can be provided by suffixing the number with 'e', an optional minus sign (indicating a negative exponent), and at least one decimal digit. LITERAL STRINGSLiteral strings are written enclosed in double quotes. Any character may appear between the double quotes except for newlines, double quotes, backslashes, percent signs, and literal zeros (NULL bytes). By using escapes, these and other characters can be conveniently inserted. The following escapes are defined:
Literal strings may also contain interpolations. Interpolations are of one of the forms %{name} %(name) It is because of this syntax that percent signs are recognized as an escapable character. Interpolations take the value of the named variable, convert that value to a string, and embed the stringified value into the string produced by evaluating the string literal. Variable names enclosed in curly brackets have their value inserted as is, while those enclosed in parentheses have their values inserted after any special characters have been quoted (this is referred to as a quoted interpolation). In this case "special characters" and the character used to quote them are defined by the application using the library, with sensible defaults. VARIABLESVariables can be used anywhere where an expression is expected. Variables are of static type, but their values may change at any time. Variables are either exported from the application using the library (referred to as the "host" or "host application"), or they are introduced by a let expression (see below). Applications using the library can export any number of variables into the KL-EL environment, allowing for simple interaction between the host application and KL-EL. For information on how applications export variables into the KL-EL environment, see klelapi(3).KL-EL comes with a standard library that defines several variables. New variables can be introduced inside an expression by introducing a new scope using a let expression; these expressions are discussed below. FUNCTION CALLSIt is not possible to define a function in KL-EL, but the application using the library can export as many functions as it needs. KL-EL also comes with a standard library of functions. The functions in this library are automatically available for use in any expression unless specifically disallowed by the host application.A function call uses syntax familiar from C: name(arg0, arg1, ... argN) Due to the way types are represented in the type checker, functions are limited to thirteen arguments. It is important to note that functions are not first-class values; they can neither be passed as arguments to nor returned as the result of other functions. For more information on how to export a function into the KL-EL environment, see klelapi(3). UNARY OPERATIONSBelow three (3) unary operators are defined in terms of their operator symbol, operand type, and result type. The type codes B, I, and R represent boolean, integer, and real types, respectively.======= DEFINITION ======= o l r r p e i e e f g s r t h u a t t l t t o r ====== OPERATION =========================== Arithmetic Negation - I I Arithmetic Negation - R R Bitwise NOT ~ I I Boolean Negation ! B B Note that unary operators bind more tightly than any binary operator. BINARY OPERATIONSBelow twenty-one (21) binary operators are defined in terms of their operator symbol, operand types, and result type. These operators are listed in decreasing order of precedence. The type codes B, I, R, and S represent boolean, integer, real, and string types, respectively.======= DEFINITION ======= o l r r p e i e e f g s r t h u a t t l t t o r ====== OPERATION =========================== Multiplication * I I I Multiplication * R I R Multiplication * I R R Multiplication * R R R Division / I I I Division / R I R Division / I R R Division / R R R Modular Division % I I I Logical Conjunction && B B B Bitwise Roll Left << I I I Bitwise Roll Right >> I I I String Concatenation . S S S Addition + I I I Addition + R I R Addition + I R R Addition + R R R Subtraction - I I I Subtraction - R I R Subtraction - I R R Subtraction - R R R Logical Disjunction || B B B Bitwise AND & I I I Bitwise XOR ^ I I I Bitwise OR | I I I Less than < I I B Less than < R R B Less than < S S B Greater than > I I B Greater than > R R B Greater than > S S B Less than or equal <= I I B Less than or equal <= R R B Less than or equal <= S S B Greater than or equal >= I I B Greater than or equal >= R R B Greater than or equal >= S S B Equal == B B B Equal == I I B Equal == R R B Equal == S S B Not equal != B B B Not equal != I I B Not equal != R R B Not equal != S S B Regex match =~ S S B Regex non-match !~ S S B Note that the environment's localization settings (particularly collation order) may affect the result when comparing strings using the relational operators. For regular expression matches, the right operand is the expression; it is expected to be a string in PCRE syntax. For more information, see pcre(3). Parentheses can be used to override the normal order of operations when necessary. THE TERNARY OPERATORKL-EL defines a single ternary operator, '?'. Ternary expressions are of the form:predicate ? expr1 : expr2 where predicate is an expression of Boolean type and expr1 and expr2 are two expressions of the same type. If predicate evaluates to true, expr1 is evaluated, otherwise expr2 is evaluated. Note that parenthesis are generally required around predicates unless they are syntactically trivial. The type of a ternary expression is the type of expr1 or expr2. LET EXPRESSIONSKL-EL is lexically scoped in the ALGOL tradition. New environments are created using let expressions. These expressions are of the form:let var = expr1 in expr2 The value of this expression is the value of expr2 with every occurrence of var substituted by the value of expr1. As let expressions are normal expressions, they can be nested. In particular, this is a valid (and common) idiom: let var1 = expr1 in let var2 = expr2 in let var3 = expr3 in var1 + var2 + var3 Variables in nested scopes can shadow variables in outer scopes, though this functionality is best used sparingly. Variables exported from the host application are in the outermost scope. GUARDED COMMANDSAside from value expressions, KL-EL supports guarded commands. A guarded command consists of a guard of boolean type, a call to the special function eval, and an optional list of return codes. The intent of guarded commands is to provide the host application with a command (where the meaning of "command" is defined by the host application), along with a set of supporting arguments, that is valid only when the guard expression is valid.Guarded commands have one of the following forms: if (guard) then eval(interpreter, program, arg0, ... argN) if (guard) then eval(interpreter, program, arg0, ... argN) pass [code0, ... codeN] if (guard) then eval(interpreter, program, arg0, ... argN) fail [code0, ... codeN] In these forms guard must be a value expression of boolean type, interpreter and program must be constant strings, and codes must be literal integers. The remaining arguments may be of any type. Guarded commands are useful in situations where it is important to distinguish between the action and the guard. Take for example a file-finding application. In this hypothetical application, a guarded command is used to specify a command to execute on every file that matches its expression thereby allowing the application to customize its behavior based on observed file attributes. In a guarded command, the arguments to the eval function and the code values are passed as-is to the host application. It is up to the application to determine their meaning. For a discussion on how guarded commands are represented in host applications, see klelapi (3). It is important to note that guarded commands cannot be nested inside other expressions or guarded commands. SEE ALSOklel-expr(1), klelapi(3), klelstdlib(3), kleltut(3), pcre(3)
Visit the GSP FreeBSD Man Page Interface. |