Lugaru's Epsilon
Programmer's
Editor

Context:
Epsilon User's Manual and Reference
   Primitives and EEL Subroutines
      . . .
      Operating System Primitives
         System Primitives
         Window System Primitives
         Timing
         Calling Windows DLLs
         Running a Process
      Control Primitives
         . . .
         Memory Allocation
         The Name Table
         Built-in and User Variables
         Buffer-specific Variables
         Bytecode Files
         . . .
      Input Primitives
         Keys
         The Mouse
         Window Events
         . . .
         Binding Primitives
      . . .

Previous   Up    Next
The Name Table  Primitives and EEL Subroutines   Buffer-specific Variables


Epsilon User's Manual and Reference > Primitives and EEL Subroutines > Control Primitives >

Built-in and User Variables

Variables that are automatically defined by Epsilon, and have no definition in eel.h, are called built-in variables. These include point, bufnum, and most of the primitive variables described in this chapter. All such built-in variables have entries in Epsilon's name table, so that you can see and set them using commands like set-variable or set-any-variable. Built-in variables have a name_type( ) code of NT_BUILTVAR.

int get_num_var(int i)
set_num_var(int i, int value)

char *get_str_var(int i)
set_str_var(int i, char *value)

Epsilon has several primitives that let you get and set the value of numeric and string global variables (including both built-in and ordinary, user-defined variables). Each primitive takes a name table index i. The get_num_var( ) and get_str_var( ) primitives return the numeric or string value (respectively) of the indicated variable, while the set_num_var( ) and set_str_var( ) primitives set the variable. If you provide an index that doesn't refer to a variable of the correct type, the setting functions do nothing, while the getting functions return zero. (See the vartype( ) primitive below.) The set_str_var( ) primitive only operates on variables with a character pointer data type, not on character arrays. Use varptr( ) below to modify character arrays.

The set-variable command and similar functions look for and try to call a function named when_setting_varname() after setting a variable named varname. For most variables a function with that name doesn't exist, and nothing happens. The want-code-coloring variable is an example of a variable with a when_setting() function. Its when_setting() function sets various other variables to match want-code-coloring's new value.

Any user attempts to set a variable (such as running set-variable or loading a command file) will call such a function, but an ordinary assignment statement in an EEL function will not. If you write an EEL function that sets a variable with a when_setting() function, you should call the function explicitly after setting the variable.

int name_user(int i)
set_name_user(int i, int is_user)

For each global variable, built-in or not, Epsilon records whether or not it is a "user" variable. Some commands such as set-variable only show user variables. Otherwise, Epsilon treats user variables the same as others. The name_user( ) primitive returns non-zero if the variable with the given name table index is a user variable, and the set_name_user( ) primitive sets whether a variable with a particular name table index is a user variable.

user int my_var;        // sample declaration

By default, variables you declare with EEL are all non-user variables, hidden from the user. If the user is supposed to set a variable directly in order to alter a command's behavior, put the user keyword before its global variable definition to make it a user variable. (In previous versions, Epsilon used a convention that any non-user variables you defined had to start with an underscore character, and all others were effectively user variables. This convention still works: set-variable will still exclude such variables from normal completion lists.)

int ptrlen(char *p, ?int in_bytes)
typedef struct eel_pointer {    /* format of EEL pointer */
        int base, size, value;
} EEL_PTR;

The ptrlen( ) primitive takes a pointer of any type and returns the size in characters of the object it points to. The value of ptrlen(p) is the lowest value i for which ((char *)p)[i] is an illegal dereference. If its optional second argument is nonzero, it returns its count in bytes, not characters. (Characters are 16 bits wide, while bytes are 8 bits wide.)

The EEL_PTR type, defined in lowlevel.h, is a structure representing the internal format of an EEL pointer (except for function pointers, which are represented as short integers internally). An EEL pointer consists of a base, a size, and a value. The base and value are standard system pointers, and the size is an integer. Epsilon compares the three fields to catch invalid pointer usage.

Whenever a function dereferences a pointer, Epsilon checks that the fields are consistent. That is, it makes sure that value is greater than or equal to base, and that value is less than base+size. Epsilon will report an illegal dereference if these conditions are not met.

