sterling os, moved sterling lang into same project directory

This commit is contained in:
fatmeat 2025-05-17 06:39:38 +02:00
commit f2ffe70790
17 changed files with 1227 additions and 0 deletions

54
.gitignore vendored Normal file
View File

@ -0,0 +1,54 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
*_exclude

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright 2025 Carle-Margueritte Alexandre
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

10
LICENSE_OVERVIEW.md Normal file
View File

@ -0,0 +1,10 @@
# Sterling OS License Overview
This repository contains multiple components under different licenses:
- **Sterling Programming Language (compiler/runtime/stdlib)** — Apache-2.0
- **Sterling OS (kernel, bootloader, userland)** — 0BSD
- **All documentation and HTML content** — MIT
We prioritize maximum freedom, composability, and reuse across all domains.

0
README.md Normal file
View File

12
boot/LICENSE Normal file
View File

@ -0,0 +1,12 @@
0BSD License
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

12
docs/LICENSE Normal file
View File

@ -0,0 +1,12 @@
MIT License
Copyright 2025 Carle-Margueritte Alexandre
Permission is hereby granted, free of charge, to any person obtaining a copy
of this documentation to deal in the Documentation without restriction,
including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Documentation, and to
permit persons to whom the Documentation is furnished to do so, subject to
the following conditions:
THE DOCUMENTATION IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

163
docs/design.html Normal file
View File

@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Operating System Design Manifesto</title>
<style>
body {
font-family: monospace;
background-color: #fefefe;
color: #111;
max-width: 960px;
margin: auto;
padding: 2rem;
}
h1, h2, h3 {
border-bottom: 1px solid #ddd;
padding-bottom: 0.2em;
margin-top: 2em;
}
pre {
background: #eee;
padding: 1em;
overflow: auto;
}
code {
background: #eee;
padding: 0.1em 0.3em;
}
ul {
padding-left: 1.5em;
}
</style>
</head>
<body>
<h1>Minimal Capability-Based Operating System</h1>
<h2>Design Principles</h2>
<ul>
<li>No global filesystem, no path resolution</li>
<li>No drivers in kernel, only sandboxed userspace driver processes</li>
<li>No GPU acceleration, all rendering is deterministic software-based</li>
<li>All resources accessed via capability tokens</li>
<li>Processes are strictly sandboxed</li>
<li>Programs operate on memory buffers, not raw file handles</li>
<li>Desktop environment is a sandboxed coordinator, not a privileged process</li>
</ul>
<h2>Authorization Token Model</h2>
<p>Programs delegate access via opaque, kernel-managed tokens.</p>
<pre><code>grant_token(target_pid, resource_id, flags) -&gt; token_id
accept_token(token_id) -&gt; resource_handle
revoke_token(token_id)</code></pre>
<h2>File Editing Flow</h2>
<ol>
<li>DE requests file via storage service</li>
<li>Storage service provides a memory buffer</li>
<li>Editor process receives buffer handle, edits</li>
<li>Changes submitted back to storage via DE</li>
</ol>
<h2>Driver Model</h2>
<ul>
<li>All drivers run as fully unprivileged user processes</li>
<li>No driver registration or kernel mediation required</li>
<li>Drivers communicate with hardware via explicit kernel-exposed capability channels</li>
<li>No dynamic linking or privileged probing allowed</li>
<li>Users can run or replace any driver without OS permission</li>
</ul>
<h2>Graphics System</h2>
<ul>
<li>No GPU support, no shaders</li>
<li>Software renderer processes draw via shared memory</li>
<li>DE composites framebuffers deterministically</li>
</ul>
<h2>Programming Language Requirements</h2>
<ul>
<li>Manual memory management</li>
<li>Low-level data layout control</li>
<li>Inline assembly support</li>
<li>Pattern matching and compile-time macros</li>
<li>No runtime, no global init, no dynamic linking</li>
</ul>
<h2>Execution Model</h2>
<ul>
<li>Programs are spawned with exact buffer and token permissions</li>
<li>No shared global state</li>
<li>All IO is mediated via explicit capability-based services</li>
<li>Everything is inspectable and reproducible</li>
</ul>
<h2>Sandboxing Model</h2>
<p>All processes are isolated via strict memory boundaries and capability-scoped access. No process can access global state, shared memory, or system calls without explicit capability grants.</p>
<h3>Memory Layout</h3>
<pre class="diagram">
+-----------------------+
| Code (RX) |
+-----------------------+
| Data (RW) |
+-----------------------+
| Shared Buffers (RWX?) | ← only if explicitly mapped by kernel
+-----------------------+
| Stack (RW) |
+-----------------------+
</pre>
<h3>Process Launch</h3>
<ul>
<li>Preallocated memory map (no heap growth)</li>
<li>Passed a syscall pointer table, token list, and init buffer</li>
<li>Cannot request global system resources directly</li>
</ul>
<h3>Capability Enforcement</h3>
<p>All access is mediated via capability tokens, handed off securely:</p>
<pre><code>
token_id = request_token(pid, SERVICE_IO, READ_WRITE);
handle = accept_token(token_id);
</code></pre>
<ul>
<li>Token scope, rights, and duration enforced by kernel</li>
<li>No access without explicit grant</li>
<li>All capability use is auditable and revocable</li>
</ul>
<h3>Filesystem Abstraction</h3>
<ul>
<li>No global file system</li>
<li>Programs receive only memory buffers with scoped access</li>
<li>Read/write must go through kernel-mapped tokens</li>
</ul>
<h3>Driver Isolation</h3>
<ul>
<li>Drivers are userland processes only</li>
<li>No direct port I/O or DMA access</li>
<li>Hardware is accessed via kernel-exposed capability channels</li>
</ul>
<h3>IPC</h3>
<ul>
<li>All inter-process communication is routed via the kernel</li>
<li>Uses named ports and token-authenticated message queues</li>
<li>No shared memory by default</li>
</ul>
<h3>Future Additions</h3>
<ul>
<li>Deterministic scheduler</li>
<li>Audit trail of all token activity</li>
<li>Formal capability typing system</li>
</ul>
<h2>Philosophy</h2>
<p>This OS is not a POSIX clone. It is a deterministic, capability-secure, user-controlled computing environment built to reject legacy complexity and embrace verifiable simplicity.</p>
</body>
</html>

172
docs/sterling.html Normal file
View File

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sterling Language Documentation</title>
<style>
body { font-family: monospace; background: #0f0f0f; color: #f0f0f0; padding: 2rem; }
h1, h2, h3 { color: #00ffe0; }
pre { background: #1a1a1a; padding: 1rem; overflow-x: auto; }
code { color: #d0d0ff; }
a { color: #00aaff; }
</style>
</head>
<body>
<h1>Sterling Language Documentation</h1>
<p>Version: <code>0.1.0-alpha</code></p>
<h2>Overview</h2>
<p>Sterling is a low-level, strongly typed, systems programming language designed for performance, ABI stability, C interoperability, and full control over memory and hardware. It supports metaprogramming, hot-reloading, inline and raw assembly, and is built for multi-file compilation and dynamic linking.</p>
<h3>This Document is a work in progress, features are not yet implemented and i use this as a design document to stay true to my vision</h3>
<h2>File Extensions</h2>
<ul>
<li>Source files: <code>.stg</code></li>
<li>Header files: <code>.sth</code> <em>(placeholder, subject to change)</em></li>
</ul>
<h2>Function Qualifiers</h2>
<p>Every function must declare its linkage explicitly:</p>
<pre><code>
fn // globally visible, default linkage
fn_static // translation unit-local only
fn_export // must be exported to dynlib/staticlib
fn_extern // declared but defined elsewhere
fn_inline // inline-only, no symbol emitted
fn_asm // raw assembly function, globally visible
fn_static_asm // raw assembly function, TU-local only
</code></pre>
<h2>Function Syntax</h2>
<p>All functions must explicitly declare their return type. The only exception is <code>void</code>, which may be omitted for brevity when no return value is intended.</p>
<pre><code>
fn u32 add(u32 a, u32 b) {
return (a + b);
}
fn_extern i32 printf(const char* fmt, ...);
fn_inline u32 max(u32 a, u32 b) {
return ((a > b) ? a : b);
}
fn exit() {
// equivalent to fn void exit()
}
</code></pre>
<h2>Assembly Functions</h2>
<p>Write raw x86_64 assembly using <code>fn_asm</code> or <code>fn_static_asm</code>. Symbol, section, and global declaration are implicit.</p>
<pre><code>
fn_asm void* memset(void* dst, u8 value, u64 size) {
test rdx, rdx
je .done
mov rax, rsi
mov rdi, rdi
mov rcx, rdx
rep stosb
.done:
mov rax, rdi
ret
}
</code></pre>
<h2>Syscalls</h2>
<p>System calls are allowed via <code>fn_asm</code> or wrapped using concrete ABI-aware interfaces. Example:</p>
<pre><code>
fn_asm void exit() {
mov rax, 60 ; syscall: exit
mov rdi, 0 ; exit code
syscall
ret
}
</code></pre>
<h2>Types</h2>
<p>Sterling supports explicitly sized, ABI-stable primitive types. Signed and unsigned integer types are defined as follows:</p>
<pre><code>
i8, i16, i32, i64 // signed integers
u8, u16, u32, u64 // unsigned integers
f32, f64 // 32-bit and 64-bit IEEE floats
bool // 1-byte boolean, 0 or 1 only
char // 1-byte character (UTF-8)
</code></pre>
<p>Pointer types:</p>
<pre><code>
T* // Pointer to type T
ptr* // Special pointer with implicit coercion allowed (e.g., for GC, reflective systems)
void* // Opaque pointer with explicit cast required
</code></pre>
<p>All types have explicitly defined size and alignment. Structs support default values and zero-initialization rules:</p>
<pre><code>
typedef struct {
u32 x = 5;
u32 y;
} vec2u;
vec2u a = {}; // x = 5, y = 0
vec2u b = {0}; // x = 0, y = 0
vec2u c; // x = 0, y = 0 (default zero-init)
</code></pre>
<p>To opt out of default zero-initialization:</p>
<pre><code>
@raw u32 raw_val; // uninitialized
</code></pre>
<h2>Memory Model</h2>
<p>Sterling uses explicit, manual memory management by default. All variables are zero-initialized unless explicitly marked with <code>@raw</code>. Heap allocation is done via standard system or custom allocators.</p>
<ul>
<li><strong>Stack</strong>: Local automatic variables</li>
<li><strong>Heap</strong>: malloc/free or custom allocators</li>
<li><strong>Zero-cost abstraction</strong>: structs and stack values are passed by value unless explicitly passed by pointer</li>
</ul>
<p>Alignment and packing are controllable per type (TBD syntax). All layout is predictable and optimized for cache behavior. There are no hidden fields, vtables, or RTTI overhead.</p>
<h2>Operating System Development Features</h2>
<ul>
<li>Direct register access: <code>reg.rax</code>, <code>reg.cr3</code>, etc.</li>
<li>Memory barriers: <code>memory_fence_acquire()</code>, <code>memory_fence_release()</code></li>
<li>Segment descriptor support: GDT, IDT, TSS descriptors definable via built-in types</li>
<li>Interrupt handler definitions: <code>fn_isr</code> for IRQ, <code>fn_trap</code> for fault handlers</li>
<li>Syscall traps: <code>fn_syscall</code> with ABI-safe handling</li>
<li>No external ASM required: use <code>fn_asm</code> to write boot routines and context switches inline</li>
<li>Inline port I/O: <code>outb(port, val)</code>, <code>inw(port)</code>, etc.</li>
<li>MSR and control register access: readable and writable constants like <code>CR0_PG</code>, <code>MSR_EFER</code></li>
<li>Binary blob inclusion: <code>embed_binary("boot.bin")</code></li>
<li>Freestanding boot targets: no runtime required, bootloader/kernels fully supported</li>
</ul>
<h2>Metaprogramming</h2>
<p><em>TODO: Describe 'meta' keyword, templating, compile-time codegen, restrictions on type inference.</em></p>
<h2>ABI and Interop</h2>
<p><em>TODO: Specify ABI model (System V AMD64), calling convention details, struct/pointer representation rules.</em></p>
<h2>Threading</h2>
<p><em>TODO: Describe standard threading model, scheduler integration, context switching, green threads API.</em></p>
<h2>Graphics and Rendering</h2>
<p><em>TODO: Describe native rendering interface, GPU abstraction layer, and access to OpenGL/DirectX backends.</em></p>
<h2>Build and Compilation Model</h2>
<p><em>TODO: AOT compilation, linker behavior, multi-file project structure, module system (if any).</em></p>
</body>
</html>

12
kernel/LICENSE Normal file
View File

@ -0,0 +1,12 @@
0BSD License
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

19
sterling/LICENSE Normal file
View File

@ -0,0 +1,19 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright 2025 Carle-Margueritte Alexandre
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

124
sterling/README.md Normal file
View File

@ -0,0 +1,124 @@
# Sterling
- another low level language
### things to add:
- the other day i forgot what but i had an idea about something to implement in the compiler that would be neat but not present in c
- also need to be able to interface directly with assembly
- also let the compiller trim unused symbole
- adding a preprocessor to allow an include to not be completly used in the compiler
- better for single header library, and also, add maybe a namespace or a directive to allow to multiple function to have the same name but give them different signature depending of their origin file exemple if their is a need to have two file that declare the same type, only specify what the user want, but it is to the user to know what they are doing, without needing to touch to the header file directly
- anyway i see my point
## Vision and Goals
### Strict ABI standardization:
- Every type, from built-in to user-defined, must have a well-defined, consistent size. This includes basic pointers (void*) and even complex dynamically sized types.
### Native Rendering Support:
- The language should support both software rendering and GPU-accelerated graphics (e.g., via OpenGL or DirectX). This requires having a standardized interface or runtime layer that abstracts GPU calls while still allowing low-level access when needed.
### Cache-Friendly Data Structures:
- Data layout in memory will be optimized for cache efficiency. This implies control over alignment, packing, and predictable memory layouts that might, for instance, allow SIMD operations or other low-level optimizations.
### Standard Threading Model:
- A built-in threading library should be part of the standard.
### C-Interoperability:
- The language must be designed from the ground up to interface smoothly with C libraries. This means standardizing calling conventions and possibly even having a translation layer for linking external C code.
___
## 1. Language Design
- low-level enough to manage memory and hardware directly (like C)
- high-level enough to be safe and productive (optional but desirable)
- well-specified, with a clear grammar and semantics
- manual memory management
- dtrong type system (static typing, ideally with type inference for ergonomics)
- interfacing with system calls and external libraries
- compilation model (AOT, also look at meta programming)
- it should absolutly not look or feel like c++.
- and it is not a plain copy of c also i will take some inspiration from holyc, it should not have oop at all
## 2. Portable Compiler
- can target multiple architectures: x86_64, ARM, RISC-V, MIPS. (for starter x64 and ARM)
- outputs native binaries, or optionally transpiles to C or LLVM IR
- is self-hosted, eventually (bootstraps itself)
- has cross-compilation built in
- need to look into backend (LLVM or Cranelift) or build myself one
## 3. Build System
- it should be really straightforware, kinda like makefile
- dependency management.
- cross-compilation support. (gcc )
- linker. (gnu ld)
- assembler. (gnu as)
- debugger. (gnu db)
## 4. OS Abstraction Layer
- each OS has its own api:
- syscalls.
- file systems.
- I/O.
- threading.
- first i will aim for linux (i will dev on sleepeelinux (my own minimal operating system UwU))
- runtime library (like libc musl) that wraps OS specifics into a unified API
- fallback implementations for exotic or minimal platforms (embedded)
## 5. Standard Library
- data structures.
- string handling.
- math.
- file I/O.
- networking.
- vulkan abstraction layer (i hate vulkan naming)
## 6. Testing Across Platforms
- i need to ensure deterministic builds
- ABI compatibility
- good error messages
## 7. Active Community and Contributions
- no fork you all, it will be open source, but most people are bad at programming so no if you want to work on it fork it -_-
#### ok UwU, if you really want to help ^^:
- undefined behavior are a nono.
- new version should not break old behavior, it need to be robust so that people that write in 20year can use code from today
- binary compatibility with system libraries or foreign function interfaces (FFI).
- platform-specific optimizations. if a feature aim for a platforme, optimize it for it.
- designing something portable and performant today is hard but maintaining that over decades is harder so yeah good look <3
___
# LANGUAGE DESIGN DOCUMENT
## Key Compiler Rules (Strict)
- Meta functions only instantiate for primitives or explicitly whitelisted types
- Struct types must be manually specialized
- Function names must be unique in the global symbol table after instantiation
- No overloads: function + type combo must be deterministic
### Advantages of This Model
- No ambiguity: Instantiation only allowed when rules are met
- Strict typing: Prevents C++-style template bloat or implicit struct operations
- C compatibility: Resulting functions look like plain C
- Low-level: You control when, how, and what gets generated
- my idea isn't to recreate the wheel but expand on thing that i like without falling into what c++ or other c inspired lang are
- for now i am just writing idea, i'll need a lot more work to have a robust design, i am not better than other, i am more flawed, but i'll aim for self improvement

View File

@ -0,0 +1,199 @@
it should look more like c, as i want my compiler to be able to compile c
#include <io.sterling>//sterling is the name of the language, but i need to think of a file extension name that doesn't conflict with other format
#include <string.h>//this should work
u32 add(u32 a, u32 b) {
return (a + b);
}
// also i should be able as the user to define
typedef struct {
u32 test = 12;
} new_struct;
// if new_struct struct = {0}; then all data even default init are set to 0
// but if new_struct struct = {}; then all data but default init are set to 0
//also pointer should work like c, but void* shouldn't be the same as ptr*
//i don't know what i could do, but i would like for ptr* to allow for implicite data cast, but void* should be explicit
meta define_add(T);
T add(T a, T b) {
return (a + b);
}
//the issue i have here is it need to have an explicit overload if T is a structure, and i don't really like it, could lead to a c++ like language with too many undefined behavior, what could be a better approach ?
#include <io.stgh>//for stg header but i could also just use .stg and have the possibility to include stg file as header
#include <string.h>
typedef struct {
u32 x = 1;
u32 y = 2;
} vec2;
typedef struct {
u32 x;
u32 y;
} vec2_raw;
u32 add(u32 a, u32 b) {
return (a + b);
}
vec2_raw make_raw(u32 a, u32 b) {
return (vec2_raw){ a, b };
}
void* memcpy_explicit(void* dst, void* src, u32 n) {
// must explicitly cast outside
return memcpy(dst, src, n);
}
fn copy(ptr* dst, ptr* src, u32 n) {
// compiler auto-casts any ptr* to void* internally
memcpy(dst, src, n);
}
meta define_add(T);
T add(T a, T b) {
return (a + b);
}
// specialization for vec2
meta define_add(vec2);
vec2 add(vec2 a, vec2 b) {
return (vec2){ a.x + b.x, a.y + b.y };
}
fn main() -> u32 {
vec2 a = {}; // defaults apply
vec2 b = {0}; // defaults ignored, all zero
vec2 result = add(a, b);
u32* data = 0;
copy(data, data, 4); // OK
memcpy_explicit((void*)data, (void*)data, 4); // must cast
return 0;
}
fn_asm exit() {
section .text
global exit
exit:
mov rax, 60 ; syscall for exit
mov rdi, 0 ; exit code
syscall
ret
}
/*
rdi: dst
rsi: value
rdx: size
Returns in rax: dst
*/
/* implicit
section .text
global <symbol>
memset:
*/
fn_asm memset(void* dst, u8 value, u64 size) {
test rdx, rdx
je .done
mov rax, rsi ; value
mov rdi, rdi ; dst
mov rcx, rdx ; size
rep stosb
.done:
mov rax, rdi ; return dst
ret
}
//This uses the rep stosb instruction to rapidly write bytes into memory.
/*
rdi: void** old_sp — destination to store current rsp
rsi: void* new_sp — new stack pointer to switch into
No return value (control flow continues on new stack)
*/
fn_asm context_switch(void** old_sp, void* new_sp) {
mov [rdi], rsp ; save current stack pointer to *old_sp
mov rsp, rsi ; load new stack pointer
ret
}
/*
rdi: dst
rsi: value
rdx: size
Returns in rax: dst
*/
/* implicit
section .text
memset:
*/
fn_static_asm memset(void* dst, u8 value, u64 size) {
test rdx, rdx
je .done
mov rax, rsi ; value
mov rdi, rdi ; dst
mov rcx, rdx ; size
rep stosb
.done:
mov rax, rdi ; return dst
ret
}
//This uses the rep stosb instruction to rapidly write bytes into memory.
/*
| Keyword | Description |
| --------------- | ---------------------------------------------------------------------- |
| `fn` | Required for every function definition |
| `fn_inline` | Inline-only function, callable in the same TU |
| `fn_static` | Internal linkage (only visible in current TU) |
| `fn_extern` | Declaration only; expects symbol at link time |
| `fn_export` | Visible outside, exported in shared/static libs |
| `fn_asm` | Assembly-defined function, globally visible unless otherwise specified |
| `fn_static_asm` | Assembly-defined, internal linkage (hidden from linker) |
*/
//Keyword Examples
// header file (user_math.stgh)
fn_extern add(u32, u32);
fn_inline max(u32 a, u32 b) {
return (a > b) ? a : b;
}
// libmath.stg
fn_export add(u32 a, u32 b) {
return a + b;
}
// internal use only
fn_static log2_floor(u32 x) {
// implementation
}
// inline asm
fn_asm fast_add(u32 a, u32 b) {
mov eax, edi
add eax, esi
ret
}
// internal-only asm
fn_static_asm zero_block(void* dst, u64 size) {
rep stosb
ret
}

96
sterling/source/ast.h Normal file
View File

@ -0,0 +1,96 @@
enum NodeKind {
NODE_FUNCTION,
NODE_VAR_DECL,
NODE_TYPEDEF,
NODE_STRUCT,
NODE_RETURN,
NODE_BINARY_OP,
NODE_LITERAL,
NODE_IDENTIFIER,
NODE_CALL,
NODE_BLOCK,
}
struct ASTNode {
NodeKind kind;
union {
FunctionNode fn;
VarDeclNode var;
TypedefNode td;
StructNode str;
ExprNode expr;
BlockNode block;
}
}
// Function
struct FunctionNode {
String name;
String return_type;
List<VarDeclNode> params;
BlockNode body;
bool is_inline;
bool is_static;
bool is_export;
bool is_extern;
bool is_asm;
}
// Variable declaration
struct VarDeclNode {
String name;
String type;
ExprNode* init; // optional
bool is_raw;
}
// Block
struct BlockNode {
List<ASTNode> statements;
}
// Typedef
struct TypedefNode {
String alias;
String type;
}
// Struct
struct StructNode {
String name;
List<VarDeclNode> fields;
}
// Expression
enum ExprKind {
EXPR_LITERAL, EXPR_IDENTIFIER, EXPR_BINARY, EXPR_CALL, EXPR_CAST
}
struct ExprNode {
ExprKind kind;
union {
LiteralNode lit;
String ident;
BinaryExprNode bin;
CallExprNode call;
CastExprNode cast;
}
}
// Binary op
struct BinaryExprNode {
String op;
ExprNode* lhs;
ExprNode* rhs;
}
// Function call
struct CallExprNode {
String callee;
List<ExprNode> args;
}
// Literal
struct LiteralNode {
String value; // numeric, char, string
}

94
sterling/source/codegen.c Normal file
View File

@ -0,0 +1,94 @@
void generate(ASTNode* node) {
switch (node.kind) {
case NODE_FUNCTION:
gen_function(&node.fn);
break;
case NODE_TYPEDEF:
printf("typedef %s %s;\n", node.td.type, node.td.alias);
break;
case NODE_STRUCT:
gen_struct(&node.str);
break;
case NODE_VAR_DECL:
gen_var_decl(&node.var);
break;
case NODE_BLOCK:
gen_block(&node.block);
break;
default:
error("unsupported node");
}
}
void gen_function(FunctionNode* fn) {
if (fn->is_inline) printf("inline ");
else if (fn->is_static) printf("static ");
else if (fn->is_extern) printf("extern ");
else if (fn->is_export) printf("__attribute__((visibility(\"default\"))) ");
printf("%s %s(", fn->return_type, fn->name);
for (i = 0; i < fn->params.len; i++) {
gen_var_decl(&fn->params[i]);
if (i < fn->params.len - 1) printf(", ");
}
printf(") ");
gen_block(&fn->body);
}
void gen_block(BlockNode* block) {
printf("{\n");
for (stmt in block.statements) {
generate(stmt);
}
printf("}\n");
}
void gen_var_decl(VarDeclNode* var) {
printf("%s %s", var->type, var->name);
if (var->init != NULL) {
printf(" = ");
gen_expr(var->init);
}
printf(";");
}
void gen_struct(StructNode* str) {
printf("typedef struct {\n");
for (field in str.fields) {
printf("\t%s %s", field.type, field.name);
if (field.init != NULL) {
printf(" = ");
gen_expr(field.init);
}
printf(";\n");
}
printf("} %s;\n", str.name);
}
void gen_expr(ExprNode* expr) {
switch (expr.kind) {
case EXPR_LITERAL:
printf("%s", expr.lit.value);
break;
case EXPR_IDENTIFIER:
printf("%s", expr.ident);
break;
case EXPR_BINARY:
gen_expr(expr.bin.lhs);
printf(" %s ", expr.bin.op);
gen_expr(expr.bin.rhs);
break;
case EXPR_CALL:
printf("%s(", expr.call.callee);
for (i = 0; i < expr.call.args.len; i++) {
gen_expr(expr.call.args[i]);
if (i < expr.call.args.len - 1) printf(", ");
}
printf(")");
break;
case EXPR_CAST:
printf("(%s)", expr.cast.to_type);
gen_expr(expr.cast.value);
break;
}
}

113
sterling/source/lexer.c Normal file
View File

@ -0,0 +1,113 @@
enum TokenType {
TOK_EOF, TOK_IDENTIFIER, TOK_INTEGER, TOK_STRING, TOK_CHAR,
TOK_FN, TOK_RETURN, TOK_STRUCT, TOK_TYPEDEF,
TOK_TYPE_U8, TOK_TYPE_U32, // ... add all types
TOK_FN_STATIC, TOK_FN_EXPORT, // ... all fn variants
TOK_LBRACE, TOK_RBRACE, TOK_LPAREN, TOK_RPAREN,
TOK_COMMA, TOK_SEMICOLON, TOK_STAR, TOK_EQUAL,
TOK_PLUS, TOK_MINUS, TOK_SLASH, TOK_BANG, TOK_ARROW,
// ...
};
struct Token {
TokenType type;
char* lexeme;
int line;
};
struct Lexer {
char* src;
int pos;
int line;
};
char peek(Lexer* l) {
return l->src[l->pos];
}
char advance(Lexer* l) {
if (l->src[l->pos] == '\n') l->line++;
return l->src[l->pos++];
}
bool match(Lexer* l, char expected) {
if (l->src[l->pos] != expected) return false;
l->pos++;
return true;
}
void skip_whitespace_and_comments(Lexer* l) {
while (true) {
char c = peek(l);
if (c == ' ' || c == '\t' || c == '\r' || c == '\n') advance(l);
else if (c == '/' && l->src[l->pos + 1] == '/') {
while (peek(l) != '\n' && peek(l) != '\0') advance(l);
}
else if (c == '/' && l->src[l->pos + 1] == '*') {
advance(l); advance(l); // skip /*
while (!(peek(l) == '*' && l->src[l->pos + 1] == '/')) {
if (peek(l) == '\0') error("unterminated comment");
advance(l);
}
advance(l); advance(l); // skip */
}
else break;
}
}
Token identifier_or_keyword(Lexer* l, int start) {
while (isalnum(peek(l)) || peek(l) == '_') advance(l);
char* text = slice(l->src + start, l->pos - start);
// check keyword map
TokenType type = lookup_keyword(text); // use hash or strcmp
return Token { type, text, l->line };
}
Token number(Lexer* l, int start) {
while (isdigit(peek(l))) advance(l);
char* text = slice(l->src + start, l->pos - start);
return Token { TOK_INTEGER, text, l->line };
}
Token string(Lexer* l) {
advance(l); // skip opening quote
int start = l->pos;
while (peek(l) != '"' && peek(l) != '\0') {
if (peek(l) == '\\') advance(l); // escape
advance(l);
}
char* text = slice(l->src + start, l->pos - start);
if (peek(l) != '"') error("unterminated string");
advance(l); // closing quote
return Token { TOK_STRING, text, l->line };
}
Token next_token(Lexer* l) {
skip_whitespace_and_comments(l);
int start = l->pos;
char c = advance(l);
switch (c) {
case '\0': return Token { TOK_EOF, "", l->line };
case '{': return Token { TOK_LBRACE, "{", l->line };
case '}': return Token { TOK_RBRACE, "}", l->line };
case '(': return Token { TOK_LPAREN, "(", l->line };
case ')': return Token { TOK_RPAREN, ")", l->line };
case ',': return Token { TOK_COMMA, ",", l->line };
case ';': return Token { TOK_SEMICOLON, ";", l->line };
case '*': return Token { TOK_STAR, "*", l->line };
case '=': return Token { TOK_EQUAL, "=", l->line };
case '+': return Token { TOK_PLUS, "+", l->line };
case '-':
if (match(l, '>')) return Token { TOK_ARROW, "->", l->line };
else return Token { TOK_MINUS, "-", l->line };
case '/': return Token { TOK_SLASH, "/", l->line };
case '!': return Token { TOK_BANG, "!", l->line };
case '"': return string(l);
default:
if (isalpha(c) || c == '_') return identifier_or_keyword(l, start);
if (isdigit(c)) return number(l, start);
error("unexpected character");
}
}

116
sterling/source/parsing.c Normal file
View File

@ -0,0 +1,116 @@
#include "ast.h"
struct Parser {
Token* tokens;
int current;
}
Token peek(Parser* p);
Token advance(Parser* p);
bool match(Parser* p, TokenKind kind);
bool expect(Parser* p, TokenKind kind, const char* error);
bool is_at_end(Parser* p);
List<ASTNode> parse_program(Parser* p) {
List<ASTNode> nodes;
while (!is_at_end(p)) {
nodes.append(parse_toplevel(p));
}
return nodes;
}
ASTNode parse_toplevel(Parser* p) {
if (match(p, KW_FN)) return parse_function(p);
if (match(p, KW_TYPEDEF)) return parse_typedef(p);
if (match(p, KW_STRUCT)) return parse_struct(p);
error("Unexpected top-level declaration");
}
ASTNode parse_function(Parser* p) {
ASTNode node;
node.kind = NODE_FUNCTION;
FunctionNode fn;
// Qualifiers (optional)
while (match(p, KW_STATIC) || match(p, KW_EXPORT) || match(p, KW_INLINE) || match(p, KW_EXTERN) || match(p, KW_ASM)) {
switch (previous(p).kind) {
case KW_STATIC: fn.is_static = true; break;
case KW_EXPORT: fn.is_export = true; break;
case KW_INLINE: fn.is_inline = true; break;
case KW_EXTERN: fn.is_extern = true; break;
case KW_ASM: fn.is_asm = true; break;
}
}
// Return type
fn.return_type = expect_identifier(p);
// Name
fn.name = expect_identifier(p);
// Parameters
expect(p, TOKEN_LPAREN, "Expected '(' after function name");
while (!match(p, TOKEN_RPAREN)) {
fn.params.append(parse_var_decl(p));
if (!match(p, TOKEN_RPAREN)) expect(p, TOKEN_COMMA, "Expected ',' between parameters");
}
// Body or semicolon (extern only)
if (fn.is_extern) {
expect(p, TOKEN_SEMICOLON, "Expected ';' after extern declaration");
fn.body = NULL;
} else {
fn.body = parse_block(p);
}
node.fn = fn;
return node;
}
VarDeclNode parse_var_decl(Parser* p) {
VarDeclNode var;
var.type = expect_identifier(p);
var.name = expect_identifier(p);
if (match(p, TOKEN_ASSIGN)) {
var.init = parse_expression(p);
}
return var;
}
ASTNode parse_struct(Parser* p) {
expect(p, KW_STRUCT, "Expected 'struct'");
String name = expect_identifier(p);
expect(p, TOKEN_LBRACE, "Expected '{' for struct definition");
List<VarDeclNode> fields;
while (!match(p, TOKEN_RBRACE)) {
fields.append(parse_var_decl(p));
expect(p, TOKEN_SEMICOLON, "Expected ';' after struct field");
}
StructNode str = { .name = name, .fields = fields };
ASTNode node = { .kind = NODE_STRUCT, .str = str };
return node;
}
BlockNode parse_block(Parser* p) {
BlockNode block;
expect(p, TOKEN_LBRACE, "Expected '{'");
while (!match(p, TOKEN_RBRACE)) {
block.statements.append(parse_statement(p));
}
return block;
}
ExprNode* parse_expression(Parser* p);
ExprNode* parse_primary(Parser* p);
ExprNode* parse_binary(Parser* p, int min_prec);
ExprNode* parse_call(Parser* p, ExprNode* callee);

12
userland/LICENSE Normal file
View File

@ -0,0 +1,12 @@
0BSD License
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.