OADL Implementation Notes

Type System

In a 32-bit implementation, OADL objects are represented internally by 32-bit numbers. To maximize the precision of Float and Int variables, the type information for them is compressed. Float variables have 31 bits of value, and the type is indicated by having the least- significant bit set to '0'. Int variables have 30 bits of value, and the type is indicated by having the least-significant two bits set to '01' (or 1). The remaining types have up to 24 bits of value, six bits of type ID (from the table below), and the least-significant two bits are set to '11' (or 3).

Type Name ID Value
Float 0 (0x00) 31-bit single-precision IEEE float (the 32nd LSB is forced to 0)
Int 1 (0x01) 30-bit signed integer
Double 2 (0x02) 24-bit atom ID of 64-bit IEEE double-precision scalar
Long 3 (0x03) 24-bit atom ID of 62-bit signed integer
Ulong 4 (0x04) 24-bit atom ID of 64-bit unsigned integer
Uint 5 (0x05) 24-bit atom ID of 32-bit unsigned integer
Array 6 (0x06) 24-bit Array ID
Object 7 (0x07) 24-bit Object ID
ArrType 8 (0x08) 24-bit ArrType ID
Enclosure 9 (0x09) 24-bit Enclosure ID
Reserved 10-15 Reserved for future expansion
Pointer 16 (0x10) 24-bit atom ID of externally defined pointer
Dict 17 (0x11) 24-bit Dict ID
Proc 18 (0x12) 24-bit Proc ID
Extern 19 (0x13) 24-bit Extern ID
Public 20 (0x14) 24-bit Public ID
Exception 21 (0x15) 24-bit Exception ID
File 22 (0x16) 24-bit File ID
Reserved 23-31 Reserved for future expansion
Reserved 32-47 Reserved for OADL internal use
Reserved 48-53 Reserved for future expansion
WideChar 54 (0x36) 21-bit Unicode character
Half 55 (0x37) 16-bit half-precision float
Ushort 56 (0x38) 16-bit unsigned integer
Short 57 (0x39) 16-bit signed integer
Ubyte 58 (0x3A) 8-bit unsigned integer
Byte 59 (0x3B) 8-bit signed integer
Char 60 (0x3C) 7-bit character value (typically, an ASCII char)
Type 61 (0x3D) 6-bit unsigned value (one of the Type constants)
Bool 62 (0x3E) 1-bit boolean
Null 63 (0x3F) 0xFFFFFFFFu in C - the OADL nil* value

It is illegal for least-significant bits of '11' (or 3) to be combined with the type bits 2-7 of Float (0) or Int (1). Attempting to create an OADL value of this form will result in undefined behavior.

In a 64-bit implementation of OADL, the first few types are arranged differently:

Type Name ID Value
Double 0 63-bit IEEE double-precision float (the 64th LSB is forced to 0)
Long 1 62-bit signed integer
Float 2 32-bit IEEE single precision float
Int 3 32-bit signed integer

Additionally, in a 64-bit implementation, IDs and variable addresses are 32 bits rather than 24. The illegal type combinations are the least-significant bits of '11' (or 3) combined with Double (0) or Long (1) type bits 2-7. The value of nil is 0xFFFF_FFFF_FFFF_FFFFul (64 consecutive '1' bits).

There are several other dynamic types supported by OADL and returned by the typeof() intrinsic procedure; these types are synthized from a combination of OadlVar base type and information stored in the dynamic object referred to by the base value:

Type Name Base Type Indirect Base Type Other information
Class Object Class
PackDouble Array Double
PackUlong Array Ulong
PackLong Array Long
PackFloat Array Float
PackUint Array Uint
PackInt Array Int
PackHalf Array Half
PackUshort Array Ushort
PackShort Array Short
PackUbyte Array Ubyte
PackByte Array Byte
PackBool Array Bool
String Array Char One dimension
PackChar Array Char Multiple dimensions
WideString Array WideChar One dimension
PackWideChar Array WideChar Multiple dimensions
List Array Array One dimension
Array[*] Array Array Universal array type

These pictures document the actual bit layout of the various types in a 32-bit implementation:

Float - 0x0
S
31
Exponent:8
30 23
Mantissa:22
22 1
0
0

Int - 0x1
Value:30
31 2
0 1
1 0