When Epsilon constructs a pointer, it sets the base field to the start of the block of storage within which the pointer points, and sets the size field to the size of the block of storage, in bytes. Epsilon then sets the value field to the actual address to which the pointer points. For example, if an EEL pointer p points to the letter "c" in the string "abcd" (which is terminated by a null character), the size field of p will contain 10 (since five 16-bit characters require ten bytes), the base field will point to the "a", and the value field will point to the "c". Adding an integer to p will change only the value field. Notice that the modified version of p is "consistent" according to the rules above exactly when dereferencing it would be legal: *(p - 2), *(p - 1), *p, *(p + 1) and *(p + 2). The ptrlen( ) primitive above is often a better way to access pointer boundary information, and is less likely to change in future versions.

#include "eel.h"
#include "lowlevel.h"

command eel_ptr_example()
{
    char *hello = "Hello world";
    char *p = hello + 6;
    EEL_PTR *ptr = (EEL_PTR *) &p;

    say("Hello's value is %x, size in bytes is %d, base is %x", hello);
    say("P's value is %x, size in bytes is %d, base is %x", p);
    say("P's value is %x, size in bytes is %d, base is %x",
          ptr->value, ptr->size, ptr->base);
    say("P's value is %x", ((int *)&p)[2]);
}

The above example shows various ways to display the internal structure of pointers for debugging purposes.

char *varptr(int i)
int pointer_to_index(void *)

The varptr( ) primitive returns a pointer to any global variable given its index in the name table. The pointer is always a character pointer and should be cast to the correct type before it's used. When varptr( ) is applied to a buffer-specific or window-specific variable, Epsilon checks the use_default variable to determine if a pointer to the default or current value should be returned (see Buffer-specific Variables). This function doesn't operate with built-in variables--use get_num_var( ) and similar functions for these.

The pointer_to_index( ) primitive does the reverse. It takes a pointer and checks to see if it refers to a global variable. If a global variable is an array or structure, the pointer can point anywhere within. It returns the name table index of the global variable, or 0 if the pointer doesn't point to the contents of any global variable.

int vartype(int i)

#define TYPE_CHAR       1       /* 16-bit Unicode character */
#define TYPE_SHORT      2       /* a 16-bit number */
#define TYPE_INT        3       /* a 32-bit number */
#define TYPE_CARRAY     4       /* character array */
#define TYPE_CPTR       5       /* character pointer */
#define TYPE_POINTER    6       /* contains pointers or spots */
#define TYPE_OTHER      7       /* none of the above */
#define TYPE_BYTE       8       /* an 8-bit number */
int vartype_class(int i)

The vartype( ) primitive returns information on the type of a global variable (or buffer-specific or window-specific variable). It takes the index of the variable in the name table and returns one of the above codes if the variable has type byte, character, short, integer, character array, or character pointer. It returns TYPE_POINTER if the variable is a spot or pointer, or a structure or union containing a spot or pointer. For other types of variables, it returns TYPE_OTHER. It returns 0 if the given index doesn't refer to a variable.

The vartype_class( ) subroutine can be more convenient than vartype( ). It returns 1 if the variable (specified by its name table index) has a numeric type, 2 if it has a string type (TYPE_CARRAY or TYPE_CPTR), and 0 otherwise.

int new_variable(char *name, int type, int vtype, ?int length)

The new_variable( ) primitive provides a way to create a new variable without having to load a bytecode file. The first argument specifies the name of the variable. The second argument is a type code of the kind returned by the name_type( ) primitive. The code must be NT_VAR for a normal variable, NT_BUFVAR for a buffer-specific variable, NT_WINVAR for a window-specific variable, or NT_COLSCHEME for a color scheme. The third argument is a type code of the kind returned by the vartype( ) primitive. This code must be one of the following: TYPE_BYTE, TYPE_CHAR, TYPE_SHORT, TYPE_INT, or TYPE_CARRAY. The last argument is a size, which is used only for TYPE_CARRAY. It returns the name table index of the new variable, or -1 if it couldn't create the variable in question.



Previous   Up    Next
The Name Table  Primitives and EEL Subroutines   Buffer-specific Variables


Lugaru Copyright (C) 1984, 2020 by Lugaru Software Ltd. All rights reserved.