# minproto.leg -- minimal prototype langauge for semantic experiments
|
|
#
|
|
# last edited: 2024-07-10 11:28:02 by piumarta on zora-1034.local
|
|
|
|
%{
|
|
;
|
|
//#define YY_DEBUG 1
|
|
|
|
#ifndef GC
|
|
# define GC 1 // do not fill memory with unreachable junk
|
|
#endif
|
|
|
|
#ifndef TAGS
|
|
# define TAGS 1 // Integer and Float are immediate values encoded in their object "pointer"
|
|
#endif
|
|
|
|
#ifndef TYPECODES // <ast>.eval() dispatches using switch(), instead of invoking a method
|
|
# define TYPECODES 0 // (approx. 210% performance increase, because no dynamic dispatch in eval())
|
|
#endif
|
|
|
|
#ifndef PRIMCLOSURE // Lambda and Closure are primitive types, not subtypes of Object
|
|
# define PRIMCLOSURE 0 // (approx. 6% performance decrease, because every Object_get() is slower)
|
|
#endif
|
|
|
|
#ifndef DELOPT // delegate is a member of Object structure, not a normal property
|
|
# define DELOPT 0 // (approx. 60% performance increase, because no associative lookup of __delegate__)
|
|
#endif
|
|
|
|
#ifndef BINOPT // store pointer to implemention function in Binop nodes
|
|
# define BINOPT 0 // (approx. 1% performance decrease due to lookup + indirect call)
|
|
#endif
|
|
|
|
#ifndef NONLOCAL // support non-local control flow (return, break, continue)
|
|
# define NONLOCAL 1 // (approx. 5% [loop] to 55% [call] performance decrease, because of jmp_buf initialisations)
|
|
#endif
|
|
|
|
#ifndef EXCEPTIONS // report errors by raising an exception
|
|
# define EXCEPTIONS 1
|
|
#endif
|
|
|
|
#ifndef FOLDCONST // fold constant expressions during parsing
|
|
# define FOLDCONST 1
|
|
#endif
|
|
|
|
#ifndef PROFILE // include profiling support
|
|
# define PROFILE 0
|
|
#endif
|
|
|
|
#ifndef PEGVM // include parsing expression grammar VM
|
|
# define PEGVM 1
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <assert.h>
|
|
#include <stdarg.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/resource.h> // getrusage()
|
|
|
|
#if GC
|
|
# include <gc.h>
|
|
# define xcalloc(N,S) (GC_malloc((N)*(S)))
|
|
# define xmalloc(N) (GC_malloc(N))
|
|
# define xmallocAtomic(N) (GC_malloc_atomic(N))
|
|
# define xrealloc(P, N) (GC_realloc(P, N))
|
|
# define xfree(P) (GC_free(P))
|
|
#else
|
|
# define GC_INIT()
|
|
# define xcalloc(N,S) (calloc(N, S))
|
|
# define xmalloc(N) (calloc(1, N))
|
|
# define xmallocAtomic(N) (calloc(1, N))
|
|
# define xrealloc(P, N) (realloc(P, N))
|
|
# define xfree(P) (free(P))
|
|
#endif
|
|
|
|
#define indexableSize(A) (sizeof(A) / sizeof(*(A)))
|
|
|
|
void warning(char *fmt, ...);
|
|
void fatal(char *fmt, ...);
|
|
|
|
int opt_O = 0;
|
|
int opt_d = 0;
|
|
int opt_p = 0;
|
|
int opt_v = 0;
|
|
|
|
union object;
|
|
typedef union object *oop;
|
|
|
|
#if PRIMCLOSURE
|
|
#define doTypes(_) \
|
|
_(Undefined) _(Integer) _(Float) _(String) _(Symbol) _(Primitive) \
|
|
_(Lambda) _(Closure)
|
|
#else
|
|
#define doTypes(_) \
|
|
_(Undefined) _(Integer) _(Float) _(String) _(Symbol) _(Primitive)
|
|
#endif
|
|
|
|
#define makeType(X) X,
|
|
enum type { doTypes(makeType) makeType(Object) };
|
|
#undef makeType
|
|
|
|
#define makeType(X) #X,
|
|
char *typeNames[] = { doTypes(makeType) makeType(Object) };
|
|
#undef makeType
|
|
|
|
typedef oop (*prim_t)(oop func, oop self, oop args, oop env);
|
|
|
|
oop codeOn(oop buf, oop obj, int indent);
|
|
oop storeOn(oop buf, oop obj, int indent);
|
|
oop printOn(oop buf, oop obj, int indent);
|
|
|
|
#if TAGS
|
|
# define TAGBITS 2
|
|
# define TAGMASK 3
|
|
# define TAGINT Integer // 1
|
|
# define TAGFLT Float // 2
|
|
#endif
|
|
|
|
#if PRIMCLOSURE
|
|
#define doProtos(_) _(Object) _(RefLocal) _(GetLocal) _(SetLocal) _(RefGlobal) _(GetGlobal) _(SetGlobal) _(RefVar) _(GetVar) _(SetVar) _(RefProp) _(GetProp) _(SetProp) _(RefArray) _(GetArray) _(SetArray) _(GetSlice) _(Call) _(Invoke) _(Super) _(Continue) _(Break) _(Return) _(TryCatch) _(TryEnsure)_(Raise)_(Binop) _(Unyop) _(If) _(While) _(Block) _(For) _(ForIn) _(ForFromTo) _(Literal)
|
|
#else
|
|
#define doProtos(_) _(Object) _(RefLocal) _(GetLocal) _(SetLocal) _(RefGlobal) _(GetGlobal) _(SetGlobal) _(RefVar) _(GetVar) _(SetVar) _(RefProp) _(GetProp) _(SetProp) _(RefArray) _(GetArray) _(SetArray) _(GetSlice) _(Call) _(Invoke) _(Super) _(Continue) _(Break) _(Return) _(TryCatch) _(TryEnsure) _(Raise) _(Binop) _(Unyop) _(If) _(While) _(Block) _(For) _(ForIn) _(ForFromTo) _(Literal) _(Lambda) _(Closure)
|
|
#endif
|
|
|
|
#define declareProto(NAME) oop p##NAME = 0;
|
|
doProtos(declareProto);
|
|
#undef declareProto
|
|
|
|
#define declareTypecode(NAME) t##NAME,
|
|
enum typecode {
|
|
UNDEFINED_TYPECODE,
|
|
doProtos(declareTypecode)
|
|
};
|
|
#undef declareTypecode
|
|
|
|
#define makeProto(NAME) oop p##NAME = 0;
|
|
doTypes(makeProto);
|
|
#undef makeProto
|
|
|
|
#define doProperties(_) _(name) _(eval) _(delegate) _(codeon) _(function) _(message) _(kind) _(owner) _(profile)
|
|
|
|
#define declareProp(NAME) oop prop_##NAME = 0;
|
|
doProperties(declareProp);
|
|
#undef declareProp
|
|
|
|
#define doSymbols(_) _(t) _(name) _(expr) _(function) _(arguments) _(object) _(index) _(key) _(value) _(self) _(method) _(parameters) _(body) _(lambda) _(environment) _(operation) _(full) _(condition) _(consequent) _(alternate) _(expression) _(identifier) _(initialise) _(update) _(first) _(last) _(fixed) _(keyvals) _(__namespaces__) _(O) _(d) _(p) _(v) _(statement) _(handler) _(kind) _(message) _(operand1) _(operand2) _(profile) _(parent) _(count) _(stamp) _(time) _(start) _(stop) _($$) _(yytext) _(yyleng)
|
|
|
|
#define declareSym(NAME) oop sym_##NAME = 0;
|
|
doSymbols(declareSym);
|
|
#undef declareSym
|
|
|
|
#if NONLOCAL
|
|
|
|
#include <setjmp.h>
|
|
|
|
enum {
|
|
NLR_INIT = 0, // initialisation, no non-local flow
|
|
NLR_CONTINUE, // non-local jump back to the start of active loop
|
|
NLR_BREAK, // non-local jump out of the active loop
|
|
NLR_RETURN, // non-local return from the active function
|
|
NLR_RAISE, // exception
|
|
};
|
|
|
|
struct NLR {
|
|
int ntrace;
|
|
jmp_buf env;
|
|
};
|
|
|
|
struct NLR *nlrs = 0;
|
|
|
|
int nnlrs = 0;
|
|
int maxnlrs = 0;
|
|
oop valnlr = 0;
|
|
|
|
#define nlrPush() ({ \
|
|
if (++nnlrs >= maxnlrs) nlrs = realloc(nlrs, sizeof(struct NLR) * (maxnlrs += 32)); \
|
|
nlrs[nnlrs - 1].ntrace = _get(trace, Object,isize); \
|
|
setjmp(nlrs[nnlrs - 1].env); \
|
|
})
|
|
|
|
#define nlrReturn(VAL, TYPE) { \
|
|
valnlr = VAL; \
|
|
longjmp(nlrs[nnlrs-1].env, TYPE); \
|
|
}
|
|
|
|
#define nlrPop() (_set(trace, Object,isize, nlrs[--nnlrs].ntrace), valnlr)
|
|
|
|
#endif
|
|
|
|
struct property { oop key, val; };
|
|
|
|
struct Integer { enum type type; long _value; };
|
|
struct Float { enum type type; double _value; };
|
|
struct String { enum type type; int length; char *value; };
|
|
#if TYPECODES
|
|
struct Symbol { enum type type; char *name; oop value; enum typecode typecode; };
|
|
#else // !TYPECODES
|
|
struct Symbol { enum type type; char *name; oop value; };
|
|
#endif
|
|
#if PROFILE
|
|
struct Primitive { enum type type; oop name; prim_t function; void *cookie; oop profile; int index; };
|
|
#else
|
|
struct Primitive { enum type type; oop name; prim_t function; void *cookie; int index; };
|
|
#endif
|
|
#if PRIMCLOSURE
|
|
struct Lambda { enum type type; oop parameters, body, parent, name; };
|
|
struct Closure { enum type type; oop fixed, function, environment; };
|
|
#endif
|
|
struct Object { enum type type; int isize, icap, psize;
|
|
# if DELOPT
|
|
oop delegate;
|
|
# endif
|
|
oop *indexed; struct property *properties; };
|
|
|
|
union object
|
|
{
|
|
enum type type;
|
|
struct Integer Integer;
|
|
struct Float Float;
|
|
struct String String;
|
|
struct Symbol Symbol;
|
|
struct Primitive Primitive;
|
|
#if PRIMCLOSURE
|
|
struct Lambda Lambda;
|
|
struct Closure Closure;
|
|
#endif
|
|
struct Object Object;
|
|
};
|
|
|
|
union object _nil = { Undefined };
|
|
|
|
#define nil (&_nil)
|
|
|
|
oop namespaces = nil;
|
|
|
|
#define UNDEFINED 0
|
|
|
|
|
|
void typeError(char *who, char *msg, oop value);
|
|
void typeError2(char *who, char *msg, oop lhs, oop rhs);
|
|
void rangeError(char *who, char *msg, oop object, int index);
|
|
void valueError(char *who, char *msg, oop value);
|
|
void keyError(char *who, char *msg, oop object, oop key);
|
|
void undefinedError(oop name);
|
|
void syntaxError(char *message);
|
|
void unknownError(char *message);
|
|
void keyboardInterrupt(void);
|
|
|
|
|
|
enum type getType(oop obj)
|
|
{
|
|
# if TAGS
|
|
if ((intptr_t)obj & TAGMASK) return ((intptr_t)obj & TAGMASK);
|
|
# endif
|
|
return obj->type;
|
|
}
|
|
|
|
char *getTypeName(oop obj)
|
|
{
|
|
int type = getType(obj); assert(0 <= type && type <= indexableSize(typeNames));
|
|
return typeNames[type];
|
|
}
|
|
|
|
int is(enum type type, oop obj) { return type == getType(obj); }
|
|
|
|
oop _checkType(oop obj, enum type type, char *file, int line)
|
|
{
|
|
if (getType(obj) != type) fatal("%s:%d: expected type %d, got %d\n", file, line, type, getType(obj));
|
|
return obj;
|
|
}
|
|
|
|
#define get(OBJ, TYPE,FIELD) (_checkType(OBJ, TYPE, __FILE__, __LINE__)->TYPE.FIELD)
|
|
#define set(OBJ, TYPE,FIELD, VAL) (_checkType(OBJ, TYPE, __FILE__, __LINE__)->TYPE.FIELD = VAL)
|
|
|
|
#ifdef NDEBUG
|
|
# define _get(OBJ, TYPE,FIELD) ((OBJ)->TYPE.FIELD)
|
|
# define _set(OBJ, TYPE,FIELD, VAL) ((OBJ)->TYPE.FIELD = VAL)
|
|
#else
|
|
# define _get(OBJ, TYPE,FIELD) get(OBJ, TYPE,FIELD)
|
|
# define _set(OBJ, TYPE,FIELD, VAL ) set(OBJ, TYPE,FIELD, VAL)
|
|
#endif
|
|
|
|
#define make(TYPE) make_(sizeof(struct TYPE), TYPE)
|
|
|
|
oop make_(size_t size, enum type type)
|
|
{
|
|
oop obj = xmalloc(size);
|
|
obj->type = type;
|
|
return obj;
|
|
}
|
|
|
|
oop newInteger(long value)
|
|
{
|
|
# if TAGS
|
|
return (oop)((intptr_t)value << TAGBITS | TAGINT);
|
|
# else
|
|
oop obj = make(Integer);
|
|
_set(obj, Integer,_value, value);
|
|
return obj;
|
|
# endif
|
|
}
|
|
|
|
#define isInteger(obj) is(Integer, obj)
|
|
|
|
long _integerValue(oop obj)
|
|
{
|
|
# if TAGS
|
|
return (intptr_t)obj >> TAGBITS;
|
|
# else
|
|
return _get(obj, Integer,_value);
|
|
# endif
|
|
}
|
|
|
|
long integerValue(oop obj, char *op)
|
|
{
|
|
if (!isInteger(obj)) typeError(op, "non-integer operand", obj);
|
|
return _integerValue(obj);
|
|
}
|
|
|
|
oop newFloat(double value)
|
|
{
|
|
# if TAGS
|
|
union { intptr_t ptr; double dbl; } bits = { .dbl = value };
|
|
return (oop)((bits.ptr & ~TAGMASK) | TAGFLT);
|
|
# else
|
|
oop obj = make(Float);
|
|
_set(obj, Float,_value, value);
|
|
return obj;
|
|
# endif
|
|
}
|
|
|
|
#define isFloat(obj) is(Float, obj)
|
|
|
|
double _floatValue(oop obj)
|
|
{
|
|
# if TAGS
|
|
union { intptr_t ptr; double dbl; } bits = { .ptr = (intptr_t)obj };
|
|
return bits.dbl;
|
|
# else
|
|
return _get(obj, Float,_value);
|
|
# endif
|
|
}
|
|
|
|
double floatValue(oop obj, char *op)
|
|
{
|
|
switch (getType(obj)) {
|
|
case Integer: return (double)_integerValue(obj);
|
|
case Float: return (double)_floatValue(obj);
|
|
default: typeError(op, "non-numeric operand", obj);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
oop newStringLen(char *value, int length)
|
|
{
|
|
oop obj = make(String);
|
|
char *str = xmallocAtomic(length+1);
|
|
if (value) memcpy(str, value, length);
|
|
else if (length) memset(str, 0, length);
|
|
str[length] = 0;
|
|
_set(obj, String,length, length);
|
|
_set(obj, String,value, str);
|
|
return obj;
|
|
}
|
|
|
|
oop newString(char *value)
|
|
{
|
|
return newStringLen(value, strlen(value));
|
|
}
|
|
|
|
#define isString(obj) is(String, obj)
|
|
|
|
int String_length(oop str) { return get(str, String,length); }
|
|
oop String_reset (oop str) { set(str, String,length, 0); return str; }
|
|
|
|
void String_clear(oop str)
|
|
{
|
|
set(str, String,length, 0);
|
|
set(str, String,value, 0);
|
|
}
|
|
|
|
oop String_append(oop str, int c)
|
|
{
|
|
int length = get(str, String,length);
|
|
char *value = get(str, String,value);
|
|
char *copy = xmalloc(length + 1);
|
|
memcpy(copy, value, length);
|
|
set(str, String,value, copy);
|
|
set(str, String,length, length+1);
|
|
copy[length] = c;
|
|
return str;
|
|
}
|
|
|
|
oop String_appendAllLen(oop str, char *s, int len)
|
|
{
|
|
if (len < 1) return str;
|
|
int length = get(str, String,length);
|
|
char *value = get(str, String,value);
|
|
char *copy = xmalloc(length + len);
|
|
memcpy(copy, value, length);
|
|
memcpy(copy + length, s, len);
|
|
set(str, String,value, copy);
|
|
set(str, String,length, length+len);
|
|
return str;
|
|
}
|
|
|
|
oop String_appendAll(oop str, char *s)
|
|
{
|
|
return String_appendAllLen(str, s, strlen(s));
|
|
}
|
|
|
|
oop String_appendString(oop str, oop val)
|
|
{
|
|
return String_appendAllLen(str, _get(val, String,value), _get(val, String,length));
|
|
}
|
|
|
|
oop String_format(oop str, char *fmt, ...)
|
|
{
|
|
size_t len = 0, cap = 16;
|
|
int length = get(str, String,length);
|
|
char *value = get(str, String,value);
|
|
for (;;) {
|
|
char *orig = value;
|
|
value = xmalloc(length + cap);
|
|
memcpy(value, orig, length);
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
len = vsnprintf(value + length, cap, fmt, ap);
|
|
va_end(ap);
|
|
if (len < cap) break;
|
|
cap += len;
|
|
}
|
|
set(str, String,value, value);
|
|
set(str, String,length, length+len);
|
|
return str;
|
|
}
|
|
|
|
char *String_content(oop str)
|
|
{
|
|
String_append(str, 0);
|
|
_get(str, String,length) -= 1;
|
|
return _get(str, String,value);
|
|
}
|
|
|
|
oop String_concat(oop a, oop b)
|
|
{
|
|
oop result = newStringLen(_get(a, String,value), _get(a, String,length));
|
|
String_appendAllLen(result, _get(b, String,value), _get(b, String,length));
|
|
return result;
|
|
}
|
|
|
|
oop String_repeat(oop s, int n)
|
|
{ assert(is(String, s));
|
|
char *chars = _get(s, String,value);
|
|
int length = _get(s, String,length);
|
|
oop result = newStringLen(0, 0);
|
|
while (n-- > 0) String_appendAllLen(result, chars, length);
|
|
return result;
|
|
}
|
|
|
|
int digitValue(int digit, int base)
|
|
{
|
|
if ('0' <= digit && digit <= '9') digit -= '0';
|
|
else if ('a' <= digit && digit <= 'z') digit -= 'a' - 10;
|
|
else if ('A' <= digit && digit <= 'Z') digit -= 'A' - 10;
|
|
else return -1;
|
|
return (digit < base) ? digit : -1;
|
|
}
|
|
|
|
int readCharValue(char **stringp, int base, int limit)
|
|
{
|
|
char *string = *stringp;
|
|
int value = 0, d = 0;
|
|
while (limit-- && *string && (d = digitValue(*string, base)) >= 0) {
|
|
++string;
|
|
value = value * base + d;
|
|
}
|
|
*stringp = string;
|
|
return value;
|
|
}
|
|
|
|
oop newStringUnescaped(char *string)
|
|
{
|
|
oop buf = newStringLen(0, 0);
|
|
while (*string) {
|
|
int c = *string++;
|
|
if ('\\' == c && *string) {
|
|
c = *string++; assert(c != 0);
|
|
switch (c) {
|
|
case '\"': c = '\"'; break;
|
|
case '\'': c = '\''; break;
|
|
case '\\': c = '\\'; break;
|
|
case 'a' : c = '\a'; break;
|
|
case 'b' : c = '\b'; break;
|
|
case 'f' : c = '\f'; break;
|
|
case 'n' : c = '\n'; break;
|
|
case 'r' : c = '\r'; break;
|
|
case 't' : c = '\t'; break;
|
|
case 'v' : c = '\v'; break;
|
|
case 'X' :
|
|
case 'x' : c = readCharValue(&string, 16, -1); break;
|
|
case '0'...'7': --string; c = readCharValue(&string, 8, 3); break;
|
|
default : warning("illegal character escape sequence: \\%c", c); break;
|
|
}
|
|
}
|
|
String_append(buf, c);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
oop String_escaped(oop obj)
|
|
{ assert(is(String, obj));
|
|
oop buf = newStringLen(0, 0);
|
|
char *str = _get(obj, String,value);
|
|
int len = _get(obj, String,length);
|
|
while (len--) {
|
|
int c = *str++;
|
|
if (c == '"') String_appendAll(buf, "\\\"");
|
|
else if (c == '\\') String_appendAll(buf, "\\\\");
|
|
else if (c >= ' ' && c <= '~') String_append(buf, c);
|
|
else {
|
|
switch (c) {
|
|
case '\a': c = 'a'; break;
|
|
case '\b': c = 'b'; break;
|
|
case '\f': c = 'f'; break;
|
|
case '\n': c = 'n'; break;
|
|
case '\r': c = 'r'; break;
|
|
case '\t': c = 't'; break;
|
|
case '\v': c = 'v'; break;
|
|
default:
|
|
String_format(buf, "\\%03o", c);
|
|
continue;
|
|
}
|
|
String_format(buf, "\\%c", c);
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
char *codeString(oop obj, int indent);
|
|
char *printString(oop obj, int indent);
|
|
|
|
oop String_push(oop obj, oop val) // val is String OR Integer
|
|
{
|
|
if (isInteger(val)) String_append(obj, _integerValue(val));
|
|
else if (is(String, val)) String_appendAllLen(obj, _get(val, String,value), _get(val, String,length));
|
|
else if (is(Symbol, val)) String_appendAllLen(obj, _get(val, Symbol,name), strlen(_get(val, Symbol,name)));
|
|
else typeError("String.push", "value is not integer, string, or symbol", val);
|
|
return val;
|
|
}
|
|
|
|
void getSliceRange(oop obj, oop ostart, oop ostop, int len, int *pstart, int *pstop)
|
|
{
|
|
int start = (nil == ostart) ? 0 : integerValue(ostart, "[:]");
|
|
int stop = (nil == ostop ) ? len : integerValue(ostop, "[:]");
|
|
if (start < 0) start += len;
|
|
if (start < 0 || start >= len) rangeError("[:]", "start index out of bounds", obj, start);
|
|
if (stop < 0) stop += len;
|
|
if (stop < 0 || stop > len) rangeError("[:]", "end index out of bounds", obj, stop);
|
|
*pstart = start;
|
|
*pstop = stop;
|
|
}
|
|
|
|
void print(oop obj, int indent);
|
|
|
|
oop String_slice(oop obj, oop ostart, oop ostop)
|
|
{
|
|
int len = _get(obj, String,length), start, stop;
|
|
getSliceRange(obj, ostart, ostop, len, &start, &stop);
|
|
if (start >= stop) return newStringLen(0, 0);
|
|
return newStringLen(_get(obj, String,value) + start, stop - start);
|
|
}
|
|
|
|
oop newSymbol(char *name)
|
|
{
|
|
oop obj = make(Symbol);
|
|
_set(obj, Symbol,name, strdup(name));
|
|
_set(obj, Symbol,value, UNDEFINED);
|
|
# if TYPECODES
|
|
_set(obj, Symbol,typecode, UNDEFINED_TYPECODE);
|
|
# endif
|
|
return obj;
|
|
}
|
|
|
|
char *stringValue(oop obj, char *who)
|
|
{
|
|
int type = getType(obj);
|
|
if (type == String) return String_content(obj);
|
|
if (type == Symbol) return _get(obj, Symbol,name);
|
|
typeError(who, "non-string operand", obj);
|
|
return 0;
|
|
}
|
|
|
|
int stringLength(oop obj, char *who)
|
|
{
|
|
int type = getType(obj);
|
|
if (type == String) return _get(obj, String,length);
|
|
if (type == Symbol) return strlen(_get(obj, Symbol,name));
|
|
typeError(who, "non-string operand", obj);
|
|
return 0;
|
|
}
|
|
|
|
oop intern(char *name);
|
|
|
|
oop Symbol_slice(oop obj, oop ostart, oop ostop)
|
|
{
|
|
char *name = _get(obj, Symbol,name);
|
|
int len = strlen(name), start, stop;
|
|
getSliceRange(obj, ostart, ostop, len, &start, &stop);
|
|
if (start >= stop) return intern(""); // ?!?
|
|
char buf[stop - start + 1];
|
|
strncpy(buf, name + start, stop - start);
|
|
buf[stop - start] = 0;
|
|
return intern(buf);
|
|
}
|
|
|
|
oop Object_put(oop obj, oop key, oop val);
|
|
oop Object_push(oop obj, oop val);
|
|
|
|
oop primitives = 0;
|
|
|
|
oop newPrimitive(prim_t function, oop name)
|
|
{
|
|
oop obj = make(Primitive);
|
|
_set(obj, Primitive,name, name);
|
|
_set(obj, Primitive,function, function);
|
|
_set(obj, Primitive,cookie, 0);
|
|
# if PROFILE
|
|
_set(obj, Primitive,profile, nil);
|
|
# endif
|
|
_set(obj, Primitive,index, _get(primitives, Object,isize));
|
|
Object_put(primitives, obj, newInteger(_get(primitives, Object,isize)));
|
|
Object_push(primitives, obj);
|
|
return obj;
|
|
}
|
|
|
|
#if PRIMCLOSURE
|
|
|
|
oop newLambda(oop parameters, oop body, oop parent, oop name)
|
|
{
|
|
oop obj = make(Lambda);
|
|
_set(obj, Lambda,parameters, parameters);
|
|
_set(obj, Lambda,body, body);
|
|
# if PROFILE
|
|
_set(obj, Lambda,profile, 0);
|
|
# endif
|
|
_set(obj, Lambda,parent, parent);
|
|
_set(obj, Lambda,name, name);
|
|
return obj;
|
|
}
|
|
|
|
oop newClosure(oop function, oop environment)
|
|
{
|
|
oop obj = make(Closure);
|
|
_set(obj, Closure,function, function);
|
|
_set(obj, Closure,environment, environment);
|
|
_set(obj, Closure,fixed, nil);
|
|
return obj;
|
|
}
|
|
|
|
int isClosure(oop obj) { return is(Closure, obj); }
|
|
|
|
#endif
|
|
|
|
oop macros = 0;
|
|
oop *symbols = 0;
|
|
size_t nsymbols = 0;
|
|
size_t maxsymbols = 0;
|
|
|
|
oop intern(char *name)
|
|
{
|
|
ssize_t lo = 0, hi = nsymbols - 1;
|
|
while (lo <= hi) {
|
|
ssize_t mid = (lo + hi) / 2;
|
|
oop sym = symbols[mid];
|
|
int cmp = strcmp(name, _get(sym, Symbol,name));
|
|
if (cmp < 0) hi = mid - 1;
|
|
else if (cmp > 0) lo = mid + 1;
|
|
else return sym;
|
|
}
|
|
if (++nsymbols >= maxsymbols) symbols = xrealloc(symbols, sizeof(*symbols) * (maxsymbols += 32));
|
|
memmove(symbols + lo + 1, symbols + lo, sizeof(*symbols) * (nsymbols - 1 - lo));
|
|
return symbols[lo] = newSymbol(name);
|
|
}
|
|
|
|
oop Object_at(oop obj, size_t index)
|
|
{
|
|
size_t size = get(obj, Object,isize);
|
|
if (index >= size) rangeError("Object[]", "index out of bounds", obj, index);
|
|
return _get(obj, Object,indexed)[index];
|
|
}
|
|
|
|
oop Object_atPut(oop obj, size_t index, oop val)
|
|
{
|
|
size_t size = get(obj, Object,isize);
|
|
if (index >= size) rangeError("Object.[]=", "index out of bounds", obj, index);
|
|
return _get(obj, Object,indexed)[index] = val;
|
|
}
|
|
|
|
oop Object_push(oop obj, oop val)
|
|
{
|
|
size_t size = get(obj, Object,isize);
|
|
size_t cap = _get(obj, Object,icap );
|
|
oop *indexed = _get(obj, Object,indexed);
|
|
if (size >= cap) {
|
|
cap = cap ? cap * 2 : 4;
|
|
indexed = xrealloc(indexed, sizeof(*indexed) * cap);
|
|
_set(obj, Object,icap, cap);
|
|
_set(obj, Object,indexed, indexed);
|
|
}
|
|
indexed[size++] = val;
|
|
_set(obj, Object,isize, size);
|
|
return val;
|
|
}
|
|
|
|
oop Object_pop(oop obj)
|
|
{
|
|
size_t size = get(obj, Object,isize);
|
|
if (!size) rangeError("Object.pop", "object is empty", obj, 0);
|
|
oop *indexed = _get(obj, Object,indexed);
|
|
oop result = indexed[--size];
|
|
_set(obj, Object,isize, size);
|
|
return result;
|
|
}
|
|
|
|
ssize_t Object_find(oop obj, oop key)
|
|
{
|
|
ssize_t hi = get(obj, Object,psize) - 1; // asserts obj is Object
|
|
if (hi < 0) return -1;
|
|
struct property *kvs = _get(obj, Object,properties);
|
|
ssize_t lo = 0;
|
|
while (lo <= hi) {
|
|
ssize_t mid = (lo + hi) / 2;
|
|
oop midkey = kvs[mid].key;
|
|
if (key < midkey) hi = mid - 1;
|
|
else if (key > midkey) lo = mid + 1;
|
|
else return mid;
|
|
}
|
|
return -1 - lo;
|
|
}
|
|
|
|
oop *Object_refLocal(oop obj, oop key)
|
|
{
|
|
if (!is(Object, obj)) return 0;
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) return &_get(obj, Object,properties)[ind].val;
|
|
return 0;
|
|
}
|
|
|
|
oop Object_getLocal(oop obj, oop key)
|
|
{
|
|
if (!is(Object, obj)) return nil;
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) return _get(obj, Object,properties)[ind].val;
|
|
return nil;
|
|
}
|
|
|
|
#if DELOPT
|
|
# define _getDelegate(OBJ) _get(OBJ, Object,delegate)
|
|
# define _setDelegate(OBJ, VAL) _set(OBJ, Object,delegate, VAL)
|
|
#else
|
|
# define _getDelegate(OBJ) Object_getLocal(OBJ, prop_delegate)
|
|
# define _setDelegate(OBJ, VAL) Object_put(OBJ, prop_delegate, VAL)
|
|
#endif
|
|
|
|
char *storeString(oop obj, int indent);
|
|
|
|
oop *Object_ref(oop obj, oop key)
|
|
{
|
|
oop o = nil;
|
|
switch (getType(obj)) {
|
|
case Undefined: o = pUndefined; break;
|
|
case Integer: o = pInteger; break;
|
|
case Float: o = pFloat; break;
|
|
case String: o = pString; break;
|
|
case Symbol: o = pSymbol; break;
|
|
case Primitive: o = pPrimitive; break;
|
|
# if PRIMCLOSURE
|
|
case Lambda:
|
|
if (key == sym_parameters) return &_get(obj, Lambda,parameters);
|
|
if (key == sym_body ) return &_get(obj, Lambda,body );
|
|
if (key == sym_parent ) return &_get(obj, Lambda,parent );
|
|
if (key == sym_name ) return &_get(obj, Lambda,name );
|
|
o = pLambda;
|
|
break;
|
|
case Closure:
|
|
if (key == sym_function ) return &_get(obj, Closure,function );
|
|
if (key == sym_environment) return &_get(obj, Closure,environment);
|
|
if (key == sym_fixed ) return &_get(obj, Closure,fixed);
|
|
o = pClosure;
|
|
break;
|
|
# endif
|
|
case Object: {
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) return &_get(obj, Object,properties)[ind].val;
|
|
o = _getDelegate(obj);
|
|
if (nil == o) o = pObject;
|
|
break;
|
|
}
|
|
}
|
|
if (key == prop_delegate) keyError("Object.", "__delegate__ is inaccessible", obj, prop_delegate);
|
|
while (is(Object, o)) {
|
|
ssize_t ind = Object_find(o, key);
|
|
if (ind >= 0) return &_get(o, Object,properties)[ind].val;
|
|
o = _getDelegate(o);
|
|
}
|
|
keyError("Object.", "undefined property", obj, key);
|
|
return 0;
|
|
}
|
|
|
|
oop Object_getOwner(oop obj, oop key, oop *ownerp)
|
|
{
|
|
oop o = nil;
|
|
switch (getType(obj)) {
|
|
case Undefined: o = pUndefined; break;
|
|
case Integer: o = pInteger; break;
|
|
case Float: o = pFloat; break;
|
|
case String: o = pString; break;
|
|
case Symbol: o = pSymbol; break;
|
|
case Primitive: o = pPrimitive; break;
|
|
# if PRIMCLOSURE
|
|
case Lambda:
|
|
if (key == sym_parameters) return _get(obj, Lambda,parameters);
|
|
if (key == sym_body ) return _get(obj, Lambda,body );
|
|
if (key == sym_parent ) return _get(obj, Lambda,parent );
|
|
if (key == sym_name ) return _get(obj, Lambda,name );
|
|
o = pLambda;
|
|
break;
|
|
case Closure:
|
|
if (key == sym_function ) return _get(obj, Closure,function );
|
|
if (key == sym_environment) return _get(obj, Closure,environment);
|
|
if (key == sym_fixed ) return _get(obj, Closure,fixed );
|
|
o = pClosure;
|
|
break;
|
|
# endif
|
|
case Object: {
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) { *ownerp = obj; return _get(obj, Object,properties)[ind].val; }
|
|
o = _getDelegate(obj);
|
|
if (nil == o) o = pObject;
|
|
break;
|
|
}
|
|
}
|
|
if (key == prop_delegate) return o;
|
|
while (is(Object, o)) {
|
|
ssize_t ind = Object_find(o, key);
|
|
if (ind >= 0) { *ownerp = o; return _get(o, Object,properties)[ind].val; }
|
|
o = _getDelegate(o);
|
|
}
|
|
keyError("Object.", "undefined property", obj, key);
|
|
return nil;
|
|
}
|
|
|
|
oop Object_get(oop obj, oop key)
|
|
{
|
|
oop o = nil;
|
|
switch (getType(obj)) {
|
|
case Undefined: o = pUndefined; break;
|
|
case Integer: o = pInteger; break;
|
|
case Float: o = pFloat; break;
|
|
case String: o = pString; break;
|
|
case Symbol: o = pSymbol; break;
|
|
case Primitive: o = pPrimitive; break;
|
|
# if PRIMCLOSURE
|
|
case Lambda:
|
|
if (key == sym_parameters) return _get(obj, Lambda,parameters);
|
|
if (key == sym_body ) return _get(obj, Lambda,body );
|
|
if (key == sym_parent ) return _get(obj, Lambda,parent );
|
|
if (key == sym_name ) return _get(obj, Lambda,name );
|
|
o = pLambda;
|
|
break;
|
|
case Closure:
|
|
if (key == sym_function ) return _get(obj, Closure,function );
|
|
if (key == sym_environment) return _get(obj, Closure,environment);
|
|
if (key == sym_fixed ) return _get(obj, Closure,fixed );
|
|
o = pClosure;
|
|
break;
|
|
# endif
|
|
case Object: {
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) return _get(obj, Object,properties)[ind].val;
|
|
o = _getDelegate(obj);
|
|
if (nil == o) o = pObject;
|
|
break;
|
|
}
|
|
}
|
|
if (key == prop_delegate) return o;
|
|
while (is(Object, o)) {
|
|
ssize_t ind = Object_find(o, key);
|
|
if (ind >= 0) return _get(o, Object,properties)[ind].val;
|
|
o = _getDelegate(o);
|
|
}
|
|
keyError("Object.", "undefined property", obj, key);
|
|
return nil;
|
|
}
|
|
|
|
oop *_refvar(oop obj, oop key)
|
|
{
|
|
while (is(Object, obj)) { // look for a binding of key in the local scopes
|
|
ssize_t ind = Object_find(obj, key);
|
|
if (ind >= 0) return &_get(obj, Object,properties)[ind].val;
|
|
obj = _getDelegate(obj);
|
|
}
|
|
int numspaces = _get(namespaces, Object,isize);
|
|
if (numspaces) { // look for a binding of key in the namespace stack
|
|
oop *nss = _get(namespaces, Object,indexed);
|
|
for (int i = numspaces; i--;) {
|
|
oop ns = nss[i];
|
|
ssize_t ind = Object_find(ns, key);
|
|
if (ind >= 0) return &_get(ns, Object,properties)[ind].val;
|
|
}
|
|
}
|
|
oop *ref = &_get(key, Symbol,value); // use the global binding
|
|
return ref;
|
|
}
|
|
|
|
oop *refvar(oop obj, oop key)
|
|
{
|
|
oop *ref = _refvar(obj, key);
|
|
if (UNDEFINED == *ref) undefinedError(key);
|
|
return ref;
|
|
}
|
|
|
|
oop getvar(oop obj, oop key)
|
|
{
|
|
return *refvar(obj, key);
|
|
}
|
|
|
|
oop Object_put(oop obj, oop key, oop val);
|
|
|
|
oop setvar(oop obj, oop key, oop val)
|
|
{
|
|
oop env = obj;
|
|
while (is(Object, env)) { // look for a binding of key in the local scopes
|
|
ssize_t ind = Object_find(env, key);
|
|
if (ind >= 0) return _get(env, Object,properties)[ind].val = val; // set it
|
|
env = _getDelegate(env);
|
|
}
|
|
if (nil != obj) return Object_put(obj, key, val); // create a new local variable
|
|
int numspaces = _get(namespaces, Object,isize);
|
|
if (numspaces) {
|
|
oop *nss = _get(namespaces, Object,indexed);
|
|
oop ns = nss[numspaces - 1];
|
|
if (is(Object, ns)) return Object_put(ns, key, val); // define a namespace variable
|
|
}
|
|
return _get(key, Symbol,value) = val; // set a global variable
|
|
}
|
|
|
|
oop Object_put(oop obj, oop key, oop val)
|
|
{
|
|
# if PRIMCLOSURE
|
|
switch (getType(obj)) {
|
|
case Lambda:
|
|
if (key == sym_parameters ) { _set(obj, Lambda,parameters, val); return val; }
|
|
if (key == sym_body ) { _set(obj, Lambda,body, val); return val; }
|
|
if (key == sym_parent ) { _set(obj, Lambda,parent, val); return val; }
|
|
if (key == sym_name ) { _set(obj, Lambda,name, val); return val; }
|
|
break;
|
|
case Closure:
|
|
if (key == sym_fixed ) { _set(obj, Closure,fixed, val); return val; }
|
|
if (key == sym_function ) { _set(obj, Closure,function, val); return val; }
|
|
if (key == sym_environment) { _set(obj, Closure,environment, val); return val; }
|
|
default:
|
|
break;
|
|
}
|
|
# endif
|
|
ssize_t ind = Object_find(obj, key);
|
|
struct property *kvs = _get(obj, Object,properties);
|
|
if (ind < 0) {
|
|
# if DELOPT
|
|
if (key == prop_delegate) return _setDelegate(obj, val);
|
|
# endif
|
|
int size = _get(obj, Object,psize);
|
|
ind = -1 - ind; assert(0 <= ind && ind <= size);
|
|
kvs = xrealloc(kvs, sizeof(*kvs) * ++size);
|
|
_set(obj, Object,properties, kvs);
|
|
_set(obj, Object,psize, size);
|
|
memmove(kvs + ind + 1, kvs + ind, sizeof(*kvs) * (size - 1 - ind));
|
|
kvs[ind].key = key;
|
|
} assert(ind < _get(obj, Object,psize));
|
|
assert(kvs[ind].key == key);
|
|
return kvs[ind].val = val;
|
|
}
|
|
|
|
oop new(oop delegate)
|
|
{
|
|
oop obj = make(Object);
|
|
_set(obj, Object,isize, 0);
|
|
_set(obj, Object,icap, 0);
|
|
_set(obj, Object,indexed, 0);
|
|
# if DELOPT
|
|
_set(obj, Object,psize, 0);
|
|
_setDelegate(obj, delegate);
|
|
# else
|
|
_set(obj, Object,psize, 1);
|
|
_set(obj, Object,properties, xmalloc(sizeof(struct property)))
|
|
[0] = (struct property) { prop_delegate, delegate };
|
|
# endif
|
|
return obj;
|
|
}
|
|
|
|
oop newObjectWith(int isize, oop *indexed, int psize, struct property *properties)
|
|
{
|
|
oop obj = make(Object);
|
|
_set(obj, Object,isize, isize);
|
|
_set(obj, Object,icap, isize);
|
|
_set(obj, Object,psize, psize);
|
|
_set(obj, Object,indexed, indexed);
|
|
_set(obj, Object,properties, properties);
|
|
# if DELOPT
|
|
_setDelegate(obj, nil);
|
|
# endif
|
|
return obj;
|
|
}
|
|
|
|
oop Object_slice(oop obj, oop ostart, oop ostop)
|
|
{
|
|
oop *indexed = _get(obj, Object,indexed);
|
|
int len = _get(obj, Object,isize), start, stop;
|
|
getSliceRange(obj, ostart, ostop, len, &start, &stop);
|
|
oop result = new(_getDelegate(obj));
|
|
for (int i = start; i < stop; ++i) Object_push(result, indexed[i]);
|
|
return result;
|
|
}
|
|
|
|
#if EXCEPTIONS
|
|
|
|
void genericError(char *who, char *message, char *kind, ...);
|
|
|
|
#define END (oop)0
|
|
|
|
void typeError(char *who, char *msg, oop value) { genericError(who, msg, "type error",
|
|
sym_value, value, END); }
|
|
|
|
void typeError2(char *who, char *msg, oop lhs, oop rhs) { genericError(who, msg, "type error",
|
|
sym_operand1, lhs, sym_operand2, rhs, END); }
|
|
|
|
void rangeError(char *who, char *msg, oop obj, int index) { genericError(who, msg, "index error",
|
|
sym_object, obj, sym_index, newInteger(index), END); }
|
|
|
|
void valueError(char *who, char *msg, oop value) { genericError(who, msg, "value error",
|
|
sym_value, value, END); }
|
|
|
|
void keyError(char *who, char *msg, oop object, oop key) { genericError(who, msg, "key error",
|
|
sym_object, object, sym_key, key, END); }
|
|
|
|
void undefinedError(oop name) { genericError( 0, 0, "undefined name",
|
|
sym_name, name, END); }
|
|
|
|
void syntaxError(char *msg) { genericError( 0, msg, "syntax error", END); }
|
|
|
|
void unknownError(char *msg) { genericError( 0, msg, "error", END); }
|
|
|
|
void keyboardInterrupt(void) { genericError( 0, 0, "keyboard interrupt", END); }
|
|
|
|
#undef END
|
|
|
|
#else
|
|
|
|
void typeError(char *who, char *msg, oop value) { fatal("%s: %s: %s", who, msg, getTypeName(value)); }
|
|
void typeError2(char *who, char *msg, oop lhs, oop rhs) { fatal("%s: %s: %s and %s", who, msg, getTypeName(lhs), getTypeName(rhs)); }
|
|
void rangeError(char *who, char *msg, oop obj, int index) { fatal("%s: %s: %s[%d]", who, msg, codeString(obj, 0), index); }
|
|
void valueError(char *who, char *msg, oop value) { fatal("%s: %s: %s", who, msg, codeString(value, 0)); }
|
|
void keyError(char *who, char *msg, oop object, oop key) { fatal("%s: %s: %s.%s", who, msg, codeString(object, 0), printString(key, 0)); }
|
|
void undefinedError(oop name) { fatal("undefined: %s", printString(name, 0)); }
|
|
void syntaxError(char *msg) { fatal("syntax error: %s", msg); }
|
|
void unknownError(char *msg) { fatal("%s", msg); }
|
|
void keyboardInterrupt(void) { fatal("keyboard interrupt"); }
|
|
|
|
#endif
|
|
|
|
|
|
int isSpecial(oop key)
|
|
{
|
|
return is(Symbol, key) && !strncmp("__", _get(key, Symbol,name), 2);
|
|
}
|
|
|
|
oop keys(oop self, int all)
|
|
{
|
|
oop keys = new(pObject);
|
|
# if DELOPT
|
|
if (all && nil != _getDelegate(self)) Object_push(keys, prop_delegate);
|
|
# endif
|
|
switch (getType(self)) {
|
|
case Undefined: case Integer: case Float: case String: case Symbol: case Primitive:
|
|
break;
|
|
# if PRIMCLOSURE
|
|
case Lambda: {
|
|
Object_push(keys, sym_parameters);
|
|
Object_push(keys, sym_body);
|
|
Object_push(keys, sym_parent);
|
|
Object_push(keys, sym_name);
|
|
break;
|
|
}
|
|
case Closure: {
|
|
Object_push(keys, sym_fixed);
|
|
Object_push(keys, sym_lambda);
|
|
Object_push(keys, sym_environment);
|
|
break;
|
|
}
|
|
# endif
|
|
case Object: {
|
|
int size = _get(self, Object,psize);
|
|
struct property *kvs = _get(self, Object,properties);
|
|
for (int i = 0; i < size; ++i) {
|
|
oop key = kvs[i].key;
|
|
if (all || !isSpecial(key)) Object_push(keys, key);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return keys;
|
|
}
|
|
|
|
intptr_t cmp(oop l, oop r, char *who)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr) return _integerValue(l) - _integerValue(r);
|
|
if (Float == tl || Float == tr) {
|
|
double ll = floatValue(l, who), rr = floatValue(r, who);
|
|
return ll < rr ? -1 : (ll > rr ? 1 : 0);
|
|
}
|
|
if (String == tl && String == tr) {
|
|
int ll = _get(l, String,length), rr = _get(r, String,length);
|
|
if (ll == rr) return strncmp(_get(l, String,value), _get(r, String,value), ll);
|
|
return ll - rr;
|
|
}
|
|
if (Symbol == tl && Symbol == tr) return strcmp(stringValue(l, who), stringValue(r, who));
|
|
return (intptr_t)l - (intptr_t)r;
|
|
}
|
|
|
|
#if defined (__linux__)
|
|
int objcmp(const void *a, const void *b, void *who) { return cmp(*(oop *)a, *(oop *)b, who); }
|
|
#else
|
|
int objcmp(void *who, const void *a, const void *b) { return cmp(*(oop *)a, *(oop *)b, who); }
|
|
#endif
|
|
|
|
oop sortObject(oop obj, char *who)
|
|
{ assert(is(Object, obj));
|
|
# if defined(__linux__)
|
|
qsort_r(_get(obj, Object,indexed), _get(obj, Object,isize), sizeof(oop), objcmp, "sort");
|
|
# else
|
|
qsort_r(_get(obj, Object,indexed), _get(obj, Object,isize), sizeof(oop), "sort", objcmp);
|
|
# endif
|
|
return obj;
|
|
}
|
|
|
|
int chrcmp(const void *a, const void *b) { return *(char *)a - *(char *)b; }
|
|
|
|
oop sortString(oop obj)
|
|
{ assert(is(String, obj));
|
|
qsort(_get(obj, String,value), _get(obj, String,length), 1, chrcmp);
|
|
return obj;
|
|
}
|
|
|
|
oop clone(oop obj) // shallow copy
|
|
{
|
|
switch (getType(obj)) {
|
|
case String: return newStringLen(_get(obj, String,value), _get(obj, String,length));
|
|
case Primitive: {
|
|
oop clone = make(Primitive);
|
|
_set(clone, Primitive,name, _get(obj, Primitive,name ));
|
|
_set(clone, Primitive,function, _get(obj, Primitive,function));
|
|
_set(clone, Primitive,cookie, _get(obj, Primitive,cookie ));
|
|
_set(clone, Primitive,index, _get(obj, Primitive,index ));
|
|
return clone;
|
|
}
|
|
case Object: {
|
|
oop clone = new(_getDelegate(obj));
|
|
oop *elts = _get(obj, Object,indexed);
|
|
int size = _get(obj, Object,isize);
|
|
for (int i = 0; i < size; ++i) Object_push(clone, elts[i]);
|
|
struct property *kvs = _get(obj, Object,properties);
|
|
size = _get(obj, Object,psize);
|
|
for (int i = 0; i < size; ++i) {
|
|
oop key = kvs[i].key;
|
|
if (prop_delegate == key) continue;
|
|
Object_put(clone, key, kvs[i].val);
|
|
}
|
|
return clone;
|
|
}
|
|
default: break;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
|
|
oop sorted(oop obj, char *who)
|
|
{
|
|
switch (getType(obj)) {
|
|
case String: return sortString(clone(obj));
|
|
case Object: return sortObject(clone(obj), who);
|
|
default: break;
|
|
}
|
|
typeError("sort", "unsortable type", obj);
|
|
return 0;
|
|
}
|
|
|
|
oop reverseString(oop obj, char *who)
|
|
{ assert(is(String, obj));
|
|
char *elts = _get(obj, String,value);
|
|
int size = _get(obj, String,length), middle = size / 2;
|
|
int left = 0, right = size;
|
|
while (left <= middle) {
|
|
int tmp = elts[left];
|
|
elts[left++] = elts[--right];
|
|
elts[right] = tmp;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
oop reverseObject(oop obj, char *who)
|
|
{ assert(is(Object, obj));
|
|
oop *elts = _get(obj, Object,indexed);
|
|
int size = _get(obj, Object,isize), middle = size / 2;
|
|
int left = 0, right = size;
|
|
while (left <= middle) {
|
|
oop tmp = elts[left];
|
|
elts[left++] = elts[--right];
|
|
elts[right] = tmp;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
oop reversed(oop obj, char *who)
|
|
{
|
|
switch (getType(obj)) {
|
|
case String: return reverseString(clone(obj), who);
|
|
case Object: return reverseObject(clone(obj), who);
|
|
default: break;
|
|
}
|
|
typeError("reverse", "unreversible type", obj);
|
|
return 0;
|
|
}
|
|
|
|
oop apply(oop func, oop self, oop args, oop env, oop owner);
|
|
|
|
void codeParametersOn(oop str, oop object, char *begin, char *end)
|
|
{
|
|
String_appendAll(str, begin);
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
int i;
|
|
for (i = 0; i < isize; ++i) {
|
|
if (i) String_appendAll(str, ", ");
|
|
printOn(str, indexed[i], 0);
|
|
}
|
|
String_appendAll(str, end);
|
|
}
|
|
|
|
void codeBlockOn(oop str, oop object)
|
|
{
|
|
String_appendAll(str, "{");
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
int i;
|
|
for (i = 0; i < isize; ++i) {
|
|
if (i) String_appendAll(str, "; "); else String_appendAll(str, " ");
|
|
codeOn(str, indexed[i], 0);
|
|
}
|
|
if (isize) String_appendAll(str, " ");
|
|
String_appendAll(str, "}");
|
|
}
|
|
|
|
oop codeOn(oop str, oop obj, int indent)
|
|
{
|
|
switch (getType(obj)) {
|
|
case Undefined: String_appendAll(str, "nil"); break;
|
|
case Integer: String_format(str, "%ld", _integerValue(obj)); break;
|
|
case Float: String_format(str, "%f" , _floatValue(obj)); break;
|
|
case String: storeOn(str, obj, 0); break;
|
|
case Symbol: String_format(str, "#%s", _get(obj, Symbol,name)); break;
|
|
case Primitive: {
|
|
String_appendAll(str, "<primitive ");
|
|
if (_get(obj, Primitive,name)) printOn(str, _get(obj, Primitive,name), 0);
|
|
else String_format(str, "%p", _get(obj, Primitive,function));
|
|
String_append(str, '>');
|
|
break;
|
|
}
|
|
#if PRIMCLOSURE
|
|
case Lambda: {
|
|
codeParametersOn(str, _get(obj, Lambda,parameters), "(", ")");
|
|
codeBlockOn(str, _get(obj, Lambda,body));
|
|
break;
|
|
}
|
|
case Closure: {
|
|
String_appendAll(str, "<closure>");
|
|
codeOn(str, _get(obj, Closure,function), indent);
|
|
break;
|
|
}
|
|
#endif
|
|
case Object: {
|
|
oop owner = nil;
|
|
oop evaluator = Object_getOwner(obj, prop_codeon, &owner);
|
|
oop args = new(pObject);
|
|
Object_push(args, str);
|
|
apply(evaluator, obj, args, nil, owner);
|
|
break;
|
|
}
|
|
default:
|
|
assert(!"this cannot happen");
|
|
}
|
|
return str;
|
|
}
|
|
|
|
void indentOn(oop buf, int indent)
|
|
{
|
|
if (indent < 1) return;
|
|
String_append(buf, '\n');
|
|
for (int i = indent; i--;) String_appendAll(buf, " | ");
|
|
String_appendAll(buf, " ");
|
|
}
|
|
|
|
void printObjectNameOn(oop buf, oop obj, int indent)
|
|
{
|
|
int level = 0;
|
|
oop proto = obj;
|
|
oop name = nil;
|
|
do {
|
|
++level;
|
|
name = Object_getLocal(proto, prop_name);
|
|
if (nil != name) break;
|
|
proto = _getDelegate(proto);
|
|
} while (is(Object, proto));
|
|
for (int i = level; i--;) String_append(buf, '<');
|
|
if (name != nil) printOn(buf, name, 0);
|
|
else String_appendAll(buf, "?");
|
|
for (int i = level; i--;) String_append(buf, '>');
|
|
}
|
|
|
|
enum {
|
|
NO_DELEGATE = -1,
|
|
NO_SPECIALS = -2,
|
|
};
|
|
|
|
int printObjectPropertiesOn(oop buf, oop obj, int indent)
|
|
{
|
|
oop names = sortObject(keys(obj, indent > 0), "print");
|
|
int nkeys = _get(names, Object,isize);
|
|
oop *elts = _get(names, Object,indexed);
|
|
int i = 0;
|
|
for (i = 0; i < nkeys; ++i) {
|
|
if (i && indent < 1) String_appendAll(buf, ", ");
|
|
oop key = elts[i];
|
|
if (prop_delegate == key) continue;
|
|
indentOn(buf, indent);
|
|
printOn(buf, key, 0);
|
|
String_appendAll(buf, ": ");
|
|
printOn(buf, Object_getLocal(obj, key), indent + (indent >= 0));
|
|
}
|
|
return i;
|
|
}
|
|
|
|
oop printOn(oop buf, oop obj, int indent)
|
|
{
|
|
switch (getType(obj)) {
|
|
case Undefined: String_appendAll(buf, "nil"); break;
|
|
case Integer: String_format(buf, "%ld", _integerValue(obj)); break;
|
|
case Float: String_format(buf, "%f" , _floatValue(obj)); break;
|
|
case String: {
|
|
char *str = _get(obj, String,value);
|
|
int len = _get(obj, String,length);
|
|
if (indent && indent != 1) {
|
|
String_append(buf, '"');
|
|
String_appendString(buf, String_escaped(obj));
|
|
String_append(buf, '"');
|
|
return buf;
|
|
}
|
|
String_format(buf, "%.*s", len, str);
|
|
break;
|
|
}
|
|
case Symbol:
|
|
if (indent < 0) String_append(buf, '#');
|
|
String_appendAll(buf, _get(obj, Symbol,name));
|
|
break;
|
|
case Primitive: {
|
|
String_appendAll(buf, "<primitive ");
|
|
if (_get(obj, Primitive,name)) printOn(buf, _get(obj, Primitive,name), 0);
|
|
else String_format(buf, "%p", _get(obj, Primitive,function));
|
|
String_append(buf, '>');
|
|
break;
|
|
}
|
|
#if PRIMCLOSURE
|
|
case Lambda: {
|
|
String_appendAll(buf, "<<Lambda>>");
|
|
if (indent < 1) break;
|
|
|
|
indentOn(buf, indent);
|
|
String_appendAll(buf, " body: ");
|
|
printOn(buf, _get(obj, Lambda,body), indent + 1);
|
|
|
|
String_append(buf, '\n');
|
|
for (int j = indent; j--;) String_appendAll(buf, " | ");
|
|
String_appendAll(buf, " parameters: ");
|
|
printOn(buf, _get(obj, Lambda,parameters), indent + 1);
|
|
break;
|
|
}
|
|
case Closure: {
|
|
String_appendAll(buf, "<<Closure>>");
|
|
if (indent < 1) break;
|
|
|
|
String_append(buf, '\n');
|
|
for (int j = indent; j--;) String_appendAll(buf, " | ");
|
|
String_appendAll(buf, " environment: ");
|
|
printOn(buf, _get(obj, Closure,environment), indent + 1);
|
|
|
|
String_append(buf, '\n');
|
|
for (int j = indent; j--;) String_appendAll(buf, " | ");
|
|
String_appendAll(buf, " function: ");
|
|
printOn(buf, _get(obj, Closure,function), indent + 1);
|
|
break;
|
|
}
|
|
#endif
|
|
case Object: {
|
|
printObjectNameOn(buf, obj, indent);
|
|
if (!indent) break;
|
|
for (;;) {
|
|
printObjectPropertiesOn(buf, obj, indent);
|
|
int isize = _get(obj, Object,isize);
|
|
oop *indexed = _get(obj, Object,indexed);
|
|
for (int i = 0; i < isize; ++i) {
|
|
String_append(buf, '\n');
|
|
for (int j = indent; j--;) String_appendAll(buf, " | ");
|
|
String_format(buf, " %d: ", i);
|
|
printOn(buf, indexed[i], indent+1);
|
|
}
|
|
oop delegate = _getDelegate(obj);
|
|
if (nil == delegate) break;
|
|
if (nil != Object_getLocal(delegate, prop_name)) break;
|
|
obj = delegate;
|
|
// ++indent;
|
|
String_appendAll(buf, " =>");
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
assert(!"this cannot happen");
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
oop storeOn(oop buf, oop obj, int indent)
|
|
{
|
|
switch (getType(obj)) {
|
|
case String: {
|
|
String_append(buf, '"');
|
|
char *str = _get(obj, String,value);
|
|
int len = _get(obj, String,length);
|
|
for (int i = 0; i < len; ++i) {
|
|
int c = (unsigned char)str[i];
|
|
switch (c) {
|
|
case '\a': String_appendAll(buf, "\\a"); break;
|
|
case '\b': String_appendAll(buf, "\\b"); break;
|
|
case '\f': String_appendAll(buf, "\\f"); break;
|
|
case '\n': String_appendAll(buf, "\\n"); break;
|
|
case '\r': String_appendAll(buf, "\\r"); break;
|
|
case '\t': String_appendAll(buf, "\\t"); break;
|
|
case '\v': String_appendAll(buf, "\\v"); break;
|
|
case '"': String_appendAll(buf, "\\\""); break;
|
|
case '\\': String_appendAll(buf, "\\\\"); break;
|
|
default:
|
|
if (c < ' ' || c > '~') String_format(buf, "\\%03o", c);
|
|
else String_append(buf, c);
|
|
break;
|
|
}
|
|
}
|
|
String_append(buf, '"');
|
|
break;
|
|
}
|
|
case Object: {
|
|
String_appendAll(buf, "[");
|
|
oop *elts = _get(obj, Object,indexed);
|
|
int size = _get(obj, Object,isize);
|
|
int i = 0;
|
|
while (i < size) {
|
|
if (i) String_appendAll(buf, ", ");
|
|
codeOn(buf, elts[i], indent);
|
|
++i;
|
|
}
|
|
oop names = sortObject(keys(obj, indent > 0), "print");
|
|
size = _get(names, Object,isize);
|
|
elts = _get(names, Object,indexed);
|
|
for (int j = 0; j < size; ++j) {
|
|
oop key = elts[j];
|
|
oop val = Object_getLocal(obj, key);
|
|
if (key == prop_delegate && val == pObject) continue;
|
|
if (i++) String_appendAll(buf, ", ");
|
|
codeOn(buf, key, indent);
|
|
String_appendAll(buf, ": ");
|
|
codeOn(buf, val, indent);
|
|
}
|
|
String_append(buf, ']');
|
|
break;
|
|
}
|
|
default: printOn(buf, obj, indent);
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
char *codeString(oop obj, int indent)
|
|
{
|
|
oop str = newStringLen(0, 0);
|
|
codeOn(str, obj, indent);
|
|
return String_content(str);
|
|
}
|
|
|
|
void code(oop obj, int indent)
|
|
{
|
|
printf("%s", codeString(obj, indent));
|
|
}
|
|
|
|
void codeln(oop obj, int indent)
|
|
{
|
|
code(obj, indent);
|
|
printf("\n");
|
|
}
|
|
|
|
char *printString(oop obj, int indent)
|
|
{
|
|
oop buf = newStringLen(0, 0);
|
|
printOn(buf, obj, indent);
|
|
return String_content(buf);
|
|
}
|
|
|
|
void print(oop obj, int indent)
|
|
{
|
|
printf("%s", printString(obj, indent));
|
|
}
|
|
|
|
void println(oop obj, int indent)
|
|
{
|
|
print(obj, indent);
|
|
printf("\n");
|
|
}
|
|
|
|
char *storeString(oop obj, int indent)
|
|
{
|
|
oop buf = newStringLen(0, 0);
|
|
storeOn(buf, obj, indent);
|
|
return String_content(buf);
|
|
}
|
|
|
|
void store(oop obj, int indent)
|
|
{
|
|
printf("%s", storeString(obj, indent));
|
|
}
|
|
|
|
void storeln(oop obj, int indent)
|
|
{
|
|
store(obj, indent);
|
|
printf("\n");
|
|
}
|
|
|
|
char *filename = "<stdin>";
|
|
int lineno = 1;
|
|
|
|
oop trace = nil;
|
|
|
|
void vwarning(char *fmt, va_list ap)
|
|
{
|
|
fflush(stdout);
|
|
fprintf(stderr, "\n%s:%d: ", filename, lineno);
|
|
vfprintf(stderr, fmt, ap);
|
|
fprintf(stderr, "\n");
|
|
}
|
|
|
|
void warning(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vwarning(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
void fatal(char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
vwarning(fmt, ap);
|
|
va_end(ap);
|
|
if (is(Object, trace)) {
|
|
int w = 1 + log10(_get(trace, Object,isize));
|
|
for (int i = _get(trace, Object,isize); i--;) {
|
|
printf("%*d: ", w, i);
|
|
codeln(_get(trace, Object,indexed)[i], 1);
|
|
}
|
|
}
|
|
exit(1);
|
|
}
|
|
|
|
|
|
#if EXCEPTIONS
|
|
|
|
void genericError(char *who, char *message, char *kind, ...)
|
|
{ assert(kind);
|
|
oop err = new(pObject);
|
|
if (who) Object_put(err, prop_function, newString(who));
|
|
if (message) Object_put(err, prop_message, newString(message));
|
|
Object_put(err, prop_kind, newString(kind));
|
|
va_list ap;
|
|
va_start(ap, kind);
|
|
oop sym = 0;
|
|
while ((sym = va_arg(ap, oop))) { assert(is(Symbol, sym));
|
|
oop arg = va_arg(ap, oop); assert(arg);
|
|
Object_put(err, sym, arg);
|
|
}
|
|
va_end(ap);
|
|
if (is(Object, trace)) {
|
|
int size = _get(trace, Object,isize);
|
|
oop *elts = _get(trace, Object,indexed);
|
|
for (int i = 0; i < size; ++i) Object_push(err, elts[i]);
|
|
}
|
|
nlrReturn(err, NLR_RAISE);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
#include <signal.h>
|
|
|
|
void sigint(int sig)
|
|
{
|
|
keyboardInterrupt();
|
|
}
|
|
|
|
typedef struct Input {
|
|
struct Input *next;
|
|
char *text;
|
|
int size;
|
|
int position;
|
|
} Input;
|
|
|
|
Input *newInput(void)
|
|
{
|
|
return xmalloc(sizeof(Input));
|
|
}
|
|
|
|
Input *input = 0;
|
|
|
|
Input *makeInput(void)
|
|
{
|
|
return xmalloc(sizeof(Input));
|
|
}
|
|
|
|
#define YYSTYPE oop
|
|
|
|
#define YY_MALLOC(C, N) GC_malloc(N)
|
|
#define YY_REALLOC(C, P, N) GC_realloc(P, N)
|
|
#define YY_FREE(C, P) GC_free(P)
|
|
|
|
#define YY_INPUT(buf, result, max_size) \
|
|
{ \
|
|
result= (input->position >= input->size) \
|
|
? 0 \
|
|
: ((*(buf)= input->text[input->position++]), 1); \
|
|
}
|
|
|
|
YYSTYPE yysval = 0;
|
|
|
|
oop eval(oop exp, oop env);
|
|
oop evargs(oop list, oop env);
|
|
|
|
oop Object_eval(oop exp, oop env) { return exp; }
|
|
|
|
void Object_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
storeOn(str, exp, 0);
|
|
}
|
|
|
|
extern inline oop mkptr(void *address)
|
|
{
|
|
// top 7 bits of virtual addresses are guaranteed to be the same,
|
|
// at least until Apple decides to break that and call it a "feature"
|
|
intptr_t p = (intptr_t)address;
|
|
oop o = newInteger(p); assert(p == _integerValue(o));
|
|
return o;
|
|
}
|
|
|
|
oop newRefLocal(oop name)
|
|
{
|
|
oop o = new(pRefLocal);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop RefLocal_eval(oop exp, oop env)
|
|
{
|
|
if (!is(Object, env)) valueError("local", "not in a local scope", exp);
|
|
oop sym = Object_get(exp, sym_name);
|
|
oop *ref = Object_refLocal(env, sym);
|
|
if (!ref) undefinedError(sym);
|
|
return mkptr(ref);
|
|
}
|
|
|
|
void RefLocal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "local ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
}
|
|
|
|
oop newGetLocal(oop name)
|
|
{
|
|
oop o = new(pGetLocal);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop GetLocal_eval(oop exp, oop env)
|
|
{
|
|
if (!is(Object, env)) valueError("local", "not in a local scope", exp);
|
|
oop sym = Object_get(exp, sym_name);
|
|
oop *ref = Object_refLocal(env, sym);
|
|
if (!ref) undefinedError(sym);
|
|
return *ref;
|
|
}
|
|
|
|
void GetLocal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "local ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
}
|
|
|
|
oop newSetLocal(oop name, oop value)
|
|
{
|
|
oop o = new(pSetLocal);
|
|
Object_put(o, sym_name, name);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop SetLocal_eval(oop exp, oop env)
|
|
{
|
|
if (!is(Object, env)) valueError("local", "not in a local scope", exp);
|
|
oop sym = Object_get(exp, sym_name );
|
|
oop val = eval(Object_get(exp, sym_value), env);
|
|
return Object_put(env, sym, val);
|
|
}
|
|
|
|
void SetLocal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "local ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
String_appendAll(str, " = ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
oop newRefGlobal(oop name)
|
|
{
|
|
oop o = new(pRefGlobal);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop RefGlobal_eval(oop exp, oop env)
|
|
{
|
|
oop sym = Object_get(exp, sym_name);
|
|
return mkptr(&_get(sym, Symbol,value));
|
|
}
|
|
|
|
void RefGlobal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "global ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
}
|
|
|
|
oop newGetGlobal(oop name)
|
|
{
|
|
oop o = new(pGetGlobal);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop GetGlobal_eval(oop exp, oop env)
|
|
{
|
|
oop sym = Object_get(exp, sym_name);
|
|
oop val = _get(sym, Symbol,value);
|
|
if (UNDEFINED == val) undefinedError(sym);
|
|
return val;
|
|
}
|
|
|
|
void GetGlobal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "global ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
}
|
|
|
|
oop newSetGlobal(oop name, oop value)
|
|
{
|
|
oop o = new(pSetGlobal);
|
|
Object_put(o, sym_name, name);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop SetGlobal_eval(oop exp, oop env)
|
|
{
|
|
oop sym = Object_get(exp, sym_name ) ;
|
|
oop val = eval(Object_get(exp, sym_value), env);
|
|
return _set(sym, Symbol,value, val);
|
|
}
|
|
|
|
void SetGlobal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "global ");
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
String_appendAll(str, " = ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
oop newRefVar(oop name)
|
|
{
|
|
oop o = new(pRefVar);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop RefVar_eval(oop exp, oop env)
|
|
{
|
|
return mkptr(refvar(env, Object_get(exp, sym_name)));
|
|
}
|
|
|
|
void RefVar_codeOn(oop exp, oop str, oop env) { printOn(str, Object_get(exp, sym_name), 0); }
|
|
|
|
oop newGetVar(oop name)
|
|
{
|
|
oop o = new(pGetVar);
|
|
Object_put(o, sym_name, name);
|
|
return o;
|
|
}
|
|
|
|
oop GetVar_eval(oop exp, oop env) { return getvar(env, Object_get(exp, sym_name)); }
|
|
|
|
void GetVar_codeOn(oop exp, oop str, oop env) { printOn(str, Object_get(exp, sym_name), 0); }
|
|
|
|
oop newSetVar(oop name, oop value)
|
|
{
|
|
oop o = new(pSetVar);
|
|
Object_put(o, sym_name, name);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop SetVar_eval(oop exp, oop env)
|
|
{
|
|
oop key = Object_get(exp, sym_name ) ;
|
|
oop val = eval(Object_get(exp, sym_value), env);
|
|
return setvar(env, key, val);
|
|
}
|
|
|
|
void SetVar_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
printOn(str, Object_get(exp, sym_name), 0);
|
|
String_appendAll(str, " = ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
oop newRefProp(oop object, oop key)
|
|
{
|
|
oop o = new(pRefProp);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_key , key );
|
|
return o;
|
|
}
|
|
|
|
oop RefProp_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop key = Object_get(exp, sym_key ) ;
|
|
return mkptr(Object_ref(obj, key));
|
|
}
|
|
|
|
void RefProp_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, ".");
|
|
printOn(str, Object_get(exp, sym_key ), 0);
|
|
}
|
|
|
|
oop newGetProp(oop object, oop key)
|
|
{
|
|
oop o = new(pGetProp);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_key , key );
|
|
return o;
|
|
}
|
|
|
|
oop GetProp_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop key = Object_get(exp, sym_key ) ;
|
|
return Object_get(obj, key);
|
|
}
|
|
|
|
void GetProp_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, ".");
|
|
printOn(str, Object_get(exp, sym_key ), 0);
|
|
}
|
|
|
|
oop newSetProp(oop object, oop key, oop value)
|
|
{
|
|
oop o = new(pSetProp);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_key , key );
|
|
Object_put(o, sym_value , value );
|
|
return o;
|
|
}
|
|
|
|
oop SetProp_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop key = Object_get(exp, sym_key ) ;
|
|
oop val = eval(Object_get(exp, sym_value ), env);
|
|
return Object_put(obj, key, val);
|
|
}
|
|
|
|
void SetProp_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
oop key = Object_get(exp, sym_key);
|
|
if (is(Symbol,key)) {
|
|
String_appendAll(str, ".");
|
|
printOn(str, key, 0);
|
|
}
|
|
else {
|
|
String_appendAll(str, "[");
|
|
codeOn(str, key, 0);
|
|
String_appendAll(str, "]");
|
|
}
|
|
String_appendAll(str, " = ");
|
|
codeOn(str, Object_get(exp, sym_value ), 0);
|
|
}
|
|
|
|
void oob(oop obj, int index)
|
|
{
|
|
rangeError("[]", "index out of bounds", obj, index);
|
|
}
|
|
|
|
char *String_aref(oop obj, int index)
|
|
{
|
|
if (index >= _get(obj, String,length)) oob(obj, index);
|
|
return _get(obj, String,value) + index;
|
|
}
|
|
|
|
char *Symbol_aref(oop obj, int index)
|
|
{
|
|
if (index >= strlen(_get(obj, Symbol,name))) oob(obj, index);
|
|
return _get(obj, Symbol,name) + index;
|
|
}
|
|
|
|
oop *Object_aref(oop obj, int index)
|
|
{
|
|
if (index >= _get(obj, Object,isize)) oob(obj, index);
|
|
return _get(obj, Object,indexed) + index;
|
|
}
|
|
|
|
oop newRefArray(oop object, oop index)
|
|
{
|
|
oop o = new(pRefArray);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_index , index );
|
|
return o;
|
|
}
|
|
|
|
oop RefArray_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop ind = eval(Object_get(exp, sym_index ), env);
|
|
if (isInteger(ind)) {
|
|
int index = _integerValue(ind);
|
|
switch (getType(obj)) {
|
|
case String: goto error;
|
|
case Symbol: goto error;
|
|
case Object: return mkptr(Object_aref(obj, index));
|
|
default: goto error;
|
|
}
|
|
}
|
|
if (is(Object, obj)) {
|
|
oop *ref = Object_refLocal(obj, ind);
|
|
if (ref) return mkptr(ref);
|
|
}
|
|
error:
|
|
typeError("[]", "not an object", obj);
|
|
return 0;
|
|
}
|
|
|
|
void RefArray_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, "[");
|
|
codeOn(str, Object_get(exp, sym_index), 0);
|
|
String_appendAll(str, "]");
|
|
}
|
|
|
|
oop newGetArray(oop object, oop index)
|
|
{
|
|
oop o = new(pGetArray);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_index , index );
|
|
return o;
|
|
}
|
|
|
|
oop GetArray_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop ind = eval(Object_get(exp, sym_index ), env);
|
|
if (isInteger(ind)) {
|
|
int index = _integerValue(ind);
|
|
switch (getType(obj)) {
|
|
case String: return newInteger(*(unsigned char *)String_aref(obj, index));
|
|
case Symbol: return newInteger(*(unsigned char *)Symbol_aref(obj, index));
|
|
case Object: return *Object_aref(obj, index);
|
|
default: typeError("[]", "non-indexable object", obj);
|
|
}
|
|
}
|
|
if (!is(Object, obj)) typeError("[]", "non-associative object", obj);
|
|
return Object_getLocal(obj, ind);
|
|
}
|
|
|
|
void GetArray_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, "[");
|
|
codeOn(str, Object_get(exp, sym_index), 0);
|
|
String_appendAll(str, "]");
|
|
}
|
|
|
|
oop newSetArray(oop object, oop index, oop value)
|
|
{
|
|
oop o = new(pSetArray);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_index , index );
|
|
Object_put(o, sym_value , value );
|
|
return o;
|
|
}
|
|
|
|
oop SetArray_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop ind = eval(Object_get(exp, sym_index ), env);
|
|
oop val = eval(Object_get(exp, sym_value ), env);
|
|
if (isInteger(ind)) {
|
|
int index = _integerValue(ind);
|
|
switch (getType(obj)) {
|
|
case String: *String_aref(obj, index) = integerValue(val, "[]="); break;
|
|
case Symbol: *Symbol_aref(obj, index) = integerValue(val, "[]="); break;
|
|
case Object: *Object_aref(obj, index) = val; break;
|
|
default: typeError("[]=", "non-indexable object", obj);
|
|
}
|
|
return val;
|
|
}
|
|
if (!is(Object, obj)) typeError("[]=", "non-associative object", obj);
|
|
return Object_put(obj, ind, val);
|
|
}
|
|
|
|
void SetArray_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, "[");
|
|
codeOn(str, Object_get(exp, sym_index), 0);
|
|
String_appendAll(str, "] = ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
oop newGetSlice(oop object, oop start, oop stop)
|
|
{
|
|
oop o = new(pGetSlice);
|
|
Object_put(o, sym_object, object);
|
|
Object_put(o, sym_start, start );
|
|
Object_put(o, sym_stop, stop );
|
|
return o;
|
|
}
|
|
|
|
oop GetSlice_eval(oop exp, oop env)
|
|
{
|
|
oop obj = eval(Object_get(exp, sym_object), env);
|
|
oop start = eval(Object_get(exp, sym_start ), env);
|
|
oop stop = eval(Object_get(exp, sym_stop ), env);
|
|
switch (getType(obj)) {
|
|
case String: return String_slice(obj, start, stop);
|
|
case Symbol: return Symbol_slice(obj, start, stop);
|
|
case Object: return Object_slice(obj, start, stop);
|
|
default: typeError("[:]", "non-indexable object", obj);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
void GetSlice_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_object), 0);
|
|
String_appendAll(str, "[");
|
|
codeOn(str, Object_get(exp, sym_start ), 0);
|
|
String_appendAll(str, ":");
|
|
codeOn(str, Object_get(exp, sym_stop ), 0);
|
|
String_appendAll(str, "]");
|
|
}
|
|
|
|
oop newCall(oop function, oop arguments)
|
|
{
|
|
oop o = new(pCall);
|
|
Object_put(o, sym_function , function );
|
|
Object_put(o, sym_arguments, arguments);
|
|
return o;
|
|
}
|
|
|
|
oop newApply(oop function, oop arguments)
|
|
{
|
|
if (_getDelegate(function) == pGetVar) {
|
|
oop symbol = Object_get(function, sym_name);
|
|
assert(is(Symbol, symbol));
|
|
oop macro = Object_getLocal(macros, symbol);
|
|
if (nil != macro) return apply(macro, nil, arguments, nil, nil);
|
|
}
|
|
return newCall(function, arguments);
|
|
}
|
|
|
|
int isFixed(oop func)
|
|
{
|
|
# if PRIMCLOSURE
|
|
return is(Closure, func) && nil != _get(func, Closure,fixed);
|
|
# else
|
|
return nil != Object_getLocal(func, sym_fixed);
|
|
# endif
|
|
}
|
|
|
|
oop Call_eval(oop exp, oop env)
|
|
{
|
|
oop cfunc = eval (Object_get(exp, sym_function ), env);
|
|
oop cargs = Object_get(exp, sym_arguments);
|
|
if (!isFixed(cfunc)) cargs = evargs(cargs, env);
|
|
return apply(cfunc, nil, cargs, env, nil);
|
|
}
|
|
|
|
void codeArgumentsOn(oop str, oop object, char *begin, char *end)
|
|
{
|
|
String_appendAll(str, begin);
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
int i;
|
|
for (i = 0; i < isize; ++i) {
|
|
if (i) String_appendAll(str, ", ");
|
|
codeOn(str, indexed[i], 0);
|
|
}
|
|
struct property *kvs = _get(object, Object,properties);
|
|
int psize = _get(object, Object,psize);
|
|
for (int j = 0; j < psize; ++j) {
|
|
if (prop_delegate == kvs[j].key && pObject == kvs[j].val) continue;
|
|
if (i++) String_appendAll(str, ", ");
|
|
printOn(str, kvs[j].key, 0);
|
|
String_appendAll(str, ": ");
|
|
codeOn(str, kvs[j].val, 0);
|
|
}
|
|
String_appendAll(str, end);
|
|
}
|
|
|
|
void Call_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_function ), 0);
|
|
codeArgumentsOn(str, Object_get(exp, sym_arguments), "(", ")");
|
|
}
|
|
|
|
oop newSuper(oop method, oop arguments)
|
|
{
|
|
oop o = new(pSuper);
|
|
Object_put(o, sym_method , method );
|
|
Object_put(o, sym_arguments, arguments);
|
|
return o;
|
|
}
|
|
|
|
oop Super_eval(oop exp, oop env)
|
|
{
|
|
oop meth = Object_get(exp, sym_method);
|
|
oop args = Object_get(exp, sym_arguments);
|
|
oop self = Object_get(env, sym_self);
|
|
oop owner = Object_get(env, prop_owner);
|
|
oop iargs = evargs(args, env);
|
|
oop ifunc = Object_getOwner(_getDelegate(owner), meth, &owner); // fails if property not defined
|
|
return apply(ifunc, self, iargs, env, owner);
|
|
}
|
|
|
|
void Super_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "super.");
|
|
printOn(str, Object_get(exp, sym_method ), 0);
|
|
codeArgumentsOn(str, Object_get(exp, sym_arguments), "(", ")");
|
|
}
|
|
|
|
oop newInvoke(oop self, oop method, oop arguments)
|
|
{
|
|
oop o = new(pInvoke);
|
|
Object_put(o, sym_self , self );
|
|
Object_put(o, sym_method , method );
|
|
Object_put(o, sym_arguments, arguments);
|
|
return o;
|
|
}
|
|
|
|
oop Invoke_eval(oop exp, oop env)
|
|
{
|
|
oop self = eval (Object_get(exp, sym_self ), env);
|
|
oop meth = Object_get(exp, sym_method);
|
|
oop iargs = evargs(Object_get(exp, sym_arguments), env);
|
|
oop owner = nil;
|
|
oop ifunc = Object_getOwner(self, meth, &owner); // fails if property not defined
|
|
return apply(ifunc, self, iargs, env, owner);
|
|
}
|
|
|
|
void Invoke_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeOn(str, Object_get(exp, sym_self ), 0);
|
|
String_appendAll(str, ".");
|
|
printOn(str, Object_get(exp, sym_method ), 0);
|
|
codeArgumentsOn(str, Object_get(exp, sym_arguments), "(", ")");
|
|
}
|
|
|
|
oop newContinue(void)
|
|
{
|
|
return new(pContinue);
|
|
}
|
|
|
|
oop Continue_eval(oop exp, oop env)
|
|
{
|
|
# if NONLOCAL
|
|
nlrReturn(nil, NLR_CONTINUE);
|
|
assert(!"this cannot happen");
|
|
# endif
|
|
return nil;
|
|
}
|
|
|
|
void Continue_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "continue");
|
|
}
|
|
|
|
oop newBreak(oop value)
|
|
{
|
|
oop o = new(pBreak);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop Break_eval(oop exp, oop env)
|
|
{
|
|
oop value = eval(Object_get(exp, sym_value), env);
|
|
# if NONLOCAL
|
|
nlrReturn(value, NLR_BREAK);
|
|
assert(!"this cannot happen");
|
|
# endif
|
|
return value;
|
|
}
|
|
|
|
void Break_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "break");
|
|
oop value = Object_get(exp, sym_value);
|
|
if (nil != value) {
|
|
String_appendAll(str, " ");
|
|
codeOn(str, value, 0);
|
|
}
|
|
}
|
|
|
|
oop newReturn(oop value)
|
|
{
|
|
oop o = new(pReturn);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop Return_eval(oop exp, oop env)
|
|
{
|
|
oop value = eval(Object_get(exp, sym_value), env);
|
|
# if NONLOCAL
|
|
nlrReturn(value, NLR_RETURN);
|
|
assert(!"this cannot happen");
|
|
# endif
|
|
return value;
|
|
}
|
|
|
|
void Return_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "return ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
oop newTryCatch(oop statement, oop identifier, oop handler)
|
|
{
|
|
oop o = new(pTryCatch);
|
|
Object_put(o, sym_statement, statement);
|
|
Object_put(o, sym_identifier, identifier);
|
|
Object_put(o, sym_handler, handler);
|
|
return o;
|
|
}
|
|
|
|
oop TryCatch_eval(oop exp, oop env)
|
|
{
|
|
oop statement = Object_get(exp, sym_statement);
|
|
# if NONLOCAL
|
|
switch (nlrPush()) {
|
|
case NLR_CONTINUE: nlrReturn(nlrPop(), NLR_CONTINUE);
|
|
case NLR_BREAK: nlrReturn(nlrPop(), NLR_BREAK);
|
|
case NLR_RETURN: nlrReturn(nlrPop(), NLR_RETURN);
|
|
case NLR_RAISE: {
|
|
oop exception = nlrPop();
|
|
oop env2 = new(pObject);
|
|
_setDelegate(env2, env);
|
|
Object_put(env2, Object_get(exp, sym_identifier), exception);
|
|
return eval(Object_get(exp, sym_handler), env2);
|
|
}
|
|
}
|
|
# endif
|
|
oop result = eval(Object_get(exp, sym_statement), env);
|
|
# if NONLOCAL
|
|
nlrPop();
|
|
# endif
|
|
return result;
|
|
}
|
|
|
|
void TryCatch_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "try ");
|
|
codeOn(str, Object_get(exp, sym_statement), 0);
|
|
String_appendAll(str, " catch (");
|
|
printOn(str, Object_get(exp, sym_identifier), 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, Object_get(exp, sym_handler), 0);
|
|
}
|
|
|
|
oop newTryEnsure(oop statement, oop handler)
|
|
{
|
|
oop o = new(pTryEnsure);
|
|
Object_put(o, sym_statement, statement);
|
|
Object_put(o, sym_handler, handler);
|
|
return o;
|
|
}
|
|
|
|
oop TryEnsure_eval(oop exp, oop env)
|
|
{
|
|
oop statement = Object_get(exp, sym_statement);
|
|
oop handler = Object_get(exp, sym_handler);
|
|
oop result = nil;
|
|
int nlreason = 0;
|
|
# if NONLOCAL
|
|
if (NLR_INIT != (nlreason = nlrPush())) {
|
|
result = nlrPop();
|
|
eval(handler, env);
|
|
nlrReturn(result, nlreason);
|
|
}
|
|
# endif
|
|
result = eval(Object_get(exp, sym_statement), env);
|
|
# if NONLOCAL
|
|
nlrPop();
|
|
# endif
|
|
eval(handler, env);
|
|
return result;
|
|
}
|
|
|
|
void TryEnsure_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "try ");
|
|
codeOn(str, Object_get(exp, sym_statement), 0);
|
|
String_appendAll(str, " catch (");
|
|
printOn(str, Object_get(exp, sym_identifier), 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, Object_get(exp, sym_handler), 0);
|
|
}
|
|
|
|
oop newRaise(oop value)
|
|
{
|
|
oop o = new(pRaise);
|
|
Object_put(o, sym_value, value);
|
|
return o;
|
|
}
|
|
|
|
oop Raise_eval(oop exp, oop env)
|
|
{
|
|
oop value = eval(Object_get(exp, sym_value), env);
|
|
# if NONLOCAL
|
|
nlrReturn(value, NLR_RAISE);
|
|
assert(!"this cannot happen");
|
|
# endif
|
|
return value;
|
|
}
|
|
|
|
void Raise_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
String_appendAll(str, "raise ");
|
|
codeOn(str, Object_get(exp, sym_value), 0);
|
|
}
|
|
|
|
#if !PRIMCLOSURE
|
|
|
|
oop newLambda(oop parameters, oop body, oop parent, oop name)
|
|
{
|
|
oop o = new(pLambda);
|
|
Object_put(o, sym_parameters, parameters );
|
|
Object_put(o, sym_body , body );
|
|
# if PROFILE
|
|
Object_put(o, sym_profile , nil );
|
|
# endif
|
|
Object_put(o, sym_parent , parent );
|
|
Object_put(o, sym_name , name );
|
|
return o;
|
|
}
|
|
|
|
oop newClosure(oop function, oop environment)
|
|
{
|
|
oop o = new(pClosure);
|
|
Object_put(o, sym_function , function );
|
|
Object_put(o, sym_environment, environment);
|
|
return o;
|
|
}
|
|
|
|
oop Lambda_eval(oop exp, oop env)
|
|
{
|
|
return newClosure(exp, env);
|
|
}
|
|
|
|
void Lambda_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeParametersOn(str, Object_get(exp, sym_parameters), "(", ")");
|
|
codeBlockOn(str, Object_get(exp, sym_body));
|
|
}
|
|
|
|
oop Closure_eval(oop exp, oop env)
|
|
{
|
|
return exp;
|
|
}
|
|
|
|
void Closure_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
printOn(str, Object_getLocal(exp, sym_function), 0);
|
|
}
|
|
|
|
int isClosure(oop obj)
|
|
{
|
|
return is(Object, obj) && pClosure == _getDelegate(obj);
|
|
}
|
|
|
|
#endif // !PRIMCLOSURE
|
|
|
|
#define doBinops(_) \
|
|
_(LogOr, ||) \
|
|
_(LogAnd, &&) \
|
|
_(BitOr, |) \
|
|
_(BitXor, ^) \
|
|
_(BitAnd, &) \
|
|
_(Eq, ==) _(NotEq, !=) \
|
|
_(Less, < ) _(LessEq, <=) _(Grtr, >=) _(GrtrEq, > ) \
|
|
_(Shl, <<) _(Shr, >>) \
|
|
_(Add, +) _(Sub, -) \
|
|
_(Mul, *) _(Div, /) _(Mod, %) \
|
|
_(PostAdd, ++) _(PostDec, --) \
|
|
_(PreSet, =) \
|
|
_(PreOr, |=) _(PreXor, ^=) _(PreAnd, &=) \
|
|
_(PreShl, >>=) _(PreShr, <<=) \
|
|
_(PreAdd, +=) _(PreSub, -=) \
|
|
_(PreMul, *=) _(PreDiv, /=) _(PreMod, %=)
|
|
|
|
#define defineBinop(NAME, OP) op##NAME,
|
|
enum binop {
|
|
doBinops(defineBinop)
|
|
};
|
|
#undef defineBinop
|
|
|
|
#define nameBinop(NAME, OP) #OP,
|
|
char *binopNames[] = {
|
|
doBinops(nameBinop)
|
|
};
|
|
#undef nameBinop
|
|
|
|
#if BINOPT
|
|
|
|
typedef oop (*binop_t)(oop lhs, oop rhs);
|
|
|
|
#define declBinop(NAME, OP) oop bin##NAME(oop, oop);
|
|
doBinops(declBinop)
|
|
#undef declBinop
|
|
|
|
#define implBinop(NAME, OP) bin##NAME,
|
|
binop_t binops[] = {
|
|
doBinops(implBinop)
|
|
};
|
|
#undef implBinop
|
|
|
|
#endif // BINOPT
|
|
|
|
#define newBoolean(TF) ((TF) ? sym_t : nil)
|
|
|
|
#define binop(NAME, OP) \
|
|
oop NAME(oop lhs, oop rhs) \
|
|
{ \
|
|
return newInteger(integerValue(lhs, #OP) OP integerValue(rhs, #OP)); \
|
|
}
|
|
|
|
binop(binBitOr, |);
|
|
binop(binBitXor, ^);
|
|
binop(binBitAnd, &);
|
|
|
|
#undef binop
|
|
|
|
#define binop(NAME, OP) \
|
|
oop NAME(oop lhs, oop rhs) \
|
|
{ \
|
|
return newBoolean(cmp(lhs, rhs, #OP) OP 0); \
|
|
}
|
|
|
|
binop(binEq, ==);
|
|
binop(binNotEq, !=);
|
|
binop(binLess, < );
|
|
binop(binLessEq, <=);
|
|
binop(binGrtrEq, >=);
|
|
binop(binGrtr, > );
|
|
|
|
#undef binop
|
|
|
|
oop binLogOr (oop lhs, oop rhs) { abort(); return 0; }
|
|
oop binLogAnd(oop lhs, oop rhs) { abort(); return 0; }
|
|
|
|
oop binPostAdd(oop lhs, oop rhs)
|
|
{ assert(isInteger(lhs)); // lval ref
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs)); // X++ -> X+=1
|
|
oop value = *ref;
|
|
int amount = _integerValue(rhs);
|
|
switch (getType(value)) {
|
|
case Integer: *ref = newInteger(_integerValue(value) + amount); break;
|
|
case Float: *ref = newFloat (_floatValue (value) + amount); break;
|
|
default: typeError("++", "non-numeric value", value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
oop binPostDec(oop lhs, oop rhs)
|
|
{ assert(isInteger(lhs)); // lval ref
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs)); // X-- -> X-=1
|
|
oop value = *ref;
|
|
int amount = _integerValue(rhs);
|
|
switch (getType(value)) {
|
|
case Integer: *ref = newInteger(_integerValue(value) - amount); break;
|
|
case Float: *ref = newFloat (_floatValue (value) - amount); break;
|
|
default: typeError("++", "non-numeric value", value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
oop binPreSet(oop lhs, oop rhs)
|
|
{ assert(isInteger(lhs));
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs));
|
|
return *ref = rhs;
|
|
}
|
|
|
|
#define binop(NAME, OP) \
|
|
oop NAME(oop lhs, oop rhs) \
|
|
{ assert(isInteger(lhs)); \
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs)); \
|
|
oop val = *ref; \
|
|
if (isInteger(val) && isInteger(rhs)) { \
|
|
long l = _integerValue(val), r = _integerValue(rhs); \
|
|
l OP r; \
|
|
return *ref = newInteger(l); \
|
|
} \
|
|
double l = floatValue(val, #OP); \
|
|
double r = floatValue(rhs, #OP); \
|
|
l OP r; \
|
|
return *ref = newFloat(l); \
|
|
}
|
|
|
|
binop(binPreAdd, +=);
|
|
binop(binPreSub, -=);
|
|
binop(binPreMul, *=);
|
|
|
|
#undef binop
|
|
|
|
oop binPreDiv(oop lhs, oop rhs)
|
|
{ assert(isInteger(lhs));
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs));
|
|
oop val = *ref;
|
|
if (isInteger(val) && isInteger(rhs)) {
|
|
long l = _integerValue(val), r = _integerValue(rhs);
|
|
if (!r) valueError("/=", "division by zero", rhs);
|
|
l /= r;
|
|
return *ref = newInteger(l);
|
|
}
|
|
double l = floatValue(val, "/=");
|
|
double r = floatValue(rhs, "/=");
|
|
if (!r) valueError("/=", "division by zero", rhs);
|
|
l /= r;
|
|
return *ref = newFloat(l);
|
|
}
|
|
|
|
oop binPreMod(oop lhs, oop rhs)
|
|
{ assert(isInteger(lhs));
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs));
|
|
oop val = *ref;
|
|
if (isInteger(val) && isInteger(rhs)) {
|
|
long l = _integerValue(val), r = _integerValue(rhs);
|
|
if (!r) valueError("%%=", "division by zero", rhs);
|
|
l /= r;
|
|
return *ref = newInteger(l);
|
|
}
|
|
double l = floatValue(val, "%=");
|
|
double r = floatValue(rhs, "%=");
|
|
if (!r) valueError("%%=", "division by zero", rhs);
|
|
return *ref = newFloat(fmod(l, r));
|
|
}
|
|
|
|
#define binop(NAME, OP) \
|
|
oop NAME(oop lhs, oop rhs) \
|
|
{ assert(isInteger(lhs)); \
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs); assert(isInteger(rhs)); \
|
|
oop val = *ref; \
|
|
long l = integerValue(val, #OP); \
|
|
long r = integerValue(rhs, #OP); \
|
|
l OP r; \
|
|
return *ref = newInteger(l); \
|
|
}
|
|
|
|
binop(binPreOr, |=);
|
|
binop(binPreXor, ^=);
|
|
binop(binPreAnd, &=);
|
|
binop(binPreShl, <<=);
|
|
binop(binPreShr, >>=);
|
|
|
|
#undef binop
|
|
|
|
oop binShl(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr)
|
|
return newInteger(_integerValue(l) << _integerValue(r));
|
|
typeError2("<<", "non-integer operand", l, r);
|
|
return 0;
|
|
}
|
|
|
|
oop binShr(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr)
|
|
return newInteger(_integerValue(l) >> _integerValue(r));
|
|
typeError2(">>", "non-integer operand", l, r);
|
|
return 0;
|
|
}
|
|
|
|
oop binAdd(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr) return newInteger(_integerValue(l ) + _integerValue(r ));
|
|
if (Float == tl || Float == tr) return newFloat ( floatValue (l, "+") + floatValue (r, "+"));
|
|
if (String == tl && String == tr) return String_concat(l, r);
|
|
typeError2("+", "illegal operand types", l, r);
|
|
return 0;
|
|
}
|
|
|
|
#define binop(NAME, OP) \
|
|
oop NAME(oop l, oop r) \
|
|
{ \
|
|
int tl = getType(l), tr = getType(r); \
|
|
if (Integer == tl && Integer == tr) return newInteger(_integerValue(l ) OP _integerValue(r )); \
|
|
if (Float == tl || Float == tr) return newFloat ( floatValue(l, #OP) OP floatValue(r, #OP)); \
|
|
typeError2(#OP, "illegal operand types", l, r); \
|
|
return 0; \
|
|
}
|
|
|
|
binop(binSub, -);
|
|
|
|
#undef binop
|
|
|
|
oop binMul(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr) return newInteger(_integerValue(l ) * _integerValue(r ));
|
|
if (Float == tl || Float == tr) return newFloat ( floatValue (l, "*") * floatValue (r, "*"));
|
|
if (String == tl && Integer == tr) return String_repeat(l, _integerValue(r));
|
|
if (Integer == tl && String == tr) return String_repeat(r, _integerValue(l));
|
|
typeError2("*", "illegal operand types", l, r);
|
|
return 0;
|
|
}
|
|
|
|
oop binDiv(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr) {
|
|
long vl = _integerValue(l), vr = _integerValue(r);
|
|
ldiv_t qr = ldiv(vl, vr);
|
|
if (!qr.rem) return newInteger(qr.quot); // division was exact
|
|
return newFloat((double)vl / (double)vr);
|
|
}
|
|
if (Float == tl || Float == tr) return newFloat (floatValue(l, "/") / floatValue(r, "/"));
|
|
typeError2("/", "illegal operand type", l, r);
|
|
return 0;
|
|
}
|
|
|
|
oop binMod(oop l, oop r)
|
|
{
|
|
int tl = getType(l), tr = getType(r);
|
|
if (Integer == tl && Integer == tr) return newInteger( _integerValue(l) % _integerValue(r ) );
|
|
if (Float == tl || Float == tr) return newFloat (fmod(floatValue(l, "%"), floatValue(r, "%")));
|
|
typeError2("/", "illegal operand types", l, r);
|
|
return 0;
|
|
}
|
|
|
|
int isNumber(oop obj) { return isInteger(obj) || isFloat(obj); }
|
|
int isAtom(oop obj) { return nil == obj || isNumber(obj) || isString(obj) || is(Symbol, obj); }
|
|
|
|
oop newBinop(enum binop operation, oop lhs, oop rhs)
|
|
{
|
|
# if FOLDCONST
|
|
if (isAtom(lhs) && isAtom(rhs)) {
|
|
switch (operation) {
|
|
case opLogOr: return newBoolean((lhs != nil) || (rhs != nil));
|
|
case opLogAnd: return newBoolean((lhs != nil) && (rhs != nil));
|
|
case opEq: return binEq (lhs, rhs);
|
|
case opNotEq: return binNotEq(lhs, rhs);
|
|
default: break;
|
|
}
|
|
}
|
|
if (isInteger(lhs) && isInteger(rhs)) {
|
|
switch (operation) {
|
|
case opBitOr: return binBitOr (lhs, rhs);
|
|
case opBitXor: return binBitXor(lhs, rhs);
|
|
case opBitAnd: return binBitAnd(lhs, rhs);
|
|
case opShl: return binShl (lhs, rhs);
|
|
case opShr: return binShr (lhs, rhs);
|
|
default: break;
|
|
}
|
|
}
|
|
if (isNumber(lhs) && isNumber(rhs)) {
|
|
switch (operation) {
|
|
case opLess: return binLess (lhs, rhs);
|
|
case opLessEq: return binLessEq(lhs, rhs);
|
|
case opGrtr: return binGrtr (lhs, rhs);
|
|
case opGrtrEq: return binGrtrEq(lhs, rhs);
|
|
case opAdd: return binAdd (lhs, rhs);
|
|
case opSub: return binSub (lhs, rhs);
|
|
case opMul: return binMul (lhs, rhs);
|
|
case opDiv: return binDiv (lhs, rhs);
|
|
case opMod: return binMod (lhs, rhs);
|
|
default: break;
|
|
}
|
|
}
|
|
# endif
|
|
oop o = new(pBinop);
|
|
Object_put(o, sym_operation, newInteger(operation));
|
|
# if BINOPT
|
|
Object_put(o, prop_function, mkptr(binops[operation]));
|
|
# endif
|
|
Object_push(o, lhs);
|
|
Object_push(o, rhs);
|
|
return o;
|
|
}
|
|
|
|
oop Binop_eval(oop exp, oop env)
|
|
{ assert(_get(exp, Object,isize) == 2);
|
|
oop op = Object_get(exp, sym_operation);
|
|
oop lhs = _get(exp, Object,indexed)[0];
|
|
oop rhs = _get(exp, Object,indexed)[1];
|
|
enum binop code = integerValue(op, "Binop.operation");
|
|
lhs = eval(lhs, env);
|
|
switch (code) {
|
|
case opLogOr: return nil != lhs ? lhs : eval(rhs, env);
|
|
case opLogAnd: return nil == lhs ? lhs : eval(rhs, env);
|
|
default: break;
|
|
}
|
|
rhs = eval(rhs, env);
|
|
# if BINOPT
|
|
return (binop_t)_integerValue(Object_get(exp, prop_function))(lhs, rhs);
|
|
# else
|
|
switch (code) {
|
|
case opLogOr: break;
|
|
case opLogAnd: break;
|
|
case opBitOr: return newInteger(integerValue(lhs, "|") | integerValue(rhs, "|"));
|
|
case opBitXor: return newInteger(integerValue(lhs, "^") ^ integerValue(rhs, "^"));
|
|
case opBitAnd: return newInteger(integerValue(lhs, "&") & integerValue(rhs, "&"));
|
|
case opEq: return newBoolean(cmp(lhs, rhs, "==") == 0);
|
|
case opNotEq: return newBoolean(cmp(lhs, rhs, "!=") != 0);
|
|
case opLess: return newBoolean(cmp(lhs, rhs, "<" ) < 0);
|
|
case opLessEq: return newBoolean(cmp(lhs, rhs, "<=") <= 0);
|
|
case opGrtrEq: return newBoolean(cmp(lhs, rhs, ">=") >= 0);
|
|
case opGrtr: return newBoolean(cmp(lhs, rhs, ">" ) > 0);
|
|
case opShl: return binShl(lhs, rhs);
|
|
case opShr: return binShr(lhs, rhs);
|
|
case opAdd: return binAdd(lhs, rhs);
|
|
case opSub: return binSub(lhs, rhs);
|
|
case opMul: return binMul(lhs, rhs);
|
|
case opDiv: return binDiv(lhs, rhs);
|
|
case opMod: return binMod(lhs, rhs);
|
|
case opPostAdd:
|
|
case opPostDec: { assert(isInteger(lhs)); // ref
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs);
|
|
oop value = *ref; assert(isInteger(rhs)); // delta
|
|
int amount = _integerValue(rhs);
|
|
if (code == opPostDec) amount = -amount;
|
|
switch (getType(value)) {
|
|
case Integer: *ref = newInteger(_integerValue(value) + amount); break;
|
|
case Float: *ref = newFloat (_floatValue (value) + amount); break;
|
|
default: typeError("++", "non-numeric value", value);
|
|
}
|
|
return value;
|
|
}
|
|
case opPreSet: { assert(isInteger(lhs)); // ref
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs);
|
|
return *ref = rhs;
|
|
}
|
|
case opPreOr ... opPreMod: {
|
|
oop *ref = (oop *)(intptr_t)_integerValue(lhs);
|
|
oop val = *ref;
|
|
switch (code) {
|
|
case opPreOr ... opPreShr: {
|
|
long l = integerValue(val, binopNames[code]);
|
|
long r = integerValue(rhs, binopNames[code]);
|
|
switch (code) {
|
|
case opPreOr: l |= r; break;
|
|
case opPreXor: l ^= r; break;
|
|
case opPreAnd: l &= r; break;
|
|
case opPreShl: l <<= r; break;
|
|
case opPreShr: l >>= r; break;
|
|
default: assert(!"this cannot happen");
|
|
}
|
|
return *ref = newInteger(l);
|
|
}
|
|
case opPreAdd ... opPreMod: {
|
|
if (isInteger(val) && isInteger(rhs)) {
|
|
long l = _integerValue(val), r = _integerValue(rhs);
|
|
switch (code) {
|
|
case opPreAdd: l += r; break;
|
|
case opPreSub: l -= r; break;
|
|
case opPreMul: l *= r; break;
|
|
case opPreDiv:
|
|
if (!r) valueError("/=", "division by zero", rhs);
|
|
l /= r;
|
|
break;
|
|
case opPreMod:
|
|
if (!r) valueError("%=", "division by zero", rhs);
|
|
l %= r;
|
|
break;
|
|
default: assert(!"this cannot happen");
|
|
}
|
|
return *ref = newInteger(l);
|
|
}
|
|
double l = floatValue(val, binopNames[code]);
|
|
double r = floatValue(rhs, binopNames[code]);
|
|
switch (code) {
|
|
case opPreAdd: l += r; break;
|
|
case opPreSub: l -= r; break;
|
|
case opPreMul: l *= r; break;
|
|
case opPreDiv:
|
|
if (!r) valueError("/=", "division by zero", rhs);
|
|
l /= r;
|
|
break;
|
|
case opPreMod:
|
|
if (!r) valueError("%=", "division by zero", rhs);
|
|
l = fmod(l, r);
|
|
break;
|
|
default: assert(!"this cannot happen");
|
|
}
|
|
return *ref = newFloat(l);
|
|
}
|
|
default: assert(!"this cannot happen");
|
|
}
|
|
}
|
|
}
|
|
fatal("illegal binary operation %d", code);
|
|
return 0;
|
|
# endif
|
|
}
|
|
|
|
void Binop_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
if (_getDelegate(exp) == pBinop) { assert(_get(exp, Object,isize) == 2);
|
|
oop op = Object_get(exp, sym_operation);
|
|
oop lhs = _get(exp, Object,indexed)[0];
|
|
oop rhs = _get(exp, Object,indexed)[1];
|
|
codeOn(str, lhs, 0);
|
|
enum binop code = integerValue(op, "Binop.operation");
|
|
assert(0 <= code && code <= indexableSize(binopNames));
|
|
String_format(str, " %s ", binopNames[code]);
|
|
codeOn(str, rhs, 0);
|
|
}
|
|
else {
|
|
printOn(str, exp, 0);
|
|
}
|
|
}
|
|
|
|
#define doUnyops(_) \
|
|
_(opNot, !) _(opCom, ~) _(opNeg, -) _(opQuasiquote, `) _(opUnquote, @)
|
|
|
|
#define defineUnyop(NAME, OP_) NAME,
|
|
|
|
enum unyop {
|
|
doUnyops(defineUnyop)
|
|
};
|
|
|
|
#undef defineUnyop
|
|
|
|
#define nameUnyop(NAME, OP) #OP,
|
|
|
|
char *unyopNames[] = {
|
|
doUnyops(nameUnyop)
|
|
};
|
|
|
|
#undef nameUnyop
|
|
|
|
oop quasiclone(oop exp, oop env)
|
|
{
|
|
if (is(Object, exp)) {
|
|
if (pUnyop == _getDelegate(exp)) {
|
|
oop op = Object_get(exp, sym_operation);
|
|
oop value = _get(exp, Object,indexed)[0];
|
|
enum unyop code = integerValue(op, "Unyop.operation");
|
|
if (code == opUnquote) return eval(value, env);
|
|
}
|
|
oop clone = new(_getDelegate(exp));
|
|
oop *indexed = _get(exp, Object,indexed);
|
|
int isize = _get(exp, Object,isize);
|
|
for (int i = 0; i < isize; ++i)
|
|
Object_push(clone, quasiclone(indexed[i], env));
|
|
struct property *kvs = _get(exp, Object,properties);
|
|
int psize = _get(exp, Object,psize);
|
|
for (int i = 0; i < psize; ++i)
|
|
if (kvs[i].key != prop_delegate)
|
|
Object_put(clone, kvs[i].key, quasiclone(kvs[i].val, env));
|
|
oop delegate = _getDelegate(exp);
|
|
if (nil != delegate) // always shallow copied
|
|
Object_put(clone, prop_delegate, delegate);
|
|
return clone;
|
|
}
|
|
return exp;
|
|
}
|
|
|
|
oop neg(oop n)
|
|
{
|
|
int tn = getType(n);
|
|
switch (tn) {
|
|
case Integer: return newInteger(-_integerValue(n));
|
|
case Float: return newFloat (-_floatValue (n));
|
|
default: break;
|
|
}
|
|
typeError("-", "non-numeric operand", n);
|
|
return 0;
|
|
}
|
|
|
|
oop com(oop n)
|
|
{
|
|
int tn = getType(n);
|
|
switch (tn) {
|
|
case Integer: return newInteger(~_integerValue(n));
|
|
default: break;
|
|
}
|
|
typeError("~", "non-numeric operand", n);
|
|
return 0;
|
|
}
|
|
|
|
oop newUnyop(int operation, oop value)
|
|
{
|
|
# if FOLDCONST
|
|
if (operation == opNot && isAtom (value)) return newBoolean(nil == value);
|
|
if (operation == opNeg && isNumber (value)) return neg(value);
|
|
if (operation == opCom && isInteger(value)) return com(value);
|
|
# endif
|
|
oop o = new(pUnyop);
|
|
Object_put(o, sym_operation, newInteger(operation));
|
|
Object_push(o, value);
|
|
return o;
|
|
}
|
|
|
|
oop Unyop_eval(oop exp, oop env)
|
|
{ assert(_get(exp, Object,isize) == 1);
|
|
oop op = Object_get(exp, sym_operation);
|
|
oop value = _get(exp, Object,indexed)[0];
|
|
enum unyop code = integerValue(op, "Unyop.operation");
|
|
if (code == opQuasiquote) return quasiclone(value, env);
|
|
if (code == opUnquote ) syntaxError("@ outside quasiquotation");
|
|
value = eval(value, env);
|
|
switch (code) {
|
|
case opNot: return newBoolean(value == nil);
|
|
case opNeg: return neg(value);
|
|
case opCom: return com(value);
|
|
default: break;
|
|
}
|
|
fatal("illegal unary operation %d", code);
|
|
return 0;
|
|
}
|
|
|
|
void Unyop_codeOn(oop exp, oop str, oop env)
|
|
{ assert(_get(exp, Object,isize) == 1);
|
|
oop op = Object_get(exp, sym_operation);
|
|
oop value = _get(exp, Object,indexed)[0];
|
|
enum unyop code = integerValue(op, "Unyop.operation");
|
|
assert(0 <= (int)code && (int)code <= indexableSize(unyopNames));
|
|
String_appendAll(str, unyopNames[code]);
|
|
codeOn(str, value, 0);
|
|
}
|
|
|
|
oop newIf(oop condition, oop consequent, oop alternate)
|
|
{
|
|
# if FOLDCONST
|
|
if (isAtom(condition)) return nil == condition ? alternate : consequent;
|
|
# endif
|
|
oop o = new(pIf);
|
|
Object_put(o, sym_condition, condition );
|
|
Object_put(o, sym_consequent, consequent);
|
|
Object_put(o, sym_alternate, alternate );
|
|
return o;
|
|
}
|
|
|
|
oop If_eval(oop exp, oop env)
|
|
{
|
|
oop condition = eval(Object_get(exp, sym_condition ), env);
|
|
oop consequent = Object_get(exp, sym_consequent) ;
|
|
oop alternate = Object_get(exp, sym_alternate ) ;
|
|
return eval(nil != condition ? consequent : alternate, env);
|
|
}
|
|
|
|
void If_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop condition = Object_get(exp, sym_condition );
|
|
oop consequent = Object_get(exp, sym_consequent);
|
|
oop alternate = Object_get(exp, sym_alternate );
|
|
String_appendAll(str, "if (");
|
|
codeOn(str, condition, 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, consequent, 0);
|
|
if (nil != alternate) {
|
|
String_appendAll(str, " else ");
|
|
codeOn(str, alternate, 0);
|
|
}
|
|
}
|
|
|
|
#define _PASTE(A, B) A##B
|
|
#define PASTE(A, B) _PASTE(A,B)
|
|
|
|
#if NONLOCAL
|
|
|
|
# define LOOP() \
|
|
PASTE(continue,__LINE__): \
|
|
switch (nlrPush()) { \
|
|
case NLR_CONTINUE: nlrPop(); goto PASTE(continue, __LINE__); \
|
|
case NLR_BREAK: return nlrPop(); \
|
|
case NLR_RETURN: nlrReturn(nlrPop(), NLR_RETURN); \
|
|
case NLR_RAISE: nlrReturn(nlrPop(), NLR_RAISE ); \
|
|
}
|
|
|
|
# define DONE() nlrPop()
|
|
#else
|
|
# define LOOP()
|
|
# define DONE()
|
|
#endif
|
|
|
|
oop newWhile(oop condition, oop body)
|
|
{
|
|
oop o = new(pWhile);
|
|
Object_put(o, sym_condition, condition );
|
|
Object_put(o, sym_body, body );
|
|
return o;
|
|
}
|
|
|
|
oop While_eval(oop exp, oop env)
|
|
{
|
|
oop condition = Object_get(exp, sym_condition);
|
|
oop body = Object_get(exp, sym_body );
|
|
oop result = nil;
|
|
LOOP();
|
|
while (nil != eval(condition, env)) result = eval(body, env);
|
|
DONE();
|
|
return result;
|
|
}
|
|
|
|
void While_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop condition = Object_get(exp, sym_condition);
|
|
oop body = Object_get(exp, sym_body );
|
|
String_appendAll(str, "while (");
|
|
codeOn(str, condition, 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, body, 0);
|
|
if (pBlock != _getDelegate(body)) String_appendAll(str, ";");
|
|
}
|
|
|
|
oop newBlock(oop body)
|
|
{
|
|
oop o = new(pBlock);
|
|
Object_put(o, sym_body, body);
|
|
return o;
|
|
}
|
|
|
|
oop Block_eval(oop exp, oop env)
|
|
{
|
|
oop body = Object_get(exp, sym_body);
|
|
oop *indexed = _get(body, Object,indexed);
|
|
int size = _get(body, Object,isize);
|
|
oop result = nil;
|
|
oop env2 = new(pObject);
|
|
_setDelegate(env2, env);
|
|
for (int i = 0; i < size; ++i) result = eval(indexed[i], env2);
|
|
return result;
|
|
}
|
|
|
|
void Block_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
codeBlockOn(str, Object_get(exp, sym_body));
|
|
}
|
|
|
|
oop newFor(oop initialise, oop condition, oop update, oop body)
|
|
{
|
|
oop o = new(pFor);
|
|
Object_put(o, sym_initialise, initialise);
|
|
Object_put(o, sym_condition, condition);
|
|
Object_put(o, sym_update, update);
|
|
Object_put(o, sym_body, body);
|
|
return o;
|
|
}
|
|
|
|
oop For_eval(oop exp, oop env)
|
|
{
|
|
oop initialise = Object_get(exp, sym_initialise);
|
|
oop condition = Object_get(exp, sym_condition);
|
|
oop update = Object_get(exp, sym_update);
|
|
oop body = Object_get(exp, sym_body);
|
|
oop env2 = new(pObject);
|
|
_setDelegate(env2, env);
|
|
oop result = eval(initialise, env2);
|
|
int n = 0;
|
|
LOOP();
|
|
if (n) goto doContinue;
|
|
n = 1;
|
|
for (;;) {
|
|
if (nil == eval(condition, env2)) break;
|
|
result = eval(body, env2);
|
|
doContinue:
|
|
eval(update, env2);
|
|
}
|
|
DONE();
|
|
return result;
|
|
}
|
|
|
|
void For_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop initialise = Object_get(exp, sym_initialise);
|
|
oop condition = Object_get(exp, sym_condition);
|
|
oop update = Object_get(exp, sym_update);
|
|
oop body = Object_get(exp, sym_body);
|
|
String_appendAll(str, "for (");
|
|
codeOn(str, initialise, 0);
|
|
String_appendAll(str, "; ");
|
|
codeOn(str, condition, 0);
|
|
String_appendAll(str, "; ");
|
|
codeOn(str, update, 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, body, 0);
|
|
}
|
|
|
|
oop newForIn(oop identifier, oop expression, oop body)
|
|
{
|
|
oop o = new(pForIn);
|
|
Object_put(o, sym_identifier, identifier);
|
|
Object_put(o, sym_expression, expression);
|
|
Object_put(o, sym_body, body);
|
|
return o;
|
|
}
|
|
|
|
oop ForIn_eval(oop exp, oop env)
|
|
{
|
|
oop identifier = Object_get(exp, sym_identifier);
|
|
oop expression = Object_get(exp, sym_expression);
|
|
oop body = Object_get(exp, sym_body);
|
|
oop result = nil;
|
|
oop vals = eval(expression, env);
|
|
oop env2 = new(pObject);
|
|
_setDelegate(env2, env);
|
|
if (isInteger(vals)) {
|
|
long i = -1, limit = _integerValue(vals);
|
|
LOOP();
|
|
while (++i < limit) {
|
|
Object_put(env2, identifier, newInteger(i));
|
|
result = eval(body, env2);
|
|
}
|
|
DONE();
|
|
return result;
|
|
}
|
|
if (is(String, vals)) {
|
|
int len = _get(vals, String,length);
|
|
char *val = _get(vals, String,value);
|
|
int i = -1;
|
|
LOOP();
|
|
while (++i < len) {
|
|
Object_put(env2, identifier, newInteger(val[i]));
|
|
result = eval(body, env2);
|
|
}
|
|
DONE();
|
|
return result;
|
|
}
|
|
if (!is(Object, vals)) typeError("for", "non-iterable value", vals);
|
|
oop *indexed = _get(vals, Object,indexed);
|
|
int size = _get(vals, Object,isize);
|
|
int i = -1;
|
|
LOOP();
|
|
while (++i < size) {
|
|
Object_put(env2, identifier, indexed[i]);
|
|
result = eval(body, env2);
|
|
}
|
|
DONE();
|
|
return result;
|
|
}
|
|
|
|
void ForIn_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop identifier = Object_get(exp, sym_identifier);
|
|
oop expression = Object_get(exp, sym_expression);
|
|
oop body = Object_get(exp, sym_body);
|
|
String_appendAll(str, "for (");
|
|
printOn(str, identifier, 0);
|
|
String_appendAll(str, " in ");
|
|
codeOn(str, expression, 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, body, 0);
|
|
}
|
|
|
|
oop newForFromTo(oop identifier, oop first, oop last, oop body)
|
|
{
|
|
oop o = new(pForFromTo);
|
|
Object_put(o, sym_identifier, identifier);
|
|
Object_put(o, sym_first, first);
|
|
Object_put(o, sym_last, last);
|
|
Object_put(o, sym_body, body);
|
|
return o;
|
|
}
|
|
|
|
oop ForFromTo_eval(oop exp, oop env)
|
|
{
|
|
oop identifier = Object_get(exp, sym_identifier);
|
|
oop first = eval(Object_get(exp, sym_first), env);
|
|
oop last = eval(Object_get(exp, sym_last ), env);
|
|
oop body = Object_get(exp, sym_body);
|
|
oop env2 = new(pObject);
|
|
_setDelegate(env2, env);
|
|
long start = integerValue(first, "for");
|
|
long stop = integerValue(last, "for");
|
|
long step = start < stop ? 1 : -1;
|
|
oop result = nil;
|
|
start -= step;
|
|
LOOP();
|
|
for (;;) {
|
|
start += step;
|
|
Object_put(env2, identifier, newInteger(start));
|
|
result = eval(body, env2);
|
|
if (start == stop) break;
|
|
}
|
|
DONE();
|
|
return result;
|
|
}
|
|
|
|
void ForFromTo_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop identifier = Object_get(exp, sym_identifier);
|
|
oop first = Object_get(exp, sym_first);
|
|
oop last = Object_get(exp, sym_last);
|
|
oop body = Object_get(exp, sym_body);
|
|
String_appendAll(str, "for (");
|
|
printOn(str, identifier, 0);
|
|
String_appendAll(str, " from ");
|
|
codeOn(str, first, 0);
|
|
String_appendAll(str, " to ");
|
|
codeOn(str, last, 0);
|
|
String_appendAll(str, ") ");
|
|
codeOn(str, body, 0);
|
|
}
|
|
|
|
#undef LOOP
|
|
#undef DONE
|
|
|
|
oop newLiteral(oop object)
|
|
{
|
|
oop o = new(pLiteral);
|
|
Object_put(o, sym_object, object);
|
|
return o;
|
|
}
|
|
|
|
#if DELOPT
|
|
|
|
oop Literal_eval(oop exp, oop env)
|
|
{
|
|
oop object = Object_get(exp, sym_object);
|
|
// if (is(String, object)) return newStringLen(_get(object, String,value), _get(object, String,length));
|
|
oop clone = new(pObject);
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
for (int i = 0; i < isize; ++i)
|
|
Object_push(clone, eval(indexed[i], env));
|
|
struct property *kvs = _get(object, Object,properties);
|
|
int psize = _get(object, Object,psize);
|
|
for (int i = 0; i < psize; ++i)
|
|
Object_put(clone, kvs[i].key, eval(kvs[i].val, env));
|
|
return clone;
|
|
}
|
|
|
|
#else // !DELOPT
|
|
|
|
oop Literal_eval(oop exp, oop env)
|
|
{
|
|
oop object = Object_get(exp, sym_object);
|
|
oop clone = new(pObject);
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
for (int i = 0; i < isize; ++i)
|
|
Object_push(clone, eval(indexed[i], env));
|
|
struct property *kvs = _get(object, Object,properties);
|
|
int psize = _get(object, Object,psize);
|
|
for (int i = 0; i < psize; ++i)
|
|
Object_put(clone, kvs[i].key, eval(kvs[i].val, env));
|
|
return clone;
|
|
}
|
|
|
|
#endif // !DELOPT
|
|
|
|
void Literal_codeOn(oop exp, oop str, oop env)
|
|
{
|
|
oop object = Object_get(exp, sym_object);
|
|
codeOn(str, object, 0);
|
|
# if 0
|
|
oop *indexed = _get(object, Object,indexed);
|
|
int isize = _get(object, Object,isize);
|
|
String_appendAll(str, "[");
|
|
int i;
|
|
for (i = 0; i < isize; ++i) {
|
|
if (i) String_appendAll(str, ", ");
|
|
codeOn(str, indexed[i], 0);
|
|
}
|
|
struct property *kvs = _get(object, Object,properties);
|
|
int psize = _get(object, Object,psize);
|
|
for (int j = 0; j < psize; ++j) {
|
|
if (i++) String_appendAll(str, ", ");
|
|
codeOn(str, kvs[j].key, 0);
|
|
String_appendAll(str, ": ");
|
|
codeOn(str, kvs[j].val, 0);
|
|
}
|
|
String_appendAll(str, "]");
|
|
# endif
|
|
}
|
|
|
|
oop lvalue(oop rval)
|
|
{
|
|
if (!is(Object,rval)) valueError("=", "non-assignable value", rval);
|
|
oop kind = _getDelegate(rval);
|
|
if (kind == pGetVar ) kind = pRefVar;
|
|
else if (kind == pGetProp ) kind = pRefProp;
|
|
else if (kind == pGetArray ) kind = pRefArray;
|
|
else if (kind == pGetLocal ) kind = pRefLocal;
|
|
else if (kind == pGetGlobal) kind = pRefGlobal;
|
|
else valueError("=", "non-assignable value", rval);
|
|
_setDelegate(rval, kind);
|
|
return rval;
|
|
}
|
|
|
|
oop assign(oop rval, oop value)
|
|
{
|
|
if (!is(Object,rval)) valueError("=", "non-assignable value", rval);
|
|
oop kind = _getDelegate(rval);
|
|
if (kind == pGetVar ) kind = pSetVar;
|
|
else if (kind == pGetProp ) kind = pSetProp;
|
|
else if (kind == pGetArray) kind = pSetArray;
|
|
else valueError("=", "non-assignable value", rval);
|
|
_setDelegate(rval, kind);
|
|
Object_put(rval, sym_value, value);
|
|
return rval;
|
|
}
|
|
|
|
void expected(char *what, char *where)
|
|
{
|
|
fatal("syntax error: %s expected near: %s", what, where);
|
|
}
|
|
|
|
%}
|
|
|
|
start = - ( s:stmt { yysval = s }
|
|
| !. { yysval = 0 }
|
|
| < (!EOL .)* > { syntaxError(yytext) }
|
|
)
|
|
|
|
stmt = WHILE LPAREN c:expr RPAREN s:stmt { $$ = newWhile(c, s) }
|
|
| IF LPAREN c:expr RPAREN s:stmt
|
|
( ELSE t:stmt { $$ = newIf(c, s, t ) }
|
|
| { $$ = newIf(c, s, nil) }
|
|
)
|
|
| CONT EOS { $$ = newContinue() }
|
|
| BREAK e:expr EOS { $$ = newBreak(e) }
|
|
| BREAK EOS { $$ = newBreak(nil) }
|
|
| RETURN e:expr EOS { $$ = newReturn(e) }
|
|
| RETURN EOS { $$ = newReturn(nil) }
|
|
| FOR LPAREN i:id IN e:expr RPAREN
|
|
s:stmt { $$ = newForIn(i, e, s) }
|
|
| FOR LPAREN i:id FROM a:expr
|
|
TO b:expr RPAREN s:stmt { $$ = newForFromTo(i, a, b, s) }
|
|
| FOR LPAREN i:expr SEMI c:expr SEMI
|
|
u:expr RPAREN s:stmt { $$ = newFor(i, c, u, s) }
|
|
| TRY t:stmt
|
|
( CATCH LPAREN i:id RPAREN c:stmt { $$ = newTryCatch(t, i, c) }
|
|
| ENSURE e:stmt { $$ = newTryEnsure(t, e) }
|
|
)
|
|
| RAISE e:expr EOS { $$ = newRaise(e) }
|
|
| LOCAL i:id p:params b:block { $$ = newSetLocal (i, newLambda(p, b, nil, i)) }
|
|
| GLOBAL i:id p:params b:block { $$ = newSetGlobal(i, newLambda(p, b, nil, i)) }
|
|
| i:id p:params b:block { $$ = newSetVar (i, newLambda(p, b, nil, i)) }
|
|
| v:proto DOT i:id p:params b:block { $$ = newSetProp(v, i, newLambda(p, b, v, i)) }
|
|
| b:block { $$ = newBlock(b) }
|
|
| e:expr EOS { $$ = e }
|
|
|
|
proto = v:var ( DOT j:id !LPAREN { v = newGetProp(v, j) }
|
|
)* { $$ = v }
|
|
|
|
EOS = SEMI+ | &RBRACE | &ELSE | &CATCH
|
|
|
|
expr = LOCAL i:id ASSIGN e:expr { $$ = newSetLocal (i, e) }
|
|
| GLOBAL i:id ASSIGN e:expr { $$ = newSetGlobal(i, e) }
|
|
| i:id ASSIGN e:expr { $$ = newSetVar (i, e) }
|
|
| l:logor ( ASSIGN r:expr { l = assign(l, r) }
|
|
| PLUSEQ r:expr { l = newBinop(opPreAdd, lvalue(l), r) }
|
|
| MINUSEQ r:expr { l = newBinop(opPreSub, lvalue(l), r) }
|
|
| STAREQ r:expr { l = newBinop(opPreMul, lvalue(l), r) }
|
|
| SLASHEQ r:expr { l = newBinop(opPreDiv, lvalue(l), r) }
|
|
| PCENTEQ r:expr { l = newBinop(opPreMod, lvalue(l), r) }
|
|
| SHLEQ r:expr { l = newBinop(opPreShl, lvalue(l), r) }
|
|
| SHREQ r:expr { l = newBinop(opPreShr, lvalue(l), r) }
|
|
| ANDEQ r:expr { l = newBinop(opPreAnd, lvalue(l), r) }
|
|
| XOREQ r:expr { l = newBinop(opPreXor, lvalue(l), r) }
|
|
| OREQ r:expr { l = newBinop(opPreOr, lvalue(l), r) }
|
|
)? { $$ = l }
|
|
|
|
logor = l:logand ( BARBAR r:logand { l = newBinop(opLogOr, l, r) }
|
|
)* { $$ = l }
|
|
|
|
logand = l:bitor ( ANDAND r:bitor { l = newBinop(opLogAnd, l, r) }
|
|
)* { $$ = l }
|
|
|
|
bitor = l:bitxor ( OR r:bitxor { l = newBinop(opBitOr, l, r) }
|
|
)* { $$ = l }
|
|
|
|
bitxor = l:bitand ( XOR r:bitand { l = newBinop(opBitXor, l, r) }
|
|
)* { $$ = l }
|
|
|
|
bitand = l:eq ( AND r:eq { l = newBinop(opBitAnd, l, r) }
|
|
)* { $$ = l }
|
|
|
|
eq = l:ineq ( EQ r:ineq { l = newBinop(opEq, l, r) }
|
|
| NOTEQ r:ineq { l = newBinop(opNotEq, l, r) }
|
|
)* { $$ = l }
|
|
|
|
ineq = l:shift ( LESS r:shift { l = newBinop(opLess, l, r) }
|
|
| LESSEQ r:shift { l = newBinop(opLessEq, l, r) }
|
|
| GRTREQ r:shift { l = newBinop(opGrtrEq, l, r) }
|
|
| GRTR r:shift { l = newBinop(opGrtr, l, r) }
|
|
)* { $$ = l }
|
|
|
|
shift = l:sum ( SHL r:sum { l = newBinop(opShl, l, r) }
|
|
| SHR r:sum { l = newBinop(opShr, l, r) }
|
|
)* { $$ = l }
|
|
|
|
sum = l:prod ( PLUS r:prod { l = newBinop(opAdd, l, r) }
|
|
| MINUS r:prod { l = newBinop(opSub, l, r) }
|
|
)* { $$ = l }
|
|
|
|
prod = l:prefix ( STAR r:prefix { l = newBinop(opMul, l, r) }
|
|
| SLASH r:prefix { l = newBinop(opDiv, l, r) }
|
|
| PCENT r:prefix { l = newBinop(opMod, l, r) }
|
|
)* { $$ = l }
|
|
|
|
prefix = PPLUS p:prefix { $$ = newBinop(opPreAdd, lvalue(p), newInteger(1)) }
|
|
| MMINUS p:prefix { $$ = newBinop(opPreSub, lvalue(p), newInteger(1)) }
|
|
| PLING p:prefix { $$ = newUnyop(opNot, p) }
|
|
| MINUS p:prefix { $$ = newUnyop(opNeg, p) }
|
|
| TILDE p:prefix { $$ = newUnyop(opCom, p) }
|
|
| BQUOTE s:stmt { $$ = newUnyop(opQuasiquote, s) }
|
|
| COMMAT e:expr { $$ = newUnyop(opUnquote, e) }
|
|
| postfix
|
|
|
|
postfix = SUPER DOT i:id a:args { $$ = newSuper(i, a) }
|
|
| p:primary
|
|
( LBRAK
|
|
( COLON ( RBRAK { p = newGetSlice(p, nil, nil) }
|
|
| e:xexpr RBRAK { p = newGetSlice(p, nil, e) }
|
|
)
|
|
| s:xexpr ( COLON ( RBRAK { p = newGetSlice(p, s, nil) }
|
|
| e:xexpr RBRAK { p = newGetSlice(p, s, e) }
|
|
)
|
|
| RBRAK { p = newGetArray(p, s) }
|
|
)
|
|
)
|
|
| DOT i:id ( a:args !LBRACE { p = newInvoke(p, i, a) }
|
|
| { p = newGetProp(p, i) }
|
|
)
|
|
| a:args !LBRACE { p = newApply(p, a) }
|
|
)*
|
|
( PPLUS { p = newBinop(opPostAdd, lvalue(p), newInteger( 1)) }
|
|
| MMINUS { p = newBinop(opPostAdd, lvalue(p), newInteger(-1)) }
|
|
)? { $$ = p }
|
|
|
|
args = LPAREN a:mkobj
|
|
( RPAREN
|
|
| ( k:id COLON e:xexpr { Object_put(a, k, e) }
|
|
| e:xexpr { Object_push(a, e) }
|
|
)
|
|
( COMMA ( k:id COLON e:xexpr { Object_put(a, k, e) }
|
|
| e:xexpr { Object_push(a, e) }
|
|
) )* RPAREN ) { $$ = a }
|
|
|
|
params = LPAREN p:mkobj
|
|
( RPAREN
|
|
| i:id ( COLON e:expr { Object_put(p, i, e) }
|
|
| { Object_push(p, i) }
|
|
)
|
|
( COMMA i:id ( COLON e:expr { Object_put(p, i, e) }
|
|
| { Object_push(p, i) }
|
|
)
|
|
)* RPAREN ) { $$ = p }
|
|
|
|
mkobj = { $$ = new(pObject) }
|
|
|
|
primary = nil | number | string | symbol | var | lambda | subexpr | literal # | regex
|
|
|
|
lambda = p:params b:block { $$ = newLambda(p, b, nil, nil) }
|
|
|
|
subexpr = LPAREN e:expr RPAREN { $$ = e }
|
|
| b:block { $$ = newBlock(b) }
|
|
|
|
literal = LBRAK o:mkobj
|
|
( RBRAK
|
|
| ( ( i:id COLON e:expr { Object_put(o, i, e) }
|
|
| e:expr { Object_push(o, e) }
|
|
) ( COMMA ( i:id COLON e:expr { Object_put(o, i, e) }
|
|
| e:expr { Object_push(o, e) }
|
|
) )* )? RBRAK ) { $$ = newLiteral(o) }
|
|
|
|
block = LBRACE b:mkobj
|
|
( e:stmt { Object_push(b, e) }
|
|
)* ( RBRACE { $$ = b }
|
|
| error @{ expected("statement or \x7D", yytext) }
|
|
)
|
|
|
|
nil = NIL { $$ = nil }
|
|
|
|
number = "-" n:unsign { $$ = neg(n) }
|
|
| "+" n:number { $$ = n }
|
|
| n:unsign { $$ = n }
|
|
|
|
unsign = < DIGIT+ '.' DIGIT* EXP? > - { $$ = newFloat(strtod(yytext, 0)) }
|
|
| < DIGIT* '.' DIGIT+ EXP? > - { $$ = newFloat(strtod(yytext, 0)) }
|
|
| "0" [bB] < BIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 2)) }
|
|
| "0" [xX] < HIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 16)) }
|
|
| "0" < OIGIT* > - { $$ = newInteger(strtol(yytext, 0, 8)) }
|
|
| < DIGIT+ > - { $$ = newInteger(strtol(yytext, 0, 10)) }
|
|
| "'" < char > "'" - { $$ = newInteger(_get(newStringUnescaped(yytext), String,value)[0]) }
|
|
|
|
string = '"' < ( !'"' char )* > '"' - { $$ = newStringUnescaped(yytext) }
|
|
|
|
char = "\\" ( ["'\\abfnrtv]
|
|
| [xX] HIGIT*
|
|
| [0-7][0-7]?[0-7]?
|
|
)
|
|
| .
|
|
|
|
symbol = HASH i:id { $$ = i }
|
|
|
|
var = LOCAL i:id { $$ = newGetLocal (i) }
|
|
| GLOBAL i:id { $$ = newGetGlobal(i) }
|
|
| i:id { $$ = newGetVar (i) }
|
|
|
|
id = < LETTER ALNUM* > - { $$ = intern(yytext) }
|
|
|
|
# regex = SLASH a:alts SLASH { $$ = a }
|
|
|
|
# alts = s:seq ( OR t:seq { s = Alt_append(t) }
|
|
# )* { $$ = s }
|
|
|
|
# seq = p:pre ( q:pre { s = Seq_append(t) }
|
|
# )* { $$ = s }
|
|
|
|
# elt = action | pre
|
|
|
|
# action = b:block { $$ = newAction(b) }
|
|
|
|
# pre = PLING p:pre { $$ = newNot(p) }
|
|
# | AND p:pre { $$ = newAnd(p) }
|
|
# | post
|
|
|
|
# post = a:atom ( STAR { a = newMany(a) }
|
|
# | PLUS { a = newMore(a) }
|
|
# | QUERY { a = newMore(a) }
|
|
# )? { $$ = a }
|
|
|
|
# atom = DOT { $$ = newDot() }
|
|
# | "[" ( !"]" "\\"? . )* "]" - { $$ = newClass(yytext) }
|
|
# | '"' xxxxxx
|
|
|
|
# class = LBRAK
|
|
|
|
BIGIT = [0-1]
|
|
OIGIT = [0-7]
|
|
DIGIT = [0-9]
|
|
HIGIT = [0-9A-Fa-f]
|
|
LETTER = [A-Za-z_$?]
|
|
ALNUM = LETTER | DIGIT
|
|
SIGN = [-+]
|
|
EXP = [eE] SIGN DIGIT+
|
|
|
|
- = SPACE*
|
|
|
|
SPACE = [ \t] | EOL | SLC | MLC
|
|
EOL = [\n\r] { ++lineno }
|
|
SLC = "//" (!EOL .)*
|
|
MLC = "/*" ( MLC | !"*/" (EOL | .))* "*/" -
|
|
|
|
NIL = "nil" !ALNUM -
|
|
WHILE = "while" !ALNUM -
|
|
IF = "if" !ALNUM -
|
|
ELSE = "else" !ALNUM -
|
|
FOR = "for" !ALNUM -
|
|
IN = "in" !ALNUM -
|
|
FROM = "from" !ALNUM -
|
|
TO = "to" !ALNUM -
|
|
CONT = "continue" !ALNUM -
|
|
BREAK = "break" !ALNUM -
|
|
RETURN = "return" !ALNUM -
|
|
TRY = "try" !ALNUM -
|
|
CATCH = "catch" !ALNUM -
|
|
ENSURE = "ensure" !ALNUM -
|
|
RAISE = "raise" !ALNUM -
|
|
GLOBAL = "global" !ALNUM -
|
|
LOCAL = "local" !ALNUM -
|
|
SUPER = "super" !ALNUM -
|
|
|
|
BQUOTE = "`" -
|
|
COMMAT = "@" -
|
|
HASH = "#" -
|
|
SEMI = ";" -
|
|
ASSIGN = "=" ![=] -
|
|
COMMA = "," -
|
|
COLON = ":" ![:] -
|
|
LPAREN = "(" -
|
|
RPAREN = ")" -
|
|
LBRAK = "[" -
|
|
RBRAK = "]" -
|
|
LBRACE = "{" -
|
|
RBRACE = "}" -
|
|
BARBAR = "||" ![=] -
|
|
ANDAND = "&&" ![=] -
|
|
OR = "|" ![|=] -
|
|
OREQ = "|=" -
|
|
XOR = "^" ![=] -
|
|
XOREQ = "^=" -
|
|
AND = "&" ![&=] -
|
|
ANDEQ = "&=" -
|
|
EQ = "==" -
|
|
NOTEQ = "!=" -
|
|
LESS = "<" ![<=] -
|
|
LESSEQ = "<=" -
|
|
GRTREQ = ">=" -
|
|
GRTR = ">" ![=] -
|
|
SHL = "<<" ![=] -
|
|
SHLEQ = "<<=" -
|
|
SHR = ">>" ![=] -
|
|
SHREQ = ">>=" -
|
|
PLUS = "+" ![+=] -
|
|
PLUSEQ = "+=" -
|
|
PPLUS = "++" -
|
|
MINUS = "-" ![-=] -
|
|
MINUSEQ = "-=" -
|
|
MMINUS = "--" -
|
|
STAR = "*" ![=] -
|
|
STAREQ = "*=" -
|
|
SLASH = "/" ![/=] -
|
|
SLASHEQ = "/=" -
|
|
PCENT = "%" ![=] -
|
|
PCENTEQ = "%=" -
|
|
DOT = "." -
|
|
PLING = "!" ![=] -
|
|
TILDE = "~" -
|
|
|
|
error = - < (!EOL .)* >
|
|
|
|
xexpr = expr | error @{ expected("expression", yytext) }
|
|
|
|
%%
|
|
;
|
|
|
|
#if PROFILE
|
|
|
|
oop *profiles = 0;
|
|
int nprofiles = 0;
|
|
|
|
oop profileInit(oop function)
|
|
{
|
|
profiles = xrealloc(profiles, sizeof(*profiles) * (nprofiles + 1));
|
|
oop p = profiles[nprofiles++] = new(pObject);
|
|
Object_put(p, sym_function, function);
|
|
Object_put(p, sym_count, newInteger(0));
|
|
Object_put(p, sym_stamp, newInteger(0));
|
|
Object_put(p, sym_time, newInteger(0));
|
|
return p;
|
|
}
|
|
|
|
#include <time.h>
|
|
|
|
long uclock(void)
|
|
{
|
|
struct rusage ru;
|
|
getrusage(RUSAGE_SELF, &ru);
|
|
return ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec;
|
|
}
|
|
|
|
void profileTick(oop p)
|
|
{
|
|
oop *ref = Object_refLocal(p, sym_count); if (!ref) fatal("profile data lost: count");
|
|
long count = _integerValue(*ref);
|
|
*ref = newInteger(count + 1);
|
|
ref = Object_refLocal(p, sym_stamp); if (!ref) fatal("profile data lost: stamp");
|
|
//*ref = newInteger(clock());
|
|
*ref = newInteger(uclock());
|
|
}
|
|
|
|
void profileTock(oop p)
|
|
{
|
|
//long ticks = clock() - _integerValue(Object_getLocal(p, sym_stamp));
|
|
long ticks = uclock() - _integerValue(Object_getLocal(p, sym_stamp));
|
|
oop *timep = Object_refLocal(p, sym_time ); if (!timep) fatal("profile data lost: time");
|
|
ticks += _integerValue(*timep);
|
|
*timep = newInteger(ticks);
|
|
}
|
|
|
|
void profileReport(void)
|
|
{
|
|
printf("%7s %7s function\n", "count", "msecs");
|
|
for (int i = 0; i < nprofiles; ++i) {
|
|
oop prof = profiles[i];
|
|
oop func = Object_getLocal(prof, sym_function);
|
|
long count = _integerValue(Object_getLocal(prof, sym_count ));
|
|
long ticks = _integerValue(Object_getLocal(prof, sym_time ));
|
|
printf("%7ld ", count);
|
|
//printf("%7ld ", (long)(1000. * ticks / CLOCKS_PER_SEC));
|
|
printf("%7ld ", ticks);
|
|
if (is(Primitive, func)) {
|
|
printf("%s\n", printString(func, 0));
|
|
continue;
|
|
}
|
|
oop parent = Object_getLocal(func, sym_parent);
|
|
oop name = Object_getLocal(func, sym_name);
|
|
if (nil != parent) printf("%s.", codeString (parent, 0));
|
|
if (nil != name ) printf("%s", printString(name, 0));
|
|
else printf("[anonymous function]");
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
oop apply(oop func, oop self, oop args, oop env, oop owner)
|
|
{
|
|
int functype = getType(func);
|
|
if (Primitive == functype) {
|
|
# if PROFILE
|
|
oop profile = nil;
|
|
if (opt_p) {
|
|
profile = _get(func, Primitive,profile);
|
|
if (nil == profile) profile = _set(func, Primitive,profile, profileInit(func));
|
|
profileTick(profile);
|
|
}
|
|
# endif
|
|
oop result = _get(func, Primitive,function)(func, self, args, env);
|
|
# if PROFILE
|
|
if (opt_p) profileTock(profile);
|
|
# endif
|
|
return result;
|
|
}
|
|
#if PRIMCLOSURE
|
|
if (Closure != functype)
|
|
valueError(nil == self ? "()" : ".()", "cannot apply", func);
|
|
oop lambda = _get(func, Closure,function);
|
|
oop environment = _get(func, Closure,environment);
|
|
oop parameters = _get(lambda, Lambda,parameters);
|
|
oop body = _get(lambda, Lambda,body);
|
|
#else
|
|
if (Object != functype || pClosure != _getDelegate(func))
|
|
valueError(nil == self ? "()" : ".()", "cannot apply", func);
|
|
oop lambda = Object_get(func, sym_function);
|
|
oop environment = Object_get(func, sym_environment);
|
|
oop parameters = Object_get(lambda, sym_parameters);
|
|
oop body = Object_get(lambda, sym_body);
|
|
# if PROFILE
|
|
oop profile = nil;
|
|
if (opt_p) {
|
|
profile = Object_getLocal(lambda, sym_profile);
|
|
if (nil == profile) profile = Object_put(lambda, sym_profile, profileInit(lambda));
|
|
profileTick(profile);
|
|
}
|
|
# endif
|
|
#endif
|
|
oop *exprs = get(body, Object,indexed);
|
|
int size = _get(body, Object,isize);
|
|
oop result = nil;
|
|
assert(is(Object, args));
|
|
// inherit from closure's captured environment
|
|
_setDelegate(args, environment);
|
|
Object_put(args, sym_self, self);
|
|
Object_put(args, prop_owner, owner);
|
|
int nparam = _get(parameters, Object,isize);
|
|
oop *pparam = _get(parameters, Object,indexed);
|
|
int nargs = _get(args, Object,isize);
|
|
oop *pargs = _get(args, Object,indexed);
|
|
# if NONLOCAL
|
|
switch (nlrPush()) {
|
|
case NLR_CONTINUE: syntaxError("continue outside loop");
|
|
case NLR_BREAK: syntaxError("break outside loop");
|
|
case NLR_RETURN: return nlrPop();
|
|
case NLR_RAISE: nlrReturn(nlrPop(), NLR_RAISE);
|
|
}
|
|
# endif
|
|
// positional args -> named parameters
|
|
for (int i = 0; i < nparam; ++i)
|
|
Object_put(args, pparam[i], i < nargs ? pargs[i] : nil);
|
|
// keyword defaults
|
|
int nkeywd = _get(parameters, Object,psize);
|
|
struct property *pkeywd = _get(parameters, Object,properties);
|
|
for (int i = 0; i < nkeywd; ++i)
|
|
if (Object_find(args, pkeywd[i].key) < 0)
|
|
Object_put(args, pkeywd[i].key, eval(pkeywd[i].val, args));
|
|
for (int i = 0; i < size; ++i)
|
|
result = eval(exprs[i], args);
|
|
# if NONLOCAL
|
|
nlrPop();
|
|
# endif
|
|
# if PROFILE
|
|
if (opt_p) profileTock(profile);
|
|
# endif
|
|
return result;
|
|
}
|
|
|
|
oop getArg(oop args, int index, char *who)
|
|
{ assert(is(Object, args));
|
|
if (index >= _get(args, Object,isize)) valueError("%s", "too few arguments", args);
|
|
return _get(args, Object,indexed)[index];
|
|
}
|
|
|
|
oop getArgType(oop args, int index, int type, char *who)
|
|
{ assert(is(Object, args));
|
|
oop arg = getArg(args, index, who);
|
|
if (type != getType(arg)) typeError(who, "illegal argument type", arg);
|
|
return arg;
|
|
}
|
|
|
|
#if TYPECODES
|
|
|
|
enum typecode getTypecode(oop exp)
|
|
{
|
|
oop delegate = _getDelegate(exp);
|
|
oop name = Object_getLocal(delegate, prop_name);
|
|
return is(Symbol, name) ? _get(name, Symbol,typecode) : UNDEFINED_TYPECODE;
|
|
}
|
|
|
|
#endif // !TYPECODES
|
|
|
|
#define defineEval(NAME) \
|
|
static inline oop prim_##NAME##_eval(oop func, oop exp, oop args, oop env) { \
|
|
if (_get(args,Object,isize) >= 1) env = Object_at(args, 0); \
|
|
return NAME##_eval(exp, env); \
|
|
}
|
|
|
|
doProtos(defineEval)
|
|
|
|
#undef defineEval
|
|
|
|
#define defineCodeOn(NAME) \
|
|
static inline oop prim_##NAME##_codeOn(oop func, oop exp, oop args, oop env) { \
|
|
NAME##_codeOn(exp, getArgType(args, 0, String, #NAME".codeOn"), env); \
|
|
return exp; \
|
|
} \
|
|
|
|
doProtos(defineCodeOn)
|
|
|
|
#undef defineCodeOn
|
|
|
|
static inline oop evalobj(oop exp, oop env)
|
|
{
|
|
# if TYPECODES
|
|
switch (getTypecode(exp)) {
|
|
case UNDEFINED_TYPECODE:
|
|
break;
|
|
# define defineEval(NAME) case t##NAME: return NAME##_eval(exp, env);
|
|
doProtos(defineEval);
|
|
# undef defineEval
|
|
}
|
|
# endif // TYPECODES
|
|
|
|
oop owner = nil;
|
|
oop evaluator = Object_getOwner(exp, prop_eval, &owner);
|
|
oop args = new(pObject);
|
|
Object_push(args, env);
|
|
return apply(evaluator, exp, args, env, owner);
|
|
}
|
|
|
|
long evaluations = 0;
|
|
|
|
oop eval(oop exp, oop env)
|
|
{
|
|
++evaluations;
|
|
enum type type = getType(exp);
|
|
# if PRIMCLOSURE
|
|
if (Lambda == type) return newClosure(exp, env);
|
|
# endif
|
|
if (Object != type) {
|
|
if (String == type) return newStringLen(_get(exp, String,value), _get(exp, String,length));
|
|
return exp;
|
|
}
|
|
if (!opt_O) {
|
|
Object_push(trace, exp);
|
|
if (opt_d && opt_v) {
|
|
printf("@@@ ");
|
|
codeln(exp, 0);
|
|
}
|
|
}
|
|
oop result = evalobj(exp, env);
|
|
if (!opt_O) Object_pop(trace);
|
|
return result;
|
|
}
|
|
|
|
oop evargs(oop list, oop env)
|
|
{
|
|
if (!is(Object, list)) return list;
|
|
int isize = _get(list, Object,isize);
|
|
int psize = _get(list, Object,psize);
|
|
oop *indexed = _get(list, Object,indexed);
|
|
struct property *props = _get(list, Object,properties);
|
|
oop *indexed2 = isize ? xmalloc(sizeof(*indexed2) * isize) : 0;
|
|
struct property *props2 = psize ? xmalloc(sizeof(*props2 ) * psize) : 0;
|
|
for (int i = 0; i < isize; ++i)
|
|
indexed2[i] = eval(indexed[i], env);
|
|
for (int i = 0; i < psize; ++i) {
|
|
props2[i].key = props[i].key ;
|
|
props2[i].val = eval(props[i].val, env);
|
|
}
|
|
return newObjectWith(isize, indexed2, psize, props2);
|
|
}
|
|
|
|
oop prim_Object_new(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
_setDelegate(args, self);
|
|
oop owner = nil;
|
|
oop ifunc = Object_getOwner(args, sym_initialise, &owner);
|
|
apply(ifunc, args, new(pObject), env, owner);
|
|
return args;
|
|
}
|
|
|
|
oop prim_Object_initialise(oop func, oop self, oop args, oop env)
|
|
{
|
|
return self;
|
|
}
|
|
|
|
oop prim_Object_push(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
int argc = _get(args, Object,isize); assert(is(Object, self));
|
|
oop *indexed = _get(args, Object,indexed);
|
|
for (int i = 0; i < argc; ++i) Object_push(self, indexed[i]);
|
|
return self;
|
|
}
|
|
|
|
oop prim_Object_pop(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, self));
|
|
int size = _get(self, Object,isize);
|
|
if (size < 1) rangeError("Object.pop", "object is empty", self, 0);
|
|
--size;
|
|
_set(self, Object,isize, size);
|
|
return _get(self, Object,indexed)[size];
|
|
}
|
|
|
|
oop prim_String_new(oop func, oop self, oop args, oop env)
|
|
{
|
|
int nargs = _get(args, Object,isize);
|
|
if (nargs == 0) return newStringLen(0, 0);
|
|
int len = _integerValue(getArgType(args, 0, Integer, "String.new"));
|
|
return newStringLen(0, len);
|
|
}
|
|
|
|
oop prim_String_escaped(oop func, oop self, oop args, oop env)
|
|
{
|
|
return String_escaped(self);
|
|
}
|
|
|
|
oop prim_String_unescaped(oop func, oop self, oop args, oop env)
|
|
{
|
|
return newStringUnescaped(String_content(self));
|
|
}
|
|
|
|
oop prim_String_push(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
int argc = _get(args, Object,isize); assert(is(String, self));
|
|
oop *indexed = _get(args, Object,indexed);
|
|
for (int i = 0; i < argc; ++i) String_push(self, indexed[i]);
|
|
return self;
|
|
}
|
|
|
|
oop prim_String_pop(oop func, oop self, oop args, oop env)
|
|
{ assert(is(String, self));
|
|
int size = _get(self, String,length);
|
|
if (size < 1) rangeError("String.pop", "string is empty", self, 0);
|
|
--size;
|
|
_set(self, String,length, size);
|
|
return newInteger(_get(self, String,value)[size]);
|
|
}
|
|
|
|
oop prim_String_asInteger(oop func, oop self, oop args, oop env)
|
|
{ assert(is(String, self));
|
|
char *str = String_content(self); // ensure nul terminator
|
|
char *end = 0;
|
|
long value = strtol(str, &end, 0);
|
|
if (*end) return nil;
|
|
return newInteger(value);
|
|
}
|
|
|
|
oop prim_String_asFloat(oop func, oop self, oop args, oop env)
|
|
{ assert(is(String, self));
|
|
char *str = String_content(self); // ensure nul terminator
|
|
char *end = 0;
|
|
double value = strtod(str, &end);
|
|
if (*end) return nil;
|
|
return newFloat(value);
|
|
}
|
|
|
|
oop prim_String_asSymbol(oop func, oop self, oop args, oop env)
|
|
{ assert(is(String, self));
|
|
return intern(String_content(self));
|
|
}
|
|
|
|
char *strnchr(char *s, int len, int c)
|
|
{
|
|
while (len--) if (c == *s++) return s-1;
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(__MACH__) // BSD has this in libc
|
|
|
|
char *strnstr(char *s, char *t, int slen)
|
|
{
|
|
int tlen = strlen(t);
|
|
int limit = slen - tlen;
|
|
for (int i = 0; i <= limit; ++i)
|
|
if (!strncmp(s + i, t, tlen))
|
|
return s+i;
|
|
return 0;
|
|
}
|
|
|
|
#endif
|
|
|
|
oop String_bitSet(oop string, int bit)
|
|
{
|
|
int index = bit / 8, shift = bit % 8;
|
|
while (index >= _get(string, String,length)) String_append(string, 0);
|
|
_get(string, String,value)[index] |= (1 << shift);
|
|
return string;
|
|
}
|
|
|
|
oop prim_String_bitSet(oop func, oop self, oop args, oop env)
|
|
{
|
|
return String_bitSet(self, _integerValue(getArgType(args, 0, Integer, "String.bitSet")));
|
|
}
|
|
|
|
oop String_bitClear(oop string, int bit)
|
|
{
|
|
int index = bit / 8, shift = bit % 8;
|
|
while (index >= _get(string, String,length)) String_append(string, 0);
|
|
_get(string, String,value)[index] &= ~(1 << shift);
|
|
return string;
|
|
}
|
|
|
|
oop prim_String_bitClear(oop func, oop self, oop args, oop env)
|
|
{
|
|
return String_bitClear(self, _integerValue(getArgType(args, 0, Integer, "String.bitClear")));
|
|
}
|
|
|
|
oop String_bitInvert(oop string, int bit)
|
|
{
|
|
int index = bit / 8, shift = bit % 8;
|
|
while (index >= _get(string, String,length)) String_append(string, 0);
|
|
_get(string, String,value)[index] ^ (1 << shift);
|
|
return string;
|
|
}
|
|
|
|
oop prim_String_bitInvert(oop func, oop self, oop args, oop env)
|
|
{
|
|
return String_bitInvert(self, _integerValue(getArgType(args, 0, Integer, "String.bitInvert")));
|
|
}
|
|
|
|
int String_bitTest(oop string, int bit)
|
|
{
|
|
int index = bit / 8, shift = bit % 8;
|
|
if (index >= _get(string, String,length)) return 0;
|
|
return (_get(string, String,value)[index] >> shift) & 1;
|
|
}
|
|
|
|
oop prim_String_bitTest(oop func, oop self, oop args, oop env)
|
|
{
|
|
return newBoolean(String_bitTest(self, _integerValue(getArgType(args, 0, Integer, "String.bitTest"))));
|
|
}
|
|
|
|
// a bit silly having this as a primitive...
|
|
|
|
int charClassNext(char **ppc)
|
|
{
|
|
int c = *(*ppc)++;
|
|
if ('\\' == c && **ppc) {
|
|
c = *(*ppc)++;
|
|
switch (c) {
|
|
case 'a': return '\a';
|
|
case 'b': return '\b';
|
|
case 'f': return '\f';
|
|
case 'n': return '\n';
|
|
case 'r': return '\r';
|
|
case 't': return '\t';
|
|
case 'v': return '\v';
|
|
case '0'...'7': {
|
|
c &= 7;
|
|
if ('0' <= **ppc && **ppc <= '7') c = (c << 3) | (*(*ppc)++ & 7);
|
|
if ('0' <= **ppc && **ppc <= '7') c = (c << 3) | (*(*ppc)++ & 7);
|
|
return c;
|
|
}
|
|
case 'x': {
|
|
c = 0;
|
|
int d;
|
|
while ((d = digitValue(**ppc, 16)) >= 0) c = (c << 4) | d, ++*ppc;
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
oop prim_String_charClass(oop func, oop self, oop args, oop env)
|
|
{
|
|
oop bits = newStringLen(0, 0);
|
|
char *spec = String_content(self);
|
|
int invert = 0;
|
|
if ((invert = ('^' == spec[0]))) ++spec;
|
|
while (*spec) {
|
|
int c = charClassNext(&spec);
|
|
if ('-' == spec[0] && spec[1]) {
|
|
++spec;
|
|
int d = charClassNext(&spec);
|
|
for (int i = c; i <= d; ++i) String_bitSet(bits, i);
|
|
continue;
|
|
}
|
|
String_bitSet(bits, c);
|
|
}
|
|
if (invert) {
|
|
int length = _get(bits, String,length);
|
|
while (length < 16) String_append(bits, 0), ++length;
|
|
char *value = _get(bits, String,value);
|
|
for (int i = 0; i < length; ++i) value[i] ^= 0xff;
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
oop prim_String_compareFrom(oop func, oop self, oop args, oop env)
|
|
{
|
|
int off = _integerValue(getArgType(args, 0, Integer, "String.compareFrom"));
|
|
oop str = getArgType(args, 1, String, "String.compareFrom");
|
|
char *myval = _get(self, String,value);
|
|
int mylen = _get(self, String,length);
|
|
char *qqval = _get(str, String,value);
|
|
int qqlen = _get(str, String,length);
|
|
if (off + qqlen > mylen) return nil;
|
|
return newInteger(strncmp(myval + off, qqval, qqlen));
|
|
}
|
|
|
|
oop prim_String_intAt(oop func, oop self, oop args, oop env)
|
|
{
|
|
int index = _integerValue(getArgType(args, 0, Integer, "String.intAt"));
|
|
int size = _get(self, String,length);
|
|
if (index < 0 || index + sizeof(int) > size)
|
|
rangeError("String.intAt", "index out of bounds", self, index);
|
|
return newInteger(*(int *)(_get(self, String,value) + index));
|
|
}
|
|
|
|
oop prim_Object_includes(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
if (!is(Object, self)) return nil;
|
|
int argc = _get(args, Object,isize);
|
|
oop *argv = _get(args, Object,indexed);
|
|
int size = _get(self, Object,isize);
|
|
oop *elts = _get(self, Object,indexed);
|
|
for (int i = 0; i < argc; ++i) {
|
|
oop arg = argv[i];
|
|
int found = 0;
|
|
for (int j = 0; j < size; ++j)
|
|
if ((found = (elts[j] == arg)))
|
|
break;
|
|
if (!found) return nil;
|
|
}
|
|
return sym_t;
|
|
}
|
|
|
|
oop prim_String_includes(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
int size = _get(args, Object,isize); assert(is(String, self));
|
|
oop *elts = _get(args, Object,indexed);
|
|
char *value = _get(self, String,value);
|
|
int length = _get(self, String,length);
|
|
for (int i = 0; i < size; ++i) {
|
|
oop arg = elts[i];
|
|
switch (getType(arg)) {
|
|
case Integer:
|
|
if (!strnchr(value, length, _integerValue(arg))) return nil;
|
|
continue;
|
|
case String:
|
|
if (!strnstr(value, String_content(arg), length)) return nil;
|
|
continue;
|
|
default:
|
|
typeError("String.includes", "non-string/integer argument", arg);
|
|
break;
|
|
}
|
|
}
|
|
return sym_t;
|
|
}
|
|
|
|
oop prim_String_sliced(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
int argc = _get(args, Object,isize); assert(is(String, self));
|
|
if (argc != 2) valueError("String.sliced", "two arguments expected", args);
|
|
oop *argv = _get(args, Object,indexed);
|
|
char *value = _get(self, String,value);
|
|
int length = _get(self, String,length);
|
|
int start = integerValue(argv[0], "String.sliced");
|
|
int end = integerValue(argv[1], "String.sliced");
|
|
if (start < 0) start += length;
|
|
if (start < 0 || start >= length) rangeError("String.sliced", "start index out of bounds", self, start);
|
|
if (end < 0) end += length;
|
|
if (end < 0 || end >= length) rangeError("String.sliced", "end index out of bounds", self, end);
|
|
oop result = newStringLen(0, 0);
|
|
String_appendAllLen(result, value + start, end - start + 1);
|
|
return result;
|
|
}
|
|
|
|
oop prim_Symbol_asString(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Symbol, self));
|
|
return newString(_get(self, Symbol,name));
|
|
}
|
|
|
|
oop prim_length(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
if (!is(Object, self)) valueError("length", "not an object", self);
|
|
return newInteger(_get(self, Object,isize));
|
|
}
|
|
|
|
oop prim_keys(oop func, oop self, oop args, oop env)
|
|
{
|
|
return keys(self, 0);
|
|
}
|
|
|
|
oop prim_allKeys(oop func, oop self, oop args, oop env)
|
|
{
|
|
return keys(self, 1);
|
|
}
|
|
|
|
oop prim_findKey(oop func, oop self, oop args, oop env)
|
|
{
|
|
if (is(Object, self)) {
|
|
if (_get(args, Object,isize) != 1) valueError("Object.findKey", "one argument expected", args);
|
|
oop key = _get(args, Object,indexed)[0];
|
|
int index = Object_find(self, key);
|
|
return newInteger(index);
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
oop prim_sorted(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
if (self == nil) {
|
|
if (_get(args, Object,isize) != 1) valueError("sorted", "one argument expected", args);
|
|
self = _get(args, Object,indexed)[0];
|
|
}
|
|
return sorted(self, "sorted");
|
|
}
|
|
|
|
oop prim_reversed(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
if (self == nil) {
|
|
if (_get(args, Object,isize) != 1) valueError("reversed", "one argument expected", args);
|
|
self = _get(args, Object,indexed)[0];
|
|
}
|
|
return reversed(self, "reversed");
|
|
}
|
|
|
|
oop prim_env(oop func, oop self, oop args, oop env)
|
|
{
|
|
return env;
|
|
}
|
|
|
|
oop prim_eval(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
oop *indexed = _get(args, Object,indexed);
|
|
oop result = nil;
|
|
for (int i = 0; i < argc; ++i) result = eval(indexed[i], env);
|
|
return result;
|
|
}
|
|
|
|
oop prim___eval__(oop func, oop self, oop args, oop env)
|
|
{
|
|
return self;
|
|
}
|
|
|
|
oop prim_print(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
oop *indexed = _get(args, Object,indexed);
|
|
oop result = nil;
|
|
oop full = Object_getLocal(args, sym_full);
|
|
int indent = isInteger(full) ? _integerValue(full) : nil != full;
|
|
for (int i = 0; i < argc; ++i) print(result = indexed[i], indent);
|
|
fflush(stdout);
|
|
return result;
|
|
}
|
|
|
|
oop prim_codeString(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
oop *indexed = _get(args, Object,indexed);
|
|
oop result = newStringLen(0, 0);
|
|
int indent = 0;
|
|
if (nil != Object_getLocal(args, sym_full)) indent = 1;
|
|
for (int i = 0; i < argc; ++i) codeOn(result, indexed[i], 0);
|
|
return result;
|
|
}
|
|
|
|
oop prim_sqrt(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (1 != argc) valueError("sqrt", "one argument expected", args);
|
|
return newFloat(sqrt(floatValue(_get(args, Object,indexed)[0], "sqrt")));
|
|
}
|
|
|
|
oop prim_round(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (1 != argc) valueError("round", "one argument expected", args);
|
|
return newInteger(round(floatValue(_get(args, Object,indexed)[0], "round")));
|
|
}
|
|
|
|
oop prim_truncate(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (1 != argc) valueError("truncate", "one argument expected", args);
|
|
return newInteger(floatValue(_get(args, Object,indexed)[0], "truncate"));
|
|
}
|
|
|
|
oop prim_cputime(oop func, oop self, oop args, oop env)
|
|
{
|
|
struct rusage ru;
|
|
getrusage(RUSAGE_SELF, &ru);
|
|
return newFloat(ru.ru_utime.tv_sec + ru.ru_utime.tv_usec / 1000000.0);
|
|
}
|
|
|
|
oop prim_evaluations(oop func, oop self, oop args, oop env)
|
|
{
|
|
return newInteger(evaluations);
|
|
}
|
|
|
|
oop prim_len(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (1 != argc) valueError("len", "one argument expected", args);
|
|
oop arg = _get(args, Object,indexed)[0];
|
|
switch (getType(arg)) {
|
|
case String: return newInteger(_get(arg, String,length));
|
|
case Symbol: return newInteger(strlen(_get(arg, Symbol,name)));
|
|
case Object: return newInteger(_get(arg, Object,isize));
|
|
default: break;
|
|
}
|
|
return newInteger(0);
|
|
}
|
|
|
|
oop prim_ord(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (1 != argc) valueError("ord", "one argument expected", args);
|
|
oop arg = _get(args, Object,indexed)[0];
|
|
if (!is(String, arg)) typeError("ord", "non-string argument", arg);
|
|
if (1 != _get(arg, String,length)) valueError("ord", "string of length one expected", arg);
|
|
return newInteger(_get(arg, String,value)[0]);
|
|
}
|
|
|
|
oop prim_chr(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
oop str = newStringLen(0, 0);
|
|
for (int i = 0; i < argc; ++i)
|
|
String_append(str, integerValue(_get(args, Object,indexed)[i], "chr"));
|
|
return str;
|
|
}
|
|
|
|
void readFile(FILE *file, char **textp, int *sizep)
|
|
{
|
|
size_t size = 0;
|
|
char *text = xmallocAtomic(4096);
|
|
for (;;) {
|
|
ssize_t n = fread(text+size, 1, 4096, file);
|
|
if (n < 1) break;
|
|
size += n;
|
|
if (n < 4096) break;
|
|
text = xrealloc(text, size + 4096);
|
|
}
|
|
*textp = text;
|
|
*sizep = size;
|
|
}
|
|
|
|
oop prim_readfile(oop func, oop self, oop args, oop env)
|
|
{
|
|
oop str = newStringLen(0, 0);
|
|
int argc = _get(args, Object,isize);
|
|
for (int i = 0; i < argc; ++i) {
|
|
oop name = _get(args, Object,indexed)[i];
|
|
if (!is(String, name)) typeError("readfile", "non-string argument", name);
|
|
FILE *file = fopen(_get(name, String,value), "r");
|
|
if (!file) valueError("readfile", strerror(errno), name);
|
|
char *text = 0;
|
|
int tlen = 0;
|
|
readFile(file, &text, &tlen);
|
|
fclose(file);
|
|
String_appendAllLen(str, text, tlen);
|
|
}
|
|
return str;
|
|
}
|
|
|
|
oop prim_exit(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
int status = 0;
|
|
if (argc > 1) valueError("exit", "too many arguments", args);
|
|
if (argc == 1) status = integerValue(_get(args, Object,indexed)[0], "exit");
|
|
exit(status);
|
|
return nil;
|
|
}
|
|
|
|
oop prim_error(oop func, oop self, oop args, oop env)
|
|
{
|
|
int argc = _get(args, Object,isize);
|
|
if (argc != 1) valueError("error", "one argument expected", args);
|
|
oop arg = _get(args, Object,indexed)[0];
|
|
if (!is(String, arg)) typeError("error", "non-string argument", arg);
|
|
unknownError(String_content(arg));
|
|
return 0;
|
|
}
|
|
|
|
oop prim_Symbol_setopt(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Symbol, self));
|
|
int argc = _get(args, Object,isize);
|
|
if (argc != 1) valueError("Symbol.setopt", "one argument expected", args);
|
|
oop val = _get(args, Object,indexed)[0];
|
|
if (!isInteger(val)) typeError("Symbol.setopt", "non-integer agument", val);
|
|
int optval = _integerValue(val);
|
|
if (sym_O == self) opt_O = optval;
|
|
else if (sym_d == self) opt_d = optval;
|
|
else if (sym_p == self) opt_p = optval;
|
|
else if (sym_v == self) opt_v = optval;
|
|
else valueError("Symbol.setopt", "unknown option", val);
|
|
return val;
|
|
}
|
|
|
|
oop prim_Symbol_getopt(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Symbol, self));
|
|
if (sym_O == self) return newInteger(opt_O);
|
|
else if (sym_d == self) return newInteger(opt_d);
|
|
else if (sym_p == self) return newInteger(opt_p);
|
|
else if (sym_v == self) return newInteger(opt_v);
|
|
else valueError("Symbol.getopt", "unknown option", self);
|
|
return 0;
|
|
}
|
|
|
|
oop prim_defined(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
if (1 != _get(args, Object,isize)) valueError("defined", "one argument expected", args);
|
|
oop arg = _get(args, Object,indexed)[0];
|
|
return UNDEFINED == *_refvar(env, arg) ? nil : sym_t; // looks in locals too
|
|
}
|
|
|
|
oop prim_Symbol_defined(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Symbol, self));
|
|
return UNDEFINED == _get(self, Symbol,value) ? nil : sym_t; // looks only at global
|
|
}
|
|
|
|
oop prim_Symbol_define(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Object, args));
|
|
int argc = _get(args, Object,isize); assert(is(Symbol, self));
|
|
if (argc != 1) valueError("Symbol.define", "one argument expected", args);
|
|
_set(self, Symbol,value, _get(args, Object,indexed)[0]);
|
|
return self;
|
|
}
|
|
|
|
oop prim_Symbol_value(oop func, oop self, oop args, oop env)
|
|
{ assert(is(Symbol, self));
|
|
oop value = _get(self, Symbol,value);
|
|
return value ? value : nil;
|
|
}
|
|
|
|
oop prim_Symbol_allInstances(oop func, oop self, oop args, oop env)
|
|
{
|
|
oop result = new(pObject);
|
|
for (int i = 0; i < nsymbols; ++i) Object_push(result, symbols[i]);
|
|
return result;
|
|
}
|
|
|
|
#include <dlfcn.h>
|
|
#include <ffi.h>
|
|
|
|
void *pointerValue(oop obj, char *who)
|
|
{
|
|
switch (getType(obj)) {
|
|
case Integer: return (void *)(intptr_t)_integerValue(obj);
|
|
case String: return String_content(obj), _get(obj, String,value);
|
|
case Symbol: return &_get(obj, Symbol,name);
|
|
default: valueError(who, "cannot convert to pointer", obj);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ffi_type *sig2type(int sig)
|
|
{
|
|
switch (sig) {
|
|
case 'v': return &ffi_type_void;
|
|
case 'c': return &ffi_type_schar;
|
|
case 'C': return &ffi_type_uchar;
|
|
case 's': return &ffi_type_sshort;
|
|
case 'S': return &ffi_type_ushort;
|
|
case 'i': return &ffi_type_sint;
|
|
case 'I': return &ffi_type_uint;
|
|
case 'l': return &ffi_type_slong;
|
|
case 'L': return &ffi_type_ulong;
|
|
case 'z': return &ffi_type_slong;
|
|
case 'Z': return &ffi_type_ulong;
|
|
case 'f': return &ffi_type_float;
|
|
case 'd': return &ffi_type_double;
|
|
case 'p':
|
|
case '*': return &ffi_type_pointer;
|
|
}
|
|
valueError("__extern__", "illegal type code", newInteger(sig));
|
|
return 0;
|
|
}
|
|
|
|
struct ffi_t {
|
|
char *name;
|
|
ffi_cif *cif;
|
|
char *signature;
|
|
void *function;
|
|
int arity;
|
|
};
|
|
|
|
oop primitiveExternalCall = 0;
|
|
|
|
void *dlprobe(char *dir, char *prefix, char *name, char *suffix, int mode)
|
|
{
|
|
oop path = newStringLen(0, 0);
|
|
String_appendAll(path, dir);
|
|
String_appendAll(path, prefix);
|
|
String_appendAll(path, name);
|
|
String_appendAll(path, suffix);
|
|
char *cpath = String_content(path);
|
|
if (opt_d) printf("dlprobe %s\n", cpath);
|
|
return dlopen(cpath, mode);
|
|
}
|
|
|
|
void *dlfind(char *name, int mode)
|
|
{
|
|
static char *dirs[] = { "", "/usr/lib/", "/lib/", "/usr/local/lib/", "/opt/local/lib/", 0 };
|
|
static char *prefixes[] = { "lib", "", 0 };
|
|
static char *suffixes[] = { ".so", ".dylib", ".dll", 0 };
|
|
for (char **dir = dirs; *dir; ++dir)
|
|
for (char **prefix = prefixes; *prefix; ++prefix)
|
|
for (char **suffix = suffixes; *suffix; ++suffix) {
|
|
void *hnd = dlprobe(*dir, *prefix, name, *suffix, mode);
|
|
if (hnd) {
|
|
if (opt_d) printf("-> %p\n", hnd);
|
|
return hnd;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void *xdlopen(oop obj)
|
|
{
|
|
if (nil == obj) return dlopen(0, RTLD_GLOBAL | RTLD_LAZY);
|
|
void *hnd = dlfind(stringValue(obj, "__extern__"), RTLD_GLOBAL | RTLD_LAZY);
|
|
if (!hnd) valueError("__extern__", "library not found", obj);
|
|
return hnd;
|
|
}
|
|
|
|
void *xdlsym(void *handle, char *name)
|
|
{
|
|
void *addr = dlsym(handle, name);
|
|
if (!addr) valueError("__extern__", dlerror(), newString(name));
|
|
return addr;
|
|
}
|
|
|
|
oop prim_extern(oop func, oop self, oop args, oop env)
|
|
{
|
|
int nargs = _get(args, Object,isize);
|
|
oop *pargs = _get(args, Object,indexed);
|
|
switch (nargs) {
|
|
case 0: {
|
|
return mkptr(xdlopen(nil));
|
|
}
|
|
case 1: { // extern("libname")
|
|
return mkptr(xdlopen(pargs[0]));
|
|
}
|
|
case 2: { // extern("libname"/handle, "name")
|
|
void *hnd = 0;
|
|
if (isInteger(pargs[0])) hnd = (void *)(intptr_t)_integerValue(pargs[0]);
|
|
else hnd = xdlopen(pargs[0]);
|
|
return mkptr(xdlsym(hnd, stringValue(pargs[1], "__extern__")));
|
|
}
|
|
}
|
|
// extern("libname"/handle", "name", "signature")
|
|
void *hnd = 0;
|
|
if (isInteger(pargs[0])) hnd = (void *)(intptr_t)_integerValue(pargs[0]);
|
|
else hnd = xdlopen(pargs[0]);
|
|
char *sym = stringValue(pargs[1], "__extern__");
|
|
void *adr = xdlsym(hnd, sym);
|
|
char *sig = stringValue(pargs[2], "__extern__");
|
|
int argc = strlen(sig);
|
|
ffi_cif *cif = xcalloc(1, sizeof(ffi_cif));
|
|
ffi_type **argv = xcalloc(argc, sizeof(*argv));
|
|
for (int i = 0; i < argc; ++i) argv[i] = sig2type(sig[i]);
|
|
ffi_prep_cif(cif, FFI_DEFAULT_ABI, argc - 1, argv[0], argv + 1);
|
|
struct ffi_t *ffi = xmalloc(sizeof(*ffi));
|
|
ffi->name = sym;
|
|
ffi->cif = cif;
|
|
ffi->signature = sig;
|
|
ffi->function = adr;
|
|
ffi->arity = argc;
|
|
oop result = clone(primitiveExternalCall);
|
|
_set(result, Primitive,cookie, ffi);
|
|
return result;
|
|
}
|
|
|
|
union arg_t {
|
|
signed char c;
|
|
unsigned char C;
|
|
signed short s;
|
|
unsigned short S;
|
|
signed int i;
|
|
unsigned int I;
|
|
signed long l;
|
|
unsigned long L;
|
|
ssize_t z;
|
|
size_t Z;
|
|
float f;
|
|
double d;
|
|
void *p;
|
|
intptr_t P;
|
|
};
|
|
|
|
oop prim_externalCall(oop func, oop self, oop args, oop env)
|
|
{
|
|
struct ffi_t *ffi = _get(func, Primitive,cookie); assert(ffi);
|
|
int argc = ffi->arity;
|
|
union arg_t vals[argc];
|
|
void *argv[argc];
|
|
for (int i = 1; i < argc; ++i) {
|
|
switch (ffi->signature[i]) {
|
|
case 'c': vals[i].c = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'C': vals[i].C = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 's': vals[i].s = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'S': vals[i].S = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'i': vals[i].i = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'I': vals[i].I = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'l': vals[i].l = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'L': vals[i].L = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'z': vals[i].z = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'Z': vals[i].Z = integerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'f': vals[i].f = floatValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'd': vals[i].d = floatValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
case 'p':
|
|
case '*': vals[i].p = pointerValue(getArg(args, i-1, ffi->name), ffi->name); break;
|
|
default: valueError(ffi->name, "illegal argument type code", newInteger(ffi->signature[i]));
|
|
}
|
|
argv[i] = vals + i;
|
|
}
|
|
ffi_call(ffi->cif, FFI_FN(ffi->function), vals, argv+1);
|
|
switch (ffi->signature[0]) {
|
|
case 'v': return nil;
|
|
case 'c': return newInteger(vals[0].c);
|
|
case 'C': return newInteger(vals[0].C);
|
|
case 's': return newInteger(vals[0].s);
|
|
case 'S': return newInteger(vals[0].S);
|
|
case 'i': return newInteger(vals[0].i);
|
|
case 'I': return newInteger(vals[0].I);
|
|
case 'l': return newInteger(vals[0].l);
|
|
case 'L': return newInteger(vals[0].L);
|
|
case 'z': return newInteger(vals[0].z);
|
|
case 'Z': return newInteger(vals[0].Z);
|
|
case 'f': return newFloat (vals[0].f);
|
|
case 'd': return newFloat (vals[0].d);
|
|
case 'p':
|
|
case '*': return mkptr (vals[0].p);
|
|
}
|
|
valueError(ffi->name, "illegal return type code", newInteger(ffi->signature[0]));
|
|
return 0;
|
|
}
|
|
|
|
#if PEGVM
|
|
|
|
oop applyThunkIn(oop func, oop env)
|
|
{
|
|
int functype = getType(func);
|
|
if (Primitive == functype) {
|
|
# if PROFILE
|
|
oop profile = nil;
|
|
if (opt_p) {
|
|
profile = _get(func, Primitive,profile);
|
|
if (nil == profile) profile = _set(func, Primitive,profile, profileInit(func));
|
|
profileTick(profile);
|
|
}
|
|
# endif
|
|
oop result = _get(func, Primitive,function)(func, nil, nil, env);
|
|
# if PROFILE
|
|
if (opt_p) profileTock(profile);
|
|
# endif
|
|
return result;
|
|
}
|
|
#if PRIMCLOSURE
|
|
if (Closure != functype) valueError("()", "cannot apply", func);
|
|
oop lambda = _get(func, Closure,function);
|
|
oop body = _get(lambda, Lambda,body);
|
|
#else
|
|
if (Object != functype || pClosure != _getDelegate(func)) valueError("()", "cannot apply", func);
|
|
oop lambda = Object_get(func, sym_function);
|
|
oop body = Object_get(lambda, sym_body);
|
|
oop parameters = Object_get(lambda, sym_parameters);
|
|
# if PROFILE
|
|
oop profile = nil;
|
|
if (opt_p) {
|
|
profile = Object_getLocal(lambda, sym_profile);
|
|
if (nil == profile) profile = Object_put(lambda, sym_profile, profileInit(lambda));
|
|
profileTick(profile);
|
|
}
|
|
# endif
|
|
#endif
|
|
oop *exprs = get(body, Object,indexed);
|
|
int size = _get(body, Object,isize);
|
|
oop result = nil;
|
|
oop args = new(pObject);
|
|
// inherit from closure's captured environment
|
|
_setDelegate(args, env);
|
|
# if NONLOCAL
|
|
switch (nlrPush()) {
|
|
case NLR_CONTINUE: syntaxError("continue outside loop");
|
|
case NLR_BREAK: syntaxError("break outside loop");
|
|
case NLR_RETURN: return nlrPop();
|
|
case NLR_RAISE: nlrReturn(nlrPop(), NLR_RAISE);
|
|
}
|
|
# endif
|
|
for (int i = 0; i < size; ++i)
|
|
result = eval(exprs[i], args);
|
|
# if NONLOCAL
|
|
nlrPop();
|
|
# endif
|
|
# if PROFILE
|
|
if (opt_p) profileTock(profile);
|
|
# endif
|
|
return result;
|
|
}
|
|
|
|
typedef unsigned char byte;
|
|
|
|
typedef enum op_t {
|
|
PUSH, DROP, POP, DOT, CLASS, STRING, TEST, RULE2, RULE, CALL, CALL2,
|
|
SUCCEED, FAIL, ACTION, BEGIN, END, UNEND, SET,
|
|
} op_t;
|
|
|
|
char *op_n[] = {
|
|
"PUSH", "DROP", "POP", "DOT", "CLASS", "STRING", "TEST", "RULE2", "RULE", "CALL", "CALL2",
|
|
"SUCCEED", "FAIL", "ACTION", "BEGIN", "END", "UNEND", "SET",
|
|
};
|
|
|
|
typedef struct vmInsn vmInsn;
|
|
|
|
struct vmInsn {
|
|
union {
|
|
char *str;
|
|
oop obj;
|
|
vmInsn *code;
|
|
int len;
|
|
} arg, arg2;
|
|
unsigned short op, ok, ko;
|
|
};
|
|
|
|
typedef struct vmState
|
|
{
|
|
oop result;
|
|
oop variables;
|
|
} vmState;
|
|
|
|
#define VM_STATE_INITIALISER { nil, nil }
|
|
|
|
void vmEnter(vmState *state, oop obj, char *yytext, int yyleng)
|
|
{
|
|
state->variables = new(state->variables);
|
|
Object_put(state->variables, sym_$$, state->result);
|
|
}
|
|
|
|
void vmSet(vmState *state, oop obj, char *yytext, int yyleng)
|
|
{
|
|
Object_put(state->variables, obj, state->result);
|
|
}
|
|
|
|
void vmAction(vmState *state, oop obj, char *yytext, int yyleng)
|
|
{
|
|
oop text = yyleng ? newStringLen(yytext, yyleng) : nil;
|
|
Object_put(state->variables, sym_yytext, text);
|
|
Object_put(state->variables, sym_yyleng, newInteger(yyleng));
|
|
applyThunkIn(obj, state->variables);
|
|
}
|
|
|
|
void vmLeave(vmState *state, oop obj, char *yytext, int yyleng)
|
|
{
|
|
state->result = Object_getLocal(state->variables, sym_$$);
|
|
state->variables = _getDelegate(state->variables);
|
|
}
|
|
|
|
void vmDisassemble(vmInsn *code, int pc)
|
|
{
|
|
vmInsn *i = &code[pc];
|
|
printf("%p ", code);
|
|
switch (i->op) {
|
|
case CLASS:
|
|
case STRING:
|
|
printf("%03d %-7s \"%s\" %2d %2d %2d\n",
|
|
pc, op_n[i->op], i->arg.str, i->arg2.len, i->ok, i->ko);
|
|
break;
|
|
case CALL:
|
|
printf("%03d %-7s %p %2d %2d %2d\n",
|
|
pc, op_n[i->op], i->arg.code, i->arg2.len, i->ok, i->ko);
|
|
break;
|
|
case CALL2:
|
|
printf("%03d %-7s %p %2d %2d %2d\n",
|
|
pc, op_n[i->op], i->arg.code, i->arg2.len, i->ok, i->ko);
|
|
break;
|
|
default:
|
|
printf("%03d %-7s %s %2d %2d %2d\n",
|
|
pc, op_n[i->op], codeString(i->arg.obj, 0), i->arg2.len, i->ok, i->ko);
|
|
break;
|
|
}
|
|
}
|
|
|
|
oop vmCache = 0;
|
|
|
|
void vmCachePut(oop grammar, oop symbol, vmInsn *code)
|
|
{
|
|
oop *ref = Object_refLocal(vmCache, grammar);
|
|
if (UNDEFINED == ref) {
|
|
Object_put(vmCache, grammar, new(pObject));
|
|
ref = _refvar(vmCache, grammar);
|
|
}
|
|
oop line = *ref;
|
|
ref = Object_refLocal(line, symbol);
|
|
if (UNDEFINED != ref) fatal("vm cache collision");
|
|
Object_put(line, symbol, mkptr(code));
|
|
}
|
|
|
|
vmInsn *vmCompile(oop grammar, oop symbol)
|
|
{
|
|
oop program = Object_get(grammar, symbol);
|
|
if (!is(Object, program)) valueError("__match__", "program is not an object", program);
|
|
oop *prog = _get(program, Object,indexed);
|
|
int plen = _get(program, Object,isize);
|
|
if (plen % 5 != 0) valueError("__match__", "program length is not a multiple of five", program);
|
|
int clen = plen / 5;
|
|
vmInsn *code = xcalloc(clen, sizeof(*code));
|
|
vmCachePut(grammar, symbol, code);
|
|
oop env = nil;
|
|
int ppc = 0;
|
|
int cpc = 0;
|
|
while (ppc < plen) {
|
|
int op = integerValue(prog[ppc++], "__match__");
|
|
oop arg = prog[ppc++]; assert(arg);
|
|
oop arg2 = prog[ppc++]; assert(arg2);
|
|
int ok = integerValue(prog[ppc++], "__match__");
|
|
int ko = integerValue(prog[ppc++], "__match__");
|
|
if (ok < 0 || ok >= clen) valueError("__match__", "OK destination out of range", program);
|
|
if (ko < 0 || ko >= clen) valueError("__match__", "KO destination out of range", program);
|
|
code[cpc] = (vmInsn){ .arg.obj = arg, .arg2.obj = arg2, .op = op, .ok = ok, .ko = ko };
|
|
switch (op) {
|
|
case CLASS:
|
|
case STRING: {
|
|
code[cpc].arg2.len = stringLength(code[cpc].arg.obj, "__match__");
|
|
code[cpc].arg.str = stringValue (code[cpc].arg.obj, "__match__");
|
|
break;
|
|
}
|
|
case TEST: {
|
|
if (!isClosure(code[cpc].arg.obj)) valueError("__match__", "TEST argument must be a closure", program);
|
|
break;
|
|
}
|
|
case RULE2: {
|
|
if (!is(Symbol, code[cpc].arg.obj )) valueError("__match__", "RULE2 argument must be a symbol", program);
|
|
if (!is(Object, code[cpc].arg2.obj)) valueError("__match__", "RULE2 argument2 must be an object", program);
|
|
break;
|
|
}
|
|
case RULE: {
|
|
if (!is(Symbol, code[cpc].arg.obj)) valueError("__match__", "RULE argument must be a symbol", program);
|
|
break;
|
|
}
|
|
case CALL: {
|
|
valueError("__match__", "program contains explicit CALL opcode", program);
|
|
break;
|
|
}
|
|
case CALL2: {
|
|
valueError("__match__", "program contains explicit CALL2 opcode", program);
|
|
break;
|
|
}
|
|
case ACTION: {
|
|
if (!isClosure(code[cpc].arg.obj)) valueError("__match__", "ACTION argument must be a closure", program);
|
|
break;
|
|
}
|
|
case SET: {
|
|
if (!is(Symbol, code[cpc].arg.obj)) valueError("__match__", "SET argument must be a symbol", program);
|
|
if (nil == env) env = new(pObject);
|
|
Object_put(env, code[cpc].arg.obj, nil);
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
++cpc;
|
|
}
|
|
|
|
if (opt_d) {
|
|
printf("---- BEGIN "); println(symbol, 0);
|
|
for (int pc = 0; pc < cpc; ++pc) vmDisassemble(code, pc);
|
|
printf("---- END "); println(symbol, 0);
|
|
}
|
|
|
|
return code;
|
|
}
|
|
|
|
vmInsn *vmCacheGet(oop grammar, oop symbol)
|
|
{
|
|
oop *ref = Object_refLocal(vmCache, grammar);
|
|
if (UNDEFINED != ref) {
|
|
oop line = *ref;
|
|
ref = Object_refLocal(line, symbol);
|
|
if (UNDEFINED != ref)
|
|
return (vmInsn *)(intptr_t)_integerValue(*ref);
|
|
}
|
|
return vmCompile(grammar, symbol);
|
|
}
|
|
|
|
int vmRun(oop grammar0, oop symbol, char *text, int start, int length)
|
|
{
|
|
vmCache = new(pObject);
|
|
|
|
int maxactions = 32;
|
|
|
|
struct Action {
|
|
void (*function)(vmState *state, oop object, char *yytext, int yyleng);
|
|
oop object;
|
|
int textbeg, textlen;
|
|
} *actions = xcalloc(maxactions, sizeof(*actions));
|
|
|
|
struct Context { // for back-tracking
|
|
int position;
|
|
int nactions;
|
|
} *cstack, context;
|
|
|
|
int csp = 0, ncstack = 32;
|
|
cstack = xmalloc(sizeof(*cstack) * ncstack);
|
|
context.position = start;
|
|
context.nactions = 0;
|
|
|
|
vmState state = VM_STATE_INITIALISER;
|
|
state.variables = new(pObject);
|
|
|
|
# define saveAction(ACT, OBJ, BEG, LEN) { \
|
|
if (context.nactions >= maxactions) \
|
|
actions = xrealloc(actions, sizeof(*actions) * (maxactions *= 2)); \
|
|
actions[context.nactions++] = (struct Action){ ACT, OBJ, BEG, LEN }; \
|
|
}
|
|
|
|
//vmInsn *code = vmCacheGet(frame.grammar, symbol);
|
|
|
|
struct Frame {
|
|
oop grammar;
|
|
oop symbol;
|
|
vmInsn *code;
|
|
int pc;
|
|
int nactions;
|
|
} *rstack, frame;
|
|
|
|
int rsp = 0, nrstack = 32;
|
|
rstack = xmalloc(sizeof(*rstack) * nrstack);
|
|
frame.grammar = grammar0;
|
|
frame.symbol = symbol;
|
|
frame.code = vmCacheGet(grammar0, symbol);
|
|
frame.pc = 0;
|
|
frame.nactions = context.nactions;
|
|
|
|
int textbeg = 0, textend = 0;
|
|
int result = 0;
|
|
|
|
#define push(C, X) { \
|
|
if (C##sp >= n##C##stack) C##stack = xrealloc(C##stack, sizeof(*C##stack) * (n##C##stack *= 2)); \
|
|
C##stack[C##sp++] = (X); \
|
|
}
|
|
|
|
#define drop(C) { \
|
|
assert(C##sp); \
|
|
--C##sp; \
|
|
}
|
|
|
|
#define pop(C) ({ \
|
|
assert(C##sp); \
|
|
C##stack[--C##sp]; \
|
|
})
|
|
|
|
for (;;) {
|
|
if (opt_d) vmDisassemble(frame.code, frame.pc);
|
|
vmInsn *i = frame.code + frame.pc++;
|
|
switch (i->op) {
|
|
case PUSH: push(c, context); frame.pc = i->ok; continue;
|
|
case DROP: drop(c); frame.pc = i->ok; continue;
|
|
case POP: context = pop(c); frame.pc = i->ok; continue;
|
|
case DOT: {
|
|
if (context.position < length) {
|
|
context.position++;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
frame.pc = i->ko;
|
|
continue;
|
|
}
|
|
case CLASS: {
|
|
if (context.position < length) {
|
|
byte c = text[context.position];
|
|
if ((((byte *)i->arg.str)[c/8] >> (c % 8)) & 1) {
|
|
context.position++;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
}
|
|
frame.pc = i->ko;
|
|
continue;
|
|
}
|
|
case STRING: {
|
|
if (context.position + i->arg2.len <= length) {
|
|
if (0 == memcmp(text + context.position, i->arg.str, i->arg2.len)) {
|
|
context.position += i->arg2.len;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
}
|
|
frame.pc = i->ko;
|
|
continue;
|
|
}
|
|
case TEST: {
|
|
oop result = apply(i->arg.obj, nil, new(pObject), nil, nil);
|
|
frame.pc = (nil == result) ? i->ko : i->ok;
|
|
continue;
|
|
}
|
|
case RULE2: {
|
|
i->op = CALL2;
|
|
i->arg.code = vmCacheGet(i->arg2.obj, i->arg.obj);
|
|
goto doCall2; // (just in case they are not consecutive ;-))
|
|
case CALL2: { doCall2:
|
|
frame.pc--; // save pc of call insn
|
|
push(r, frame);
|
|
frame.grammar = i->arg2.obj;
|
|
frame.code = i->arg.code;
|
|
frame.pc = 0;
|
|
saveAction(vmEnter, nil, 0, 0);
|
|
frame.nactions = context.nactions;
|
|
continue;
|
|
}
|
|
goto doCall;
|
|
}
|
|
case RULE: {
|
|
// frame.pc--; // save pc of call insn
|
|
// push(r, frame);
|
|
// frame.code = vmCacheGet(grammar, i->arg.obj);
|
|
// frame.pc = 0;
|
|
// if (((Node *)(i->arg))->Symbol.nvars) {
|
|
// saveAction(enter, i->arg, 0, 0);
|
|
// frame.nactions = context.nactions;
|
|
// }
|
|
// continue;
|
|
//i->op = CALL;
|
|
//i->arglen = ((Node *)i->arg)->Symbol.nvars;
|
|
//i->arg = ((Node *)i->arg)->Symbol.code; assert(i->arg);
|
|
i->op = CALL;
|
|
i->arg.code = vmCacheGet(frame.grammar, i->arg.obj);
|
|
goto doCall; // (just in case they are not consecutive ;-))
|
|
}
|
|
case CALL: { doCall:
|
|
frame.pc--; // save pc of call insn
|
|
push(r, frame);
|
|
frame.code = i->arg.code;
|
|
frame.pc = 0;
|
|
saveAction(vmEnter, nil, 0, 0);
|
|
frame.nactions = context.nactions;
|
|
continue;
|
|
}
|
|
case SUCCEED: {
|
|
if (frame.nactions == context.nactions) // no actions were added
|
|
context.nactions--; // remove the enter action
|
|
else
|
|
saveAction(vmLeave, nil, 0, 0);
|
|
if (rsp) {
|
|
frame = pop(r);
|
|
i = frame.code + frame.pc;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
result = context.position - start;
|
|
break;
|
|
}
|
|
case FAIL: {
|
|
if (rsp) {
|
|
context.nactions = frame.nactions - 1; // remove all actions added by this rule
|
|
frame = pop(r);
|
|
i = frame.code + frame.pc;
|
|
frame.pc = i->ko;
|
|
continue;
|
|
}
|
|
else {
|
|
context.nactions = 0;
|
|
}
|
|
result = -1;
|
|
break;
|
|
}
|
|
case ACTION: {
|
|
// printf("--> ACTION %d [%d %d]\n", context.nactions, textbeg, textend);
|
|
saveAction(vmAction, i->arg.obj, textbeg, textend - textbeg);
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
case BEGIN: {
|
|
textbeg = textend = context.position;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
case END: {
|
|
textend = context.position;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
case UNEND: {
|
|
textbeg = textend = 0;
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
case SET: {
|
|
saveAction(vmSet, i->arg.obj, 0, 0);
|
|
frame.pc = i->ok;
|
|
continue;
|
|
}
|
|
default: {
|
|
fatal("this cannot happen %d", i->op);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
saveAction(vmLeave, nil, 0, 0);
|
|
|
|
#undef pop
|
|
#undef drop
|
|
#undef push
|
|
|
|
xfree(cstack);
|
|
xfree(rstack);
|
|
|
|
for (int i = 0; i < context.nactions; ++i) {
|
|
char *yytext = text + actions[i].textbeg;
|
|
int yyleng = actions[i].textlen;
|
|
// printf("==> ACTION %d [%d %d]\n", i, actions[i].textbeg, actions[i].textbeg + actions[i].textlen);
|
|
actions[i].function(&state, actions[i].object, yytext, yyleng);
|
|
}
|
|
|
|
vmCache = nil;
|
|
return result;
|
|
}
|
|
|
|
oop prim_match(oop func, oop self, oop args, oop env)
|
|
{
|
|
oop grammar = getArg (args, 0, "__match__");
|
|
oop symbol = getArgType(args, 1, Symbol, "__match__");
|
|
oop string = getArgType(args, 2, String, "__match__");
|
|
int start = _integerValue(getArgType(args, 3, Integer, "__match__"));
|
|
|
|
char *text = get(string, String,value);
|
|
int length = get(string, String,length);
|
|
oop program = Object_get(grammar, symbol);
|
|
|
|
int result = vmRun(grammar, symbol, text, start, length);
|
|
|
|
return newInteger(result);
|
|
}
|
|
|
|
#endif // PEGVM
|
|
|
|
oop replFile(FILE *in)
|
|
{
|
|
int oldline = lineno;
|
|
lineno = 1;
|
|
input = newInput();
|
|
readFile(in, &input->text, &input->size);
|
|
oop result = nil;
|
|
# if NONLOCAL
|
|
switch (nlrPush()) {
|
|
case NLR_CONTINUE: syntaxError("continue outside loop");
|
|
case NLR_BREAK: syntaxError("break outside loop");
|
|
case NLR_RETURN: syntaxError("return outside function");
|
|
case NLR_RAISE: {
|
|
if (!is(Object, valnlr)) fatal("%s%s",
|
|
is(String, valnlr) ? "" : "unhandled exception: ",
|
|
printString(valnlr, 1));
|
|
oop msg = newStringLen(0, 0);
|
|
if (Object_find(valnlr, prop_function) >= 0) {
|
|
String_push(msg, Object_get(valnlr, prop_function));
|
|
String_appendAll(msg, ": ");
|
|
}
|
|
if (Object_find(valnlr, prop_kind) >= 0)
|
|
String_push(msg, Object_get(valnlr, prop_kind));
|
|
else
|
|
String_appendAll(msg, "unhandled exception");
|
|
if (Object_find(valnlr, prop_message) >= 0) {
|
|
String_appendAll(msg, ": ");
|
|
String_push(msg, Object_get(valnlr, prop_message));
|
|
}
|
|
int size = _get(valnlr, Object,psize);
|
|
struct property *kvs = _get(valnlr, Object,properties);
|
|
if (size) String_appendAll(msg, ": ");
|
|
int n = 0;
|
|
for (int i = 0; i < size; ++i) {
|
|
if (isSpecial(kvs[i].key)) continue;
|
|
if (n++) String_appendAll(msg, ", ");
|
|
String_push(msg, kvs[i].key);
|
|
String_appendAll(msg, " = ");
|
|
storeOn(msg, kvs[i].val, 0);
|
|
}
|
|
String_appendAll(msg, ":");
|
|
if (Object_find(valnlr, sym_message) >= 0) {
|
|
String_append(msg, ' ');
|
|
String_push(msg, Object_get(valnlr, sym_message));
|
|
}
|
|
size = _get(valnlr, Object,isize);
|
|
oop *elts = _get(valnlr, Object,indexed);
|
|
int w = 2 + log10(size);
|
|
for (int i = size; i--;) {
|
|
String_format(msg, "\n%*d: ", w, i);
|
|
codeOn(msg, elts[i], 0);
|
|
}
|
|
trace = nil;
|
|
fatal(String_content(msg));
|
|
}
|
|
}
|
|
# endif
|
|
while (yyparse() && yysval) {
|
|
if (opt_v) {
|
|
printf(">>> ");
|
|
(opt_d ? println : codeln)(yysval, opt_v >2);
|
|
}
|
|
result = eval(yysval, nil);
|
|
if (opt_v) {
|
|
printf("==> ");
|
|
if (opt_v >= 3) storeln(result, 1);
|
|
else if (opt_v >= 1) storeln(result, 0);
|
|
}
|
|
}
|
|
# if NONLOCAL
|
|
nlrPop();
|
|
# endif
|
|
lineno = oldline;
|
|
return result;
|
|
}
|
|
|
|
oop replPath(char *path)
|
|
{
|
|
FILE *in = fopen(path, "r");
|
|
if (!in) fatal("%s: %s", path, strerror(errno));
|
|
char *oldname = filename;
|
|
filename = path;
|
|
oop result = replFile(in);
|
|
filename = oldname;
|
|
fclose(in);
|
|
return result;
|
|
}
|
|
|
|
void cleanup(void)
|
|
{
|
|
# if PROFILE
|
|
if (opt_p) profileReport();
|
|
# endif
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
GC_INIT();
|
|
|
|
# define defineProp(NAME) prop_##NAME = intern("__"#NAME"__");
|
|
doProperties(defineProp);
|
|
# undef defineProp
|
|
|
|
# define defineSym(NAME) sym_##NAME = intern(#NAME);
|
|
doSymbols(defineSym);
|
|
# undef defineSym
|
|
|
|
pObject = nil;
|
|
|
|
# define defineProto(NAME) \
|
|
p##NAME = new(pObject); \
|
|
Object_put(p##NAME, prop_name, intern(#NAME)); \
|
|
_set(intern(#NAME), Symbol,value, p##NAME);
|
|
|
|
doProtos(defineProto);
|
|
doTypes(defineProto);
|
|
|
|
# undef defineProto
|
|
|
|
primitives = new(pObject);
|
|
|
|
_set(intern("__primitives__"), Symbol,value, primitives);
|
|
|
|
Object_put(pObject, prop_eval, newPrimitive(prim___eval__, newString("Object.__eval__"))); // inherited by all objects
|
|
|
|
#if TYPECODES
|
|
|
|
# define defineEvaluator(NAME) \
|
|
_set(intern(#NAME), Symbol,typecode, t##NAME);
|
|
|
|
# undef defineEvaluator
|
|
|
|
#endif // !TYPECODES
|
|
|
|
# define defineEvaluator(NAME) \
|
|
Object_put(p##NAME, prop_eval, newPrimitive(prim_##NAME##_eval, newString(#NAME".__eval__")));
|
|
|
|
doProtos(defineEvaluator);
|
|
|
|
# undef defineEvaluator
|
|
|
|
# define defineCodeOn(NAME) \
|
|
Object_put(p##NAME, prop_codeon, newPrimitive(prim_##NAME##_codeOn, newString(#NAME".codeOn")));
|
|
|
|
doProtos(defineCodeOn);
|
|
|
|
# undef defineCodeOn
|
|
|
|
macros = Object_put(pSymbol, intern("macros"), new(pObject));
|
|
|
|
# define prim(NAME, FUNC) _set(intern(#NAME), Symbol,value, newPrimitive(FUNC, newString(#NAME)))
|
|
|
|
prim(__env__ , prim_env);
|
|
prim(eval , prim_eval);
|
|
prim(print , prim_print);
|
|
prim(codeString , prim_codeString);
|
|
prim(sqrt , prim_sqrt);
|
|
prim(round , prim_round);
|
|
prim(truncate , prim_truncate);
|
|
prim(cputime , prim_cputime);
|
|
prim(evaluations, prim_evaluations);
|
|
prim(len , prim_len);
|
|
prim(ord , prim_ord);
|
|
prim(chr , prim_chr);
|
|
prim(readfile , prim_readfile);
|
|
prim(exit , prim_exit);
|
|
prim(error , prim_error);
|
|
prim(defined , prim_defined);
|
|
prim(__extern__ , prim_extern);
|
|
prim(__match__ , prim_match);
|
|
|
|
# undef prim
|
|
|
|
primitiveExternalCall = newPrimitive(prim_externalCall, newString("externalCall"));
|
|
|
|
# define method(CLASS, NAME, FUNC) Object_put(p##CLASS, intern(#NAME), newPrimitive(FUNC, newString(#CLASS"."#NAME)))
|
|
|
|
method(Object,new, prim_Object_new );
|
|
method(Object,initialise, prim_Object_initialise );
|
|
method(Object,push, prim_Object_push );
|
|
method(Object,pop, prim_Object_pop );
|
|
method(Object,length, prim_length );
|
|
method(Object,keys, prim_keys );
|
|
method(Object,allKeys, prim_allKeys );
|
|
method(Object,findKey, prim_findKey );
|
|
method(Object,sorted, prim_sorted );
|
|
method(Object,reversed, prim_reversed );
|
|
method(Object,includes, prim_Object_includes );
|
|
method(String,new, prim_String_new );
|
|
method(String,escaped, prim_String_escaped );
|
|
method(String,unescaped, prim_String_unescaped );
|
|
method(String,push, prim_String_push );
|
|
method(String,pop, prim_String_pop );
|
|
method(String,asInteger, prim_String_asInteger );
|
|
method(String,asFloat, prim_String_asFloat );
|
|
method(String,asSymbol, prim_String_asSymbol );
|
|
method(String,includes, prim_String_includes );
|
|
method(String,sliced, prim_String_sliced );
|
|
method(String,bitSet, prim_String_bitSet );
|
|
method(String,bitClear, prim_String_bitClear );
|
|
method(String,bitInvert, prim_String_bitInvert );
|
|
method(String,bitTest, prim_String_bitTest );
|
|
method(String,charClass, prim_String_charClass );
|
|
method(String,compareFrom, prim_String_compareFrom );
|
|
method(String,intAt, prim_String_intAt );
|
|
method(Symbol,asString, prim_Symbol_asString );
|
|
method(Symbol,setopt, prim_Symbol_setopt );
|
|
method(Symbol,getopt, prim_Symbol_getopt );
|
|
method(Symbol,defined, prim_Symbol_defined );
|
|
method(Symbol,define, prim_Symbol_define );
|
|
method(Symbol,value, prim_Symbol_value );
|
|
method(Symbol,allInstances, prim_Symbol_allInstances);
|
|
|
|
# undef method
|
|
|
|
namespaces = _set(sym___namespaces__, Symbol,value, new(pObject));
|
|
|
|
trace = new(pObject);
|
|
|
|
oop args = new(pObject);
|
|
_set(intern("__argv__"), Symbol,value, args);
|
|
|
|
signal(SIGINT, sigint);
|
|
|
|
int argn = 1;
|
|
|
|
while (argn < argc) {
|
|
char *arg = argv[argn];
|
|
if ('-' != *arg) break;
|
|
while (*++arg) {
|
|
switch (*arg) {
|
|
case 'O': ++opt_O; continue;
|
|
case 'd': ++opt_d, ++opt_v; continue;
|
|
case 'p': ++opt_p; continue;
|
|
case 'v': ++opt_v; continue;
|
|
default: fatal("unknown command-line option '%c'", *arg);
|
|
}
|
|
}
|
|
++argn;
|
|
}
|
|
|
|
for (int i = argn; i < argc; ++i)
|
|
Object_push(args, newString(argv[i]));
|
|
|
|
atexit(cleanup);
|
|
|
|
if (argn == argc)
|
|
replFile(stdin);
|
|
else
|
|
replPath(argv[argn]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Local Variables:
|
|
// eval: (setq indent-tabs-mode nil)
|
|
// eval: (untabify (point-min) (point-max))
|
|
// End:
|