24-bit variables - 0x02 through 0x1F
Value:24
31 8
0 t t t t t
7 2
1 1
1 0

WideChar - 0x36
n/a
31 29
Value:21
28 8
1 1 0 1 1 0
7 2
1 1
1 0

Half - 0x37
n/a
31 24
S
23
Exponent:5
22 18
Mantissa:10
17 8
1 1 0 1 1 1
7 2
1 1
1 0

Ushort, Short - 0x38, 0x39
n/a
31 24
Value:16
23 8
1 1 1 0 0 t
7 2
1 1
1 0

Ubyte, Byte - 0x3A, 0x3B
n/a
31 16
Value:8
15 8
1 1 1 0 1 t
7 2
1 1
1 0

Char, Type - 0x3C, 0x3D
n/a
31 15
Value:7
14 8
1 1 1 1 0 t
7 2
1 1
1 0

Bool - 0x3E
n/a
31 9
V
8
1 1 1 1 1 0
7 2
1 1
1 0

Null - 0x3F
0xFFFFFF
31 8
1 1 1 1 1 1
7 2
1 1
1 0

These pictures document the actual bit layout of the various types in a 64-bit implementation:

Double - 0x0
S
63
Exponent:11
62 52
MantHi:20
51 32
MantLo:31
31 1
0
0

Long - 0x1
ValueHi:32
63 32
ValueLo:30
31 2
0 1
1 0

Float - 0x2
S
63
Exponent:8
62 55
Mantissa:23
54 32
n/a
31 8
0 0 0 0 1 0
7 2
1 1
1 0

32-bit variables - 0x03 through 0x1F
Value:32
63 32
n/a
31 8
0 t t t t t
7 2
1 1
1 0

WideChar - 0x36
n/a
63 53
Value:21
52 32
n/a
31 8
1 1 0 1 1 0
7 2
1 1
1 0

Half - 0x37
n/a
63 48
S
47
Exponent:5
46 42
Mantissa:10
41 32
n/a
31 8
1 1 0 1 1 1
7 2
1 1
1 0

Ushort, Short - 0x38, 0x39
n/a
63 48
Value:16
47 32
n/a
31 8
1 1 1 0 0 t
7 2
1 1
1 0

Ubyte, Byte - 0x3A, 0x3B
n/a
63 40
Value:8
39 32
n/a
31 8
1 1 1 0 1 t
7 2
1 1
1 0

Char, Type - 0x3C, 0x3D
n/a
63 39
Value:7
38 32
n/a
31 8
1 1 1 1 0 t
7 2
1 1
1 0

Bool - 0x3E
n/a
63 33
V
32
n/a
31 8
1 1 1 1 1 0
7 2
1 1
1 0

Null - 0x3F
0xFFFFFFFF
63 32
0xFFFFFF
31 8
1 1 1 1 1 1
7 2
1 1
1 0

Several types refer to "IDs" - these are virtual indexes into a dynamic memory pool. Implementations must provide automatic garbage collection for these IDs, and all implementations must be able to distinguish read-only from writeable Array and Object contents.

Pointer values are provided only for the convenience of interfacing to system library routines. They are completely opaque to OADL programs. Pointer values are not saved to disk.

Generic arrays are arrays of 32-bit or 64-bit tagged values, as described above. Packed arrays are stored more efficiently, since the type of each element is homogeneous and stored as part of the array type.

Arguably, for Null values, one could store no data in a packed array. However, this is seen as an optimization of no value.

The type system is implemented in C at oadlvar.h .

OADL machine internals

In some cases, it can be useful to understand the OADL stack machine. Although it is not complete documentation, the opcodes are implemented in C at opcode.h and builtin.h and in OADL at oadlmach.oah . Additionally, note that some of the more complicated parts of OADL's machine are implemented in an extended form of OADL (extended by some intrinsics that are not supported for general use). See intrinsic.oad for more information.

External procedure libraries

External procedure libraries are typically implemented as dynamically loaded system libraries. These libraries are kept in a system-dependent location (frequently the syslib directory of the OADL installation location).

The dynamic library should implement an entry point named "OADL_libname" (where "libname" is the same as that used in the using extern statement). That entry point then performs callbacks to the OADL runtime to register external procedures, as well as to capture pointers to important OADL implementation functions. These callbacks are defined in oadlsys.h .

