cleanup, add more stringops

This commit is contained in:
zongor 2025-09-09 21:53:52 -07:00
parent d5479d1b7e
commit 7634bbe1c9
5 changed files with 40 additions and 62 deletions

3
.gitattributes vendored
View File

@ -1 +1,2 @@
*.ul linguist-language=fortran *.ul linguist-language=fortran
*.zl linguist-language=zig

View File

@ -1,4 +1,4 @@
#+TITLE: A Language for Enduring Realities #+TITLE: The Reality Engine
#+AUTHOR: Zongor #+AUTHOR: Zongor
#+EMAIL: archive@undar-lang.org #+EMAIL: archive@undar-lang.org
#+DATE: [2025-04-05] #+DATE: [2025-04-05]
@ -15,8 +15,6 @@
· · · ᚾ] · · · ᚾ]
#+END_SRC #+END_SRC
* The Reality Engine
The =Reality Engine= is a register-based virtual machine designed to render not just graphics, but persistent, inspectable, reproducible computational worlds. The =Reality Engine= is a register-based virtual machine designed to render not just graphics, but persistent, inspectable, reproducible computational worlds.
It is: It is:
@ -55,16 +53,16 @@ Undâr is a permacomputing oriented, statically-typed language with **first-clas
It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=. It runs on the =Reality Engine=, a minimal C89 VM inspired by Uxn, Plan 9, and Forth - but built for =spatial software=, =deterministic execution=, and =software that lasts=.
Sċieppan is a bytecode assembler that is inspired by Webassemblys WAT format. Sċieppan is a minimal lisp inpsired by sectorlisp.
You can view some examples in the =.lisp= files in =/test= You can view some examples in the =.lisp= files in =/test=
**Core Types** **Core Types**
| Type | Description | | Type | Description |
|------+-------------------------------------------| |--------+-------------------------------------------|
| =int= | 32-bit signed integer | | =int= | 32-bit signed integer |
| =nat= | 32-bit natural number | | =nat= | 32-bit natural number |
| =real= | Q16.16 fixed-point real number | | =real= | Float/Q16.16 fixed-point real number |
| =str= | 4-byte packed string or fat pointer | | =str= | 4-byte packed string or fat pointer |
| =bool= | Compile-time flag | | =bool= | Compile-time flag |
| =ref= | Reference prefix for passing by reference | | =ref= | Reference prefix for passing by reference |
@ -105,7 +103,6 @@ A =plex= is a **Platonic form** - a structured definition of a kind of being in
#+BEGIN_SRC ul #+BEGIN_SRC ul
plex Player { plex Player {
version 1;
str name; str name;
real[3] pos; real[3] pos;
@ -120,36 +117,8 @@ plex Player {
- Not a class: no inheritance, no vtables - Not a class: no inheritance, no vtables
- Methods are functions with implicit =this= argument - Methods are functions with implicit =this= argument
- Instances are **atoms** - persistent, versioned, serializable - Instances are **atoms**
- Stored in the internal graph - A plex defines what a thing is. An atom is its instance.
> *"A plex defines what a thing is. An atom is its instance in that reality."*
* Versioning & Shadowing (Forth-Inspired)
When you redefine a =plex=, the old version is **shadowed but preserved** - unless explicitly discarded.
#+BEGIN_SRC ul
plex Counter { version 1; nat value; inc() { value += 1; } }
plex Counter { version 2; nat value; inc() { value += 2; } } ! shadows v1
Counter c1 = Counter(); ! uses v2 (latest)
Counter c2 = Counter.v1(); ! uses v1 - still available
discard Counter.v1; ! optional: free memory
#+END_SRC
Internally, plex versions form a **linked version chain**:
- =head= -> latest version
- =tail= -> oldest retained version
- =migrate(obj, Counter)= -> converts data layout
- =versions(Counter)= -> list available versions
This enables:
- Non-destructive evolution
- Safe refactoring
- Historical reproducibility
- Code archaeology
* Graphics & Devices * Graphics & Devices
@ -213,23 +182,8 @@ if (server.attach(auth)) {
Tunnels make I/O **uniform, composable, and archival**. Tunnels make I/O **uniform, composable, and archival**.
* Development Environment
supports **live coding** and **temporal development**:
**Live Coding Features** **Live Coding Features**
- Hot module reloading: inject code while VM runs
- REPL-style interaction: inspect memory, call functions, test logic - REPL-style interaction: inspect memory, call functions, test logic
- Shadowing: redefine =plex=es without restarting
- Symbol table manipulation: runtime introspection and patching
**Final Binaries**
- Are **snapshots** of:
- Memory state
- Symbol table
- Version chains
- Can be saved, restored, or archived as =.zbin= files
- Are fully deterministic and reproducible
* Getting Started * Getting Started

View File

@ -71,9 +71,6 @@ typedef enum {
#define OP(opcode, dest, src1, src2) \ #define OP(opcode, dest, src1, src2) \
((opcode << 24) | (dest << 16) | (src1 << 8) | (src2)) ((opcode << 24) | (dest << 16) | (src1 << 8) | (src2))
#define OP_SYSCALL_OPCODE(syscall_id, arg_count, src) \
((OP_SYSCALL << 24) | ((syscall_id & 0xFF) << 16) | (arg_count & 0xFF) | src)
typedef union value_u { typedef union value_u {
int32_t i; /* Integers */ int32_t i; /* Integers */
float f; /* Float */ float f; /* Float */

View File

@ -189,6 +189,10 @@ bool step_vm(VM *vm) {
case OP_GET_PC: { case OP_GET_PC: {
vm->frames[vm->fp].registers[dest].u = vm->pc; vm->frames[vm->fp].registers[dest].u = vm->pc;
return true; return true;
}
case OP_JMP: {
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
return true;
} }
case OP_SYSCALL: { case OP_SYSCALL: {
uint32_t syscall_id = dest; uint32_t syscall_id = dest;
@ -385,10 +389,6 @@ bool step_vm(VM *vm) {
(float)(vm->frames[vm->fp].registers[src1].u); (float)(vm->frames[vm->fp].registers[src1].u);
return true; return true;
} }
case OP_JMP: {
vm->pc = vm->frames[vm->fp].registers[dest].u; /* Jump to address */
return true;
}
case OP_JEQ_UINT: { case OP_JEQ_UINT: {
COMPARE_AND_JUMP(uint32_t, u, ==); COMPARE_AND_JUMP(uint32_t, u, ==);
} }
@ -458,6 +458,28 @@ bool step_vm(VM *vm) {
vm->frames[vm->fp].registers[dest].u = ptr; vm->frames[vm->fp].registers[dest].u = ptr;
return true; return true;
} }
case OP_STRING_TO_INT: {
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
char *endptr;
int32_t value = (int32_t)strtol((char*)&vm->memory[src_addr], &endptr, 10);
vm->memory[dest_addr].i = value;
return true;
}
case OP_STRING_TO_UINT: {
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
long value = atol((char*)&vm->memory[src_addr]);
vm->memory[dest_addr].u = value;
return true;
}
case OP_STRING_TO_REAL: {
uint32_t src_addr = (uint32_t)vm->frames[vm->fp].registers[src1].u;
uint32_t dest_addr = (uint32_t)vm->frames[vm->fp].registers[dest].u;
float value = atof((char*)&vm->memory[src_addr]);
vm->memory[dest_addr].u = value;
return true;
}
case OP_DBG_READ_STRING: { case OP_DBG_READ_STRING: {
uint32_t str_addr = vm->mp++; uint32_t str_addr = vm->mp++;
uint32_t length = 0; uint32_t length = 0;

View File

@ -1 +1,5 @@
(puts "nuqneH 'u'?") hello:
"nuqneH 'u'?"
puts &hello
halt