mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
Lookahead cleanup. Start work on throws. Fix try expressions. Beginning work on initializer. Some fixes on LLVM types. Moving more target info into C3. Moving alignment into types Introducing a meta type (incomplete). Start work on macros. Splitting '@' from macro name.
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@@ -52,4 +52,9 @@ Mkfile.old
|
||||
dkms.conf
|
||||
cmake-build-debug/
|
||||
|
||||
.idea/
|
||||
.idea/
|
||||
/resources/grammar.tab.c
|
||||
/resources/grammar.vcg
|
||||
/resources/lex.yy.c
|
||||
/resources/y.tab.c
|
||||
/resources/y.tab.h
|
||||
|
||||
@@ -84,7 +84,7 @@ add_executable(c3c
|
||||
src/compiler/llvm_codegen_type.c
|
||||
src/compiler/llvm_codegen_function.c
|
||||
src/build/builder.c
|
||||
src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c)
|
||||
src/utils/toml.c src/build/project.c src/build/build_internal.h src/compiler/sema_name_resolution.c src/target_info/target_info.c)
|
||||
|
||||
target_compile_options(c3c PRIVATE -Wimplicit-int -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ void comment(void);
|
||||
[_]*[A-Z]{UA}* { count(); return(CONST_IDENT); }
|
||||
[_]*[A-Z]{UA}*[a-z]{AN}* { count(); return(TYPE_IDENT); }
|
||||
[_]*[a-z]{AN}* { count(); return(IDENT); }
|
||||
@{L}+[!]? { count(); return(AT_IDENT); }
|
||||
${L}+ { count(); return(CT_IDENT); }
|
||||
#{L}+ { count(); return(HASH_IDENT); }
|
||||
0[xX]{H}+{IS}? { count(); return(CONSTANT); }
|
||||
@@ -119,6 +118,7 @@ L?\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
|
||||
":" { count(); return(':'); }
|
||||
"=" { count(); return('='); }
|
||||
"(" { count(); return('('); }
|
||||
"@" { count(); return(AT); }
|
||||
")" { count(); return(')'); }
|
||||
("[") { count(); return('['); }
|
||||
("]") { count(); return(']'); }
|
||||
|
||||
268
resources/examples/acornvm/avm_array.c3
Normal file
268
resources/examples/acornvm/avm_array.c3
Normal file
@@ -0,0 +1,268 @@
|
||||
module acorn::arr;
|
||||
|
||||
/** Implements arrays: variable-sized, ordered collections of Values (see avm_array.h)
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
/* Return a new Array, allocating len slots for Values. */
|
||||
func Value new(Value th, Value *dest, Value type, AuintIdx len)
|
||||
{
|
||||
// Create an array object
|
||||
ArrInfo* val = @cast(mem::new(th, ArrEnc, sizeof(ArrInfo), ArrInfo*);
|
||||
val.flags1 = 0; // Initialize Flags1 flags
|
||||
val.type = type;
|
||||
val.avail = len;
|
||||
val.size = 0;
|
||||
val.arr = nil;
|
||||
if (len > 0) mem::reallocvector(th, val.arr, 0, len, Value);
|
||||
return *dest = @cast(val, Value);
|
||||
}
|
||||
|
||||
/* Return a new Array, allocating len slots for Values. */
|
||||
func Value newClosure(Value *th, Value *dest, Value type, AuintIdx len)
|
||||
{
|
||||
// Create an array object
|
||||
ArrInfo* val = @cast(mem::new(th, ArrEnc, sizeof(ArrInfo), ArrInfo*);
|
||||
val.flags1 = TypeClo; // Initialize Flags1 flags
|
||||
val.type = type;
|
||||
val.avail = len;
|
||||
val.size = 0;
|
||||
val.arr = NULL;
|
||||
if (len > 0) mem::reallocvector(th, val.arr, 0, len, Value);
|
||||
return *dest = @cast(val, Value);
|
||||
}
|
||||
|
||||
/* Return 1 if the value is an Array, otherwise 0 */
|
||||
func int Value.isArr(Value* val)
|
||||
{
|
||||
return val.isEnc(ArrEnc);
|
||||
}
|
||||
|
||||
/* Return 1 if the value is an Array, otherwise 0 */
|
||||
func int Value.isClosure(Value* val)
|
||||
{
|
||||
return val.isEnc(ArrEnc) && arr_info(val)->flags1 & TypeClo;
|
||||
}
|
||||
|
||||
private func ArrInfo.fill(ArrInfo* a, AuintIdx start, AuintIdx end, Value value) @inline
|
||||
{
|
||||
for (AuintIdx i = start; i < end; i++) a.arr[i] = value;
|
||||
}
|
||||
|
||||
/* Ensure array has room for len Values, allocating memory as needed.
|
||||
* Allocated space will not shrink. Changes nothing about array's contents. */
|
||||
func void makeRoom(Value th, Value arr, AuintIdx len)
|
||||
{
|
||||
ArrInfo* a = arr_info(arr);
|
||||
if (len > a.avail)
|
||||
{
|
||||
mem::gccheck(th); // Incremental GC before memory allocation events
|
||||
mem::reallocvector(th, a.arr, a.avail, len, Value);
|
||||
a.avail = len;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of elements in the array, growing it if needed.
|
||||
* If less than current number array size, array is not shrunk.
|
||||
*/
|
||||
func void setSize(Value th, Value arr, AuintIdx len)
|
||||
{
|
||||
ArrInfo* a = arr_info(arr);
|
||||
AuintIdx size = arr_size(arr);
|
||||
if (len > size) makeRoom(arr, len);
|
||||
arr_size(arr) = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force allocated and used array to a specified size, truncating
|
||||
* or expanding as needed. Growth space is initialized to aNull.
|
||||
* @require val.isArr()
|
||||
*/
|
||||
func void forceSize(Value th, Value val, AuintIdx len)
|
||||
{
|
||||
ArrInfo *arr = arr_info(val);
|
||||
|
||||
// Expand or contract allocation, as needed
|
||||
if (len != arr->avail)
|
||||
{
|
||||
mem::gccheck(th); // Incremental GC before memory allocation events
|
||||
mem::reallocvector(th, arr.arr, 0, len, Value);
|
||||
arr.avail = len;
|
||||
}
|
||||
|
||||
// Fill growth area with nulls
|
||||
arr.fill(arr.size, len, aNull);
|
||||
arr.size = len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the value in array at specified position.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func Value get(Value th, Value arr, AuintIdx pos)
|
||||
{
|
||||
ArrInfo* a = arr_info(arr);
|
||||
return pos >= a.size ? aNull : a.arr[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Put val into the array starting at pos.
|
||||
* This can expand the size of the array.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void set(Value th, Value arr, AuintIdx pos, Value val)
|
||||
{
|
||||
ArrInfo* a = arr_info(arr);
|
||||
|
||||
// Grow, if needed
|
||||
if (pos + 1 >= a.avail) makeRoom(th, arr, pos + 1);
|
||||
// Fill with nulls if pos starts after end of array
|
||||
if (pos >= a.size) a.fill(a.size, pos, aNull);
|
||||
// Perform copy
|
||||
a.arr[pos] = val;
|
||||
mem::markChk(th, arr, val);
|
||||
// If final fill is past array size, reset size higher
|
||||
if (pos + 1 >= a.size) a.size = pos + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append val to the end of the array (increasing array's size).
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void add(Value th, Value arr, Value val)
|
||||
{
|
||||
ArrInfo *a = arr_info(arr);
|
||||
AuintIdx sz = arr_size(arr);
|
||||
|
||||
// Double size, if more space is needed
|
||||
if (sz + 1 > a.avail) makeRoom(th, arr, sz + (sz > 0 ? sz : 1));
|
||||
|
||||
// Append value
|
||||
a.arr[sz] = val;
|
||||
mem::markChk(th, arr, val);
|
||||
a.size++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate n copies of val into the array starting at pos.
|
||||
* This can expand the size of the array.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void repeat(Value th, Value arr, AuintIdx pos, AuintIdx n, Value val)
|
||||
{
|
||||
ArrInfo* a = arr_info(arr);
|
||||
|
||||
// Prevent unlikely overflow
|
||||
if (pos +% n < n) return;
|
||||
|
||||
// Grow, if needed
|
||||
if (pos + n >= a.avail) makeRoom(th, arr, pos + n);
|
||||
// Fill with nulls if pos starts after end of array
|
||||
if (pos >= a.size) a.fill(a.size, pos, aNull);
|
||||
// Perform repeat copy
|
||||
a.fill(pos, pos + n, val);
|
||||
mem::markChk(th, arr, val); // only need to check once
|
||||
// If final fill is past array size, reset size higher
|
||||
if (pos + n >= a.size) a.size = pos + n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete n values out of the array starting at pos.
|
||||
* All values after these are preserved, essentially shrinking the array.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void del(Value th, Value arr, AuintIdx pos, AuintIdx n)
|
||||
{
|
||||
ArrInfo *a = arr_info(arr);
|
||||
|
||||
// Nothing to delete (or overflow)
|
||||
if (pos >= a.size || pos +% n < n) return;
|
||||
|
||||
// Copy high end down over deleted portion
|
||||
if (pos + n < a.size)
|
||||
{
|
||||
memmove(&a.arr[pos], &a.arr[pos + n], (a.size - pos - n) * sizeof(Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
n = a.size - pos; // Clip n to end of array, if too large
|
||||
}
|
||||
a.size -= n; // Adjust size accordingly
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert n copies of val into the array starting at pos, expanding the array's size.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void ins(Value th, Value arr, AuintIdx pos, AuintIdx n, Value val)
|
||||
{
|
||||
ArrInfo *a = arr_info(arr);
|
||||
|
||||
// Prevent unlikely overflow
|
||||
if (a.size +% n < n) return;
|
||||
|
||||
// Ensure array is large enough
|
||||
if (n + a.size >= a.avail) makeRoom(th, arr, n + a.size);
|
||||
|
||||
// Move values up to make room for insertions
|
||||
if (pos <= a.size) memmove(&a,arr[pos+n], &a.arr[pos], (a.size - pos) * sizeof(Value));
|
||||
a.size += n;
|
||||
|
||||
// Do any needed null fill plus the repeat copy
|
||||
rpt(th, arr, pos, n, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy n2 values from arr2 starting at pos2 into array, replacing the n values in first array starting at pos.
|
||||
* This can increase or decrease the size of the array. arr and arr2 may be the same array.
|
||||
* @require arr.isArr()
|
||||
*/
|
||||
func void sub(Value th, Value arr, AuintIdx pos, AuintIdx n, Value arr2, AuintIdx pos2, AuintIdx n2)
|
||||
{
|
||||
ArrInfo *a = arr_info(arr);
|
||||
|
||||
// Prevent unlikely overflow
|
||||
if ((a.size - n) +% n2 < n2) return;
|
||||
|
||||
// Ensure array is large enough
|
||||
if (a.size - n + n2 > a.avail) makeRoom(th, arr, a.size - n + n2);
|
||||
|
||||
// Adjust position of upper values to make precise space for copy
|
||||
if (n != n2 && pos < a.size) memmove(&a.arr[pos + n2], &a.arr[pos + n], (a.size - pos - n) * sizeof(Value));
|
||||
|
||||
// Fill with nulls if pos starts after end of array
|
||||
if (pos > a->size) a.fill(a.size, pos, aNull);
|
||||
|
||||
// Perform copy
|
||||
if (arr2 && arr2.isPtr()) memmove(&a.arr[pos], &arr_info(arr2).arr[pos2], n2 * sizeof(Value));
|
||||
for (AintIdx i = n2 - 1; i >= 0; i--)
|
||||
{
|
||||
mem::markChk(th, arr, a.arr[pos+i]);
|
||||
}
|
||||
|
||||
a.size += n2 - n;
|
||||
}
|
||||
|
||||
/* Serialize an array's contents to indented text */
|
||||
func void serialize(Value th, Value str, int indent, Value arr)
|
||||
{
|
||||
// TODO
|
||||
ArrInfo *a = arr_info(arr);
|
||||
AuintIdx sz = arr_size(arr);
|
||||
string type = arr_info(arr).flags1 & TypeClo ? "+Closure" : "+List";
|
||||
|
||||
strAppend(th, str, typ, strlen(typ));
|
||||
for (AuintIdx i = 0; i < sz; i++)
|
||||
{
|
||||
strAppend(th, str, "\n", 1);
|
||||
int ind = indent+1;
|
||||
while (ind--) strAppend(th, str, "\t", 1);
|
||||
serialize(th, str, indent+1, a.arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
164
resources/examples/acornvm/avm_memory.c3
Normal file
164
resources/examples/acornvm/avm_memory.c3
Normal file
@@ -0,0 +1,164 @@
|
||||
/** Memory allocation and garbage collection
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
module acorn::mem;
|
||||
|
||||
/** Garbage-collection savvy memory malloc, free and realloc function
|
||||
* - If nsize==0, it frees the memory block (if non-NULL)
|
||||
* - If ptr==NULL, it allocates a new uninitialized memory block
|
||||
* - Otherwise it changes the size of the memory block (and may move its location)
|
||||
* It returns the location of the new block or NULL (if freed). */
|
||||
func void* gcrealloc(Value th, void *block, Auint osize, Auint nsize)
|
||||
{
|
||||
Value newblock;
|
||||
|
||||
// Check consistency of block and osize (both must be null or specified)
|
||||
Auint realosize = block ? osize : 0;
|
||||
assert((realosize == 0) == (block == nil));
|
||||
|
||||
// Allocate/free/resize the memory block
|
||||
newblock = @cast(frealloc(block, nsize), Value);
|
||||
|
||||
$if (defined(MEMORYLOG))
|
||||
{
|
||||
if (nsize==0)
|
||||
{
|
||||
vmLog("Freeing %p size %d", block, osize);
|
||||
}
|
||||
else
|
||||
{
|
||||
vmLog("Allocating %p from %p for %d", newblock, block, nsize);
|
||||
}
|
||||
}
|
||||
|
||||
// If alloc or resize failed, compact memory and try again
|
||||
if (newblock == nil && nsize > 0)
|
||||
{
|
||||
// realloc cannot fail when shrinking a block
|
||||
gcfull(th, 1); // try to free some memory...
|
||||
newblock = @cast(frealloc(block, nsize), Value); // try again
|
||||
if (newblock == nil)
|
||||
{
|
||||
logSevere("Out of memory trying allocate or grow a memory block.");
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure it worked, adjust GC debt and return address of new block
|
||||
assert((nsize == 0) == (newblock == nil));
|
||||
vm(th).totalbytes += nsize - realosize;
|
||||
return newblock;
|
||||
}
|
||||
|
||||
func void* gcreallocv(Value th, void* block, Auint osize, Auint nsize, Auint esize)
|
||||
{
|
||||
// Ensure we are not asking for more memory than available in address space
|
||||
// If we do not do this, calculating the needed memory will overflow
|
||||
if (nsize+1 > ~((Auint)0) / esize)
|
||||
{
|
||||
logSevere("Out of memory trying to ask for more memory than address space has.");
|
||||
}
|
||||
return gcrealloc(th, block, osize*esize, nsize*esize);
|
||||
}
|
||||
|
||||
/** General-purpose memory malloc, free and realloc function.
|
||||
* - If size==0, it frees the memory block (if non-NULL)
|
||||
* - If block==NULL, it allocates a new uninitialized memory block
|
||||
* - Otherwise it changes the size of the memory block (and may move its location)
|
||||
* It returns the location of the new block or NULL (if freed).
|
||||
**/
|
||||
func void* frealloc(void* block, Auint size)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
free(block);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
return realloc(block, size);
|
||||
}
|
||||
}
|
||||
|
||||
macro type($type) @amalloc($type)
|
||||
{
|
||||
return @cast(mem_frealloc(NULL, sizeof($type)), $type);
|
||||
}
|
||||
|
||||
|
||||
/* Create a new pointer object (with given encoding and size) and add to front of *list. */
|
||||
MemInfo* new(Value th, int enc, Auint sz)
|
||||
{
|
||||
// Perform garbage collection before a memory allocation
|
||||
$if (defined(AVM_GCHARDMEMTEST))
|
||||
{
|
||||
// force a full GC to see if any unattached objects die
|
||||
if (vm(th).gcrunning) gcfull(th, 1);
|
||||
}
|
||||
$else
|
||||
{
|
||||
gccheck(th); // Incremental GC before memory allocation events
|
||||
}
|
||||
vm(th).gcnbrnew++;
|
||||
MemInfo* o = (MemInfo*) (char *) gcrealloc(th, nil, 0, sz);
|
||||
o.marked = vm(th).currentwhite & WHITEBITS;
|
||||
o.enctyp = enc;
|
||||
|
||||
// Use the standard list for collectable objects
|
||||
MemInfo **list = &vm(th).objlist;
|
||||
o.next = *list;
|
||||
*list = o;
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new pointer object (with given encoding and size).
|
||||
* Caller must add itself to its own private list
|
||||
*/
|
||||
func MemInfo* newnolink(Value th, int enc, Auint sz)
|
||||
{
|
||||
// Perform garbage collection before a memory allocation
|
||||
$if (defined(AVM_GCHARDMEMTEST))
|
||||
{
|
||||
// force a full GC to see if any unattached objects die
|
||||
if (vm(th)->gcrunning) gcfull(th, 1);
|
||||
}
|
||||
$else
|
||||
{
|
||||
gccheck(th); // Incremental GC before memory allocation events
|
||||
}
|
||||
vm(th)->gcnbrnew++;
|
||||
// Allocate and initialize
|
||||
MemInfo *o = (MemInfo*) (char *) gcrealloc(th, NULL, 0, sz);
|
||||
o.marked = vm(th)->currentwhite & WHITEBITS;
|
||||
o.enctyp = enc;
|
||||
return o;
|
||||
}
|
||||
|
||||
/* double size of vector array, up to limits */
|
||||
func void growaux_(Value th, void *block, AuintIdx *size, AuintIdx size_elems, AuintIdx limit)
|
||||
{
|
||||
void* newblock;
|
||||
AuintIdx newsize;
|
||||
// cannot double it?
|
||||
if (*size >= limit / 2)
|
||||
{
|
||||
// cannot grow even a little?
|
||||
if (*size >= limit) logSevere("Out of memory trying to grow a vector array.");
|
||||
newsize = limit; /* still have at least one free place */
|
||||
}
|
||||
else
|
||||
{
|
||||
newsize = (*size) * 2;
|
||||
// minimum size
|
||||
if (newsize < MINSIZEARRAY) newsize = MINSIZEARRAY;
|
||||
}
|
||||
newblock = gcreallocv(th, block, *size, newsize, size_elems);
|
||||
// update only when everything else is OK
|
||||
*size = newsize;
|
||||
return newblock;
|
||||
}
|
||||
|
||||
520
resources/examples/acornvm/avm_stack.c3
Normal file
520
resources/examples/acornvm/avm_stack.c3
Normal file
@@ -0,0 +1,520 @@
|
||||
module acorn::stack;
|
||||
import acorn::sym;
|
||||
|
||||
/** Implements the data stack that belongs to a thread.
|
||||
* A thread has one data stack which is an allocated array of Values, initialized to 'null'.
|
||||
*
|
||||
* The stack implementation is optimized for lean performance first, as its functions
|
||||
* are called several times for every method call. Therefore, stack indices are not checked for
|
||||
* validity (except when running in debug mode, where invalid indices generate exceptions).
|
||||
*
|
||||
* A current method's area of the data stack is bounded by pointers:
|
||||
* - th(th)->curmethod->begin points to the bottom (at 0 index)
|
||||
* - th(th)->stk_top points just above the last (top) value
|
||||
* - th(th)->curmethod->end points just above last allocated value on stack for method
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
|
||||
/* ****************************************
|
||||
HELPER MACROS
|
||||
***************************************/
|
||||
|
||||
/** Size of the method's stack area: base to top */
|
||||
func AintIdx stkSz(Value th) @inline
|
||||
{
|
||||
return th(th).stk_top - th(th).curmethod.begin;
|
||||
}
|
||||
|
||||
/** Is there room to increment stack top up by 1 and null it to ensure we do not mark it when making it available for a new value */
|
||||
#define stkCanIncTop(th) {assert((th(th)->stk_top+1 <= th(th)->curmethod->end) && "stack top overflow");*th(th)->stk_top=aNull;}
|
||||
|
||||
/** Point to current method's stack value at position i.
|
||||
* For a method: i=0 is self, i=1 is first parameter, etc. */
|
||||
func void Value.at(Value* th, AintIdx i) @inline
|
||||
{
|
||||
@assert_exp(i >= 0 && i < stkSz(th), "invalid stack index");
|
||||
return &th(*th).curmethod.begin[i];
|
||||
}
|
||||
|
||||
/* ****************************************
|
||||
INDEX-ONLY STACK MANIPULATION
|
||||
***************************************/
|
||||
|
||||
/* Retrieve the stack value at the index. Be sure 0<= idx < top.
|
||||
* Good for getting method's parameters: 0=self, 1=parm 1, etc. */
|
||||
func Value Value.getLocal(Value *th, AintIdx idx)
|
||||
{
|
||||
return *th.at(idx);
|
||||
}
|
||||
|
||||
/* Put the value on the stack at the designated position. Be sure 0<= idx < top. */
|
||||
func void Value.setLocal(Value th, AintIdx idx, Value val)
|
||||
{
|
||||
*th.at(idx) = val;
|
||||
mem::markChk(th, th, val);
|
||||
}
|
||||
|
||||
/* Copy the stack value at fromidx into toidx */
|
||||
func void Value.copyLocal(Value* th, AintIdx toidx, AintIdx fromidx)
|
||||
{
|
||||
*th.at(toidx) = *th.at(fromidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the value at index (shifting down all values above it to top)
|
||||
* @require stkSz(th) > 0
|
||||
*/
|
||||
func void Value.deleteLocal(Value* th, AintIdx idx)
|
||||
{
|
||||
Value* p = th.at(idx);
|
||||
memmove(p, p + 1, sizeof(Value)*(stkSz(th) - idx - 1));
|
||||
th(*th).stk_top--;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the popped value into index (shifting up all values above it)
|
||||
* @require stkSz(th) > 0
|
||||
*/
|
||||
func void Value.insertLocal(Value *th, AintIdx idx)
|
||||
{
|
||||
Value *p = th.at(idx);
|
||||
Value val = *(th(*th).stk_top - 1);
|
||||
memmove(p+1, p, sizeof(Value) * (stkSz(th) - idx - 1));
|
||||
*p = val;
|
||||
}
|
||||
|
||||
|
||||
/* ****************************************
|
||||
TOP-BASED STACK MANIPULATION
|
||||
***************************************/
|
||||
|
||||
/* Push a value on the stack's top */
|
||||
func Value Value.pushValue(Value* th, Value val)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
*th(*th).stk_top++ = val;
|
||||
mem::markChk(th, th, val); // Keep, if marked for deletion?
|
||||
return val;
|
||||
}
|
||||
|
||||
/* Push and return the corresponding Symbol value for a 0-terminated c-string */
|
||||
func Value Value.pushSym(Value* th, string str)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return sym::newSym(*th, th(*th).stk_top++, str);
|
||||
}
|
||||
|
||||
/* Push and return the corresponding Symbol value for a byte sequence of specified length */
|
||||
func Value Value.pushSyml(Value th, string str)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return sym::newSym(*th, th(*th).stk_top++, str);
|
||||
}
|
||||
|
||||
/* Push and return a new String value */
|
||||
Value pushString(Value th, Value type, const char *str)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newStr(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeTextm) : type, str, strlen(str));
|
||||
}
|
||||
|
||||
/* Push and return a new String value of size with a copy of str bytes */
|
||||
Value pushStringl(Value th, Value type, const char *str, AuintIdx size) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newStr(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeTextm) : type, str, size);
|
||||
}
|
||||
|
||||
/* Push and return a new typed CData value of size */
|
||||
Value pushCData(Value th, Value type, unsigned char cdatatyp, AuintIdx size, unsigned int extrahdr) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newCData(th, th(th)->stk_top++, type, cdatatyp, size, extrahdr);
|
||||
}
|
||||
|
||||
/* Push and return a new Array value */
|
||||
Value pushArray(Value th, Value type, AuintIdx size) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newArr(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeListm) : type, size);
|
||||
}
|
||||
|
||||
/* Push and return a new Closure value.
|
||||
Size is get and set methods plus closure variables, all pushed on stack */
|
||||
Value pushClosure(Value th, AintIdx size) {
|
||||
Value closure;
|
||||
assert(size>=2 && stkSz(th)>=size); // All closure variables should be on stack
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
closure = newClosure(th, th(th)->stk_top++, vmlit(TypeClom), size);
|
||||
// Copy closure variables into closure
|
||||
for (int i=0; i<size; i++)
|
||||
arrSet(th, closure, i, *(th(th)->stk_top-size-1+i));
|
||||
*(th(th)->stk_top-size-1) = closure; // move created closure down
|
||||
th(th)->stk_top -= size; // pop off closure variables
|
||||
return closure;
|
||||
}
|
||||
|
||||
/* Push a closure variable. */
|
||||
Value pushCloVar(Value th, AuintIdx idx) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
Value closure = *th(th)->curmethod->methodbase;
|
||||
return *th(th)->stk_top++ = (isArr(closure) && idx<getSize(closure))? arrGet(th, closure, idx) : aNull;
|
||||
}
|
||||
|
||||
/* Pop a value into a closure variable. */
|
||||
void popCloVar(Value th, AuintIdx idx) {
|
||||
assert(stkSz(th)>0); // Must be at least one value to remove!
|
||||
Value closure = *th(th)->curmethod->methodbase;
|
||||
if (isArr(closure) && idx<getSize(closure))
|
||||
arrSet(th, closure, idx, *--th(th)->stk_top);
|
||||
else
|
||||
--th(th)->stk_top;
|
||||
}
|
||||
|
||||
/* Push and return a new hashed table value */
|
||||
Value pushTbl(Value th, Value type, AuintIdx size) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newTbl(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeIndexm) : type, size);
|
||||
}
|
||||
|
||||
/* Push and return a new Type value */
|
||||
Value pushType(Value th, Value type, AuintIdx size) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newType(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeObject) : type, size);
|
||||
}
|
||||
|
||||
/* Push and return a new Mixin value */
|
||||
Value pushMixin(Value th, Value type, Value inheritype, AuintIdx size) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newMixin(th, th(th)->stk_top++, (type==aNull)? vmlit(TypeObject) : type, inheritype, size);
|
||||
}
|
||||
|
||||
/* Push and return the value for a method written in C */
|
||||
Value pushCMethod(Value th, AcMethodp meth)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newCMethod(th, th(th)->stk_top++, meth);
|
||||
}
|
||||
|
||||
/* Push and return the VM's value */
|
||||
Value pushVM(Value th) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return *th(th)->stk_top++ = vm(th);
|
||||
}
|
||||
|
||||
/* Push and return a new CompInfo value, compiler state for an Acorn method */
|
||||
Value pushCompiler(Value th, Value src, Value url) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return newCompiler(th, th(th)->stk_top++, src, url);
|
||||
}
|
||||
|
||||
/* Push a value's serialized Text */
|
||||
Value pushSerialized(Value th, Value val) {
|
||||
Value serstr = pushStringl(th, aNull, NULL, 16);
|
||||
serialize(th, serstr, 0, val);
|
||||
return serstr;
|
||||
}
|
||||
|
||||
/* Push and return the value of the named member of the table found at the stack's specified index */
|
||||
Value pushTblGet(Value th, AintIdx tblidx, const char *mbrnm) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
Value tbl = *stkAt(th, tblidx);
|
||||
assert(isTbl(tbl));
|
||||
newSym(th, th(th)->stk_top++, mbrnm, strlen(mbrnm));
|
||||
return *(th(th)->stk_top-1) = tblGet(th, tbl, *(th(th)->stk_top-1));
|
||||
}
|
||||
|
||||
/* Put the local stack's top value into the named member of the table found at the stack's specified index */
|
||||
void popTblSet(Value th, AintIdx tblidx, const char *mbrnm) {
|
||||
assert(stkSz(th)>0); // Must be at least one value to remove!
|
||||
Value tbl = *stkAt(th, tblidx);
|
||||
assert(isTbl(tbl));
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
newSym(th, th(th)->stk_top++, mbrnm, strlen(mbrnm));
|
||||
tblSet(th, tbl, *(th(th)->stk_top-1), *(th(th)->stk_top-2));
|
||||
th(th)->stk_top -= 2; // Pop key & value after value is safely in table
|
||||
}
|
||||
|
||||
/* Push and return the value held by the uncalled property of the value found at the stack's specified index. */
|
||||
Value pushProperty(Value th, AintIdx validx, const char *propnm) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
Value val = *stkAt(th, validx);
|
||||
newSym(th, th(th)->stk_top++, propnm, strlen(propnm));
|
||||
return *(th(th)->stk_top-1) = getProperty(th, val, *(th(th)->stk_top-1));
|
||||
}
|
||||
|
||||
/* Store the local stack's top value into the uncalled property of the type found at the stack's specified index
|
||||
* Note: Unlike pushProperty, popProperty is restricted to the type being changed. */
|
||||
void popProperty(Value th, AintIdx typeidx, const char *mbrnm) {
|
||||
assert(stkSz(th)>0); // Must be at least one value to remove!
|
||||
Value tbl = *stkAt(th, typeidx);
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
newSym(th, th(th)->stk_top++, mbrnm, strlen(mbrnm));
|
||||
if (isType(tbl))
|
||||
tblSet(th, tbl, *(th(th)->stk_top-1), *(th(th)->stk_top-2));
|
||||
th(th)->stk_top -= 2; // Pop key & value after value is stored
|
||||
}
|
||||
|
||||
/* Push and return the value held by the perhaps-called property of the value found at the stack's specified index.
|
||||
* Note: This lives in between pushProperty (which never calls) and getCall (which always calls).
|
||||
* This calls the property's value only if it is callable, otherwise it just pushes the property's value. */
|
||||
Value pushGetActProp(Value th, AintIdx selfidx, const char *propnm) {
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
Value self = *stkAt(th, selfidx);
|
||||
newSym(th, th(th)->stk_top++, propnm, strlen(propnm));
|
||||
Value ret = *(th(th)->stk_top-1) = getProperty(th, self, *(th(th)->stk_top-1));
|
||||
|
||||
// If it is callable (e.g., a method), call it to get property value
|
||||
if (canCall(ret)) {
|
||||
// Finish setting up stack for call
|
||||
stkCanIncTop(th); /* Check if there is room for self */
|
||||
*(th(th)->stk_top++) = self;
|
||||
// Do the call, expecting (and returning) just one return value
|
||||
switch (canCallMorC(th(th)->stk_top-2)? callMorCPrep(th, th(th)->stk_top-2, 1, 0)
|
||||
: callYielderPrep(th, th(th)->stk_top-2, 1, 0)) {
|
||||
case MethodBC:
|
||||
methodRunBC(th);
|
||||
break;
|
||||
}
|
||||
ret = *(th(th)->stk_top-1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Store the local stack's top value into the perhaps-called property of the value found at the stack's specified index
|
||||
* Note: This lives in between popProperty (which never calls) and setCall (which always calls).
|
||||
* This calls the property's value only if it is a closure with a set method.
|
||||
* Otherwise, it sets the property's value directly if (and only if) self is a type. */
|
||||
void popSetActProp(Value th, AintIdx selfidx, const char *mbrnm) {
|
||||
assert(stkSz(th)>0); // Must be at least one value to remove!
|
||||
Value self = *stkAt(th, selfidx);
|
||||
stkCanIncTop(th); /* Check if there is room for symbol */
|
||||
newSym(th, th(th)->stk_top++, mbrnm, strlen(mbrnm));
|
||||
Value propval = getProperty(th, self, *(th(th)->stk_top-1));
|
||||
|
||||
// If it is callable (e.g., a method), call it to set property value
|
||||
if (canCall(propval)) {
|
||||
// Set up stack for call
|
||||
stkCanIncTop(th); /* Check if there is room for self */
|
||||
Value set = getFromTop(th, 1); // the value to set
|
||||
*(th(th)->stk_top-2) = propval;
|
||||
*(th(th)->stk_top-1) = self;
|
||||
*(th(th)->stk_top++) = set;
|
||||
// Do the set call, expecting (and returning) just one return value
|
||||
switch (canCallMorC(propval)? callMorCPrep(th, th(th)->stk_top-3, 1, 0)
|
||||
: callYielderPrep(th, th(th)->stk_top-3, 1, 0)) {
|
||||
case MethodBC:
|
||||
methodRunBC(th);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Only if self is a type, store value in property
|
||||
if (isType(self))
|
||||
tblSet(th, self, *(th(th)->stk_top-1), *(th(th)->stk_top-2));
|
||||
th(th)->stk_top -= 2; // Pop key & value
|
||||
}
|
||||
}
|
||||
|
||||
/* Push a copy of a stack's value at index onto the stack's top */
|
||||
func Value Value.pushLocal(Value* th, AintIdx idx)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return *th(*th).stk_top++ = th.getLocal(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop a value off the top of the stack
|
||||
* @require stkSz(th) > 0
|
||||
*/
|
||||
func Value Value.popValue()
|
||||
{
|
||||
return *--th(*th).stk_top;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pops the top value and writes it at idx. Often used to set return value
|
||||
* @require stkSz(th) > 0, idx >= 0, idx < stkSz(th) - 1
|
||||
*/
|
||||
func void Value.popLocal(Value* th, AintIdx idx)
|
||||
{
|
||||
th.setLocal(idx, *(th(*th).stk_top - 1));
|
||||
// Pop after value is safely in Global
|
||||
--th(*th).stk_top;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stack value at the index from top. Be sure 0<= idx < top.
|
||||
* @require idx >= 0, idx < stkSz(th)
|
||||
*/
|
||||
func Value Value.getFromTop(Value* th, AintIdx idx)
|
||||
{
|
||||
return *th.at(stkSz(th) - idx - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return number of values on the current method's stack
|
||||
*/
|
||||
func AuintIdx Value.getTop(Value* th)
|
||||
{
|
||||
return cast(stkSz(th), AuintIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* When index is positive, this indicates how many Values are on the method's stack.
|
||||
* This can shrink the stack or grow it (padding with 'null's).
|
||||
* A negative index removes that number of values off the top.
|
||||
*/
|
||||
func void Value.setTop(Value* th, AintIdx idx)
|
||||
{
|
||||
// TODO
|
||||
Value *base = th(*th).curmethod.begin;
|
||||
|
||||
// If positive, idx is the index of top value on stack
|
||||
if (idx >= 0)
|
||||
{
|
||||
assert((base + idx <= th(th)->stk_last) && "stack top overflow"); // Cannot grow past established limit
|
||||
while (th(th)->stk_top < base + idx)
|
||||
*th(th)->stk_top++ = aNull; // If growing, fill with nulls
|
||||
th(th)->stk_top = base + idx;
|
||||
}
|
||||
// If negative, idx is which Value from old top is new top (-1 means no change, -2 pops one)
|
||||
else {
|
||||
assert((-(idx) <= th(th)->stk_top - base) && "invalid new top");
|
||||
th(th)->stk_top += idx; // Adjust top using negative index
|
||||
}
|
||||
}
|
||||
|
||||
/* ****************************************
|
||||
GLOBAL VARIABLE ACCESS
|
||||
***************************************/
|
||||
|
||||
/**
|
||||
* Push and return the symbolically-named global variable's value
|
||||
* @require vm(*th).global.isTbl()
|
||||
**/
|
||||
func Value Value.pushGloVar(Value* th, string var)
|
||||
{
|
||||
// Check if there is room
|
||||
stkCanIncTop(th);
|
||||
Value val = sym::newSym(th, th(th).stk_top++, var);
|
||||
mem::markChk(th, th, val); /* Mark it if needed */
|
||||
return *(th(*th).stk_top - 1) = tbl::get(th, vm(th).global, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alter the symbolically-named global variable to have the value popped off the local stack
|
||||
* @require stkSz(th) > 0, vm(th).global.isTbl()
|
||||
**/
|
||||
func void Value.popGloVar(Value* th, string var)
|
||||
{
|
||||
// Check if there is room
|
||||
stkCanIncTop(th);
|
||||
Value val = sym::newSym(th, th(th).stk_top++, var);
|
||||
tbl::set(th, vm(th).global, *(th(th)->stk_top-1), *(th(th)->stk_top-2));
|
||||
th(*th).stk_top -= 2; // Pop key & value after value is safely in Global
|
||||
}
|
||||
|
||||
/* Push the value of the current process thread's global variable table. */
|
||||
Value pushGlobal(Value th)
|
||||
{
|
||||
stkCanIncTop(th); /* Check if there is room */
|
||||
return *th(th).stk_top++ = vm(th).global;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal function to re-allocate stack's size
|
||||
* @require newsize <= STACK_MAXSIZE || newsize == STACK_ERRORSIZE
|
||||
**/
|
||||
func void realloc(Value th, int newsize)
|
||||
{
|
||||
// Incremental GC before memory allocation events
|
||||
mem::gccheck(th);
|
||||
Value *oldstack = th(th).stack;
|
||||
int osize = th(th).size; // size of old stack
|
||||
|
||||
// Ensure we not asking for more than allowed, and that old stack's values are consistent
|
||||
assert(osize == 0 || ((th(th).stk_last - th(th).stack) == th(th)->size - STACK_EXTRA));
|
||||
|
||||
// Allocate new stack (assume success) and fill any growth with nulls
|
||||
mem::reallocvector(th, th(th)->stack, th(th)->size, newsize, Value);
|
||||
for (; osize < newsize; osize++)
|
||||
{
|
||||
th(th).stack[osize] = aNull;
|
||||
}
|
||||
|
||||
// Correct stack values for new size
|
||||
th(th)->size = newsize;
|
||||
th(th)->stk_last = th(th)->stack + newsize - STACK_EXTRA;
|
||||
|
||||
// Correct all data stack pointers, given that data stack may have moved in memory
|
||||
if (oldstack) {
|
||||
CallInfo *ci;
|
||||
AintIdx shift = th(th)->stack - oldstack;
|
||||
th(th)->stk_top = th(th)->stk_top + shift;
|
||||
for (ci = th(th)->curmethod; ci != NULL; ci = ci->previous) {
|
||||
ci->end += shift;
|
||||
ci->methodbase += shift;
|
||||
ci->retTo += shift;
|
||||
ci->begin += shift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Internal function to grow current method's stack area by at least n past stk_top.
|
||||
May double stack instead. May abort if beyond stack max. */
|
||||
void stkGrow(Value th, AuintIdx extra) {
|
||||
|
||||
// Already past max? Abort!
|
||||
if (th(th)->size > STACK_MAXSIZE) {
|
||||
logSevere("Acorn VM wants to overflow max stack size. Runaway recursive method?");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the max between how much we need (based on requested growth)
|
||||
// and doubling the stack size (capped at maximum)
|
||||
AuintIdx needed = (AuintIdx)(th(th)->stk_top - th(th)->stack) + extra + STACK_EXTRA;
|
||||
AuintIdx newsize = 2 * th(th)->size;
|
||||
if (newsize > STACK_MAXSIZE)
|
||||
newsize = STACK_MAXSIZE;
|
||||
if (newsize < needed) newsize = needed;
|
||||
|
||||
// re-allocate stack (preserves contents)
|
||||
if (newsize > STACK_MAXSIZE) {
|
||||
stkRealloc(th, STACK_ERRORSIZE); // How much we give if asking for too much
|
||||
}
|
||||
else
|
||||
stkRealloc(th, newsize);
|
||||
}
|
||||
|
||||
/* Ensure method's stack has room for 'needed' values above top. Return 0 on failure.
|
||||
* This may grow the stack, but never shrinks it.
|
||||
*/
|
||||
int needMoreLocal(Value th, AuintIdx needed) {
|
||||
int success;
|
||||
CallInfo *ci = th(th)->curmethod;
|
||||
vm_lock(th);
|
||||
|
||||
// Check if we already have enough allocated room on stack for more values
|
||||
if ((AuintIdx)(th(th)->stk_last - th(th)->stk_top) > needed + STACK_EXTRA)
|
||||
success = 1; // Success! Stack is already big enough
|
||||
else {
|
||||
// Will this overflow max stack size?
|
||||
if ((AuintIdx)(th(th)->stk_top - th(th)->stack) > STACK_MAXSIZE - needed - STACK_EXTRA)
|
||||
success = 0; // Fail! - don't grow
|
||||
else {
|
||||
stkGrow(th, needed);
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// adjust method's last allowed value upwards, as needed
|
||||
if (success && ci->end < th(th)->stk_top + needed)
|
||||
ci->end = th(th)->stk_top + needed;
|
||||
|
||||
vm_unlock(th);
|
||||
return success;
|
||||
}
|
||||
|
||||
999
resources/examples/acornvm/gen.c3
Normal file
999
resources/examples/acornvm/gen.c3
Normal file
@@ -0,0 +1,999 @@
|
||||
/** Bytecode generator for Acorn compiler
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
#include "acorn.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace avm {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Create a new bytecode method value. */
|
||||
void newBMethod(Value th, Value *dest) {
|
||||
BMethodInfo *meth = (BMethodInfo*) mem_new(th, MethEnc, sizeof(BMethodInfo));
|
||||
*dest = (Value) meth;
|
||||
|
||||
methodFlags(meth) = 0;
|
||||
methodNParms(meth) = 1; // 'self'
|
||||
|
||||
meth->code = NULL;
|
||||
meth->maxstacksize = 20;
|
||||
meth->avail = 0;
|
||||
meth->size = 0;
|
||||
meth->lits = NULL;
|
||||
meth->litsz = 0;
|
||||
meth->nbrlits = 0;
|
||||
meth->nbrexterns = 0;
|
||||
meth->nbrlocals = 0;
|
||||
}
|
||||
|
||||
/* Put new instruction in code array */
|
||||
void genPutInstr(CompInfo *comp, AuintIdx loc, Instruction i) {
|
||||
mem_growvector(comp->th, comp->method->code, loc, comp->method->avail, Instruction, INT_MAX);
|
||||
comp->method->code[loc] = i;
|
||||
}
|
||||
|
||||
/* Append new instruction to code array */
|
||||
void genAddInstr(CompInfo *comp, Instruction i) {
|
||||
mem_growvector(comp->th, comp->method->code, comp->method->size, comp->method->avail, Instruction, INT_MAX);
|
||||
comp->method->code[comp->method->size++] = i;
|
||||
}
|
||||
|
||||
/* Add a literal and return its index */
|
||||
int genAddLit(CompInfo *comp, Value val) {
|
||||
BMethodInfo* f = comp->method;
|
||||
|
||||
// See if we already have it
|
||||
int i = f->nbrlits;
|
||||
while (i-- > 0)
|
||||
if (f->lits[i] == val)
|
||||
return i;
|
||||
|
||||
// If not found, add it
|
||||
mem_growvector(comp->th, f->lits, f->nbrlits, f->litsz, Value, INT_MAX);
|
||||
if (isStr(val))
|
||||
str_info(val)->flags1 |= StrLiteral; // Make strings read only
|
||||
f->lits[f->nbrlits] = val;
|
||||
mem_markChk(comp->th, comp, val);
|
||||
return f->nbrlits++;
|
||||
}
|
||||
|
||||
/* Indicate the method has a variable number of parameters */
|
||||
void genVarParms(CompInfo *comp) {
|
||||
methodFlags(comp->method) = METHOD_FLG_VARPARM;
|
||||
}
|
||||
|
||||
/** Allocate block's local variables */
|
||||
Value genLocalVars(CompInfo *comp, Value blockvarseg,int nexpected) {
|
||||
Value th = comp->th;
|
||||
Value svLocalVars = comp->locvarseg;
|
||||
if (blockvarseg!=aNull) {
|
||||
int nbrvars = arr_size(blockvarseg)-2;
|
||||
if (nbrvars>0) {
|
||||
comp->locvarseg = blockvarseg;
|
||||
arrSet(th, comp->locvarseg, 1, anInt(comp->nextreg));
|
||||
if (nbrvars-nexpected>0)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadNulls, comp->nextreg+nexpected, nbrvars-nexpected, 0));
|
||||
comp->nextreg += nbrvars;
|
||||
if (comp->method->maxstacksize < comp->nextreg+nbrvars)
|
||||
comp->method->maxstacksize = comp->nextreg+nbrvars;
|
||||
}
|
||||
}
|
||||
return svLocalVars;
|
||||
}
|
||||
|
||||
/* Raise method's max stack size if register is above it */
|
||||
void genMaxStack(CompInfo *comp, AuintIdx reg) {
|
||||
if (comp->method->maxstacksize < reg)
|
||||
comp->method->maxstacksize = reg+1;
|
||||
}
|
||||
|
||||
/** Get a node from an AST segment */
|
||||
#define astGet(th, astseg, idx) (arrGet(th, astseg, idx))
|
||||
|
||||
void genExp(CompInfo *comp, Value astseg);
|
||||
void genStmts(CompInfo *comp, Value astseg);
|
||||
void genDoProp(CompInfo *comp, Value astseg, char byteop, Value rval, int nexpected);
|
||||
|
||||
/** Return next available register to load values into */
|
||||
unsigned int genNextReg(CompInfo *comp) {
|
||||
// Keep track of high-water mark for later stack allocation purposes
|
||||
if (comp->method->maxstacksize < comp->nextreg+1)
|
||||
comp->method->maxstacksize = comp->nextreg+1;
|
||||
return comp->nextreg++;
|
||||
}
|
||||
|
||||
/** Return register number for expression (if it already is one), otherwise return -1 */
|
||||
int genExpReg(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
if (isSym(astseg)) {
|
||||
if (vmlit(SymThis) == astseg)
|
||||
return comp->thisreg;
|
||||
else if (vmlit(SymSelf) == astseg)
|
||||
return 0;
|
||||
} else {
|
||||
Value op = astGet(th, astseg, 0);
|
||||
if (vmlit(SymLocal) == op)
|
||||
return findLocalVar(comp, astGet(th, astseg, 1));
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Get the destination where Jump is going */
|
||||
int genGetJump(CompInfo *comp, int ip) {
|
||||
int offset = bc_j(comp->method->code[ip]);
|
||||
if (offset == BCNO_JMP) /* point to itself represents end of list */
|
||||
return BCNO_JMP; /* end of list */
|
||||
else
|
||||
return (ip+1)+offset; /* turn offset into absolute position */
|
||||
}
|
||||
|
||||
/** Set the Jump instruction at ip to jump to dest instruction */
|
||||
void genSetJump(CompInfo *comp, int ip, int dest) {
|
||||
if (ip==BCNO_JMP)
|
||||
return;
|
||||
Instruction *jmp = &comp->method->code[ip];
|
||||
int offset = dest-(ip+1);
|
||||
assert(dest != BCNO_JMP);
|
||||
if (((offset+BCBIAS_J) >> 16)!=0)
|
||||
assert(0 && "control structure too long");
|
||||
*jmp = setbc_j(*jmp, offset);
|
||||
}
|
||||
|
||||
/* Set the jump instruction link chain starting at listip to jump to dest */
|
||||
void genSetJumpList(CompInfo *comp, int listip, int dest) {
|
||||
while (listip != BCNO_JMP) {
|
||||
int next = genGetJump(comp, listip);
|
||||
genSetJump(comp, listip, dest);
|
||||
listip = next;
|
||||
}
|
||||
}
|
||||
|
||||
/** Generate a jump that goes forward, possibly as part of an jump chain */
|
||||
void genFwdJump(CompInfo *comp, int op, int reg, int *ipchain) {
|
||||
// If part of a jmp chain, add this jump to the chain
|
||||
if (*ipchain != BCNO_JMP) {
|
||||
// Find last jump in chain
|
||||
int jumpip;
|
||||
int nextip = *ipchain;
|
||||
do {
|
||||
jumpip = nextip;
|
||||
nextip = genGetJump(comp, jumpip);
|
||||
} while (nextip != BCNO_JMP);
|
||||
// Fix it to point to jump we are about to generate
|
||||
genSetJump(comp, jumpip, comp->method->size);
|
||||
}
|
||||
else
|
||||
*ipchain = comp->method->size; // New chain starts with this jump
|
||||
genAddInstr(comp, BCINS_AJ(op, reg, BCNO_JMP));
|
||||
}
|
||||
|
||||
/** Generate conditional tests & appropriate jump(s), handled recursively for boolean operators.
|
||||
failjump is ip for first jump past the code to run on condition's success.
|
||||
passjump is ip for first jump directly to condition's success.
|
||||
notflag is true if under influence of 'not' operator: reversing jumps and and/or.
|
||||
lastjump specifies how last jump should behave: true for fail jump, false for passjump. true reverses jump condition. */
|
||||
void genJumpExp(CompInfo *comp, Value astseg, int *failjump, int *passjump, bool notflag, bool lastjump) {
|
||||
Value th = comp->th;
|
||||
unsigned int svnextreg = comp->nextreg;
|
||||
Value condop = isArr(astseg)? astGet(th, astseg, 0) : astseg;
|
||||
bool revjump = notflag ^ lastjump; // Reverse jump based on not flag and lastjump
|
||||
|
||||
// Comparison ops (e.g., == or <) based on rocket operator - generation code comes later.
|
||||
int jumpop;
|
||||
if (condop == vmlit(SymLt)) jumpop = revjump? OpJGeN : OpJLt;
|
||||
else if (condop == vmlit(SymLe)) jumpop = revjump? OpJGtN : OpJLe;
|
||||
else if (condop == vmlit(SymGt)) jumpop = revjump? OpJLeN : OpJGt;
|
||||
else if (condop == vmlit(SymGe)) jumpop = revjump? OpJLtN : OpJGe;
|
||||
else if (condop == vmlit(SymEq)) jumpop = revjump? OpJNeN : OpJEq;
|
||||
else if (condop == vmlit(SymNe)) jumpop = revjump? OpJEqN : OpJNe;
|
||||
|
||||
// '===' exact equivalence
|
||||
else if (condop == vmlit(SymEquiv)) {
|
||||
genExp(comp, astGet(th, astseg, 1));
|
||||
Value arg2 = astGet(th, astseg, 2);
|
||||
if (isArr(arg2) && astGet(th, arg2, 0)==vmlit(SymLit) && astGet(th, arg2, 1)==aNull) {
|
||||
genFwdJump(comp, revjump? OpJNNull : OpJNull, svnextreg, lastjump? failjump : passjump);
|
||||
}
|
||||
else {
|
||||
genExp(comp, arg2);
|
||||
genFwdJump(comp, revjump? OpJDiff : OpJSame, svnextreg, lastjump? failjump : passjump);
|
||||
}
|
||||
comp->nextreg = svnextreg;
|
||||
return;
|
||||
}
|
||||
|
||||
// '~~' pattern match
|
||||
else if (condop == vmlit(SymMatchOp)) {
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, vmlit(SymMatchOp))));
|
||||
genExp(comp, astGet(th, astseg, 2)); // '~~' uses right hand value for object call
|
||||
genExp(comp, astGet(th, astseg, 1));
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, svnextreg, comp->nextreg - svnextreg-1, 1));
|
||||
genFwdJump(comp, revjump? OpJFalse : OpJTrue, svnextreg, lastjump? failjump : passjump);
|
||||
comp->nextreg = svnextreg;
|
||||
return;
|
||||
}
|
||||
|
||||
else if (condop == vmlit(SymNot)) {
|
||||
genJumpExp(comp, astGet(th, astseg, 1), failjump, passjump, !notflag, lastjump);
|
||||
return;
|
||||
}
|
||||
|
||||
else if (condop == vmlit(SymOr) || condop == vmlit(SymAnd)) {
|
||||
bool isAnd = (condop == vmlit(SymAnd)) ^ notflag; // Treat it as 'And' (or 'Or')?
|
||||
AuintIdx segi = 1;
|
||||
if (isAnd) {
|
||||
while (segi < getSize(astseg)-1) {
|
||||
genJumpExp(comp, astGet(th, astseg, segi++), failjump, passjump, notflag, true);
|
||||
}
|
||||
genJumpExp(comp, astGet(th, astseg, segi), failjump, passjump, notflag, lastjump);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
int newpassjump = BCNO_JMP;
|
||||
while (segi < getSize(astseg)-1) {
|
||||
int newfailjump = BCNO_JMP;
|
||||
genJumpExp(comp, astGet(th, astseg, segi++), &newfailjump, &newpassjump, notflag, false);
|
||||
genSetJump(comp, newfailjump, comp->method->size);
|
||||
}
|
||||
genJumpExp(comp, astGet(th, astseg, segi), failjump, &newpassjump, notflag, lastjump);
|
||||
genSetJumpList(comp, newpassjump, comp->method->size); // Fix 'or' jumps to here
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, an expression to be interpreted as false/null or true (anything else)
|
||||
// (which includes explicit use of <==>)
|
||||
else {
|
||||
genExp(comp, astseg);
|
||||
genFwdJump(comp, revjump? OpJFalse : OpJTrue, svnextreg, lastjump? failjump : passjump);
|
||||
comp->nextreg = svnextreg;
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate code for rocket-based comparisons
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, vmlit(SymRocket))));
|
||||
genExp(comp, astGet(th, astseg, 1));
|
||||
genExp(comp, astGet(th, astseg, 2));
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, svnextreg, comp->nextreg - svnextreg-1, 1));
|
||||
genFwdJump(comp, jumpop, svnextreg, lastjump? failjump : passjump);
|
||||
comp->nextreg = svnextreg;
|
||||
}
|
||||
|
||||
/** Generate return or yield */
|
||||
void genReturn(CompInfo *comp, Value aststmt, int op, int expected) {
|
||||
Value th = comp->th;
|
||||
AuintIdx svnextreg = comp->nextreg;
|
||||
Value retexp = astGet(th, aststmt, 1);
|
||||
if (retexp==aNull)
|
||||
genAddInstr(comp, BCINS_ABC(op, 0, 0, expected)); // return with no values
|
||||
else {
|
||||
int reg = genExpReg(comp, retexp);
|
||||
// Return from a local variable registers
|
||||
if (reg>=0)
|
||||
genAddInstr(comp, BCINS_ABC(op, reg, 1, expected));
|
||||
// Do tail call if we are calling another method as the return value
|
||||
else if (op==OpReturn && isArr(retexp) && astGet(th, retexp, 0)==vmlit(SymCallProp))
|
||||
genDoProp(comp, retexp, OpTailCall, aNull, 1);
|
||||
// For solo splat, load parameter varargs and return them
|
||||
else if (retexp == vmlit(SymSplat)) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, svnextreg, 0xFF, 0));
|
||||
genAddInstr(comp, BCINS_ABC(op, svnextreg, 0xFF, expected));
|
||||
}
|
||||
// For comma-separated rvals, special handling in case ... splat appears (at end)
|
||||
else if (isArr(retexp) && arrGet(th, retexp, 0)==vmlit(SymComma)) {
|
||||
int nvals = arr_size(retexp)-1;
|
||||
bool varrvals = false;
|
||||
for (int i=1; i<=nvals; i++) {
|
||||
Value rvali = astGet(th, retexp, i);
|
||||
if (i==nvals && rvali==vmlit(SymSplat)) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, genNextReg(comp), 0xFF, 0));
|
||||
varrvals = true;
|
||||
}
|
||||
else if (i==nvals && isArr(rvali) && astGet(th, rvali, 0)==vmlit(SymYield)) {
|
||||
genReturn(comp, rvali, OpYield, 0xFF);
|
||||
varrvals = true;
|
||||
}
|
||||
else
|
||||
genExp(comp, rvali);
|
||||
}
|
||||
genAddInstr(comp, BCINS_ABC(op, svnextreg, varrvals? 0xFF : comp->nextreg - svnextreg, expected));
|
||||
}
|
||||
// Return calculated values on stack
|
||||
else {
|
||||
genExp(comp, retexp);
|
||||
genAddInstr(comp, BCINS_ABC(op, svnextreg, comp->nextreg - svnextreg, expected));
|
||||
}
|
||||
}
|
||||
comp->nextreg = svnextreg;
|
||||
}
|
||||
|
||||
/** Return nonzero opcode if ast operator is a property/method call */
|
||||
char genIsProp(Value th, Value op, int setflag) {
|
||||
if (vmlit(SymActProp) == op)
|
||||
return setflag? OpSetActProp : OpGetActProp;
|
||||
else if (vmlit(SymRawProp) == op)
|
||||
return setflag? OpSetProp : OpGetProp;
|
||||
else if (vmlit(SymCallProp) == op)
|
||||
return setflag? OpSetCall : OpGetCall;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Generate code for some kind of property/method call.
|
||||
rval is aNull for 'get' mode and either a register integer or ast segment for 'set' mode.
|
||||
nexpected specifies how many return values expected from called method */
|
||||
void genDoProp(CompInfo *comp, Value astseg, char byteop, Value rval, int nexpected) {
|
||||
Value th = comp->th;
|
||||
unsigned int svreg = comp->nextreg; // Save
|
||||
|
||||
// <<<< optimize here by seeing if property is a std symbol and self is in register
|
||||
|
||||
genExp(comp, astGet(th, astseg, 2)); // property
|
||||
genExp(comp, astGet(th, astseg, 1)); // self
|
||||
|
||||
// Handle value to be set (if provided) as first parameter
|
||||
if (isInt(rval)) // already loaded into a register
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), toAint(rval), 0));
|
||||
else if (rval!=aNull) {
|
||||
AuintIdx rvalreg = comp->nextreg;
|
||||
genExp(comp, rval); // Load into next available register
|
||||
comp->nextreg = rvalreg+1;
|
||||
}
|
||||
|
||||
// Load as many parameters as we have, then do property get
|
||||
bool varparms = false;
|
||||
for (AuintIdx i = 3; i<getSize(astseg); i++) {
|
||||
AuintIdx rvalreg = comp->nextreg;
|
||||
Value parm = astGet(th, astseg, i);
|
||||
if (parm == vmlit(SymSplat)) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, rvalreg, 0xFF, 0));
|
||||
varparms = true;
|
||||
break;
|
||||
}
|
||||
else if (i==getSize(astseg)-1 && isArr(parm) && arrGet(th, parm, 0)==vmlit(SymYield)) {
|
||||
genReturn(comp, parm, OpYield, 0xFF);
|
||||
varparms = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
genExp(comp, parm);
|
||||
comp->nextreg = rvalreg+1;
|
||||
}
|
||||
}
|
||||
genAddInstr(comp, BCINS_ABC(byteop, svreg, varparms? 0xFF : comp->nextreg - svreg-1, nexpected));
|
||||
comp->nextreg = svreg+1;
|
||||
}
|
||||
|
||||
/** Generate code for an assignment */
|
||||
void genAssign(CompInfo *comp, Value lval, Value rval) {
|
||||
Value th = comp->th;
|
||||
Value lvalop = isArr(lval)? astGet(th, lval, 0) : aNull;
|
||||
|
||||
// Handle assignment to property or method
|
||||
char opcode = genIsProp(th, lvalop, true);
|
||||
if (opcode)
|
||||
genDoProp(comp, lval, opcode, rval, 1);
|
||||
else {
|
||||
// Handle parallel, local, closure, global variable assignments where rval is loaded first
|
||||
int nlvals = lvalop==vmlit(SymComma)? arr_size(lval)-1 : 1;
|
||||
bool varrvals = false;
|
||||
AuintIdx rvalreg;
|
||||
if (isInt(rval))
|
||||
rvalreg = toAint(rval); // rval is already in a register, so use that reg
|
||||
else {
|
||||
// Special handling for right-hand values for parallel assignment
|
||||
rvalreg = comp->nextreg; // Save where we put rvals
|
||||
int opcode;
|
||||
// For method call, specify expected number of return values
|
||||
if (isArr(rval) && (opcode = genIsProp(th, astGet(th, rval, 0), false))) {
|
||||
genDoProp(comp, rval, opcode, aNull, nlvals);
|
||||
varrvals = true;
|
||||
}
|
||||
else if (isArr(rval) && arrGet(th, rval, 0)==vmlit(SymYield)) {
|
||||
genReturn(comp, rval, OpYield, nlvals);
|
||||
varrvals = true;
|
||||
}
|
||||
// For solo splat, load needed number from parameter varargs
|
||||
else if (rval == vmlit(SymSplat)) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, genNextReg(comp), nlvals, 0));
|
||||
varrvals = true;
|
||||
}
|
||||
// For comma-separated rvals, special handling in case ... splat appears (at end)
|
||||
else if (nlvals>1 && isArr(rval) && arrGet(th, rval, 0)==vmlit(SymComma)) {
|
||||
int nvals = arr_size(rval)-1;
|
||||
for (int i=1; i<=nvals; i++) {
|
||||
Value rvali = astGet(th, rval, i);
|
||||
if (i==nvals && i<=nlvals && rvali==vmlit(SymSplat)) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, genNextReg(comp), nlvals-i+1, 0));
|
||||
varrvals = true;
|
||||
}
|
||||
else
|
||||
genExp(comp, rvali);
|
||||
}
|
||||
}
|
||||
else
|
||||
genExp(comp, rval);
|
||||
}
|
||||
// Handle parallel assignment for lvals
|
||||
if (vmlit(SymComma) == lvalop) {
|
||||
int nrneed = varrvals? 0 : nlvals - (comp->nextreg - rvalreg);
|
||||
// Ensure we fill up right values with nulls to as high as left values
|
||||
if (nrneed > 0) {
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadNulls, comp->nextreg, nrneed, 0));
|
||||
comp->nextreg += nrneed;
|
||||
// Keep track of high-water mark for later stack allocation purposes
|
||||
if (comp->method->maxstacksize < comp->nextreg+nrneed)
|
||||
comp->method->maxstacksize = comp->nextreg+nrneed;
|
||||
}
|
||||
// Assign each lval, one at a time, from corresponding loaded rval in a register
|
||||
for (int i = 0; i<nlvals; i++) {
|
||||
genAssign(comp, astGet(th, lval, i+1), anInt(rvalreg+i));
|
||||
}
|
||||
}
|
||||
// Load into local or closure variable
|
||||
else if (vmlit(SymLocal) == lvalop) {
|
||||
Value symnm = astGet(th, lval, 1);
|
||||
int localreg = findLocalVar(comp, symnm);
|
||||
if (localreg != -1)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, localreg, rvalreg, 0));
|
||||
else if ((localreg = findClosureVar(comp, symnm))!=-1)
|
||||
genAddInstr(comp, BCINS_ABC(OpSetClosure, localreg, rvalreg, 0));
|
||||
// Load into a global variable
|
||||
} else if (vmlit(SymGlobal) == lvalop)
|
||||
genAddInstr(comp, BCINS_ABx(OpSetGlobal, rvalreg, genAddLit(comp, astGet(th, lval, 1))));
|
||||
}
|
||||
// We do not consume values, so we don't restore comp->nextreg
|
||||
}
|
||||
|
||||
/** Generate optimized code for assignment when it is just a statement and
|
||||
its right-hand values do not have to be put on stack */
|
||||
void genOptAssign(CompInfo *comp, Value lval, Value rval) {
|
||||
Value th = comp->th;
|
||||
Value lvalop = astGet(th, lval, 0);
|
||||
|
||||
// Handle assignments that require we load rval (and other stuff) first
|
||||
unsigned int fromreg = genExpReg(comp, rval);
|
||||
if (vmlit(SymLocal) == lvalop) {
|
||||
Value symnm = astGet(th, lval, 1);
|
||||
int localreg = findLocalVar(comp, symnm);
|
||||
if (localreg != -1) {
|
||||
// Optimize load straight into register, if possible (this, self, local var)
|
||||
if (fromreg!=-1)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, localreg, fromreg, 0));
|
||||
else if (vmlit(SymBaseurl) == rval)
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, localreg, genAddLit(comp, comp->lex->url)));
|
||||
else {
|
||||
Value rvalop = astGet(th, rval, 0);
|
||||
if (vmlit(SymLit) == rvalop) {
|
||||
Value litval = astGet(th, rval, 1);
|
||||
if (litval==aNull)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, localreg, 0, 0));
|
||||
else if (litval==aFalse)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, localreg, 1, 0));
|
||||
else if (litval==aTrue)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, localreg, 2, 0));
|
||||
else
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, localreg, genAddLit(comp, litval)));
|
||||
} else if (vmlit(SymLocal) == rvalop) {
|
||||
// We did local already - this must be a load from a closure variable
|
||||
genAddInstr(comp, BCINS_ABC(OpGetClosure, localreg, findClosureVar(comp, astGet(th, rval, 1)), 0));
|
||||
} else if (vmlit(SymGlobal) == rvalop) {
|
||||
genAddInstr(comp, BCINS_ABx(OpGetGlobal, localreg, genAddLit(comp, astGet(th, rval, 1))));
|
||||
} else {
|
||||
fromreg = comp->nextreg; // Save where we put rvals
|
||||
genExp(comp, rval);
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, localreg, fromreg, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((localreg = findClosureVar(comp, symnm))!=-1) {
|
||||
fromreg = comp->nextreg; // Save where we put rvals
|
||||
genExp(comp, rval);
|
||||
genAddInstr(comp, BCINS_ABC(OpSetClosure, localreg, fromreg, 0));
|
||||
}
|
||||
} else if (vmlit(SymGlobal) == lvalop) {
|
||||
if (fromreg != -1)
|
||||
genAddInstr(comp, BCINS_ABx(OpSetGlobal, fromreg, genAddLit(comp, astGet(th, lval, 1))));
|
||||
else {
|
||||
fromreg = comp->nextreg; // Save where we put rvals
|
||||
genExp(comp, rval);
|
||||
genAddInstr(comp, BCINS_ABx(OpSetGlobal, fromreg, genAddLit(comp, astGet(th, lval, 1))));
|
||||
}
|
||||
} else
|
||||
genAssign(comp, lval, rval);
|
||||
}
|
||||
|
||||
/** Return true if the expression makes no use of any logical or comparative operators */
|
||||
bool hasNoBool(Value th, Value astseg) {
|
||||
for (AuintIdx segi = 1; segi < getSize(astseg)-1; segi++) {
|
||||
Value op = astGet(th, astseg, segi);
|
||||
op = isArr(op)? astGet(th, op, 0) : op;
|
||||
if (vmlit(SymAnd)==op || vmlit(SymOr)==op || vmlit(SymNot)==op
|
||||
|| vmlit(SymEquiv) == op || vmlit(SymMatchOp) == op
|
||||
|| vmlit(SymEq)==op || vmlit(SymNe)==op
|
||||
|| vmlit(SymGt)==op || vmlit(SymGe)==op || vmlit(SymLt)==op || vmlit(SymLe)==op)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Generate the appropriate code for something that places one or more values on the stack
|
||||
beginning at comp->nextreg (which should be saved before calling this). The last value is at comp->nextreg-1 */
|
||||
void genExp(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
if (isSym(astseg)) {
|
||||
if (vmlit(SymThis) == astseg)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), comp->thisreg, 0));
|
||||
else if (vmlit(SymSelf) == astseg)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), 0, 0));
|
||||
else if (vmlit(SymContext) == astseg)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadContext, genNextReg(comp), 0, 0));
|
||||
else if (vmlit(SymSelfMeth) == astseg)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadContext, genNextReg(comp), 1, 0));
|
||||
else if (vmlit(SymBaseurl) == astseg)
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, comp->lex->url)));
|
||||
else if (vmlit(SymSplat) == astseg)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadVararg, genNextReg(comp), 1, 0)); // By default, only get one value
|
||||
} else if (isArr(astseg)) {
|
||||
Value op = astGet(th, astseg, 0);
|
||||
char opcode = genIsProp(th, op, false);
|
||||
if (opcode) // Property or method use
|
||||
genDoProp(comp, astseg, opcode, aNull, 1);
|
||||
else if (vmlit(SymComma) == op) {
|
||||
int nvals = arr_size(astseg)-1;
|
||||
for (int i=1; i<=nvals; i++)
|
||||
genExp(comp, astGet(th, astseg, i));
|
||||
} else if (vmlit(SymLit) == op) {
|
||||
Value litval = astGet(th, astseg, 1);
|
||||
if (litval==aNull)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, genNextReg(comp), 0, 0));
|
||||
else if (litval==aFalse)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, genNextReg(comp), 1, 0));
|
||||
else if (litval==aTrue)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, genNextReg(comp), 2, 0));
|
||||
else
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, litval)));
|
||||
} else if (vmlit(SymExt) == op) {
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), toAint(astGet(th, astseg, 1))));
|
||||
} else if (vmlit(SymLocal) == op) {
|
||||
Value symnm = astGet(th, astseg, 1);
|
||||
Aint idx;
|
||||
if ((idx = findLocalVar(comp, symnm))!=-1)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), idx, 0));
|
||||
else if ((idx = findClosureVar(comp, symnm))!=-1)
|
||||
genAddInstr(comp, BCINS_ABC(OpGetClosure, genNextReg(comp), idx, 0));
|
||||
} else if (vmlit(SymGlobal) == op) {
|
||||
genAddInstr(comp, BCINS_ABx(OpGetGlobal, genNextReg(comp), genAddLit(comp, astGet(th, astseg, 1))));
|
||||
} else if (vmlit(SymAssgn) == op) {
|
||||
genAssign(comp, astGet(th, astseg, 1), astGet(th, astseg, 2));
|
||||
} else if (vmlit(SymYield) == op) {
|
||||
genReturn(comp, astseg, OpYield, 1);
|
||||
} else if (vmlit(SymClosure) == op) {
|
||||
Value newcloseg = astGet(th, astseg, 2);
|
||||
// If no closure variables nor set method, don't generate closure, just the 'get' method
|
||||
Value setmethseg = astGet(th, newcloseg, 4);
|
||||
if (arr_size(newcloseg)==5 && isArr(setmethseg) && astGet(th, setmethseg, 1)==vmlit(SymNull))
|
||||
genExp(comp, astGet(th, newcloseg, 3));
|
||||
else
|
||||
genExp(comp, newcloseg);
|
||||
} else if (vmlit(SymOrAssgn) == op) {
|
||||
// Assumes that lvar is a local variable
|
||||
assert(astGet(th, astGet(th, astseg, 1), 0)==vmlit(SymLocal));
|
||||
int varreg = findLocalVar(comp, astGet(th, astGet(th, astseg, 1), 1));
|
||||
int jumpip = BCNO_JMP;
|
||||
genFwdJump(comp, OpJNNull, varreg, &jumpip);
|
||||
Value valseg = astGet(th, astseg, 2);
|
||||
int reg = genExpReg(comp, astseg);
|
||||
if (reg>=0)
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, varreg, reg, 0));
|
||||
else if (isArr(valseg) && astGet(th, valseg, 0) == vmlit(SymLit))
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, varreg, genAddLit(comp, astGet(th, valseg, 1))));
|
||||
else {
|
||||
AuintIdx rreg = comp->nextreg; // Save where we put rvals
|
||||
genExp(comp, valseg);
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, varreg, rreg, 0));
|
||||
}
|
||||
genSetJumpList(comp, jumpip, comp->method->size);
|
||||
} else if (vmlit(SymThisBlock) == op) {
|
||||
unsigned int svthis = comp->thisreg;
|
||||
unsigned int svthisopreg = comp->thisopreg;
|
||||
comp->thisopreg = 0;
|
||||
// Generate "using" operator, if specified
|
||||
Value thisop = astGet(th, astseg, 3);
|
||||
if (thisop != aNull) {
|
||||
comp->thisopreg = comp->nextreg;
|
||||
genExp(comp, thisop);
|
||||
}
|
||||
// Generate 'this' value
|
||||
int thisreg = comp->nextreg;
|
||||
genExp(comp, astGet(th, astseg, 1));
|
||||
comp->nextreg = thisreg+1; // Only use first value
|
||||
comp->thisreg = thisreg;
|
||||
// Optimize "using" operator to a method
|
||||
if (thisop != aNull)
|
||||
genAddInstr(comp, BCINS_ABC(OpGetMeth, comp->thisopreg, 0, 0));
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, 2), 0);
|
||||
genStmts(comp, astGet(th, astseg, 4));
|
||||
// Value of a this block is 'this'. Needed for returns or this blocks within this blocks.
|
||||
if (thisop != aNull) {
|
||||
// Move 'this' down, so its value is in the right place
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, comp->thisopreg, comp->thisreg, 0));
|
||||
comp->nextreg = comp->thisreg;
|
||||
}
|
||||
else
|
||||
comp->nextreg = comp->thisreg+1;
|
||||
comp->locvarseg = svLocalVars;
|
||||
comp->thisopreg = svthisopreg;
|
||||
comp->thisreg = svthis;
|
||||
} else if (vmlit(SymQuestion) == op) { // Ternary
|
||||
int svnextreg = comp->nextreg;
|
||||
int failjump = BCNO_JMP;
|
||||
int passjump = BCNO_JMP;
|
||||
genJumpExp(comp, astGet(th, astseg, 1), &failjump, NULL, false, true);
|
||||
int nextreg = genNextReg(comp);
|
||||
comp->nextreg = svnextreg;
|
||||
genExp(comp, astGet(th, astseg, 2));
|
||||
genFwdJump(comp, OpJump, 0, &passjump);
|
||||
genSetJumpList(comp, failjump, comp->method->size);
|
||||
comp->nextreg = svnextreg;
|
||||
genExp(comp, astGet(th, astseg, 3));
|
||||
genSetJumpList(comp, passjump, comp->method->size);
|
||||
} else if ((vmlit(SymOr)==op || vmlit(SymAnd)==op) && hasNoBool(th, astseg)) {
|
||||
// 'Pure' and/or conditional processing
|
||||
int svnextreg = comp->nextreg;
|
||||
int jumpip = BCNO_JMP;
|
||||
AuintIdx segi;
|
||||
for (segi = 1; segi < getSize(astseg)-1; segi++) {
|
||||
genExp(comp, astGet(th, astseg, segi));
|
||||
comp->nextreg = svnextreg;
|
||||
genFwdJump(comp, op==vmlit(SymOr)? OpJTrue : OpJFalse, svnextreg, &jumpip);
|
||||
}
|
||||
genExp(comp, astGet(th, astseg, segi));
|
||||
genSetJumpList(comp, jumpip, comp->method->size);
|
||||
} else if (vmlit(SymAnd)==op || vmlit(SymOr)==op || vmlit(SymNot)==op
|
||||
|| vmlit(SymEquiv) == op || vmlit(SymMatchOp) == op
|
||||
|| vmlit(SymEq)==op || vmlit(SymNe)==op
|
||||
|| vmlit(SymGt)==op || vmlit(SymGe)==op || vmlit(SymLt)==op || vmlit(SymLe)==op)
|
||||
{
|
||||
// Conditional/boolean expression, resolved to 'true' or 'false'
|
||||
int failjump = BCNO_JMP;
|
||||
genJumpExp(comp, astseg, &failjump, NULL, false, true);
|
||||
int nextreg = genNextReg(comp);
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, nextreg, 2, 0));
|
||||
genAddInstr(comp, BCINS_AJ(OpJump, 0, 1));
|
||||
genSetJumpList(comp, failjump, comp->method->size);
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadPrim, nextreg, 1, 0));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/** Generate all if/elif/else blocks */
|
||||
void genIf(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
|
||||
int jumpEndIp = BCNO_JMP; // Instruction pointer to first jump to end of if
|
||||
|
||||
// Process all condition/blocks in astseg
|
||||
AuintIdx ifindx = 1; // Index into astseg for each cond/block group
|
||||
do {
|
||||
unsigned int savereg = comp->nextreg;
|
||||
// Generate conditional jump for bypassing block on condition failure
|
||||
Value condast = astGet(th, astseg, ifindx);
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, ifindx+1), 0);
|
||||
int jumpNextIp = BCNO_JMP; // Instruction pointer to jump to next elif/else block
|
||||
if (condast != vmlit(SymElse)) {
|
||||
unsigned int condreg = comp->nextreg;
|
||||
genJumpExp(comp, condast, &jumpNextIp, NULL, false, true);
|
||||
comp->nextreg = condreg;
|
||||
}
|
||||
genStmts(comp, astGet(th, astseg, ifindx+2)); // Generate block
|
||||
// Generate/fix jumps after clause's block
|
||||
if (condast != vmlit(SymElse)) {
|
||||
if (ifindx+2 < getSize(astseg))
|
||||
genFwdJump(comp, OpJump, 0, &jumpEndIp);
|
||||
genSetJumpList(comp, jumpNextIp, comp->method->size); // Fix jumps to next elif/else block
|
||||
}
|
||||
comp->locvarseg = svLocalVars;
|
||||
comp->nextreg = savereg;
|
||||
ifindx += 3;
|
||||
} while (ifindx < getSize(astseg));
|
||||
genSetJumpList(comp, jumpEndIp, comp->method->size); // Fix jumps to end of 'if'
|
||||
}
|
||||
|
||||
/* Generate specific match call */
|
||||
void genMatchWith(CompInfo *comp, Value pattern, unsigned int matchreg, int nexpected) {
|
||||
// pattern '~~' matchval
|
||||
comp->nextreg = matchreg+2;
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), matchreg+1, 0));
|
||||
genExp(comp, pattern);
|
||||
comp->nextreg = matchreg+4; // only want one value from genExp
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), matchreg, 0));
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, matchreg+2, 2, nexpected==0? 1 : nexpected));
|
||||
}
|
||||
|
||||
/** Generate match block */
|
||||
void genMatch(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
|
||||
int jumpEndIp = BCNO_JMP; // Instruction pointer of first jump to end of match
|
||||
unsigned int matchreg = comp->nextreg;
|
||||
genExp(comp, astGet(th, astseg, 1));
|
||||
Value mtchmethexp = astGet(th, astseg, 2);
|
||||
if (mtchmethexp==vmlit(SymMatchOp))
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, mtchmethexp)));
|
||||
else
|
||||
genExp(comp, mtchmethexp);
|
||||
|
||||
// Process all 'with' blocks in astseg
|
||||
AuintIdx mtchindx = 3; // Index into astseg for each 'with' block
|
||||
while (mtchindx < getSize(astseg)) {
|
||||
comp->nextreg = matchreg+2;
|
||||
Value condast = astGet(th, astseg, mtchindx);
|
||||
int nexpected = toAint(astGet(th, astseg, mtchindx+2));
|
||||
// Perform match and then bypass block on failure
|
||||
int jumpNextIp = BCNO_JMP; // Instruction pointer to jump past this block
|
||||
if (isArr(condast) && arrGet(th, condast, 0)==vmlit(SymComma)) {
|
||||
int jumpDoIp = BCNO_JMP;
|
||||
for (AuintIdx i=1; i<arr_size(condast); i++) {
|
||||
genMatchWith(comp, arrGet(th,condast,i), matchreg, nexpected);
|
||||
if (i==arr_size(condast)-1)
|
||||
genFwdJump(comp, OpJFalse, matchreg+2, &jumpNextIp);
|
||||
else
|
||||
genFwdJump(comp, OpJTrue, matchreg+2, &jumpDoIp);
|
||||
}
|
||||
genSetJumpList(comp, jumpDoIp, comp->method->size); // Fix jumps to block
|
||||
}
|
||||
else if (condast != vmlit(SymElse)) {
|
||||
genMatchWith(comp, condast, matchreg, nexpected);
|
||||
genFwdJump(comp, OpJFalse, matchreg+2, &jumpNextIp);
|
||||
}
|
||||
comp->nextreg = matchreg+2;
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, mtchindx+1), nexpected);
|
||||
genStmts(comp, astGet(th, astseg, mtchindx+3)); // Generate block
|
||||
// Generate/fix jumps after clause's block
|
||||
if (condast != vmlit(SymElse)) {
|
||||
if (mtchindx+2 < getSize(astseg))
|
||||
genFwdJump(comp, OpJump, 0, &jumpEndIp);
|
||||
genSetJumpList(comp, jumpNextIp, comp->method->size); // Fix jumps to next with/else block
|
||||
}
|
||||
comp->locvarseg = svLocalVars;
|
||||
mtchindx += 4;
|
||||
}
|
||||
genSetJumpList(comp, jumpEndIp, comp->method->size); // Fix jumps to end of 'match'
|
||||
comp->nextreg = matchreg;
|
||||
}
|
||||
|
||||
/** Generate while block */
|
||||
void genWhile(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
unsigned int savereg = comp->nextreg;
|
||||
|
||||
// Allocate block's local variables
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, 1), 0);
|
||||
|
||||
// Perform conditional expression and jump
|
||||
int svJumpBegIp = comp->whileBegIp;
|
||||
int svJumpEndIp = comp->whileEndIp;
|
||||
comp->whileBegIp = comp->method->size;
|
||||
comp->whileEndIp = BCNO_JMP;
|
||||
genJumpExp(comp, astGet(th, astseg, 2), &comp->whileEndIp, NULL, false, true);
|
||||
|
||||
// Generate block and jump to beginning. Fix conditional jump to after 'while' block
|
||||
genStmts(comp, astGet(th, astseg, 3)); // Generate block
|
||||
genAddInstr(comp, BCINS_AJ(OpJump, 0, comp->whileBegIp - comp->method->size-1));
|
||||
genSetJumpList(comp, comp->whileEndIp, comp->method->size); // Fix jump to end of 'while' block
|
||||
|
||||
// Restore block's saved values
|
||||
comp->nextreg = savereg;
|
||||
comp->whileBegIp = svJumpBegIp;
|
||||
comp->whileEndIp = svJumpEndIp;
|
||||
comp->locvarseg = svLocalVars;
|
||||
}
|
||||
|
||||
/** Generate each block */
|
||||
void genEach(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
unsigned int savereg = comp->nextreg;
|
||||
|
||||
// Prepare iterator for 'each' block outside of main loop (loaded in savereg)
|
||||
Value iter = astGet(th, astseg, 3);
|
||||
if (iter == vmlit(SymSplat))
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, anInt(0))));
|
||||
else {
|
||||
int fromreg = genExpReg(comp, iter);
|
||||
if (fromreg==-1) {
|
||||
genExp(comp, iter);
|
||||
genAddInstr(comp, BCINS_ABC(OpEachPrep, savereg, savereg, 0));
|
||||
}
|
||||
else
|
||||
genAddInstr(comp, BCINS_ABC(OpEachPrep, genNextReg(comp), fromreg, 0));
|
||||
}
|
||||
|
||||
// Allocate block's local variables
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, 1), 0);
|
||||
|
||||
// Perform conditional expression and jump
|
||||
int svJumpBegIp = comp->whileBegIp;
|
||||
int svJumpEndIp = comp->whileEndIp;
|
||||
comp->whileBegIp = comp->method->size;
|
||||
comp->whileEndIp = BCNO_JMP;
|
||||
genAddInstr(comp, BCINS_ABC(iter == vmlit(SymSplat)? OpEachSplat : OpEachCall, savereg, 0, toAint(astGet(th, astseg,2))));
|
||||
genFwdJump(comp, OpJFalse, savereg+1, &comp->whileEndIp);
|
||||
|
||||
// Generate block and jump to beginning. Fix conditional jump to after 'while' block
|
||||
genStmts(comp, astGet(th, astseg, 4)); // Generate block
|
||||
genAddInstr(comp, BCINS_AJ(OpJump, 0, comp->whileBegIp - comp->method->size-1));
|
||||
genSetJumpList(comp, comp->whileEndIp, comp->method->size); // Fix jump to end of 'while' block
|
||||
|
||||
// Restore block's saved values
|
||||
comp->nextreg = savereg;
|
||||
comp->whileBegIp = svJumpBegIp;
|
||||
comp->whileEndIp = svJumpEndIp;
|
||||
comp->locvarseg = svLocalVars;
|
||||
}
|
||||
|
||||
/** Generate do block */
|
||||
void genDo(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
unsigned int savereg = comp->nextreg;
|
||||
unsigned int lowreg, highreg;
|
||||
|
||||
Value svLocalVars = genLocalVars(comp, astGet(th, astseg, 1), 0);
|
||||
Value exp = astGet(th, astseg, 2);
|
||||
if (exp!=aNull) {
|
||||
lowreg = comp->nextreg;
|
||||
genExp(comp, exp);
|
||||
highreg = comp->nextreg;
|
||||
for (unsigned int reg=lowreg; reg<highreg; reg++) {
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, vmlit(SymBegin))));
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), reg, 0));
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, highreg, 1, 0));
|
||||
}
|
||||
}
|
||||
genStmts(comp, astGet(th, astseg, 3));
|
||||
if (exp!=aNull) {
|
||||
comp->nextreg = highreg;
|
||||
for (unsigned int reg=highreg-1; reg>=lowreg; reg--) {
|
||||
genAddInstr(comp, BCINS_ABx(OpLoadLit, genNextReg(comp), genAddLit(comp, vmlit(SymEnd))));
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadReg, genNextReg(comp), reg, 0));
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, highreg, 1, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// Restore block's saved values
|
||||
comp->nextreg = savereg;
|
||||
comp->locvarseg = svLocalVars;
|
||||
}
|
||||
|
||||
/** Generate a statement */
|
||||
void genStmt(CompInfo *comp, Value aststmt) {
|
||||
Value th = comp->th;
|
||||
AuintIdx svnextreg = comp->nextreg;
|
||||
|
||||
// Set up a call for every statement
|
||||
AuintIdx svthisopreg;
|
||||
if (comp->thisopreg != 0) {
|
||||
svthisopreg = comp->nextreg;
|
||||
// We have to copy this+method, because the method's tail call may destroy them
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadRegs, genNextReg(comp), comp->thisopreg, 2));
|
||||
comp->nextreg++;
|
||||
}
|
||||
|
||||
// Handle various kinds of statements
|
||||
Value op = isArr(aststmt)? astGet(th, aststmt, 0) : aststmt;
|
||||
if (op==vmlit(SymIf)) genIf(comp, aststmt);
|
||||
else if (op==vmlit(SymMatch)) genMatch(comp, aststmt);
|
||||
else if (op==vmlit(SymWhile)) genWhile(comp, aststmt);
|
||||
else if (op==vmlit(SymEach)) genEach(comp, aststmt);
|
||||
else if (op==vmlit(SymDo)) genDo(comp, aststmt);
|
||||
else if (op==vmlit(SymBreak) && comp->whileBegIp!=-1)
|
||||
genFwdJump(comp, OpJump, 0, &comp->whileEndIp);
|
||||
else if (op==vmlit(SymContinue) && comp->whileBegIp!=-1)
|
||||
genAddInstr(comp, BCINS_AJ(OpJump, 0, comp->whileBegIp - comp->method->size-1));
|
||||
else if (op==vmlit(SymReturn))
|
||||
genReturn(comp, aststmt, OpReturn, 0);
|
||||
else if (op==vmlit(SymYield))
|
||||
genReturn(comp, aststmt, OpYield, 0);
|
||||
else if (op==vmlit(SymAssgn))
|
||||
genOptAssign(comp, astGet(th, aststmt,1), astGet(th, aststmt,2));
|
||||
else
|
||||
genExp(comp, aststmt);
|
||||
|
||||
// Finish append (or other this op)
|
||||
if (comp->thisopreg != 0)
|
||||
genAddInstr(comp, BCINS_ABC(OpGetCall, svthisopreg, comp->nextreg - svthisopreg-1, 0));
|
||||
|
||||
comp->nextreg = svnextreg;
|
||||
}
|
||||
|
||||
/** Generate one or a sequence of statements */
|
||||
void genStmts(CompInfo *comp, Value astseg) {
|
||||
Value th = comp->th;
|
||||
if (isArr(astseg) && astGet(comp->th, astseg, 0)==vmlit(SymSemicolon)) {
|
||||
for (AuintIdx i=1; i<getSize(astseg); i++) {
|
||||
genStmt(comp, astGet(comp->th, astseg, i));
|
||||
}
|
||||
}
|
||||
else
|
||||
genStmt(comp, astseg);
|
||||
}
|
||||
|
||||
#define astAddValue(th, astseg, val) (arrAdd(th, astseg, val))
|
||||
Value astAddSeg(Value th, Value oldseg, Value astop, AuintIdx size);
|
||||
Value astAddSeg2(Value th, Value oldseg, Value astop, Value val);
|
||||
Value astInsSeg(Value th, Value oldseg, Value astop, AuintIdx size);
|
||||
|
||||
/** Recursively turn a method's implicit returns in the AST into explicit returns */
|
||||
void genFixReturns(CompInfo *comp, Value aststmts) {
|
||||
Value th = comp->th;
|
||||
if (!isArr(aststmts) || astGet(th, aststmts, 0)!=vmlit(SymSemicolon)) {
|
||||
vmLog("A method's block is not properly formed (should use ';' AST)");
|
||||
return;
|
||||
}
|
||||
Value laststmt = astGet(th, aststmts, arr_size(aststmts)-1);
|
||||
Value lastop = isArr(laststmt)? astGet(th, laststmt, 0) : laststmt;
|
||||
// Implicit return for loops is to return 'null' afterwards
|
||||
if (lastop==vmlit(SymWhile) || lastop==vmlit(SymEach) || lastop==vmlit(SymDo)
|
||||
|| lastop==vmlit(SymYield) || lastop==vmlit(SymBreak) || lastop==vmlit(SymContinue))
|
||||
astAddSeg2(th, aststmts, vmlit(SymReturn), aNull);
|
||||
// Implicit return for 'if'
|
||||
else if (lastop==vmlit(SymIf) || lastop==vmlit(SymMatch)) {
|
||||
// Recursively handle implicit return for each clause's statement block
|
||||
int step = lastop==vmlit(SymMatch)? 4 : 3;
|
||||
Auint i = lastop==vmlit(SymMatch)? 6 : 3;
|
||||
for (; i<arr_size(laststmt); i+=step)
|
||||
genFixReturns(comp, astGet(th, laststmt, i));
|
||||
// If 'if' or 'match' has no 'else', add one that returns null
|
||||
if (astGet(th, laststmt, i-step-step+1)!=vmlit(SymElse)) {
|
||||
astAddValue(th, laststmt, vmlit(SymElse));
|
||||
astAddValue(th, laststmt, aNull);
|
||||
if (lastop==vmlit(SymMatch))
|
||||
astAddValue(th, laststmt, anInt(0));
|
||||
astAddSeg2(th, laststmt, vmlit(SymReturn), aNull);
|
||||
}
|
||||
}
|
||||
// Replace non-return expression statement with 'return' in front
|
||||
else if (lastop!=vmlit(SymReturn))
|
||||
astInsSeg(th, aststmts, vmlit(SymReturn), 2);
|
||||
}
|
||||
|
||||
/* Generate a complete byte-code method by walking the
|
||||
* Abstract Syntax Tree generated by the parser */
|
||||
void genBMethod(CompInfo *comp) {
|
||||
Value th = comp->th;
|
||||
// AST: ('method', localvars, closurevars, parminitstmts, statements)
|
||||
// Initialize generation state for method
|
||||
comp->method->nbrexterns = comp->method->nbrlits;
|
||||
comp->nextreg = comp->method->maxstacksize = comp->method->nbrlocals;
|
||||
comp->thisreg = 0; // Starts with 'self'
|
||||
comp->thisopreg = 0;
|
||||
comp->locvarseg = astGet(comp->th, comp->ast, 1);
|
||||
arrSet(th, comp->locvarseg, 1, anInt(1));
|
||||
|
||||
// If 'self' is bound to this closure, override passed self with it
|
||||
int idx;
|
||||
if ((idx = findClosureVar(comp, vmlit(SymSelf)))!=-1)
|
||||
genAddInstr(comp, BCINS_ABC(OpGetClosure, 0, idx, 0));
|
||||
|
||||
// Generate the method's code based on AST
|
||||
int nbrnull = comp->method->nbrlocals - methodNParms(comp->method);
|
||||
if (nbrnull>0) // Initialize non-parm locals to null
|
||||
genAddInstr(comp, BCINS_ABC(OpLoadNulls, methodNParms(comp->method), nbrnull, 0));
|
||||
genStmts(comp, astGet(th, comp->ast, 2)); // Generate code for parameter defaults
|
||||
Value aststmts = astGet(th, comp->ast, 3);
|
||||
genFixReturns(comp, aststmts); // Turn implicit returns into explicit returns
|
||||
genStmts(comp, aststmts); // Generate method's code block
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace avm
|
||||
#endif
|
||||
692
resources/examples/acornvm/lexer.c3
Normal file
692
resources/examples/acornvm/lexer.c3
Normal file
@@ -0,0 +1,692 @@
|
||||
module acorn::lex;
|
||||
/** Lexer for Acorn compiler
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Crude algorithm for determining if character is a Unicode letter
|
||||
*/
|
||||
func bool isualpha(Auchar c) @inline
|
||||
{
|
||||
return c > 0xA0 || isalpha(c);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Algorithm for determining if character is a digit 0-9
|
||||
*/
|
||||
func bool isudigit(Auchar c) @inline
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new LexInfo value, lexer context for a source program
|
||||
*/
|
||||
func Value new(Value th, Value *dest, Value src, Value url)
|
||||
{
|
||||
LexInfo *lex;
|
||||
|
||||
// Create an lexer object
|
||||
lex = mem::new(th, LexEnc, sizeof(LexInfo));
|
||||
|
||||
// Values
|
||||
lex.token = aNull;
|
||||
lex.th = th;
|
||||
lex.source = src;
|
||||
mem::markChk(th, lex, src);
|
||||
lex.url = url;
|
||||
mem::markChk(th, lex, url);
|
||||
|
||||
// Position info (ignoring initial UTF8 byte-order mark)
|
||||
// TODO
|
||||
lex.bytepos = lex.linebeg = getSize(src) >= 3 && 0 == strncmp("\xEF\xBB\xBF", toStr(src), 3) ? 3 : 0;
|
||||
lex.linenbr = 1;
|
||||
|
||||
// indent state
|
||||
lex.curindent = lex.newindent = 0;
|
||||
|
||||
lex.newline = false;
|
||||
lex.newprogram = true;
|
||||
lex.insertSemi = false;
|
||||
lex.undentcont = false;
|
||||
lex.optype = 0;
|
||||
return *dest = cast(lex, Value);;
|
||||
}
|
||||
|
||||
/** Return the current unicode character whose UTF-8 bytes start at lex->bytepos */
|
||||
func Auchar LexInfo.thischar(LexInfo* lex)
|
||||
{
|
||||
byte *src = &toStr(lex.source)[lex.bytepos];
|
||||
int nbytes;
|
||||
Auchar chr;
|
||||
|
||||
// Get info from first UTF-8 byte
|
||||
if ((*src&0xF0) == 0xF0) { nbytes=4; chr = *src&0x07;}
|
||||
else if ((*src&0xE0) == 0xE0) {nbytes=3; chr = *src&0x0F;}
|
||||
else if ((*src&0xC0) == 0xC0) {nbytes=2; chr = *src&0x1F;}
|
||||
else if ((*src&0x80) == 0x00) {nbytes=1; chr = *src&0x7F;}
|
||||
else {nbytes=1; chr = 0;} // error
|
||||
|
||||
// Obtain remaining bytes
|
||||
while (--nbytes)
|
||||
{
|
||||
src++;
|
||||
if (*src & 0xC0 ==0x80) chr = chr << 6 + *src & 0x3F;
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
/** Return the current unicode character whose UTF-8 bytes start at lex->bytepos */
|
||||
func Auchar LexInfo.nextchar(LexInfo* lex)
|
||||
{
|
||||
const char *src = &toStr(lex->source)[lex->bytepos];
|
||||
int nbytes;
|
||||
Auchar chr;
|
||||
|
||||
// Skip past current character
|
||||
if ((*src&0xF0) == 0xF0) {nbytes=4;}
|
||||
else if ((*src&0xE0) == 0xE0) {nbytes=3;}
|
||||
else if ((*src&0xC0) == 0xC0) {nbytes=2;}
|
||||
else if ((*src&0x80) == 0x00) {nbytes=1;}
|
||||
else {nbytes=1;} // error
|
||||
src += nbytes;
|
||||
|
||||
// Get info from first UTF-8 byte
|
||||
if ((*src&0xF0) == 0xF0) {nbytes=4; chr = *src&0x07;}
|
||||
else if ((*src&0xE0) == 0xE0) {nbytes=3; chr = *src&0x0F;}
|
||||
else if ((*src&0xC0) == 0xC0) {nbytes=2; chr = *src&0x1F;}
|
||||
else if ((*src&0x80) == 0x00) {nbytes=1; chr = *src&0x7F;}
|
||||
else {nbytes=1; chr = 0;} // error
|
||||
|
||||
// Obtain remaining bytes
|
||||
while (--nbytes) {
|
||||
src++;
|
||||
if ((*src&0xC0)==0x80)
|
||||
chr = (chr<<6) + (*src&0x3F);
|
||||
}
|
||||
return chr;
|
||||
}
|
||||
|
||||
/** Skip lex->bytepos past the unicode character whose UTF-8 bytes start at lex->bytepos */
|
||||
func void LexInfo.skipchar(LexInfo* lex)
|
||||
{
|
||||
const char *src = &toStr(lex->source)[lex->bytepos];
|
||||
int nbytes;
|
||||
|
||||
if (*src=='\0')
|
||||
return;
|
||||
|
||||
// Get character size from first byte
|
||||
if ((*src&0xF0) == 0xF0) {nbytes=4;}
|
||||
else if ((*src&0xE0) == 0xE0) {nbytes=3;}
|
||||
else if ((*src&0xC0) == 0xC0) {nbytes=2;}
|
||||
else if ((*src&0x80) == 0x00) {nbytes=1;}
|
||||
else {nbytes=1;} // error
|
||||
|
||||
lex->bytepos += nbytes;
|
||||
}
|
||||
|
||||
/** Return true if at end of source */
|
||||
#define lex_isEOF(lex) (lex_thischar(lex) == '\0')
|
||||
|
||||
/** Scan past non-tokenized white space.
|
||||
* Handle line indentation and continuation */
|
||||
func bool LexInfo.scanWhite(LexInfo *lex)
|
||||
{
|
||||
Value th = lex.th; // for vmlit
|
||||
|
||||
// Insert semicolon as a token, if requested by implied closing brace
|
||||
if (lex.insertSemi)
|
||||
{
|
||||
lex.insertSemi = false;
|
||||
lex.toktype=Res_Token;
|
||||
lex.token=vmlit(SYM_SEMICOLON);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ignore all forms of white space
|
||||
Auchar chr;
|
||||
bool lookForWhiteSpace = true;
|
||||
while (lookForWhiteSpace) {
|
||||
|
||||
switch (chr=lex_thischar(lex)) {
|
||||
|
||||
// Skip past spaces and tabs
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\r':
|
||||
lex_skipchar(lex);
|
||||
break;
|
||||
|
||||
// Skip past new line
|
||||
case '\n':
|
||||
lex->linenbr++;
|
||||
lex->linebeg = lex->bytepos;
|
||||
lex->newline = true;
|
||||
lex_skipchar(lex);
|
||||
|
||||
// Count line-leading tabs
|
||||
lex->newindent = 0;
|
||||
while (lex_thischar(lex)=='\t') {
|
||||
lex->newindent++;
|
||||
lex_skipchar(lex);
|
||||
}
|
||||
|
||||
// Handle continuation.
|
||||
if (lex_thischar(lex)=='\\') {
|
||||
// Undenting requires we spawn some semi-colons and right braces
|
||||
if (lex->newindent < lex->curindent)
|
||||
lex->undentcont = true;
|
||||
else {
|
||||
lex->newline = false;
|
||||
// Pretend indent did not change for extra-indented continuation
|
||||
if (lex->newindent > lex->curindent)
|
||||
lex->newindent = lex->curindent;
|
||||
}
|
||||
lex_skipchar(lex);
|
||||
}
|
||||
break;
|
||||
|
||||
// Skip comment starting with '#' until end of line
|
||||
case '#':
|
||||
{
|
||||
const char *scanp = &toStr(lex->source)[lex->bytepos];
|
||||
if (strncmp("###", scanp, 3)) {
|
||||
// Inline comment skips to end of line
|
||||
while (!lex_isEOF(lex) && lex_thischar(lex)!='\n')
|
||||
lex_skipchar(lex);
|
||||
break;
|
||||
}
|
||||
// Multi-line comment goes until next '###'
|
||||
scanp+=3;
|
||||
while (*scanp && 0!=strncmp("###", scanp, 3)) {
|
||||
if (*scanp=='\n')
|
||||
lex->linenbr++;
|
||||
scanp++;
|
||||
}
|
||||
if (*scanp)
|
||||
scanp+=3;
|
||||
lex->bytepos += scanp - &toStr(lex->source)[lex->bytepos];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
lookForWhiteSpace = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mark start of a real token
|
||||
lex->tokbeg = lex->bytepos;
|
||||
lex->toklinepos = lex->tokbeg - lex->linebeg;
|
||||
lex->tokline = lex->linenbr;
|
||||
|
||||
// We now know the next character starts a real token
|
||||
// But first, we must handle insertion of ; { and } characters
|
||||
// depending on the indentation changes and newline flag
|
||||
|
||||
// Handle increasing indentation
|
||||
if (lex->newindent > lex->curindent) {
|
||||
lex->toktype=Res_Token;
|
||||
lex->token=vmlit(SymLBrace);
|
||||
lex->curindent++;
|
||||
lex->newline = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do not generate leading ';'
|
||||
if (lex->newprogram)
|
||||
lex->newprogram = lex->newline = false;
|
||||
|
||||
// End previous line's statement with a ';'
|
||||
if (lex->newline) {
|
||||
lex->toktype=Res_Token;
|
||||
lex->token=vmlit(SymSemicolon);
|
||||
lex->newline = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure end-of-file flushes all indent levels to 0
|
||||
if (lex_isEOF(lex))
|
||||
lex->newindent = 0;
|
||||
|
||||
// Handle decreasing indentation
|
||||
if (lex->newindent < lex->curindent) {
|
||||
lex->toktype=Res_Token;
|
||||
lex->token=vmlit(SymRBrace);
|
||||
lex->curindent--;
|
||||
if (lex->undentcont && lex->newindent==lex->curindent)
|
||||
lex->undentcont = false; // Continued line at right indent now. No semi-colon.
|
||||
else
|
||||
lex->insertSemi = true; // Insert semi-colon after implied closing brace
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** End of source program is a token */
|
||||
bool lexScanEof(LexInfo *lex) {
|
||||
if (!lex_isEOF(lex))
|
||||
return false;
|
||||
|
||||
lex->toktype = Eof_Token;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Tokenize an integer or floating point number */
|
||||
bool lexScanNumber(LexInfo *lex) {
|
||||
|
||||
// A number token's first character is always 0-9
|
||||
// We cannot handle negative sign here, as it might be a subtraction
|
||||
if (!isudigit(lex_thischar(lex)))
|
||||
return false;
|
||||
|
||||
int base = 10;
|
||||
bool exp = false;
|
||||
int digval = 0;
|
||||
long nbrval = 0;
|
||||
|
||||
// A leading zero may indicate a non-base 10 number
|
||||
if (lex_thischar(lex)=='0') {
|
||||
lex_skipchar(lex);
|
||||
if (toupper(lex_thischar(lex))=='X') {base = 16; lex_skipchar(lex);}
|
||||
// else if (toupper(lex_thischar(lex))=='B') {base = 2; lex_skipchar(lex);}
|
||||
else if (toupper(lex_thischar(lex))=='.') {base = -1; lex_skipchar(lex);}
|
||||
// else base = 8;
|
||||
}
|
||||
|
||||
// Validate and process remaining numeric digits
|
||||
while (1) {
|
||||
// Handle characters in a suspected integer
|
||||
if (base>0) {
|
||||
// Decimal point means it is floating point after all
|
||||
if (base==10 && lex_thischar(lex)=='.') {
|
||||
// If next character is a symbol/range, treat '.' as method operator instead
|
||||
Auchar nchr = lex_nextchar(lex);
|
||||
if (isualpha(nchr) || nchr=='_' || nchr=='$' || nchr=='(' || nchr=='\'' || nchr=='.')
|
||||
break;
|
||||
lex_skipchar(lex);
|
||||
base = -1;
|
||||
continue;
|
||||
}
|
||||
// Extract a number digit value from the character
|
||||
if (isudigit(lex_thischar(lex)))
|
||||
digval = lex_thischar(lex)-'0';
|
||||
else if (isalpha(lex_thischar(lex)))
|
||||
digval = toupper(lex_thischar(lex))-'A'+10;
|
||||
else
|
||||
break;
|
||||
// Ensure digit is within base, then process
|
||||
if (digval>=base)
|
||||
break;
|
||||
nbrval = nbrval*base + digval;
|
||||
lex_skipchar(lex);
|
||||
}
|
||||
|
||||
// Validate characters in a floating point number
|
||||
else {
|
||||
// Only one exponent allowed
|
||||
if (!exp && toupper(lex_thischar(lex))=='E') {
|
||||
exp = true;
|
||||
lex_skipchar(lex);
|
||||
if (lex_thischar(lex)=='-')
|
||||
lex_skipchar(lex);
|
||||
continue;
|
||||
}
|
||||
if (!isudigit(lex_thischar(lex)))
|
||||
break;
|
||||
lex_skipchar(lex);
|
||||
}
|
||||
}
|
||||
|
||||
// Set value and type
|
||||
if (base>=0) {
|
||||
lex->token = anInt(nbrval);
|
||||
lex->toktype = Lit_Token;
|
||||
}
|
||||
else {
|
||||
lex->token = aFloat((Afloat) atof(&toStr(lex->source)[lex->tokbeg]));
|
||||
lex->toktype = Lit_Token;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** List of all reserved names (excluding literals) */
|
||||
static VmLiterals ReservedNames[] = {
|
||||
SymAnd,
|
||||
SymAsync,
|
||||
SymBaseurl,
|
||||
SymBreak,
|
||||
SymContext,
|
||||
SymContinue,
|
||||
SymDo,
|
||||
SymEach,
|
||||
SymElse,
|
||||
SymElif,
|
||||
SymIf,
|
||||
SymIn,
|
||||
SymInto,
|
||||
SymLocal,
|
||||
SymMatch,
|
||||
SymNot,
|
||||
SymOr,
|
||||
SymReturn,
|
||||
SymSelf,
|
||||
SymSelfMeth,
|
||||
SymThis,
|
||||
SymUsing,
|
||||
SymWait,
|
||||
SymWhile,
|
||||
SymWith,
|
||||
SymYield
|
||||
};
|
||||
|
||||
/** Tokenize a name. The result could be Name_Token (e.g., for variables)
|
||||
* Res_Token, a reserved keyword, or Lit_Token for null, false and true. */
|
||||
bool lexScanName(LexInfo *lex) {
|
||||
|
||||
// Name token's first character is always a-z, _ or $
|
||||
Auchar chr = lex_thischar(lex);
|
||||
if (!(isualpha(chr) || chr=='_' || chr=='$'))
|
||||
return false;
|
||||
|
||||
// Walk through all valid characters in name
|
||||
lex_skipchar(lex);
|
||||
while ((chr=lex_thischar(lex))=='_' || chr=='$' || isudigit(chr) || isualpha(chr))
|
||||
lex_skipchar(lex);
|
||||
|
||||
// Allow ? as trailing character
|
||||
if (chr=='?')
|
||||
lex_skipchar(lex);
|
||||
|
||||
// Create name token as a symbol
|
||||
newSym(lex->th, &lex->token, &toStr(lex->source)[lex->tokbeg], lex->bytepos - lex->tokbeg);
|
||||
mem_markChk(lex->th, lex, lex->token);
|
||||
|
||||
// If it is a reserved name for a literal, say so.
|
||||
Value th = lex->th;
|
||||
lex->toktype = Lit_Token;
|
||||
if (lex->token == vmlit(SymNull)) {lex->token = aNull; return true;}
|
||||
else if (lex->token == vmlit(SymFalse)) {lex->token = aFalse; return true;}
|
||||
else if (lex->token == vmlit(SymTrue)) {lex->token = aTrue; return true;}
|
||||
|
||||
// If it is a reserved name, set toktype to say so
|
||||
VmLiterals *vmtblendp = &ReservedNames[sizeof(ReservedNames)/sizeof(VmLiterals)];
|
||||
for (VmLiterals *vmtblp = ReservedNames; vmtblp<vmtblendp; vmtblp++) {
|
||||
if (lex->token == vmlit(*vmtblp)) {
|
||||
lex->toktype = Res_Token;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
lex->toktype = Name_Token;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Tokenize a string (double quotes) or symbol (single quotes)
|
||||
* Handle escape sequences. Ignore line-end and leading tabs for multi-line. */
|
||||
bool lexScanString(LexInfo *lex) {
|
||||
|
||||
// String token's first character should be a quote mark
|
||||
Auchar quotemark = lex_thischar(lex);
|
||||
if (!(quotemark=='"' || quotemark=='\'' ))
|
||||
return false;
|
||||
lex_skipchar(lex);
|
||||
|
||||
// Create a string value to place the contents into
|
||||
const char *begp = &toStr(lex->source)[lex->bytepos];
|
||||
const char *scanp = strchr(begp, quotemark); // An estimate, as it may not be the end
|
||||
Value buildstr = pushStringl(lex->th, aNull, NULL, scanp==NULL? strlen(begp) : scanp-begp);
|
||||
|
||||
// Repetitively scan source looking for various delimiters
|
||||
scanp = begp;
|
||||
while (*scanp && *scanp!=quotemark) {
|
||||
|
||||
// Process any escape sequences within the string
|
||||
if (*scanp=='\\') {
|
||||
// Copy over string segment up to the escape sequence
|
||||
if (scanp-begp > 0)
|
||||
strAppend(lex->th, buildstr, begp, scanp-begp);
|
||||
// Process escape sequence
|
||||
switch (*++scanp) {
|
||||
case 'n': strAppend(lex->th, buildstr, "\n", 1); scanp++; break;
|
||||
case 'r': strAppend(lex->th, buildstr, "\r", 1); scanp++; break;
|
||||
case 't': strAppend(lex->th, buildstr, "\t", 1); scanp++; break;
|
||||
case 'u': case 'U':
|
||||
{
|
||||
// Convert a hexadecimal string of cnt digits to a unicode character
|
||||
Auchar unichar=0;
|
||||
int cnt = *scanp=='u'? 4 :8;
|
||||
if (*(scanp+1)=='+')
|
||||
scanp++;
|
||||
while (*++scanp && cnt--) {
|
||||
if (isudigit(*scanp))
|
||||
unichar = unichar*16 + *scanp -'0';
|
||||
if (isalpha(*scanp) && toupper(*scanp)<='F')
|
||||
unichar = unichar*16 + toupper(*scanp)-'A'+10;
|
||||
}
|
||||
|
||||
// Encode an unicode character into UTF-8 bytes
|
||||
char utf8str[8];
|
||||
char *utfp=&utf8str[sizeof(utf8str)-1];
|
||||
*utfp-- = '\0'; // make it a sizeable string
|
||||
if (unichar < 0x7f) {
|
||||
*utfp = (char)unichar;
|
||||
strAppend(lex->th, buildstr, utfp, 1);
|
||||
}
|
||||
else {
|
||||
// multi-byte encoding, byte by byte backwards
|
||||
int cnt=0;
|
||||
while (unichar) {
|
||||
cnt++;
|
||||
char byt = unichar & 0x3f;
|
||||
unichar = unichar >> 6;
|
||||
// Put appropriate flags if it is the first byte
|
||||
if (unichar==0) {
|
||||
switch (cnt) {
|
||||
case 2: *utfp = byt | 0xC0; break;
|
||||
case 3: *utfp = byt | 0xE0; break;
|
||||
case 4: *utfp = byt | 0xF0; break;
|
||||
case 5: *utfp = byt | 0xF8; break;
|
||||
case 6: *utfp = byt | 0xFC; break;
|
||||
}
|
||||
}
|
||||
else
|
||||
*utfp-- = byt | 0x80;
|
||||
}
|
||||
strAppend(lex->th, buildstr, utfp, cnt);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: strAppend(lex->th, buildstr, scanp, 1); scanp++; break;
|
||||
}
|
||||
begp=scanp;
|
||||
}
|
||||
|
||||
// Ignore line end and line leading tabs
|
||||
else if (*scanp=='\r' || *scanp=='\n') {
|
||||
// Copy over string segment up to the escape sequence
|
||||
if (scanp-begp > 0)
|
||||
strAppend(lex->th, buildstr, begp, scanp-begp);
|
||||
// Ignore line end and leading tabs
|
||||
while (*scanp=='\r' || *scanp=='\n' || *scanp=='\t') {
|
||||
if (*scanp=='\n')
|
||||
lex->linenbr++;
|
||||
scanp++;
|
||||
}
|
||||
begp=scanp;
|
||||
}
|
||||
|
||||
// Otherwise process rest of string
|
||||
else
|
||||
scanp++;
|
||||
}
|
||||
|
||||
// Copy over rest of string segment
|
||||
if (scanp-begp > 0)
|
||||
strAppend(lex->th, buildstr, begp, scanp-begp);
|
||||
|
||||
// Update lex position
|
||||
if (*scanp==quotemark)
|
||||
*scanp++;
|
||||
lex->bytepos += scanp - &toStr(lex->source)[lex->bytepos];
|
||||
|
||||
// Create string (or symbol)
|
||||
lex->toktype = Lit_Token;
|
||||
if (quotemark=='"')
|
||||
lex->token = buildstr;
|
||||
else
|
||||
newSym(lex->th, &lex->token, toStr(buildstr), getSize(buildstr));
|
||||
mem_markChk(lex->th, lex, lex->token);
|
||||
popValue(lex->th); // buildstr
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Tokenize a punctuation-oriented operator symbol.
|
||||
* By this point we take at least one character, unless multi-char op is recognized. */
|
||||
bool lexScanResource(LexInfo *lex) {
|
||||
if (lex_thischar(lex)!='@')
|
||||
return false;
|
||||
Value th = lex->th;
|
||||
lex_skipchar(lex);
|
||||
Auchar delim = lex_thischar(lex);
|
||||
if (delim=='\'' || delim=='"' || delim=='(' || delim<=' ') {
|
||||
lex->token = vmlit(SymAt);
|
||||
lex->toktype = Res_Token;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Mark beginning and look for end of url
|
||||
const char *begp = &toStr(lex->source)[lex->bytepos];
|
||||
const char *scanp = begp;
|
||||
while ((unsigned char)(*++scanp)>' '); // end with space, tab, cr, lf, eof, etc.
|
||||
lex->bytepos += scanp - begp;
|
||||
|
||||
// Create +Resource from literal url, and return it as token
|
||||
pushValue(th, vmlit(SymNew));
|
||||
pushValue(th, vmlit(TypeResc));
|
||||
pushStringl(th, aNull, begp, scanp-begp);
|
||||
pushValue(th, lex->url);
|
||||
getCall(th, 3, 1);
|
||||
lex->token = getFromTop(th, 0);
|
||||
mem_markChk(lex->th, lex, lex->token);
|
||||
popValue(th);
|
||||
lex->toktype = Url_Token;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Tokenize a punctuation-oriented operator symbol.
|
||||
* By this point we take at least one character, unless multi-char op is recognized. */
|
||||
bool lexScanOp(LexInfo *lex) {
|
||||
const char *begp = &toStr(lex->source)[lex->bytepos];
|
||||
Auchar ch1 = lex_thischar(lex);
|
||||
lex_skipchar(lex);
|
||||
Auchar ch2 = lex_thischar(lex);
|
||||
|
||||
// Look for 2- and 3- character combos
|
||||
if (ch1=='.' && ch2=='.') {
|
||||
if ('.'==lex_nextchar(lex)) lex_skipchar(lex);
|
||||
lex_skipchar(lex);
|
||||
} else if (ch1=='=' && ch2=='=') {
|
||||
if ('='==lex_nextchar(lex)) lex_skipchar(lex);
|
||||
lex_skipchar(lex);
|
||||
} else if (ch1=='<' && ch2=='=') {
|
||||
if ('>'==lex_nextchar(lex)) lex_skipchar(lex);
|
||||
lex_skipchar(lex);
|
||||
} else if ((ch1=='>' && ch2=='=')
|
||||
|| (ch1=='!' && ch2=='=')
|
||||
|| (ch1=='~' && ch2=='~')
|
||||
|| (ch1=='<' && ch2=='<')
|
||||
|| (ch1=='>' && ch2=='>')
|
||||
|| (ch1=='+' && ch2=='=')
|
||||
|| (ch1=='-' && ch2=='=')
|
||||
|| (ch1=='*' && ch2=='=')
|
||||
|| (ch1=='/' && ch2=='=')
|
||||
|| (ch1=='.' && ch2==':')
|
||||
|| (ch1==':' && ch2==':')
|
||||
|| (ch1==':' && ch2=='=')
|
||||
|| (ch1=='&' && ch2=='&')
|
||||
|| (ch1=='|' && ch2=='|')
|
||||
|| (ch1=='*' && ch2=='*')
|
||||
|| (ch1=='.' && ch2=='&')
|
||||
|| (ch1=='+' && ch2=='[')
|
||||
|| (ch1=='*' && ch2=='[')
|
||||
) lex_skipchar(lex);
|
||||
|
||||
newSym(lex->th, &lex->token, begp, &toStr(lex->source)[lex->bytepos]-begp);
|
||||
mem_markChk(lex->th, lex, lex->token);
|
||||
lex->toktype = Res_Token;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Get the next token */
|
||||
func void LexInfo.getNextToken(LexInfo *lex)
|
||||
{
|
||||
|
||||
// Scan until we find a token
|
||||
(!lex.scanWhite()
|
||||
&& !lex.scanEof()
|
||||
&& !lex.scanNumber()
|
||||
&& !lex.scanName()
|
||||
&& !lex.scanString()
|
||||
&& !lex.scanResource()
|
||||
&& !lex.scanOp());
|
||||
|
||||
#ifdef COMPILERLOG
|
||||
switch (lex->toktype) {
|
||||
case Lit_Token: {
|
||||
pushSerialized(lex->th, lex->token);
|
||||
vmLog("Literal token: %s", toStr(getFromTop(lex->th, 0)));
|
||||
popValue(lex->th);
|
||||
} break;
|
||||
case Url_Token: {
|
||||
pushSerialized(lex->th, lex->token);
|
||||
vmLog("Literal url token: %s", toStr(getFromTop(lex->th, 0)));
|
||||
popValue(lex->th);
|
||||
} break;
|
||||
case Name_Token: {
|
||||
pushSerialized(lex->th, lex->token);
|
||||
vmLog("Name token: %s", toStr(getFromTop(lex->th, 0)));
|
||||
popValue(lex->th);
|
||||
} break;
|
||||
case Res_Token: {
|
||||
pushSerialized(lex->th, lex->token);
|
||||
vmLog("Reserved token: %s", toStr(getFromTop(lex->th, 0)));
|
||||
popValue(lex->th);
|
||||
} break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Match current token to a reserved symbol. */
|
||||
bool lexMatch(LexInfo *lex, const char *sym) {
|
||||
return (lex->toktype==Res_Token && 0==strcmp(sym, toStr(lex->token)));
|
||||
}
|
||||
|
||||
/* Match current token to a reserved symbol.
|
||||
* If it matches, advance to the next token */
|
||||
bool lexMatchNext(LexInfo *lex, const char *sym) {
|
||||
if (lex->toktype==Res_Token && 0==strcmp(sym, toStr(lex->token))) {
|
||||
lexGetNextToken(lex);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Log an compiler message */
|
||||
void lexLog(LexInfo *lex, const char *msg) {
|
||||
vmLog("While compiling %s(%d:%d): %s", toStr(lex->url), lex->tokline, lex->toklinepos, msg);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
} // namespace avm
|
||||
#endif
|
||||
126
resources/examples/acornvm/main.c3
Normal file
126
resources/examples/acornvm/main.c3
Normal file
@@ -0,0 +1,126 @@
|
||||
module acornvm::compiler;
|
||||
|
||||
|
||||
/* Return a new CompInfo value, compiler state for an Acorn method */
|
||||
func Value new_compiler(Value th, Value *dest, Value src, Value url)
|
||||
{
|
||||
CompInfo *comp;
|
||||
|
||||
// Create an compiler context (this block of code can be gc-locked as atomic)
|
||||
comp = (CompInfo *)mem_new(th, CompEnc, sizeof(CompInfo));
|
||||
*dest = (Value) comp;
|
||||
comp.th = th;
|
||||
comp.lex = nil;
|
||||
comp.ast = nil;
|
||||
comp.method = nil;
|
||||
comp.prevcomp = aNull;
|
||||
|
||||
// pgmsrc is a Text collection of characters
|
||||
if (src.isStr())
|
||||
{
|
||||
// Create lexer using source characters
|
||||
Value lexer = new_lexer(th, &comp->lex, src, url);
|
||||
mem_markChk(th, comp, comp->lex);
|
||||
|
||||
// Prime the pump by getting the first token
|
||||
lexGetNextToken(comp->lex);
|
||||
comp->clovarseg = aNull;
|
||||
}
|
||||
// pgmsrc is CompInfo. Make use of its info.
|
||||
else
|
||||
{
|
||||
comp->lex = (@cast(src, CompInfo*).lex;
|
||||
mem_markChk(th, comp, comp->lex);
|
||||
comp->prevcomp = src;
|
||||
comp->clovarseg = ((CompInfo*)src)->clovarseg;
|
||||
comp->newcloseg = ((CompInfo*)src)->newcloseg;
|
||||
}
|
||||
|
||||
// Setup AST and method to parse and generate into
|
||||
newArr(th, &comp->ast, aNull, 2);
|
||||
mem_markChk(th, comp, comp->ast);
|
||||
newBMethod(th, (Value *)&comp->method);
|
||||
mem_markChk(th, comp, comp->method);
|
||||
|
||||
comp.nextreg = 0;
|
||||
comp.whileBegIp = -1;
|
||||
comp.forcelocal = false;
|
||||
|
||||
return @cast(*dest, Value);
|
||||
}
|
||||
|
||||
/* Method to compile an Acorn method. Parameters:
|
||||
- pgmsrc: CompInfo or Text string containing the program source
|
||||
- baseurl: a symbol or null
|
||||
It returns the compiled byte-code method. */
|
||||
func int acn_newmethod(Value th)
|
||||
{
|
||||
// Retrieve pgmsrc and baseurl from parameters
|
||||
Value pgmsrc, baseurl;
|
||||
if (th.getTop() < 2 || !(Value.isStr(pgmsrc = th.getLocal(1)) || pgmsrc.isPtr() && pgmsrc.isEnc(COMP))))
|
||||
{
|
||||
pushValue(th, aNull);
|
||||
return 1;
|
||||
}
|
||||
if (th.getTop() < 3 || !Value.isSym(baseurl = th.getLocal(2)))
|
||||
{
|
||||
baseurl = aNull;
|
||||
}
|
||||
// Create compiler context, then parse source to AST
|
||||
CompInfo* comp = (CompInfo*) pushCompiler(th, pgmsrc, baseurl);
|
||||
parseProgram(comp);
|
||||
$if (@defined(COMPILERLOG))
|
||||
{
|
||||
Value aststr = pushSerialized(th, comp->ast);
|
||||
vmLog("Resulting AST is: %s", toStr(aststr));
|
||||
th.pop(th);
|
||||
}
|
||||
// Generate method instructions from AST
|
||||
genBMethod(comp);
|
||||
if (@defined(COMPILERLOG))
|
||||
{
|
||||
Value bmethod = pushSerialized(th, comp->method);
|
||||
vmLog("Resulting bytecode is: %s", toStr(bmethod));
|
||||
popValue(th);
|
||||
}
|
||||
|
||||
// Return generated method
|
||||
th.push(comp->method);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Found in typ_resource.cpp
|
||||
AuintIdx resource_resolve(Value th, Value meth, Value *resource);
|
||||
|
||||
/* Try to resolve all static Resources (externs) in 'self's method and its extern methods.
|
||||
Will start the loading of any static resources not already loading.
|
||||
null is returned if link is successful, otherwise it returns number of unresolved Resources */
|
||||
int acn_linker(Value th)
|
||||
{
|
||||
BMethodInfo* meth = @cast(th.getLocal(0), BMethodInfo*);
|
||||
|
||||
// Return null when there are no unresolved externs
|
||||
if (meth.nbrexterns == 0) return 0;
|
||||
|
||||
AuintIdx counter = 0;
|
||||
Value *externp = meth.lits;
|
||||
for (Auint i = 0; i < meth.nbrexterns; i++)
|
||||
{
|
||||
counter += th.resource_resolve(meth, externp);
|
||||
externp++;
|
||||
}
|
||||
|
||||
// Return null if all externs resolved.
|
||||
if (counter == 0)
|
||||
{
|
||||
meth.nbrexterns = 0; // Mark that no more static Resources externs are to be found
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
th.pushValue(anInt(counter)); // Return count of unresolved static resources
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
1015
resources/examples/acornvm/parser.c3
Normal file
1015
resources/examples/acornvm/parser.c3
Normal file
File diff suppressed because it is too large
Load Diff
131
resources/examples/acornvm/parser_ast.c3
Normal file
131
resources/examples/acornvm/parser_ast.c3
Normal file
@@ -0,0 +1,131 @@
|
||||
module acorn::parser::ast;
|
||||
import acorn::arr;
|
||||
|
||||
/** Parser for Acorn compiler. See Acorn documentation for syntax diagrams.
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
|
||||
/* **********************
|
||||
* Abstract Syntax Tree construction helpers for parser
|
||||
* (isolates that we are working with arrays to encode s-expressions)
|
||||
* **********************/
|
||||
|
||||
/** Append a value to AST segment - growing as needed */
|
||||
func void addValue(Value th, Value astseg, Value val) @inline
|
||||
{
|
||||
arr::add(th, astseg, val);
|
||||
}
|
||||
|
||||
/** Get a value within the AST segment */
|
||||
func Value get(Value th, Value astseg, AuintIdx idx) @inline
|
||||
{
|
||||
return arr::get(th, astseg, idx);
|
||||
}
|
||||
|
||||
/** Set a value within the AST segment */
|
||||
func void set(Value th, Value astseg, AuintIdx idx, Value val)
|
||||
{
|
||||
arr::set(th, astseg, idx, val);
|
||||
}
|
||||
|
||||
/** Create and append a new AST segment (of designated size) to current segment.
|
||||
* Append the AST op to the new segment, then return it */
|
||||
Value addSeg(Value th, Value oldseg, Value astop, AuintIdx size)
|
||||
{
|
||||
Value newseg = pushArray(th, aNull, size);
|
||||
arr::add(th, oldseg, newseg);
|
||||
th.popValue();
|
||||
arr::add(th, newseg, astop);
|
||||
return newseg;
|
||||
}
|
||||
|
||||
/** Create and append a new AST segment (with two slots) to current segment.
|
||||
* Append the AST op and val to the new segment, then return it */
|
||||
Value addSeg2(Value th, Value oldseg, Value astop, Value val)
|
||||
{
|
||||
Value newseg = pushArray(th, aNull, 2);
|
||||
arr::add(th, oldseg, newseg);
|
||||
popValue(th);
|
||||
arr::add(th, newseg, astop);
|
||||
arr::add(th, newseg, val);
|
||||
return newseg;
|
||||
}
|
||||
|
||||
/** Get last node from ast segment */
|
||||
Value getLast(Value th, Value astseg) @inline
|
||||
{
|
||||
return get(th, astseg, arr_size(astseg) - 1);
|
||||
}
|
||||
|
||||
/** Create a new segment of designated size to replace last value of oldseg.
|
||||
* Append the AST op and the value from the oldseg to the new segment,
|
||||
* then return it. */
|
||||
Value insSeg(Value th, Value oldseg, Value astop, AuintIdx size)
|
||||
{
|
||||
AuintIdx oldpos = arr_size(oldseg)-1;
|
||||
Value saveval = arr::get(th, oldseg, oldpos);
|
||||
Value newseg = pushArray(th, aNull, size);
|
||||
arr::set(th, oldseg, oldpos, newseg);
|
||||
popValue(th);
|
||||
arr::add(th, newseg, astop);
|
||||
arr::add(th, newseg, saveval);
|
||||
return newseg;
|
||||
}
|
||||
|
||||
/** Create a new segment of designated size to replace last value of oldseg.
|
||||
* Append the AST op, propval, and the value from the oldseg to the new segment,
|
||||
* then return it. */
|
||||
Value astInsSeg2(Value th, Value oldseg, Value astop, Value propval, AuintIdx size)
|
||||
{
|
||||
AuintIdx oldpos = arr_size(oldseg) - 1;
|
||||
Value saveval = arr::get(th, oldseg, oldpos);
|
||||
Value newseg = pushArray(th, aNull, size);
|
||||
arrSet(th, oldseg, oldpos, newseg);
|
||||
th.popValue();
|
||||
arr::add(th, newseg, astop);
|
||||
if (isSym(propval))
|
||||
{
|
||||
if (propval == vmlit(SymThis))
|
||||
{
|
||||
arr::add(th, newseg, propval);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value propseg = addSeg(th, newseg, vmlit(SymLit), 2); // Assume propval is a literal symbol
|
||||
arr::add(th, propseg, propval);
|
||||
}
|
||||
}
|
||||
arr::add(th, newseg, saveval);
|
||||
return newseg;
|
||||
}
|
||||
|
||||
/** Create a new untethered, sized AST segment that has astop as first element */
|
||||
Value pushNew(Value th, Value astop, AuintIdx size)
|
||||
{
|
||||
Value newseg = pushArray(th, aNull, size);
|
||||
arr::add(th, newseg, astop);
|
||||
return newseg;
|
||||
}
|
||||
|
||||
/** Attach newseg into last slot of oldseg, whose old value is appended to newseg */
|
||||
void popNew(Value th, Value oldseg, Value newseg)
|
||||
{
|
||||
AuintIdx oldpos = arr_size(oldseg) - 1;
|
||||
Value saveval = arr::get(th, oldseg, oldpos);
|
||||
arr::set(th, oldseg, oldpos, newseg);
|
||||
arr::add(th, newseg, saveval);
|
||||
th.popValue();
|
||||
}
|
||||
|
||||
/** Return true if ast segment can be assigned a value: variable or settable property/method */
|
||||
func bool isLval(Value th, Value astseg)
|
||||
{
|
||||
if (!astseg.isArr()) return false;
|
||||
Value op = get(th, astseg, 0);
|
||||
return op == vmlit(SYM_LOCAL) || op == vmlit(SYM_GLOGAL) || op==vmlit(SYM_ACT_PROP) || op==vmlit(SYM_RAW_PROP) || op==vmlit(SYM_CALL_PROP);
|
||||
}
|
||||
164
resources/examples/acornvm/symbol.c3
Normal file
164
resources/examples/acornvm/symbol.c3
Normal file
@@ -0,0 +1,164 @@
|
||||
module acornvm::sym;
|
||||
|
||||
/** modulo operation for hashing (size is always a power of 2) */
|
||||
macro @hash_binmod(s, size)
|
||||
{
|
||||
assert_exp(size & (size-1) == 0);
|
||||
return @cast(s & (size-1), AuintIdx);
|
||||
}
|
||||
|
||||
/** Resize the symbol table */
|
||||
func void resizeTable(Value th, Auint newsize)
|
||||
{
|
||||
SymTable* sym_tbl = &vm(th)->sym_table;
|
||||
Auint i;
|
||||
|
||||
// If we need to grow, allocate more cleared space for array
|
||||
if (newsize > sym_tbl.nbrAvail)
|
||||
{
|
||||
//mem_gccheck(th); // Incremental GC before memory allocation events
|
||||
mem_reallocvector(th, sym_tbl->symArray, sym_tbl->nbrAvail, newsize, SymInfo *);
|
||||
for (i = sym_tbl->nbrAvail; i < newsize; i++) sym_tbl->symArray[i] = NULL;
|
||||
}
|
||||
|
||||
// Move all symbols to re-hashed positions in array
|
||||
for (i = 0; i < sym_tbl->nbrAvail; i++)
|
||||
{
|
||||
SymInfo *p = sym_tbl.symArray[i];
|
||||
sym_tbl.symArray[i] = NULL;
|
||||
while (p)
|
||||
{ // for each node in the list
|
||||
SymInfo *next = (SymInfo*) p->next; // save next
|
||||
AuintIdx h = hash_binmod(p->hash, newsize); // new position
|
||||
p->next = (MemInfo*) sym_tbl->symArray[h]; // chain it
|
||||
sym_tbl->symArray[h] = (SymInfo*) p;
|
||||
resetoldbit(p); // see MOVE OLD rule
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
// Shrink array
|
||||
if (newsize < sym_tbl.nbrAvail)
|
||||
{
|
||||
// shrinking slice must be empty
|
||||
assert(sym_tbl->symArray[newsize] == NULL && sym_tbl->symArray[sym_tbl->nbrAvail - 1] == NULL);
|
||||
mem_reallocvector(th, sym_tbl->symArray, sym_tbl->nbrAvail, newsize, SymInfo *);
|
||||
}
|
||||
sym_tbl->nbrAvail = newsize;
|
||||
}
|
||||
|
||||
/** Initialize the symbol table that hash indexes all symbols */
|
||||
func void init(Value th)
|
||||
{
|
||||
SymTable* sym_tbl = &vm(th).sym_table;
|
||||
sym_tbl.nbrAvail = 0;
|
||||
sym_tbl.nbrUsed = 0;
|
||||
sym_tbl.symArray = nil;
|
||||
resizeTable(th, AVM_SYMTBLMINSIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Free the symbol table
|
||||
*/
|
||||
void free(Value th)
|
||||
{
|
||||
mem::freearray(th, vm(th).sym_table.symArray, vm(th).sym_table.nbrAvail);
|
||||
}
|
||||
|
||||
/* If symbol exists in symbol table, reuse it. Otherwise, add it.
|
||||
Anchor (store) symbol value in dest and return it. */
|
||||
func Value newSym(Value th, Value* dest, string str, AuintIdx len)
|
||||
{
|
||||
SymInfo* sym;
|
||||
SymTable* sym_tbl = &vm(th)->sym_table;
|
||||
unsigned int hash = tblCalcStrHash(str, len, th(th)->vm->hashseed);
|
||||
|
||||
// Look for symbol in symbol table. Return it, if found.
|
||||
for (sym = sym_tbl->symArray[hash_binmod(hash, sym_tbl->nbrAvail)]; sym != NULL; sym = (SymInfo*) sym->next) {
|
||||
if (hash == sym->hash &&
|
||||
len == sym->size &&
|
||||
(memcmp(str, sym_cstr(sym), len) == 0)) {
|
||||
mem_keepalive(th, (MemInfo*) sym); // Keep it alive, if it had been marked for deletion
|
||||
return *dest = (Value) sym;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found. Double symbol table size if needed to hold another entry
|
||||
if (sym_tbl->nbrUsed >= sym_tbl->nbrAvail)
|
||||
sym_resize_tbl(th, sym_tbl->nbrAvail*2);
|
||||
|
||||
// Create a symbol object, adding to symbol table at hash entry
|
||||
sym = (SymInfo *) mem_newnolink(th, SymEnc, sym_memsize(len));
|
||||
MemInfo **linkp = (MemInfo**) &sym_tbl->symArray[hash_binmod(hash, sym_tbl->nbrAvail)];
|
||||
sym->next = *linkp;
|
||||
*linkp = (MemInfo*)sym;
|
||||
sym->size = len;
|
||||
sym->hash = hash;
|
||||
memcpy(sym_cstr(sym), str, len);
|
||||
(sym_cstr(sym))[len] = '\0';
|
||||
sym_tbl->nbrUsed++;
|
||||
return *dest = (Value) sym;
|
||||
}
|
||||
|
||||
/* Return 1 if the value is a Symbol, otherwise 0 */
|
||||
func int Value.isSym(Value *sym) @inline
|
||||
{
|
||||
return sym.isEnc(SymEnc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return 1 if symbol starts with a uppercase letter or $
|
||||
*/
|
||||
int isGlobal(Value sym)
|
||||
{
|
||||
assert(isSym(sym));
|
||||
wchar_t c = (sym_cstr(sym))[0];
|
||||
return iswupper(c) || c == '$';
|
||||
}
|
||||
|
||||
/* Iterate to next symbol after key in symbol table (or first if key is NULL). Return Null if no more.
|
||||
* This can be used to sequentially iterate through the symbol table.
|
||||
* Results may be inaccurate if the symbol table is changed during iteration.
|
||||
*/
|
||||
func Value next(Value th, Value key)
|
||||
{
|
||||
SymTable *sym_tbl = &th(th)->vm->sym_table;
|
||||
SymInfo *sym;
|
||||
|
||||
// If table empty, return null
|
||||
if (sym_tbl.nbrUsed == 0) return aNull;
|
||||
|
||||
// If key is null, return first symbol in table
|
||||
if (key == aNull)
|
||||
{
|
||||
SymInfo **symtblp = sym_tbl->symArray;
|
||||
while ((sym=*symtblp++) == nil);
|
||||
return cast(sym, Value);
|
||||
}
|
||||
|
||||
// If key is not a symbol, return null
|
||||
if (!key.isSym()) return aNull;
|
||||
|
||||
// Look for the symbol in table, then return next one
|
||||
AuintIdx hash = ((SymInfo*)key)->hash;
|
||||
Auint len = ((SymInfo*)key)->size;
|
||||
Auint i = hash_binmod(hash, sym_tbl->nbrAvail);
|
||||
for (sym = sym_tbl->symArray[i]; sym != NULL; sym = (SymInfo*) sym->next) {
|
||||
if (hash == sym->hash &&
|
||||
len == sym->size &&
|
||||
(memcmp(sym_cstr(key), sym_cstr(sym), len) == 0)) {
|
||||
// If the next one is populated, return it
|
||||
if ((sym = (SymInfo*) sym->next))
|
||||
return (Value) sym;
|
||||
// Look for next non-null entry in symbol array
|
||||
for (i++; i<sym_tbl->nbrAvail; i++) {
|
||||
if ((sym=sym_tbl->symArray[i]))
|
||||
return (Value) sym;
|
||||
}
|
||||
return aNull; // No next symbol, return null
|
||||
}
|
||||
}
|
||||
return aNull;
|
||||
}
|
||||
|
||||
|
||||
140
resources/examples/acornvm/typ_all.c3
Normal file
140
resources/examples/acornvm/typ_all.c3
Normal file
@@ -0,0 +1,140 @@
|
||||
/** All type methods and properties
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
#include "avmlib.h"
|
||||
|
||||
/** <=> */
|
||||
int Value.all_compare(Value* th)
|
||||
{
|
||||
if (th.getTop() > 1 && th.getLocal(0) == th.getLocal(1))
|
||||
{
|
||||
th.pushValue(anInt(0));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** === Exact match of values */
|
||||
int Value.all_same(Value* th)
|
||||
{
|
||||
th.pushValue(th.getTop()> 1 && th.getLocal(0) == th.getLocal(1)? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
macro auto @all_rocket!($th)
|
||||
{
|
||||
if (th.getTop(th) < 2) return 0;
|
||||
th.pushValue(vmlit(SymRocket));
|
||||
th.pushValue(th.getLocal(0));
|
||||
th.pushValue(th.getLocal(1));
|
||||
th.getCall(2, 1);
|
||||
return th.popValue();
|
||||
}
|
||||
|
||||
/** ~~, == equal using <=> */
|
||||
int Value.all_equal(Value *th)
|
||||
{
|
||||
th.pushValue(@all_rocket!(th) == anInt(0) ? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** < */
|
||||
int Value.all_lesser(Value *th)
|
||||
{
|
||||
th.pushValue(@all_rocket!(th) == anInt(-1)? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** > */
|
||||
int Value.all_greater(Value *th) {
|
||||
th.pushValue(@all_rocket!(th) == anInt(1)? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** <= */
|
||||
int Value.all_lesseq(Value* th)
|
||||
{
|
||||
th.pushValue(@all_rocket!(th) == anInt(-1) || ret == anInt(0)? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** >= */
|
||||
int Value.all_greateq(Value* th)
|
||||
{
|
||||
Value ret = all_rocket!(th);
|
||||
th.pushValue(ret == anInt(1) || ret == anInt(0)? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** executable? */
|
||||
int Value.all_isexec(Value* th)
|
||||
{
|
||||
th.pushValue(canCall(th.getLocal(0)) ? aTrue : aFalse);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** type */
|
||||
int Value.all_type(Value* th)
|
||||
{
|
||||
th.pushValue(th.getType(th.getLocal(0)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** property */
|
||||
int Value.all_property(Value* th)
|
||||
{
|
||||
if (th.getTop() > 1)
|
||||
{
|
||||
th.pushValue(th.getProperty(th.getLocal(0), th.getLocal(1)));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** .Mixin(mixin) */
|
||||
int Value.all_mixin(Value* th)
|
||||
{
|
||||
if (th.getTop() > 1) th.addMixin(th.getLocal(0), th.getLocal(1));
|
||||
th.setTop(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** Initialize the All type */
|
||||
void core_all_init(Value th)
|
||||
{
|
||||
vmlit(TypeAll) = th.pushMixin(vmlit(TypeObject), aNull, 32);
|
||||
th.pushSym("All");
|
||||
popProperty(th, 0, "_name");
|
||||
pushCMethod(th, all_compare);
|
||||
popProperty(th, 0, "<=>");
|
||||
pushCMethod(th, all_equal);
|
||||
popProperty(th, 0, "~~");
|
||||
pushCMethod(th, all_equal);
|
||||
popProperty(th, 0, "==");
|
||||
pushCMethod(th, all_same);
|
||||
popProperty(th, 0, "===");
|
||||
pushCMethod(th, all_lesser);
|
||||
popProperty(th, 0, "<");
|
||||
pushCMethod(th, all_lesseq);
|
||||
popProperty(th, 0, "<=");
|
||||
pushCMethod(th, all_greater);
|
||||
popProperty(th, 0, ">");
|
||||
pushCMethod(th, all_greateq);
|
||||
popProperty(th, 0, ">=");
|
||||
pushCMethod(th, all_isexec);
|
||||
popProperty(th, 0, "callable?");
|
||||
pushCMethod(th, all_property);
|
||||
popProperty(th, 0, "property");
|
||||
pushCMethod(th, all_type);
|
||||
popProperty(th, 0, "type");
|
||||
pushCMethod(th, all_mixin);
|
||||
popProperty(th, 0, "Mixin");
|
||||
th.popGloVar("All");
|
||||
return;
|
||||
}
|
||||
|
||||
80
resources/examples/acornvm/types.c3
Normal file
80
resources/examples/acornvm/types.c3
Normal file
@@ -0,0 +1,80 @@
|
||||
module acornvm::types;
|
||||
|
||||
enum TokenType
|
||||
{
|
||||
LITERAL, //!< Literal token: null, true, false, int, float, symbol, string
|
||||
URL, //!< Literal url
|
||||
NAME, //!< Named identifier (e.g., for a variable)
|
||||
RESERVED, //!< Reserved word or operator
|
||||
EOF //!< End of file
|
||||
}
|
||||
|
||||
typedef int as AintIdx;
|
||||
typedef uint as AuintIdx;
|
||||
typedef byte as AByte;
|
||||
|
||||
struct MemCommonInfo
|
||||
{
|
||||
MemInfo* next; /**< Pointer to next memory block in chain */ \
|
||||
AByte enctyp; /**< Encoding type (see EncType) */ \
|
||||
AByte marked; /**< Garbage collection flags */ \
|
||||
AByte flags1; /**< Encoding-specific flags */ \
|
||||
AByte flags2; /**< Encoding-specific flags */ \
|
||||
AuintIdx size /**< Encoding-specific sizing info */
|
||||
}
|
||||
|
||||
struct MemCommonInfoGray
|
||||
{
|
||||
inline MemCommonInfo;
|
||||
MemCommonInfoGray* grayLink;
|
||||
}
|
||||
|
||||
struct MemCommonInfoT
|
||||
{
|
||||
inline MemCommonInfoGray;
|
||||
Value type;
|
||||
}
|
||||
|
||||
struct MemInfo
|
||||
{
|
||||
inline MemCommonInfo;
|
||||
}
|
||||
|
||||
struct MemInfoGray
|
||||
{
|
||||
inline MemCommonInfoGray;
|
||||
}
|
||||
|
||||
struct MemInfoT
|
||||
{
|
||||
inline MemCommonInfoT;
|
||||
}
|
||||
|
||||
struct LexInfo
|
||||
{
|
||||
inline MemInfoGray; //!< Common header
|
||||
|
||||
Value source; //!< The source text
|
||||
Value url; //!< The url where the source text came from
|
||||
Value token; //!< Current token
|
||||
Value th; //!< Current thread
|
||||
|
||||
// Position info
|
||||
AuintIdx bytepos; //!< Current byte position in source
|
||||
AuintIdx linenbr; //!< Current line number
|
||||
AuintIdx linebeg; //!< Beginning of current line
|
||||
AuintIdx tokbeg; //!< Start of current token in source
|
||||
AuintIdx tokline; //!< Line number for current token
|
||||
AuintIdx toklinepos; //!< Column position in line for current token
|
||||
|
||||
// indent state
|
||||
uint curindent; //!< Current level of indentation
|
||||
uint newindent; //!< Indentation level for current line
|
||||
|
||||
int optype; //!< sub-type of operator (when type==Op_Token)
|
||||
TokenType toktype; //!< type of the current token
|
||||
bool newline; //!< True if we just started a new non-continued line
|
||||
bool newprogram; //!< True if we have not yet processed any token
|
||||
bool insertSemi; //!< True if we need to insert ';' as next token
|
||||
bool undentcont; //!< True if we are processing undenting on a line continuation
|
||||
}
|
||||
194
resources/examples/acornvm/value.c3
Normal file
194
resources/examples/acornvm/value.c3
Normal file
@@ -0,0 +1,194 @@
|
||||
module acornvm::value;
|
||||
|
||||
|
||||
/** A convenience macro for assert(), establishing the conditions expected to be true,
|
||||
* before returning expression e */
|
||||
macro @assert_exp($c, auto $e)
|
||||
{
|
||||
assert($c);
|
||||
return $e;
|
||||
}
|
||||
|
||||
/**
|
||||
Define Value and C-types.
|
||||
We want all our Value-based types sized the same,
|
||||
according to the architecture (e.g., all 32-bit or all 64-bit).
|
||||
*/
|
||||
|
||||
/** A signed integer, whose size matches Value */
|
||||
typedef isize Aint;
|
||||
/** An unsigned integer, whose size matches Value */
|
||||
typedef usize Auint;
|
||||
|
||||
|
||||
/** A float, whose size matches Value (see avm_env.h) */
|
||||
$assert(usize.size == 8 || usize.size == 4)
|
||||
$if (usize.size == 8)
|
||||
{
|
||||
typedef double as Afloat;
|
||||
}
|
||||
$else
|
||||
{
|
||||
typedef float as Afloat;
|
||||
}
|
||||
|
||||
/** A unicode character */
|
||||
typedef ulong Auchar;
|
||||
|
||||
/** A fixed-sized, self-typed encoded value which holds any kind of data.
|
||||
* It can be passed to or returned from Acorn or C-methods.
|
||||
* Never manipulate a Value directly; always use an AcornVM api function.
|
||||
*
|
||||
* Its size is that of a full address-space pointer (32- or 64-bits).
|
||||
* It holds either an immediate value (null, true, false, integer, float, symbol)
|
||||
* or a pointer to a compound/complex value's header.
|
||||
*/
|
||||
typedef void* as distinct Value
|
||||
|
||||
/** Prototype for a C method callable by the VM.
|
||||
It is passed the thread, through which it obtains parameters via the data stack.
|
||||
When done, it returns how many return values it has placed on the stack. */
|
||||
typedef func int(Value) as AcMethodp;
|
||||
|
||||
/** Quick, exact equivalence check between two values ('===')
|
||||
* Great for null, false, true, integers and symbols.
|
||||
* Less suitable for floats (no epsilon) and comparing contents of containers (e.g., strings).
|
||||
* Is fast because it avoids using type-specific methods. */
|
||||
macro @isSame(a, b) { return (a == b); }
|
||||
|
||||
/** What type of data is encoded within the value, as established by the last 2 bits.
|
||||
* Because of 32/64-bit allocation alignment, pointers always have 0 in last 2 bits.
|
||||
* Thus, non-zero bits can be used to indicate a non-pointer Value. */
|
||||
enum ValBits
|
||||
{
|
||||
POINTER = 0, /*! Value points to a compound value's header */
|
||||
INT = 1, /*! Value is a signed integer */
|
||||
FLOAT = 2, /*! Value is a floating-point number */
|
||||
CONS = 3 /*! Value is a constant (null, false, true) */
|
||||
}
|
||||
|
||||
/** The mask used to isolate the value's ValBits info */
|
||||
const int VAL_MASK = 0x3;
|
||||
/** How many bits to shift a Value to remove or make space for ValBits info */
|
||||
const int VAL_SHIFT = 2;
|
||||
|
||||
|
||||
func bool Value.isEnc(Value *value, EncType type) @inline
|
||||
{
|
||||
return value.isPtr() && @cast(value, MemInfo*).enctyp == type;
|
||||
}
|
||||
|
||||
/* Return true if the value is a c-String, otherwise 0 */
|
||||
bool Value.isStr(Value *str)
|
||||
{
|
||||
return str.isEnc(StrEnc) && !(str_info(str)->flags1 & StrCData);
|
||||
}
|
||||
|
||||
macro @isType(v, ValBits e)
|
||||
{
|
||||
return @cast(v, Auint) & VAL_MASK == e;
|
||||
}
|
||||
|
||||
// Integer value functions
|
||||
|
||||
|
||||
/** Is v an Integer? */
|
||||
func bool Value.isInt(Value *v)
|
||||
{
|
||||
return @isType(*v, INT);
|
||||
}
|
||||
|
||||
/** Cast c-integer n into an Integer value
|
||||
* This loses top two-bits of integer precision.
|
||||
* If integer is too large, this could result in an unexpected value and change of sign. */
|
||||
macro @anInt(n)
|
||||
{
|
||||
return @cast(@cast(n, Aint) << VAL_SHIFT + ValInt, Value);
|
||||
}
|
||||
|
||||
/** Cast an Integer value into a c-integer
|
||||
* Note: It assumes (and won't verify) that v is an Integer */
|
||||
macro @toAint(v)
|
||||
{
|
||||
return @cast(v, Aint) >> VAL_SHIFT;
|
||||
}
|
||||
|
||||
// Float value functions
|
||||
|
||||
/** Is v a Float? */
|
||||
func Value.isFloat(Value *v)
|
||||
{
|
||||
return @isType(*v, FLOAT);
|
||||
}
|
||||
|
||||
/** Cast c-float n into a Float value
|
||||
* This loses bottom two-bits of Float mantissa precision. */
|
||||
AVM_API Value aFloat(Afloat n);
|
||||
|
||||
/** Cast an Float value into a c-float
|
||||
* Note: It assumes (and won't verify) that v is an Float */
|
||||
AVM_API Afloat toAfloat(Value v);
|
||||
|
||||
/* *******************************************************
|
||||
null, false and true values and functions.
|
||||
(they are encoded in the impossible space for a symbol pointer
|
||||
**************************************************** */
|
||||
|
||||
/** The null value */
|
||||
macro @aNull()
|
||||
{
|
||||
return @cast(0 << VAL_SHIFT, ValCons, Value);
|
||||
}
|
||||
|
||||
/** The false value */
|
||||
macro @aFalse()
|
||||
{
|
||||
return @cast(1 << VAL_SHIFT + ValCons, Value);
|
||||
}
|
||||
|
||||
/** The true value */
|
||||
macro @aTrue()
|
||||
{
|
||||
return @cast(2 << VAL_SHIFT + ValCons, Value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Is value null?
|
||||
* @require value != nil
|
||||
*/
|
||||
func bool Value.isNull(Value *value) @inline
|
||||
{
|
||||
return *v == aNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is value false or null?
|
||||
* @require value != nil
|
||||
*/
|
||||
func bool Value.isFalse(Value *value) @inline
|
||||
{
|
||||
return *v == aFalse || *v == aNull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is value true or false?
|
||||
*/
|
||||
func bool Value.isBool(Value *value) @inline
|
||||
{
|
||||
return *v >= aFalse;
|
||||
}
|
||||
|
||||
|
||||
// Pointer functions.
|
||||
|
||||
/** Is value a pointer? */
|
||||
func bool Value.isPtr(Value *value) @inline
|
||||
{
|
||||
return @isType(*v, POINTER);
|
||||
}
|
||||
|
||||
|
||||
/** Append serialized val to end of str. */
|
||||
void serialize(Value th, Value str, int indent, Value val);
|
||||
|
||||
596
resources/examples/acornvm/vm.c3
Normal file
596
resources/examples/acornvm/vm.c3
Normal file
@@ -0,0 +1,596 @@
|
||||
module acornvm::vm;
|
||||
|
||||
|
||||
void vm_litinit(Value th); // Initializer for literals
|
||||
void vm_stdinit(Value th); // Initializer for standard symbols
|
||||
void core_init(Value th); // Initialize all core types
|
||||
|
||||
|
||||
/** Manage the Virtual Machine instance.
|
||||
*
|
||||
* This is the heart of the Acorn Virtual Machine. It manages:
|
||||
* - All memory and garbage collection (avm_memory.h), working with the
|
||||
* different encoding types.
|
||||
* - The symbol table, which is shared across everywhere
|
||||
* - The main thread, which is the recursive root for garbage collection.
|
||||
* The thread manages the global namespace, including all registered
|
||||
* core types (including the Acorn compiler and resource types).
|
||||
*
|
||||
* See newVm() for more detailed information on VM initialization.
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* This source file is part of avm - Acorn Virtual Machine.
|
||||
* See Copyright Notice in avm.h
|
||||
*/
|
||||
|
||||
|
||||
/** Virtual Machine instance information
|
||||
* Is never garbage collected, but is the root for garbage collection. */
|
||||
struct VmInfo
|
||||
{
|
||||
inline MemCommonInfoGray; //!< Common header for value-containing object
|
||||
|
||||
ulong pcgrng_state; //!< PCG random-number generator state
|
||||
ulong pcgrng_inc; //!< PCG random-number generator inc value
|
||||
|
||||
Value global; //!< VM's "built in" Global hash table
|
||||
|
||||
Value main_thread; //!< VM's main thread
|
||||
ThreadInfo main_thr; //!< State space for main thread
|
||||
|
||||
SymTable sym_table; //!< global symbol table
|
||||
AuintIdx hashseed; //!< randomized seed for hashing strings
|
||||
Value literals; //!< array of all built-in symbol and type literals
|
||||
Value stdidx; //!< Table to convert std symbol to index
|
||||
Value* stdsym; //!< c-array to convert index to std symbol
|
||||
|
||||
// Garbage Collection state
|
||||
MemInfo* objlist; //!< linked list of all collectable objects
|
||||
MemInfo** sweepgc; //!< current position of sweep in list 'objlist'
|
||||
MemInfoGray* gray; //!< list of gray objects
|
||||
MemInfo* threads; //!< list of all threads
|
||||
|
||||
Auint sweepsymgc; //!< position of sweep in symbol table
|
||||
|
||||
// Metrics used to govern when GC runs
|
||||
int gctrigger; //!< Memory alloc will trigger GC step when this >= 0
|
||||
int gcstepdelay; //!< How many alloc's to wait between steps
|
||||
int gcnbrnew; //!< number of new objects created since last GC cycle
|
||||
int gcnbrold; //!< number of old objects created since last gen GC cycle
|
||||
int gcnewtrigger; //!< Start a new GC cycle after this exceeds gcnbrnew
|
||||
int gcoldtrigger; //!< Make next GC cycle full if this exceeds gcnbrold
|
||||
int gcstepunits; //!< How many work units left to consume in GC step
|
||||
|
||||
// Statistics gathering for GC
|
||||
int gcnbrmarks; //!< How many objects were marked this cycle
|
||||
int gcnbrfrees; //!< How many objects were freed during cycle's sweep
|
||||
int gcmicrodt; //!< The clock's micro-seconds measured at start of cycle
|
||||
|
||||
Auint totalbytes; //!< number of bytes currently allocated
|
||||
|
||||
char gcmode; //!< Collection mode: Normal, Emergency, Gen
|
||||
char gcnextmode; //!< Collection mode for next cycle
|
||||
char gcstate; //!< state of garbage collector
|
||||
char gcrunning; //!< true if GC is running
|
||||
char currentwhite; //!< Current white color for new objects
|
||||
|
||||
char gcbarrieron; //!< Is the write protector on? Yes prevents black->white
|
||||
}
|
||||
|
||||
/** Mark all in-use thread values for garbage collection
|
||||
* Increments how much allocated memory the thread uses.
|
||||
*/
|
||||
macro @vmMark(th, v)
|
||||
{
|
||||
mem_markobj(th, v.main_thread);
|
||||
mem_markobj(th, v.global);
|
||||
mem_markobj(th, v.literals);
|
||||
mem_markobj(th, v.stdidx);
|
||||
}
|
||||
|
||||
macro vmStdSym(th, idx) { return vm(th).stdsym[idx]; }
|
||||
|
||||
const N_STD_SYM = 256;
|
||||
|
||||
/** C index values for all VM literal values used throughout the code
|
||||
for common symbols and core types. They are forever immune from garbage collection
|
||||
by being anchored to the VM. */
|
||||
enum VmLiterals
|
||||
{
|
||||
// Compiler symbols
|
||||
SYM_NULL, //!< 'null'
|
||||
SYM_FALSE, //!< 'false'
|
||||
SYM_TRUE, //!< 'true'
|
||||
SYM_AND, //!< 'and'
|
||||
SymAsync, //!< 'async'
|
||||
SymBaseurl, //!< 'baseurl'
|
||||
SymBreak, //!< 'break'
|
||||
SymContext, //!< 'context'
|
||||
SymContinue, //!< 'continue'
|
||||
SymDo, //!< 'do'
|
||||
SymEach, //!< 'each'
|
||||
SymElse, //!< 'else'
|
||||
SymElif, //!< 'elif'
|
||||
SymIf, //!< 'if'
|
||||
SymIn, //!< 'in'
|
||||
SymInto, //!< 'into'
|
||||
SymMatch, //!< 'match'
|
||||
SymNot, //!< 'not'
|
||||
SymOr, //!< 'or'
|
||||
SymReturn, //!< 'return'
|
||||
SymSelf, //!< 'self'
|
||||
SymSelfMeth, //!< 'selfmethod'
|
||||
SymThis, //!< 'this'
|
||||
SymUsing, //!< 'using'
|
||||
SymVar, //!< 'var'
|
||||
SymWait, //!< 'wait'
|
||||
SymWhile, //!< 'while'
|
||||
SymWith, //!< 'with'
|
||||
SymYield, //!< 'yield'
|
||||
SymLBrace, //!< '{'
|
||||
SymRBrace, //!< '}'
|
||||
SymSemicolon, //!< ';'
|
||||
SymComma, //!< ','
|
||||
SymQuestion, //!< '?'
|
||||
SymAt, //!< '@'
|
||||
SymSplat, //!< '...'
|
||||
SymDot, //!< '.'
|
||||
SymColons, //!< '::'
|
||||
SymDotColon, //!< '.:'
|
||||
|
||||
// Compiler symbols that are also methods
|
||||
SymAppend, //!< '<<'
|
||||
SymPrepend, //!< '>>'
|
||||
SymPlus, //!< '+'
|
||||
SymMinus, //!< '-'
|
||||
SymMult, //!< '*'
|
||||
SymDiv, //!< '/'
|
||||
SymRocket, //!< '<=>'
|
||||
SymEquiv, //!< '==='
|
||||
SymMatchOp, //!< '~~'
|
||||
SymLt, //!< '<'
|
||||
SymLe, //!< '<='
|
||||
SymGt, //!< '>'
|
||||
SymGe, //!< '>='
|
||||
SymEq, //!< '=='
|
||||
SymNe, //!< '!='
|
||||
|
||||
// Methods that are not compiler symbols
|
||||
// Byte-code (and parser) standard methods
|
||||
SYM_NEW, //!< 'New'
|
||||
SYM_INIT, //!< 'Init'
|
||||
SYM_LOAD, //!< 'Load'
|
||||
SymGet, //!< 'Get'
|
||||
SymParas, //!< '()'
|
||||
SymBrackets, //!< '[]'
|
||||
SymNeg, //!< '-@'
|
||||
SymValue, //!< 'value'
|
||||
SymEachMeth, //!< 'Each'
|
||||
SymBegin, //!< 'Begin'
|
||||
SymEnd, //!< 'End'
|
||||
|
||||
SymFinalizer, //!< '_finalizer' method for CData
|
||||
SymName, //!< '_type' symbol
|
||||
|
||||
// AST symbols
|
||||
SymMethod, //!< 'method'
|
||||
SymAssgn, //!< '='
|
||||
SymOrAssgn, //!< '||='
|
||||
SymColon, //!< ':'
|
||||
SymThisBlock, //!< 'thisblock'
|
||||
SymCallProp, //!< 'callprop'
|
||||
SymActProp, //!< 'activeprop'
|
||||
SymRawProp, //!< 'rawprop'
|
||||
SymGlobal, //!< 'global'
|
||||
SymLocal, //!< 'local'
|
||||
SymLit, //!< 'lit'
|
||||
SymExt, //!< 'ext'
|
||||
SymRange, //!< 'Range'
|
||||
SymClosure, //!< 'Closure'
|
||||
SymYielder, //!< 'Yielder'
|
||||
SymResource, //!< 'Resource'
|
||||
|
||||
// Core type type
|
||||
TypeObject, //!< Type
|
||||
TypeMixinc, //!< Mixin class
|
||||
TypeMixinm, //!< Mixin mixin
|
||||
TypeNullc, //!< Null class
|
||||
TypeNullm, //!< Null mixin
|
||||
TypeBoolc, //!< Float class
|
||||
TypeBoolm, //!< Float mixin
|
||||
TypeIntc, //!< Integer class
|
||||
TypeIntm, //!< Integer mixin
|
||||
TypeFloc, //!< Float class
|
||||
TypeFlom, //!< Float mixin
|
||||
TypeMethc, //!< Method class
|
||||
TypeMethm, //!< Method mixin
|
||||
TypeYieldc, //!< Yielder class
|
||||
TypeYieldm, //!< Yielder mixin
|
||||
TypeVmc, //!< Vm class
|
||||
TypeVmm, //!< Vm mixin
|
||||
TypeSymc, //!< Symbol class
|
||||
TypeSymm, //!< Symbol mixin
|
||||
TypeRangec, //!< Range class
|
||||
TypeRangem, //!< Range mixin
|
||||
TypeTextc, //!< Text class
|
||||
TypeTextm, //!< Text mixin
|
||||
TypeListc, //!< List class
|
||||
TypeListm, //!< List mixin
|
||||
TypeCloc, //!< Closure class
|
||||
TypeClom, //!< Closure mixin
|
||||
TypeIndexc, //!< Index class
|
||||
TypeIndexm, //!< Index mixin
|
||||
TypeResc, //!< Index class
|
||||
TypeResm, //!< Index mixin
|
||||
TypeAll, //!< All
|
||||
|
||||
//! Number of literals
|
||||
nVmLits
|
||||
}
|
||||
|
||||
macro @vmlit!(lit) { return arr_inf(vm(th)->literals)->arr[list]; }
|
||||
|
||||
|
||||
/** Used by vm_init to build random seed */
|
||||
macro @memcpy_Auint(i, val)
|
||||
{
|
||||
Auint anint = @cast(val, Auint);
|
||||
memcpy(seedstr + i * sizeof(Auint), &anint, sizeof(Auint));
|
||||
}
|
||||
|
||||
/** Create and initialize new Virtual Machine
|
||||
* When a VM is started:
|
||||
* - Iit dynamically allocates the VmInfo
|
||||
* which holds all universal information about the VM instance.
|
||||
* - Memory management and garbage collection (avm_memory.h) is managed at this level.
|
||||
* The GC root value (the main thread) determines what allocated values to keep
|
||||
* and which to discard.
|
||||
* - All value encodings are initialized next, including the single symbol table
|
||||
* used across the VM.
|
||||
* - The main thread is started up, initializing its global namespace.
|
||||
* - All core types are progressively loaded, establishing the default types for
|
||||
* each encoding. This includes the resource types and Acorn compiler. */
|
||||
func Value new(void)
|
||||
{
|
||||
logInfo(AVM_RELEASE " started.");
|
||||
|
||||
// Create VM info block and start up memory management
|
||||
VmInfo* vm = @amalloc(VmInfo);
|
||||
vm.enctyp = VmEnc;
|
||||
mem_init(vm); /* Initialize memory & garbage collection */
|
||||
|
||||
// VM is GC Root: Never marked or collected. Black will trigger write barrier
|
||||
vm.marked = bitmask(BLACKBIT);
|
||||
|
||||
// Initialize main thread (allocated as part of VmInfo)
|
||||
Value th = cast(vm->main_thread = &vm->main_thr, Value);
|
||||
ThreadInfo* threadInfo = cast(th, threadInfo);
|
||||
threadInfo.marked = vm.currentwhite;
|
||||
threadInfo.enctyp = ThrEnc;
|
||||
threadInfo.next = nil;
|
||||
thrInit(&vm.main_thr, vm, aNull, STACK_NEWSIZE, 0);
|
||||
vm.threads = nil;
|
||||
|
||||
// Initialize PCG random number generator to starting values
|
||||
vm.pcgrng_state = 0x853c49e6748fea9b;
|
||||
vm.pcgrng_inc = 0xda3e39cb94b95bdb;
|
||||
|
||||
// Compute a randomized seed, using address space layout to increaase randomness
|
||||
// Seed is used to help calculate randomly distributed symbol hashes
|
||||
char seedstr[4 * sizeof(Auint)];
|
||||
Time timehash = time(nil);
|
||||
memcpy_Auint(0, vm) // heap pointe
|
||||
memcpy_Auint(1, timehash) // current time in seconds
|
||||
memcpy_Auint(2, &timehash) // local variable pointe
|
||||
memcpy_Auint(3, &newVM) // public function
|
||||
vm->hashseed = tblCalcStrHash(seedstr, sizeof(seedstr), (AuintIdx) timehash);
|
||||
|
||||
// Initialize vm-wide symbol table, global table and literals
|
||||
sym_init(th); // Initialize hash table for symbols
|
||||
newTbl(th, &vm->global, aNull, GLOBAL_NEWSIZE); // Create global hash table
|
||||
mem_markChk(th, vm, vm->global);
|
||||
vm_litinit(th); // Load reserved and standard symbols into literal list
|
||||
core_init(th); // Load up global table and literal list with core types
|
||||
setType(th, vm->global, vmlit(TypeIndexm)); // Fix up type info for global table
|
||||
|
||||
// Initialize byte-code standard methods and the Acorn compiler
|
||||
vm_stdinit(th);
|
||||
|
||||
// Start garbage collection
|
||||
mem_gcstart(th);
|
||||
|
||||
return th;
|
||||
}
|
||||
|
||||
/* Close down the virtual machine, freeing all allocated memory */
|
||||
void vmClose(Value th) {
|
||||
th = vm(th)->main_thread;
|
||||
VmInfo* vm = vm(th);
|
||||
mem::freeAll(th); /* collect all objects */
|
||||
mem::reallocvector(th, vm->stdsym, nStdSym, 0, Value);
|
||||
sym_free(th);
|
||||
thrFreeStacks(th);
|
||||
assert(vm(th)->totalbytes == sizeof(VmInfo));
|
||||
mem::frealloc(vm(th), 0); /* free main block */
|
||||
logInfo(AVM_RELEASE " ended.");
|
||||
}
|
||||
|
||||
/* Lock the Vm */
|
||||
void vm_lock(Value th)
|
||||
{
|
||||
}
|
||||
|
||||
/* Unlock the Vm */
|
||||
void vm_unlock(Value th)
|
||||
{
|
||||
}
|
||||
|
||||
/* Interval timer */
|
||||
$if ($platform == "win32" || $platform == "win64")
|
||||
{
|
||||
|
||||
int64_t vmStartTimer()
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
QueryPerformanceCounter(&li);
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
float vmEndTimer(int64_t starttime)
|
||||
{
|
||||
LARGE_INTEGER now, freq;
|
||||
QueryPerformanceCounter(&now);
|
||||
QueryPerformanceFrequency(&freq);
|
||||
return float(now.QuadPart-starttime)/float(freq.QuadPart);
|
||||
}
|
||||
|
||||
}
|
||||
$else
|
||||
{
|
||||
func int64_t vmStartTimer()
|
||||
{
|
||||
TimeVal start;
|
||||
start.gettimeofday();
|
||||
return start.tv_sec * 1000000 + start.tv_usec;
|
||||
}
|
||||
|
||||
float vmEndTimer(int64_t starttime)
|
||||
{
|
||||
TimeVal now;
|
||||
now.gettimeofday();
|
||||
int64_t end = now.tv_sec * 1000000 + end.tv_usec;
|
||||
return @cast(end - starttime)/1000000.0, float);
|
||||
}
|
||||
}
|
||||
|
||||
#include <stdarg.h>
|
||||
/* Log a message to the logfile */
|
||||
|
||||
void vmLog(const char *msg, ...)
|
||||
{
|
||||
// Start line with timestamp
|
||||
time_t ltime;
|
||||
char timestr[80];
|
||||
ltime=time(NULL);
|
||||
strftime (timestr, sizeof(timestr), "%X %x ", localtime(<ime));
|
||||
fputs(timestr, stderr);
|
||||
|
||||
// Do a formatted output, passing along all parms
|
||||
va_list argptr;
|
||||
va_start(argptr, msg);
|
||||
vfprintf(stderr, msg, argptr);
|
||||
va_end(argptr);
|
||||
fputs("\n", stderr);
|
||||
|
||||
// Ensure log file gets it
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/** Mapping structure correlating a VM literal symbol's number with its name */
|
||||
struct vmLitSymEntry
|
||||
{
|
||||
int litindex; //!< Literal symbol's number
|
||||
string symnm; //!< Literal symbol's string
|
||||
};
|
||||
|
||||
/** Constant array that identifies and maps all VM literal symbols */
|
||||
vmLitSymEntry[+] vmLitSymTable = {
|
||||
// Compiler reserved names
|
||||
{SymNull, "null"},
|
||||
{SymFalse, "false"},
|
||||
{SymTrue, "true"},
|
||||
{SymAnd, "and"},
|
||||
{SymAsync, "async"},
|
||||
{SymBaseurl, "baseurl"},
|
||||
{SymBreak, "break"},
|
||||
{SymContext, "context"},
|
||||
{SymContinue, "continue"},
|
||||
{SymDo, "do"},
|
||||
{SymEach, "each"},
|
||||
{SymElse, "else"},
|
||||
{SymElif, "elif"},
|
||||
{SymIf, "if"},
|
||||
{SymIn, "in"},
|
||||
{SymInto, "into"},
|
||||
{SymLocal, "local"},
|
||||
{SymMatch, "match"},
|
||||
{SymNot, "not"},
|
||||
{SymOr, "or"},
|
||||
{SymReturn, "return"},
|
||||
{SymSelf, "self"},
|
||||
{SymSelfMeth, "selfmethod"},
|
||||
{SymThis, "this"},
|
||||
{SymUsing, "using"},
|
||||
{SymWait, "wait"},
|
||||
{SymWhile, "while"},
|
||||
{SymWith, "with"},
|
||||
{SymYield, "yield"},
|
||||
{SymLBrace, "{"},
|
||||
{SymRBrace, "}"},
|
||||
{SymSemicolon, ";"},
|
||||
{SymComma, ","},
|
||||
{SymQuestion, "?"},
|
||||
{SymAt, "@"},
|
||||
{SymSplat, "..."},
|
||||
{SymDot, "."},
|
||||
{SymColons, "::"},
|
||||
{SymDotColon, ".:"},
|
||||
|
||||
// Compiler symbols that are also methods
|
||||
{SymAppend, "<<"},
|
||||
{SymPrepend, ">>"},
|
||||
{SymPlus, "+"},
|
||||
{SymMinus, "-"},
|
||||
{SymMult, "*"},
|
||||
{SymDiv, "/"},
|
||||
{SymRocket, "<=>"},
|
||||
{SymEquiv, "==="},
|
||||
{SymMatchOp, "~~"},
|
||||
{SymLt, "<"},
|
||||
{SymLe, "<="},
|
||||
{SymGt, ">"},
|
||||
{SymGe, ">="},
|
||||
{SymEq, "=="},
|
||||
{SymNe, "!="},
|
||||
|
||||
// Methods that are not compiler symbols
|
||||
{SymNew, "New"},
|
||||
{SymInit, "Init"},
|
||||
{SymLoad, "Load"},
|
||||
{SymGet, "Get"},
|
||||
{SymParas, "()"},
|
||||
{SymBrackets, "[]"},
|
||||
{SymNeg, "-@"},
|
||||
{SymValue, "value"},
|
||||
{SymEachMeth, "Each"},
|
||||
{SymBegin, "Begin"},
|
||||
{SymEnd, "End"},
|
||||
|
||||
{SymFinalizer, "_finalizer"},
|
||||
{SymName, "_name"},
|
||||
|
||||
// AST symbols
|
||||
{SymMethod, "method"},
|
||||
{SymAssgn, "="},
|
||||
{SymOrAssgn, "||="},
|
||||
{SymColon, ":"},
|
||||
{SymThisBlock, "thisblock"},
|
||||
{SymCallProp, "callprop"},
|
||||
{SymActProp, "activeprop"},
|
||||
{SymRawProp, "rawprop"},
|
||||
{SymGlobal, "global"},
|
||||
{SymLit, "lit"},
|
||||
{SymExt, "ext"},
|
||||
{SymRange, "Range"},
|
||||
{SymClosure, "Closure"},
|
||||
{SymYielder, "Yielder"},
|
||||
{SymResource, "Resource"},
|
||||
|
||||
// End of literal table
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
/** Initialize vm's literals. */
|
||||
void vm_litinit(Value th) {
|
||||
// Allocate untyped array for literal storage
|
||||
VmInfo* vm = vm(th);
|
||||
newArr(th, &vm->literals, aNull, nVmLits);
|
||||
mem_markChk(th, vm, vm->literals);
|
||||
arrSet(th, vm->literals, nVmLits-1, aNull); // Ensure it is full with nulls
|
||||
|
||||
Value *vmlits = arr_info(vm->literals)->arr;
|
||||
vmlits[TypeObject] = aNull;
|
||||
|
||||
// Load up literal symbols from table
|
||||
const struct vmLitSymEntry *vmlittblp = &vmLitSymTable[0];
|
||||
while (vmlittblp->symnm) {
|
||||
newSym(th, &vmlits[vmlittblp->litindex], vmlittblp->symnm, strlen(vmlittblp->symnm));
|
||||
vmlittblp++;
|
||||
}
|
||||
}
|
||||
|
||||
/** Map byte-code's standard symbols to VM's literals (max. number at 256) */
|
||||
const int stdTblMap[] = {
|
||||
// Commonly-called methods
|
||||
SymNew, // 'new'
|
||||
SymParas, // '()'
|
||||
SymAppend, // '<<'
|
||||
SymPlus, // '+'
|
||||
SymMinus, // '-'
|
||||
SymMult, // '*'
|
||||
SymDiv, // '/'
|
||||
SymNeg, // '-@'
|
||||
-1
|
||||
};
|
||||
|
||||
/** Initialize vm's standard symbols */
|
||||
void vm_stdinit(Value th) {
|
||||
// Allocate mapping tables
|
||||
VmInfo* vm = vm(th);
|
||||
Value stdidx = newTbl(th, &vm->stdidx, aNull, nStdSym);
|
||||
mem_markChk(th, vm, vm->stdidx);
|
||||
vm->stdsym = NULL;
|
||||
mem_reallocvector(th, vm->stdsym, 0, nStdSym, Value);
|
||||
|
||||
// Populate the mapping tables with the corresponding VM literals
|
||||
const int *mapp = &stdTblMap[0];
|
||||
int idx = 0;
|
||||
while (*mapp >= 0 && idx<nStdSym) {
|
||||
tblSet(th, stdidx, vmlit(*mapp), anInt(idx));
|
||||
vm->stdsym[idx] = vmlit(*mapp);
|
||||
idx++;
|
||||
mapp++;
|
||||
}
|
||||
}
|
||||
|
||||
void core_null_init(Value th);
|
||||
void core_bool_init(Value th);
|
||||
void core_int_init(Value th);
|
||||
void core_float_init(Value th);
|
||||
void core_symbol_init(Value th);
|
||||
void core_range_init(Value th);
|
||||
void core_text_init(Value th);
|
||||
void core_list_init(Value th);
|
||||
void core_clo_init(Value th);
|
||||
void core_index_init(Value th);
|
||||
void core_object_init(Value th);
|
||||
void core_mixin_init(Value th);
|
||||
|
||||
void core_thread_init(Value th);
|
||||
void core_vm_init(Value th);
|
||||
void core_all_init(Value th);
|
||||
|
||||
void core_resource_init(Value th);
|
||||
void core_method_init(Value th);
|
||||
void core_file_init(Value th);
|
||||
|
||||
/** Initialize all core types */
|
||||
void core_init(Value th) {
|
||||
|
||||
core_object_init(th); // Type must be first, so other types can use this as their type
|
||||
vmlit(TypeAll) = pushType(th, aNull, 0);
|
||||
popGloVar(th, "All");
|
||||
core_mixin_init(th);
|
||||
|
||||
core_null_init(th);
|
||||
core_bool_init(th);
|
||||
core_int_init(th);
|
||||
core_float_init(th);
|
||||
core_symbol_init(th);
|
||||
core_range_init(th);
|
||||
core_text_init(th);
|
||||
core_list_init(th);
|
||||
core_clo_init(th);
|
||||
core_index_init(th);
|
||||
|
||||
core_thread_init(th);
|
||||
core_vm_init(th);
|
||||
core_all_init(th);
|
||||
|
||||
// Load resource before the types it uses
|
||||
core_resource_init(th);
|
||||
core_method_init(th);
|
||||
core_file_init(th);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ func int main()
|
||||
|
||||
func string@ bin(int x)
|
||||
{
|
||||
int bits = (x == 0) ? 1 : log10(cast(double, x)) / log10(2);
|
||||
int bits = (x == 0) ? 1 : log10(cast(x, double)) / log10(2);
|
||||
string@ ret = string.make_repeat('0', bits);
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
|
||||
46
resources/examples/functions.c3
Normal file
46
resources/examples/functions.c3
Normal file
@@ -0,0 +1,46 @@
|
||||
module functions;
|
||||
|
||||
module vararray(Type)
|
||||
|
||||
struct VarArray
|
||||
{
|
||||
uint capacity;
|
||||
uint size;
|
||||
Type* type;
|
||||
}
|
||||
|
||||
VarArray* make(uint size = startingSize)
|
||||
{
|
||||
VarArray *array = malloc(VarArray.size);
|
||||
array.capacity = startingSize;
|
||||
array.size = 0;
|
||||
array.type = startingSize > 0 ? malloc(Type.size * startingSize) : null;
|
||||
return array;
|
||||
}
|
||||
|
||||
generic Type[].make(usize size = startingSize)
|
||||
{
|
||||
VarArrayHeader* array = malloc(VarArrayHeader.size + Type.size * startingSize);
|
||||
array.capacity = startingSize;
|
||||
array.size = 0;
|
||||
return @cast(array[1], Type[]);
|
||||
}
|
||||
|
||||
macro Type Type[].@index(&Type[] array, usize index)
|
||||
{
|
||||
VarArrayHeader* array = @cast(array, VarArrayHeader*)[-1];
|
||||
assert(index < array.size, "Out of bounds access");
|
||||
return @cast(array, Type *)[index];
|
||||
}
|
||||
|
||||
foo :: proc($N: $I, $T: typeid) -> (res: [N]T) {
|
||||
// `N` is the constant value passed
|
||||
// `I` is the type of N
|
||||
// `T` is the type passed
|
||||
fmt.printf("Generating an array of type %v from the value %v of type %v\n",
|
||||
typeid_of(type_of(res)), N, typeid_of(I));
|
||||
for i in 0..<N {
|
||||
res[i] = i*i;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -15,17 +15,18 @@ public struct Map
|
||||
uint mod;
|
||||
}
|
||||
|
||||
public func Map@ Map.new()
|
||||
public func Map* Map.init(Map *map)
|
||||
{
|
||||
return @calloc(Map);
|
||||
*map = { };
|
||||
return map;
|
||||
}
|
||||
|
||||
public func Type *Map.valueForKey(Map *map, Key key)
|
||||
public func Type* Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return nil;
|
||||
usize hash = key.hash();
|
||||
usize pos = hash & map.mod;
|
||||
Entry *entry = &map.map[pop];
|
||||
Entry* entry = &map.map[pop];
|
||||
if () return nil;
|
||||
while (entry)
|
||||
{
|
||||
@@ -80,7 +81,7 @@ public func void Map.removeLast(Vector *vector)
|
||||
vector.array.pop();
|
||||
}
|
||||
|
||||
public macro Vector.@foreach(Vector *vector : @body(Type value))
|
||||
public macro Vector.foreach(Vector *vector, macro void(Type value) body)
|
||||
{
|
||||
for (usize i = 0, i < vector.array.size; i++)
|
||||
{
|
||||
@@ -98,7 +99,7 @@ test
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
vector.@foreach(int i)
|
||||
@vector.foreach(int i)
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ const uint MaxDepth = 8;
|
||||
|
||||
//#define DEBUG_NODES
|
||||
|
||||
$if (DEBUG_NODES)
|
||||
$if (DEBUG_NODES):
|
||||
|
||||
func void Blocks.dump(const Blocks* b)
|
||||
{
|
||||
@@ -77,7 +77,7 @@ func void Blocks.dump(const Blocks* b)
|
||||
}
|
||||
}
|
||||
|
||||
$endif
|
||||
$endif;
|
||||
|
||||
/**
|
||||
* @ensure const(a), const(b)
|
||||
@@ -142,11 +142,11 @@ func void Parser.parseTopLevel(Parser* p) throws ParseError
|
||||
{
|
||||
switch (p.tok.kind)
|
||||
{
|
||||
case TokenKind.WORD:
|
||||
case WORD:
|
||||
p.parseKeyValue();
|
||||
case TokenKind.LBRACE:
|
||||
case LBRACE:
|
||||
p.parseTable();
|
||||
case TokenKind.Lbrace2:
|
||||
case LBRACE2:
|
||||
p.parseTableArray();
|
||||
default:
|
||||
sprintf(p.errorMsg, "syntax error %s", p.tok.loc.str());
|
||||
@@ -162,7 +162,7 @@ func uint getRawValue(uint raw) @(inline)
|
||||
|
||||
func ValueType getRawType(uint raw) @(inline)
|
||||
{
|
||||
return cast(ValueType, (raw >> ValueTypeOffset) & 0x3);
|
||||
return cast((raw >> ValueTypeOffset) & 0x3, ValueType);
|
||||
}
|
||||
|
||||
func uint addType(uint raw, ValueType t) @(inline)
|
||||
|
||||
@@ -139,17 +139,17 @@ func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||
t.loc.column = 1;
|
||||
case '=':
|
||||
result.loc = t.loc;
|
||||
result.kind = TokenKind.EQUALS;
|
||||
result.kind = EQUALS;
|
||||
t.advance(1);
|
||||
return;
|
||||
case '.':
|
||||
result.loc = t.loc;
|
||||
result.kind = TokenKind.DOT;
|
||||
result.kind = DOT;
|
||||
t.advance(1);
|
||||
return;
|
||||
case ',':
|
||||
result.loc = t.loc;
|
||||
result.kind = TokenKind.COMMA;
|
||||
result.kind = COMMA;
|
||||
t.advance(1);
|
||||
return;
|
||||
case '[':
|
||||
@@ -157,12 +157,12 @@ func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||
if (t.current[1] == '[')
|
||||
{
|
||||
t.advance(2);
|
||||
result.kind = TokenKind.LBRACE2;
|
||||
result.kind = LBRACE2;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.advance(1);
|
||||
result.kind = TokenKind.LBRACE;
|
||||
result.kind = LBRACE;
|
||||
}
|
||||
return;
|
||||
case ']':
|
||||
@@ -170,12 +170,12 @@ func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||
if (t.current[1] == ']')
|
||||
{
|
||||
t.advance(2);
|
||||
result.kind = TokenKind.RBRACE2;
|
||||
result.kind = RBRACE2;
|
||||
}
|
||||
else
|
||||
{
|
||||
t.advance(1);
|
||||
result.kind = TokenKind.RBRACE;
|
||||
result.kind = RBRACE;
|
||||
}
|
||||
return;
|
||||
case '"':
|
||||
@@ -199,13 +199,13 @@ func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||
if (t.current[0] == 'f' && strncmp("false", t.current, 5) == 0) {
|
||||
t.advance(5);
|
||||
result.number = 0;
|
||||
result.kind = TokenKind.Kw_false;
|
||||
result.kind = KW_FALSE;
|
||||
return;
|
||||
}
|
||||
if (t.current[0] == 't' && strncmp("true", t.current, 4) == 0) {
|
||||
t.advance(4);
|
||||
result.number = 1;
|
||||
result.kind = TokenKind.Kw_true;
|
||||
result.kind = KW_TRUE;
|
||||
return;
|
||||
}
|
||||
if (isalpha(t.current[0]))
|
||||
@@ -214,7 +214,7 @@ func void Tokenizer.lex(Tokenizer* t, Token* result)
|
||||
return;
|
||||
}
|
||||
sprintf(t.text, "unexpected char '%c' at %s", t.current[0], t.loc.str());
|
||||
result.kind = TokenKind.Error;
|
||||
result.kind = ERROR;
|
||||
result.text = t.text;
|
||||
return;
|
||||
}
|
||||
@@ -267,7 +267,7 @@ func void Tokenizer.parseText(Tokenizer* t, Token* result)
|
||||
const char* start = t.current;
|
||||
while (t.current[0] && t.current[0] != '"') t.current++;
|
||||
|
||||
uint len = cast(uint, t.current - start);
|
||||
uint len = cast(t.current - start, uint);
|
||||
// assert(len < MaxText);
|
||||
memcpy(t.text, start, len);
|
||||
t.text[len] = 0;
|
||||
@@ -325,7 +325,7 @@ func void Tokenizer.parseNumber(Tokenizer* t, Token* result)
|
||||
// handle hexadecimal/ocal/binary number
|
||||
// handle '_', like 1_000_000
|
||||
|
||||
u32 number = cast<u32>(atoi(t.current));
|
||||
uint number = cast(atoi(t.current), uint);
|
||||
result.kind = TokenKind.Number;
|
||||
result.number = number;
|
||||
while (t.current[0] && isdigit(t.current[0]))
|
||||
@@ -347,9 +347,9 @@ func bool isKeyChar(u8 c)
|
||||
func void Tokenizer.parseKey(Tokenizer* t, Token* result)
|
||||
{
|
||||
char* start = t.current;
|
||||
while (t.current[0] && isKeyChar(cast<u8>(t.current[0]))) t.current++;
|
||||
while (t.current[0] && isKeyChar(cast(t.current[0], byte))) t.current++;
|
||||
|
||||
uint len = cast(uint, t.current - start);
|
||||
uint len = cast(t.current - start, uint);
|
||||
// assert(len < MaxText);
|
||||
memcpy(t.text, start, len);
|
||||
t.text[len] = 0;
|
||||
|
||||
@@ -45,7 +45,7 @@ public func bool Vector.empty(Vector *vector)
|
||||
return !vector.array.size;
|
||||
}
|
||||
|
||||
public macro Vector.@foreach(Vector *vector : @body(Type value))
|
||||
public macro Vector.foreach(Vector *vector, macro void(Type value) body)
|
||||
{
|
||||
for (usize i = 0, i < vector.array.size; i++)
|
||||
{
|
||||
@@ -59,11 +59,11 @@ test
|
||||
IntVector *vector = @calloc(IntVector);
|
||||
vector.add(1);
|
||||
vector.add(2);
|
||||
foreach (int i : vector)
|
||||
for (int i : vector)
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
vector.@foreach(int i)
|
||||
@vector.foreach(int i)
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,12 @@ int yylex(void);
|
||||
void yyerror(char *s);
|
||||
%}
|
||||
|
||||
%token IDENT AT_IDENT CT_IDENT CONSTANT CONST_IDENT TYPE_IDENT STRING_LITERAL SIZEOF
|
||||
%token IDENT CT_IDENT CONSTANT CONST_IDENT TYPE_IDENT STRING_LITERAL SIZEOF
|
||||
%token INC_OP DEC_OP LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP
|
||||
%token AND_OP OR_OP MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN
|
||||
%token SUB_ASSIGN LEFT_ASSIGN RIGHT_ASSIGN AND_ASSIGN
|
||||
%token XOR_ASSIGN OR_ASSIGN VAR NIL ELVIS HASH_IDENT NEXT
|
||||
%token AT
|
||||
|
||||
%token TYPEDEF MODULE IMPORT
|
||||
%token CHAR SHORT INT LONG FLOAT DOUBLE CONST VOLATILE VOID
|
||||
@@ -26,6 +27,7 @@ void yyerror(char *s);
|
||||
|
||||
%token FN_BLOCK_START FN_BLOCK_END
|
||||
%token MULTW ADDW SUBW
|
||||
%token AUTO
|
||||
|
||||
%start translation_unit
|
||||
%%
|
||||
@@ -44,7 +46,6 @@ ident_expression
|
||||
: CONST_IDENT
|
||||
| IDENT
|
||||
| CT_IDENT
|
||||
| AT_IDENT
|
||||
;
|
||||
|
||||
primary_expression
|
||||
@@ -91,6 +92,7 @@ unary_operator
|
||||
| SUBW
|
||||
| '~'
|
||||
| '!'
|
||||
| '@'
|
||||
;
|
||||
|
||||
|
||||
@@ -199,6 +201,8 @@ identifier_list
|
||||
macro_argument
|
||||
: CT_IDENT
|
||||
| IDENT
|
||||
| type_expression IDENT
|
||||
| type_expression CT_IDENT
|
||||
;
|
||||
|
||||
macro_argument_list
|
||||
@@ -235,6 +239,7 @@ parameter_list
|
||||
|
||||
base_type
|
||||
: VOID
|
||||
| AUTO
|
||||
| BOOL
|
||||
| CHAR
|
||||
| BYTE
|
||||
@@ -426,11 +431,14 @@ jump_statement
|
||||
| RETURN expression ';'
|
||||
;
|
||||
|
||||
path_ident
|
||||
: IDENT
|
||||
| path IDENT
|
||||
;
|
||||
|
||||
attribute
|
||||
: AT_IDENT
|
||||
| path AT_IDENT
|
||||
| AT_IDENT '(' constant_expression ')'
|
||||
| path AT_IDENT '(' constant_expression ')'
|
||||
: AT path_ident
|
||||
| AT path_ident '(' constant_expression ')'
|
||||
;
|
||||
|
||||
attribute_list
|
||||
@@ -480,7 +488,8 @@ func_definition
|
||||
;
|
||||
|
||||
macro_declaration
|
||||
: MACRO AT_IDENT '(' macro_argument_list ')' compound_statement
|
||||
: MACRO type_expression IDENT '(' macro_argument_list ')' compound_statement
|
||||
: MACRO IDENT '(' macro_argument_list ')' compound_statement
|
||||
;
|
||||
|
||||
|
||||
@@ -576,8 +585,8 @@ attribute_domains
|
||||
;
|
||||
|
||||
attribute_declaration
|
||||
: ATTRIBUTE attribute_domains AT_IDENT ';'
|
||||
| ATTRIBUTE attribute_domains AT_IDENT '(' parameter_type_list ')' ';'
|
||||
: ATTRIBUTE attribute_domains IDENT ';'
|
||||
| ATTRIBUTE attribute_domains IDENT '(' parameter_type_list ')' ';'
|
||||
;
|
||||
|
||||
global_declaration
|
||||
@@ -631,7 +640,7 @@ module_param
|
||||
: CT_IDENT
|
||||
| HASH_IDENT
|
||||
| TYPE_IDENT
|
||||
| AT_IDENT
|
||||
| IDENT
|
||||
;
|
||||
|
||||
module_params
|
||||
|
||||
@@ -13,6 +13,16 @@ macro @goo(i, $e)
|
||||
|
||||
}
|
||||
|
||||
struct Foo
|
||||
{
|
||||
$if ($use_bar > 0)
|
||||
{
|
||||
int bar;
|
||||
}
|
||||
int baz;
|
||||
}
|
||||
|
||||
|
||||
macro @soom!(i, $e)
|
||||
{}
|
||||
|
||||
@@ -125,6 +135,12 @@ $else
|
||||
generic test(i) { }
|
||||
}
|
||||
|
||||
#if $b > 0
|
||||
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
generic boofer2(i, g, eok)
|
||||
{
|
||||
case int, char[], type($eoo):
|
||||
@@ -133,6 +149,8 @@ generic boofer2(i, g, eok)
|
||||
return 1000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
func void hello() throws Errors
|
||||
{
|
||||
int i, j;
|
||||
|
||||
@@ -30,8 +30,56 @@ union Test3
|
||||
int b;
|
||||
}
|
||||
|
||||
struct Teob
|
||||
{
|
||||
int x;
|
||||
double y;
|
||||
int xy;
|
||||
int oekfeo;
|
||||
}
|
||||
|
||||
error Error
|
||||
{
|
||||
BLURB,
|
||||
NO_SUCH_FILE,
|
||||
|
||||
}
|
||||
error OtherError
|
||||
{
|
||||
FOO_BAR
|
||||
}
|
||||
|
||||
func int jumptest()
|
||||
{
|
||||
if (1) goto LABELX;
|
||||
return 1;
|
||||
LABELX:
|
||||
return 2;
|
||||
}
|
||||
func int borok() throws
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
func int barok() throws Error, OtherError
|
||||
{
|
||||
if (true)
|
||||
{
|
||||
throw Error.NO_SUCH_FILE;
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
|
||||
func int boba(int y, int j)
|
||||
{
|
||||
// hello();
|
||||
//$e = type(Teob);
|
||||
//Teob xbd = type(Teob);
|
||||
//Teob xb = { 1, 1.0, 100, 1000 };
|
||||
//Test2 tee = { { 3 }, 4 };
|
||||
//Test3 xc = { eo = 1, t.a = 1 };
|
||||
// throw Error.NO_SUCH_FILE;
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
|
||||
@@ -106,13 +154,14 @@ typedef func void(int) as Foo;
|
||||
//typedef int as Foo;
|
||||
extern func void printf(char *hello);
|
||||
|
||||
macro @hello()
|
||||
macro hello()
|
||||
{
|
||||
printf("Hello world!\n");
|
||||
}
|
||||
|
||||
func void bob()
|
||||
{
|
||||
|
||||
byte a = 2;
|
||||
short b = 3;
|
||||
int c = 4;
|
||||
|
||||
@@ -310,11 +310,6 @@ void parse_arguments(int argc, const char *argv[])
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
build_options.cshort_size = sizeof(short);
|
||||
build_options.cint_size = sizeof(int);
|
||||
build_options.clong_size = sizeof(long);
|
||||
build_options.clonglong_size = sizeof(long long);
|
||||
build_options.pointer_size = sizeof(void *);
|
||||
build_options.path = ".";
|
||||
build_options.emit_llvm = false;
|
||||
build_options.emit_bitcode = true;
|
||||
|
||||
@@ -100,12 +100,6 @@ typedef struct
|
||||
uint32_t symtab_size;
|
||||
CompileOption compile_option;
|
||||
DiagnosticsSeverity severity[DIAG_END_SENTINEL];
|
||||
int pointer_size;
|
||||
int cshort_size;
|
||||
int cint_size;
|
||||
int clong_size;
|
||||
int clonglong_size;
|
||||
int clongdouble_size;
|
||||
OptimizationLevel optimization_level;
|
||||
SizeOptimizationLevel size_optimization_level;
|
||||
bool debug_info;
|
||||
|
||||
@@ -16,6 +16,7 @@ Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility)
|
||||
return decl;
|
||||
}
|
||||
|
||||
|
||||
Type poisoned_type = { .type_kind = TYPE_POISONED };
|
||||
|
||||
TypeInfo poisoned_type_info = { .kind = TYPE_INFO_POISON };
|
||||
@@ -174,8 +175,18 @@ Type* type_get_unsigned(Type *type)
|
||||
|
||||
*/
|
||||
|
||||
bool func_return_value_as_out(FunctionSignature *func_sig)
|
||||
{
|
||||
Type *return_type = func_sig->rtype->type->canonical;
|
||||
if (return_type->type_kind == TYPE_VOID) return false;
|
||||
if (func_has_error_return(func_sig)) return true;
|
||||
// TODO improve
|
||||
return type_size(return_type) > 8 * 4;
|
||||
}
|
||||
|
||||
BinaryOp binary_op[256] = {
|
||||
|
||||
|
||||
BinaryOp binary_op[TOKEN_LAST + 1] = {
|
||||
[TOKEN_STAR] = BINARYOP_MULT,
|
||||
[TOKEN_MULT_MOD] = BINARYOP_MULT_MOD,
|
||||
[TOKEN_DIV] = BINARYOP_DIV,
|
||||
@@ -214,7 +225,7 @@ BinaryOp binary_op[256] = {
|
||||
};
|
||||
|
||||
|
||||
static BinaryOp assign_binop[256] = {
|
||||
static BinaryOp assign_binop[BINARYOP_LAST + 1] = {
|
||||
[BINARYOP_MULT_ASSIGN] = BINARYOP_MULT,
|
||||
[BINARYOP_MULT_MOD_ASSIGN] = BINARYOP_MULT_MOD,
|
||||
[BINARYOP_ADD_ASSIGN] = BINARYOP_ADD,
|
||||
@@ -237,10 +248,7 @@ BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op)
|
||||
return assign_binop[(int)assign_binary_op];
|
||||
}
|
||||
|
||||
AssignOp assign_op[256] = {
|
||||
};
|
||||
|
||||
UnaryOp unary_op[256] = {
|
||||
UnaryOp unary_op[TOKEN_LAST + 1] = {
|
||||
[TOKEN_STAR] = UNARYOP_DEREF,
|
||||
[TOKEN_AMP] = UNARYOP_ADDR,
|
||||
[TOKEN_BIT_NOT] = UNARYOP_BITNEG,
|
||||
@@ -250,25 +258,13 @@ UnaryOp unary_op[256] = {
|
||||
[TOKEN_MINUSMINUS] = UNARYOP_DEC,
|
||||
};
|
||||
|
||||
PostUnaryOp post_unary_op[256] = {
|
||||
|
||||
PostUnaryOp post_unary_op[TOKEN_LAST + 1] = {
|
||||
[TOKEN_PLUSPLUS] = POSTUNARYOP_INC,
|
||||
[TOKEN_MINUSMINUS] = POSTUNARYOP_DEC,
|
||||
};
|
||||
|
||||
|
||||
AssignOp assignop_from_token(TokenType type)
|
||||
{
|
||||
return assign_op[type];
|
||||
}
|
||||
|
||||
TokenType assignop_to_token(AssignOp type)
|
||||
{
|
||||
for (unsigned i = 0; i < 256; i++)
|
||||
{
|
||||
if (assign_op[i] == type) return (TokenType)i;
|
||||
}
|
||||
return TOKEN_INVALID_TOKEN;
|
||||
}
|
||||
|
||||
BinaryOp binaryop_from_token(TokenType type)
|
||||
{
|
||||
@@ -277,7 +273,7 @@ BinaryOp binaryop_from_token(TokenType type)
|
||||
|
||||
TokenType binaryop_to_token(BinaryOp type)
|
||||
{
|
||||
for (unsigned i = 0; i < 256; i++)
|
||||
for (unsigned i = 0; i <= TOKEN_LAST; i++)
|
||||
{
|
||||
if (binary_op[i] == type) return (TokenType)i;
|
||||
}
|
||||
@@ -291,7 +287,7 @@ UnaryOp unaryop_from_token(TokenType type)
|
||||
|
||||
TokenType unaryop_to_token(UnaryOp type)
|
||||
{
|
||||
for (unsigned i = 0; i < 256; i++)
|
||||
for (unsigned i = 0; i <= TOKEN_LAST; i++)
|
||||
{
|
||||
if (unary_op[i] == type) return (TokenType)i;
|
||||
}
|
||||
@@ -305,7 +301,7 @@ PostUnaryOp post_unaryop_from_token(TokenType type)
|
||||
|
||||
TokenType postunaryop_to_token(PostUnaryOp type)
|
||||
{
|
||||
for (unsigned i = 0; i < 256; i++)
|
||||
for (unsigned i = 0; i <= TOKEN_LAST; i++)
|
||||
{
|
||||
if (post_unary_op[i] == type) return (TokenType)i;
|
||||
}
|
||||
@@ -348,6 +344,10 @@ void fprint_type_recursive(FILE *file, Type *type, int indent)
|
||||
case TYPE_POISONED:
|
||||
fprintf_indented(file, indent, "(type poison)\n");
|
||||
return;
|
||||
case TYPE_META_TYPE:
|
||||
fprintf_indented(file, indent, "(meta-type");
|
||||
fprint_type_recursive(file, type->child, indent + 1);
|
||||
fprint_endparen(file, indent);
|
||||
case TYPE_FUNC:
|
||||
fprintf_indented(file, indent, "(type-func %s)\n", type->func.signature->mangled_signature);
|
||||
return;
|
||||
@@ -675,9 +675,16 @@ void fprint_func_signature(FILE *file, FunctionSignature *signature, int indent)
|
||||
fprintf_indented(file, indent, "(params\n");
|
||||
fprint_decl_list(file, signature->params, indent + 1);
|
||||
fprint_endparen(file, indent);
|
||||
fprintf_indented(file, indent, "(throws\n");
|
||||
fprint_decl_list(file, signature->throws, indent + 1);
|
||||
fprint_endparen(file, indent);
|
||||
if (signature->throw_any)
|
||||
{
|
||||
fprintf_indented(file, indent, "(throws any)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf_indented(file, indent, "(throws\n");
|
||||
fprint_decl_list(file, signature->throws, indent + 1);
|
||||
fprint_endparen(file, indent);
|
||||
}
|
||||
}
|
||||
void fprint_decl_recursive(FILE *file, Decl *decl, int indent)
|
||||
{
|
||||
@@ -1039,7 +1046,7 @@ static void fprint_ast_recursive(FILE *file, Ast *ast, int indent)
|
||||
break;
|
||||
case AST_THROW_STMT:
|
||||
fprintf(file, "(throw\n");
|
||||
fprint_expr_recursive(file, ast->throw_stmt, indent + 1);
|
||||
fprint_expr_recursive(file, ast->throw_stmt.throw_value, indent + 1);
|
||||
break;
|
||||
case AST_TRY_STMT:
|
||||
TODO
|
||||
|
||||
@@ -38,7 +38,7 @@ static bool sema_type_mismatch(Expr *expr, Type *type, CastType cast_type)
|
||||
break;
|
||||
|
||||
}
|
||||
SEMA_ERROR(expr, "Cannot %s '%s' to '%s'", action, type_to_error_string(expr->type), type_to_error_string(type));
|
||||
SEMA_ERROR(expr, "Cannot %s '%s' to '%s'.", action, type_to_error_string(expr->type), type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -580,6 +580,7 @@ CastKind cast_to_bool_kind(Type *type)
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_SUBARRAY:
|
||||
case TYPE_META_TYPE:
|
||||
// Improve consider vararray / subarray conversion to boolean.
|
||||
return CAST_ERROR;
|
||||
case TYPE_BOOL:
|
||||
@@ -614,6 +615,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_VOID:
|
||||
case TYPE_META_TYPE:
|
||||
break;
|
||||
case TYPE_BOOL:
|
||||
if (type_is_integer(canonical)) return boxi(expr, from_type, canonical, to_type, cast_type);
|
||||
|
||||
@@ -36,7 +36,6 @@ static void compiler_lex(BuildTarget *target)
|
||||
|
||||
void compiler_parse(BuildTarget *target)
|
||||
{
|
||||
builtin_setup();
|
||||
VECEACH(target->sources, i)
|
||||
{
|
||||
bool loaded = false;
|
||||
@@ -149,7 +148,6 @@ void compile_files(BuildTarget *target)
|
||||
}
|
||||
target_expand_source_names(target);
|
||||
target_setup();
|
||||
builtin_setup();
|
||||
|
||||
if (!vec_size(target->sources)) error_exit("No files to compile.");
|
||||
switch (build_options.compile_option)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "../build/build_options.h"
|
||||
#include "compiler.h"
|
||||
#include "enums.h"
|
||||
#include "target.h"
|
||||
|
||||
typedef uint32_t SourceLoc;
|
||||
#define INVALID_LOC UINT32_MAX
|
||||
@@ -18,6 +19,9 @@ typedef uint32_t SourceLoc;
|
||||
#define MAX_SCOPE_DEPTH 0xFF
|
||||
#define MAX_PATH 1024
|
||||
#define MAX_DEFERS 0xFFFF
|
||||
#define MAX_FUNCTION_SIGNATURE_SIZE 2048
|
||||
#define MAX_PARAMS 512
|
||||
#define MAX_ERRORS 0xFFFF
|
||||
|
||||
typedef struct _Ast Ast;
|
||||
typedef struct _Decl Decl;
|
||||
@@ -102,8 +106,10 @@ typedef struct _Path
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned bitsize : 16;
|
||||
unsigned char bitsize;
|
||||
unsigned char bytesize;
|
||||
unsigned char min_alignment;
|
||||
unsigned char pref_alignment;
|
||||
} TypeBuiltin;
|
||||
|
||||
typedef struct
|
||||
@@ -128,7 +134,7 @@ struct _Type
|
||||
TypeKind type_kind : 8;
|
||||
struct _Type *canonical;
|
||||
const char *name;
|
||||
struct _Type **ptr_cache;
|
||||
struct _Type **type_cache;
|
||||
void *backend_type;
|
||||
void *backend_debug_type;
|
||||
union
|
||||
@@ -138,6 +144,7 @@ struct _Type
|
||||
TypeArray array;
|
||||
TypeFunc func;
|
||||
Type *pointer;
|
||||
Type *child;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -183,6 +190,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t alignment;
|
||||
uint64_t size;
|
||||
Decl **members;
|
||||
} StructDecl;
|
||||
|
||||
@@ -230,6 +239,7 @@ typedef struct _FunctionSignature
|
||||
{
|
||||
CallConvention convention : 4;
|
||||
bool variadic : 1;
|
||||
bool throw_any : 1;
|
||||
TypeInfo *rtype;
|
||||
Decl** params;
|
||||
Decl** throws;
|
||||
@@ -339,6 +349,7 @@ typedef struct _Decl
|
||||
CtIfDecl ct_elif_decl;
|
||||
Decl** ct_else_decl;
|
||||
Expr *incr_array_decl;
|
||||
TypeInfo *throws;
|
||||
};
|
||||
} Decl;
|
||||
|
||||
@@ -474,6 +485,7 @@ struct _Expr
|
||||
ExprStructValue struct_value_expr;
|
||||
ExprTypeRef type_access;
|
||||
ExprTry try_expr;
|
||||
Expr* macro_expr;
|
||||
ExprBinary binary_expr;
|
||||
ExprTernary ternary_expr;
|
||||
ExprUnary unary_expr;
|
||||
@@ -651,6 +663,12 @@ typedef struct
|
||||
DeferList defers;
|
||||
} AstNextStmt;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Expr *throw_value;
|
||||
DeferList defers;
|
||||
} AstThrowStmt;
|
||||
|
||||
typedef struct _Ast
|
||||
{
|
||||
SourceRange span;
|
||||
@@ -663,9 +681,9 @@ typedef struct _Ast
|
||||
AstCompoundStmt function_block_stmt;
|
||||
Decl *declare_stmt;
|
||||
Expr *expr_stmt;
|
||||
Expr *throw_stmt;
|
||||
struct _Ast *volatile_stmt;
|
||||
struct _Ast *try_stmt;
|
||||
AstThrowStmt throw_stmt;
|
||||
Ast *volatile_stmt;
|
||||
Ast *try_stmt;
|
||||
AstLabelStmt label_stmt;
|
||||
AstReturnStmt return_stmt;
|
||||
AstWhileStmt while_stmt;
|
||||
@@ -764,6 +782,12 @@ typedef struct _Context
|
||||
Token *next_lead_comment;
|
||||
DynamicScope *current_scope;
|
||||
Decl *evaluating_macro;
|
||||
// Error handling
|
||||
struct
|
||||
{
|
||||
Decl **errors;
|
||||
int try_nesting;
|
||||
};
|
||||
Type *rtype;
|
||||
int in_volatile_section;
|
||||
Decl *locals[MAX_LOCALS];
|
||||
@@ -780,7 +804,7 @@ typedef struct _Context
|
||||
Token next_tok;
|
||||
struct
|
||||
{
|
||||
bool has_stored;
|
||||
bool in_lookahead;
|
||||
const char *current;
|
||||
const char *start;
|
||||
Token tok;
|
||||
@@ -788,7 +812,6 @@ typedef struct _Context
|
||||
Token *lead_comment;
|
||||
Token *trailing_comment;
|
||||
Token *next_lead_comment;
|
||||
unsigned comments;
|
||||
} stored;
|
||||
} Context;
|
||||
|
||||
@@ -859,7 +882,7 @@ static inline Ast *extend_ast_with_prev_token(Context *context, Ast *ast)
|
||||
}
|
||||
|
||||
|
||||
void builtin_setup();
|
||||
void builtin_setup(Target *target);
|
||||
|
||||
static inline bool builtin_may_negate(Type *canonical)
|
||||
{
|
||||
@@ -915,6 +938,8 @@ CastKind cast_to_bool_kind(Type *type);
|
||||
bool cast_to_runtime(Expr *expr);
|
||||
|
||||
void llvm_codegen(Context *context);
|
||||
void llvm_set_struct_size_alignment(Decl *decl);
|
||||
|
||||
|
||||
bool sema_analyse_expr(Context *context, Type *to, Expr *expr);
|
||||
bool sema_analyse_decl(Context *context, Decl *decl);
|
||||
@@ -969,6 +994,12 @@ void fprint_decl(FILE *file, Decl *dec);
|
||||
void fprint_type_info_recursive(FILE *file, TypeInfo *type_info, int indent);
|
||||
void fprint_expr_recursive(FILE *file, Expr *expr, int indent);
|
||||
|
||||
bool func_return_value_as_out(FunctionSignature *func_sig);
|
||||
static inline bool func_has_error_return(FunctionSignature *func_sig)
|
||||
{
|
||||
return func_sig->throws || func_sig->throw_any;
|
||||
}
|
||||
|
||||
|
||||
Token lexer_scan_token(Lexer *lexer);
|
||||
Token lexer_scan_ident_test(Lexer *lexer, const char *scan);
|
||||
@@ -1035,6 +1066,7 @@ void target_setup();
|
||||
int target_alloca_addr_space();
|
||||
void *target_data_layout();
|
||||
void *target_machine();
|
||||
void *target_target();
|
||||
|
||||
#define TOKEN_MAX_LENGTH 0xFFFF
|
||||
#define TOK2VARSTR(_token) _token.span.length, _token.start
|
||||
@@ -1047,9 +1079,10 @@ static inline Token wrap(const char *string)
|
||||
}
|
||||
|
||||
Type *type_get_ptr(Type *ptr_type);
|
||||
Type *type_get_meta(Type *meta_type);
|
||||
Type *type_get_array(Type *arr_type, uint64_t len);
|
||||
Type *type_signed_int_by_size(int bytesize);
|
||||
Type *type_unsigned_int_by_size(int bytesize);
|
||||
Type *type_signed_int_by_bitsize(unsigned bytesize);
|
||||
Type *type_unsigned_int_by_bitsize(unsigned bytesize);
|
||||
bool type_is_subtype(Type *type, Type *possible_subtype);
|
||||
Type *type_find_common_ancestor(Type *left, Type *right);
|
||||
const char *type_to_error_string(Type *type);
|
||||
@@ -1141,7 +1174,6 @@ static inline bool type_is_number(Type *type)
|
||||
__type->name_loc = _name; __type->unresolved.module = _module; __type; })
|
||||
#define TYPE_UNRESOLVED(_name) ({ TypeInfo *__type = type_new(TYPE_USER_DEFINED); __type->name_loc = _name; __type; })
|
||||
|
||||
AssignOp assignop_from_token(TokenType type);
|
||||
UnaryOp unaryop_from_token(TokenType type);
|
||||
TokenType unaryop_to_token(UnaryOp type);
|
||||
PostUnaryOp post_unaryop_from_token(TokenType type);
|
||||
@@ -1159,8 +1191,7 @@ static inline const char* struct_union_name_from_token(TokenType type)
|
||||
return type == TOKEN_STRUCT ? "struct" : "union";
|
||||
}
|
||||
|
||||
#define BACKEND_TYPE(type) gencontext_get_llvm_type(context, type)
|
||||
#define BACKEND_TYPE_GLOBAL(type) gencontext_get_llvm_type(NULL, type)
|
||||
#define llvm_type(type) gencontext_get_llvm_type(context, type)
|
||||
#define DEBUG_TYPE(type) gencontext_get_debug_type(context, type)
|
||||
|
||||
void advance(Context *context);
|
||||
|
||||
@@ -63,6 +63,7 @@ typedef enum
|
||||
BINARYOP_BIT_XOR_ASSIGN,
|
||||
BINARYOP_SHR_ASSIGN,
|
||||
BINARYOP_SHL_ASSIGN,
|
||||
BINARYOP_LAST = BINARYOP_SHL_ASSIGN
|
||||
} BinaryOp;
|
||||
|
||||
typedef enum
|
||||
@@ -218,6 +219,7 @@ typedef enum
|
||||
EXIT_BREAK,
|
||||
EXIT_GOTO,
|
||||
EXIT_CONTINUE,
|
||||
EXIT_THROW,
|
||||
EXIT_RETURN,
|
||||
} ExitType;
|
||||
|
||||
@@ -243,6 +245,7 @@ typedef enum
|
||||
EXPR_EXPRESSION_LIST,
|
||||
EXPR_CAST,
|
||||
EXPR_SCOPED_EXPR,
|
||||
EXPR_MACRO_EXPR,
|
||||
} ExprKind;
|
||||
|
||||
|
||||
@@ -259,6 +262,7 @@ typedef enum
|
||||
{
|
||||
PREC_NONE,
|
||||
PREC_ASSIGNMENT, // =, *=, /=, %=, ...
|
||||
PREC_TRY, // try
|
||||
PREC_TERNARY, // ?:
|
||||
PREC_LOGICAL, // && ||
|
||||
PREC_RELATIONAL, // < > <= >= == !=
|
||||
@@ -266,7 +270,7 @@ typedef enum
|
||||
PREC_BIT, // ^ | &
|
||||
PREC_SHIFT, // << >> >>>
|
||||
PREC_MULTIPLICATIVE, // * / %
|
||||
PREC_UNARY, // ! - + ~ * & prefix ++/--
|
||||
PREC_UNARY, // @ ! - + ~ * & prefix ++/--
|
||||
PREC_CALL, // . () [] postfix ++/--
|
||||
} Precedence;
|
||||
|
||||
@@ -401,9 +405,8 @@ typedef enum
|
||||
TOKEN_CONST_IDENT, // Any purely upper case ident,
|
||||
TOKEN_TYPE_IDENT, // Any ident on the format FooBar or __FooBar
|
||||
|
||||
// We want to parse @foo / #foo / $foo separately.
|
||||
// Otherwise we allow things like "@ foo" which would be pretty bad.
|
||||
TOKEN_AT_IDENT, // @foobar
|
||||
// We want to parse #foo / $foo separately.
|
||||
// Otherwise we allow things like "# foo" which would be pretty bad.
|
||||
TOKEN_HASH_IDENT, // #foobar
|
||||
TOKEN_CT_IDENT, // $foobar
|
||||
|
||||
@@ -486,6 +489,7 @@ typedef enum
|
||||
|
||||
TOKEN_EOF, // \n - SHOULD ALWAYS BE THE LAST TOKEN.
|
||||
|
||||
TOKEN_LAST = TOKEN_EOF,
|
||||
} TokenType;
|
||||
|
||||
|
||||
@@ -523,9 +527,11 @@ typedef enum
|
||||
TYPE_ARRAY,
|
||||
TYPE_VARARRAY,
|
||||
TYPE_SUBARRAY,
|
||||
TYPE_META_TYPE,
|
||||
TYPE_LAST = TYPE_META_TYPE
|
||||
} TypeKind;
|
||||
|
||||
#define TYPE_KINDS (TYPE_SUBARRAY + 1)
|
||||
#define TYPE_KINDS (TYPE_LAST + 1)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
@@ -537,6 +543,7 @@ typedef enum
|
||||
UNARYOP_NOT,
|
||||
UNARYOP_INC,
|
||||
UNARYOP_DEC,
|
||||
UNARYOP_LAST = UNARYOP_DEC
|
||||
} UnaryOp;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -120,7 +120,11 @@ static inline bool sema_expr_analyse_identifier(Context *context, Type *to, Expr
|
||||
SEMA_ERROR(expr, "Functions from other modules, must be prefixed with the module name");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decl->decl_kind == DECL_MACRO)
|
||||
{
|
||||
SEMA_ERROR(expr, "Macro expansions must be prefixed with '@', try using '@%s(...)' instead.", decl->name);
|
||||
return false;
|
||||
}
|
||||
assert(decl->type);
|
||||
expr->identifier_expr.decl = decl;
|
||||
expr->type = decl->type;
|
||||
@@ -133,29 +137,17 @@ static inline bool sema_expr_analyse_binary_sub_expr(Context *context, Expr *lef
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr *expr) { TODO }
|
||||
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *expr, Decl *macro)
|
||||
{
|
||||
Ast *macro_parent;
|
||||
// TODO handle loops
|
||||
Decl *stored_macro = context->evaluating_macro;
|
||||
Type *stored_rtype = context->rtype;
|
||||
context->evaluating_macro = macro;
|
||||
context->rtype = macro->macro_decl.rtype->type;
|
||||
// Handle escaping macro
|
||||
bool success = sema_analyse_statement(context, macro->macro_decl.body);
|
||||
context->evaluating_macro = stored_macro;
|
||||
context->rtype = stored_rtype;
|
||||
if (!success) return false;
|
||||
|
||||
TODO
|
||||
return success;
|
||||
};
|
||||
static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO };
|
||||
|
||||
static inline bool sema_expr_analyse_func_call(Context *context, Type *to, Expr *expr, Decl *decl)
|
||||
{
|
||||
Expr **args =expr->call_expr.arguments;
|
||||
Decl **func_params = decl->func.function_signature.params;
|
||||
unsigned error_params = decl->func.function_signature.throw_any || decl->func.function_signature.throws;
|
||||
if (error_params)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
unsigned num_args = vec_size(args);
|
||||
// unsigned num_params = vec_size(func_params);
|
||||
// TODO handle named parameters, handle default parameters, varargs etc
|
||||
@@ -192,7 +184,8 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
case DECL_FUNC:
|
||||
return sema_expr_analyse_func_call(context, to, expr, decl);
|
||||
case DECL_MACRO:
|
||||
return sema_expr_analyse_macro_call(context, to, expr, decl);
|
||||
SEMA_ERROR(expr, "Macro calls must be preceeded by '@'.");
|
||||
return false;
|
||||
case DECL_GENERIC:
|
||||
return sema_expr_analyse_generic_call(context, to, expr);
|
||||
case DECL_POISONED:
|
||||
@@ -402,57 +395,143 @@ static inline bool sema_expr_analyse_type_access(Context *context, Type *to, Exp
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline Decl *decl_find_by_name(Decl** decls, const char *name)
|
||||
static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr);
|
||||
|
||||
static Decl *sema_analyse_init_identifier_string(Context *context, Decl *strukt, const char *string)
|
||||
{
|
||||
VECEACH(decls, i)
|
||||
assert(decl_is_struct_type(strukt));
|
||||
Decl **members = strukt->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (decls[i]->name == name) return decls[i];
|
||||
Decl *member = members[i];
|
||||
if (member->name == string) return member;
|
||||
if (!member->name)
|
||||
{
|
||||
Decl *anonymous_member = sema_analyse_init_identifier_string(context, member->type->decl, string);
|
||||
if (anonymous_member) return anonymous_member;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static inline bool expr_may_be_struct_field_decl(Expr *maybe_binary)
|
||||
|
||||
static Decl *sema_analyse_init_identifier(Context *context, Decl *strukt, Expr *expr)
|
||||
{
|
||||
if (maybe_binary->expr_kind != EXPR_BINARY) return false;
|
||||
if (maybe_binary->binary_expr.operator != BINARYOP_EQ) return false;
|
||||
Expr *expr = maybe_binary->binary_expr.left;
|
||||
while (1)
|
||||
assert(expr->resolve_status == RESOLVE_NOT_DONE);
|
||||
expr->resolve_status = RESOLVE_RUNNING;
|
||||
expr->identifier_expr.decl = sema_analyse_init_identifier_string(context, strukt, expr->identifier_expr.identifier);
|
||||
expr->resolve_status = RESOLVE_DONE;
|
||||
return expr->identifier_expr.decl;
|
||||
}
|
||||
static Decl *sema_analyse_init_access(Context *context, Decl *strukt, Expr *access_expr)
|
||||
{
|
||||
assert(access_expr->resolve_status == RESOLVE_NOT_DONE);
|
||||
access_expr->resolve_status = RESOLVE_RUNNING;
|
||||
Decl *decl = sema_analyse_init_path(context, strukt, access_expr->access_expr.parent);
|
||||
if (!decl || !decl_is_struct_type(decl->type->decl))
|
||||
{
|
||||
if (expr->expr_kind == EXPR_IDENTIFIER) return true;
|
||||
if (expr->expr_kind != EXPR_ACCESS) return false;
|
||||
expr = expr->access_expr.parent;
|
||||
access_expr->resolve_status = RESOLVE_DONE;
|
||||
return NULL;
|
||||
}
|
||||
decl = access_expr->access_expr.ref = sema_analyse_init_identifier_string(context, decl->type->decl, access_expr->access_expr.sub_element.string);
|
||||
access_expr->resolve_status = RESOLVE_DONE;
|
||||
return decl;
|
||||
}
|
||||
|
||||
static Decl *sema_analyse_init_subscript(Context *context, Decl *array, Expr *subscript)
|
||||
{
|
||||
TODO
|
||||
if (array->type->type_kind != TYPE_ARRAY)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static Decl *sema_analyse_init_path(Context *context, Decl *strukt, Expr *expr)
|
||||
{
|
||||
switch (expr->expr_kind)
|
||||
{
|
||||
case EXPR_ACCESS:
|
||||
return sema_analyse_init_access(context, strukt, expr);
|
||||
case EXPR_IDENTIFIER:
|
||||
return sema_analyse_init_identifier(context, strukt, expr);
|
||||
case EXPR_SUBSCRIPT:
|
||||
return sema_analyse_init_subscript(context, strukt, expr);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INIT_SEMA_ERROR,
|
||||
INIT_SEMA_NOT_FOUND,
|
||||
INIT_SEMA_OK
|
||||
} InitSemaResult;
|
||||
|
||||
static InitSemaResult sema_expr_analyse_struct_named_initializer_list(Context *context, Decl *assigned, Expr *expr_list)
|
||||
{
|
||||
VECEACH(expr_list->initializer_expr, i)
|
||||
{
|
||||
Expr *expr = expr_list->initializer_expr[i];
|
||||
if (expr->expr_kind != EXPR_BINARY && expr->binary_expr.operator != BINARYOP_ASSIGN)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
SEMA_ERROR(expr, "Named and non-named initializers are not allowed together, please choose one or the other.");
|
||||
return INIT_SEMA_ERROR;
|
||||
}
|
||||
// If there is an unexpected expression and no previous element then this is a normal initializer list.
|
||||
return INIT_SEMA_NOT_FOUND;
|
||||
}
|
||||
Expr *path = expr->binary_expr.left;
|
||||
Expr *value = expr->binary_expr.right;
|
||||
Decl *result = sema_analyse_init_path(context, assigned, path);
|
||||
if (!result)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
SEMA_ERROR(path, "Unexpected element when initializing '%s', did you get the name right?", assigned->name);
|
||||
return INIT_SEMA_ERROR;
|
||||
}
|
||||
return INIT_SEMA_NOT_FOUND;
|
||||
}
|
||||
if (!sema_analyse_expr(context, result->type, value)) return INIT_SEMA_ERROR;
|
||||
}
|
||||
return INIT_SEMA_OK;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_struct_initializer_list(Context *context, Type *assigned, Expr *expr)
|
||||
{
|
||||
Decl **members = assigned->decl->strukt.members;
|
||||
unsigned size = vec_size(members);
|
||||
// Zero size init will initialize to empty.
|
||||
if (size == 0) return true;
|
||||
|
||||
InitSemaResult result = sema_expr_analyse_struct_named_initializer_list(context, assigned->decl, expr);
|
||||
if (result == INIT_SEMA_ERROR) return false;
|
||||
if (result == INIT_SEMA_OK)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
if (assigned->type_kind == TYPE_UNION)
|
||||
{
|
||||
SEMA_ERROR(expr->initializer_expr[0], "Initializer list for unions must use named initializers, e.g. { a = 4 }");
|
||||
return false;
|
||||
}
|
||||
if (size < vec_size(expr->initializer_expr))
|
||||
{
|
||||
SEMA_ERROR(expr->initializer_expr[size], "Too many elements in initializer, expected only %d.", size);
|
||||
return false;
|
||||
}
|
||||
VECEACH(expr->initializer_expr, i)
|
||||
{
|
||||
Expr *field = expr->initializer_expr[i];
|
||||
Decl *decl;
|
||||
if (expr_may_be_struct_field_decl(field))
|
||||
{
|
||||
if (field->expr_kind == EXPR_IDENTIFIER)
|
||||
{
|
||||
decl = decl_find_by_name(members, field->identifier_expr.identifier);
|
||||
}
|
||||
TODO
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i >= size)
|
||||
{
|
||||
SEMA_ERROR(field, "Too many elements in initializer");
|
||||
return false;
|
||||
}
|
||||
decl = members[i];
|
||||
}
|
||||
if (!cast(field, decl->type, CAST_TYPE_IMPLICIT_ASSIGN)) return false;
|
||||
if (!sema_analyse_expr(context, members[i]->type, expr->initializer_expr[i])) return false;
|
||||
}
|
||||
expr->type = assigned;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_initializer_list(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
assert(to);
|
||||
@@ -1303,6 +1382,7 @@ static bool sema_expr_analyse_not(Context *context, Type *to, Expr *expr, Expr *
|
||||
case TYPE_STRING:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERROR:
|
||||
case TYPE_META_TYPE:
|
||||
SEMA_ERROR(expr, "Cannot use 'not' on %s", type_to_error_string(inner->type));
|
||||
return false;
|
||||
}
|
||||
@@ -1428,8 +1508,8 @@ static inline bool sema_expr_analyse_unary(Context *context, Type *to, Expr *exp
|
||||
case UNARYOP_DEC:
|
||||
case UNARYOP_INC:
|
||||
return sema_expr_analyse_incdec(context, to, expr, inner);
|
||||
default:
|
||||
UNREACHABLE
|
||||
case UNARYOP_ERROR:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1457,9 +1537,398 @@ static inline bool sema_expr_analyse_try(Context *context, Type *to, Expr *expr)
|
||||
return true;
|
||||
}
|
||||
|
||||
static Ast *ast_shallow_copy(Ast *source)
|
||||
{
|
||||
Ast *copy = malloc_arena(sizeof(Ast));
|
||||
memcpy(copy, source, sizeof(Ast));
|
||||
return copy;
|
||||
}
|
||||
|
||||
static Expr *expr_shallow_copy(Expr *source)
|
||||
{
|
||||
Expr *copy = malloc_arena(sizeof(Expr));
|
||||
memcpy(copy, source, sizeof(Expr));
|
||||
return copy;
|
||||
}
|
||||
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list);
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr);
|
||||
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source);
|
||||
static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert);
|
||||
|
||||
static TypeInfo *type_info_copy_from_macro(Context *context, Expr *macro, TypeInfo *source)
|
||||
{
|
||||
if (!source) return NULL;
|
||||
TypeInfo *copy = malloc_arena(sizeof(TypeInfo));
|
||||
memcpy(copy, source, sizeof(TypeInfo));
|
||||
switch (source->kind)
|
||||
{
|
||||
case TYPE_INFO_POISON:
|
||||
return copy;
|
||||
case TYPE_INFO_IDENTIFIER:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
TODO
|
||||
break;
|
||||
case TYPE_INFO_EXPRESSION:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->unresolved_type_expr = expr_copy_from_macro(context, macro, source->unresolved_type_expr);
|
||||
return copy;
|
||||
case TYPE_INFO_ARRAY:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.len = expr_copy_from_macro(context, macro, source->array.len);
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
return copy;
|
||||
case TYPE_INFO_INC_ARRAY:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->array.base = type_info_copy_from_macro(context, macro, source->array.base);
|
||||
return copy;
|
||||
case TYPE_INFO_POINTER:
|
||||
assert(source->resolve_status == RESOLVE_NOT_DONE);
|
||||
copy->pointer = type_info_copy_from_macro(context, macro, source->pointer);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Expr *expr_copy_from_macro(Context *context, Expr *macro, Expr *source_expr)
|
||||
{
|
||||
#define EXPR_COPY(x) x = expr_copy_from_macro(context, macro, x)
|
||||
if (!source_expr) return NULL;
|
||||
Expr *expr = expr_shallow_copy(source_expr);
|
||||
switch (source_expr->expr_kind)
|
||||
{
|
||||
case EXPR_POISONED:
|
||||
return source_expr;
|
||||
case EXPR_TRY:
|
||||
EXPR_COPY(expr->try_expr.expr);
|
||||
EXPR_COPY(expr->try_expr.else_expr);
|
||||
return expr;
|
||||
case EXPR_CONST:
|
||||
return expr;
|
||||
case EXPR_BINARY:
|
||||
EXPR_COPY(expr->binary_expr.left);
|
||||
EXPR_COPY(expr->binary_expr.right);
|
||||
return expr;
|
||||
case EXPR_TERNARY:
|
||||
EXPR_COPY(expr->ternary_expr.cond);
|
||||
EXPR_COPY(expr->ternary_expr.then_expr);
|
||||
EXPR_COPY(expr->ternary_expr.else_expr);
|
||||
return expr;
|
||||
case EXPR_UNARY:
|
||||
EXPR_COPY(expr->unary_expr.expr);
|
||||
return expr;
|
||||
case EXPR_POST_UNARY:
|
||||
EXPR_COPY(expr->post_expr.expr);
|
||||
return expr;
|
||||
case EXPR_TYPE:
|
||||
expr->type_expr.type = type_info_copy_from_macro(context, macro, expr->type_expr.type);
|
||||
return expr;
|
||||
case EXPR_IDENTIFIER:
|
||||
TODO
|
||||
break;
|
||||
case EXPR_TYPE_ACCESS:
|
||||
expr->type_access.type = type_info_copy_from_macro(context, macro, expr->type_expr.type);
|
||||
return expr;
|
||||
case EXPR_CALL:
|
||||
EXPR_COPY(expr->call_expr.function);
|
||||
expr->call_expr.arguments = expr_copy_expr_list_from_macro(context, macro, expr->call_expr.arguments);
|
||||
return expr;
|
||||
case EXPR_SIZEOF:
|
||||
TODO
|
||||
break;
|
||||
case EXPR_SUBSCRIPT:
|
||||
EXPR_COPY(expr->subscript_expr.expr);
|
||||
EXPR_COPY(expr->subscript_expr.index);
|
||||
return expr;
|
||||
case EXPR_ACCESS:
|
||||
EXPR_COPY(expr->access_expr.parent);
|
||||
return expr;
|
||||
case EXPR_STRUCT_VALUE:
|
||||
expr->struct_value_expr.type = type_info_copy_from_macro(context, macro, expr->struct_value_expr.type);
|
||||
EXPR_COPY(expr->struct_value_expr.init_expr);
|
||||
return expr;
|
||||
case EXPR_STRUCT_INIT_VALUES:
|
||||
TODO
|
||||
return expr;
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
expr->initializer_expr = expr_copy_expr_list_from_macro(context, macro, expr->initializer_expr);
|
||||
return expr;
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
expr->expression_list = expr_copy_expr_list_from_macro(context, macro, expr->expression_list);
|
||||
return expr;
|
||||
case EXPR_CAST:
|
||||
EXPR_COPY(expr->cast_expr.expr);
|
||||
expr->cast_expr.type_info = expr->cast_expr.type_info = type_info_copy_from_macro(context, macro, expr->cast_expr.type_info);
|
||||
return expr;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
EXPR_COPY(expr->expr_scope.expr);
|
||||
return expr;
|
||||
case EXPR_MACRO_EXPR:
|
||||
EXPR_COPY(expr->macro_expr);
|
||||
return expr;
|
||||
}
|
||||
#undef EXPR_COPY
|
||||
}
|
||||
|
||||
static Expr **expr_copy_expr_list_from_macro(Context *context, Expr *macro, Expr **expr_list)
|
||||
{
|
||||
Expr **result = NULL;
|
||||
VECEACH(expr_list, i)
|
||||
{
|
||||
vec_add(result, expr_copy_from_macro(context, macro, expr_list[i]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void ast_copy_list_from_macro(Context *context, Expr *macro, Ast ***to_convert)
|
||||
{
|
||||
Ast **result = NULL;
|
||||
Ast **list = *to_convert;
|
||||
VECEACH(list, i)
|
||||
{
|
||||
vec_add(result, ast_copy_from_macro(context, macro, list[i]));
|
||||
}
|
||||
*to_convert = result;
|
||||
}
|
||||
|
||||
static void type_info_copy_list_from_macro(Context *context, Expr *macro, TypeInfo ***to_convert)
|
||||
{
|
||||
TypeInfo **result = NULL;
|
||||
TypeInfo **list = *to_convert;
|
||||
VECEACH(list, i)
|
||||
{
|
||||
vec_add(result, type_info_copy_from_macro(context, macro, list[i]));
|
||||
}
|
||||
*to_convert = result;
|
||||
}
|
||||
|
||||
static Ast *ast_copy_from_macro(Context *context, Expr *macro, Ast *source)
|
||||
{
|
||||
#define EXPR_COPY(x) x = expr_copy_from_macro(context, macro, x)
|
||||
#define AST_COPY(x) x = ast_copy_from_macro(context, macro, x)
|
||||
Ast *ast = ast_shallow_copy(source);
|
||||
switch (source->ast_kind)
|
||||
{
|
||||
case AST_POISONED:
|
||||
return ast;
|
||||
case AST_ASM_STMT:
|
||||
TODO
|
||||
case AST_ATTRIBUTE:
|
||||
UNREACHABLE
|
||||
case AST_BREAK_STMT:
|
||||
return ast;
|
||||
case AST_CASE_STMT:
|
||||
AST_COPY(ast->case_stmt.body);
|
||||
EXPR_COPY(ast->case_stmt.expr);
|
||||
return ast;
|
||||
break;
|
||||
case AST_CATCH_STMT:
|
||||
AST_COPY(ast->catch_stmt.body);
|
||||
return ast;
|
||||
case AST_COMPOUND_STMT:
|
||||
ast_copy_list_from_macro(context, macro, &ast->compound_stmt.stmts);
|
||||
return ast;
|
||||
case AST_CONTINUE_STMT:
|
||||
return ast;
|
||||
case AST_CT_IF_STMT:
|
||||
EXPR_COPY(ast->ct_if_stmt.expr);
|
||||
AST_COPY(ast->ct_if_stmt.elif);
|
||||
AST_COPY(ast->ct_if_stmt.then);
|
||||
return ast;
|
||||
case AST_CT_ELIF_STMT:
|
||||
EXPR_COPY(ast->ct_elif_stmt.expr);
|
||||
AST_COPY(ast->ct_elif_stmt.then);
|
||||
AST_COPY(ast->ct_elif_stmt.elif);
|
||||
return ast;
|
||||
case AST_CT_ELSE_STMT:
|
||||
AST_COPY(ast->ct_else_stmt);
|
||||
return ast;
|
||||
case AST_CT_FOR_STMT:
|
||||
AST_COPY(ast->ct_for_stmt.body);
|
||||
EXPR_COPY(ast->ct_for_stmt.expr);
|
||||
return ast;
|
||||
case AST_CT_SWITCH_STMT:
|
||||
EXPR_COPY(ast->ct_switch_stmt.cond);
|
||||
ast_copy_list_from_macro(context, macro, &ast->ct_switch_stmt.body);
|
||||
return ast;
|
||||
case AST_CT_DEFAULT_STMT:
|
||||
AST_COPY(ast->ct_default_stmt);
|
||||
return ast;
|
||||
case AST_CT_CASE_STMT:
|
||||
AST_COPY(ast->ct_case_stmt.body);
|
||||
type_info_copy_list_from_macro(context, macro, &ast->ct_case_stmt.types);
|
||||
return ast;
|
||||
case AST_DECLARE_STMT:
|
||||
TODO
|
||||
return ast;
|
||||
case AST_DEFAULT_STMT:
|
||||
AST_COPY(ast->case_stmt.body);
|
||||
return ast;
|
||||
case AST_DEFER_STMT:
|
||||
assert(!ast->defer_stmt.prev_defer);
|
||||
AST_COPY(ast->defer_stmt.body);
|
||||
return ast;
|
||||
case AST_DO_STMT:
|
||||
AST_COPY(ast->do_stmt.body);
|
||||
EXPR_COPY(ast->do_stmt.expr);
|
||||
return ast;
|
||||
case AST_EXPR_STMT:
|
||||
EXPR_COPY(ast->expr_stmt);
|
||||
return ast;
|
||||
case AST_FOR_STMT:
|
||||
EXPR_COPY(ast->for_stmt.cond);
|
||||
EXPR_COPY(ast->for_stmt.incr);
|
||||
AST_COPY(ast->for_stmt.body);
|
||||
AST_COPY(ast->for_stmt.init);
|
||||
return ast;
|
||||
case AST_FUNCTION_BLOCK_STMT:
|
||||
ast_copy_list_from_macro(context, macro, &ast->function_block_stmt.stmts);
|
||||
return ast;
|
||||
case AST_GENERIC_CASE_STMT:
|
||||
AST_COPY(ast->generic_case_stmt.body);
|
||||
// ast->generic_case_stmt.types = ...
|
||||
TODO
|
||||
return ast;
|
||||
case AST_GENERIC_DEFAULT_STMT:
|
||||
AST_COPY(ast->generic_default_stmt);
|
||||
return ast;
|
||||
case AST_GOTO_STMT:
|
||||
AST_COPY(ast->goto_stmt.label);
|
||||
// TODO fixup name, which needs to be macro local.
|
||||
TODO
|
||||
return ast;
|
||||
case AST_IF_STMT:
|
||||
AST_COPY(ast->if_stmt.cond);
|
||||
AST_COPY(ast->if_stmt.decl);
|
||||
AST_COPY(ast->if_stmt.else_body);
|
||||
AST_COPY(ast->if_stmt.then_body);
|
||||
return ast;
|
||||
case AST_LABEL:
|
||||
assert(!ast->label_stmt.defer);
|
||||
assert(!ast->label_stmt.in_defer);
|
||||
// TODO fixup name which needs to be macro local.
|
||||
TODO
|
||||
return ast;
|
||||
case AST_NOP_STMT:
|
||||
return ast;
|
||||
case AST_RETURN_STMT:
|
||||
EXPR_COPY(ast->return_stmt.expr);
|
||||
// TODO handle conversions?
|
||||
TODO
|
||||
return ast;
|
||||
case AST_DECL_EXPR_LIST:
|
||||
ast_copy_list_from_macro(context, macro, &ast->decl_expr_stmt);
|
||||
return ast;
|
||||
case AST_SWITCH_STMT:
|
||||
AST_COPY(ast->switch_stmt.decl);
|
||||
AST_COPY(ast->switch_stmt.cond);
|
||||
ast_copy_list_from_macro(context, macro, &ast->switch_stmt.cases);
|
||||
return ast;
|
||||
case AST_THROW_STMT:
|
||||
EXPR_COPY(ast->throw_stmt.throw_value);
|
||||
return ast;
|
||||
case AST_TRY_STMT:
|
||||
AST_COPY(ast->try_stmt);
|
||||
return ast;
|
||||
case AST_NEXT_STMT:
|
||||
TODO
|
||||
return ast;
|
||||
case AST_VOLATILE_STMT:
|
||||
TODO
|
||||
return ast;
|
||||
case AST_WHILE_STMT:
|
||||
AST_COPY(ast->while_stmt.cond);
|
||||
AST_COPY(ast->while_stmt.decl);
|
||||
AST_COPY(ast->while_stmt.body);
|
||||
return ast;
|
||||
case AST_SCOPED_STMT:
|
||||
AST_COPY(ast->scoped_stmt.stmt);
|
||||
return ast;
|
||||
}
|
||||
|
||||
#undef EXPR_COPY
|
||||
#undef AST_COPY
|
||||
}
|
||||
static inline bool sema_expr_analyse_macro_call(Context *context, Type *to, Expr *macro, Expr *inner)
|
||||
{
|
||||
Expr *func_expr = inner->call_expr.function;
|
||||
|
||||
if (!sema_analyse_expr(context, NULL, func_expr)) return false;
|
||||
|
||||
Decl *decl;
|
||||
switch (func_expr->expr_kind)
|
||||
{
|
||||
case EXPR_TYPE_ACCESS:
|
||||
TODO
|
||||
case EXPR_IDENTIFIER:
|
||||
decl = func_expr->identifier_expr.decl;
|
||||
break;
|
||||
default:
|
||||
TODO
|
||||
}
|
||||
if (decl->decl_kind != DECL_MACRO)
|
||||
{
|
||||
SEMA_ERROR(macro, "A macro was expected here.");
|
||||
return false;
|
||||
}
|
||||
Expr **args =func_expr->call_expr.arguments;
|
||||
Decl **func_params = decl->macro_decl.parameters;
|
||||
// TODO handle bare macros.
|
||||
// TODO handle $ args and # args
|
||||
unsigned num_args = vec_size(args);
|
||||
// unsigned num_params = vec_size(func_params);
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
{
|
||||
Expr *arg = args[i];
|
||||
Decl *param = func_params[i];
|
||||
if (!sema_analyse_expr(context, param->type, arg)) return false;
|
||||
}
|
||||
Ast *body = ast_copy_from_macro(context, inner, decl->macro_decl.body);
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_macro_call2(Context *context, Type *to, Expr *expr, Decl *macro)
|
||||
{
|
||||
Ast *macro_parent;
|
||||
// TODO handle loops
|
||||
Decl *stored_macro = context->evaluating_macro;
|
||||
Type *stored_rtype = context->rtype;
|
||||
context->evaluating_macro = macro;
|
||||
context->rtype = macro->macro_decl.rtype->type;
|
||||
// Handle escaping macro
|
||||
bool success = sema_analyse_statement(context, macro->macro_decl.body);
|
||||
context->evaluating_macro = stored_macro;
|
||||
context->rtype = stored_rtype;
|
||||
if (!success) return false;
|
||||
|
||||
TODO
|
||||
return success;
|
||||
};
|
||||
|
||||
static inline bool sema_expr_analyse_macro_expr(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
Expr *inner = expr->macro_expr;
|
||||
switch (inner->expr_kind)
|
||||
{
|
||||
case EXPR_CALL:
|
||||
return sema_expr_analyse_macro_call(context, to, expr, inner);
|
||||
case EXPR_ACCESS:
|
||||
case EXPR_IDENTIFIER:
|
||||
// Allow @f unrolling?
|
||||
default:
|
||||
SEMA_ERROR(expr, "Expected a macro name after '@'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr)
|
||||
{
|
||||
TODO
|
||||
if (!sema_resolve_type_info(context, expr->type_expr.type))
|
||||
{
|
||||
return expr_poison(expr);
|
||||
}
|
||||
expr->type = type_get_meta(expr->type_expr.type->type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1476,6 +1945,8 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr *
|
||||
return false;
|
||||
case EXPR_SCOPED_EXPR:
|
||||
UNREACHABLE
|
||||
case EXPR_MACRO_EXPR:
|
||||
return sema_expr_analyse_macro_expr(context, to, expr);
|
||||
case EXPR_TRY:
|
||||
return sema_expr_analyse_try(context, to, expr);
|
||||
case EXPR_CONST:
|
||||
|
||||
@@ -504,7 +504,7 @@ Token lexer_scan_token(Lexer *lexer)
|
||||
switch (c)
|
||||
{
|
||||
case '@':
|
||||
return scan_prefixed_ident(lexer, TOKEN_AT_IDENT, TOKEN_AT, true, "@");
|
||||
return make_token(lexer, TOKEN_AT, "@");
|
||||
case '\'':
|
||||
return scan_char(lexer);
|
||||
case '"':
|
||||
|
||||
@@ -170,12 +170,6 @@ void llvm_codegen(Context *context)
|
||||
gencontext_emit_function_decl(&gen_context, context->functions[i]);
|
||||
}
|
||||
|
||||
VECEACH(gen_context.generated_types, i)
|
||||
{
|
||||
Type *type = gen_context.generated_types[i];
|
||||
type->backend_debug_type = NULL;
|
||||
type->backend_type = NULL;
|
||||
}
|
||||
|
||||
gencontext_print_llvm_ir(&gen_context);
|
||||
|
||||
|
||||
@@ -109,6 +109,7 @@ LLVMMetadataRef gencontext_get_debug_type(GenContext *context, Type *type)
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
case TYPE_META_TYPE:
|
||||
UNREACHABLE
|
||||
case TYPE_BOOL:
|
||||
return gencontext_simple_debug_type(context, type, DW_ATE_boolean);
|
||||
|
||||
@@ -41,7 +41,7 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
TODO
|
||||
case TYPE_POINTER:
|
||||
return LLVMBuildGEP2(context->builder,
|
||||
BACKEND_TYPE(type->pointer),
|
||||
llvm_type(type->pointer),
|
||||
gencontext_emit_expr(context, expr->subscript_expr.expr),
|
||||
&index, 1, "[]");
|
||||
case TYPE_VARARRAY:
|
||||
@@ -56,7 +56,7 @@ static inline LLVMValueRef gencontext_emit_subscript_addr(GenContext *context, E
|
||||
static inline LLVMValueRef gencontext_emit_access_addr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
|
||||
return LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
return LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
}
|
||||
|
||||
LLVMValueRef gencontext_emit_scoped_expr(GenContext *context, Expr *expr)
|
||||
@@ -103,6 +103,7 @@ LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr)
|
||||
case EXPR_INITIALIZER_LIST:
|
||||
case EXPR_EXPRESSION_LIST:
|
||||
case EXPR_CAST:
|
||||
case EXPR_MACRO_EXPR:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
@@ -115,9 +116,9 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV
|
||||
case CAST_ERROR:
|
||||
UNREACHABLE
|
||||
case CAST_PTRPTR:
|
||||
return LLVMBuildPointerCast(context->builder, value, BACKEND_TYPE(type), "ptrptr");
|
||||
return LLVMBuildPointerCast(context->builder, value, llvm_type(type), "ptrptr");
|
||||
case CAST_PTRXI:
|
||||
return LLVMBuildPtrToInt(context->builder, value, BACKEND_TYPE(type), "ptrxi");
|
||||
return LLVMBuildPtrToInt(context->builder, value, llvm_type(type), "ptrxi");
|
||||
case CAST_VARRPTR:
|
||||
TODO
|
||||
case CAST_ARRPTR:
|
||||
@@ -125,45 +126,45 @@ LLVMValueRef gencontext_emit_cast(GenContext *context, CastKind cast_kind, LLVMV
|
||||
case CAST_STRPTR:
|
||||
TODO
|
||||
case CAST_PTRBOOL:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(BACKEND_TYPE(type->canonical->pointer)), "ptrbool");
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstPointerNull(llvm_type(type->canonical->pointer)), "ptrbool");
|
||||
case CAST_BOOLINT:
|
||||
return LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "boolsi");
|
||||
return LLVMBuildTrunc(context->builder, value, llvm_type(type), "boolsi");
|
||||
case CAST_FPBOOL:
|
||||
return LLVMBuildFCmp(context->builder, LLVMRealUNE, value, LLVMConstNull(LLVMTypeOf(value)), "fpbool");
|
||||
case CAST_BOOLFP:
|
||||
return LLVMBuildSIToFP(context->builder, value, BACKEND_TYPE(type), "boolfp");
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(type), "boolfp");
|
||||
case CAST_INTBOOL:
|
||||
return LLVMBuildICmp(context->builder, LLVMIntNE, value, LLVMConstNull(LLVMTypeOf(value)), "intbool");
|
||||
case CAST_FPFP:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildFPTrunc(context->builder, value, BACKEND_TYPE(type), "fpfptrunc")
|
||||
: LLVMBuildFPExt(context->builder, value, BACKEND_TYPE(type), "fpfpext");
|
||||
? LLVMBuildFPTrunc(context->builder, value, llvm_type(type), "fpfptrunc")
|
||||
: LLVMBuildFPExt(context->builder, value, llvm_type(type), "fpfpext");
|
||||
case CAST_FPSI:
|
||||
return LLVMBuildFPToSI(context->builder, value, BACKEND_TYPE(type), "fpsi");
|
||||
return LLVMBuildFPToSI(context->builder, value, llvm_type(type), "fpsi");
|
||||
case CAST_FPUI:
|
||||
return LLVMBuildFPToUI(context->builder, value, BACKEND_TYPE(type), "fpui");
|
||||
return LLVMBuildFPToUI(context->builder, value, llvm_type(type), "fpui");
|
||||
case CAST_SISI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "sisitrunc")
|
||||
: LLVMBuildSExt(context->builder, value, BACKEND_TYPE(type), "sisiext");
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "sisitrunc")
|
||||
: LLVMBuildSExt(context->builder, value, llvm_type(type), "sisiext");
|
||||
case CAST_SIUI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "siuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "siuiext");
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "siuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "siuiext");
|
||||
case CAST_SIFP:
|
||||
return LLVMBuildSIToFP(context->builder, value, BACKEND_TYPE(type), "sifp");
|
||||
return LLVMBuildSIToFP(context->builder, value, llvm_type(type), "sifp");
|
||||
case CAST_XIPTR:
|
||||
return LLVMBuildIntToPtr(context->builder, value, BACKEND_TYPE(type), "xiptr");
|
||||
return LLVMBuildIntToPtr(context->builder, value, llvm_type(type), "xiptr");
|
||||
case CAST_UISI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "uisitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "uisiext");
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uisitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uisiext");
|
||||
case CAST_UIUI:
|
||||
return type_convert_will_trunc(type, target_type)
|
||||
? LLVMBuildTrunc(context->builder, value, BACKEND_TYPE(type), "uiuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, BACKEND_TYPE(type), "uiuiext");
|
||||
? LLVMBuildTrunc(context->builder, value, llvm_type(type), "uiuitrunc")
|
||||
: LLVMBuildZExt(context->builder, value, llvm_type(type), "uiuiext");
|
||||
case CAST_UIFP:
|
||||
return LLVMBuildUIToFP(context->builder, value, BACKEND_TYPE(type), "uifp");
|
||||
return LLVMBuildUIToFP(context->builder, value, llvm_type(type), "uifp");
|
||||
case CAST_ENUMSI:
|
||||
TODO
|
||||
}
|
||||
@@ -177,11 +178,11 @@ static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *
|
||||
static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, bool use_mod, LLVMValueRef current_value, Expr *expr, int diff)
|
||||
{
|
||||
Type *type = expr->type->canonical;
|
||||
LLVMTypeRef llvm_type = BACKEND_TYPE(type);
|
||||
LLVMTypeRef llvm_type = llvm_type(type);
|
||||
|
||||
if (type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
LLVMValueRef add = LLVMConstInt(diff < 0 ? BACKEND_TYPE(type_isize) : BACKEND_TYPE(type_usize), diff, diff < 0);
|
||||
LLVMValueRef add = LLVMConstInt(diff < 0 ? llvm_type(type_isize) : llvm_type(type_usize), diff, diff < 0);
|
||||
return LLVMBuildGEP2(context->builder, llvm_type, current_value, &add, 1, "ptrincdec");
|
||||
}
|
||||
|
||||
@@ -200,7 +201,7 @@ static inline LLVMValueRef gencontext_emit_inc_dec_change(GenContext *context, b
|
||||
static inline LLVMValueRef gencontext_emit_pre_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
|
||||
{
|
||||
LLVMValueRef addr = gencontext_emit_address(context, expr);
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->type), addr, "");
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, llvm_type(expr->type), addr, "");
|
||||
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
|
||||
LLVMBuildStore(context->builder, result, addr);
|
||||
return result;
|
||||
@@ -209,7 +210,7 @@ static inline LLVMValueRef gencontext_emit_pre_inc_dec(GenContext *context, Expr
|
||||
static inline LLVMValueRef gencontext_emit_post_inc_dec(GenContext *context, Expr *expr, int diff, bool use_mod)
|
||||
{
|
||||
LLVMValueRef addr = gencontext_emit_address(context, expr);
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->type), addr, "");
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, llvm_type(expr->type), addr, "");
|
||||
LLVMValueRef result = gencontext_emit_inc_dec_change(context, use_mod, value, expr, diff);
|
||||
LLVMBuildStore(context->builder, result, addr);
|
||||
return value;
|
||||
@@ -234,7 +235,7 @@ LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr)
|
||||
case UNARYOP_ADDR:
|
||||
return gencontext_emit_address(context, expr->unary_expr.expr);
|
||||
case UNARYOP_DEREF:
|
||||
return LLVMBuildLoad2(context->builder, BACKEND_TYPE(expr->unary_expr.expr->type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||
return LLVMBuildLoad2(context->builder, llvm_type(expr->unary_expr.expr->type), gencontext_emit_expr(context, expr->unary_expr.expr), "deref");
|
||||
case UNARYOP_INC:
|
||||
return gencontext_emit_pre_inc_dec(context, expr->unary_expr.expr, 1, false);
|
||||
case UNARYOP_DEC:
|
||||
@@ -277,9 +278,9 @@ static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *ex
|
||||
|
||||
// Simplify for LLVM by entering the constants we already know of.
|
||||
LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false);
|
||||
LLVMValueRef logicValues[2] = { result_on_skip, rhs };
|
||||
LLVMValueRef logic_values[2] = { result_on_skip, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { start_block, rhs_block };
|
||||
LLVMAddIncoming(phi, logicValues, blocks, 2);
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
@@ -293,7 +294,7 @@ static inline LLVMValueRef gencontext_emit_initialization_from_expr(GenContext *
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_struct_value_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef temp_alloc = gencontext_emit_alloca(context, BACKEND_TYPE(expr->type), "temp");
|
||||
LLVMValueRef temp_alloc = gencontext_emit_alloca(context, llvm_type(expr->type), "temp");
|
||||
return gencontext_emit_initialization_from_expr(context, temp_alloc, expr->struct_value_expr.init_expr);
|
||||
}
|
||||
|
||||
@@ -314,7 +315,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
LLVMValueRef rhs_value;
|
||||
if (lhs_addr)
|
||||
{
|
||||
lhs_value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(lhs->type), lhs_addr, "");
|
||||
lhs_value = LLVMBuildLoad2(context->builder, llvm_type(lhs->type), lhs_addr, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -348,7 +349,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
{
|
||||
if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff");
|
||||
rhs_value = LLVMBuildNeg(context->builder, rhs_value, "");
|
||||
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptrsub");
|
||||
return LLVMBuildGEP2(context->builder, llvm_type(lhs->type), lhs_value, &rhs_value, 1, "ptrsub");
|
||||
}
|
||||
if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub");
|
||||
return gencontext_emit_sub_int(context, lhs->type->canonical, binary_op == BINARYOP_SUB_MOD, lhs_value, rhs_value);
|
||||
@@ -357,7 +358,7 @@ static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, LLVM
|
||||
if (lhs->type->canonical->type_kind == TYPE_POINTER)
|
||||
{
|
||||
assert(type_is_integer(rhs->type->canonical));
|
||||
return LLVMBuildGEP2(context->builder, BACKEND_TYPE(lhs->type), lhs_value, &rhs_value, 1, "ptradd");
|
||||
return LLVMBuildGEP2(context->builder, llvm_type(lhs->type), lhs_value, &rhs_value, 1, "ptradd");
|
||||
}
|
||||
if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd");
|
||||
return gencontext_emit_add_int(context, lhs->type->canonical, binary_op == BINARYOP_ADD_MOD, lhs_value, rhs_value);
|
||||
@@ -491,9 +492,9 @@ LLVMValueRef gencontext_emit_elvis_expr(GenContext *context, Expr *expr)
|
||||
gencontext_emit_block(context, phi_block);
|
||||
LLVMValueRef phi = LLVMBuildPhi(context->builder, expr->type->backend_type, "val");
|
||||
|
||||
LLVMValueRef logicValues[2] = { lhs, rhs };
|
||||
LLVMValueRef logic_values[2] = { lhs, rhs };
|
||||
LLVMBasicBlockRef blocks[2] = { current_block, rhs_block };
|
||||
LLVMAddIncoming(phi, logicValues, blocks, 2);
|
||||
LLVMAddIncoming(phi, logic_values, blocks, 2);
|
||||
|
||||
return phi;
|
||||
}
|
||||
@@ -539,7 +540,7 @@ static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *e
|
||||
|
||||
LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMTypeRef type = BACKEND_TYPE(expr->type);
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
switch (expr->const_expr.type)
|
||||
{
|
||||
case CONST_INT:
|
||||
@@ -578,7 +579,8 @@ LLVMValueRef gencontext_emit_call_expr(GenContext *context, Expr *expr)
|
||||
Decl *function = expr->call_expr.function->identifier_expr.decl;
|
||||
|
||||
LLVMValueRef func = function->func.backend_value;
|
||||
LLVMTypeRef func_type = BACKEND_TYPE(function->type);
|
||||
LLVMTypeRef func_type = llvm_type(function->type);
|
||||
// TODO fix throws and return optimization
|
||||
LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, args, "call");
|
||||
/*
|
||||
if (function->func.function_signature.convention)
|
||||
@@ -595,7 +597,7 @@ static inline LLVMValueRef gencontext_emit_access_expr(GenContext *context, Expr
|
||||
{
|
||||
// Improve, add string description to the access?
|
||||
LLVMValueRef value = gencontext_emit_address(context, expr->access_expr.parent);
|
||||
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, BACKEND_TYPE(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
LLVMValueRef val = LLVMBuildStructGEP2(context->builder, llvm_type(expr->access_expr.parent->type), value, (unsigned)expr->access_expr.index, "");
|
||||
return LLVMBuildLoad2(context->builder, gencontext_get_llvm_type(context, expr->type), val, "");
|
||||
}
|
||||
|
||||
@@ -611,19 +613,22 @@ static inline LLVMValueRef gencontext_emit_expression_list_expr(GenContext *cont
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_initializer_list_expr(GenContext *context, Expr *expr)
|
||||
{
|
||||
LLVMValueRef value = LLVMGetUndef(LLVMTYPE(expr->type));
|
||||
LLVMTypeRef type = llvm_type(expr->type);
|
||||
LLVMValueRef value = LLVMGetUndef(type);
|
||||
|
||||
/*
|
||||
for (expr->initializer_expr)
|
||||
expr->type.
|
||||
else if (littype->tag == StructTag) {
|
||||
LLVMValueRef strval = LLVMGetUndef(genlType(gen, littype));
|
||||
unsigned int pos = 0;
|
||||
for (nodesFor(lit->args, cnt, nodesp))
|
||||
strval = LLVMBuildInsertValue(gen->builder, strval, genlExpr(gen, *nodesp), pos++, "literal");
|
||||
return strval;
|
||||
if (!vec_size(expr->initializer_expr))
|
||||
{
|
||||
LLVMValueRef ref = gencontext_emit_alloca(context, type, "temp");
|
||||
value = LLVMBuildMemSet(context->builder, ref, LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false), expr->type->decl->strukt.alignment);
|
||||
return ref;
|
||||
}
|
||||
|
||||
VECEACH(expr->initializer_expr, i)
|
||||
{
|
||||
LLVMValueRef init_value = gencontext_emit_expr(context, expr->initializer_expr[i]);
|
||||
value = LLVMBuildInsertValue(context->builder, value, init_value, i, "literal");
|
||||
}
|
||||
TODO*/
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -654,6 +659,7 @@ LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr)
|
||||
case EXPR_SIZEOF:
|
||||
case EXPR_TYPE_ACCESS:
|
||||
case EXPR_TRY:
|
||||
case EXPR_MACRO_EXPR:
|
||||
// These are folded in the semantic analysis step.
|
||||
UNREACHABLE
|
||||
case EXPR_IDENTIFIER:
|
||||
|
||||
@@ -78,10 +78,22 @@ static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, un
|
||||
assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM);
|
||||
|
||||
// Allocate room on stack and copy.
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name);
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref);
|
||||
}
|
||||
|
||||
void gencontext_emit_implicit_return(GenContext *context)
|
||||
{
|
||||
if (func_has_error_return(&context->cur_func_decl->func.function_signature))
|
||||
{
|
||||
LLVMBuildRet(context->builder, LLVMConstInt(llvm_type(type_ulong), 0, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMBuildRetVoid(context->builder);
|
||||
}
|
||||
}
|
||||
|
||||
void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
{
|
||||
assert(decl->func.backend_value);
|
||||
@@ -90,6 +102,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
LLVMBuilderRef prev_builder = context->builder;
|
||||
|
||||
context->function = decl->func.backend_value;
|
||||
context->cur_func_decl = decl;
|
||||
|
||||
LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry");
|
||||
context->current_block = entry;
|
||||
@@ -100,10 +113,22 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point");
|
||||
context->alloca_point = alloca_point;
|
||||
|
||||
unsigned return_parameter = func_return_value_as_out(&decl->func.function_signature) ? 1 : 0;
|
||||
|
||||
if (return_parameter)
|
||||
{
|
||||
context->return_out = gencontext_emit_alloca(context, llvm_type(decl->func.function_signature.rtype->type), "retval");
|
||||
LLVMBuildStore(context->builder, LLVMGetParam(context->function, 0), context->return_out);
|
||||
}
|
||||
else
|
||||
{
|
||||
context->return_out = NULL;
|
||||
}
|
||||
|
||||
// Generate LLVMValueRef's for all parameters, so we can use them as local vars in code
|
||||
VECEACH(decl->func.function_signature.params, i)
|
||||
{
|
||||
gencontext_emit_parameter(context, decl->func.function_signature.params[i], i);
|
||||
gencontext_emit_parameter(context, decl->func.function_signature.params[i], i + return_parameter);
|
||||
}
|
||||
|
||||
VECEACH(decl->func.labels, i)
|
||||
@@ -130,7 +155,7 @@ void gencontext_emit_function_body(GenContext *context, Decl *decl)
|
||||
assert(decl->func.function_signature.rtype->type->type_kind == TYPE_VOID);
|
||||
assert(decl->func.body->compound_stmt.defer_list.end == NULL);
|
||||
gencontext_emit_defer(context, decl->func.body->compound_stmt.defer_list.start, NULL);
|
||||
LLVMBuildRetVoid(context->builder);
|
||||
gencontext_emit_implicit_return(context);
|
||||
}
|
||||
|
||||
// erase alloca point
|
||||
@@ -151,7 +176,7 @@ void gencontext_emit_function_decl(GenContext *context, Decl *decl)
|
||||
assert(decl->decl_kind == DECL_FUNC);
|
||||
// Resolve function backend type for function.
|
||||
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||
BACKEND_TYPE(decl->type));
|
||||
llvm_type(decl->type));
|
||||
|
||||
// Specify appropriate storage class, visibility and call convention
|
||||
// extern functions (linkedited in separately):
|
||||
@@ -208,11 +233,11 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
|
||||
UNREACHABLE;
|
||||
case DECL_FUNC:
|
||||
decl->func.backend_value = LLVMAddFunction(context->module, decl->external_name,
|
||||
BACKEND_TYPE(decl->type));
|
||||
llvm_type(decl->type));
|
||||
LLVMSetVisibility(decl->func.backend_value, LLVMDefaultVisibility);
|
||||
break;
|
||||
case DECL_VAR:
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, BACKEND_TYPE(decl->type), decl->external_name);
|
||||
decl->var.backend_ref = LLVMAddGlobal(context->module, llvm_type(decl->type), decl->external_name);
|
||||
LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case DECL_TYPEDEF:
|
||||
@@ -221,7 +246,7 @@ void gencontext_emit_extern_decl(GenContext *context, Decl *decl)
|
||||
TODO
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
BACKEND_TYPE(decl->type);
|
||||
llvm_type(decl->type);
|
||||
break;
|
||||
case DECL_ENUM:
|
||||
TODO
|
||||
|
||||
@@ -64,8 +64,7 @@ typedef struct
|
||||
Context *ast_context;
|
||||
BreakContinue break_continue_stack[BREAK_STACK_MAX];
|
||||
size_t break_continue_stack_index;
|
||||
LLVMTypeRef error_type;
|
||||
Type **generated_types;
|
||||
LLVMValueRef return_out;
|
||||
} GenContext;
|
||||
|
||||
|
||||
@@ -89,6 +88,7 @@ static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context
|
||||
return LLVMCreateBasicBlockInContext(context->context, name);
|
||||
}
|
||||
|
||||
void gencontext_emit_implicit_return(GenContext *context);
|
||||
void gencontext_emit_function_decl(GenContext *context, Decl *decl);
|
||||
void gencontext_emit_extern_decl(GenContext *context, Decl *decl);
|
||||
LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr);
|
||||
|
||||
@@ -35,11 +35,6 @@ static inline LLVMTypeRef gencontext_create_basic_llvm_type(GenContext *context,
|
||||
}
|
||||
}
|
||||
|
||||
static inline void gencontext_init_basic_llvm_type(GenContext *context, Type *type)
|
||||
{
|
||||
vec_add(context->generated_types, type);
|
||||
type->backend_type = gencontext_create_basic_llvm_type(context, type);
|
||||
}
|
||||
void gencontext_begin_module(GenContext *context)
|
||||
{
|
||||
assert(!context->module && "Expected no module");
|
||||
@@ -72,18 +67,6 @@ void gencontext_begin_module(GenContext *context)
|
||||
// Setup all types. Not thread-safe, but at this point in time we can assume a single context.
|
||||
// We need to remove the context from the cache after this.
|
||||
// This would seem to indicate that we should change Type / actual type.
|
||||
gencontext_init_basic_llvm_type(context, type_char);
|
||||
gencontext_init_basic_llvm_type(context, type_byte);
|
||||
gencontext_init_basic_llvm_type(context, type_short);
|
||||
gencontext_init_basic_llvm_type(context, type_ushort);
|
||||
gencontext_init_basic_llvm_type(context, type_int);
|
||||
gencontext_init_basic_llvm_type(context, type_uint);
|
||||
gencontext_init_basic_llvm_type(context, type_long);
|
||||
gencontext_init_basic_llvm_type(context, type_ulong);
|
||||
gencontext_init_basic_llvm_type(context, type_float);
|
||||
gencontext_init_basic_llvm_type(context, type_double);
|
||||
gencontext_init_basic_llvm_type(context, type_void);
|
||||
gencontext_init_basic_llvm_type(context, type_bool);
|
||||
|
||||
context->pointer_alignment = LLVMPointerSizeForAS(target_data_layout(), 0);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
{
|
||||
Decl *decl = ast->declare_stmt;
|
||||
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, BACKEND_TYPE(decl->type), decl->name);
|
||||
decl->var.backend_ref = gencontext_emit_alloca(context, llvm_type(decl->type), decl->name);
|
||||
// TODO NRVO
|
||||
// TODO debug info
|
||||
/*
|
||||
@@ -42,6 +42,16 @@ static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast)
|
||||
*/
|
||||
if (decl->var.init_expr)
|
||||
{
|
||||
Expr *expr = decl->var.init_expr;
|
||||
// Quick path for empty initializer list
|
||||
if (expr->expr_kind == EXPR_INITIALIZER_LIST && vec_size(expr->initializer_expr) == 0)
|
||||
{
|
||||
LLVMBuildMemSet(context->builder, decl->var.backend_ref, LLVMConstInt(llvm_type(type_byte), 0, false),
|
||||
LLVMConstInt(llvm_type(type_ulong), expr->type->decl->strukt.size, false),
|
||||
expr->type->decl->strukt.alignment);
|
||||
return decl->var.backend_ref;
|
||||
}
|
||||
|
||||
LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr);
|
||||
LLVMBuildStore(context->builder, value, decl->var.backend_ref);
|
||||
return decl->var.backend_ref;
|
||||
@@ -100,14 +110,37 @@ static inline void gencontext_emit_return(GenContext *context, Ast *ast)
|
||||
{
|
||||
// Ensure we are on a branch that is non empty.
|
||||
if (!gencontext_check_block_branch_emit(context)) return;
|
||||
|
||||
LLVMValueRef ret_value = ast->return_stmt.expr ? gencontext_emit_expr(context, ast->return_stmt.expr) : NULL;
|
||||
gencontext_emit_defer(context, ast->return_stmt.defer, NULL);
|
||||
if (!ret_value)
|
||||
{
|
||||
LLVMBuildRetVoid(context->builder);
|
||||
gencontext_emit_implicit_return(context);
|
||||
return;
|
||||
}
|
||||
LLVMBuildRet(context->builder, ret_value);
|
||||
if (context->return_out)
|
||||
{
|
||||
LLVMBuildStore(context->builder, ret_value, context->return_out);
|
||||
gencontext_emit_implicit_return(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
LLVMBuildRet(context->builder, ret_value);
|
||||
}
|
||||
context->current_block = NULL;
|
||||
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
|
||||
gencontext_emit_block(context, post_ret_block);
|
||||
}
|
||||
|
||||
static inline void gencontext_emit_throw(GenContext *context, Ast *ast)
|
||||
{
|
||||
// Ensure we are on a branch that is non empty.
|
||||
if (!gencontext_check_block_branch_emit(context)) return;
|
||||
|
||||
gencontext_emit_defer(context, ast->throw_stmt.defers.start, ast->throw_stmt.defers.end);
|
||||
// TODO handle throw if simply a jump
|
||||
LLVMBuildRet(context->builder, LLVMConstInt(llvm_type(type_ulong), 10 + ast->throw_stmt.throw_value->identifier_expr.decl->error_constant.value, false));
|
||||
|
||||
context->current_block = NULL;
|
||||
LLVMBasicBlockRef post_ret_block = gencontext_create_free_block(context, "ret");
|
||||
gencontext_emit_block(context, post_ret_block);
|
||||
@@ -154,10 +187,7 @@ void gencontext_emit_if(GenContext *context, Ast *ast)
|
||||
}
|
||||
|
||||
|
||||
static void gencontext_push_next(GenContext *context, LLVMBasicBlockRef nextBlock)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
static void
|
||||
gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block,
|
||||
LLVMBasicBlockRef next_block)
|
||||
@@ -474,7 +504,7 @@ LLVMValueRef gencontext_get_defer_bool(GenContext *context, Ast *defer)
|
||||
assert(defer->ast_kind == AST_DEFER_STMT && defer->defer_stmt.emit_boolean);
|
||||
if (!defer->defer_stmt.bool_var)
|
||||
{
|
||||
defer->defer_stmt.bool_var = gencontext_emit_alloca(context, BACKEND_TYPE(type_bool), "defer");
|
||||
defer->defer_stmt.bool_var = gencontext_emit_alloca(context, llvm_type(type_bool), "defer");
|
||||
}
|
||||
return defer->defer_stmt.bool_var;
|
||||
}
|
||||
@@ -492,7 +522,7 @@ void gencontext_emit_defer(GenContext *context, Ast *defer_start, Ast *defer_end
|
||||
LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "skip.defer");
|
||||
LLVMBasicBlockRef defer_block = LLVMCreateBasicBlockInContext(context->context, "do.defer");
|
||||
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, BACKEND_TYPE(type_bool), gencontext_get_defer_bool(context, defer), "will.defer");
|
||||
LLVMValueRef value = LLVMBuildLoad2(context->builder, llvm_type(type_bool), gencontext_get_defer_bool(context, defer), "will.defer");
|
||||
|
||||
gencontext_emit_cond_br(context, value, defer_block, exit_block);
|
||||
|
||||
@@ -520,7 +550,7 @@ void gencontext_emit_goto(GenContext *context, Ast *ast)
|
||||
Ast *defer = ast->goto_stmt.label->label_stmt.defer;
|
||||
while (defer != ast->goto_stmt.defer.end)
|
||||
{
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 0, false),
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 0, false),
|
||||
gencontext_get_defer_bool(context, defer));
|
||||
defer = defer->defer_stmt.prev_defer;
|
||||
}
|
||||
@@ -604,7 +634,7 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
case AST_DEFER_STMT:
|
||||
if (ast->defer_stmt.emit_boolean)
|
||||
{
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(BACKEND_TYPE(type_bool), 1, false),
|
||||
LLVMBuildStore(context->builder, LLVMConstInt(llvm_type(type_bool), 1, false),
|
||||
gencontext_get_defer_bool(context, ast));
|
||||
}
|
||||
break;
|
||||
@@ -612,9 +642,11 @@ void gencontext_emit_stmt(GenContext *context, Ast *ast)
|
||||
break;
|
||||
case AST_CATCH_STMT:
|
||||
case AST_TRY_STMT:
|
||||
case AST_THROW_STMT:
|
||||
// Should have been lowered.
|
||||
UNREACHABLE
|
||||
case AST_THROW_STMT:
|
||||
gencontext_emit_throw(context, ast);
|
||||
break;
|
||||
case AST_ASM_STMT:
|
||||
TODO
|
||||
case AST_ATTRIBUTE:
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
#define LLVMCONTEXT(gen_context) (gen_context ? gen_context->context : LLVMGetGlobalContext())
|
||||
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type);
|
||||
|
||||
static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
static inline LLVMTypeRef llvm_type_from_decl(LLVMContextRef context, Decl *decl)
|
||||
{
|
||||
static LLVMTypeRef params[512];
|
||||
static LLVMTypeRef params[MAX_PARAMS];
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_ATTRIBUTE:
|
||||
@@ -28,26 +28,25 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
||||
{
|
||||
VECEACH(decl->func.function_signature.params, i)
|
||||
{
|
||||
params[i] = BACKEND_TYPE(decl->func.function_signature.params[i]->type);
|
||||
params[i] = llvm_get_type(context, decl->func.function_signature.params[i]->type);
|
||||
}
|
||||
unsigned param_size = vec_size(decl->func.function_signature.params);
|
||||
return LLVMFunctionType(BACKEND_TYPE(decl->func.function_signature.rtype->type),
|
||||
return LLVMFunctionType(llvm_get_type(context, decl->func.function_signature.rtype->type),
|
||||
params,
|
||||
param_size,
|
||||
decl->func.function_signature.variadic);
|
||||
|
||||
}
|
||||
case DECL_TYPEDEF:
|
||||
return BACKEND_TYPE(decl->typedef_decl.type);
|
||||
return llvm_get_type(context, decl->typedef_decl.type);
|
||||
case DECL_STRUCT:
|
||||
{
|
||||
LLVMTypeRef *types = NULL;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
VECADD(types, BACKEND_TYPE(decl->strukt.members[i]->type));
|
||||
vec_add(types, llvm_get_type(context, decl->strukt.members[i]->type));
|
||||
}
|
||||
// TODO fix name.
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->external_name);
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name);
|
||||
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
||||
return type;
|
||||
}
|
||||
@@ -57,7 +56,7 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
||||
unsigned long long max_size = 0;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
LLVMTypeRef type = BACKEND_TYPE(decl->strukt.members[i]->type);
|
||||
LLVMTypeRef type = llvm_get_type(context, decl->strukt.members[i]->type);
|
||||
unsigned long long size = LLVMStoreSizeOfType(target_data_layout(), type);
|
||||
if (size > max_size || !max_type)
|
||||
{
|
||||
@@ -65,13 +64,15 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
||||
max_type = type;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(LLVMCONTEXT(context), decl->external_name);
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context, decl->external_name);
|
||||
LLVMStructSetBody(type, &max_type, 1, false);
|
||||
return type;
|
||||
}
|
||||
case DECL_ENUM:
|
||||
return BACKEND_TYPE(decl->type);
|
||||
return llvm_get_type(context, decl->type);
|
||||
case DECL_ERROR:
|
||||
TODO
|
||||
/*
|
||||
if (!context->error_type)
|
||||
{
|
||||
LLVMTypeRef domain_type = LLVMInt64TypeInContext(LLVMCONTEXT(context));
|
||||
@@ -81,117 +82,144 @@ static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *cont
|
||||
LLVMStructSetBody(error_type, types, 2, false);
|
||||
context->error_type = error_type;
|
||||
}
|
||||
return context->error_type;
|
||||
return context->error_type;*/
|
||||
case DECL_THROWS:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
static inline LLVMTypeRef gencontext_create_llvm_type_from_ptr(GenContext *context, Type *type)
|
||||
static inline LLVMTypeRef llvm_type_from_ptr(LLVMContextRef context, Type *type)
|
||||
{
|
||||
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->pointer);
|
||||
vec_add(context->generated_types, type);
|
||||
LLVMTypeRef base_llvm_type = llvm_get_type(context, type->pointer);
|
||||
|
||||
if (type->canonical != type)
|
||||
{
|
||||
return type->backend_type = BACKEND_TYPE(type->canonical);
|
||||
return type->backend_type = llvm_get_type(context, type->canonical);
|
||||
}
|
||||
|
||||
return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0);
|
||||
}
|
||||
|
||||
static inline LLVMTypeRef gencontext_create_llvm_type_from_array(GenContext *context, Type *type)
|
||||
static inline LLVMTypeRef llvm_type_from_array(LLVMContextRef context, Type *type)
|
||||
{
|
||||
LLVMTypeRef base_llvm_type = BACKEND_TYPE(type->array.base);
|
||||
|
||||
vec_add(context->generated_types, type);
|
||||
LLVMTypeRef base_llvm_type = llvm_get_type(context, type->array.base);
|
||||
|
||||
if (type->canonical != type)
|
||||
{
|
||||
return type->backend_type = BACKEND_TYPE(type->canonical);
|
||||
return type->backend_type = llvm_get_type(context, type->canonical);
|
||||
}
|
||||
|
||||
return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0);
|
||||
}
|
||||
|
||||
LLVMTypeRef gencontext_create_llvm_func_type(GenContext *context, Type *type)
|
||||
LLVMTypeRef llvm_func_type(LLVMContextRef context, Type *type)
|
||||
{
|
||||
LLVMTypeRef *params = NULL;
|
||||
FunctionSignature *signature = type->func.signature;
|
||||
// TODO throws
|
||||
if (vec_size(signature->params))
|
||||
bool return_parameter = func_return_value_as_out(signature);
|
||||
bool return_error = func_has_error_return(signature);
|
||||
unsigned parameters = vec_size(signature->params) + return_parameter;
|
||||
if (parameters)
|
||||
{
|
||||
params = malloc_arena(sizeof(LLVMTypeRef) * vec_size(signature->params));
|
||||
params = malloc_arena(sizeof(LLVMTypeRef) * parameters);
|
||||
if (return_parameter)
|
||||
{
|
||||
params[0] = llvm_get_type(context, signature->rtype->type);
|
||||
}
|
||||
VECEACH(signature->params, i)
|
||||
{
|
||||
params[i] = BACKEND_TYPE(signature->params[i]->type->canonical);
|
||||
params[i + return_parameter] = llvm_get_type(context, signature->params[i]->type->canonical);
|
||||
}
|
||||
}
|
||||
return LLVMFunctionType(
|
||||
BACKEND_TYPE(type->func.signature->rtype->type),
|
||||
params, vec_size(signature->params), signature->variadic);
|
||||
LLVMTypeRef ret_type;
|
||||
if (return_error)
|
||||
{
|
||||
ret_type = llvm_get_type(context, type_ulong);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret_type = return_parameter ? llvm_get_type(context, type_void) : llvm_get_type(context, type->func.signature->rtype->type);
|
||||
}
|
||||
return LLVMFunctionType( ret_type, params, parameters, signature->variadic);
|
||||
}
|
||||
|
||||
|
||||
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
||||
LLVMTypeRef llvm_get_type(LLVMContextRef context, Type *type)
|
||||
{
|
||||
if (type->backend_type)
|
||||
if (type->backend_type && LLVMGetTypeContext(type->backend_type) == context)
|
||||
{
|
||||
assert(LLVMGetTypeContext(type->backend_type) == context->context);
|
||||
return type->backend_type;
|
||||
}
|
||||
vec_add(context->generated_types, type);
|
||||
|
||||
DEBUG_LOG("Generating type %s", type->name);
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_IXX:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
case TYPE_META_TYPE:
|
||||
UNREACHABLE;
|
||||
case TYPE_TYPEDEF:
|
||||
return type->backend_type = BACKEND_TYPE(type->canonical);
|
||||
return type->backend_type = llvm_get_type(context, type->canonical);
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERROR:
|
||||
case TYPE_ERROR_UNION:
|
||||
return type->backend_type = gencontext_create_llvm_type_from_decl(context, type->decl);
|
||||
return type->backend_type = llvm_type_from_decl(context, type->decl);
|
||||
case TYPE_FUNC:
|
||||
return type->backend_type = gencontext_create_llvm_func_type(context, type);
|
||||
return type->backend_type = llvm_func_type(context, type);
|
||||
case TYPE_VOID:
|
||||
return type->backend_type = LLVMVoidTypeInContext(context);
|
||||
case TYPE_F64:
|
||||
return type->backend_type = LLVMDoubleTypeInContext(context);
|
||||
case TYPE_F32:
|
||||
return type->backend_type = LLVMFloatTypeInContext(context);
|
||||
case TYPE_U64:
|
||||
case TYPE_POISONED:
|
||||
case TYPE_BOOL:
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
case TYPE_IXX:
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 64U);
|
||||
case TYPE_U32:
|
||||
case TYPE_UXX:
|
||||
case TYPE_FXX:
|
||||
UNREACHABLE;
|
||||
case TYPE_I32:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 32U);
|
||||
case TYPE_U16:
|
||||
case TYPE_I16:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 16U);
|
||||
case TYPE_U8:
|
||||
case TYPE_I8:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 8U);
|
||||
case TYPE_BOOL:
|
||||
return type->backend_type = LLVMIntTypeInContext(context, 1U);
|
||||
case TYPE_POINTER:
|
||||
return type->backend_type = gencontext_create_llvm_type_from_ptr(context, type);
|
||||
return type->backend_type = llvm_type_from_ptr(context, type);
|
||||
case TYPE_STRING:
|
||||
// TODO
|
||||
return type->backend_type = LLVMPointerType(LLVMTYPE(type_char), 0);
|
||||
case TYPE_ARRAY:
|
||||
return type->backend_type = gencontext_create_llvm_type_from_array(context, type);
|
||||
return type->backend_type = llvm_type_from_array(context, type);
|
||||
case TYPE_SUBARRAY:
|
||||
{
|
||||
LLVMTypeRef base_type = BACKEND_TYPE(type->array.base);
|
||||
LLVMTypeRef size_type = BACKEND_TYPE(type_usize);
|
||||
LLVMTypeRef base_type = llvm_get_type(context, type->array.base);
|
||||
LLVMTypeRef size_type = llvm_get_type(context, type_usize);
|
||||
assert(type->array.base->canonical->type_kind == TYPE_POINTER);
|
||||
LLVMTypeRef array_type = LLVMStructCreateNamed(LLVMCONTEXT(context), type->name);
|
||||
LLVMTypeRef array_type = LLVMStructCreateNamed(context, type->name);
|
||||
LLVMTypeRef types[2] = { base_type, size_type };
|
||||
LLVMStructSetBody(array_type, types, 2, false);
|
||||
return type->backend_type = array_type;
|
||||
}
|
||||
case TYPE_VARARRAY:
|
||||
return type->backend_type = LLVMPointerType(BACKEND_TYPE(type->array.base), 0);
|
||||
return type->backend_type = LLVMPointerType(llvm_get_type(context, type->array.base), 0);
|
||||
}
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type)
|
||||
{
|
||||
return llvm_get_type(context->context, type);
|
||||
}
|
||||
|
||||
void llvm_set_struct_size_alignment(Decl *decl)
|
||||
{
|
||||
LLVMTypeRef type = llvm_get_type(LLVMGetGlobalContext(), decl->type);
|
||||
decl->strukt.size = LLVMStoreSizeOfType(target_data_layout(), type);
|
||||
decl->strukt.alignment = LLVMPreferredAlignmentOfType(target_data_layout(), type);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "compiler_internal.h"
|
||||
|
||||
const int MAX_DOCS_ROWS = 1024;
|
||||
|
||||
Token module = { .type = TOKEN_INVALID_TOKEN };
|
||||
static Ast *parse_stmt(Context *context);
|
||||
static Expr *parse_expr(Context *context);
|
||||
@@ -29,8 +27,8 @@ extern ParseRule rules[TOKEN_EOF + 1];
|
||||
|
||||
void context_store_lexer_state(Context *context)
|
||||
{
|
||||
assert(!context->stored.has_stored && "Nested lexer store is forbidden");
|
||||
context->stored.has_stored = true;
|
||||
assert(!context->stored.in_lookahead && "Nested lexer store is forbidden");
|
||||
context->stored.in_lookahead = true;
|
||||
context->stored.current = context->lexer.current;
|
||||
context->stored.start = context->lexer.lexing_start;
|
||||
context->stored.tok = context->tok;
|
||||
@@ -38,13 +36,12 @@ void context_store_lexer_state(Context *context)
|
||||
context->stored.lead_comment = context->lead_comment;
|
||||
context->stored.trailing_comment = context->trailing_comment;
|
||||
context->stored.next_lead_comment = context->next_lead_comment;
|
||||
context->stored.comments = vec_size(context->comments);
|
||||
}
|
||||
|
||||
void context_restore_lexer_state(Context *context)
|
||||
{
|
||||
assert(context->stored.has_stored && "Tried to restore missing stored state.");
|
||||
context->stored.has_stored = false;
|
||||
assert(context->stored.in_lookahead && "Tried to restore missing stored state.");
|
||||
context->stored.in_lookahead = false;
|
||||
context->lexer.current = context->stored.current;
|
||||
context->lexer.lexing_start = context->stored.start;
|
||||
context->tok = context->stored.tok;
|
||||
@@ -53,7 +50,6 @@ void context_restore_lexer_state(Context *context)
|
||||
context->next_lead_comment = context->stored.next_lead_comment;
|
||||
context->trailing_comment = context->stored.trailing_comment;
|
||||
context->prev_tok_end = context->tok.span.end_loc;
|
||||
vec_resize(context->comments, context->stored.comments);
|
||||
}
|
||||
|
||||
inline void advance(Context *context)
|
||||
@@ -70,6 +66,12 @@ inline void advance(Context *context)
|
||||
|
||||
if (context->next_tok.type == TOKEN_INVALID_TOKEN) continue;
|
||||
|
||||
if (context->stored.in_lookahead && (context->next_tok.type == TOKEN_COMMENT
|
||||
|| context->next_tok.type == TOKEN_DOC_COMMENT))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Walk through any regular comments
|
||||
if (context->next_tok.type == TOKEN_COMMENT)
|
||||
{
|
||||
@@ -340,8 +342,6 @@ static Ast* parse_function_block(Context *context)
|
||||
return ast;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static Path *parse_path_prefix(Context *context)
|
||||
{
|
||||
if (context->tok.type != TOKEN_IDENT || context->next_tok.type != TOKEN_SCOPE) return NULL;
|
||||
@@ -402,7 +402,7 @@ static Path *parse_path_prefix(Context *context)
|
||||
* ;
|
||||
*
|
||||
* Assume prev_token is the type.
|
||||
* @return Type (poisoned if fails)
|
||||
* @return TypeInfo (poisoned if fails)
|
||||
*/
|
||||
static inline TypeInfo *parse_base_type(Context *context)
|
||||
{
|
||||
@@ -657,15 +657,6 @@ static Ast *parse_declaration_stmt(Context *context)
|
||||
}
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NEXT_WAS_ERROR,
|
||||
NEXT_WAS_EXPR,
|
||||
NEXT_WAS_LABEL,
|
||||
NEXT_WAS_DECL
|
||||
} ExprCheck;
|
||||
|
||||
|
||||
/**
|
||||
* expr_stmt ::= expression EOS
|
||||
* @return Ast* poisoned if expression fails to parse.
|
||||
@@ -784,6 +775,14 @@ static inline Ast* parse_if_stmt(Context *context)
|
||||
return if_ast;
|
||||
}
|
||||
|
||||
/**
|
||||
* while_stmt
|
||||
* : WHILE '(' control_expression ')' statement
|
||||
* ;
|
||||
*
|
||||
* @param context
|
||||
* @return the while AST
|
||||
*/
|
||||
static inline Ast* parse_while_stmt(Context *context)
|
||||
{
|
||||
Ast *while_ast = AST_NEW_TOKEN(AST_WHILE_STMT, context->tok);
|
||||
@@ -801,12 +800,13 @@ static inline Ast* parse_while_stmt(Context *context)
|
||||
* : DEFER statement
|
||||
* | DEFER catch statement
|
||||
* ;
|
||||
* @return
|
||||
* @return the defer AST
|
||||
*/
|
||||
static inline Ast* parse_defer_stmt(Context *context)
|
||||
{
|
||||
Ast *defer_stmt = AST_NEW_TOKEN(AST_DEFER_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_DEFER);
|
||||
// TODO catch
|
||||
defer_stmt->defer_stmt.body = TRY_AST(parse_stmt(context));
|
||||
return defer_stmt;
|
||||
}
|
||||
@@ -840,7 +840,8 @@ static inline Ast* parse_catch_stmt(Context *context)
|
||||
return catch_stmt;
|
||||
}
|
||||
|
||||
static inline Ast* parse_asm_stmt(Context *context)
|
||||
|
||||
static inline Ast* parse_asm_stmt(Context *context __unused)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
@@ -994,7 +995,7 @@ static inline Ast* parse_ct_switch_stmt(Context *context)
|
||||
stmt->ct_case_stmt.body = TRY_AST_OR(parse_stmt(context), &poisoned_ast);
|
||||
vec_add(switch_statements, stmt);
|
||||
break;
|
||||
case TOKEN_DEFAULT:
|
||||
case TOKEN_CT_DEFAULT:
|
||||
stmt = AST_NEW_TOKEN(AST_CT_CASE_STMT, context->tok);
|
||||
advance(context);
|
||||
CONSUME_OR(TOKEN_COLON, &poisoned_ast);
|
||||
@@ -1131,7 +1132,7 @@ static Ast *parse_throw_stmt(Context *context)
|
||||
{
|
||||
Ast *ast = AST_NEW_TOKEN(AST_THROW_STMT, context->tok);
|
||||
advance_and_verify(context, TOKEN_THROW);
|
||||
ast->throw_stmt = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
ast->throw_stmt.throw_value = TRY_EXPR_OR(parse_expr(context), &poisoned_ast);
|
||||
RETURN_AFTER_EOS(ast);
|
||||
}
|
||||
|
||||
@@ -1174,7 +1175,7 @@ static inline bool is_expr_after_type_ident(Context *context)
|
||||
return context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LPAREN;
|
||||
}
|
||||
|
||||
static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **typePtr)
|
||||
static bool parse_type_or_expr(Context *context, Expr **expr_ptr, TypeInfo **type_ptr)
|
||||
{
|
||||
switch (context->tok.type)
|
||||
{
|
||||
@@ -1203,8 +1204,8 @@ static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **type
|
||||
case TOKEN_C_ULONGLONG:
|
||||
case TOKEN_TYPE_IDENT:
|
||||
if (context->next_tok.type == TOKEN_DOT || context->next_tok.type == TOKEN_LPAREN) break;
|
||||
*typePtr = parse_type_expression(context);
|
||||
return type_info_ok(*typePtr);
|
||||
*type_ptr = parse_type_expression(context);
|
||||
return type_info_ok(*type_ptr);
|
||||
case TOKEN_IDENT:
|
||||
if (context->next_tok.type == TOKEN_SCOPE)
|
||||
{
|
||||
@@ -1217,8 +1218,8 @@ static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **type
|
||||
if (context->tok.type == TOKEN_TYPE_IDENT && !is_expr_after_type_ident(context))
|
||||
{
|
||||
context_restore_lexer_state(context);
|
||||
*typePtr = parse_type_expression(context);
|
||||
return type_info_ok(*typePtr);
|
||||
*type_ptr = parse_type_expression(context);
|
||||
return type_info_ok(*type_ptr);
|
||||
}
|
||||
context_restore_lexer_state(context);
|
||||
}
|
||||
@@ -1234,20 +1235,20 @@ static bool parse_type_or_expr(Context *context, Expr **exprPtr, TypeInfo **type
|
||||
CONSUME_OR(TOKEN_RPAREN, false);
|
||||
if (inner_expr)
|
||||
{
|
||||
*typePtr = type_info_new(TYPE_INFO_EXPRESSION);
|
||||
(**typePtr).unresolved_type_expr = inner_expr;
|
||||
*type_ptr = type_info_new(TYPE_INFO_EXPRESSION);
|
||||
(**type_ptr).unresolved_type_expr = inner_expr;
|
||||
return true;
|
||||
}
|
||||
Expr *type_expr = expr_new(EXPR_TYPE, start);
|
||||
type_expr->type_expr.type = inner_type;
|
||||
*exprPtr = parse_precedence_with_left_side(context, type_expr, PREC_ASSIGNMENT);
|
||||
return expr_ok(*exprPtr);
|
||||
*expr_ptr = parse_precedence_with_left_side(context, type_expr, PREC_ASSIGNMENT);
|
||||
return expr_ok(*expr_ptr);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*exprPtr = parse_expr(context);
|
||||
return expr_ok(*exprPtr);
|
||||
*expr_ptr = parse_expr(context);
|
||||
return expr_ok(*expr_ptr);
|
||||
|
||||
}
|
||||
|
||||
@@ -1361,6 +1362,7 @@ static inline Ast* parse_switch_stmt(Context *context)
|
||||
return switch_ast;
|
||||
}
|
||||
|
||||
|
||||
static Ast *parse_stmt(Context *context)
|
||||
{
|
||||
switch (context->tok.type)
|
||||
@@ -1413,6 +1415,8 @@ static Ast *parse_stmt(Context *context)
|
||||
return parse_label_stmt(context);
|
||||
}
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_AT:
|
||||
return parse_expr_stmt(context);
|
||||
case TOKEN_IDENT:
|
||||
if (context->next_tok.type == TOKEN_SCOPE)
|
||||
{
|
||||
@@ -1487,7 +1491,6 @@ static Ast *parse_stmt(Context *context)
|
||||
case TOKEN_PLUS:
|
||||
case TOKEN_MINUSMINUS:
|
||||
case TOKEN_PLUSPLUS:
|
||||
case TOKEN_AT_IDENT:
|
||||
case TOKEN_HASH_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_STRING:
|
||||
@@ -1500,7 +1503,6 @@ static Ast *parse_stmt(Context *context)
|
||||
case TOKEN_INVALID_TOKEN:
|
||||
advance(context);
|
||||
return &poisoned_ast;
|
||||
case TOKEN_AT:
|
||||
case TOKEN_COLON:
|
||||
case TOKEN_COMMA:
|
||||
case TOKEN_EQ:
|
||||
@@ -1638,7 +1640,6 @@ static inline bool parse_optional_module_params(Context *context, Token **tokens
|
||||
case TOKEN_COMMA:
|
||||
sema_error_range(context->next_tok.span, "Unexpected ','");
|
||||
return false;
|
||||
case TOKEN_AT_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_HASH_IDENT:
|
||||
case TOKEN_TYPE_IDENT:
|
||||
@@ -1830,24 +1831,14 @@ static Expr *parse_precedence(Context *context, Precedence precedence)
|
||||
return parse_precedence_with_left_side(context, left_side, precedence);
|
||||
}
|
||||
|
||||
static inline Expr* parse_non_assign_expr(Context *context)
|
||||
{
|
||||
return parse_precedence(context, PREC_ASSIGNMENT + 1);
|
||||
}
|
||||
|
||||
static inline Expr* parse_expr(Context *context)
|
||||
{
|
||||
|
||||
SourceRange start = context->tok.span;
|
||||
bool found_try = try_consume(context, TOKEN_TRY);
|
||||
Expr *expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr);
|
||||
if (found_try)
|
||||
{
|
||||
Expr *try_expr = expr_new(EXPR_TRY, start);
|
||||
try_expr->try_expr.expr = expr;
|
||||
if (try_consume(context, TOKEN_ELSE))
|
||||
{
|
||||
try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr);
|
||||
}
|
||||
return try_expr;
|
||||
}
|
||||
return expr;
|
||||
return parse_precedence(context, PREC_ASSIGNMENT);
|
||||
}
|
||||
|
||||
static inline Expr *parse_paren_expr(Context *context)
|
||||
@@ -1942,10 +1933,10 @@ static inline Decl *parse_global_declaration(Context *context, Visibility visibi
|
||||
* ;
|
||||
*
|
||||
* attribute
|
||||
* : AT_IDENT
|
||||
* | path AT_IDENT
|
||||
* | AT_IDENT '(' constant_expression ')'
|
||||
* | path AT_IDENT '(' constant_expression ')'
|
||||
* : AT IDENT
|
||||
* | AT path IDENT
|
||||
* | AT IDENT '(' constant_expression ')'
|
||||
* | AT path IDENT '(' constant_expression ')'
|
||||
* ;
|
||||
*
|
||||
* @return true if parsing succeeded, false if recovery is needed
|
||||
@@ -1954,7 +1945,7 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl)
|
||||
{
|
||||
parent_decl->attributes = NULL;
|
||||
|
||||
while (context->tok.type == TOKEN_AT_IDENT || (context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE))
|
||||
while (try_consume(context, TOKEN_AT))
|
||||
{
|
||||
Path *path = parse_path_prefix(context);
|
||||
|
||||
@@ -1963,7 +1954,7 @@ static inline bool parse_attributes(Context *context, Decl *parent_decl)
|
||||
attr->name = context->tok;
|
||||
attr->path = path;
|
||||
|
||||
TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected an attribute", false);
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected an attribute", false);
|
||||
|
||||
if (context->tok.type == TOKEN_LPAREN)
|
||||
{
|
||||
@@ -2259,19 +2250,21 @@ static inline bool parse_opt_throw_declaration(Context *context, Visibility visi
|
||||
}
|
||||
|
||||
if (!try_consume(context, TOKEN_THROWS)) return true;
|
||||
if (context->tok.type != TOKEN_TYPE_IDENT)
|
||||
if (context->tok.type != TOKEN_TYPE_IDENT && context->tok.type != TOKEN_IDENT)
|
||||
{
|
||||
VECADD(signature->throws, &all_error);
|
||||
signature->throw_any = true;
|
||||
return true;
|
||||
}
|
||||
Decl **throws = NULL;
|
||||
while (context->tok.type == TOKEN_TYPE_IDENT)
|
||||
{
|
||||
Decl *error = decl_new(DECL_ERROR, context->tok, visibility);
|
||||
advance(context);
|
||||
VECADD(throws, error);
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
}
|
||||
while (1)
|
||||
{
|
||||
TypeInfo *type_info = parse_base_type(context);
|
||||
if (!type_info_ok(type_info)) return false;
|
||||
Decl *throw = decl_new(DECL_THROWS, context->tok, visibility);
|
||||
throw->throws = type_info;
|
||||
VECADD(throws, throw);
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
}
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_TYPE_IDENT:
|
||||
@@ -2346,8 +2339,8 @@ static AttributeDomains TOKEN_TO_ATTR[TOKEN_EOF + 1] = {
|
||||
|
||||
/**
|
||||
* attribute_declaration
|
||||
* : ATTRIBUTE attribute_domains AT_IDENT ';'
|
||||
* | ATTRIBUTE attribute_domains AT_IDENT '(' parameter_type_list ')' ';'
|
||||
* : ATTRIBUTE attribute_domains IDENT ';'
|
||||
* | ATTRIBUTE attribute_domains IDENT '(' parameter_type_list ')' ';'
|
||||
* ;
|
||||
*
|
||||
* attribute_domains
|
||||
@@ -2387,8 +2380,8 @@ static inline Decl *parse_attribute_declaration(Context *context, Visibility vis
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
last_domain = TOKEN_TO_ATTR[context->tok.type];
|
||||
}
|
||||
TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected an attribute name.", &poisoned_decl);
|
||||
Decl *decl = decl_new(DECL_ATTRIBUTE, context->tok, visibility);
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected an attribute name.", &poisoned_decl);
|
||||
if (last_domain == 0)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(context->tok, "Expected at least one domain for attribute '%s'.", decl->name);
|
||||
@@ -2449,14 +2442,14 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
|
||||
advance_and_verify(context, TOKEN_MACRO);
|
||||
|
||||
TypeInfo *rtype = NULL;
|
||||
if (context->tok.type != TOKEN_AT_IDENT)
|
||||
if (context->tok.type != TOKEN_IDENT)
|
||||
{
|
||||
rtype = TRY_TYPE_OR(parse_type_expression(context), &poisoned_decl);
|
||||
}
|
||||
|
||||
Decl *decl = decl_new(DECL_MACRO, context->tok, visibility);
|
||||
decl->macro_decl.rtype = rtype;
|
||||
TRY_CONSUME_OR(TOKEN_AT_IDENT, "Expected a macro name starting with '@'", &poisoned_decl);
|
||||
TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here", &poisoned_decl);
|
||||
|
||||
CONSUME_OR(TOKEN_LPAREN, &poisoned_decl);
|
||||
Decl **params = NULL;
|
||||
@@ -2467,7 +2460,6 @@ static inline Decl *parse_macro_declaration(Context *context, Visibility visibil
|
||||
switch (context->tok.type)
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_AT_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_HASH_IDENT:
|
||||
break;
|
||||
@@ -3456,7 +3448,6 @@ static Expr *parse_maybe_scope(Context *context, Expr *left)
|
||||
{
|
||||
case TOKEN_IDENT:
|
||||
case TOKEN_CT_IDENT:
|
||||
case TOKEN_AT_IDENT:
|
||||
case TOKEN_CONST_IDENT:
|
||||
return parse_identifier_with_path(context, path);
|
||||
case TOKEN_TYPE_IDENT:
|
||||
@@ -3480,6 +3471,28 @@ static Expr *parse_type_expr(Context *context, Expr *left)
|
||||
return expr;
|
||||
}
|
||||
|
||||
static Expr *parse_try_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *try_expr = EXPR_NEW_TOKEN(EXPR_TRY, context->tok);
|
||||
advance_and_verify(context, TOKEN_TRY);
|
||||
try_expr->try_expr.expr = TRY_EXPR_OR(parse_precedence(context, PREC_TRY + 1), &poisoned_expr);
|
||||
if (try_consume(context, TOKEN_ELSE))
|
||||
{
|
||||
try_expr->try_expr.else_expr = TRY_EXPR_OR(parse_precedence(context, PREC_ASSIGNMENT), &poisoned_expr);
|
||||
}
|
||||
return try_expr;
|
||||
}
|
||||
|
||||
static Expr *parse_macro_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
Expr *macro_expr = EXPR_NEW_TOKEN(EXPR_MACRO_EXPR, context->tok);
|
||||
advance_and_verify(context, TOKEN_AT);
|
||||
macro_expr->macro_expr = TRY_EXPR_OR(parse_precedence(context, PREC_UNARY + 1), &poisoned_expr);
|
||||
return macro_expr;
|
||||
}
|
||||
|
||||
static Expr *parse_cast_expr(Context *context, Expr *left)
|
||||
{
|
||||
assert(!left && "Unexpected left hand side");
|
||||
@@ -3501,6 +3514,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL },
|
||||
[TOKEN_TYPE] = { parse_type_expr, NULL, PREC_NONE },
|
||||
[TOKEN_CAST] = { parse_cast_expr, NULL, PREC_NONE },
|
||||
[TOKEN_TRY] = { parse_try_expr, NULL, PREC_TRY },
|
||||
//[TOKEN_SIZEOF] = { parse_sizeof, NULL, PREC_NONE },
|
||||
[TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL },
|
||||
[TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE },
|
||||
@@ -3532,7 +3546,7 @@ ParseRule rules[TOKEN_EOF + 1] = {
|
||||
[TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE },
|
||||
[TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_CT_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_AT_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_AT] = { parse_macro_expr, NULL, PREC_UNARY },
|
||||
[TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE },
|
||||
[TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE },
|
||||
[TOKEN_FLOAT] = { parse_double, NULL, PREC_NONE },
|
||||
|
||||
@@ -60,6 +60,14 @@ static inline void context_pop_defers_to(Context *context, DeferList *list)
|
||||
context_pop_defers(context);
|
||||
}
|
||||
|
||||
static inline void context_add_exit(Context *context, ExitType exit)
|
||||
{
|
||||
if (context->current_scope->exit < exit)
|
||||
{
|
||||
context->current_scope->exit = exit;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void context_pop_scope(Context *context)
|
||||
{
|
||||
assert(context->current_scope != &context->scopes[0]);
|
||||
@@ -201,7 +209,6 @@ static inline bool sema_analyse_struct_union(Context *context, Decl *decl)
|
||||
}
|
||||
}
|
||||
DEBUG_LOG("Analysis complete.");
|
||||
// Todo, resolve alignment, size etc.
|
||||
return decl_ok(decl);
|
||||
}
|
||||
|
||||
@@ -235,7 +242,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo
|
||||
|
||||
static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function)
|
||||
{
|
||||
char buffer[2048];
|
||||
char buffer[MAX_FUNCTION_SIGNATURE_SIZE + 200];
|
||||
size_t buffer_write_offset = 0;
|
||||
bool all_ok = true;
|
||||
all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok;
|
||||
@@ -244,8 +251,14 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi
|
||||
type_append_signature_name(signature->rtype->type, buffer, &buffer_write_offset);
|
||||
buffer[buffer_write_offset++] = '(';
|
||||
}
|
||||
if (vec_size(signature->params) > MAX_PARAMS)
|
||||
{
|
||||
SEMA_ERROR(signature->params[MAX_PARAMS], "Number of params exceeds %d which is unsupported.", MAX_PARAMS);
|
||||
return false;
|
||||
}
|
||||
STable *names = &context->scratch_table;
|
||||
stable_clear(names);
|
||||
|
||||
VECEACH(signature->params, i)
|
||||
{
|
||||
Decl *param = signature->params[i];
|
||||
@@ -284,20 +297,28 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi
|
||||
buffer[buffer_write_offset++] = '.';
|
||||
}
|
||||
buffer[buffer_write_offset++] = ')';
|
||||
if (signature->throw_any)
|
||||
{
|
||||
assert(!signature->throws);
|
||||
buffer[buffer_write_offset++] = '!';
|
||||
}
|
||||
if (vec_size(signature->throws))
|
||||
{
|
||||
buffer[buffer_write_offset++] = '!';
|
||||
VECEACH(signature->throws, i)
|
||||
{
|
||||
TODO
|
||||
Decl *err_decl = signature->throws[i];
|
||||
if (!sema_analyse_decl(context, err_decl))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (i > 0 && all_ok)
|
||||
{
|
||||
buffer[buffer_write_offset++] = ',';
|
||||
buffer[buffer_write_offset++] = '|';
|
||||
}
|
||||
// type_append_signature_name(signature->tparam->var.type, buffer, &buffer_write_offset);
|
||||
type_append_signature_name(err_decl->type, buffer, &buffer_write_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_ok) return NULL;
|
||||
TokenType type = TOKEN_INVALID_TOKEN;
|
||||
signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type);
|
||||
@@ -587,6 +608,7 @@ static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement)
|
||||
}
|
||||
}
|
||||
vec_add(context->gotos, statement);
|
||||
context_add_exit(context, EXIT_GOTO);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -864,14 +886,47 @@ static bool sema_analyse_switch_stmt(Context *context, Ast *statement)
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool sema_analyse_try_stmt(Context *context __unused, Ast *statement __unused)
|
||||
static bool sema_analyse_try_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
TODO
|
||||
context->try_nesting++;
|
||||
unsigned errors = vec_size(context->errors);
|
||||
if (!sema_analyse_statement(context, statement->try_stmt))
|
||||
{
|
||||
context->try_nesting--;
|
||||
return false;
|
||||
}
|
||||
unsigned new_errors = vec_size(context->errors);
|
||||
if (new_errors == errors)
|
||||
{
|
||||
SEMA_ERROR(statement, "No error to 'try' in the statement that follows, please remove the 'try'.");
|
||||
return false;
|
||||
}
|
||||
for (unsigned i = errors; i < new_errors; i++)
|
||||
{
|
||||
// At least one uncaught error found!
|
||||
if (context->errors[i]) return true;
|
||||
}
|
||||
SEMA_ERROR(statement, "All errors in the following statement was caught, please remove the 'try'.");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool sema_analyse_throw_stmt(Context *context __unused, Ast *statement __unused)
|
||||
static bool sema_analyse_throw_stmt(Context *context, Ast *statement)
|
||||
{
|
||||
TODO
|
||||
Expr *throw_value = statement->throw_stmt.throw_value;
|
||||
if (!sema_analyse_expr(context, NULL, throw_value)) return false;
|
||||
Type *type = throw_value->type->canonical;
|
||||
if (type->type_kind != TYPE_ERROR)
|
||||
{
|
||||
SEMA_ERROR(throw_value, "Only 'error' types can be thrown, this is a '%s'.", type->name);
|
||||
return false;
|
||||
}
|
||||
if (!context->try_nesting && !func_has_error_return(&context->active_function_for_analysis->func.function_signature))
|
||||
{
|
||||
SEMA_ERROR(statement, "This 'throw' is not handled, please add a 'throws %s' clause to the function signature or use try-catch.", type->name);
|
||||
return false;
|
||||
}
|
||||
VECADD(context->errors, type->decl);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -998,6 +1053,12 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func)
|
||||
context->current_scope = &context->scopes[0];
|
||||
// Clean out the current scope.
|
||||
memset(context->current_scope, 0, sizeof(*context->current_scope));
|
||||
|
||||
// Clear try handling
|
||||
vec_resize(context->errors, 0);
|
||||
context->try_nesting = 0;
|
||||
|
||||
|
||||
context->labels = NULL;
|
||||
context->gotos = NULL;
|
||||
context->last_local = &context->locals[0];
|
||||
@@ -1222,10 +1283,42 @@ static inline bool sema_analyse_enum(Context *context, Decl *decl)
|
||||
return success;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_throws(Context *context, Decl *decl)
|
||||
{
|
||||
if (!sema_resolve_type_info(context, decl->throws)) return false;
|
||||
decl->type = decl->throws->type;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_analyse_error(Context *context, Decl *decl)
|
||||
{
|
||||
// TODO assign numbers to constants
|
||||
return true;
|
||||
Decl **constants = decl->error.error_constants;
|
||||
unsigned size = vec_size(constants);
|
||||
if (size > MAX_ERRORS)
|
||||
{
|
||||
SEMA_ERROR(decl, "More than %d errors declared in a single error type.", MAX_ERRORS);
|
||||
return false;
|
||||
}
|
||||
bool success = true;
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
{
|
||||
Decl *constant = constants[i];
|
||||
for (unsigned j = 0; j < i; j++)
|
||||
{
|
||||
if (constant->name == constants[j]->name)
|
||||
{
|
||||
SEMA_ERROR(constant, "Duplicate error names, please remove one of them.");
|
||||
SEMA_PREV(constants[j], "The previous declaration was here.");
|
||||
decl_poison(constant);
|
||||
decl_poison(constants[j]);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
constant->error_constant.value = i;
|
||||
constant->resolve_status = RESOLVE_DONE;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
@@ -1245,10 +1338,12 @@ bool sema_analyse_decl(Context *context, Decl *decl)
|
||||
switch (decl->decl_kind)
|
||||
{
|
||||
case DECL_THROWS:
|
||||
TODO
|
||||
if (!sema_analyse_throws(context, decl)) return decl_poison(decl);
|
||||
break;
|
||||
case DECL_STRUCT:
|
||||
case DECL_UNION:
|
||||
if (!sema_analyse_struct_union(context, decl)) return decl_poison(decl);
|
||||
llvm_set_struct_size_alignment(decl);
|
||||
decl_set_external_name(decl);
|
||||
break;
|
||||
case DECL_FUNC:
|
||||
@@ -1408,17 +1503,18 @@ static bool sema_resolve_type_identifier(Context *context, TypeInfo *type_info)
|
||||
type_info->unresolved.path,
|
||||
&ambiguous_decl);
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
// Already handled
|
||||
if (!decl_ok(decl))
|
||||
{
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
if (!decl)
|
||||
{
|
||||
SEMA_TOKEN_ERROR(type_info->unresolved.name_loc, "Unknown type '%s'.", type_info->unresolved.name_loc.string);
|
||||
return type_info_poison(type_info);
|
||||
}
|
||||
|
||||
if (ambiguous_decl)
|
||||
{
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
#include <llvm-c/Target.h>
|
||||
#include <llvm-c/TargetMachine.h>
|
||||
#include <llvm-c/Core.h>
|
||||
#include <target_info/target_info.h>
|
||||
#include "compiler_internal.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
LLVMTargetRef target;
|
||||
LLVMTargetMachineRef machine;
|
||||
LLVMTargetDataRef data_layout;
|
||||
int alloca_address_space;
|
||||
} Target;
|
||||
static unsigned arch_pointer_bit_width(ArchType arch);
|
||||
static ArchType arch_from_llvm_string(const char *string);
|
||||
static unsigned os_target_c_type_bits(OsType os, ArchType arch, CType type);
|
||||
static OsType os_from_llvm_string(const char *string);
|
||||
static VendorType vendor_from_llvm_string(const char *string);
|
||||
|
||||
static Target target = {};
|
||||
Target build_target = {};
|
||||
|
||||
int target_alloca_addr_space()
|
||||
{
|
||||
return target.alloca_address_space;
|
||||
return build_target.alloca_address_space;
|
||||
}
|
||||
|
||||
|
||||
void target_setup()
|
||||
{
|
||||
assert(!target.target);
|
||||
assert(!build_target.target);
|
||||
|
||||
LLVMInitializeAllTargetInfos();
|
||||
LLVMInitializeAllTargetMCs();
|
||||
@@ -27,20 +28,20 @@ void target_setup()
|
||||
LLVMInitializeAllAsmPrinters();
|
||||
LLVMInitializeAllAsmParsers();
|
||||
|
||||
target.target = NULL;
|
||||
build_target.target = NULL;
|
||||
if (!build_options.target)
|
||||
{
|
||||
build_options.target = LLVMGetDefaultTargetTriple();
|
||||
}
|
||||
char *err = NULL;
|
||||
|
||||
if (LLVMGetTargetFromTriple(build_options.target, &target.target, &err) != 0)
|
||||
if (LLVMGetTargetFromTriple(build_options.target, ((LLVMTargetRef *)&build_target.target), &err) != 0)
|
||||
{
|
||||
error_exit("Could not create target: %s", err);
|
||||
// Usually we would dispose of err, but no need to do it due to exit.
|
||||
}
|
||||
|
||||
target.alloca_address_space = 0;
|
||||
build_target.alloca_address_space = 0;
|
||||
|
||||
DEBUG_LOG("Target set to %s.", build_options.target);
|
||||
// Create a specific target machine
|
||||
@@ -76,29 +77,395 @@ void target_setup()
|
||||
{
|
||||
opt->features = "";
|
||||
}*/
|
||||
if (!(target.machine = LLVMCreateTargetMachine(target.target, build_options.target, build_options.cpu, "", level, reloc_mode,
|
||||
LLVMCodeModelDefault))) {
|
||||
if (!(build_target.machine = LLVMCreateTargetMachine(build_target.target, build_options.target, "", "", level, reloc_mode,
|
||||
LLVMCodeModelDefault))) {
|
||||
error_exit("Failed to create target machine.");
|
||||
}
|
||||
|
||||
// The below is broken for the AMDGPU target.
|
||||
target.alloca_address_space = 0;
|
||||
target.data_layout = LLVMCreateTargetDataLayout(target.machine);
|
||||
build_options.pointer_size = (int)LLVMPointerSize(target.data_layout);
|
||||
DEBUG_LOG("Deduced pointer size to be %d bits", build_options.pointer_size * 8);
|
||||
|
||||
build_target.llvm_data_layout = LLVMCreateTargetDataLayout(build_target.machine);
|
||||
|
||||
char *target_triple = LLVMGetTargetMachineTriple(build_target.machine);
|
||||
|
||||
build_target.arch_name = strdup(strtok(target_triple, "-"));
|
||||
build_target.vendor_name = strdup(strtok(NULL, "-"));
|
||||
build_target.os_name = strdup(strtok(NULL, "0123456789"));
|
||||
|
||||
LLVMDisposeMessage(target_triple);
|
||||
|
||||
build_target.arch = arch_from_llvm_string(build_target.arch_name);
|
||||
build_target.os = os_from_llvm_string(build_target.os_name);
|
||||
build_target.vendor = vendor_from_llvm_string(build_target.vendor_name);
|
||||
|
||||
build_target.width_pointer = arch_pointer_bit_width(build_target.arch);
|
||||
assert(build_target.width_pointer == LLVMPointerSize(build_target.llvm_data_layout) * 8);
|
||||
build_target.alloca_address_space = 0;
|
||||
|
||||
|
||||
LLVMTypeRef byte_type = LLVMIntType(8);
|
||||
LLVMTypeRef short_type = LLVMIntType(16);
|
||||
LLVMTypeRef int_type = LLVMIntType(32);
|
||||
LLVMTypeRef long_type = LLVMIntType(64);
|
||||
LLVMTypeRef float_type = LLVMFloatType();
|
||||
LLVMTypeRef double_type = LLVMDoubleType();
|
||||
LLVMTypeRef quad_type = LLVMFP128Type();
|
||||
build_target.align_byte = LLVMABIAlignmentOfType(build_target.llvm_data_layout, byte_type);
|
||||
build_target.align_short = LLVMABIAlignmentOfType(build_target.llvm_data_layout, short_type);
|
||||
build_target.align_int = LLVMABIAlignmentOfType(build_target.llvm_data_layout, int_type);
|
||||
build_target.align_long = LLVMABIAlignmentOfType(build_target.llvm_data_layout, long_type);
|
||||
build_target.align_f128 = LLVMABIAlignmentOfType(build_target.llvm_data_layout, quad_type);
|
||||
build_target.align_double = LLVMABIAlignmentOfType(build_target.llvm_data_layout, double_type);
|
||||
build_target.align_float = LLVMABIAlignmentOfType(build_target.llvm_data_layout, float_type);
|
||||
build_target.little_endian = LLVMByteOrder(build_target.llvm_data_layout) == LLVMLittleEndian;
|
||||
build_target.width_c_short = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_SHORT);
|
||||
build_target.width_c_int = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_INT);
|
||||
build_target.width_c_long = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_LONG);
|
||||
build_target.width_c_long_long = os_target_c_type_bits(build_target.os, build_target.arch, CTYPE_LONG_LONG);
|
||||
|
||||
builtin_setup(&build_target);
|
||||
|
||||
}
|
||||
|
||||
void target_destroy()
|
||||
{
|
||||
assert(target.machine);
|
||||
LLVMDisposeTargetMachine(target.machine);
|
||||
assert(build_target.machine);
|
||||
LLVMDisposeTargetMachine(build_target.machine);
|
||||
}
|
||||
|
||||
void *target_target()
|
||||
{
|
||||
return build_target.target;
|
||||
}
|
||||
|
||||
void *target_machine()
|
||||
{
|
||||
return target.machine;
|
||||
return build_target.machine;
|
||||
}
|
||||
void *target_data_layout()
|
||||
{
|
||||
return target.data_layout;
|
||||
}
|
||||
return build_target.llvm_data_layout;
|
||||
}
|
||||
|
||||
static ArchType arch_from_llvm_string(const char *string)
|
||||
{
|
||||
#define STRCASE(_str, _arch) if (strcmp(string, _str) == 0) return _arch;
|
||||
STRCASE("i386", ARCH_TYPE_X86)
|
||||
STRCASE("i486", ARCH_TYPE_X86)
|
||||
STRCASE("i586", ARCH_TYPE_X86)
|
||||
STRCASE("i686", ARCH_TYPE_X86)
|
||||
STRCASE("i786", ARCH_TYPE_X86)
|
||||
STRCASE("i886", ARCH_TYPE_X86)
|
||||
STRCASE("i986", ARCH_TYPE_X86)
|
||||
STRCASE("aarch64", ARCH_TYPE_AARCH64)
|
||||
STRCASE("arm64", ARCH_TYPE_AARCH64)
|
||||
STRCASE("aarch64_be", ARCH_TYPE_AARCH64_BE)
|
||||
STRCASE("aarch64_32", ARCH_TYPE_AARCH64_32)
|
||||
STRCASE("arm64_32", ARCH_TYPE_AARCH64_32)
|
||||
STRCASE("arm", ARCH_TYPE_ARM)
|
||||
STRCASE("xscale", ARCH_TYPE_ARM)
|
||||
STRCASE("armeb", ARCH_TYPE_ARMB)
|
||||
STRCASE("xscaleeb", ARCH_TYPE_ARMB)
|
||||
STRCASE("arc", ARCH_TYPE_ARC)
|
||||
STRCASE("avr", ARCH_TYPE_AVR)
|
||||
STRCASE("bpfeb", ARCH_TYPE_BPFEB)
|
||||
STRCASE("bpfel", ARCH_TYPE_BPFEL)
|
||||
STRCASE("hexagon", ARCH_TYPE_HEXAGON)
|
||||
STRCASE("mips", ARCH_TYPE_MIPS)
|
||||
STRCASE("mipseb", ARCH_TYPE_MIPS)
|
||||
STRCASE("mipsallegrex", ARCH_TYPE_MIPS)
|
||||
STRCASE("mipsisa32r6", ARCH_TYPE_MIPS)
|
||||
STRCASE("mipsr6", ARCH_TYPE_MIPS)
|
||||
STRCASE("mipsel", ARCH_TYPE_MIPSEL)
|
||||
STRCASE("mipsallegrexel", ARCH_TYPE_MIPSEL)
|
||||
STRCASE("mipsisa32r6el", ARCH_TYPE_MIPSEL)
|
||||
STRCASE("mipsr6el", ARCH_TYPE_MIPSEL)
|
||||
STRCASE("mips64", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mips64eb", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mipsn32", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mipsisa64r6", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mips64r6", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mipsn32r6", ARCH_TYPE_MIPS64)
|
||||
STRCASE("mips64el", ARCH_TYPE_MIPS64EL)
|
||||
STRCASE("mipsn32el", ARCH_TYPE_MIPS64EL)
|
||||
STRCASE("mipsisa64r6el", ARCH_TYPE_MIPS64EL)
|
||||
STRCASE("mips64r6el", ARCH_TYPE_MIPS64EL)
|
||||
STRCASE("mipsn32r6el", ARCH_TYPE_MIPS64EL)
|
||||
STRCASE("msp430", ARCH_TYPE_MSP430)
|
||||
STRCASE("powerpc64", ARCH_TYPE_PPC64)
|
||||
STRCASE("ppu", ARCH_TYPE_PPC64)
|
||||
STRCASE("ppc64", ARCH_TYPE_PPC64)
|
||||
STRCASE("powerpc64le", ARCH_TYPE_PPC64LE)
|
||||
STRCASE("ppc64le", ARCH_TYPE_PPC64LE)
|
||||
STRCASE("powerpc", ARCH_TYPE_PPC)
|
||||
STRCASE("ppc", ARCH_TYPE_PPC)
|
||||
STRCASE("ppc32", ARCH_TYPE_PPC)
|
||||
STRCASE("r600", ARCH_TYPE_R600)
|
||||
STRCASE("amdgcn", ARCH_TYPE_AMDGCN)
|
||||
STRCASE("riscv32", ARCH_TYPE_RISCV32)
|
||||
STRCASE("riscv64", ARCH_TYPE_RISCV64)
|
||||
STRCASE("sparc", ARCH_TYPE_SPARC)
|
||||
STRCASE("sparcel", ARCH_TYPE_SPARCEL)
|
||||
STRCASE("sparcv9", ARCH_TYPE_SPARCV9)
|
||||
STRCASE("sparc64", ARCH_TYPE_SPARCV9)
|
||||
STRCASE("systemz", ARCH_TYPE_SYSTEMZ)
|
||||
STRCASE("s390x", ARCH_TYPE_SYSTEMZ)
|
||||
STRCASE("tce", ARCH_TYPE_TCE)
|
||||
STRCASE("tcele", ARCH_TYPE_TCELE)
|
||||
STRCASE("thumb", ARCH_TYPE_THUMB)
|
||||
STRCASE("thumbeb", ARCH_TYPE_THUMBEB)
|
||||
STRCASE("x86_64", ARCH_TYPE_X86_64)
|
||||
STRCASE("amd64", ARCH_TYPE_X86_64)
|
||||
STRCASE("x86_64h", ARCH_TYPE_X86_64)
|
||||
STRCASE("xcore", ARCH_TYPE_XCORE)
|
||||
STRCASE("nvptx", ARCH_TYPE_NVPTX)
|
||||
STRCASE("nvptx64", ARCH_TYPE_NVPTX64)
|
||||
STRCASE("le32", ARCH_TYPE_LE32)
|
||||
STRCASE("le64", ARCH_TYPE_LE64)
|
||||
STRCASE("amdil", ARCH_TYPE_AMDIL)
|
||||
STRCASE("amdil64", ARCH_TYPE_AMDIL64)
|
||||
STRCASE("hsail", ARCH_TYPE_HSAIL)
|
||||
STRCASE("hsail64", ARCH_TYPE_HSAIL64)
|
||||
STRCASE("spir", ARCH_TYPE_SPIR)
|
||||
STRCASE("spir64", ARCH_TYPE_SPIR64)
|
||||
STRCASE("kalimba", ARCH_TYPE_KALIMBA)
|
||||
STRCASE("lanai", ARCH_TYPE_LANAI)
|
||||
STRCASE("shave", ARCH_TYPE_SHAVE)
|
||||
STRCASE("wasm32", ARCH_TYPE_WASM32)
|
||||
STRCASE("wasm64", ARCH_TYPE_WASM64)
|
||||
STRCASE("renderscript32", ARCH_TYPE_RSCRIPT32)
|
||||
STRCASE("renderscript64", ARCH_TYPE_RSCRIPT64)
|
||||
return ARCH_TYPE_UNKNOWN;
|
||||
#undef STRCASE
|
||||
// TODO parse arm & bpf names
|
||||
}
|
||||
|
||||
|
||||
static OsType os_from_llvm_string(const char *string)
|
||||
{
|
||||
#define STRCASE(_str, _os) if (strcmp(string, _str) == 0) return _os;
|
||||
STRCASE("ananas", OS_TYPE_ANANAS)
|
||||
STRCASE("cloudabi", OS_TYPE_CLOUD_ABI)
|
||||
STRCASE("darwin", OS_TYPE_DARWIN)
|
||||
STRCASE("dragonfly", OS_TYPE_DRAGON_FLY)
|
||||
STRCASE("freebsd", OS_TYPE_FREE_BSD)
|
||||
STRCASE("fuchsia", OS_TYPE_FUCHSIA)
|
||||
STRCASE("ios", OS_TYPE_IOS)
|
||||
STRCASE("kfreebsd", OS_TYPE_KFREEBSD)
|
||||
STRCASE("linux", OS_TYPE_LINUX)
|
||||
STRCASE("lv2", OS_TYPE_PS3)
|
||||
STRCASE("macosx", OS_TYPE_MACOSX)
|
||||
STRCASE("netbsd", OS_TYPE_NETBSD)
|
||||
STRCASE("openbsd", OS_TYPE_OPENBSD)
|
||||
STRCASE("solaris", OS_TYPE_SOLARIS)
|
||||
STRCASE("windows", OS_TYPE_WIN32)
|
||||
STRCASE("haiku", OS_TYPE_HAIKU)
|
||||
STRCASE("minix", OS_TYPE_MINIX)
|
||||
STRCASE("rtems", OS_TYPE_RTEMS)
|
||||
STRCASE("nacl", OS_TYPE_NACL)
|
||||
STRCASE("cnk", OS_TYPE_CNK)
|
||||
STRCASE("aix", OS_TYPE_AIX)
|
||||
STRCASE("cuda", OS_TYPE_CUDA)
|
||||
STRCASE("nvcl", OS_TYPE_NVOPENCL)
|
||||
STRCASE("amdhsa", OS_TYPE_AMDHSA)
|
||||
STRCASE("ps4", OS_TYPE_PS4)
|
||||
STRCASE("elfiamcu", OS_TYPE_ELFIAMCU)
|
||||
STRCASE("tvos", OS_TYPE_TVOS)
|
||||
STRCASE("watchos", OS_TYPE_WATCHOS)
|
||||
STRCASE("mesa3d", OS_TYPE_MESA3D)
|
||||
STRCASE("contiki", OS_TYPE_CONTIKI)
|
||||
STRCASE("amdpal", OS_TYPE_AMDPAL)
|
||||
STRCASE("hermit", OS_TYPE_HERMITCORE)
|
||||
STRCASE("hurd", OS_TYPE_HURD)
|
||||
STRCASE("wasi", OS_TYPE_WASI)
|
||||
STRCASE("emscripten", OS_TYPE_EMSCRIPTEN)
|
||||
return OS_TYPE_UNKNOWN;
|
||||
#undef STRCASE
|
||||
}
|
||||
|
||||
static VendorType vendor_from_llvm_string(const char *string)
|
||||
{
|
||||
#define STRCASE(_str, _vendor) if (strcmp(string, _str) == 0) return _vendor;
|
||||
STRCASE("apple", VENDOR_APPLE)
|
||||
STRCASE("pc", VENDOR_PC)
|
||||
STRCASE("scei", VENDOR_SCEI)
|
||||
STRCASE("bgp", VENDOR_BGP)
|
||||
STRCASE("bgq", VENDOR_BGQ)
|
||||
STRCASE("fsl", VENDOR_FREESCALE)
|
||||
STRCASE("ibm", VENDOR_IBM)
|
||||
STRCASE("img", VENDOR_IMAGINATION_TECHNOLOGIES)
|
||||
STRCASE("mti", VENDOR_MIPS_TECHNOLOGIES)
|
||||
STRCASE("nvidia", VENDOR_NVIDIA)
|
||||
STRCASE("csr", VENDOR_CSR)
|
||||
STRCASE("myriad", VENDOR_MYRIAD)
|
||||
STRCASE("amd", VENDOR_AMD)
|
||||
STRCASE("mesa", VENDOR_MESA)
|
||||
STRCASE("suse", VENDOR_SUSE)
|
||||
STRCASE("oe", VENDOR_OPEN_EMBEDDED)
|
||||
return VENDOR_UNKNOWN;
|
||||
#undef STRCASE
|
||||
}
|
||||
|
||||
static unsigned arch_pointer_bit_width(ArchType arch)
|
||||
{
|
||||
switch (arch)
|
||||
{
|
||||
case ARCH_TYPE_UNKNOWN:
|
||||
return 0;
|
||||
case ARCH_TYPE_MSP430:
|
||||
case ARCH_TYPE_AVR:
|
||||
return 16;
|
||||
case ARCH_TYPE_ARM:
|
||||
case ARCH_TYPE_ARMB:
|
||||
case ARCH_TYPE_AARCH64_32:
|
||||
case ARCH_TYPE_ARC:
|
||||
case ARCH_TYPE_HEXAGON:
|
||||
case ARCH_TYPE_MIPS:
|
||||
case ARCH_TYPE_MIPSEL:
|
||||
case ARCH_TYPE_PPC:
|
||||
case ARCH_TYPE_R600:
|
||||
case ARCH_TYPE_RISCV32:
|
||||
case ARCH_TYPE_SPARC:
|
||||
case ARCH_TYPE_SPARCEL:
|
||||
case ARCH_TYPE_TCE:
|
||||
case ARCH_TYPE_TCELE:
|
||||
case ARCH_TYPE_THUMB:
|
||||
case ARCH_TYPE_THUMBEB:
|
||||
case ARCH_TYPE_X86:
|
||||
case ARCH_TYPE_XCORE:
|
||||
case ARCH_TYPE_NVPTX:
|
||||
case ARCH_TYPE_LE32:
|
||||
case ARCH_TYPE_AMDIL:
|
||||
case ARCH_TYPE_HSAIL:
|
||||
case ARCH_TYPE_SPIR:
|
||||
case ARCH_TYPE_KALIMBA:
|
||||
case ARCH_TYPE_SHAVE:
|
||||
case ARCH_TYPE_LANAI:
|
||||
case ARCH_TYPE_WASM32:
|
||||
case ARCH_TYPE_RSCRIPT32:
|
||||
return 32;
|
||||
case ARCH_TYPE_SPIR64:
|
||||
case ARCH_TYPE_RSCRIPT64:
|
||||
case ARCH_TYPE_WASM64:
|
||||
case ARCH_TYPE_LE64:
|
||||
case ARCH_TYPE_BPFEL:
|
||||
case ARCH_TYPE_BPFEB:
|
||||
case ARCH_TYPE_AARCH64:
|
||||
case ARCH_TYPE_AARCH64_BE:
|
||||
case ARCH_TYPE_X86_64:
|
||||
case ARCH_TYPE_SYSTEMZ:
|
||||
case ARCH_TYPE_PPC64:
|
||||
case ARCH_TYPE_SPARCV9:
|
||||
case ARCH_TYPE_MIPS64:
|
||||
case ARCH_TYPE_NVPTX64:
|
||||
case ARCH_TYPE_AMDIL64:
|
||||
case ARCH_TYPE_HSAIL64:
|
||||
case ARCH_TYPE_RISCV64:
|
||||
case ARCH_TYPE_AMDGCN:
|
||||
case ARCH_TYPE_MIPS64EL:
|
||||
case ARCH_TYPE_PPC64LE:
|
||||
return 64;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
unsigned os_target_c_type_bits(OsType os, ArchType arch, CType type)
|
||||
{
|
||||
switch (os)
|
||||
{
|
||||
case OS_TYPE_UNKNOWN:
|
||||
if (arch == ARCH_TYPE_MSP430)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CTYPE_SHORT:
|
||||
case CTYPE_INT:
|
||||
return 16;
|
||||
case CTYPE_LONG:
|
||||
return 32;
|
||||
case CTYPE_LONG_LONG:
|
||||
return 64;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
// Use default
|
||||
break;
|
||||
case OS_TYPE_LINUX:
|
||||
case OS_TYPE_DARWIN:
|
||||
case OS_TYPE_MACOSX:
|
||||
case OS_TYPE_FREE_BSD:
|
||||
case OS_TYPE_NETBSD:
|
||||
case OS_TYPE_DRAGON_FLY:
|
||||
case OS_TYPE_OPENBSD:
|
||||
case OS_TYPE_WASI:
|
||||
case OS_TYPE_EMSCRIPTEN:
|
||||
// Use default
|
||||
break;
|
||||
case OS_TYPE_WIN32:
|
||||
switch (type)
|
||||
{
|
||||
case CTYPE_SHORT:
|
||||
return 16;
|
||||
case CTYPE_INT:
|
||||
case CTYPE_LONG:
|
||||
return 32;
|
||||
case CTYPE_LONG_LONG:
|
||||
return 64;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
case OS_TYPE_IOS:
|
||||
switch (type)
|
||||
{
|
||||
case CTYPE_SHORT:
|
||||
return 16;
|
||||
case CTYPE_INT:
|
||||
return 32;
|
||||
case CTYPE_LONG:
|
||||
case CTYPE_LONG_LONG:
|
||||
return 64;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
case OS_TYPE_ANANAS:
|
||||
case OS_TYPE_CLOUD_ABI:
|
||||
case OS_TYPE_FUCHSIA:
|
||||
case OS_TYPE_KFREEBSD:
|
||||
case OS_TYPE_PS3:
|
||||
case OS_TYPE_SOLARIS:
|
||||
case OS_TYPE_HAIKU:
|
||||
case OS_TYPE_MINIX:
|
||||
case OS_TYPE_RTEMS:
|
||||
case OS_TYPE_NACL:
|
||||
case OS_TYPE_CNK:
|
||||
case OS_TYPE_AIX:
|
||||
case OS_TYPE_CUDA:
|
||||
case OS_TYPE_NVOPENCL:
|
||||
case OS_TYPE_AMDHSA:
|
||||
case OS_TYPE_PS4:
|
||||
case OS_TYPE_ELFIAMCU:
|
||||
case OS_TYPE_TVOS:
|
||||
case OS_TYPE_WATCHOS:
|
||||
case OS_TYPE_MESA3D:
|
||||
case OS_TYPE_CONTIKI:
|
||||
case OS_TYPE_AMDPAL:
|
||||
case OS_TYPE_HERMITCORE:
|
||||
case OS_TYPE_HURD:
|
||||
TODO
|
||||
}
|
||||
switch (type)
|
||||
{
|
||||
case CTYPE_SHORT:
|
||||
return 16;
|
||||
case CTYPE_INT:
|
||||
return 32;
|
||||
case CTYPE_LONG:
|
||||
return arch_pointer_bit_width(arch);
|
||||
case CTYPE_LONG_LONG:
|
||||
return 64;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
197
src/compiler/target.h
Normal file
197
src/compiler/target.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
|
||||
// Note: This list is based on Clang's
|
||||
typedef enum
|
||||
{
|
||||
ARCH_TYPE_UNKNOWN,
|
||||
ARCH_TYPE_ARM, // ARM (little endian): arm, armv.*, xscale
|
||||
ARCH_TYPE_ARMB, // ARM (big endian): armeb
|
||||
ARCH_TYPE_AARCH64, // AArch64 (little endian): aarch64
|
||||
ARCH_TYPE_AARCH64_BE, // AArch64 (big endian): aarch64_be
|
||||
ARCH_TYPE_AARCH64_32, // AArch64 (little endian) ILP32: aarch64_32
|
||||
ARCH_TYPE_ARC, // ARC: Synopsys ARC
|
||||
ARCH_TYPE_AVR, // AVR: Atmel AVR microcontroller
|
||||
ARCH_TYPE_BPFEL, // eBPF or extended BPF or 64-bit BPF (little endian)
|
||||
ARCH_TYPE_BPFEB, // eBPF or extended BPF or 64-bit BPF (big endian)
|
||||
ARCH_TYPE_HEXAGON, // Hexagon: hexagon
|
||||
ARCH_TYPE_MIPS, // MIPS: mips, mipsallegrex, mipsr6
|
||||
ARCH_TYPE_MIPSEL, // MIPSEL: mipsel, mipsallegrexe, mipsr6el
|
||||
ARCH_TYPE_MIPS64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6
|
||||
ARCH_TYPE_MIPS64EL, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el
|
||||
ARCH_TYPE_MSP430, // MSP430: msp430
|
||||
ARCH_TYPE_PPC, // PPC: powerpc
|
||||
ARCH_TYPE_PPC64, // PPC64: powerpc64, ppu
|
||||
ARCH_TYPE_PPC64LE, // PPC64LE: powerpc64le
|
||||
ARCH_TYPE_R600, // R600: AMD GPUs HD2XXX - HD6XXX
|
||||
ARCH_TYPE_AMDGCN, // AMDGCN: AMD GCN GPUs
|
||||
ARCH_TYPE_RISCV32, // RISC-V (32-bit): riscv32
|
||||
ARCH_TYPE_RISCV64, // RISC-V (64-bit): riscv64
|
||||
ARCH_TYPE_SPARC, // Sparc: sparc
|
||||
ARCH_TYPE_SPARCV9, // Sparcv9: Sparcv9
|
||||
ARCH_TYPE_SPARCEL, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant
|
||||
ARCH_TYPE_SYSTEMZ, // SystemZ: s390x
|
||||
ARCH_TYPE_TCE, // TCE (http://tce.cs.tut.fi/): tce
|
||||
ARCH_TYPE_TCELE, // TCE little endian (http://tce.cs.tut.fi/): tcele
|
||||
ARCH_TYPE_THUMB, // Thumb (little endian): thumb, thumbv.*
|
||||
ARCH_TYPE_THUMBEB, // Thumb (big endian): thumbeb
|
||||
ARCH_TYPE_X86, // X86: i[3-9]86
|
||||
ARCH_TYPE_X86_64, // X86-64: amd64, x86_64
|
||||
ARCH_TYPE_XCORE, // XCore: xcore
|
||||
ARCH_TYPE_NVPTX, // NVPTX: 32-bit
|
||||
ARCH_TYPE_NVPTX64, // NVPTX: 64-bit
|
||||
ARCH_TYPE_LE32, // le32: generic little-endian 32-bit CPU (PNaCl)
|
||||
ARCH_TYPE_LE64, // le64: generic little-endian 64-bit CPU (PNaCl)
|
||||
ARCH_TYPE_AMDIL, // AMDIL
|
||||
ARCH_TYPE_AMDIL64, // AMDIL with 64-bit pointers
|
||||
ARCH_TYPE_HSAIL, // AMD HSAIL
|
||||
ARCH_TYPE_HSAIL64, // AMD HSAIL with 64-bit pointers
|
||||
ARCH_TYPE_SPIR, // SPIR: standard portable IR for OpenCL 32-bit version
|
||||
ARCH_TYPE_SPIR64, // SPIR: standard portable IR for OpenCL 64-bit version
|
||||
ARCH_TYPE_KALIMBA, // Kalimba: generic kalimba
|
||||
ARCH_TYPE_SHAVE, // SHAVE: Movidius vector VLIW processors
|
||||
ARCH_TYPE_LANAI, // Lanai: Lanai 32-bit
|
||||
ARCH_TYPE_WASM32, // WebAssembly with 32-bit pointers
|
||||
ARCH_TYPE_WASM64, // WebAssembly with 64-bit pointers
|
||||
ARCH_TYPE_RSCRIPT32, // 32-bit RenderScript
|
||||
ARCH_TYPE_RSCRIPT64, // 64-bit RenderScript
|
||||
ARCH_TYPE_LAST = ARCH_TYPE_RSCRIPT64
|
||||
|
||||
} ArchType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CTYPE_SHORT,
|
||||
CTYPE_INT,
|
||||
CTYPE_LONG,
|
||||
CTYPE_LONG_LONG
|
||||
} CType;
|
||||
typedef enum
|
||||
{
|
||||
OS_TYPE_UNKNOWN,
|
||||
OS_TYPE_ANANAS,
|
||||
OS_TYPE_CLOUD_ABI,
|
||||
OS_TYPE_DARWIN,
|
||||
OS_TYPE_DRAGON_FLY,
|
||||
OS_TYPE_FREE_BSD,
|
||||
OS_TYPE_FUCHSIA,
|
||||
OS_TYPE_IOS,
|
||||
OS_TYPE_KFREEBSD,
|
||||
OS_TYPE_LINUX,
|
||||
OS_TYPE_PS3,
|
||||
OS_TYPE_MACOSX,
|
||||
OS_TYPE_NETBSD,
|
||||
OS_TYPE_OPENBSD,
|
||||
OS_TYPE_SOLARIS,
|
||||
OS_TYPE_WIN32,
|
||||
OS_TYPE_HAIKU,
|
||||
OS_TYPE_MINIX,
|
||||
OS_TYPE_RTEMS,
|
||||
OS_TYPE_NACL, // Native Client
|
||||
OS_TYPE_CNK, // BG/P Compute-Node Kernel
|
||||
OS_TYPE_AIX,
|
||||
OS_TYPE_CUDA,
|
||||
OS_TYPE_NVOPENCL,
|
||||
OS_TYPE_AMDHSA,
|
||||
OS_TYPE_PS4,
|
||||
OS_TYPE_ELFIAMCU,
|
||||
OS_TYPE_TVOS,
|
||||
OS_TYPE_WATCHOS,
|
||||
OS_TYPE_MESA3D,
|
||||
OS_TYPE_CONTIKI,
|
||||
OS_TYPE_AMDPAL,
|
||||
OS_TYPE_HERMITCORE,
|
||||
OS_TYPE_HURD,
|
||||
OS_TYPE_WASI,
|
||||
OS_TYPE_EMSCRIPTEN,
|
||||
OS_TYPE_LAST = OS_TYPE_EMSCRIPTEN
|
||||
} OsType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
VENDOR_UNKNOWN,
|
||||
VENDOR_APPLE,
|
||||
VENDOR_PC,
|
||||
VENDOR_SCEI,
|
||||
VENDOR_BGP,
|
||||
VENDOR_BGQ,
|
||||
VENDOR_FREESCALE,
|
||||
VENDOR_IBM,
|
||||
VENDOR_IMAGINATION_TECHNOLOGIES,
|
||||
VENDOR_MIPS_TECHNOLOGIES,
|
||||
VENDOR_NVIDIA,
|
||||
VENDOR_CSR,
|
||||
VENDOR_MYRIAD,
|
||||
VENDOR_AMD,
|
||||
VENDOR_MESA,
|
||||
VENDOR_SUSE,
|
||||
VENDOR_OPEN_EMBEDDED,
|
||||
VENDOR_LAST = VENDOR_OPEN_EMBEDDED
|
||||
} VendorType;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *target;
|
||||
void *machine;
|
||||
void *llvm_data_layout;
|
||||
ArchType arch;
|
||||
const char *arch_name;
|
||||
OsType os;
|
||||
const char *os_name;
|
||||
VendorType vendor;
|
||||
const char *vendor_name;
|
||||
int alloca_address_space;
|
||||
bool little_endian;
|
||||
bool tls_supported;
|
||||
bool asm_supported;
|
||||
bool float_128;
|
||||
bool float_16;
|
||||
unsigned align_min_pointer;
|
||||
unsigned align_min_byte;
|
||||
unsigned align_min_short;
|
||||
unsigned align_min_int;
|
||||
unsigned align_min_long;
|
||||
unsigned align_min_half;
|
||||
unsigned align_min_float;
|
||||
unsigned align_min_double;
|
||||
unsigned align_min_f128;
|
||||
unsigned align_pointer;
|
||||
unsigned align_byte;
|
||||
unsigned align_short;
|
||||
unsigned align_int;
|
||||
unsigned align_long;
|
||||
unsigned align_half;
|
||||
unsigned align_float;
|
||||
unsigned align_double;
|
||||
unsigned align_f128;
|
||||
unsigned align_c_long_double;
|
||||
unsigned align_c_int;
|
||||
unsigned align_c_long;
|
||||
unsigned align_c_long_long;
|
||||
unsigned align_simd_default;
|
||||
unsigned align_max_vector;
|
||||
unsigned align_global_min;
|
||||
unsigned align_new;
|
||||
unsigned align_large_array;
|
||||
unsigned width_pointer;
|
||||
unsigned width_c_short;
|
||||
unsigned width_c_int;
|
||||
unsigned width_c_long;
|
||||
unsigned width_c_long_long;
|
||||
unsigned width_c_long_double;
|
||||
unsigned width_c_wchar;
|
||||
unsigned width_c_wint;
|
||||
unsigned width_large_array_min;
|
||||
unsigned reg_param_max;
|
||||
unsigned sse_reg_param_max;
|
||||
unsigned builtin_ms_valist;
|
||||
unsigned aarch64sve_types;
|
||||
char *platform_name;
|
||||
} Target;
|
||||
|
||||
extern Target build_target;
|
||||
@@ -140,8 +140,6 @@ const char *token_type_to_string(TokenType type)
|
||||
// Identifiers
|
||||
case TOKEN_IDENT:
|
||||
return "IDENT";
|
||||
case TOKEN_AT_IDENT:
|
||||
return "AT_IDENT";
|
||||
case TOKEN_HASH_IDENT:
|
||||
return "HASH_IDENT";
|
||||
case TOKEN_CT_IDENT:
|
||||
|
||||
@@ -21,26 +21,31 @@ Type t_cus, t_cui, t_cul, t_cull;
|
||||
Type t_cs, t_ci, t_cl, t_cll;
|
||||
Type t_voidstar;
|
||||
|
||||
Type *type_signed_int_by_size(int bytesize)
|
||||
#define META_OFFSET 0
|
||||
#define PTR_OFFSET 1
|
||||
#define VAR_ARRAY_OFFSET 2
|
||||
#define ARRAY_OFFSET 3
|
||||
|
||||
Type *type_signed_int_by_bitsize(unsigned bytesize)
|
||||
{
|
||||
switch (bytesize)
|
||||
{
|
||||
case 1: return type_char;
|
||||
case 2: return type_short;
|
||||
case 4: return type_int;
|
||||
case 8: return type_long;
|
||||
default: FATAL_ERROR("Illegal bytesize %d", bytesize);
|
||||
case 8: return type_char;
|
||||
case 16: return type_short;
|
||||
case 32: return type_int;
|
||||
case 64: return type_long;
|
||||
default: FATAL_ERROR("Illegal bitsize %d", bytesize);
|
||||
}
|
||||
}
|
||||
Type *type_unsigned_int_by_size(int bytesize)
|
||||
Type *type_unsigned_int_by_bitsize(unsigned bytesize)
|
||||
{
|
||||
switch (bytesize)
|
||||
{
|
||||
case 1: return type_byte;
|
||||
case 2: return type_ushort;
|
||||
case 4: return type_uint;
|
||||
case 8: return type_ulong;
|
||||
default: FATAL_ERROR("Illegal bytesize %d", bytesize);
|
||||
case 8: return type_byte;
|
||||
case 16: return type_ushort;
|
||||
case 32: return type_uint;
|
||||
case 64: return type_ulong;
|
||||
default: FATAL_ERROR("Illegal bitsize %d", bytesize);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +78,9 @@ const char *type_to_error_string(Type *type)
|
||||
case TYPE_UNION:
|
||||
case TYPE_ERROR:
|
||||
return type->name;
|
||||
case TYPE_META_TYPE:
|
||||
asprintf(&buffer, "type %s", type_to_error_string(type->child));
|
||||
return buffer;
|
||||
case TYPE_POINTER:
|
||||
asprintf(&buffer, "%s*", type_to_error_string(type->pointer));
|
||||
return buffer;
|
||||
@@ -135,9 +143,25 @@ static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_
|
||||
}
|
||||
void type_append_signature_name(Type *type, char *dst, size_t *offset)
|
||||
{
|
||||
assert(*offset < 2000);
|
||||
memcpy(dst + *offset, type->name, strlen(type->name));
|
||||
*offset += strlen(type->name);
|
||||
assert(*offset < MAX_FUNCTION_SIGNATURE_SIZE);
|
||||
const char *name;
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
case TYPE_TYPEDEF:
|
||||
UNREACHABLE;
|
||||
case TYPE_ERROR:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
name = type->decl->external_name;
|
||||
break;
|
||||
default:
|
||||
name = type->name;
|
||||
break;
|
||||
}
|
||||
memcpy(dst + *offset, name, strlen(name));
|
||||
*offset += strlen(name);
|
||||
}
|
||||
|
||||
|
||||
@@ -149,6 +173,8 @@ size_t type_size(Type *canonical)
|
||||
{
|
||||
case TYPE_POISONED:
|
||||
UNREACHABLE;
|
||||
case TYPE_META_TYPE:
|
||||
return 0;
|
||||
case TYPE_ENUM:
|
||||
case TYPE_TYPEDEF:
|
||||
case TYPE_STRUCT:
|
||||
@@ -188,27 +214,29 @@ size_t type_size(Type *canonical)
|
||||
TODO
|
||||
}
|
||||
|
||||
static inline void create_ptr_cache(Type *canonical_type)
|
||||
static inline void create_type_cache(Type *canonical_type)
|
||||
{
|
||||
assert(canonical_type->canonical == canonical_type);
|
||||
canonical_type->ptr_cache = VECADD(canonical_type->ptr_cache, NULL);
|
||||
canonical_type->ptr_cache = VECADD(canonical_type->ptr_cache, NULL);
|
||||
for (int i = 0; i < ARRAY_OFFSET; i++)
|
||||
{
|
||||
vec_add(canonical_type->type_cache, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static Type *type_generate_ptr(Type *ptr_type, bool canonical)
|
||||
{
|
||||
if (canonical) ptr_type = ptr_type->canonical;
|
||||
if (!ptr_type->ptr_cache)
|
||||
if (!ptr_type->type_cache)
|
||||
{
|
||||
create_ptr_cache(ptr_type);
|
||||
create_type_cache(ptr_type);
|
||||
}
|
||||
|
||||
Type *ptr = ptr_type->ptr_cache[0];
|
||||
Type *ptr = ptr_type->type_cache[PTR_OFFSET];
|
||||
if (ptr == NULL)
|
||||
{
|
||||
ptr = type_new(TYPE_POINTER, strformat("%s*", ptr_type->name));
|
||||
ptr->pointer = ptr_type;
|
||||
ptr_type->ptr_cache[0] = ptr;
|
||||
ptr_type->type_cache[PTR_OFFSET] = ptr;
|
||||
if (ptr_type == ptr_type->canonical)
|
||||
{
|
||||
ptr->canonical = ptr;
|
||||
@@ -221,24 +249,56 @@ static Type *type_generate_ptr(Type *ptr_type, bool canonical)
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static Type *type_generate_meta(Type *type, bool canonical)
|
||||
{
|
||||
if (canonical) type = type->canonical;
|
||||
if (!type->type_cache)
|
||||
{
|
||||
create_type_cache(type);
|
||||
}
|
||||
|
||||
Type *meta = type->type_cache[META_OFFSET];
|
||||
if (meta == NULL)
|
||||
{
|
||||
meta = type_new(TYPE_META_TYPE, strformat("type %s", type->name));
|
||||
meta->child = type;
|
||||
type->type_cache[META_OFFSET] = meta;
|
||||
if (type == type->canonical)
|
||||
{
|
||||
meta->canonical = meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
meta->canonical = type_generate_meta(type->canonical, true);
|
||||
}
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
|
||||
Type *type_get_ptr(Type *ptr_type)
|
||||
{
|
||||
return type_generate_ptr(ptr_type, false);
|
||||
}
|
||||
|
||||
Type *type_get_meta(Type *meta_type)
|
||||
{
|
||||
return type_generate_meta(meta_type, false);
|
||||
}
|
||||
|
||||
|
||||
Type *type_create_array(Type *arr_type, uint64_t len, bool canonical)
|
||||
{
|
||||
if (canonical) arr_type = arr_type->canonical;
|
||||
if (!arr_type->ptr_cache)
|
||||
if (!arr_type->type_cache)
|
||||
{
|
||||
create_ptr_cache(arr_type);
|
||||
create_type_cache(arr_type);
|
||||
}
|
||||
|
||||
// Dynamic array
|
||||
if (len == 0)
|
||||
{
|
||||
Type *array = arr_type->ptr_cache[1];
|
||||
Type *array = arr_type->type_cache[VAR_ARRAY_OFFSET];
|
||||
if (array == NULL)
|
||||
{
|
||||
array = type_new(TYPE_VARARRAY, strformat("%s[]", arr_type->name));
|
||||
@@ -252,15 +312,15 @@ Type *type_create_array(Type *arr_type, uint64_t len, bool canonical)
|
||||
{
|
||||
array->canonical = type_create_array(arr_type, len, true);
|
||||
}
|
||||
arr_type->ptr_cache[1] = array;
|
||||
arr_type->type_cache[VAR_ARRAY_OFFSET] = array;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
int entries = (int)vec_size(arr_type->ptr_cache);
|
||||
for (int i = 1; i < entries; i++)
|
||||
int entries = (int)vec_size(arr_type->type_cache);
|
||||
for (int i = ARRAY_OFFSET; i < entries; i++)
|
||||
{
|
||||
Type *ptr = arr_type->ptr_cache[i];
|
||||
Type *ptr = arr_type->type_cache[i];
|
||||
if (ptr->array.len == arr_type->array.len)
|
||||
{
|
||||
return ptr;
|
||||
@@ -277,7 +337,7 @@ Type *type_create_array(Type *arr_type, uint64_t len, bool canonical)
|
||||
{
|
||||
array->canonical = type_create_array(arr_type, len, true);
|
||||
}
|
||||
VECADD(arr_type->ptr_cache, array);
|
||||
VECADD(arr_type->type_cache, array);
|
||||
return array;
|
||||
}
|
||||
|
||||
@@ -286,14 +346,17 @@ Type *type_get_array(Type *arr_type, uint64_t len)
|
||||
return type_create_array(arr_type, len, false);
|
||||
}
|
||||
|
||||
static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bytesize, unsigned bitsize)
|
||||
static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bitsize,
|
||||
unsigned align, unsigned pref_align)
|
||||
{
|
||||
*location = (Type) {
|
||||
.type_kind = kind,
|
||||
.builtin.bytesize = bytesize,
|
||||
.builtin.bytesize = (bitsize + 7) / 8,
|
||||
.builtin.bitsize = bitsize,
|
||||
.builtin.min_alignment = align,
|
||||
.builtin.pref_alignment = pref_align,
|
||||
.name = name,
|
||||
.canonical = location
|
||||
.canonical = location,
|
||||
};
|
||||
location->name = name;
|
||||
location->canonical = location;
|
||||
@@ -311,56 +374,58 @@ static void type_create_alias(const char *name, Type *location, Type **ptr, Type
|
||||
}
|
||||
|
||||
|
||||
void builtin_setup()
|
||||
void builtin_setup(Target *target)
|
||||
{
|
||||
type_create("void", &t_u0, &type_void, TYPE_VOID, 1, 8);
|
||||
type_create("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8);
|
||||
create_ptr_cache(type_void);
|
||||
type_void->ptr_cache[0] = &t_voidstar;
|
||||
type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0);
|
||||
t_voidstar.pointer = type_void;
|
||||
|
||||
/*TODO
|
||||
* decl_string = (Decl) { .decl_kind = DECL_BUILTIN, .name.string = "string" };
|
||||
create_type(&decl_string, &type_string);
|
||||
type_string.type_kind = TYPE_STRING;
|
||||
*/
|
||||
#define DEF_TYPE(_name, _shortname, _type, _bits) \
|
||||
type_create(#_name, &_shortname, &type_ ## _name, _type, (_bits + 7) / 8, _bits);
|
||||
#define DEF_TYPE(_name, _shortname, _type, _bits, _align) \
|
||||
type_create(#_name, &_shortname, &type_ ## _name, _type, _bits, target->align_min_ ## _align, target->align_ ## _align)
|
||||
|
||||
DEF_TYPE(compint, t_ixx, TYPE_IXX, 64);
|
||||
DEF_TYPE(compuint, t_uxx, TYPE_UXX, 64);
|
||||
DEF_TYPE(compfloat, t_fxx, TYPE_FXX, 64);
|
||||
DEF_TYPE(bool, t_u1, TYPE_BOOL, 1);
|
||||
DEF_TYPE(bool, t_u1, TYPE_BOOL, 1, byte);
|
||||
DEF_TYPE(float, t_f32, TYPE_F32, 32, float);
|
||||
DEF_TYPE(double, t_f64, TYPE_F64, 64, double);
|
||||
|
||||
DEF_TYPE(float, t_f32, TYPE_F32, 32);
|
||||
DEF_TYPE(double, t_f64, TYPE_F64, 64);
|
||||
DEF_TYPE(char, t_i8, TYPE_I8, 8, byte);
|
||||
DEF_TYPE(short, t_i16, TYPE_I16, 16, short);
|
||||
DEF_TYPE(int, t_i32, TYPE_I32, 32, int);
|
||||
DEF_TYPE(long, t_i64, TYPE_I64, 64, long);
|
||||
|
||||
DEF_TYPE(char, t_i8, TYPE_I8, 8);
|
||||
DEF_TYPE(short, t_i16, TYPE_I16, 16);
|
||||
DEF_TYPE(int, t_i32, TYPE_I32, 32);
|
||||
DEF_TYPE(long, t_i64, TYPE_I64, 64);
|
||||
DEF_TYPE(byte, t_u8, TYPE_U8, 8, byte);
|
||||
DEF_TYPE(ushort, t_u16, TYPE_U16, 16, short);
|
||||
DEF_TYPE(uint, t_u32, TYPE_U32, 32, int);
|
||||
DEF_TYPE(ulong, t_u64, TYPE_U64, 64, long);
|
||||
|
||||
DEF_TYPE(byte, t_u8, TYPE_U8, 8);
|
||||
DEF_TYPE(ushort, t_u16, TYPE_U16, 16);
|
||||
DEF_TYPE(uint, t_u32, TYPE_U32, 32);
|
||||
DEF_TYPE(ulong, t_u64, TYPE_U64, 64);
|
||||
|
||||
type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_size(build_options.pointer_size));
|
||||
type_create_alias("isize", &t_isz, &type_isize, type_signed_int_by_size(build_options.pointer_size));
|
||||
|
||||
type_create_alias("c_ushort", &t_cus, &type_c_ushort, type_unsigned_int_by_size(build_options.cshort_size));
|
||||
type_create_alias("c_uint", &t_cui, &type_c_uint, type_unsigned_int_by_size(build_options.cint_size));
|
||||
type_create_alias("c_ulong", &t_cul, &type_c_ulong, type_unsigned_int_by_size(build_options.clong_size));
|
||||
type_create_alias("c_ulonglong", &t_cull, &type_c_ulonglong, type_unsigned_int_by_size(build_options.clonglong_size));
|
||||
|
||||
type_create_alias("c_short", &t_cs, &type_c_short, type_signed_int_by_size(build_options.cshort_size));
|
||||
type_create_alias("c_int", &t_ci, &type_c_int, type_signed_int_by_size(build_options.cint_size));
|
||||
type_create_alias("c_long", &t_cl, &type_c_long, type_signed_int_by_size(build_options.clong_size));
|
||||
type_create_alias("c_longlong", &t_cll, &type_c_longlong, type_signed_int_by_size(build_options.clonglong_size));
|
||||
DEF_TYPE(void, t_u0, TYPE_VOID, 8, byte);
|
||||
DEF_TYPE(string, t_str, TYPE_STRING, target->width_pointer, pointer);
|
||||
|
||||
#undef DEF_TYPE
|
||||
|
||||
type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, target->width_pointer, target->align_min_pointer, target->align_pointer);
|
||||
create_type_cache(type_void);
|
||||
type_void->type_cache[0] = &t_voidstar;
|
||||
t_voidstar.pointer = type_void;
|
||||
type_create("compint", &t_ixx, &type_compint, TYPE_IXX, 64, 0, 0);
|
||||
type_create("compuint", &t_uxx, &type_compuint, TYPE_UXX, 64, 0, 0);
|
||||
type_create("compfloat", &t_fxx, &type_compfloat, TYPE_FXX, 64, 0, 0);
|
||||
|
||||
type_create_alias("usize", &t_usz, &type_usize, type_unsigned_int_by_bitsize(target->width_pointer));
|
||||
type_create_alias("isize", &t_isz, &type_isize, type_signed_int_by_bitsize(target->width_pointer));
|
||||
|
||||
type_create_alias("c_ushort", &t_cus, &type_c_ushort, type_unsigned_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_uint", &t_cui, &type_c_uint, type_unsigned_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("c_ulong", &t_cul, &type_c_ulong, type_unsigned_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_ulonglong", &t_cull, &type_c_ulonglong, type_unsigned_int_by_bitsize(target->width_c_long_long));
|
||||
|
||||
type_create_alias("c_short", &t_cs, &type_c_short, type_signed_int_by_bitsize(target->width_c_short));
|
||||
type_create_alias("c_int", &t_ci, &type_c_int, type_signed_int_by_bitsize(target->width_c_int));
|
||||
type_create_alias("c_long", &t_cl, &type_c_long, type_signed_int_by_bitsize(target->width_c_long));
|
||||
type_create_alias("c_longlong", &t_cll, &type_c_longlong, type_signed_int_by_bitsize(target->width_c_long_long));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -395,6 +460,7 @@ bool type_may_have_method_functions(Type *type)
|
||||
case TYPE_UNION:
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERROR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -441,9 +507,9 @@ Type *type_find_max_num_type(Type *num_type, Type *other_num)
|
||||
assert (other_num->type_kind != TYPE_FXX);
|
||||
return other_num;
|
||||
case LS:
|
||||
return type_signed_int_by_size(num_type->builtin.bytesize);
|
||||
return type_signed_int_by_bitsize(num_type->builtin.bytesize * 8);
|
||||
case RS:
|
||||
return type_signed_int_by_size(other_num->builtin.bytesize);
|
||||
return type_signed_int_by_bitsize(other_num->builtin.bytesize * 8);
|
||||
case FL:
|
||||
return type_double;
|
||||
default:
|
||||
@@ -579,6 +645,7 @@ Type *type_find_max_type(Type *type, Type *other)
|
||||
case TYPE_FUNC:
|
||||
case TYPE_UNION:
|
||||
case TYPE_ERROR_UNION:
|
||||
case TYPE_META_TYPE:
|
||||
return NULL;
|
||||
case TYPE_STRUCT:
|
||||
TODO
|
||||
|
||||
72
src/target_info/target_info.c
Normal file
72
src/target_info/target_info.c
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "target_info_internal.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
AVXABI_LEVEL_NONE,
|
||||
AVXABI_AVX,
|
||||
AVXABI_512
|
||||
} X86AVXABILevel;
|
||||
|
||||
static unsigned x86avxabi_vector_size(X86AVXABILevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case AVXABI_512:
|
||||
return 512;
|
||||
case AVXABI_AVX:
|
||||
return 256;
|
||||
case AVXABI_LEVEL_NONE:
|
||||
return 128;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
TargetInfo target_info_new()
|
||||
{
|
||||
// From the glibc documentation, on GNU systems, malloc guarantees 16-byte
|
||||
// alignment on 64-bit systems and 8-byte alignment on 32-bit systems. See
|
||||
// https://www.gnu.org/software/libc/manual/html_node/Malloc-Examples.html.
|
||||
// This alignment guarantee also applies to Windows and Android.
|
||||
/*
|
||||
if (T.isGNUEnvironment() || T.isWindowsMSVCEnvironment() || T.isAndroid())
|
||||
NewAlign = Triple.isArch64Bit() ? 128 : Triple.isArch32Bit() ? 64 : 0;
|
||||
else
|
||||
NewAlign = 0; // Infer from basic type alignment.
|
||||
*/
|
||||
TargetInfo target_info = {
|
||||
.little_endian = true,
|
||||
.asm_supported = false,
|
||||
.float_128 = false,
|
||||
.float_16 = false,
|
||||
.align_byte = 8,
|
||||
.align_c_int = 32,
|
||||
.align_c_long = 32,
|
||||
.align_c_long_long = 64,
|
||||
.align_c_long_double = 64,
|
||||
.align_f128 = 128,
|
||||
.align_large_array = 0,
|
||||
.align_global_min = 0,
|
||||
.align_new = 0,
|
||||
.align_max_vector = 0,
|
||||
.align_simd_default = 0,
|
||||
.width_pointer = 32,
|
||||
.width_c_int = 32,
|
||||
.width_c_long = 32,
|
||||
.width_c_long_long = 64,
|
||||
.width_c_long_double = 64,
|
||||
.width_large_array_min = 0,
|
||||
.width_c_wchar = 32,
|
||||
.width_c_wint = 32,
|
||||
.reg_param_max = 0,
|
||||
.sse_reg_param_max = 0,
|
||||
.builtin_ms_valist = false,
|
||||
.aarch64sve_types = false,
|
||||
.platform_name = "unknown"
|
||||
};
|
||||
return target_info;
|
||||
}
|
||||
|
||||
121
src/target_info/target_info.h
Normal file
121
src/target_info/target_info.h
Normal file
@@ -0,0 +1,121 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "utils/common.h"
|
||||
|
||||
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SUB_ARCH_NONE,
|
||||
SUB_ARCH_ARM_V8_5A,
|
||||
SUB_ARCH_ARM_V8_4A,
|
||||
SUB_ARCH_ARM_V8_3A,
|
||||
SUB_ARCH_ARM_V8_2A,
|
||||
SUB_ARCH_ARM_V8_1A,
|
||||
SUB_ARCH_ARM_V8,
|
||||
SUB_ARCH_ARM_V8R,
|
||||
SUB_ARCH_ARM_V8M_BASELINE,
|
||||
SUB_ARCH_ARM_V8M_MAINLINE,
|
||||
SUB_ARCH_ARM_V8_1M_MAINLINE,
|
||||
SUB_ARCH_ARM_V7,
|
||||
SUB_ARCH_ARM_V7EM,
|
||||
SUB_ARCH_ARM_V7M,
|
||||
SUB_ARCH_ARM_V7S,
|
||||
SUB_ARCH_ARM_V7K,
|
||||
SUB_ARCH_ARM_V7VE,
|
||||
SUB_ARCH_ARM_V6,
|
||||
SUB_ARCH_ARM_V6M,
|
||||
SUB_ARCH_ARM_V6K,
|
||||
SUB_ARCH_ARM_V6T2,
|
||||
SUB_ARCH_ARM_V5,
|
||||
SUB_ARCH_ARM_V5TE,
|
||||
SUB_ARCH_ARM_V4,
|
||||
SUB_ARCH_KALIMBA_V3,
|
||||
SUB_ARCH_KALIMBA_V4,
|
||||
SUB_ARCH_KALIMBA_V5,
|
||||
SUB_ARCH_MIPS_V6,
|
||||
} SubArchType;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ENV_TYPE_UNKNOWN,
|
||||
ENV_TYPE_GNU,
|
||||
ENV_TYPE_GNUABIN32,
|
||||
ENV_TYPE_SNUABI64,
|
||||
ENV_TYPE_GNUEABI,
|
||||
ENV_TYPE_GNUEABIHF,
|
||||
ENV_TYPE_GNUX32,
|
||||
ENV_TYPE_CODE16,
|
||||
ENV_TYPE_EABI,
|
||||
ENV_TYPE_EABIHF,
|
||||
ENV_TYPE_ELFV1,
|
||||
ENV_TYPE_ELFV2,
|
||||
ENV_TYPE_ANDROID,
|
||||
ENV_TYPE_MUSL,
|
||||
ENV_TYPE_MUSLEABI,
|
||||
ENV_TYPE_MUSLEABIHF,
|
||||
ENV_TYPE_MSVC,
|
||||
ENV_TYPE_ITANIUM,
|
||||
ENV_TYPE_CYGNUS,
|
||||
ENV_TYPE_CORECLR,
|
||||
ENV_TYPE_SIMULATOR,
|
||||
ENV_TYPE_MACABI,
|
||||
ENV_TYPE_LAST = ENV_TYPE_MACABI
|
||||
} EnvironmentType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
OBJ_FORMAT_COFF,
|
||||
OBJ_FORMAT_ELF,
|
||||
OBJ_FORMAT_MACHO,
|
||||
OBJ_FORMAT_WASM,
|
||||
OBJ_FORMAT_XCOFF
|
||||
} ObjectFormatType;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool little_endian;
|
||||
bool tls_supported;
|
||||
bool asm_supported;
|
||||
bool float_128;
|
||||
bool float_16;
|
||||
unsigned align_pointer;
|
||||
unsigned align_byte;
|
||||
unsigned align_short;
|
||||
unsigned align_int;
|
||||
unsigned align_long;
|
||||
unsigned align_half;
|
||||
unsigned align_float;
|
||||
unsigned align_double;
|
||||
unsigned align_f128;
|
||||
unsigned align_c_long_double;
|
||||
unsigned align_c_int;
|
||||
unsigned align_c_long;
|
||||
unsigned align_c_long_long;
|
||||
unsigned align_simd_default;
|
||||
unsigned align_max_vector;
|
||||
unsigned align_global_min;
|
||||
unsigned align_new;
|
||||
unsigned align_large_array;
|
||||
unsigned width_size;
|
||||
unsigned width_pointer;
|
||||
unsigned width_c_int;
|
||||
unsigned width_c_long;
|
||||
unsigned width_c_long_long;
|
||||
unsigned width_c_long_double;
|
||||
unsigned width_c_wchar;
|
||||
unsigned width_c_wint;
|
||||
unsigned width_large_array_min;
|
||||
unsigned reg_param_max;
|
||||
unsigned sse_reg_param_max;
|
||||
unsigned builtin_ms_valist;
|
||||
unsigned aarch64sve_types;
|
||||
char *platform_name;
|
||||
} TargetInfo;
|
||||
7
src/target_info/target_info_internal.h
Normal file
7
src/target_info/target_info_internal.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
// Copyright (c) 2020 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by a LGPLv3.0
|
||||
// a copy of which can be found in the LICENSE file.
|
||||
|
||||
#include "target_info.h"
|
||||
Reference in New Issue
Block a user