This entry point must be implemented with the following prototype (where "xxx" is the name of the dynamic library):

int OADL_xxx(void *ctx, OADL_FindProc_fp findProc);

The first argument passed to that entrypoint is a "context" pointer, which must be passed back to the findProc entry point. The second argument passed to the entry point is the findProc routine. This routine is used to find all the other OADL implementation procedures which may be needed by the utility library. The typedef for this function pointer is:

typedef int (*OADL_FindProc_fp)(void *ctx, const char *name,
                                int version, OADLproc *rProc);

The name is the name of the procedure that is desired (see the table below for the complete list). The OADL_SYS_VER constant must be given for version. The proc is returned in the rProc pointer. The OADLproc typedef exists only to provide a "generic" function pointer; the returned pointer should be typecast according to the table below before it is used.

The most important procedure which should be queried and used is the OADL_AddExtern() procedure. The utility library must use this procedure to define the Extern procs which are implemented by the library. The following fragment queries and calls the OADL_AddExtern procedure to implement an Extern named foo::bar"

OADL_AddExtern_fp OADL_AddExtern;
int foo_bar(void *ctx, OadlVar *pRes, int nargs, const OadlVar *args);

if (!findProc(ctx, "OADL_AddExtern",
               OADL_SYS_VER, (OADLproc *) &OADL_AddExtern))
{
    return 0;
}
if (!OADL_AddExtern(ctx, "foo::bar", foo_bar)) {
    return 0;
}

All Extern procs are implemented with the same function prototype:

typedef int (*OADL_Extern_fp)(void *ctx, OadlVar *pRes,
                              int nargs, const OadlVar *args);

The function returns 1 for success, and 0 if any error occurred (see ThrowError(), below, for how to raise an exception which will be handled after the function returns). The result OadlVar should be returned in the pRes pointer. nargs contains the number of arguments which were passed to the Extern, and args is an array containing all the arguments. See the oadlvar.h header file for the definition of the OadlVar structure. The ctx argument must be passed back to any callbacks called by this entry point.

The following is the complete list of functions that may be queried by findProc. Generally, these functions return 1 on success and 0 if any error occurred.

OADL_AddExtern
typedef int (*OADL_AddExtern_fp)(void *ctx, const char *name, OADL_Extern_fp proc);

Defines an Extern entry point (see above for more information)
OADL_GetProp
typedef int (*OADL_GetProp_fp)(void *ctx, OadlVar *pRes, int propNum);

Returns, in pRes, the object property of self at offset propNum. Note that the layout of OADL object properties is undefined, and can change from compile to compile. It is generally better to use the GetPublic/SetPublic routines, below.
OADL_SetProp
typedef int (*OADL_SetProp_fp)(void *ctx, int propNum, OadlVar val);

Sets property number propNum of self to val. See caveats in GetProc, above.
OADL_GetIndex
typedef int (*OADL_GetIndex_fp)(void *ctx, OadlVar *pRes, OadlVar v, int nIdx, int idx[]);

Returns, in pRes, the contents of v[idx0, idx1, ...].
OADL_SetIndex
typedef int (*OADL_SetIndex_fp)(void *ctx, OadlVar v, int nIdx, int idx[], OadlVar val);

Sets v[idx0, idx1, ...] to val. Note that OADL_SetIndex() can trigger a GC.
OADL_GetSubr
typedef int (*OADL_GetSubr_fp)(void *ctx, OadlVar *pRes, OadlVar v, int numSub, OadlVar *subrs);

The same as v.subr(subrs0, subrs1, ...) Creates a new subrange of the given var v. The subrange is returned in pRes.
OADL_ArrayInfo
The OADL_ArrayInfo struct contains information about an OADL array. It is used both for information queries as well as for allocation for newly created arrays. See OADL_GetArrayInfo() and OADL_CreateArray() for more information. Here is the structure and its associated flags:
#define OADL_ARRAY_READONLY     0x01    /* Do not modify contents */
#define OADL_ARRAY_PERMANENT    0x02    /* Versus dyamic / GC memory */
#define OADL_ARRAY_USER_DATA    0x04    /* Points to user data, not OADL data */
#define OADL_ARRAY_ITERATOR     0x08    /* Iterator - only if not expanded */

