Files
damn_simple_architecture/docs/dsc-language-spec.md
T
zxq5 5e575e2cd8 used claude to write a language spec (syntax and simple examples) for
DSC that we can follow as a reference for implementation.
2026-02-10 10:05:13 +00:00

24 KiB

DSC Language Specification v1.0

Damn Simple C (DSC) - A systems programming language for the DSA architecture


Table of Contents

  1. Introduction
  2. Lexical Structure
  3. Types
  4. Variables and Declarations
  5. Classes
  6. Functions
  7. Expressions
  8. Statements
  9. Control Flow
  10. Memory Management
  11. Generics
  12. Modules and Imports
  13. Keywords
  14. Operators
  15. Complete Example Programs
  16. Grammar Summary
  17. Appendix A: Differences from Rust
  18. Appendix B: Standard Library Conventions

1. Introduction

DSC is a statically-typed systems programming language designed for the DSA (Damn Simple Architecture). It combines modern syntax with explicit memory management and direct assembly interoperability.

Design Goals

  • Explicit: No hidden control flow or implicit conversions
  • Simple: Easy to understand what the compiler generates
  • Safe: Scope-based resource management via defer
  • Interoperable: Direct assembly library integration

2. Lexical Structure

2.1 Comments

// Single-line comment

/* Multi-line
   comment */

2.2 Identifiers

Identifiers must start with a letter or underscore, followed by any number of letters, digits, or underscores.

valid_identifier
_private
MyClass
get_value_42

2.3 Reserved Keywords

as          break       class       const       continue
defer       else        false       field       fn
if          impl        include     let         loop
mut         priv        pub         return      self
static      struct      true        void        while

2.4 Literals

Integer Literals

42          // Decimal
0x2A        // Hexadecimal
0b101010    // Binary
0o52        // Octal

String Literals

"Hello, world!"
"Escape sequences: \n \t \\ \""

Boolean Literals

true
false

3. Types

3.1 Primitive Types

Integer Types

i8          // Signed 8-bit integer
i16         // Signed 16-bit integer
i32         // Signed 32-bit integer
i64         // Signed 64-bit integer

u8          // Unsigned 8-bit integer
u16         // Unsigned 16-bit integer
u32         // Unsigned 32-bit integer
u64         // Unsigned 64-bit integer

Boolean Type

bool        // true or false

String Type

str         // String literal type (pointer to null-terminated byte array)

Void Type

void        // Absence of a value (only valid as return type)

3.2 Pointer Types

Pointers are declared using the & prefix:

&u32        // Pointer to u32
&bool       // Pointer to bool
&Point      // Pointer to Point class

Note: Pointers in DSC are raw memory addresses (like C pointers), not Rust-style references with borrow checking.

3.3 Array Types

[u32; 10]       // Fixed-size array of 10 u32 values
[bool; 256]     // Fixed-size array of 256 bool values

3.4 Generic Types

Vec<u32>        // Generic type with one type parameter
Map<str, i32>   // Generic type with multiple type parameters

4. Variables and Declarations

4.1 Variable Declaration

Variables are declared with let and are immutable by default:

let x: u32 = 42;
let name: str = "Alice";
let ptr: &u32 = &x;

4.2 Mutable Variables

Use mut to declare mutable variables:

let mut count: u32 = 0;
count = count + 1;

4.3 Type Inference

Type annotations can be omitted when the type can be inferred:

let x = 42;         // Inferred as u32 (default integer type)
let name = "Bob";   // Inferred as str

5. Classes

Classes are DSC's primary means of encapsulation, combining data and behavior.

5.1 Class Definition

class ClassName {
    // Class body
}

5.2 Fields

Fields represent the data members of a class. They must be explicitly marked as pub or priv:

class Point {
    pub field x: u32;
    pub field y: u32;
    priv field cached: bool;
}

Syntax: [pub|priv] field <name>: <type>;

5.3 Constants

Constants are compile-time values associated with a class:

class Math {
    pub const PI: u32 = 3;
    pub const E: u32 = 2;
    priv const INTERNAL_CONSTANT: u32 = 42;
}

Syntax: [pub|priv] const <NAME>: <type> = <value>;

Usage: Math::PI

5.4 Static Fields

Static fields are global variables associated with a class:

class Logger {
    pub static mut INSTANCE_COUNT: u32 = 0;
    priv static mut INTERNAL_STATE: u32 = 0;
}

Syntax: [pub|priv] static mut <name>: <type> = <value>;

Usage:

Logger::INSTANCE_COUNT = Logger::INSTANCE_COUNT + 1;

Note: All static fields must be declared mut as they represent mutable global state.

5.5 Methods

Methods are functions associated with a class. Instance methods take a self parameter:

class Point {
    pub field x: u32;
    pub field y: u32;
    
    // Constructor (static method)
    pub fn new(x: u32, y: u32) -> Point {
        return Point { x: x, y: y };
    }
    
    // Instance method
    pub fn get_x(self: &Point) -> u32 {
        return self.x;
    }
    
    // Mutable instance method
    pub fn set_x(self: &Point, value: u32) {
        self.x = value;
    }
    
    // Static method
    pub fn zero() -> Point {
        return Point { x: 0, y: 0 };
    }
}

Method Syntax:

  • Instance method: [pub|priv] fn <name>(self: &<ClassName>, ...) -> <return_type>
  • Static method: [pub|priv] fn <name>(...) -> <return_type>

5.6 Class Instantiation

// Using constructor
let p1: Point = Point::new(10, 20);

// Using struct literal syntax
let p2: Point = Point { x: 5, y: 15 };

5.7 Method Calls

let p: Point = Point::new(10, 20);

// Method call syntax (preferred)
let x: u32 = p.get_x();

// Explicit syntax (equivalent)
let x: u32 = Point::get_x(&p);

// Static method call
let origin: Point = Point::zero();

5.8 Field Access

let p: Point = Point { x: 10, y: 20 };

// Read field
let x: u32 = p.x;

// Write field (if mutable)
let mut p2: Point = Point { x: 0, y: 0 };
p2.x = 42;

5.9 Complete Class Example

class Vec {
    priv field data: &u32;
    priv field len: u32;
    priv field capacity: u32;
    
    pub const DEFAULT_CAPACITY: u32 = 16;
    pub static mut TOTAL_ALLOCATIONS: u32 = 0;
    
    pub fn new(capacity: u32) -> Vec {
        Vec::TOTAL_ALLOCATIONS = Vec::TOTAL_ALLOCATIONS + 1;
        return Vec {
            data: alloc::malloc(capacity * 4) as &u32,
            len: 0,
            capacity: capacity
        };
    }
    
    pub fn with_default_capacity() -> Vec {
        return Vec::new(Vec::DEFAULT_CAPACITY);
    }
    
    pub fn push(self: &Vec, item: u32) {
        if self.len == self.capacity {
            self.grow();
        }
        *(self.data + self.len) = item;
        self.len = self.len + 1;
    }
    
    pub fn get(self: &Vec, index: u32) -> u32 {
        return *(self.data + index);
    }
    
    priv fn grow(self: &Vec) {
        let new_capacity: u32 = self.capacity * 2;
        let new_data: &u32 = alloc::malloc(new_capacity * 4) as &u32;
        
        // Copy old data
        let mut i: u32 = 0;
        while i < self.len {
            *(new_data + i) = *(self.data + i);
            i = i + 1;
        }
        
        alloc::free(self.data as u32);
        self.data = new_data;
        self.capacity = new_capacity;
    }
    
    pub fn drop(self: &Vec) {
        alloc::free(self.data as u32);
    }
}

6. Functions

6.1 Function Declaration

fn function_name(param1: Type1, param2: Type2) -> ReturnType {
    // Function body
}

6.2 Return Type

Functions without a return value use void or omit the return type:

fn print_message(msg: str) -> void {
    // No return value
}

// Equivalent
fn print_message(msg: str) {
    // No return value
}

6.3 Return Statement

fn add(a: u32, b: u32) -> u32 {
    return a + b;
}

6.4 Function Parameters

fn process(value: u32, ptr: &u32, flag: bool) -> u32 {
    // ...
}

7. Expressions

7.1 Literals

42              // Integer literal
0xFF            // Hex literal
true            // Boolean literal
"hello"         // String literal

7.2 Binary Operations

a + b           // Addition
a - b           // Subtraction
a * b           // Multiplication
a / b           // Division
a % b           // Modulo

7.3 Comparison Operations

a == b          // Equal
a != b          // Not equal
a < b           // Less than
a > b           // Greater than
a <= b          // Less than or equal
a >= b          // Greater than or equal

7.4 Logical Operations

a && b          // Logical AND
a || b          // Logical OR
!a              // Logical NOT

7.5 Bitwise Operations

a & b           // Bitwise AND
a | b           // Bitwise OR
a ^ b           // Bitwise XOR
~a              // Bitwise NOT
a << b          // Left shift
a >> b          // Right shift

7.6 Address-of and Dereference

&x              // Address-of (get pointer to x)
*ptr            // Dereference (read/write through pointer)

7.7 Type Casting

value as u32    // Cast value to u32
ptr as &u8      // Cast pointer type

7.8 Field Access

obj.field       // Access field of object
ptr.field       // Access field through pointer (implicit dereference)

7.9 Method Call

obj.method(args)        // Instance method call
Class::method(args)     // Static method call

7.10 Array Indexing

arr[index]      // Access array element

8. Statements

8.1 Expression Statement

function_call();
x = y + 1;

8.2 Variable Declaration

let x: u32 = 42;
let mut y: u32 = 0;

8.3 Assignment

x = 10;
*ptr = 20;
obj.field = 30;
arr[0] = 40;

8.4 Compound Assignment

x += 5;         // x = x + 5
x -= 3;         // x = x - 3
x *= 2;         // x = x * 2
x /= 4;         // x = x / 4

9. Control Flow

9.1 If Statement

if condition {
    // then block
}

if condition {
    // then block
} else {
    // else block
}

if condition1 {
    // block 1
} else if condition2 {
    // block 2
} else {
    // block 3
}

9.2 While Loop

while condition {
    // loop body
}

9.3 Loop (Infinite Loop)

loop {
    // infinite loop
    if should_exit {
        break;
    }
}

9.4 Break and Continue

while condition {
    if skip_condition {
        continue;   // Skip to next iteration
    }
    
    if exit_condition {
        break;      // Exit loop
    }
}

10. Memory Management

10.1 Defer Statement

The defer keyword schedules a statement to execute when the current scope exits.

fn example() {
    let ptr: u32 = alloc::malloc(256);
    defer alloc::free(ptr);
    
    // Use ptr...
    
    if error {
        return;     // defer runs here
    }
    
    // More work...
    
}   // defer runs here

10.2 Defer Execution Rules

  1. Scope-based: Defers execute when their enclosing scope exits
  2. LIFO order: Multiple defers execute in reverse order of declaration
  3. All exit paths: Defers run on return, break, continue, or natural scope end
fn nested_defers() {
    let a: u32 = alloc::malloc(100);
    defer alloc::free(a);       // Runs second
    
    if condition {
        let b: u32 = alloc::malloc(200);
        defer alloc::free(b);   // Runs first (if in this scope)
        
        // ... work ...
        
    }   // b's defer runs here
    
    // ... more work ...
    
}   // a's defer runs here

10.3 Defer with Methods

class Resource {
    priv field handle: u32;
    
    pub fn acquire() -> Resource {
        return Resource { handle: alloc::malloc(1024) };
    }
    
    pub fn release(self: &Resource) {
        alloc::free(self.handle);
    }
}

fn use_resource() {
    let res: Resource = Resource::acquire();
    defer res.release();
    
    // Use resource...
    
}   // res.release() automatically called

11. Generics

11.1 Generic Classes

class Box<T> {
    priv field value: T;
    
    pub fn new(value: T) -> Box<T> {
        return Box { value: value };
    }
    
    pub fn get(self: &Box<T>) -> T {
        return self.value;
    }
    
    pub fn set(self: &Box<T>, value: T) {
        self.value = value;
    }
}

11.2 Generic Usage

let int_box: Box<u32> = Box::new(42);
let str_box: Box<str> = Box::new("hello");

let value: u32 = int_box.get();

11.3 Multiple Type Parameters

class Pair<T, U> {
    pub field first: T;
    pub field second: U;
    
    pub fn new(first: T, second: U) -> Pair<T, U> {
        return Pair { first: first, second: second };
    }
}

let p: Pair<u32, str> = Pair::new(42, "answer");

11.4 Generic Functions

fn swap<T>(a: &T, b: &T) {
    let temp: T = *a;
    *a = *b;
    *b = temp;
}

let mut x: u32 = 1;
let mut y: u32 = 2;
swap(&x, &y);

11.5 Generic Constraints

Note: DSC does not support trait bounds or where clauses. Generic types are instantiated via monomorphization without constraints.


12. Modules and Imports

12.1 Assembly Imports

DSC can import assembly modules for low-level operations:

include print: "./lib/io/print.dsa";
include alloc: "./lib/memory/alloc.dsa";

Syntax: include <namespace>: "<path>";

12.2 Using Imported Functions

print::println("Hello, world!");
let ptr: u32 = alloc::malloc(256);

13. Keywords

13.1 Complete Keyword List

Keyword Purpose
as Type casting
break Exit loop
class Class definition
const Constant declaration
continue Next loop iteration
defer Defer statement execution
else Alternative branch
false Boolean literal
field Class field declaration
fn Function declaration
if Conditional statement
impl Reserved for future use
include Import assembly module
let Variable declaration
loop Infinite loop
mut Mutable binding
priv Private visibility
pub Public visibility
return Return from function
self Instance reference
static Static field declaration
struct Reserved for future use
true Boolean literal
void No return type
while While loop

14. Operators

14.1 Operator Precedence (Highest to Lowest)

Precedence Operators Associativity
1 () [] . :: Left to right
2 ! ~ & * (unary) - (unary) Right to left
3 as Left to right
4 * / % Left to right
5 + - Left to right
6 << >> Left to right
7 & Left to right
8 ^ Left to right
9 ` `
10 == != < > <= >= Left to right
11 && Left to right
12 `
13 = += -= *= /= Right to left

14.2 Operator Summary

Arithmetic Operators

+       Addition
-       Subtraction
*       Multiplication
/       Division
%       Modulo

Comparison Operators

==      Equal
!=      Not equal
<       Less than
>       Greater than
<=      Less than or equal
>=      Greater than or equal

Logical Operators

&&      Logical AND
||      Logical OR
!       Logical NOT

Bitwise Operators

&       Bitwise AND
|       Bitwise OR
^       Bitwise XOR
~       Bitwise NOT
<<      Left shift
>>      Right shift

Memory Operators

&       Address-of
*       Dereference

Assignment Operators

=       Assignment
+=      Add and assign
-=      Subtract and assign
*=      Multiply and assign
/=      Divide and assign

15. Complete Example Programs

15.1 Hello World

include print: "./lib/io/print.dsa";

fn main() -> void {
    print::println("Hello, world!");
}

15.2 Vector Implementation

include alloc: "./lib/memory/alloc.dsa";

class Vec<T> {
    priv field data: &T;
    priv field len: u32;
    priv field capacity: u32;
    
    pub const DEFAULT_CAPACITY: u32 = 16;
    
    pub fn new(capacity: u32) -> Vec<T> {
        let size: u32 = capacity * 4;  // Assume sizeof(T) = 4
        return Vec {
            data: alloc::malloc(size) as &T,
            len: 0,
            capacity: capacity
        };
    }
    
    pub fn push(self: &Vec<T>, item: T) {
        if self.len >= self.capacity {
            self.resize();
        }
        *(self.data + self.len) = item;
        self.len = self.len + 1;
    }
    
    pub fn get(self: &Vec<T>, index: u32) -> T {
        return *(self.data + index);
    }
    
    pub fn len(self: &Vec<T>) -> u32 {
        return self.len;
    }
    
    priv fn resize(self: &Vec<T>) {
        let new_capacity: u32 = self.capacity * 2;
        let new_size: u32 = new_capacity * 4;
        let new_data: &T = alloc::malloc(new_size) as &T;
        
        let mut i: u32 = 0;
        while i < self.len {
            *(new_data + i) = *(self.data + i);
            i = i + 1;
        }
        
        alloc::free(self.data as u32);
        self.data = new_data;
        self.capacity = new_capacity;
    }
    
    pub fn drop(self: &Vec<T>) {
        alloc::free(self.data as u32);
    }
}

fn main() -> void {
    let vec: Vec<u32> = Vec::new(10);
    defer vec.drop();
    
    vec.push(1);
    vec.push(2);
    vec.push(3);
    
    let mut i: u32 = 0;
    while i < vec.len() {
        let value: u32 = vec.get(i);
        // Use value...
        i = i + 1;
    }
}

15.3 Resource Management Example

include alloc: "./lib/memory/alloc.dsa";
include print: "./lib/io/print.dsa";

class File {
    priv field handle: u32;
    priv field is_open: bool;
    
    pub fn open(path: str) -> File {
        let handle: u32 = 0;  // Platform-specific open
        return File { handle: handle, is_open: true };
    }
    
    pub fn write(self: &File, data: str) {
        if !self.is_open {
            return;
        }
        // Write implementation...
    }
    
    pub fn close(self: &File) {
        if self.is_open {
            // Platform-specific close
            self.is_open = false;
        }
    }
}

fn process_file(path: str) -> bool {
    let file: File = File::open(path);
    defer file.close();  // Ensures file is closed on all exit paths
    
    let buffer: u32 = alloc::malloc(1024);
    defer alloc::free(buffer);
    
    if !file.is_open {
        print::println("Failed to open file");
        return false;  // Defers run: free(buffer), file.close()
    }
    
    file.write("Hello, file!");
    
    return true;  // Defers run: free(buffer), file.close()
}

fn main() -> void {
    let success: bool = process_file("output.txt");
    
    if success {
        print::println("File processed successfully");
    } else {
        print::println("File processing failed");
    }
}

16. Grammar Summary

16.1 EBNF Grammar

program = { import_statement | class_definition | function_definition }

import_statement = "include" identifier ":" string_literal ";"

class_definition = "class" identifier [ generic_params ] "{" { class_member } "}"

class_member = field_declaration
             | const_declaration
             | static_declaration
             | method_declaration

field_declaration = visibility "field" identifier ":" type ";"

const_declaration = visibility "const" IDENTIFIER ":" type "=" expression ";"

static_declaration = visibility "static" "mut" identifier ":" type "=" expression ";"

method_declaration = visibility "fn" identifier [ generic_params ] "(" parameters ")" [ "->" type ] block

function_definition = "fn" identifier [ generic_params ] "(" parameters ")" [ "->" type ] block

generic_params = "<" identifier { "," identifier } ">"

parameters = [ parameter { "," parameter } ]

parameter = identifier ":" type

visibility = "pub" | "priv"

type = primitive_type
     | pointer_type
     | array_type
     | generic_type
     | identifier

primitive_type = "u8" | "u16" | "u32" | "u64"
               | "i8" | "i16" | "i32" | "i64"
               | "bool" | "str" | "void"

pointer_type = "&" type

array_type = "[" type ";" integer_literal "]"

generic_type = identifier "<" type { "," type } ">"

block = "{" { statement } "}"

statement = let_statement
          | expression_statement
          | if_statement
          | while_statement
          | loop_statement
          | return_statement
          | defer_statement
          | break_statement
          | continue_statement

let_statement = "let" [ "mut" ] identifier ":" type "=" expression ";"

defer_statement = "defer" expression ";"

if_statement = "if" expression block [ "else" ( if_statement | block ) ]

while_statement = "while" expression block

loop_statement = "loop" block

return_statement = "return" [ expression ] ";"

break_statement = "break" ";"

continue_statement = "continue" ";"

expression_statement = expression ";"

expression = assignment_expression

assignment_expression = logical_or_expression [ assignment_operator assignment_expression ]

assignment_operator = "=" | "+=" | "-=" | "*=" | "/="

logical_or_expression = logical_and_expression { "||" logical_and_expression }

logical_and_expression = equality_expression { "&&" equality_expression }

equality_expression = relational_expression { equality_operator relational_expression }

equality_operator = "==" | "!="

relational_expression = bitwise_or_expression { relational_operator bitwise_or_expression }

relational_operator = "<" | ">" | "<=" | ">="

bitwise_or_expression = bitwise_xor_expression { "|" bitwise_xor_expression }

bitwise_xor_expression = bitwise_and_expression { "^" bitwise_and_expression }

bitwise_and_expression = shift_expression { "&" shift_expression }

shift_expression = additive_expression { shift_operator additive_expression }

shift_operator = "<<" | ">>"

additive_expression = multiplicative_expression { additive_operator multiplicative_expression }

additive_operator = "+" | "-"

multiplicative_expression = cast_expression { multiplicative_operator cast_expression }

multiplicative_operator = "*" | "/" | "%"

cast_expression = unary_expression [ "as" type ]

unary_expression = postfix_expression
                 | unary_operator unary_expression

unary_operator = "!" | "~" | "&" | "*" | "-"

postfix_expression = primary_expression { postfix_operator }

postfix_operator = "." identifier [ call_suffix ]
                 | "::" identifier [ call_suffix ]
                 | "[" expression "]"
                 | call_suffix

call_suffix = "(" [ arguments ] ")"

arguments = expression { "," expression }

primary_expression = identifier
                   | literal
                   | "(" expression ")"
                   | struct_literal

struct_literal = identifier "{" [ field_init { "," field_init } ] "}"

field_init = identifier ":" expression

literal = integer_literal
        | string_literal
        | boolean_literal

boolean_literal = "true" | "false"

Appendix A: Differences from Rust

While DSC uses Rust-like syntax, there are key differences:

Feature Rust DSC
Encapsulation struct + impl class
Field syntax field: Type field field: Type;
References Borrow-checked &T Raw pointers &T
Ownership Compile-time borrow checker Manual with defer
Traits Supported Not supported
Lifetimes Supported Not supported
Pattern matching Supported Not supported
Enums Sum types with data Not yet supported
Modules Native Assembly imports only

Appendix B: Standard Library Conventions

By convention, standard library modules should provide:

Allocation Module (alloc)

include alloc: "./lib/memory/alloc.dsa";

// Functions:
// alloc::malloc(size: u32) -> u32
// alloc::free(ptr: u32) -> void

Print Module (print)

include print: "./lib/io/print.dsa";

// Functions:
// print::print(msg: str) -> void
// print::println(msg: str) -> void
// print::print_num(n: u32) -> void
// print::print_hex(n: u32) -> void

End of DSC Language Specification v1.0