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:
24-bit variables - 0x02 through 0x1F
|
|
|
|
Ushort, Short - 0x38, 0x39
|
|
|
|
|
These pictures document the actual bit layout of the various types
in a 64-bit implementation:
32-bit variables - 0x03 through 0x1F
|
|
|
Ushort, Short - 0x38, 0x39
|
|
|
|
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.

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