/* If the array is an iterator and the caller requested the parameters,
 * the ArrData is a pointer to an array of the following items (in the
 * type given by ArrType):
 */
#define OADL_ARR_ITER_PARAM_FIRST       0
#define OADL_ARR_ITER_PARAM_LAST        1
#define OADL_ARR_ITER_PARAM_INCR        2
#define OADL_ARR_ITER_PARAM_COUNT       3 // Total size of iterator param array

typedef struct {
    OADL_U32 ArrSize;                   /* Total size of array, in elements */
    OADL_U8 ArrType;                    /* One of OADL_VT_* */
    OADL_U8 ElemSize;                   /* Size of a single element, in bytes */
    OADL_U8 ArrRank;                    /* Rank - number of dimensions */
    OADL_U32 ArrFlags;                  /* Flags from OADL_ARRAY_*, above */
    OADL_U32 ArrShape[OADL_MAX_RANK];   /* Shape of the array */
    OADL_U32 ArrStrides[OADL_MAX_RANK]; /* Dimension strides, in elements */
    OadlVar ArrVar;                     /* Only if iter. expanded to heap */
    void *ArrData;                      /* Pointer to array data or params */
    void *ArrAlloc;                     /* Only if iter. expanded to malloc */
} OADL_ArrayInfo;

See caveats in OADL_WriteCopy() and OADL_GetGcCounter() regarding dereferencing the ArrData pointer.

OADL_GetArrayInfo
typedef int (*OADL_GetArrayInfo_fp)(void *ctx, OADL_ArrayInfo *pArr, OadlVar a, int flags);

Returns info about a in OADL_ArrayInfo structure *pArr. The flags are composed from the following bits:
#define OADL_AIF_ITERATOR_EXPANSION     0x00000007 // Mask for AIF_ITERATOR*
#define OADL_AIF_ITERATOR_HEAP          0x00000001 // ...expand into heap
#define OADL_AIF_ITERATOR_MALLOC        0x00000002 // ...expand into malloc
#define OADL_AIF_ITERATOR_SCRATCH       0x00000003 // ...expand into scratch
#define OADL_AIF_ITERATOR_PARAMS        0x00000004 // ...raw params in ArrData
OADL_GetGcCounter
typedef int (*OADL_GetGcCounter_fp)(void *ctx);

OADL keeps a count of how many times GC occurs. If the GC counter changes after calls to OADL_WriteCopy() or OADL_CreateArray() then a GC has occurred, and therefore OADL_GetArrayInfo() should be called again on all cached OADL_ArrayInfo structures.
OADL_WriteCopy
typedef int (*OADL_WriteCopy_fp)(void *ctx, int *pRes, OadlVar v);

To write directly into an array's data (i.e., instead of calling OADL_SetIndex()), a call to OADL_WriteCopy() must be made. The integer *pRes will be changed to 1 if a write copy was created and 0 otherwise. It is not necessary to call OADL_WriteCopy() on arrays created by the current Extern call. Note that OADL_WriteCopy() can trigger a GC.
OADL_Writeable
typedef int (*OADL_Writeable_fp)(void *ctx, int *pRes, OadlVar v);

Queries whether array v is writeable (that is, whether SetIndex may be called on it). The result of the query is returned in pRes.
OADL_TypeOf
typedef int (*OADL_TypeOf_fp)(void *ctx, int *pRes, OadlVar v);

Returns, in pRes, the abstract type of v - exactly the same as the typeof intrinsic.
OADL_FatalError
typedef int (*OADL_FatalError_fp)(void *ctx, int errNum)

Raises fatal exception errNum from oadlerr.h
OADL_ThrowError
typedef int (*OADL_ThrowError_fp)(void *ctx, int errNum);

