Skip to content

Instantly share code, notes, and snippets.

@monomere
Created October 28, 2024 20:32
Show Gist options
  • Save monomere/3c079761985359fe8e292f55d64f7044 to your computer and use it in GitHub Desktop.
Save monomere/3c079761985359fe8e292f55d64f7044 to your computer and use it in GitHub Desktop.
abomination
# Some example programs in Om to showcase just how bad it is to program in.
# These are from my CS course.
$Task2 = {
@_ = $_;
/Cone = [
$h = @_/input-float (@_/string "height");
$r = @_/input-float (@_/string "little radius");
$R = @_/input-float (@_/string "big radius");
M/if ($h /less-than-or-equal-to 0/f) { @_ = @_; @h=$h;@r=$r;@R=$R; /do = [
IO/println (@_/string "the height cannot be negative or zero.");
0;
]; /else=[M/if (
(@r /less-than-or-equal-to 0/f) /or
(@R /less-than-or-equal-to 0/f)
) { @_ = @_; @h=@h;@r=@r;@R=@R; /do = [
IO/println (@_/string "the radii cannot be negative or zero.");
0;
]; /else=[M/if (@r /greater-than @R) { @_ = @_; @h=@h;@r=@r;@R=@R; /do = [
IO/println (@_/string "the little radius has to be smaller than the big radius.");
0;
]; /else=[
$l = ((@R /minus @r)/squared /plus @h/squared)/sqrt;
$V = (((1/f /over 3/f) /times 1/f/pi) /times @h) /times (
(@R/squared /plus (@R /times @r)) /plus @r/squared
);
$S = 1/f/pi /times (
(@R/squared /plus ((@R /plus @r) /times $l)) /plus @r/squared
);
IO/println $V;
IO/println $S;
1;
];};];};];};
];
/Branching = [
$a = @_/input-float (@_/string "a");
$x = @_/input-float (@_/string "x");
IO/println (M/if ($x/abs /less-than 1/f) {
@x = $x; @a = $a;
/do = @a /times @x/abs/ln;
/else = (@a /minus @x/squared)/sqrt;
});
1;
];
/Function = [
$b = @_/input-float (@_/string "b");
$x = @_/input-float (@_/string "x");
$y = @_/input-float (@_/string "y");
M/if (
(($b /minus $y) /less-than-or-equal-to 0/f) /or
(($b /minus $x) /less-than 0/f)
) {
@x = $x; @y = $y; @b = $b;
/do = [IO/println "invariant: b - y > 0 and b - x ≥ 0."; 0;];
/else = [IO/println ((@b /minus @y)/ln /times (@b /minus @x)/sqrt); 1;];
};
];
/Order = [
$n = @_/input-int (@_/string "starting number");
M/if ($n /less-than 0) {
@n = $n; @_=@_;
/do = [IO/println "expected natural number, got something negative."; 0;];
/else = [
(@_/range @n (@n /plus 10))/each {
/do i = IO/println $i;
};
1;
];
};
];
/Table = [
$fn = {
/eval-at x = (($x/squared /minus (2/f/times $x)) /plus 2/f)/over
($x /minus 1/f);
};
(@_/range-step -4/f (4/f /plus (1/f /over 10/f)) (1/f /over 2/f))/each {
@_=@_;
@fn=$fn;
/do x = [
IO/print $x;
IO/print (@_/string " ");
IO/println (@fn/eval-at $x);
];
};
1;
];
};
$msgs = $_/list;
$msgs/append-item ($_/string "Cone");
$msgs/append-item ($_/string "Branching");
$msgs/append-item ($_/string "Function");
$msgs/append-item ($_/string "Order");
$msgs/append-item ($_/string "Table");
M/while {
@_ = $_;
@continue = 1;
@Task2 = $Task2;
@msgs = $msgs;
@index = 0;
/continue = @continue;
/do = [
IO/print (@_/string "Task #2: ");
IO/println (@msgs/item-at @index);
$res = M/send @Task2 (@msgs/item-at @index)/data;
@index = @index /plus $res;
@continue = @index /less-than @msgs/item-count;
M/if $res/complement {
@_ = @_;
/do = IO/println (@_/string "try again!");
/else = nil;
};
];
};
$Task3 = {
@_ = $_;
/Loan = [
$S = @_/input-float (@_/string "Loan ($)");
$n = @_/input-float (@_/string "Years ");
$p = @_/input-float (@_/string "Rate (%)");
$r = $p /over 100/f;
$q = 1/f /plus $r;
$m = (($S /times $r) /times ($q/pow $n))
/over (12/f /times (($q/pow $n) /minus 1/f));
IO/print (@_/string "Monthly payment: ");
IO/println $m;
1;
];
/Loan-But-Different = [
IO/println (@_/string "I'm lazy and this abomination");
IO/println (@_/string "doesn't have break/continue...");
IO/println (@_/string "what have i done...");
1;
];
/File-Copy = [
$path = @_/string "./my-file-42.txt";
IO/print (@_/string "File: ");
IO/println $path;
@_/open-WO $path {
@path = $path;
@_=@_;
/do f = [
IO/print (@_/string "Created ");
IO/println @path;
$o = @_/string "hello from om";
IO/write $f $o/length $o/data;
IO/write1 $f 10;
];
};
$s = @_/read-whole-file $path;
IO/print $s;
1;
];
};
$Task3/Loan;
$Task3/Loan-But-Different;
$Task3/File-Copy;
$Task4 = {
@_ = $_;
/File = [
$path = @_/string "./my-file-42-1.txt";
IO/print (@_/string "File: ");
IO/println $path;
@_/open-WO $path {
@_ = @_;
/do f = [
(@_/range 0 10)/each {
@_ = @_;
@f = $f;
/do i = [
$i/print @f;
IO/write1 @f 32;
];
};
];
};
$sum = @_/open-RO $path {
@_ = @_;
/do f = [
$sum = 0;
M/while {
@_ = @_;
@f = $f;
@sum- = L/sum;
/continue = [
@_/skip-ws-from @f;
(@_/peek-from @f) /not-equal-to IO/EOF;
];
/do = [
@sum-/get/set (@sum-/get/get /plus
(@_/input-int-from @f));
];
};
$sum;
];
};
IO/println $sum;
];
/Sign = [
$x = @_/input-float (@_/string "number");
M/if ($x /equal-to 0/f) {
@x = $x;
/do = IO/println 0;
/else = [
M/if (@x /greater-than 0/f) {
/do = IO/println 1;
/else = IO/println -1;
};
];
};
];
};
$Task4/File;
$Task4/Sign;
// Author: monomere
// License: WTFPL v2
// Created September 2024
// Om stands for "Object Model"
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <stdarg.h>
#include <ctype.h>
#include <stdio.h>
#include <limits.h>
#include <math.h>
#include <sys/stat.h>
#if __STDC_VERSION__ >= 202311L
#define MO_NORETURN [[noreturn]]
#define MO_NODISCARD [[nodiscard]]
#elif !defined(_MSC_VER)
#include <stdbool.h>
#define MO_NORETURN _Noreturn
#define MO_NODISCARD
#else
#include <stdbool.h>
#define MO_NORETURN
#define MO_NODISCARD
#endif
static bool omRunFile(const char *fpath);
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "usage: %s <program-path> - run from source file\n", argv[0]);
return EXIT_FAILURE;
}
return omRunFile(argv[1]) ? EXIT_SUCCESS : EXIT_FAILURE;
}
static signed long long omAllocCount_ = 0;
static inline void *omAlloc(size_t size) {
++omAllocCount_;
return malloc(size);
}
static inline void *omAllocArray(size_t count, size_t size) {
++omAllocCount_;
return calloc(count, size);
}
static inline void *omRealloc(void *ptr, size_t size) {
return realloc(ptr, size);
}
static inline void omFree(void *ptr) {
--omAllocCount_;
free(ptr);
}
typedef struct OmSys_T OmSys;
typedef struct { uint64_t v; } OmVal;
static inline bool omIsValInt(OmVal v) { return (v.v & 1) == 1; }
static inline bool omIsValPtr(OmVal v) { return (v.v & 1) == 0; }
// static inline int64_t omValAsInt(OmVal v);
#define omValAsInt(V) ((int64_t)(assert(omIsValInt((V))), (V)).v >> 1)
// static inline void *omValAsPtr(OmVal v);
#define omValAsPtr(V) ((void *)(uintptr_t)((assert(omIsValPtr((V))), (V)).v & ~1))
static inline bool omIsValTruthy(OmVal v) {
return omIsValInt(v) ? omValAsInt(v) != 0 : omValAsPtr(v) != NULL;
}
static inline OmVal omValOfInt(int64_t v) { return (OmVal){((uint64_t)(v) << 1) | 1}; }
static inline OmVal omValOfPtr(void *p) { return (OmVal){(uint64_t)(uintptr_t)p}; }
typedef struct OmObj_T OmObj;
typedef OmVal (*OmRecvFn)(OmSys *sys, OmObj *self, char const *name, OmVal *args);
struct OmObj_T { intptr_t refCount : CHAR_BIT * sizeof(intptr_t) - 1; intptr_t releasing : 1; OmRecvFn recv; };
#define OM_MAX_OBJ_REFCOUNT ((((intptr_t)(1) << (CHAR_BIT * sizeof(intptr_t) - 1 - 1)) - 1))
struct OmObj_Mem { OmObj base; int64_t alloc_cnt; };
struct OmObj_StrBase { OmObj base; };
struct OmObj_Str { struct OmObj_StrBase base; char data[]; };
struct OmObj_GFunc { OmObj base; };
struct OmObj_Func { OmObj base; size_t argCount; char argNames[]; };
struct OmObj_IO { OmObj base; FILE *file; };
struct OmObj_Lvar { OmObj base; char *name; OmVal wrap; };
struct OmObj_L { OmObj base; };
MO_NORETURN static void omPanic(const char *fmt, ...) {
fputs("Panic: ", stderr);
va_list va;
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
fputs("\n", stderr);
#if defined(__has_builtin)
#if __has_builtin(__builtin_debugtrap)
__builtin_debugtrap();
#endif
#endif
abort();
}
typedef enum OmErr { OM_ERROR_BAD_MESSAGE, OM_ERROR_BAD_ALLOC, } OmErr;
MO_NORETURN static void omFail(OmErr err, const char *src, const char *arg) { omPanic("Fail: 0x%x: '%s'", err, arg); }
OmVal omGetGlobal(OmSys *sys, const char *name);
typedef struct {
char *data;
size_t length, capacity;
} OmString;
void omStringFree(OmString *self) {
if (self->capacity != 0 && self->data != NULL)
omFree(self->data);
}
void omStringPushN(OmString *self, const char *s, size_t n) {
size_t end = self->length;
self->length += n;
if (self->capacity == 0) {
while (self->capacity < n) self->capacity += 128;
self->data = omAlloc(self->capacity);
} else {
while (self->length + 1 > self->capacity) {
self->capacity += (self->capacity + 1) / 2;
}
self->data = omRealloc(self->data, self->capacity);
}
memcpy(self->data + end, s, n);
self->data[self->length] = '\0';
}
void omStringPushStr(OmString *self, const char *s) {
omStringPushN(self, s, strlen(s));
}
void omStringPush(OmString *self, char c) {
omStringPushN(self, &c, 1);
}
#define OM_DEFINE_LIST_TYPE(TNAME, FNAME, Telem, INIT_CAP) \
typedef struct { \
Telem *items; \
size_t count, capacity; \
} TNAME; \
void FNAME##Free(TNAME *self) { \
if (self->capacity != 0 && self->items != NULL) \
omFree(self->items); \
} \
Telem *FNAME##Push(TNAME *self) { \
if (self->capacity == 0) { \
self->capacity = (INIT_CAP); \
self->count = 0; \
self->items = omAllocArray(self->capacity, sizeof(Telem)); \
} else { \
while (self->count + 1 > self->capacity) { \
self->capacity += (self->capacity + 1) / 2; \
} \
self->items = omRealloc(self->items, self->capacity * sizeof(Telem)); \
} \
Telem *item = &self->items[self->count++]; \
memset(item, 0, sizeof(Telem)); \
return item; \
}
struct OmObj_Umsg {
OmString name;
OmString args;
OmString source;
};
OM_DEFINE_LIST_TYPE(OmLvarList, omLvarList, struct OmObj_Lvar, 2)
OM_DEFINE_LIST_TYPE(OmUmsgList, omUmsgList, struct OmObj_Umsg, 2)
OM_DEFINE_LIST_TYPE(OmValList, omValList, OmVal, 2)
struct OmObj_U {
OmObj base;
OmLvarList privs;
OmUmsgList msgs;
};
typedef struct {
OmLvarList vars;
struct OmObj_U *uself;
} OmFrame;
OM_DEFINE_LIST_TYPE(OmFrameList, omFrameList, OmFrame, 2)
struct OmSys_T {
struct OmObj_L _lObj;
struct OmObj_Mem _memObj;
struct OmObj_IO _ioObj;
struct OmObj_StrBase _strObj;
// OmFrameList _lexicalFrames;
OmString literals;
OmFrameList frames;
};
struct OmObj_F { OmObj base; double val; };
OmVal omSend(OmSys *sys, OmVal receiver, const char *msg, OmVal args[]);
static OmVal omNewF(OmSys *sys, double val);
OmVal omIntRecv(OmSys *sys, int64_t value, const char *msg, OmVal args[]) {
if (strcmp(msg, "plus") == 0) return omValOfInt(value + omValAsInt(args[0]));
if (strcmp(msg, "minus") == 0) return omValOfInt(value - omValAsInt(args[0]));
if (strcmp(msg, "times") == 0) return omValOfInt(value * omValAsInt(args[0]));
if (strcmp(msg, "divided-by") == 0) return omValOfInt(value / omValAsInt(args[0]));
if (strcmp(msg, "over") == 0) return omValOfInt(value / omValAsInt(args[0]));
if (strcmp(msg, "negated") == 0) return omValOfInt(-value);
if (strcmp(msg, "absolute") == 0) return omValOfInt(value < 0 ? -value : value);
if (strcmp(msg, "less-than") == 0) return omValOfInt(value < omValAsInt(args[0]));
if (strcmp(msg, "greater-than") == 0) return omValOfInt(value > omValAsInt(args[0]));
if (strcmp(msg, "less-than-or-equal-to") == 0) return omValOfInt(value <= omValAsInt(args[0]));
if (strcmp(msg, "greater-than-or-equal-to") == 0) return omValOfInt(value >= omValAsInt(args[0]));
if (strcmp(msg, "equal-to") == 0) return omValOfInt(value == omValAsInt(args[0]));
if (strcmp(msg, "not-equal-to") == 0) return omValOfInt(value != omValAsInt(args[0]));
if (strcmp(msg, "and") == 0) return omValOfInt(value && omValAsInt(args[0]));
if (strcmp(msg, "or") == 0) return omValOfInt(value || omValAsInt(args[0]));
if (strcmp(msg, "xor") == 0) return omValOfInt(value ^ omValAsInt(args[0]));
if (strcmp(msg, "complement") == 0) return omValOfInt(!value);
if (strcmp(msg, "between") == 0)
return omValOfInt(
value >= omValAsInt(args[0]) &&
value < omValAsInt(args[1])
);
if (strcmp(msg, "equal-to-any-of-n") == 0) {
for (int i = 0; i < omValAsInt(args[0]); ++i) {
if (value == omValAsInt(args[i + 1]))
return omValOfInt(1);
}
return omValOfInt(0);
}
if (strcmp(msg, "print") == 0) {
fprintf((void*)(uintptr_t)omValAsInt(args[0]), "%lli", value);
return omValOfPtr(NULL);
}
if (strcmp(msg, "f") == 0) return omNewF(sys, value);
omFail(OM_ERROR_BAD_MESSAGE, "Int", msg);
}
OmVal omSend(OmSys *sys, OmVal receiver, const char *msg, OmVal args[]) {
if (omIsValPtr(receiver)) {
OmObj *obj = omValAsPtr(receiver);
if (obj == NULL) omPanic("cannot send messages (/%s) to nil", msg);
return obj->recv(sys, omValAsPtr(receiver), msg, args);
} else {
return omIntRecv(sys, omValAsInt(receiver), msg, args);
}
}
static inline void omAcquire(OmSys *sys, OmObj *obj) {
if (obj == NULL || obj->refCount == OM_MAX_OBJ_REFCOUNT) return;
++obj->refCount;
}
static inline void omRelease(OmSys *sys, OmObj *obj) {
if (obj == NULL || obj->refCount == OM_MAX_OBJ_REFCOUNT || obj->releasing) return;
if (obj->refCount <= 1) {
OmVal v = omValOfPtr(obj);
obj->releasing = true;
omSend(sys, v, "release", NULL);
omSend(sys, omGetGlobal(sys, "M"), "deallocate", &v);
} else {
--obj->refCount;
}
}
OmVal omFRecv(OmSys *sys, OmObj *o_self, const char *msg, OmVal args[]);
static OmVal omNewF(OmSys *sys, double val) {
struct OmObj_F *f = omAlloc(sizeof(*f)); // TODO: use M/allocate?
f->base.recv = &omFRecv;
f->base.refCount = 0;
f->base.releasing = false;
f->val = val;
return omValOfPtr(f);
}
OmVal omFRecv(OmSys *sys, OmObj *o_self, const char *msg, OmVal args[]) {
struct OmObj_F *f = (void*)o_self;
omAcquire(sys, o_self);
if (strcmp(msg, "release") == 0) {
omRelease(sys, o_self);
return omValOfPtr(NULL);
}
if (strcmp(msg, "print") == 0) {
fprintf((void*)(uintptr_t)omValAsInt(args[0]), "%f", f->val);
omRelease(sys, o_self);
return omValOfPtr(NULL);
}
#define O(name, op) \
if (strcmp(msg, name) == 0) { \
OmVal r = omNewF(sys, op); \
omRelease(sys, o_self); \
return r; \
}
O("sqrt", sqrt(f->val));
O("ln", log(f->val));
O("exp", exp(f->val));
O("squared", f->val * f->val);
O("pi", f->val * 3.14159265358979);
O("over-pi", f->val / 3.14159265358979);
O("negated", -f->val);
O("absolute", fabs(f->val));
O("abs", fabs(f->val));
#undef O
#define O(name, op) \
if (strcmp(msg, name) == 0) { \
if (args == NULL || omValAsPtr(args[0]) == NULL) \
omPanic("float binary op with nil or no args"); \
omAcquire(sys, omValAsPtr(args[0])); \
OmVal r = omNewF(sys, f->val op ((struct OmObj_F *)omValAsPtr(args[0]))->val); \
omRelease(sys, omValAsPtr(args[0])); \
omRelease(sys, o_self); \
return r; \
}
O("plus", +);
O("minus", -);
O("times", *);
O("over", /);
O("divided-by", /);
#undef O
#define O(name, op) \
if (strcmp(msg, name) == 0) { \
if (args == NULL || omValAsPtr(args[0]) == NULL) \
omPanic("float binary op with nil or no args"); \
omAcquire(sys, omValAsPtr(args[0])); \
OmVal r = omNewF(sys, op(f->val, ((struct OmObj_F *)omValAsPtr(args[0]))->val)); \
omRelease(sys, omValAsPtr(args[0])); \
omRelease(sys, o_self); \
return r; \
}
O("pow", pow);
#undef O
#define O(name, op) \
if (strcmp(msg, name) == 0) { \
if (args == NULL || omValAsPtr(args[0]) == NULL) \
omPanic("float binary op with nil or no args"); \
omAcquire(sys, omValAsPtr(args[0])); \
OmVal r = omValOfInt(f->val op ((struct OmObj_F *)omValAsPtr(args[0]))->val); \
omRelease(sys, omValAsPtr(args[0])); \
omRelease(sys, o_self); \
return r; \
}
O("less-than", <);
O("greater-than", >);
O("less-than-or-equal-to", <=);
O("greater-than-or-equal-to", >=);
O("equal-to", ==);
O("not-equal-to", !=);
#undef O
omFail(OM_ERROR_BAD_MESSAGE, "F", msg);
}
static OmVal omObjStrRecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
struct OmObj_Str *self = (void*)o_self;
assert(self->base.base.recv == &omObjStrRecv);
omAcquire(sys, o_self);
if (strcmp(name, "recv") == 0) {
OmVal r = omValOfInt((uintptr_t)self->base.base.recv);
omRelease(sys, o_self);
return r;
} else if (strcmp(name, "data") == 0) {
OmVal r = omValOfInt((uintptr_t)(void*)self->data);
omRelease(sys, o_self);
return r;
} else if (strcmp(name, "print") == 0) {
fprintf((void*)(uintptr_t)omValAsInt(args[0]), "%s", self->data);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "length") == 0) {
size_t len = strlen(self->data);
omRelease(sys, o_self);
return omValOfInt(len);
} else if (strcmp(name, "release") == 0) {
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else {
omFail(OM_ERROR_BAD_MESSAGE, "String", name);
}
}
// static OmVal omObjFuncRecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
// struct OmObj_GFunc *self = (void*)o_self;
// assert(self->base.recv == &omObjFuncRecv);
// if (strcmp(name, "recv") == 0) {
// // return omValOfInt((uintptr_t)self->base.recv);
// } else if (strcmp(name, "of") == 0) {
// // TODO: create struct OmObj_Func
// // return omValOfInt((uintptr_t)(void*)self->data);
// omFail(OM_ERROR_BAD_MESSAGE, name);
// } else if (strcmp(name, "release") == 0) {
// return omValOfPtr(NULL);
// } else if (strcmp(name, "call") == 0) {
// struct OmObj_Func *func = (void*)self;
// // T
// return omValOfPtr(NULL);
// } else {
// }
// omFail(OM_ERROR_BAD_MESSAGE, name);
// }
static OmVal omObjLvarRecv(OmSys *sys, OmObj *o_self, char const *msg, OmVal *args) {
struct OmObj_Lvar *self = (void*)o_self;
assert(self->base.recv == &omObjLvarRecv);
if (strcmp(msg, "set") == 0) {
if (self->wrap.v == args[0].v) return omValOfPtr(NULL);
if (omIsValPtr(self->wrap)) omRelease(sys, omValAsPtr(self->wrap));
self->wrap = args[0];
if (omIsValPtr(self->wrap)) omAcquire(sys, omValAsPtr(self->wrap));
return omValOfPtr(NULL);
} else if (strcmp(msg, "get") == 0) {
return self->wrap;
} else if (strcmp(msg, "forget") == 0) {
if (omIsValPtr(self->wrap)) omRelease(sys, omValAsPtr(self->wrap));
self->wrap = omValOfPtr(NULL);
return omValOfPtr(NULL);
} else {
return omSend(sys, self->wrap, msg, args);
}
}
OmVal omGetLocal(OmSys *sys, const char *name) {
for (size_t i = sys->frames.count; i --> 0;) {
OmFrame *frame = &sys->frames.items[i];
for (size_t j = frame->vars.count; j --> 0;) {
struct OmObj_Lvar *lvar = &frame->vars.items[j];
if (strcmp(lvar->name, name) == 0)
return omValOfPtr(lvar);
}
break;
}
struct OmObj_Lvar *lvar
= omLvarListPush(&sys->frames.items[sys->frames.count - 1].vars);
lvar->base.recv = &omObjLvarRecv;
lvar->base.refCount = OM_MAX_OBJ_REFCOUNT;
lvar->base.releasing = false;
size_t nameLength = strlen(name) + 1;
lvar->name = omAlloc(nameLength);
memcpy(lvar->name, name, nameLength);
lvar->wrap = omValOfPtr(NULL);
return omValOfPtr(lvar);
}
static void omPopFrame(OmSys *sys) {
if (sys->frames.count == 0) {
omPanic("opPopFrame: stack underflow!!\n");
}
OmFrame *frame = &sys->frames.items[sys->frames.count - 1];
for (size_t i = 0; i < frame->vars.count; ++i) {
omSend(sys, omValOfPtr(&frame->vars.items[i].base), "forget", NULL);
// NB: omSend can change sys->frames.items. so we have to re-reference!
frame = &sys->frames.items[sys->frames.count - 1];
}
for (size_t i = 0; i < frame->vars.count; ++i) {
omFree(frame->vars.items[i].name);
frame->vars.items[i].name = NULL;
}
omLvarListFree(&frame->vars);
--sys->frames.count;
}
static OmVal omEval(OmSys *sys, const char *source, size_t sourceLength);
static OmVal omObjURecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
struct OmObj_U *self = (void*)o_self;
assert(self->base.recv == &omObjURecv);
OmVal ret = omValOfPtr(NULL);
bool hadRet = false;
for (size_t i = 0; i < self->msgs.count; ++i) {
struct OmObj_Umsg *umsg = &self->msgs.items[i];
if (strcmp(umsg->name.data, name) == 0) {
OmFrame *frame = omFrameListPush(&sys->frames);
frame->uself = self;
omAcquire(sys, o_self);
if (umsg->args.length > 0) {
const char *currentArgName = umsg->args.data;
if (*currentArgName != '\0' && args == NULL)
omPanic("expected arguments for /%s\n", name);
OmVal *currentArg = args;
while (*currentArgName) {
omSend(sys, omGetLocal(sys, currentArgName), "set", currentArg);
currentArgName += strlen(currentArgName) + 1;
currentArg += 1;
}
}
ret = omEval(sys, umsg->source.data, umsg->source.length);
hadRet = true;
if (omIsValPtr(ret)) omAcquire(sys, omValAsPtr(ret)); // prevents return value from getting omPopFrame'd
omPopFrame(sys);
if (omIsValPtr(ret) && omValAsPtr(ret) != NULL) --((OmObj*)omValAsPtr(ret))->refCount; // UNSAFE
omRelease(sys, o_self);
break;
}
}
if (strcmp(name, "release") == 0) {
for (size_t i = 0; i < self->msgs.count; ++i) {
struct OmObj_Umsg *umsg = &self->msgs.items[i];
omStringFree(&umsg->name);
omStringFree(&umsg->args);
omStringFree(&umsg->source);
}
for (size_t i = 0; i < self->privs.count; ++i) {
omSend(sys, omValOfPtr(&self->privs.items[i]), "forget", NULL);
omFree(self->privs.items[i].name);
}
omUmsgListFree(&self->msgs);
omLvarListFree(&self->privs);
return ret;
}
if (!hadRet) {
omFail(OM_ERROR_BAD_MESSAGE, "U", name);
}
return ret;
}
// TODO:
// - {} -> [].
// - object literals.
// - unknown message handling.
// - dynamic message `o/(expr)`
static OmVal omObjMemRecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
struct OmObj_Mem *self = (void*)o_self;
assert(self->base.recv == &omObjMemRecv);
omAcquire(sys, o_self);
if (strcmp(name, "allocate-bytes") == 0) {
size_t size = omValAsInt(args[0]);
if (size == 0) return omValOfInt(0);
++self->alloc_cnt;
void *ptr = omAllocArray(1, size);
if (ptr == NULL) omFail(OM_ERROR_BAD_ALLOC, "M", name);
omRelease(sys, o_self);
return omValOfInt((uintptr_t)ptr);
} else if (strcmp(name, "allocate") == 0) {
++self->alloc_cnt;
size_t size = omValAsInt(args[0]);
void *ptr = omAllocArray(1, sizeof(OmObj) + size);
if (ptr == NULL) omFail(OM_ERROR_BAD_ALLOC, "M", name);
omRelease(sys, o_self);
return omValOfInt((uintptr_t)ptr);
} else if (strcmp(name, "object-at") == 0) {
uintptr_t ptr = (uint64_t)omValAsInt(args[0]);
omRelease(sys, o_self);
return omValOfPtr((void*)ptr);
} else if (strcmp(name, "value-at") == 0) {
OmVal* ptr = (void*)(uintptr_t)omValAsInt(args[0]);
omRelease(sys, o_self);
return *ptr;
} else if (strcmp(name, "address-of") == 0) {
uint64_t ptr = (uintptr_t)omValAsPtr(args[0]);
omRelease(sys, o_self);
return omValOfInt((int64_t)ptr);
} else if (strcmp(name, "deallocate") == 0) {
void *ptr = omValAsPtr(args[0]);
if (ptr == NULL) return omValOfPtr(NULL);
--self->alloc_cnt;
omFree(ptr);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "deallocate-bytes") == 0) {
void *ptr = (void*)(uintptr_t)omValAsInt(args[0]);
if (ptr == NULL) return omValOfPtr(NULL);
--self->alloc_cnt;
omFree(ptr);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "morph") == 0) {
OmObj *obj = omValAsPtr(args[0]);
obj->recv = (OmRecvFn)(uintptr_t)omValAsInt(args[1]);
omRelease(sys, o_self);
return args[0];
} else if (strcmp(name, "c-string-length") == 0) {
char *s = sys->literals.data + (uintptr_t)omValAsInt(args[0]);
omRelease(sys, o_self);
return omValOfInt(strlen(s));
} else if (strcmp(name, "copy-from-c-string") == 0) {
char *dst = (void*)(uintptr_t)omValAsInt(args[0]);
char *src = sys->literals.data + (uintptr_t)omValAsInt(args[1]);
size_t len = (size_t)omValAsInt(args[2]);
memcpy(dst, src, len);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "copy-bytes") == 0) {
char *dst = (void*)(uintptr_t)omValAsInt(args[0]);
char *src = (void*)(uintptr_t)omValAsInt(args[1]);
size_t len = (size_t)omValAsInt(args[2]);
memcpy(dst, src, len);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "copy-from-value") == 0) {
OmVal *dst = (void*)(uintptr_t)omValAsInt(args[0]);
*dst = args[1];
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "value-size") == 0) {
omRelease(sys, o_self);
return omValOfInt(sizeof(OmVal));
} else if (strcmp(name, "acquire") == 0) {
if (omIsValPtr(args[0])) omAcquire(sys, omValAsPtr(args[0]));
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "release") == 0) {
if (omIsValPtr(args[0])) omRelease(sys, omValAsPtr(args[0]));
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "send") == 0) {
OmVal r = omSend(sys, args[0], (void*)(uintptr_t)omValAsInt(args[1]), args + 2);
omRelease(sys, o_self);
return r;
} else if (strcmp(name, "while") == 0) {
if (omIsValPtr(args[0])) omAcquire(sys, omValAsPtr(args[0]));
while (omIsValTruthy(omSend(sys, args[0], "continue", NULL)))
omSend(sys, args[0], "do", NULL);
if (omIsValPtr(args[0])) omRelease(sys, omValAsPtr(args[0]));
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "if") == 0) {
if (omIsValPtr(args[0])) omAcquire(sys, omValAsPtr(args[0]));
if (omIsValPtr(args[1])) omAcquire(sys, omValAsPtr(args[1]));
OmVal ret = omValOfPtr(NULL);
if (omIsValTruthy(args[0])) ret = omSend(sys, args[1], "do", NULL);
else ret = omSend(sys, args[1], "else", NULL);
if (omIsValPtr(args[0])) omRelease(sys, omValAsPtr(args[0]));
if (omIsValPtr(args[1])) omRelease(sys, omValAsPtr(args[1]));
omRelease(sys, o_self);
return ret;
} else {
omFail(OM_ERROR_BAD_MESSAGE, "M", name);
omRelease(sys, o_self);
}
}
static OmVal omObjIORecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
struct OmObj_IO *self = (void*)o_self;
assert(self->base.recv == &omObjIORecv);
omAcquire(sys, o_self);
if (strcmp(name, "println") == 0) {
OmVal v = omValOfInt((uintptr_t)stdout);
omSend(sys, args[0], "print", &v);
fputc('\n', self->file);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "print") == 0) {
OmVal v = omValOfInt((uintptr_t)stdout);
omSend(sys, args[0], "print", &v);
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "open") == 0) {
FILE *f = fopen(
(void*)(uintptr_t)omValAsInt(args[0]),
(void*)(uintptr_t)omValAsInt(args[1])
);
omRelease(sys, o_self);
return omValOfInt((uintptr_t)(void*)f);
} else if (strcmp(name, "close") == 0) {
if (omValAsInt(args[0]) != 0) {
fclose((void*)(uintptr_t)omValAsInt(args[0]));
}
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "read") == 0) {
uint64_t r = fread(
(void*)(uintptr_t)omValAsInt(args[2]),
omValAsInt(args[1]), 1,
(void*)(uintptr_t)omValAsInt(args[0])
);
omRelease(sys, o_self);
return omValOfInt(r);
} else if (strcmp(name, "write") == 0) {
uint64_t r = fwrite(
(void*)(uintptr_t)omValAsInt(args[2]),
omValAsInt(args[1]), 1,
(void*)(uintptr_t)omValAsInt(args[0])
);
omRelease(sys, o_self);
return omValOfInt(r);
} else if (strcmp(name, "write1") == 0) {
uint64_t r = fputc(
omValAsInt(args[1]),
(void*)(uintptr_t)omValAsInt(args[0])
);
omRelease(sys, o_self);
return omValOfInt(r);
} else if (strcmp(name, "read1") == 0) {
int r = fgetc((void*)(uintptr_t)omValAsInt(args[0]));
omRelease(sys, o_self);
return omValOfInt(r);
} else if (strcmp(name, "unread1") == 0) {
ungetc(omValAsInt(args[1]), (void*)(uintptr_t)omValAsInt(args[0]));
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "flush") == 0) {
fflush((void*)(uintptr_t)omValAsInt(args[0]));
omRelease(sys, o_self);
return omValOfPtr(NULL);
} else if (strcmp(name, "stdout") == 0) {
omRelease(sys, o_self);
return omValOfInt((uintptr_t)stdout);
} else if (strcmp(name, "stdin") == 0) {
omRelease(sys, o_self);
return omValOfInt((uintptr_t)stdin);
} else if (strcmp(name, "stderr") == 0) {
omRelease(sys, o_self);
return omValOfInt((uintptr_t)stderr);
} else if (strcmp(name, "EOF") == 0) {
omRelease(sys, o_self);
return omValOfInt(EOF);
} else if (strcmp(name, "file-size") == 0) {
#ifdef _WIN32
int fd = _fileno((void*)(uintptr_t)omValAsInt(args[0]));
struct _stat64 stat;
_fstat64(fd, &stat);
#else
int fd = fileno((void*)(uintptr_t)omValAsInt(args[0]));
struct stat stat;
fstat(fd, &stat);
#endif
omRelease(sys, o_self);
return omValOfInt(stat.st_size);
} else {
omFail(OM_ERROR_BAD_MESSAGE, "IO", name);
omRelease(sys, o_self);
return omValOfPtr(NULL);
}
}
static OmVal omObjLRecv(OmSys *sys, OmObj *o_self, char const *name, OmVal *args) {
struct OmObj_L *self = (void*)o_self;
assert(self->base.recv == &omObjLRecv);
return omGetLocal(sys, name);
}
typedef enum {
OM_TT_EOF = -1,
OM_TT_VAR, // [a-zA-Z0-9_][a-zA-Z0-9_\-]*
OM_TT_INT, // -?[0-9]+
OM_TT_STR, // '"([^"\\]|\\.)*"'
OM_TT_OPEN, // '('
OM_TT_CLOSE, // ')'
OM_TT_COPEN, // '{'
OM_TT_CCLOSE, // '}'
OM_TT_SOPEN, // '['
OM_TT_SCLOSE, // ']'
OM_TT_EQUALS, // '<-' or '='
OM_TT_SEMI, // ';'
OM_TT_DOLLAR, // '$'
OM_TT_PERCENT, // '%'
OM_TT_QUEST, // '?'
OM_TT_SLASH, // '/'
OM_TT_AT, // '@'
OM_TT_COLON, // ':'
OM_TT_FLAG = 99,
} OmTokType;
typedef struct { OmTokType type; uint32_t where; union { size_t offset; int64_t num; }; } OmTok;
typedef struct OmInputStream_T OmInputStream;
struct OmInputStream_T {
int (*_getc)(OmInputStream *self);
int _peekBuf;
struct { unsigned char havePeek : 1; } _flags;
size_t offset;
};
typedef struct {
OmInputStream base;
size_t length;
const char *source;
} OmStrInputStream;
int omStrInputStreamGet(OmInputStream *oself) {
OmStrInputStream *self = (void*)oself;
if (oself->offset >= self->length) return EOF;
return self->source[oself->offset];
}
typedef struct {
OmInputStream base;
FILE *source;
} OmFileInputStream;
int omFileInputStreamGet(OmInputStream *oself) {
OmFileInputStream *self = (void*)oself;
return fgetc(self->source);
}
MO_NODISCARD static inline int omStreamGet(OmInputStream *self) {
if (self->_flags.havePeek) {
self->_flags.havePeek = 0;
return self->_peekBuf;
}
int r = self->_getc(self);
++self->offset;
return r;
}
MO_NODISCARD static inline int omStreamPeek(OmInputStream *self) {
if (!self->_flags.havePeek) {
self->_peekBuf = self->_getc(self);
++self->offset;
self->_flags.havePeek = 1;
}
return self->_peekBuf;
}
OM_DEFINE_LIST_TYPE(OmUInt32List, omUInt32List, uint32_t, 16)
typedef struct OmLexer_T OmLexer;
struct OmLexer_T {
OmString data;
OmInputStream *fin;
OmTok _peek;
bool _havePeek;
const char *fname;
OmUInt32List _lines;
};
MO_NORETURN static void omParserErrorV(OmLexer *lex, uint32_t where, const char *fmt, va_list va) {
size_t lineno = 0;
for (size_t i = 1; i < lex->_lines.count; ++i) {
if (lex->_lines.items[i] < where) {
lineno = i - 1;
} else break;
}
uint32_t lineOffset = lex->_lines.count > 0 ? lex->_lines.items[lineno] : 0;
uint32_t column = where - lineOffset;
fprintf(stderr, "parse error @ %s:%zu:%u: ", lex->fname, lineno + 1, column + 1);
vfprintf(stderr, fmt, va);
fputs("\n", stderr);
omPanic("parse error");
}
MO_NORETURN static void omParserError(OmLexer *lex, uint32_t where, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
omParserErrorV(lex, where, fmt, va);
va_end(va);
}
OmVal omParsePrimary(OmSys *sys, OmLexer *lex);
OmVal omParseSuffix(OmSys *sys, OmLexer *lex, OmVal after, OmTokType until);
OmVal omParseExpr(OmSys *sys, OmLexer *lex, OmTokType until);
OmVal omParseBlock(OmSys *sys, OmLexer *lex, bool toplevel);
void omLexerPushChar(OmLexer* lex, char c);
void omLexerPushChar(OmLexer* lex, char c) {
omStringPush(&lex->data, c);
}
OmTok omLexerEatTok(OmLexer *lex);
static inline OmTok omLexerPeekTok(OmLexer *lex) {
if (!lex->_havePeek) {
lex->_peek = omLexerEatTok(lex);
lex->_havePeek = true;
}
return lex->_peek;
}
OmTok omLexerEatTok(OmLexer *lex) {
if (lex->_havePeek) {
lex->_havePeek = false;
return lex->_peek;
}
if (omStreamPeek(lex->fin) == EOF) return (OmTok){ OM_TT_EOF, lex->fin->offset, {0} };
while(isspace(omStreamPeek(lex->fin))) {
if (omStreamGet(lex->fin) == '\n')
*omUInt32ListPush(&lex->_lines) = lex->fin->offset;
}
while (omStreamPeek(lex->fin) == '#') {
while (omStreamPeek(lex->fin) != '\n' && omStreamPeek(lex->fin) != EOF)
(void)omStreamGet(lex->fin);
while(isspace(omStreamPeek(lex->fin))) {
if (omStreamGet(lex->fin) == '\n')
*omUInt32ListPush(&lex->_lines) = lex->fin->offset;
}
}
bool hadDash = false;
if (omStreamPeek(lex->fin) == '-') {
hadDash = true;
(void)omStreamGet(lex->fin);
}
if (isalpha(omStreamPeek(lex->fin)) || omStreamPeek(lex->fin) == '_' || omStreamPeek(lex->fin) == '-') {
size_t startOffset = lex->data.length;
if (hadDash) omLexerPushChar(lex, '-');
while (isalnum(omStreamPeek(lex->fin)) || omStreamPeek(lex->fin) == '_' || omStreamPeek(lex->fin) == '-')
omLexerPushChar(lex, omStreamGet(lex->fin));
omLexerPushChar(lex, '\0');
return (OmTok){ OM_TT_VAR, lex->fin->offset, { .offset = startOffset } };
} else if (isdigit(omStreamPeek(lex->fin))) {
int64_t v = 0;
while (isdigit(omStreamPeek(lex->fin))) v = v * 10 + omStreamGet(lex->fin) - '0';
return (OmTok){ OM_TT_INT, lex->fin->offset, { .num = hadDash ? -v : v } };
} else if (omStreamPeek(lex->fin) == '"') {
size_t startOffset = lex->data.length;
(void)omStreamGet(lex->fin);
while (omStreamPeek(lex->fin) != '"' && omStreamPeek(lex->fin) != EOF) {
int c = omStreamGet(lex->fin);
if (c == '\n') *omUInt32ListPush(&lex->_lines) = lex->fin->offset;
omLexerPushChar(lex, c);
}
if (omStreamPeek(lex->fin) != '"') {
omPanic("expected '\"', got '%c'\n", omStreamPeek(lex->fin));
} else (void)omStreamGet(lex->fin);
omLexerPushChar(lex, '\0');
return (OmTok){ OM_TT_STR, lex->fin->offset, { .offset = startOffset } };
}
#define O(c, e) else if (omStreamPeek(lex->fin) == c) \
return (void)omStreamGet(lex->fin), (OmTok){ e, lex->fin->offset, {0} }
O('$', OM_TT_DOLLAR);
O('/', OM_TT_SLASH);
O('(', OM_TT_OPEN);
O(')', OM_TT_CLOSE);
O('{', OM_TT_COPEN);
O('}', OM_TT_CCLOSE);
O('[', OM_TT_SOPEN);
O(']', OM_TT_SCLOSE);
O(';', OM_TT_SEMI);
O('=', OM_TT_EQUALS);
O('?', OM_TT_QUEST);
O('@', OM_TT_AT);
O('%', OM_TT_PERCENT);
O(':', OM_TT_COLON);
#undef O
else if (omStreamPeek(lex->fin) == EOF) return (OmTok){ OM_TT_EOF, lex->fin->offset, {0} };
else {
omParserError(lex, lex->fin->offset, "unknown character: '%c' (%d)\n",
omStreamPeek(lex->fin), omStreamPeek(lex->fin));
}
}
MO_NORETURN static void omParserErrorPeek(OmLexer *lex, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
omParserErrorV(lex, omLexerPeekTok(lex).where, fmt, va);
va_end(va);
}
OmVal omParsePrimary(OmSys *sys, OmLexer *lex) {
switch (omLexerPeekTok(lex).type) {
case OM_TT_VAR: {
return omGetGlobal(sys, lex->data.data + omLexerEatTok(lex).offset);
}
case OM_TT_DOLLAR: {
omLexerEatTok(lex);
if (omLexerPeekTok(lex).type != OM_TT_VAR)
omParserErrorPeek(lex, "expected identifier after '$'");
OmVal r = omGetLocal(sys, lex->data.data + omLexerEatTok(lex).offset);
if (omLexerPeekTok(lex).type == OM_TT_EQUALS) {
omLexerEatTok(lex);
OmVal v = omParseExpr(sys, lex, OM_TT_SEMI);
r = omSend(sys, r, "set", &v);
} else if (omLexerPeekTok(lex).type != OM_TT_SLASH) {
r = omSend(sys, r, "get", NULL);
}
return r;
}
case OM_TT_AT: {
omLexerEatTok(lex);
OmFrame *frame = &sys->frames.items[sys->frames.count - 1];
if (frame->uself == NULL) omParserErrorPeek(lex, "not in an object context (no valid '@')");
if (omLexerPeekTok(lex).type == OM_TT_VAR) {
const char *name = lex->data.data + omLexerEatTok(lex).offset;
struct OmObj_Lvar *lvar = NULL;
for (size_t i = 0; i < frame->uself->privs.count; ++i) {
lvar = &frame->uself->privs.items[i];
if (strcmp(lvar->name, name) == 0) break;
}
if (lvar == NULL) omParserErrorPeek(lex, "@%s does not exist", name);
OmVal r = omValOfPtr(lvar);
if (omLexerPeekTok(lex).type == OM_TT_EQUALS) {
omLexerEatTok(lex);
OmVal v = omParseExpr(sys, lex, OM_TT_SEMI);
r = omSend(sys, r, "set", &v);
} else if (omLexerPeekTok(lex).type != OM_TT_SLASH) {
r = omSend(sys, r, "get", NULL);
}
return r;
} else {
return omValOfPtr(frame->uself);
}
}
case OM_TT_INT: return omValOfInt(omLexerEatTok(lex).num);
case OM_TT_STR: {
// TODO: hashmap
const char *s = lex->data.data + omLexerEatTok(lex).offset;
size_t offset = sys->literals.length;
while (*s) omStringPush(&sys->literals, *s++);
omStringPush(&sys->literals, '\0');
return omValOfInt(offset);
}
case OM_TT_OPEN: {
omLexerEatTok(lex);
OmVal r = omParseExpr(sys, lex, OM_TT_CLOSE);
if (omLexerPeekTok(lex).type != OM_TT_CLOSE)
omParserErrorPeek(lex, "expected ')'");
else omLexerEatTok(lex);
return r;
}
case OM_TT_SOPEN: return omParseBlock(sys, lex, false);
case OM_TT_COPEN: {
omLexerEatTok(lex);
struct OmObj_U *u = omAlloc(sizeof(*u)); // TODO: use M/allocate?
memset(u, 0, sizeof(*u));
u->base.recv = &omObjURecv;
u->base.refCount = 0;
u->base.releasing = false;
while (
omLexerPeekTok(lex).type != OM_TT_CCLOSE &&
omLexerPeekTok(lex).type != OM_TT_EOF
) {
if (omLexerPeekTok(lex).type == OM_TT_SLASH) {
omLexerEatTok(lex);
if (omLexerPeekTok(lex).type != OM_TT_VAR)
omParserErrorPeek(lex, "expected message name after '/'");
struct OmObj_Umsg *umsg = omUmsgListPush(&u->msgs);
omStringPushStr(&umsg->name, lex->data.data + omLexerEatTok(lex).offset);
while (
omLexerPeekTok(lex).type != OM_TT_EQUALS &&
omLexerPeekTok(lex).type != OM_TT_EOF
) {
omStringPushStr(&umsg->args, lex->data.data + omLexerEatTok(lex).offset);
omStringPush(&umsg->args, '\0');
}
omStringPush(&umsg->args, '\0');
if (omLexerPeekTok(lex).type != OM_TT_EQUALS) {
omParserErrorPeek(lex, "expected '='");
} else omLexerEatTok(lex);
// bracked levels
int b[3] = { 0, 0, 0 }; // () [] {}
int str = false; // in string
while (
b[0] >= 0 &&
b[1] >= 0 &&
b[2] >= 0 &&
!(b[0] + b[1] + b[2] == 0 && omStreamPeek(lex->fin) == ';') &&
omStreamPeek(lex->fin) != EOF
) {
int c = omStreamGet(lex->fin);
if (c == '"') str = !str;
while (c == '#' && !str) {
while (omStreamPeek(lex->fin) != '\n' && omStreamPeek(lex->fin) != EOF) {
c = omStreamGet(lex->fin);
}
while (isspace(omStreamPeek(lex->fin))) {
c = omStreamGet(lex->fin);
}
}
switch (c) {
case '{': ++b[2]; break;
case '}': --b[2]; break;
case '[': ++b[1]; break;
case ']': --b[1]; break;
case '(': ++b[0]; break;
case ')': --b[0]; break;
}
omStringPush(&umsg->source, c);
}
if (omLexerPeekTok(lex).type != OM_TT_SEMI) {
omParserErrorPeek(lex, "expected ';' after message declaration");
} else omLexerEatTok(lex);
} else if (omLexerPeekTok(lex).type == OM_TT_AT) {
omLexerEatTok(lex);
if (omLexerPeekTok(lex).type != OM_TT_VAR)
omParserErrorPeek(lex, "expected name after '@' in object literal");
size_t varNameOffset = omLexerEatTok(lex).offset;
if (omLexerPeekTok(lex).type != OM_TT_EQUALS)
omParserErrorPeek(lex, "expected '=' after private state declaration in object literal");
else omLexerEatTok(lex);
const char *varName = lex->data.data + varNameOffset;
struct OmObj_Lvar *lvar = omLvarListPush(&u->privs);
lvar->base.recv = &omObjLvarRecv;
lvar->base.refCount = OM_MAX_OBJ_REFCOUNT;
lvar->base.releasing = false;
size_t nameLength = strlen(varName);
lvar->name = omAlloc(nameLength + 1);
memcpy(lvar->name, varName, nameLength + 1);
lvar->wrap = omValOfPtr(NULL);
OmVal v = omParseExpr(sys, lex, OM_TT_SEMI);
omSend(sys, omValOfPtr(lvar), "set", &v);
if (omLexerPeekTok(lex).type != OM_TT_SEMI)
omParserErrorPeek(lex, "expected ';' after private state declaration in object literal");
else omLexerEatTok(lex);
} else {
omParserErrorPeek(lex, "expected '/' or '@' in object literal");
}
}
if (omLexerPeekTok(lex).type != OM_TT_CCLOSE)
omParserErrorPeek(lex, "expected '}'");
else omLexerEatTok(lex);
return omValOfPtr(u);
}
default: omParserErrorPeek(lex, "expected primary expression, got %d", omLexerPeekTok(lex).type);
}
}
OmVal omParseSuffix(OmSys *sys, OmLexer *lex, OmVal after, OmTokType until) {
while (omLexerPeekTok(lex).type == OM_TT_SLASH) {
omLexerEatTok(lex);
if (omLexerPeekTok(lex).type != OM_TT_VAR)
omParserErrorPeek(lex, "expected identifier after '/'");
size_t msgStrOffset = omLexerEatTok(lex).offset;
if (omLexerPeekTok(lex).type != OM_TT_SLASH && until != OM_TT_FLAG) {
OmValList args = {0};
while (omLexerPeekTok(lex).type != until && omLexerPeekTok(lex).type != OM_TT_EOF) {
*omValListPush(&args) = omParseSuffix(sys, lex, omParsePrimary(sys, lex), OM_TT_FLAG);
}
OmVal r = omSend(sys, after, lex->data.data + msgStrOffset, args.items);
omValListFree(&args);
return r;
} else {
after = omSend(sys, after, lex->data.data + msgStrOffset, NULL);
}
}
return after;
}
OmVal omParseExpr(OmSys *sys, OmLexer *lex, OmTokType until) {
return omParseSuffix(sys, lex, omParsePrimary(sys, lex), until);
}
OmVal omParseBlock(OmSys *sys, OmLexer *lex, bool toplevel) {
if (!toplevel) {
if (omLexerPeekTok(lex).type != OM_TT_SOPEN)
omParserErrorPeek(lex, "expected '['");
else omLexerEatTok(lex);
}
OmTokType until = toplevel ? OM_TT_EOF : OM_TT_SCLOSE;
OmVal res = omValOfPtr(NULL);
while (omLexerPeekTok(lex).type != until && omLexerPeekTok(lex).type != OM_TT_EOF) {
// if (omIsValPtr(res)) omRelease(sys, omValAsPtr(res));
res = omParseExpr(sys, lex, OM_TT_SEMI);
if (omLexerPeekTok(lex).type != OM_TT_SEMI)
omParserErrorPeek(lex, "expected ';', got %d", omLexerPeekTok(lex).type);
else omLexerEatTok(lex);
}
if (!toplevel) {
if (omLexerPeekTok(lex).type != OM_TT_SCLOSE)
omParserErrorPeek(lex, "expected ']', got %d", omLexerPeekTok(lex).type);
else omLexerEatTok(lex);
} else {
if (omLexerPeekTok(lex).type != OM_TT_EOF)
omParserErrorPeek(lex, "expected eof, got %d", omLexerPeekTok(lex).type);
}
return res;
}
OmVal omGetGlobal(OmSys *sys, const char *name) {
if (strcmp(name, "L") == 0) return omValOfPtr(&sys->_lObj);
if (strcmp(name, "M") == 0) return omValOfPtr(&sys->_memObj);
if (strcmp(name, "String") == 0) return omValOfPtr(&sys->_strObj);
if (strcmp(name, "IO") == 0) return omValOfPtr(&sys->_ioObj);
if (strcmp(name, "nil") == 0) return omValOfPtr(NULL);
else omPanic("unknown global '%s'!", name);
}
static OmVal omEval(OmSys *sys, const char *source, size_t sourceLength) {
OmStrInputStream sin = {
.base._getc = &omStrInputStreamGet,
.source = source,
.length = sourceLength,
};
OmLexer lex = { .fin = &sin.base };
OmVal ret = omParseExpr(sys, &lex, OM_TT_EOF);
omStringFree(&lex.data);
omUInt32ListFree(&lex._lines);
return ret;
}
bool omRunFile(const char *fpath) {
OmSys sys_ = {
._memObj = { .base.refCount = OM_MAX_OBJ_REFCOUNT, .base.releasing = false, .base.recv = &omObjMemRecv, .alloc_cnt = 0 },
._strObj = { .base.refCount = OM_MAX_OBJ_REFCOUNT, .base.releasing = false, .base.recv = &omObjStrRecv },
._ioObj = { .base.refCount = OM_MAX_OBJ_REFCOUNT, .base.releasing = false, .base.recv = &omObjIORecv, .file = stdout },
._lObj = { .base.refCount = OM_MAX_OBJ_REFCOUNT, .base.releasing = false, .base.recv = &omObjLRecv },
.literals = {0},
};
OmSys *sys = &sys_;
FILE *fin = fopen(fpath, "rb");
if (fin == NULL) {
perror("failed to open file");
return false;
}
OmFileInputStream input = {
.base = { ._getc = &omFileInputStreamGet },
.source = fin,
};
omFrameListPush(&sys->frames);
OmLexer lex = { 0 };
lex.fin = &input.base;
lex.fname = fpath;
omParseBlock(sys, &lex, true);
omStringFree(&lex.data);
omUInt32ListFree(&lex._lines);
fclose(fin);
omPopFrame(sys);
omFrameListFree(&sys->frames);
omStringFree(&sys->literals);
if (omAllocCount_ != 0) {
fprintf(stderr, "LEAKS! alloc count: %lli != 0\n", omAllocCount_);
}
return true;
}
# The standard library:
# To use it, copy the following into your file (no imports yet)
$_ = {
/string s = [
$len = M/c-string-length $s;
$obj = M/object-at (M/allocate ($len /plus 1));
M/morph $obj String/recv;
M/copy-from-c-string $obj/data $s $len;
$obj;
];
/range-step a b c = {
@a = $a; @b = $b; @c = $c;
/begin = @a;
/end = @b;
/step = @c;
/each o = [
M/while {
@o = $o;
@_ = @;
@i = @/begin;
/continue = @i /less-than @_/end;
/do = [ @o/do @i; @i = @i /plus @_/step; ];
};
];
};
/range a b = @/range-step $a $b 1;
/list = [
$obj = {
@_ = @;
@data = M/allocate-bytes 0;
@count = 0;
/item-count = @count;
/byte-count = @/item-count /times M/value-size;
/append-item item = [
$old-bytes = @/byte-count;
@count = @count /plus 1;
$new-bytes = @/byte-count;
$new-data = M/allocate-bytes $new-bytes;
M/copy-bytes $new-data @data $old-bytes;
M/deallocate-bytes @data;
M/acquire $item;
@data = $new-data;
M/copy-from-value (@/address-of-item-at (@count /minus 1)) $item;
];
/address-of-item-at index = @data /plus ($index /times M/value-size);
/item-at index = M/value-at (@/address-of-item-at $index);
/release = [
(@_/range 0 @count)/each {
@o = @;
/do i = M/release (@o/item-at $i);
};
M/deallocate-bytes @data;
];
};
$obj;
];
/read-whole-file path = [
@/open-RO $path {
/do f = [
$size = IO/file-size $f;
$obj = M/object-at (M/allocate ($size /plus 1));
M/morph $obj String/recv;
IO/read $f $size $obj/data;
IO/close $f;
$obj;
];
};
];
/peek-from f = [
$c = IO/read1 $f;
IO/unread1 $f $c;
$c;
];
/is-space c = ($c /equal-to-any-of-n 4 9 10 13 32);
/skip-ws-from file = [
M/while {
@_ = @;
@file = $file;
/continue = @_/is-space (@_/peek-from @file);
/do = IO/read1 @file;
};
];
/input-float prompt = [
IO/print $prompt;
IO/print (@/string ": ");
IO/flush IO/stdout;
@/input-float-from IO/stdin;
];
/input-float-from file = [
@/skip-ws-from $file;
$res = 0/f;
$sign = M/if ((@/peek-from $file) /equal-to 45) {
@file = $file;
/do = [ IO/read1 @file; -1; ];
/else = 1;
};
M/while {
@_ = @;
@res- = L/res;
@file = $file;
/continue = (@_/peek-from @file) /between 48 58;
/do = [
$b = IO/read1 @file;
@res-/get/set ((@res-/get/get /times 10/f) /plus ($b /minus 48)/f);
];
};
M/if ((@/peek-from $file) /equal-to 46) {
@_ = @;
@res- = L/res;
@file = $file;
/do = [
IO/read1 @file;
M/while {
@_ = @_;
@res- = @res-;
@p = 1/f /over 10/f;
@file = @file;
/continue = (@_/peek-from @file) /between 48 58;
/do = [
$b = IO/read1 @file;
@res-/get/set (@res-/get/get /plus (($b /minus 48)/f/times @p));
@p = @p /over 10/f;
];
};
];
/else = nil;
};
$res /times $sign/f;
];
/input-int prompt = [
IO/print $prompt;
IO/print (@/string ": ");
IO/flush IO/stdout;
@/input-int-from IO/stdin;
];
/input-int-from file = [
@/skip-ws-from $file;
$res = 0;
$sign = M/if ((@/peek-from $file) /equal-to 45) {
@file = $file;
/do = [ IO/read1 @file; -1; ];
/else = 1;
};
M/while {
@_ = @;
@res- = L/res;
@file = $file;
/continue = (@_/peek-from @file) /between 48 58;
/do = [
$b = IO/read1 @file;
@res-/get/set ((@res-/get/get /times 10) /plus ($b /minus 48));
];
};
$res /times $sign;
];
/open-WO file-path context = [
$mode = @/string "w";
$f = IO/open $file-path/data $mode/data;
$r = $context/do $f;
IO/close $f;
$r;
];
/open-RO file-path context = [
$mode = @/string "r";
$f = IO/open $file-path/data $mode/data;
$r = $context/do $f;
IO/close $f;
$r;
];
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment