ox

The Ox programming language, compiler and tools (WIP)
Log | Files | Refs | README | LICENSE

commit 830a9f0d339d5de45af8bcee5279f42de1ea575c
parent b8ecba974bf631cdb7814419a2c588a24232f9b1
Author: citbl <citbl@citbl.org>
Date:   Thu, 20 Nov 2025 18:05:23 +1000

wip

Diffstat:
M.gitignore | 2++
MTODO | 17++++++++++++++++-
Aoxdesign.ox | 248+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Astuff/micro/syntax/ox.yaml | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 325 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -6,3 +6,5 @@ fox err.log *.o out +notes.* +*.webarchive diff --git a/TODO b/TODO @@ -7,7 +7,7 @@ [x] printing `-5` crashes, I think parsing is wrong for `-`, sees as value 5 [x] print allow more than 1 arg [x] handle return values -[ ] print allow format +[ ] print allow format [x] print anything else than a string [x] call another function from main, that prints something, [x] call another function that prints the passed argument @@ -24,3 +24,18 @@ [x] redo arguments as list and not linked list, handle in parse and in gen (2 places in gen?) [x] get rid of count_args and search for 'argc' [ ] clean up symbols table from parsing to jit time + +@notes + +to get specific access to float sizes, gccjit@16 is required, which can't be easily built on macOS atm. + + CC_JIT_TYPE_FLOAT16 + GCC_JIT_TYPE_FLOAT32 + GCC_JIT_TYPE_FLOAT64 + GCC_JIT_TYPE_FLOAT128 + + otherwise these are target platform dependent + + GCC_JIT_TYPE_FLOAT + GCC_JIT_TYPE_DOUBLE + GCC_JIT_TYPE_LONG_DOUBLE diff --git a/oxdesign.ox b/oxdesign.ox @@ -0,0 +1,248 @@ +i32 ~x = 42 // make a integer 32bit that is a variable (can be updated) `~` initialised with 2. +i32 x = 42 // like a "let" statement or final variable, runtime constant. +i32 #x = 42 // compile time constant + +// give me the raw pointer of x and assign it to y +i32* y = &x + + +// default types +// i8, i16, i32, i64, +// u8, u16, u32, u64, +// f8, f16, f32, f64, +// str, chr, bool, + +// arrays (fixed) and lists (dynamic) +arr<i32>[16] ages // fixed slice of 16 +vec<i32> bobs // dynamic list +set<str> names // sets + +type Person { + i32 age + str name +} + +fn say_hello () void { + print("hello") // print is part of stdlib.local which is auto imported + warn("hello") // this print to stderr instead + print_("hello\n") // all of the above but with `_` won't insert a trailing \n + fatal("hello") // fatal print, returning non-zero +} + +// strings are really arrays of special u32, aka vec<chr> +typedef chr u32 // (but u32 is not a rune/chr, as the whole UTF-16 range does not cover u32) + +// exported function returns one i32 +fx add (i32 a, b) i32 => a + b + +// fx = function is exported +// fn = function is static + +fx passed (Person p) {} // ownership passed and cannot be used after +fn borrow (Person& p) {} // borrowed and unmutable +fn mutate (Person~ p) {} // borrowed and mutable +fn unsafe (Person* p) unsafe {} // C style passing of pointer, must tag `unsafe` + +// lambda style function definition +fx add = (i32 a, b) i32 => a + b + +// void return is implicit, so is void type arg +fx say_hello() { + print("hello") +} + +// multiple return values go into parens +fx flip(i32 a, b) (i32, i32) => b, a + +struct Person { + str name + i16 age + i32 number + str street + str suburb + str postcode + str country + // string interpolation `$.` is identifying a type component + fx address () str => "$.number, $.street, $.suburb $.postcode\n$.country" +} + +extend Person { + ... +} + +// lambda style main function +fn main => print("hello world") + +fn main(i32 argc, arr<str> argv) int = { + if argc != 0 { + print("usage...") + return 0 + } + print("hello world! $argc") // string interpolation similar to Dart's + return 0 +} + +fn rename({str name}) str {} // enforce the label to be passed: rename(name:"jack") + +// if statement don't have required parens, they're optional + +if a == b { + do_this(); +} else if a == c { + do_that(); +} else { + do_nothing(); +} + +// inline if can be done as is +if cache_miss() { print("cached missed!") } + +// ternary if +i32 bla = cond ? 42 : 420 + +// for statements +// classic for statement use commas separated init, cond and modifier +for (i32 i = 0, i < 42, i++) {} + +for i32 i in 0..< 42 {} // shorthand for loop with for each + +for chr c in word {} // for each .. in + +for (chr c, i32 i) in word {} // for each in with index, parens optional + +// while loops +while cond == true { + // body +} + +// infinite loop until broken +loop {} + +// switch +switch (action.key) { + KEY_ENTER { + open_door() + } + KEY_UP { move_up() } + default { + warn("unsupported!") + } +} + +// match expr +chr c = match key { + 61 => 'a' + 62 => 'b' + default => '_' +} + + +// dealing with nil values. They're not allowed unless unsafe in play. +Person? p = find("jack") + +if Person x = p { + print("found jack of ${x.age} age") // safe usage +} + +if p { + print("found jack of ${p!.age} age") // force unwrap +} + +Person* p = NULL // is allowed. NULL is a constant 0xffffff of some kind. + +// not allowed: Person&? Person~? + +// null coalescing of maybes +print("jack may be of ${p?.age ?? \"some unknown\"}") + +i32 res = some_test() ?? 42 + +// namespacing + +ns main // is default and not required, this unit will require a main function +ns tools // loosely required to be in tools/ + +// using namespaces, importing, namespaces are forced lowercase to not impact types + +use math // bring all of math functions in, to be prefixed by math.something +use math { random, sin, cos } // limit the import to these symbols, directly accessible: sin() +use math as mth // alias math to `mth` + + +// async operations using the Aloha assignment operator `~=` + +i32? response = await some_long_action() + +// error handling + +fn fetch_data({str url}) async str! { + // fetch the data of the website, parse the body content + // return the body content + await ... + return content +} + +struct Error { + u16 code + str message + Error? cause +} + +str website_data = await try fetch_data(url: "fleacebook.com") or Error e => print("could not fetch data ${e.message}") + + +fx flip(i32 a, b) (i32, i32)! { + +} + + +// APPENDIX + +// keywords +// return, break, continue, if, else, for, while, loop, goto, defer, heap, free, +// type, ext, union, arr, vec, set, typedef, fx, fn, maybe, mut, ref, ptr, voidptr, +// unsafe, inline + + +// types +// all of the +// stdlib.local auto imported +// equivalent of `use always { print, print_, warn, warn_, fatal }` +// print, print_, warn, warn_, fatal + +// FFI interaction with C and C types +// core FFI aliases +typedef voidptr = void* +typedef cstr = char* +typedef ccstr = const char* + +use math +use c <stdio.h> +use c <stdlib.h> +use c <stdint.h> +use c "include/termbox2.h" + +extern c { + fn printf(ccstr, ...) int + fn free(voidptr) + fn malloc(size_t) voidptr + fn tb_print(i32, i32, u16, u16, cstr); // x, y, fg, bg, text + + // NOTES + // don't use `const char*` etc. and warn against it at compile time + // use cstr or ccstr instead to avoid the baggage of weird + // pointer precedence and const position + // +} + +fn main() { + voidptr ptr = malloc(65128) + // invalid: *ptr = 420 // voidptr should be undereferenceable + // no deref of voidptr without a cast + ptr<u8> bytes = cast(ptr<u8> p) + bytes[0] = 42 + free(ptr) + + // presume init called etc. + tb_print(12, 12, TB_MAGENTA, TB_BLACK, c_const_str("some words of wisdom")) +} diff --git a/stuff/micro/syntax/ox.yaml b/stuff/micro/syntax/ox.yaml @@ -0,0 +1,59 @@ +filetype: ox + +detect: + filename: "(\\.(ox|OX)$)" + +rules: + - identifier: "\\b[A-Z_][0-9A-Z_]+\\b" + - type: "\\b(float|bool|char|int|uint|short|str|err|bool|string|long|enum|void|union|voidptr|typeof|typeof_unqual|(un)?signed|_Noreturn)\\b" + - type: "\\b((s?size)|ptr|cstr|ccstr|(i|u|f)(8|16|32)|chr)_?t?\\b" + - type: "\\b(_Float16|__fp16|_Float32|_Float32x|_Float64|_Float64x|__float80|_Float128|_Float128x|__float128|__ibm128|__int128|_Fract|_Sat|_Accum)\\b" + - type: "\\b[a-z_][0-9a-z_]+(_t|_T)\\b" + - type: "\\b[A-Z]I?[0-9a-zA-Z_]+\\b" + - statement: "\\b(auto|volatile|register|typedef|new|extend|restrict|_Alignas|alignas|_Alignof|alignof|static|async|await|inline|const|var|constexpr|extern c|_Thread_local|thread_local)\\b" + - statement: "\\b(fx|fn|use ?c?|as|try|or|export|c_str|c_const_str|unsafe|struct|set|vec|arr|free|heap|print_?|warn_?|fatal|cast|in|ns|for|each|if|while|loop|match|else|case|default|switch|_Generic|_Static_assert|static_assert)\\b" + - statement: "\\b(goto|continue|break|return)\\b" + - statement: "\\b(asm|fortran)\\b" + - preproc: "^[[:space:]]*#[[:space:]]*(define|embed|pragma|include|(un|ifn?)def|endif|el(if|ifdef|ifndef|se)|if|line|warning|error|__has_include|__has_embed|__has_c_attribute)" + - preproc: "^[[:space:]]*_Pragma\\b" + # GCC builtins + - statement: "__attribute__[[:space:]]*\\(\\([^)]*\\)\\)" + - statement: "__(aligned|asm|builtin|extension|hidden|inline|packed|restrict|section|typeof|weak)__" + # Operator Color + - symbol.operator: "[-+*/%=<>.:;,~&|^!?]|\\b(offsetof|sizeof)\\b" + - symbol.brackets: "[(){}]|\\[|\\]" + # Integer Constants + - constant.number: "(\\b([1-9][0-9]*|0[0-7]*|0[Xx][0-9A-Fa-f]+|0[Bb][01]+)([Uu][Ll]?[Ll]?|[Ll][Ll]?[Uu]?)?\\b)" + # Decimal Floating Constants + - constant.number: "(\\b(([0-9]*[.][0-9]+|[0-9]+[.][0-9]*)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)[FfLl]?\\b)" + # Hexadecimal Floating Constants + - constant.number: "(\\b0[Xx]([0-9A-Za-z]*[.][0-9A-Za-z]+|[0-9A-Za-z]+[.][0-9A-Za-z]*)[Pp][+-]?[0-9]+[FfLl]?\\b)" + - constant.bool: "(\\b(true|false|NULL|nullptr|TRUE|FALSE)\\b)" + + - constant.string: + start: "\"" + end: "\"" + skip: "\\\\." + rules: + - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" + + - constant.string: + start: "'" + end: "'" + skip: "\\\\." + rules: + # TODO: Revert back to - error: "..+" once #3127 is merged + - error: "[[:graph:]]{2,}'" + - constant.specialChar: "\\\\([\"'abfnrtv\\\\]|[0-3]?[0-7]{1,2}|x[0-9A-Fa-f]{1,2}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})" + + - comment: + start: "//" + end: "$" + rules: + - todo: "(TODO|XXX|FIXME):?" + + - comment: + start: "/\\*" + end: "\\*/" + rules: + - todo: "(TODO|XXX|FIXME):?"