Schedules the error errNum (from oadlerr.h for an exception handling event after the Extern function returns.
OADL_FindPublic
typedef int (*OADL_FindPublic_fp)(void *ctx, OadlVar *pRes, const char *name);

Find the Public named name and return its value in pRes.
OADL_FindExtern
typedef int (*OADL_FindExtern_fp)(void *ctx, OadlVar *pRes, const char *name);

Find the Extern named name and return its value in pRes.
OADL_FindObject
typedef int (*OADL_FindObject_fp)(void *ctx, OadlVar *pRes, const char *name);

Find the object named name and return its value in pRes. The name must be one located in global scope.
OADL_GetSelf
typedef int (*OADL_GetSelf_fp)(void *ctx, OadlVar *pSelf);

Returns self in pSelf
OADL_GetPublic
typedef int (*OADL_GetPublic_fp)(void *ctx, OadlVar *pRes, OadlVar obj, OadlVar pub);

Returns, in pRes, the value of public property pub of object obj
OADL_SetPublic
typedef int (*OADL_SetPublic_fp)(void *ctx, OadlVar obj, OadlVar pub, OadlVar v);

Sets the public property pub of object obj to v
OADL_FindGlobal
typedef int (*OADL_FindGlobal_fp)(void *ctx, OadlVar *pRes, const char *name);

Looks up name in the OADL global symbol talbe and returns the result in pRes
OADL_GetGlobal
typedef int (*OADL_GetGlobal_fp)(void *ctx, OadlVar *pRes, int idx);

Gets the global variable at index idx and puts it in pRes.
OADL_SetGlobal
typedef int (*OADL_SetGlobal_fp)(void *ctx, int idx, OadlVar val);

Sets the global variable at index idx to val.
OADL_AllocMach
typedef int (*OADL_AllocMach_fp)(void *ctx, void **pRes, int stackAlloc);

Creates an OADL Machine capable of calling procedures immediately. It allocates stackAlloc entries for the initial size of the execution stack.
OADL_CallProc
typedef int (*OADL_CallProc_fp)(void *ctx, OadlVar *pRes, int typ, void *mach, OadlVar obj, OadlVar proc, int nargs, const OadlVar *args);

Makes an immediate or deferred call to the OADL procedure whose value is proc, or to the method whose value is obj.proc if obj is not nil (proc must be of type Public in that case). Returns 1 on success and 0 on failure.

The typ parameter defines how the call will be executed:

If typ is OADL_CALL_IMMEDIATE, an immediate call is performed. The mach parameter must be a machine allocated by OADL_AllocMach(). OADL_CallProc() will not return until the OADL procedure returns. The return value of the immediate call will be placed in the OadlVar pointed to by the pRes parameter. Most OADL procedures allocate heap objects. This may trigger a GC cycle which will invalidate any cached heap pointers. The calling routine must revalidate any pointers to heap objects after OADL_CallProc() returns.

If typ is OADL_CALL_DEFERRED, a deferred call is scheduled. A deferred call is a call placed on the current executing machine's call stack. OADL_CallProc() will return immediately after scheduling the call. Note that deferred calls do not return any value to the calling context. This allows multiple deferred calls to be scheduled simultaneously without overflowing the stack with unused results. Since the call is deferred, no heap allocations will occur at the time of the OADL_CallProc() call. If an Extern proc implementation defers one or more calls, it must return OADL_CALL_DEFERRED to OADL.

If typ is OADL_CALL_CHAINED, a chained call is scheduled. A chained call is also a deferred call; however, its result is returned to the calling context (which means that only one call may be chained). An Extern proc implementation must return OADL_CALL_CHAINED to OADL if it schedules a chained call.
OADL_CreateArray
typedef int (*OADL_CreateArray_fp)(void *ctx, OadlVar *pRes, OADL_ArrayInfo *pArr);

Creates an array with parameters taken from OADL_ArrayInfo *pArr. Returns the resulting array in *pRes. This does allocate heap memory which could trigger a GC and invalidate any cached heap pointers.

Note that pArr->ArrData and pArr->ArrStrides are ignored unless OADL_ARRAY_USER_DATA is specified in pArr->ArrFlags.

pArr->ArrSize and pArr->ElemSize are always ignored, as they are calculated from the input in pArr->ArrType and pArr->ArrShape.

The pArr structure is updated with the actual array created.
OADL_MakePointer
typedef int (*OADL_MakePointer_fp)(void *ctx, OadlVar *pRes, void *ptr);

Creates a new Pointer value from the given native ptr
OADL_DelPointer
typedef int (*OADL_DelPointer_fp)(void *ctx, OadlVar v);

Deletes the native Pointer variable v
OADL_FindPointer
typedef int (*OADL_FindPointer_fp)(void *ctx, void **pRes, OadlVar v);

Returns the native pointer *pRes associated with the Pointer v
OADL_GetPubName
typedef int (*OADL_GetPubName_fp)(void *ctx, char **pRes, OadlVar v);

Returns the name of the Public v. Same as pubname(v)
OADL_GetExtName
typedef int (*OADL_GetExtName_fp)(void *ctx, char **pRes, OadlVar v);

Returns the name of the Extern v.
OADL_GetObjName
typedef int (*OADL_GetObjName_fp)(void *ctx, char **pRes, OadlVar v);

Returns the name of the object v. Same as objname(v)
OADL_fgets
typedef int (*OADL_fgets_fp)(void *ctx, OadlVar *pRes, OadlVar f);

Reads a string from the OADL file f returning the resulting OADL String or WideString object in *pRes
OADL_fgetc
typedef int (*OADL_fgetc_fp)(void *ctx, int *pRes, OadlVar f);

Reads a UTF8 character from the OADL file f and returns the character as *pRes (note - returns the int, not the OadlVar)
OADL_fputc
typedef int (*OADL_fputc_fp)(void *ctx, OadlVar f, int c);

Puts the (possibly wide) character c into OADL file f
OADL_fputs
typedef int (*OADL_fputs_fp)(void *ctx, OadlVar f, const char *s, int len);

Puts the ASCII character string s into OADL file f
OADL_wfputs
typedef int (*OADL_wfputs_fp)(void *ctx, OadlVar f, const OADL_U32 *s, int len);

Puts the 32-bit Unicode character string s into OADL file f
OADL_ungetc
typedef int (*OADL_ungetc_fp)(void *ctx, OadlVar f, int c);

Pushes the character c back onto OADL file f to be returned by subsequent UTF8 reads
OADL_fopen
typedef int (*OADL_fopen_fp)(void *ctx, OadlVar *pRes, const char *name, const char *access);

Opens a UTF8 stream and returns the File result in *pRes
OADL_fclose
typedef int (*OADL_fclose_fp)(void *ctx, OadlVar f);

Close the OADL file f
OADL_fflush
OADL_fseek
OADL_ftell
OADL_fread
OADL_fwrite
OADL_feof
OADL_ferror
OADL_clearerr
typedef int (*OADL_fflush_fp)(void *ctx, OadlVar f);
typedef OADL_64 (*OADL_fseek_fp)(void *ctx, OadlVar f, OADL_64 offs, int seekType);
typedef OADL_64 (*OADL_ftell_fp)(void *ctx, OadlVar f);
typedef int (*OADL_fread_fp)(void *ctx, void *p, int n, int s, OadlVar f);
typedef int (*OADL_fwrite_fp)(void *ctx, void *p, int n, int s, OadlVar f);
typedef int (*OADL_feof_fp)(void *ctx, OadlVar f);
typedef int (*OADL_ferror_fp)(void *ctx, OadlVar f);
typedef int (*OADL_clearerr_fp)(void *ctx, OadlVar f);

Other OADL equivalents to stdio routines. A program which uses OADL_fopen must use these instead of real stdio routines.
OADL_ucCharType
typedef int (*OADL_ucCharType_fp)(int wch)

Determines the Unicode Character Type from a given WideChar value between 0 and 0x11000, inclusive. Returns -1 on error. See unicode.h for possible character types.
OADL_ucToUpper(int wch)
OADL_ucToLower(int wch)
typedef int (*OADL_ucToUpper_fp)(int wch)
typedef int (*OADL_ucToLower_fp)(int wch)

Converts a WideChar value betwen 0 and 0x110000, inclusive. Returns the original character if no single-character conversion if found.
OADL_utf8_bytelen
typedef int (*OADL_utf8_bytelen_fp)(void *ctx, int *pRes, OADL_U32 *src, int n);

Returns the number of bytes that would be required to encode the 32-bit string src
OADL_utf8_strlen
typedef int (*OADL_utf8_strlen_fp)(void *ctx, int *pRes, unsigned char *src, int n);

Returns (in *pRes) the number of Unicode characters encoded by the n chars of UTF8 string src
OADL_utf8_strcpy
typedef int (*OADL_utf8_strcpy_fp)(void *ctx, unsigned char *dst, OADL_U32 *src, int n);

Copies the n-character 32-bit string src into the UTF8 string dst which must have enough space allocated to hold the complete string.
OADL_PushIntrinsic
typedef int (*OADL_PushIntrinsic_fp)(void *ctx)

Increments the intrinsic depth. Returns 1 on success, 0 if an error occurred.
OADL_PopIntrinsic
typedef int (*OADL_PopIntrinsic_fp)(void *ctx)

Decrements the intrinsic depth. Returns 1 on success, 0 if an error occurred.
OADL_AddProc
typedef int (*OADL_AddProc_fp)(void *ctx, OadlVar *pVar, const OADL_U8 instrs[], int numInst)

Adds a new intrinsic procedure based on the numInst OADL bytecodes found in the instrs array. The instrs / numInst parameters are typically obtained by using the cvtintr utility. The resulting intrinsic is returned in *pVar.
OADL_RegComp
typedef int (*OADL_RegComp_fp)(void *ctx, void **pRE, char *pattStr, int options);

Compiles the PCRE pattern pattStr into *pRE for subsequent use with OADL_RegEx(). Options are standard PCRE options, found in oadlpcre.h .
OADL_RegEx
typedef int (*OADL_RegEx_fp)(void *ctx, int *pRes, void *re, char *str, int first, int len, int options, int patOffs[], int patOffSize);

Executes the PCRE reg exp. machine with compiled expression RE. The matches are returned in patOffs. patOffs will be an array of begin,end+1 pairs indicating the captured substrings, with patOffs[0] and patOffs[1] indicating the entire match. first and len indicate the substring which is to be matched. Options are PCRE options, from the list found in oadlpcre.h . The number of pairs is returned in *pRes.
OADL_RegFree
typedef int (*OADL_RegFree_fp)(void *ctx, void *re);

Frees a PCRE regular expression previously created by OADL_RegComp()
OADL_malloc
OADL_realloc
OADL_free
typedef int (*OADL_malloc_fp)(void *ctx, void **pRes, int nBytes);
typedef int (*OADL_realloc_fp)(void *ctx, void **pRes, void *ptr, int nBytes);
typedef int (*OADL_free_fp)(void *ctx, void *ptr);

Just like libc malloc/realloc/free but with OADL debugging hooks. Also more likely to work despite any interesting details about how malloc pools are implemented with shared libraries.
OADL_AllocScratch
typedef int (*OADL_AllocScratch_fp)(void *ctx, void **pRes, int nBytes);

Allocates scratch space that is only valid until the next OADL_AllocScratch. There is no need to free this memory; it is recycled by clients of OADL_AllocScratch
OADL say() and read() replacement
typedef int (*OADL_GetChar_fp)(void *ctx, void *fp);
typedef void (*OADL_SayChar_fp)(void *ctx, int wch);
typedef void (*OADL_SayStr_fp)(void *ctx, char *s, int n);
typedef void (*OADL_SayLStr_fp)(void *ctx, OADL_U32 *s, int n);

These function pointers types are used by OADL built-in input and output routines, and can be updated via OADL_IoProcs(), below. Note that these functions are NOT queryable via the normal OADL_FindProc mechanism; use the OADL_GetIoProcs() function to get them instead.
OADL_IoProcs
typedef int (*OADL_IoProcs_fp)(void *ctx, OADL_GetChar_fp getChar, OADL_SayChar_fp sayChar, OADL_SayStr_fp sayStr, OADL_SayLStr_fp sayLStr);

If the extern library wants to hook/intercept say() and read(), it should call this routine with the four entry points specified.
OADL_GetIoProcs
typedef int (*OADL_GetIoProcs_fp)(void *ctx, OADL_GetChar_fp *pGetChar, OADL_SayChar_fp *pSayChar, OADL_SayStr_fp *pSayStr, OADL_SayLStr_fp *pSayLStr);

Retrieve the current IO procs
OADL_ArrayIncr
int OADL_ArrayIncr(int offs, int rank, const OADL_U32 strides[], const OADL_U32 shape[], OADL_U32 index[])

Increment a multidimensional array index and return the new offset to the current element. Note that a scalar may be aliased as an array by using a rank of 0. Note also that the proper offset to the next element (based on the contents of index[]) is computed if an offs of 0 is consistently provided in a loop over all elements.

Inputs:

offs - Current offset (start at 0)
rank - Rank (dimensionality) of array
strides[] - Array of strides, from OADL_ArrayInfo
shape[] - Shape of array, from OADL_ArrayInfo
index[] - Current indices of array (start at {0})

Note that this is not a function pointer which is queried from OADL; rather, it is an inline function. Updates the index[] array and returns an updated offs

Simple External Library Example

The following is a very simple external library implementation. It can be compiled and used with the using extern example from the chapter on External procedures. The example should be compiled as follows:

Linux .so
clang -x c++ --std=c++11 -fPIC -c libfoo.c -I../../INCLUDE
clang++ --std=c++11 -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1 libfoo.o
Cygwin .dll
gcc -x c++ --std=c++11 -c libfoo.c -I../../INCLUDE
g++ -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1 libfoo.o -lc
Windows .dll
cl /c libfoo.c /I../../INCLUDE /DWIN32
link /dll libfoo.obj /out:libfoo.dll
MacOS .dylib
clang -x c++ --std=c++11 -c libfoo.c -I../../INCLUDE
clang++ --std=c++11 -dynamiclib -o libfoo.dylib libfoo.o -lc++
/*
 * Copyright (c) 2024 Ross Cunniff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifdef WIN32
#include <windows.h>
#endif

#include <stdio.h>

#include "oadlvar.h"
#include "oadlsys.h"

static OADL_GetArrayInfo_fp OADL_GetArrayInfo;
static OADL_AddExtern_fp OADL_AddExtern;
static OADL_TypeOf_fp OADL_TypeOf;

static int foo_bar(void *ctx, OadlVar *pRes, int nargs, const OadlVar *args)
{
    printf("foo::bar!\n");
    *pRes = OADL_NIL;
    return 1;
}

/* tables of extern functions defined */
static struct {
    const char *name;
    OADL_Extern_fp func;
} funcList[] = {
    { "foo::bar",       foo_bar },
    { 0, 0 }
};

/* table of OADL exports needed */
static struct {
    const char *name;
    OADLproc *func;
} oadlCB[] = {
    { "OADL_AddExtern",    (OADLproc *) &OADL_AddExtern },
    { "OADL_TypeOf",       (OADLproc *) &OADL_TypeOf },
    { "OADL_GetArrayInfo", (OADLproc *) &OADL_GetArrayInfo },
    { 0, 0 }
};

#ifdef __cplusplus
extern "C" {
#endif

#ifdef WIN32
__declspec(dllexport)
#endif
int OADL_libfoo(void *ctx, OADL_FindProc_fp findProc)
{
    int i;

    /* Get the OADL exports needed to run */
    for (i = 0; oadlCB[i].name; i++) {
        if (!findProc(ctx, oadlCB[i].name, OADL_SYS_VER, oadlCB[i].func)) {
            return 0;
        }
    }

    /* Define the extern routines */
    for (i = 0; funcList[i].name; i++) {
        if (!OADL_AddExtern(ctx, funcList[i].name, funcList[i].func)) {
            return 0;
        }
    }

    return 1;
}

#ifdef __cplusplus
}
#endif

#ifdef WIN32
// DLL entry function (called on load, unload, ...)
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
    return TRUE;
}
#endif

Token state machine

Although much of OADL lexical analysis can be implemented via simple switch statements and single-character lookahead, distinguishing identifiers (names), integer constants, hexadecimal constants, and floating point constants from each other is more complicated. The lex program given in this document does not implement it fully correctly - it does not handle embedded underscores in integer/hex/float constants. The following picture shows a complete deterministic finite-state automaton (DFA) which will recognize these tokens. States with double circles around them are legitimate final states (and each is labelled with the type of the token recognized at that state). Transitions are only labelled with upper-case letters; lower-case letters follow the same transitions. Finally, the character "u" represents the ASCII underline character '_'. The state numbers correlate exactly to the DFA in OADL's reference implementation. In non-terminal states, any other character than those given in the outbound transitions is a lexical error condition.

Picture of OADL token
DFA

The token state machine is directly implemented in C by tokmach.h and tokmach.c . The state numbers correspond directly to the state diagram.

Back to Glossary

Continue to Lex / Yacc Grammar

Return to Introduction