Content-type: text/html
esh accepts comments in the style of C/C++ and uses a preprocessor similar to C/C++. See later in this document. Lines starting with #! are not interpreted by esh. If the script is executable and the first line is
where path is the full path name of the command esh, you can use it like an ordinary command. I prefer the following variation, which is independent from the installation place of esh:
Expressions are terminated either by a semicolon or a linefeed, tabs and spaces are skipped. An expression may also end if there is no right operator following a term. In this case and if the next character is a punctuation character, it is used as termination key, else a space is used. On some places, e.g. inside the argument list of a function, a linefeed does not terminate the expression and is skipped like tabs or spaces.
In the outermost level (outside any block or function body) every statement is evaluated immediately after parsing. If an expression is not terminated by a semicolon, the result is written to standard output followed by the terminating character.
For example: The line
If esh is called without script name or if the script name is a single minus, commands are read from standard input. If standard input and standard output is connected to a terminal, esh runs interactive and readline is used for reading lines from terminal. Readline control keys are active and ! at beginning of a new line is used as control key to run history and system commands.
The use of readline in interactive mode and the automatic display of results in the outermost level allows to use esh as a comfortable desk calculator.
The complete EFEU interpreter language is implemented with C library functions. esh is a simple command which uses this functions. The interpreter shares data pointers directly with C functions. You can add your own functions and types to the interpreter. So it is easy to use this language for configuration files or to test functions with it.
If EFEU is build with shared libraries (e.g. on Linux) you can expand esh at run time. If shared libraries are not available, you can take a copy of esh.c and add your extensions there.
The following options and arguments are accepted by esh:
The EFEU interpreter language uses a preprocessor similar to C. The preprocessor is built in as a filter and can be used independent of the interpreter. The preprocessor evaluates the input per line and not per file. So you can create or change variables which may be used later in conditional directives.
In esh, the following construction is legal:
str header = paste("/", "SubDir", "MyHeader");
#include "<" + header + ">"
The variable header is defined in the outermost level, so it is immediately executed and can be used in the following #include directive. Adding < and > avoids searching the current directory (if IncPath does not include the current directory).
The directives
The simplest form of a conditional is:
A more complex conditional may look like this:
As seen in the section "Including files", you can use any variable or function in expr previously declared in the outermost level.
The name of a macro must start with an alphabetic or underline character and may contain only alphanumeric or underline characters.
In esh macros are rare used. In most of all places, variables and functions are the better solution. Normally they are only used to protect header files for multiple inclusions.
A macro could be removed with the #undef directive.
The next tables show the available operators of esh. They are sorted by descending priority. Operators not separated by a line have the same priority.
| Prefix operators | ||
| :: | global | ::name |
| ++ | pre increment | ++lvalue |
| - | pre decrement | -lvalue |
| ~ | complement | ~expr |
| ! | not | !expr |
| - | unary minus | -expr |
| + | unary plus | +expr |
| { | list grouping | { expr [, expr ] } |
| ( | grouping | ( expr ) |
| [ | expression | [ expr ] |
| () | cast (type conversion) | (type) expr |
The expression operator parses an expression without evaluation. This expression may be stored in a variable or passed as function argument for later evaluation.
| Postfix operators | ||
| ++ | post increment | lvalue++ |
| - | post decrement | lvalue- |
| :: | scope resolution | type::name |
| :: | variable selection | vartab::name |
| . | member selection | expr.name |
| [] | sub scripting | expr[expr] |
| () | function call | expr(list) |
| Arithmetic operators | ||
| * | multiply | expr * expr |
| / | division | expr / expr |
| % | modulo (remainder) | expr % expr |
| + | add (plus) | expr + expr |
| - | subtract | expr - expr |
| << | shift left | expr << expr |
| >> | shift right | expr >> expr |
| Comparison operators | ||
| < | less than | expr < expr |
| <= | less than or equal | expr <= expr |
| > | greater than | expr > expr |
| >= | greater than or equal | expr >= expr |
| == | equal | expr == expr |
| != | not equal | expr != expr |
| Bit wise operators | ||
| & | bit wise AND | expr & expr |
| ^ | bit wise exclusive OR | expr ^ expr |
| | | bit wise inclusive OR | expr | expr |
| Logical operators | ||
| && | logical AND | expr && expr |
| || | logical OR | expr || expr |
| Conditional and range operator | ||
| ? : | conditional operator | cond ? expr1 : expr2 |
| : : | range operator | start : end [ : step ] |
| Assign operators | ||
| = | simple assignment | lvalue = expr |
| *= | multiply and assign | lvalue *= expr |
| /= | divide and assign | lvalue /= expr |
| %= | modulo and assign | lvalue %= expr |
| += | add and assign | lvalue += expr |
| -= | subtract and assign | lvalue -= expr |
| <<= | shift left and assign | lvalue <<= expr |
| >>= | shift right and assign | lvalue >>= expr |
| &= | AND and assign | lvalue &= expr |
| ^= | exclusive OR and assign | lvalue ^= expr |
| |= | inclusive OR and assign | lvalue |= expr |
| List separator | ||
| , | list separator | expr , expr |
If you do not use the return value (in the most common use), there is no difference between the comma operator and the list separator.
In any case of loop, the statement break breaks out of the loop and the statement continue starts the next cycle.
The syntax of a switch statement is:
switch (expr)
{
label:
cmdlist
label:
cmdlist
}
where label may be case val or default.
The expression val is evaluated on parsing and not on executing the
switch statement.
The value of expr is compared with all labels in order of
definition. If the comparison is true, all following
statements until break, continue, return or the end of the
switch block is reached, are executed.
If none of the labels compares with expr, all statements after
default (if present) are executed.
In opposite to C, any data type is allowed in the switch statement as long as the operator == is defined. In particular you can use strings in switch statements.
For example:
int x; double a, b; x = (int y = 5);
declares first the integer variable x and the double variables a and b. Afterwards the integer variable y is declared with value 5 and the result (the value 5) is assigned to x. It is allowed to declare a variable more than once with the same type. All but the first declarations are converted to a assignment statement.
Every predefined data type in the interpreter language has a corresponding data type in C. The EFEU interpreter language does not have pointers, but data types may be represented by pointer types in C.
The interpreter distinguishes between lvalues and constants. An lvalue is anything, that could stand on the left side of an assignment. Typical lvalues are variables. The result of an expression or a function call may be an lvalue or not.
| esh type | C type |
| bool | int |
| int | int |
| unsigned | unsigned int |
| varint | int64_t |
| varsize | uint64_t |
The syntax of integral constants is like C/C++. The keywords true (integral value 0) and false (integral value 1) are used for boolean values.
Strings are completely different implemented in esh than in C. They are not fields of type char, they have the data type str. If you assign a string to a value or use it as function argument, the whole string (not the address) is copied. Copying strings is always done with memory allocation and there is a built in garbage collection for it (and generally for all dynamic allocated objects).
Character constants are delimited by single quotes, string constants by double quotes. The backslash is used as escape character like in C.
String constants may contain linefeed. In esh, two strings next to each other are not concatenated like in C/C++. You need the add operator + to do this.
For long string constants there exists the keyword string, which is used in the following form:
There must be a newline after ! in the starting line and ! must be the first character in the last line. A so defined string always contains a linefeed at end. The backslash is no longer used as escape character with one exception: protecting a ! at beginning of line to be interpreted as string termination. This construction of strings may be used anywhere inside an expression.
Comments are skipped and preprocessor directives are interpreted inside this string definitions.
For example: you can write
to get the file file included in the string s.
Note, that in EFEU (and so in esh) null strings (character pointer to NULL) can be used like ordinary strings and there is a difference between null strings and empty strings (strings containing only the terminating 0) are handled . The EFEU libraries contains tools for handling dynamic allocated strings and you can mix them with constant strings. The memory allocation tools in EFEU knows, if the memory of a string could be freed.
The type _Ref_ and all other types with a pointer representation in C are subclasses of _Ptr_. This is also the type of the constant NULL.
Types which starts and ends with the underline symbol are reserved for internal use. Normally, you don't declare variables of this type. But this types may be used for arguments in virtual functions, e.g. to distinguish between the constant NULL and a string initialized with NULL.
Any object of type List_t have the two components:
In absence of pointers in esh, you may use List_t as substitution.
In the first case and if the data field is initialized by a list of values, dim may be omitted and the number of elements in the list determines the size of the field. In the second case, a new type is implicit created. The field size is necessary.
In the second case 0 or a missing value of dim indicates a variable length array. The data field is implicit enlarged as you use a higher index. Data types of the form type[] are subclasses of EfiVec.
If you have more then one dimension, a declaration of the form
Data fields are always packed into a object of type EfiVec on use. A data field can always be converted to a list and you can assign values to a data field with a list. If the list has less elements than the data field, only the corresponding elements are changed.
EFEU provides you the following data types for a more powerful handling of data than ordinary data fields:
The simplest way to create a new type is typedef, as in
Structures are created with the struct statement. The syntax is
The following two types
struct T1 {
int a;
int b;
}
struct T2 : int a {
int b;
}
have the same components, but T2 can be used as representation of an integer.
Any previously defined type can be used in this form of type declaration. Any structure type could be converted to a list and any list with corresponding elements could be converted to a structure type.
Example for a more complex structure:
struct MyDataType {
int i;
double d;
str s;
int v[10];
};
The EFEU interpreter supports enumeration types. The syntax is
The following statement:
Color c1 = "Red"; Color c2 = 0; str s = Color::Red; int n = Color::Red;
The function enumlist(type) returns a list of all valid identifiers of the enumeration type type or an empty list, if type has no identifiers or is not an enumeration type.
Normally expr is a block structure, but in esh you can also use a single (but not empty) expression. If the function does not return any value, use the special type void.
The following function declarations are equivalent:
int f (int x) x + 1;
int f (int x) return x + 1;
inline int f (int x) { return x + 1; }
In esh, the keyword inline has nothing to do with optimation, but with visibility. A inline function sees all variable tables as in the line where it is called. All functions defined with a single expression are default of type inline.
Here is a example where inline functions are needed:
inline str f (str fmt)
{
return psub(fmt);
}
{
str x = "foo";
f("x = $(x)");
}
The function psub substitutes parameter according to a format string. If f is not inline, psub does not see the variable x and the substitution $(x) would fail.
Functions have the type Func and you can use it like function pointers in C. Typing the function name in outermost level gives you the prototype of the function.
As in C++ function arguments may have default values. The general form of a function argument is:
Behind the most operators stands a function with the name of the operator. You can use either operator "name" or operatorname<space> for such function names. For example: operator+ is the name of the addition function. Function names of prefix operators have an additional "()" in the name to distinguish between postfix and binary operators. So operator+() is the name of the unary plus.
You can convert a virtual function to a regular function with a prototype cast:
Func f = operator+ (int a, int b);;
Now you can use f to add two integer values. Note the two semicolons: The first is part of the prototype, the second terminates the expression and may be replaced by a linefeed.
The & after the function name indicates that it can only be used for lvalues. A bounded function is called
where obj is an object of type btype. Object bound functions have the type ObjFunc and may be virtual or not.
All assignment operators are bounded functions. In bounded functions you can use this to refer to the corresponding object.
Constructors have the form:
The declaration
Converters have the form:
Because of internal garbage collection, there is normally no need on copy constructor and destructor. You must be very carefully in defining this functions, because you get an infinite recursive call if an object of the type is copied inside the function.
If you want to know how a function is used, just enter the function name and the prototype is displayed. For a data type type you can call the function type.info() to get additional information's.
Copyright (C) 1994, 2001 Erich Frühstück