Catalog · How the catalog works
sard
Sard script language
- ★ 1 on GitHub
Anyone may press Interesting, signed in or not. It helps others find this project.
README
Sard Script Language
A prototype-based, expression-oriented scripting language with gradual typing, financial calculations, and modern conveniences.
Goal
I created Sard to teach my kids how to code. In the 8-bit era, BASIC on machines like the C64, ZX Spectrum, and MSX wasn't just a language—it was a complete platform. You had instant access to a Console, a Canvas, and Sound. Today, no modern language offers this all-in-one experience. Retro alternatives merely emulate old hardware limitations, while modern languages require complex setups just to draw a pixel. Worse, beginners constantly trip over syntax friction: case sensitivity, strict whitespace (Tabs), confusing comparisons (==), and missing semicolons. Sard bridges the gap: the instant, multimedia-rich fun of classic BASIC, combined with a forgiving, modern syntax designed for beginners.
Overview
Sard is a small but expressive scripting language designed for clarity, flexibility, and precision.
Sard is not trying to be a generic "everyone" language — it is a BASIC/Pascal-flavored, expression-oriented scripting language for two specific audiences: kids making games and accountants doing calculations. That is a much more coherent identity.
Key Characteristics
- Gradual Typing: Untyped variables for flexibility (
x = 10) or typed variables for safety (x : integer = 10) - Everything is an Object: No functions, only callable objects; no reserved words beyond built-in type names
- Expression-Oriented: Every block evaluates to a value; blocks are expressions, not just statements
- Unicode-Aware: Full Unicode support in identifiers and strings
- Financial Precision: Built-in
currencytype with 6 decimal places and accountant calculator mode - No Global Scope: Lexical parent-chain scoping
- Pascal-Style Casts: Convert values with
integer(x),number(x),string(x), etc. - Date format: d = 0t2026_10_20
Quick Start
// Hello World
print("Hello, World!");
// Variables - untyped (dynamic)
name = "Sard"
version = 1.0
name = 42 // Valid - can change types
// Variables - typed (strict)
count : integer = 10;
count = 20; // Valid - same type
count = "30"; // Auto-cast to 30
count = "test"; // Runtime error - invalid conversion
// Numeric literals with digit separators
population = 1_000_000;
pi = 3.14_15_92;
mask = 0xDE_AD_BE_EF;
// Arithmetic with power operator
area = 10 ^ 2; // 100 (10 squared)
cube = 2 ^ 3; // 8 (2 cubed)
// Financial calculations
price = $99.99;
tax = $12.3456;
total = price * 110%; // 109.989 (add 10%)
discount = price * 75%; // 74.9925 (25% off)
// Percent operator
rate = 50%; // 0.5
tax_amount = 200 * 20%; // 40
// Objects and prototypes
point : {
x : integer = 0;
y : integer = 0;
}
p = ~point; // Create instance
p.x = 100;
p.y = 200;
// Callable objects (functions)
factorial : (n) {
= if (n <= 1) {
1
} else {
n * factorial(n - 1)
}
}
print(factorial(5)); // 120
// Arrays
nums = [10, 20, 30];
print(nums[0]); // 10
nums[1] = 99;
// Array repetition and append
zeros = [0] * 3; // [0, 0, 0]
nums = nums + [40, 50]; // append elements
// Control flow
// There is no `else if`
score : integer = 85;
if (score >= 90) {
print("Grade: A");
} else {
if (score >= 80) {
print("Grade: B");
} else {
print("Grade: C or lower");
}
};
// Loops
i = 0;
while (i < 5) {
print(i);
i++;
}
loop(3) {
print("hello");
}
loop(3, i) {
print(i); // 0, 1, 2
}
for([10, 20, 30], v) {
print(v);
}
// Blocks as expressions
result = {
temp = x + y;
= temp * 2; // Block returns this value
};
// String concatenation with escapes
greeting = "Hello" \n "World" \t "!";
// Dates
d : date = 0t1971_10_19;
print(now); // current date/time (parens optional)
print(timestamp); // current Unix timestampType System
Sard supports gradual typing — you choose between dynamic and static typing per variable:
Untyped Variables (Dynamic)
x = 10; // x is integer
x = "test"; // Valid - x becomes string
x = 3.14; // Valid - x becomes numberTyped Variables (Strict)
y : integer = 10; // Strictly typed
y = 20; // Valid
y = "30"; // Auto-cast to 30
y = "test"; // Runtime error!Core Types
| Type | Description | Example |
|---|---|---|
integer |
Signed integer | 42 |
number |
Floating-point | 3.14 |
string |
Unicode string | "hello" |
boolean |
Boolean value | true, false |
color |
RGB (DWORD) | #ff5733, #fff |
currency |
Fixed-point 64-bit | $99.99 |
date |
Date/time value | 0t2024_01_15, now() |
array |
Ordered collection | [1, 2, 3] |
object |
User-defined | ~proto |
Type Comparison
is_int = (x == integer); // Check if x is an integer
is_str = (x == string); // Check if x is a stringType Casting
i = integer(3.14); // 3
n = number("42"); // 42.0
s = string(42); // "42"
b = boolean(1); // true
c = currency(3.14); // 3.140000
d = date(0); // epoch dateOperators
Arithmetic
| Operator | Description |
|---|---|
+ |
Addition |
- |
Subtraction |
* |
Multiplication |
/ |
Division |
^ |
Power (exponentiation) |
mod |
Modulo (remainder) |
% |
Percent (divide by 100) |
Assignment & Compound
| Operator | Description |
|---|---|
= |
Assignment (statement) / Equality (expression) |
+= |
Add and assign |
-= |
Subtract and assign |
++ |
Increment (prefix/postfix) |
-- |
Decrement (prefix/postfix) |
Comparison
| Operator | Description |
|---|---|
= |
Value equality (in expressions) |
<> != |
Not equal |
< |
Less than |
> |
Greater than |
<= |
Less or equal |
>= |
Greater or equal |
== |
Type check (like Pascal is) |
Logical
| Operator | Description |
|---|---|
& and |
Logical AND |
| ` | or` |
! not |
Logical NOT |
Precedence (high to low)
^(power) - right-associative*,/,mod+,-=,<>,!=,<,>,<=,>=(chaining supported)&,and|,or==(type comparison)
Special Features
Accountant Calculator Mode
When % is used with + or - at statement level, percentages are calculated relative to the left operand:
// Standard mode
result = (100 + 50%); // 100.5
// Accountant mode (top level)
result = 100 + 50%; // 150 (100 + 50% of 100)
result = 200 - 20%; // 160 (200 - 20% of 200)Currency Type
Fixed-point decimal with 6 fractional digits, stored as 64-bit integer:
price = $100; // 100.000000
tax = $12.3456; // 12.345600
fraction = $0.000001; // Smallest unit
salary = $1_000_000; // 1000000.000000Numeric Literals
Numeric literals can use underscores as digit separators:
one_million = 1_000_000;
pi = 3.14_15_92;
hex = 0xDE_AD_BE_EF;
red = #FF_57_33;
price = $1_000.99;Color Shorthand
Colors support 3-digit CSS-like shorthand:
white = #fff; // #ffffff
magenta = #f0f; // #ff00ff
grey = #abc; // #aabbccObject Construction
p1 = ~point; // New instance with prototype
p2 = ~~point; // New instance with shallow copy
p3 = @p1; // Reference to same objectChained Comparisons (Python-style)
if (0 <= x < 100) { // Equivalent to (0 <= x) and (x < 100)
print("valid index");
}Callable Objects
Callable parameters support type annotations, default values, open (variadic) parameters, and return type annotations:
// Simple callable
add : (a, b) {
= a + b
}
// Typed parameters
divide : number (a: number, b: number) {
= a / b
}
// Default values
greet : (name: string, suffix: string = "!") {
= "Hello, " + name + suffix
}
// Open (variadic) parameters
sum : (values...) {
result = 0
for(values, v) {
result += v
}
= result
}
print(sum(1, 2, 3, 4)); // 10Parameterless callables can be invoked with or without parentheses:
greet : { = "Hello" }
print(greet); // "Hello"
print(greet()); // "Hello"Arrays
Array Literals
nums = [10, 20, 30];
mixed = [1, "hello", true];
empty = [];Array Multiplication
zeros = [0] * 3; // [0, 0, 0]
ones = [1] * 5; // [1, 1, 1, 1, 1]
pairs = [1, 2] * 3; // [1, 2, 1, 2, 1, 2]Array Addition
arr = []
arr = arr + [10]; // [10]
arr += [20]; // [10, 20]
arr = arr + [30, 40]; // [10, 20, 30, 40]Array addition creates a new array; the original is not modified.
Object Model
Prototype-Based
Objects inherit from prototypes via parent chain:
bank_account : {
balance : currency = $0;
deposit : (amount) {
balance = balance + amount;
= balance;
};
withdraw : (amount) {
if (amount <= balance) {
balance = balance - amount;
= true;
} else {
= false;
}
};
get_balance : () {
= balance;
};
}
acc = ~bank_account;
acc.deposit($100);
acc.withdraw($30);
print(acc.get_balance()); // $70Block Returns
Use = at statement level to set block return value:
result = {
x = 10;
y = 20;
= x + y; // Block returns 30
print("This still executes!");
};Control Flow
If/Else
if (condition) {
// true branch
} else {
// false branch
};While Loop
while (condition) {
// loop body
}
// With else (runs if condition initially false)
while (condition) {
// loop body
} else {
// runs once if condition starts false
}
// Endless loop
while {
// infinite loop
}Loop
// Repeat a fixed number of times
loop(5) {
print("hello");
}
// With an index variable
loop(3, i) {
print(i); // 0, 1, 2
}
// Endless loop
loop {
// loop forever until break
}For
// Iterate over an array
for([10, 20, 30], v) {
print(v);
}
// Iterate over a string
for("hello", c) {
print(c);
}
// Sum elements
sum = 0
for([1, 2, 3, 4, 5], v) {
sum += v
}
print(sum); // 15Built-ins
| Object | Description |
|---|---|
print |
Print arguments to output |
len |
Length of array or string |
now |
Current date/time as date |
timestamp |
Current Unix timestamp as integer |
if / while / loop / for |
Control flow constructs |
break |
Exit loop or block (provided by runtime/addons) |
arr = [10, 20, 30];
print(len(arr)); // 3
print(len("hello")); // 5
print(now); // current date/time
print(timestamp); // current Unix timestampComments
// Single-line comment
/*
* Multi-line comment
* (does not nest)
*/Examples
Fibonacci
fib : (n) {
= if (n <= 1) {
n
} else {
fib(n - 1) + fib(n - 2)
}
}
print(fib(10)); // 55Bank Account with Interest
account : {
balance : currency = $0;
deposit : (amount : currency) {
balance = balance + amount;
= balance;
};
apply_interest : (rate) {
interest = balance * rate;
balance = balance + interest;
= balance;
};
}
my_acc = ~account;
my_acc.deposit($1000);
my_acc.apply_interest(5%); // Add 5% interest
print(my_acc.balance); // $1050Array Operations
numbers = [10, 20, 30, 40, 50];
// Sum all elements
sum = 0;
for(numbers, v) {
sum += v;
}
print(sum); // 150Language Philosophy
- Consistency: Everything is an object; blocks are expressions, except operators and flow controls
- Flexibility: Choose dynamic or static typing as needed
- Precision: Built-in support for financial calculations
- Extensibility: All constructs can be overridden or extended
- Clarity: Minimal reserved words; symbols have fixed meanings
Implementation Notes
- Can be embedded in host applications (Pascal, JS, C, Rust, Python, etc.)
- Supports multi-threaded execution with independent AST trees
- UTF-8 source encoding throughout
- Case-insensitive identifiers
Version: 1.0
License: See LICENSE file
Repository: https://github.com/zaher/sard

