Created
October 28, 2024 20:32
-
-
Save monomere/3c079761985359fe8e292f55d64f7044 to your computer and use it in GitHub Desktop.
abomination
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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