so hopefully not too much breaks). New syntax features:
1. Call-by-name function arguments. You can specify a hash literal in
place of ordered function arguments, and it will become the local
variable namespace for the called function, making functions with many
arguments more readable. Ex:
view_manager.lookat(heading:180, pitch:20, roll:0, x:X0, y:Y0, z:Z0,
time:now, fov:55);
Declared arguments are checked and defaulted as would be expected:
it's an error if you fail to pass a value for an undefaulted argument,
missing default arguments get assigned, and any rest parameter
(e.g. "func(a,b=2,rest...){}") will be assigned with an empty vector.
2. Vector slicing. Vectors (lists) can now be created from others
using an ordered list of indexes and ranges. For example:
var v1 = ["a","b","c","d","e"]
var v2 = v1[3,2]; # == ["d","c"];
var v3 = v1[1:3]; # i.e. range from 1 to 3: ["b","c","d"];
var v4 = v1[1:]; # no value means "to the end": ["b","c","d","e"]
var i = 2;
var v5 = v1[i]; # runtime expressions are fine: ["c"]
var v6 = v1[-2,-1]; # negative indexes are relative to end: ["d","e"]
The range values can be computed at runtime (e.g. i=1; v5=v1[i:]).
Negative indices work the same way the do with the vector functions
(-1 is the last element, -2 is 2nd to last, etc...).
3. Multi-assignment expressions. You can assign more than one
variable (or lvalue) at a time by putting them in a parenthesized
list:
(var a, var b) = (1, 2);
var (a, b) = (1, 2); # Shorthand for (var a, var b)
(var a, v[0], obj.field) = (1,2,3) # Any assignable lvalue works
var color = [1, 1, 0.5];
var (r, g, b) = color; # works with runtime vectors too
127 lines
3.3 KiB
C
127 lines
3.3 KiB
C
#ifndef _CODE_H
|
|
#define _CODE_H
|
|
|
|
#include <setjmp.h>
|
|
#include "nasal.h"
|
|
#include "data.h"
|
|
|
|
#define MAX_STACK_DEPTH 512
|
|
#define MAX_RECURSION 128
|
|
#define MAX_MARK_DEPTH 128
|
|
|
|
// Number of objects (per pool per thread) asked for using naGC_get().
|
|
// The idea is that contexts can "cache" allocations to prevent thread
|
|
// contention on the global pools. But in practice this interacts
|
|
// very badly with small subcontext calls, which grab huge numbers of
|
|
// cached objects and don't use them, causing far more collections
|
|
// than necessary. Just leave it at 1 pending a rework of the
|
|
// collector synchronization.
|
|
#define OBJ_CACHE_SZ 1
|
|
|
|
enum {
|
|
OP_NOT, OP_MUL, OP_PLUS, OP_MINUS, OP_DIV, OP_NEG, OP_CAT, OP_LT, OP_LTE,
|
|
OP_GT, OP_GTE, OP_EQ, OP_NEQ, OP_EACH, OP_JMP, OP_JMPLOOP, OP_JIFNOTPOP,
|
|
OP_JIFEND, OP_FCALL, OP_MCALL, OP_RETURN, OP_PUSHCONST, OP_PUSHONE,
|
|
OP_PUSHZERO, OP_PUSHNIL, OP_POP, OP_DUP, OP_XCHG, OP_INSERT, OP_EXTRACT,
|
|
OP_MEMBER, OP_SETMEMBER, OP_LOCAL, OP_SETLOCAL, OP_NEWVEC, OP_VAPPEND,
|
|
OP_NEWHASH, OP_HAPPEND, OP_MARK, OP_UNMARK, OP_BREAK, OP_SETSYM, OP_DUP2,
|
|
OP_INDEX, OP_BREAK2, OP_PUSHEND, OP_JIFTRUE, OP_JIFNOT, OP_FCALLH,
|
|
OP_MCALLH, OP_XCHG2, OP_UNPACK, OP_SLICE, OP_SLICE2
|
|
};
|
|
|
|
struct Frame {
|
|
naRef func; // naFunc object
|
|
naRef locals; // local per-call namespace
|
|
int ip; // instruction pointer into code
|
|
int bp; // opStack pointer to start of frame
|
|
};
|
|
|
|
struct Globals {
|
|
// Garbage collecting allocators:
|
|
struct naPool pools[NUM_NASAL_TYPES];
|
|
int allocCount;
|
|
|
|
// Dead blocks waiting to be freed when it is safe
|
|
void** deadBlocks;
|
|
int deadsz;
|
|
int ndead;
|
|
|
|
// Threading stuff
|
|
int nThreads;
|
|
int waitCount;
|
|
int needGC;
|
|
int bottleneck;
|
|
void* sem;
|
|
void* lock;
|
|
|
|
// Constants
|
|
naRef meRef;
|
|
naRef argRef;
|
|
naRef parentsRef;
|
|
|
|
// A hash of symbol names
|
|
naRef symbols;
|
|
|
|
naRef save;
|
|
|
|
struct Context* freeContexts;
|
|
struct Context* allContexts;
|
|
};
|
|
|
|
struct Context {
|
|
// Stack(s)
|
|
struct Frame fStack[MAX_RECURSION];
|
|
int fTop;
|
|
naRef opStack[MAX_STACK_DEPTH];
|
|
int opFrame; // like Frame::bp, but for C functions
|
|
int opTop;
|
|
int markStack[MAX_MARK_DEPTH];
|
|
int markTop;
|
|
|
|
// Free object lists, cached from the global GC
|
|
struct naObj** free[NUM_NASAL_TYPES];
|
|
int nfree[NUM_NASAL_TYPES];
|
|
|
|
// GC-findable reference point for objects that may live on the
|
|
// processor ("real") stack during execution. naNew() places them
|
|
// here, and clears the array each instruction
|
|
struct naObj** temps;
|
|
int ntemps;
|
|
int tempsz;
|
|
|
|
// Error handling
|
|
jmp_buf jumpHandle;
|
|
char error[128];
|
|
naRef dieArg;
|
|
|
|
// Sub-call lists
|
|
struct Context* callParent;
|
|
struct Context* callChild;
|
|
|
|
// Linked list pointers in globals
|
|
struct Context* nextFree;
|
|
struct Context* nextAll;
|
|
|
|
void* userData;
|
|
};
|
|
|
|
#define globals nasal_globals
|
|
extern struct Globals* globals;
|
|
|
|
// Threading low-level functions
|
|
void* naNewLock();
|
|
void naFreeLock(void* lock);
|
|
void naLock(void* lock);
|
|
void naUnlock(void* lock);
|
|
void* naNewSem();
|
|
void naFreeSem(void* sem);
|
|
void naSemDown(void* sem);
|
|
void naSemUp(void* sem, int count);
|
|
|
|
void naCheckBottleneck();
|
|
|
|
#define LOCK() naLock(globals->lock)
|
|
#define UNLOCK() naUnlock(globals->lock)
|
|
|
|
#endif // _CODE_H
|