add header gen tool, add main, add archs
This commit is contained in:
parent
5a7f954c24
commit
7dd2fd521e
|
|
@ -102,4 +102,7 @@ flycheck_*.el
|
|||
# network security
|
||||
/network-security.data
|
||||
|
||||
|
||||
# project specific
|
||||
out/
|
||||
.ccls-cache/
|
||||
.vscode/
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
# undar-lang
|
||||
|
||||
A permacomputing & 3D video game oriented language transpiler.
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
TITLE: Undâr Programming Language
|
||||
#+AUTHOR: Zongor
|
||||
#+EMAIL: archive@undar-lang.org
|
||||
#+DATE: [2025-04-05]
|
||||
#+LANGUAGE: en
|
||||
#+OPTIONS: H:4 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
|
||||
#+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
|
||||
#+TAGS: { TODO(t) NEXT(n) DONE(d) | HOLD(h) WAITING(w) CANCELLED(c) }
|
||||
#+PROPERTY: header-args :tangle-mode (identity #o0644)
|
||||
|
||||
A permacomputing & 3D video game oriented language transpiler.
|
||||
|
||||
|
||||
* Getting Started
|
||||
|
||||
** Requirements
|
||||
*** Main
|
||||
- C compiler that supports C89
|
||||
|
||||
** Build
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
git clone https://git.alfrescocavern.com/zongor/undar-lang.git
|
||||
cd undar-lang && ./build
|
||||
#+END_SRC
|
||||
|
||||
* Memory Management
|
||||
|
||||
memory is stack based only.
|
||||
|
||||
For a limited dynamic style you can create unbounded arrays which go on a pre-allocated memory pool.
|
||||
|
||||
dynamic allocations push pointers within a call frame. when a frame exits, the pointer is reset and the child copys its return value to the end of the parent, thus freeing memory deterministically.
|
||||
|
||||
values passed to functions must be explicitly returned to propagate. if a value is modified in a child function it will change the parents value, unless the size of the structure changes then it will copy the parents value and append it to its own frame with the modification. this allows for the low resource usage of a C but the convenience of a Java/Go without the garbage collection.
|
||||
|
||||
**Core Types**
|
||||
|
||||
| Type | Description |
|
||||
|------+------------------------------------|
|
||||
| =byte= | Character/8 bit unsigned int |
|
||||
| =u8= | 8 bit unsigned int |
|
||||
| =i8= | 8 bit signed int |
|
||||
| =u16= | 16 bit unsigned int |
|
||||
| =i16= | 16 bit signed int |
|
||||
| =u32= | exactly 32-bit unsigned integer |
|
||||
| =i32= | exactly 32-bit signed integer |
|
||||
| =int= | signed number |
|
||||
| =nat= | unsigned number |
|
||||
| =real= | Q16.16 fixed-point real number |
|
||||
| =str= | fat pointer [length + data] string |
|
||||
| =bool= | true/false |
|
||||
|
||||
primitive types like int, nat, real, etc. will always be safe to change in child frames.
|
||||
|
||||
complex types like =str,= =plex= or arrays will be references to their location in the memory pool
|
||||
|
||||
* Roadmap
|
||||
|
||||
[[./ROADMAP.org][Compiler, Plex, Immidate mode GUI, Constructive solid geometry, Tunnels, Actor model]]
|
||||
|
||||
* License
|
||||
|
||||
MIT-0
|
||||
|
||||
* Inspirations
|
||||
|
||||
- [[https://plan9.io/][Plan 9]] / 9P - Unified I/O, Tunnels.
|
||||
- [[https://en.wikipedia.org/wiki/Lisp_(programming_language)][Lisp]] - Live coding, REPL, introspection.
|
||||
- [[https://fortran-lang.org/][Fortran]] - Array semantics.
|
||||
- [[https://en.wikipedia.org/wiki/C_(programming_language)][C]] / [[https://ziglang.org/][Zig]] - Portability, control, minimalism.
|
||||
- [[https://lua.org][Lua]] - Friendly syntax, portable, and minimal.
|
||||
- [[https://www.craftinginterpreters.com/the-lox-language.html][Lox]] - The start of my programming language creation journey.
|
||||
- [[https://wiki.xxiivv.com/site/uxn.html][Uxn]] - Major inspiration, espeically around the core VM.
|
||||
- [[http://duskos.org/][Dusk OS]] - A much better system for doing permacomputing.
|
||||
- [[https://doc.cat-v.org/inferno/4th_edition/dis_VM_specification][Dis VM]] - CISC VM structure
|
||||
- Retro Systems - N64, PS1, Mac Classic, Windows 95 - UI esthetics
|
||||
|
|
@ -0,0 +1,309 @@
|
|||
#+TITLE: Undâr Roadmap
|
||||
#+AUTHOR: Zongor
|
||||
#+EMAIL: archive@undar-lang.org
|
||||
#+DATE: [2025-04-05]
|
||||
#+LANGUAGE: en
|
||||
#+OPTIONS: H:4 num:t toc:t \n:nil @:t ::t |:t ^:t -:t f:t *:t <:t
|
||||
#+STARTUP: align fold nodlcheck hidestars oddeven lognotestate
|
||||
#+TAGS: { TODO(t) NEXT(n) DONE(d) | HOLD(h) WAITING(w) CANCELLED(c) }
|
||||
#+PROPERTY: header-args :tangle-mode (identity #o0644)
|
||||
|
||||
* Roadmap
|
||||
|
||||
** Example: Hello world (=hello.ul=)
|
||||
|
||||
*WIP syntax, not final implementation**
|
||||
|
||||
#+BEGIN_SRC ul
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const str nl = "\n";
|
||||
|
||||
plex Terminal {
|
||||
nat handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function
|
||||
*/
|
||||
function main() {
|
||||
pln("nuqneH 'u'?");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print with a newline
|
||||
*/
|
||||
function pln(str message) {
|
||||
Terminal term = open("/dev/term/0", 0);
|
||||
write(term, message, message.length);
|
||||
write(term, nl, nl.length);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
** Example: 3D Client (=client.ul=)
|
||||
|
||||
**WIP syntax, not final implementation**
|
||||
|
||||
Common:
|
||||
|
||||
#+BEGIN_SRC ul
|
||||
/**
|
||||
* Camera.
|
||||
*/
|
||||
plex Camera {
|
||||
int setting;
|
||||
real pov;
|
||||
real[3] up;
|
||||
real[3] pos;
|
||||
real[3] look;
|
||||
|
||||
init(real[3] pos, real[3] look) {
|
||||
this.setting = CAMERA_PERSPECTIVE;
|
||||
this.pov = 45.0;
|
||||
this.up = [0.0, 1.0, 0.0];
|
||||
this.pos = pos;
|
||||
this.look = look;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Player.
|
||||
*/
|
||||
plex Player {
|
||||
Client client;
|
||||
str username;
|
||||
real[3] pos;
|
||||
nat color;
|
||||
Camera camera;
|
||||
|
||||
init(str username, real[3] pos, Color color) {
|
||||
this.client = Client("tcp://localhost:25565");
|
||||
this.username = username;
|
||||
this.pos = pos;
|
||||
this.color = color;
|
||||
this.camera =
|
||||
Camera([this.pos.x + 10.0, this.pos.y + 10.0, this.pos.z], this.pos);
|
||||
}
|
||||
|
||||
login(str password) Player[] { // looks like a method but really it just has an implied "Player this" as the first argument
|
||||
this.client.attach(this.username, password);
|
||||
this.players = client.open("players");
|
||||
return players.read();
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.players.flush();
|
||||
this.client.clunk();
|
||||
}
|
||||
|
||||
update() {
|
||||
if (key_down("right")) {
|
||||
this.pos.x += 0.2;
|
||||
this.client.write(Command(this.username, KEY_RIGHT))
|
||||
}
|
||||
|
||||
if (key_down("left")) {
|
||||
this.pos.x -= 0.2;
|
||||
this.client.write(Command(this.username, KEY_LEFT))
|
||||
}
|
||||
|
||||
if (key_down("down")) {
|
||||
this.pos.z += 0.2;
|
||||
this.client.write(Command(this.username, KEY_DOWN))
|
||||
}
|
||||
|
||||
if (key_down("up")) {
|
||||
this.pos.z -= 0.2;
|
||||
this.client.write(Command(this.username, KEY_UP))
|
||||
}
|
||||
this.camera.sync();
|
||||
}
|
||||
}
|
||||
|
||||
const nat RED = rgb332([255, 0, 0]);
|
||||
const nat WHITE = rgb332([0, 0, 0]);
|
||||
const nat PURPLE = rgb332([255, 255, 0]);
|
||||
#+END_SRC
|
||||
|
||||
Client:
|
||||
#+BEGIN_SRC ul
|
||||
use <str.ul>; // from stdlib
|
||||
use "common.ul"; // from local
|
||||
|
||||
function main(int argc, str[] argv) {
|
||||
nat screen_width = 800;
|
||||
nat screen_height = 450;
|
||||
|
||||
if (argv < 2) {
|
||||
pln("usage: undar client.ul <username> <password>");
|
||||
exit(-1);
|
||||
}
|
||||
str username = argv[1];
|
||||
str password = argv[2];
|
||||
|
||||
Player me(username, [0.0, 1.0, 2.0], PURPLE);
|
||||
|
||||
bool running = true;
|
||||
while (running) {
|
||||
window("zwl client", screen_width, screen_height) {
|
||||
splitbox(parent.size 0.25) {
|
||||
canvas() {
|
||||
if (button("logout")) {
|
||||
me.logout();
|
||||
running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
splitbox(parent.size 0.75) {
|
||||
canvas() {
|
||||
model(Floor([0, 0, 0], 30));
|
||||
me.update();
|
||||
model(Cube(me.pos, [0.5, 0.5, 0.5], me.appearance));
|
||||
if (Player[] players = me.server.read("players")) {
|
||||
for (p in players) {
|
||||
model(Cube(p.pos, [0.5, 0.5, 0.5], p.apperance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
Server:
|
||||
#+BEGIN_SRC ul
|
||||
use "common.ul";
|
||||
|
||||
function main(int argc, str[] argv) {
|
||||
Server s("tcp://0.0.0.0:25565");
|
||||
bool running = true;
|
||||
Player[] players = [Player("user", [0.0, 0.0, 0.0], RED)];
|
||||
while (running) {
|
||||
if (Client client = s.accept("players")) {
|
||||
if (Message message = client.read()) {
|
||||
if (message.t == "close") {
|
||||
client.close();
|
||||
running = false;
|
||||
} else if (message.t == "players") {
|
||||
client.write(players);
|
||||
} else {
|
||||
print("unknown message {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
** Plex
|
||||
|
||||
A =plex= is a structure which, works like a struct but syntactically looks like a class. the naming of "plex" comes from douglas ross's paper "a generalized technique for symbol manipulation and numerical calculation". It is used instead of "class" or "struct" is to make a break from the historical baggage of "classes". unlike classes it does not describe a object in real life and copy it, but it allows for a convenient way to mediate states and handling. i.e. compositions + encodings instead of oop/polymorphisms. for example, instead of having a struct with a bool flag in it (like for a game alive/dead), we can create a multilist where the structs move from a "alive" list to a "dead" list; or things like that. instances of a plex in memory are called "atoms".
|
||||
|
||||
Plexes support permacomputing by allowing the developer to use lower level traits with a friendly syntax.
|
||||
|
||||
**WIP syntax, not final implementation**
|
||||
|
||||
#+BEGIN_SRC c
|
||||
plex Player {
|
||||
str name;
|
||||
real[3] pos;
|
||||
|
||||
init(str name, real[3] pos) {
|
||||
this.name = name;
|
||||
this.pos = pos;
|
||||
}
|
||||
update() {}
|
||||
logout() {}
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
the language is statically typed and similar to c but with some array semantic ideas from fortran like row major, fortran style replaces need for vec or mat. arrays are first class values, the compiler uses optimized opcodes for array manipulation.
|
||||
|
||||
**WIP syntax, not final implementation**
|
||||
|
||||
#+BEGIN_SRC c
|
||||
real[3] pos = [1.0, 2.0, 3.0];
|
||||
real[3][3] mat = identity(3);
|
||||
real[3] result = mat * pos; // compiler generates matrix-vector multiply
|
||||
#+END_SRC
|
||||
|
||||
- Row-major order
|
||||
- No =vec3=/=mat4x4= structs needed
|
||||
- Supports composition, slicing, element-wise ops
|
||||
|
||||
it has a abstraction layer for devices that work as system calls that can open, read, write, close, and use ioclt for extra system calls that are beyond ops.
|
||||
|
||||
** Devices
|
||||
|
||||
(partially implemented)
|
||||
|
||||
core devices include
|
||||
- screen
|
||||
- mouse
|
||||
- keyboard
|
||||
- terminal
|
||||
- tunnel
|
||||
|
||||
Devices are accessed via a namespace "path/to/device" and are implemented outside of the VM's runtime. This allows for the trait of the system to be the same within the VM but allow for specific variations on a concept depending on the device it is running on.
|
||||
|
||||
*** Immediate Mode GUI
|
||||
|
||||
UI elements are draw directly on the canvas and syntactically are spacial to show which elements are child elements of each other. Calling a function is the equivalent of a redraw of that element
|
||||
|
||||
*** 3D Modeling
|
||||
|
||||
3D Primitives + Constructive solid geometry (Similar to OpenSCAD or .kkrieger) allow for the construction of more complex 3D models from the manipulation of primitives. It also has the ability to create textures dynamically similar to .kkrieger, textures can be imported and auto mipmapped from larger sizes down to the internal RGB332 + 8x8 - 32x32 tile system.
|
||||
|
||||
** Tunnels: Unified I/O (Plan 9 / 9P-Inspired)
|
||||
|
||||
Tunnels are an abstraction called a tunnel which acts like a device like a screen, mouse, etc. that is inspired by Plan9. Plan 9 was an operating system developed at Bell Labs that treated all resources (including network connections and UI elements) as files in a hierarchical namespace. it allows files, web requests, sockets, etc. to be viewed through a simple unified trait. it is very similar to the 9p protocol where everything is a file in a filesystem, it just might be a file located on a server halfway across the world or on another planet. the only thing that is the difference is that it takes longer to read the file on mars compared to the file on your local system. the way the device system works is that it is written by a developer in another language like c, but from Undâr's pov the trait remains the same, its just a namespaced device like all the other devices, so that it is easy to understand by a new developer. so it is comparable with tcp/udp sockets or something esoteric like a lorawan network, or some new communication method that hasn't been invented yet.
|
||||
|
||||
**WIP syntax, not final implementation**
|
||||
|
||||
#+BEGIN_SRC ul
|
||||
Tunnel server = Tunnel("tcp://localhost:25565");
|
||||
if (server.attach(auth)) {
|
||||
Player[] players = server.read("players");
|
||||
server.write("me", me.update());
|
||||
server.clunk();
|
||||
}
|
||||
#+END_SRC
|
||||
|
||||
*** Tunnel Operations
|
||||
|
||||
| Op | Meaning |
|
||||
|------------+-------------------------------------------------------------------|
|
||||
| =.attach()= | Authenticate and open communication |
|
||||
| =.open()= | opens a tunnel for doing operations on |
|
||||
| =.create()= | creates the plex from the database graph file from file structure |
|
||||
| =.remove()= | removes the plex from the database graph file from file structure |
|
||||
| =.read()= | reads from a tunnel |
|
||||
| =.write()= | writes to a tunnel |
|
||||
| =.walk()= | moves around the filesystem or through the graph |
|
||||
| =.flush()= | cancels long operation and dumps whatever is in buffer |
|
||||
| =.clunk()= | Close communication |
|
||||
| =.stat()= | returns the status of the file resource |
|
||||
| =.version()= | returns the version code for the connected tunnel |
|
||||
|
||||
** Actor System
|
||||
|
||||
*** Communication Protocol
|
||||
- Bounded mailbox system
|
||||
- Message flow:
|
||||
1. Main thread sends via =send_message()=
|
||||
2. Message copied into actor's mailbox
|
||||
3. Actor processes in =on_message()= during next cycle
|
||||
4. Actor sends response via =send_response()=
|
||||
|
||||
*** Failure Behaviors
|
||||
| Behavior | Action | Example / Use Case |
|
||||
|----------+-------------------------+------------------------|
|
||||
| =RESTART= | Reset arena offset to 0 | Network reconnection |
|
||||
| =EXIT= | Free arena | Session termination |
|
||||
| =CRASH= | Halt entire VM | Failure of core system |
|
||||
| | | |
|
||||
|
|
@ -0,0 +1 @@
|
|||
// wip
|
||||
|
|
@ -0,0 +1 @@
|
|||
// wip
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z $MODE ]; then
|
||||
MODE='debug'
|
||||
fi
|
||||
|
||||
if [ -z $CC ]; then
|
||||
CC='gcc'
|
||||
fi
|
||||
|
||||
# setup dirs
|
||||
BUILD_DIR=./out
|
||||
GEN_DIR=$BUILD_DIR/gen
|
||||
TOOL_SRC=tools/file2header.c
|
||||
TOOL_EXE=$BUILD_DIR/file2header
|
||||
|
||||
# clean cmd
|
||||
case $1 in
|
||||
"clean")
|
||||
echo "Deleting $BUILD_DIR"
|
||||
rm -rf $BUILD_DIR
|
||||
exit 0
|
||||
esac
|
||||
|
||||
# create the build dir if it doesnt exist
|
||||
if [ -d $BUILD_DIR ]; then
|
||||
echo "Building to $BUILD_DIR"
|
||||
else
|
||||
echo "$BUILD_DIR not found, creating"
|
||||
mkdir -p $BUILD_DIR
|
||||
fi
|
||||
|
||||
# Ensure gen dir exists
|
||||
mkdir -p $GEN_DIR
|
||||
|
||||
# Check if tool source exists
|
||||
if [ ! -f $TOOL_SRC ]; then
|
||||
echo "Error: $TOOL_SRC not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Compile tool if exe missing or source changed
|
||||
if [ ! -f $TOOL_EXE ] || [ $TOOL_SRC -nt $TOOL_EXE ]; then
|
||||
echo "Compiling file2header tool..."
|
||||
$CC $TOOL_SRC -o $TOOL_EXE
|
||||
fi
|
||||
|
||||
if [ -d "arch" ]; then
|
||||
for file in arch/*.c; do
|
||||
name=$(basename "$file" .c)
|
||||
out_header="$GEN_DIR/${name}.h"
|
||||
|
||||
if [ ! -f "$out_header" ] || [ "$file" -nt "$out_header" ]; then
|
||||
echo "Packing $file -> $out_header"
|
||||
$TOOL_EXE "$file" > "$out_header"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "Warning: 'arch' directory not found."
|
||||
fi
|
||||
|
||||
# setup the build flags based on the build mode
|
||||
case $MODE in
|
||||
"debug")
|
||||
BUILD_FLAGS="-g -Wall -Wextra -Werror -pedantic"
|
||||
;;
|
||||
"release")
|
||||
BUILD_FLAGS="-O3 -Wall -Wextra -Werror -pedantic"
|
||||
;;
|
||||
esac
|
||||
|
||||
BUILD_CMD="$CC -o $BUILD_DIR/undar main.c -I$GEN_DIR $BUILD_FLAGS"
|
||||
|
||||
echo "$BUILD_CMD"
|
||||
${BUILD_CMD}
|
||||
|
||||
echo "Finished building to $BUILD_DIR/undar"
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define EMBED_FILE(name) \
|
||||
void emit_##name(const char *filename) { \
|
||||
FILE *f = fopen(filename, "wb"); \
|
||||
if (f) { \
|
||||
fwrite(name, 1, name##_len, f); \
|
||||
fclose(f); \
|
||||
} \
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
char *name;
|
||||
|
||||
if (argc > 1) {
|
||||
name = argv[1];
|
||||
} else {
|
||||
name = "'u'";
|
||||
}
|
||||
|
||||
printf("nuqneH %s?\n", name);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
FILE *in;
|
||||
int c;
|
||||
long count = 0;
|
||||
long col = 0;
|
||||
char *var_name;
|
||||
char *p;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <input_file>\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
in = fopen(argv[1], "rb");
|
||||
if (!in) {
|
||||
perror("Error opening input file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
var_name = (char *)malloc(strlen(argv[1]) + 1);
|
||||
if (!var_name) {
|
||||
perror("Memory allocation failed");
|
||||
fclose(in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
strcpy(var_name, argv[1]);
|
||||
|
||||
for (p = var_name; *p; ++p) {
|
||||
if (!isalnum((unsigned char)*p)) {
|
||||
*p = '_';
|
||||
}
|
||||
}
|
||||
|
||||
printf("unsigned char %s[] = {\n", var_name);
|
||||
|
||||
c = fgetc(in);
|
||||
while (c != EOF) {
|
||||
printf(" 0x%02x", c);
|
||||
count++;
|
||||
|
||||
int next = fgetc(in);
|
||||
if (next != EOF) {
|
||||
printf(",");
|
||||
ungetc(next, in);
|
||||
if (++col >= 12) {
|
||||
printf("\n");
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
|
||||
c = fgetc(in);
|
||||
}
|
||||
|
||||
printf("\n};\n");
|
||||
printf("unsigned int %s_len = %lu;\n", var_name, count);
|
||||
free(var_name);
|
||||
fclose(in);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue