feat: Std::String, Std::Map, Result/Option, ? operator, sizeof fix, docs
Add standard library modules: - Std::String: strlen, strcmp, concat, copy, starts_with wrappers - Std::Map: linear-probing hash map with String keys, int values Add error handling: - Result and Option algebraic enum examples - ? try operator for automatic error propagation in HIR lowering Compiler bugfixes: - Fix sizeof() for user-defined structs (new hSizeOf HIR node) - Fix HIR type lowering for char8, bool8, int8, uint8, etc. - Fix let statement lowering for pointer types (*char8 → int* bug) - Fix PrintInt ABI mismatch (int64_t → int in io.c) - Fix Makefile test bug (_test_tmp_pkg already exists) Documentation: - Rewrite README.md with features, examples, project structure - Add docs/LanguageRef.md — complete language reference - Add docs/Stdlib.md — standard library documentation - Add docs/BuildAndTest.md — build and test guide New examples: strings, map, result_option, try_operator (13 total)
This commit is contained in:
@@ -3,7 +3,7 @@ SRC := src/main.nim
|
|||||||
OUT := buxc
|
OUT := buxc
|
||||||
BUILD_DIR := build
|
BUILD_DIR := build
|
||||||
|
|
||||||
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics pattern_matching
|
EXAMPLES := hello fibonacci factorial structs enums methods algebraic_enums generics pattern_matching strings map result_option try_operator
|
||||||
|
|
||||||
.PHONY: all build dev test clean test-examples
|
.PHONY: all build dev test clean test-examples
|
||||||
|
|
||||||
@@ -25,6 +25,7 @@ test: build test-examples
|
|||||||
@echo "Running HIR tests..."
|
@echo "Running HIR tests..."
|
||||||
$(NIM) c -r tests/hir_test.nim
|
$(NIM) c -r tests/hir_test.nim
|
||||||
@echo "Running integration tests..."
|
@echo "Running integration tests..."
|
||||||
|
rm -rf _test_tmp_pkg
|
||||||
./$(OUT) new _test_tmp_pkg
|
./$(OUT) new _test_tmp_pkg
|
||||||
./$(OUT) --version
|
./$(OUT) --version
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
Bux is a fast, compiled, strongly-typed systems programming language inspired by [Rux](https://rux-lang.dev/). The long-term goal is a self-hosted compiler with a minimal runtime, native x86-64 backend, and modern tooling.
|
Bux is a fast, compiled, strongly-typed systems programming language inspired by [Rux](https://rux-lang.dev/). The long-term goal is a self-hosted compiler with a minimal runtime, native x86-64 backend, and modern tooling.
|
||||||
|
|
||||||
|
Bux improves on Rux with a richer standard library (`Map`, `String`), modern error handling (`Result`, `Option`, and the `?` operator), and a portable C transpiler backend that runs on Linux, macOS, and Windows.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -12,26 +16,156 @@ make build
|
|||||||
|
|
||||||
# Create a new project
|
# Create a new project
|
||||||
bux new hello
|
bux new hello
|
||||||
|
cd hello
|
||||||
|
|
||||||
# Build and run
|
# Build and run
|
||||||
bux run
|
bux run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Syntax Preview
|
## Syntax Preview
|
||||||
|
|
||||||
|
### Hello World
|
||||||
```bux
|
```bux
|
||||||
import Std::Io::PrintLine;
|
import Std::Io::PrintLine;
|
||||||
|
|
||||||
func Main() -> int {
|
func Main() -> int {
|
||||||
let message: *char8 = c8"Hello, Bux!";
|
PrintLine("Hello, Bux!");
|
||||||
PrintLine(message);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Roadmap
|
### Error Handling with `?`
|
||||||
|
```bux
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
See [`PLAN.md`](PLAN.md) for the full roadmap to self-hosting.
|
func Divide(a: int, b: int) -> Result {
|
||||||
|
if b == 0 {
|
||||||
|
return Result_NewErr("division by zero");
|
||||||
|
}
|
||||||
|
return Result_NewOk(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compute() -> Result {
|
||||||
|
let x: int = Divide(10, 2)?; // auto-propagates Err
|
||||||
|
let y: int = Divide(x, 5)?;
|
||||||
|
return Result_NewOk(y);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structs and Methods
|
||||||
|
```bux
|
||||||
|
struct Rectangle {
|
||||||
|
width: int;
|
||||||
|
height: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
extend Rectangle {
|
||||||
|
func Area(self: Rectangle) -> int {
|
||||||
|
return self.width * self.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let rect: Rectangle = Rectangle { width: 10, height: 5 };
|
||||||
|
PrintInt(rect.Area());
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generics
|
||||||
|
```bux
|
||||||
|
func Max<T>(a: T, b: T) -> T {
|
||||||
|
if a > b {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let m: int = Max<int>(10, 20);
|
||||||
|
PrintInt(m);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hash Map
|
||||||
|
```bux
|
||||||
|
import Std::Map::{Map, Map_New, Map_Set, Map_Get};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let m: Map = Map_New(16);
|
||||||
|
Map_Set(&m, "answer", 42);
|
||||||
|
PrintInt(Map_Get(&m, "answer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
| Feature | Status |
|
||||||
|
|---------|--------|
|
||||||
|
| **Types** | Primitives, pointers, slices, tuples, structs, enums, unions |
|
||||||
|
| **Generics** | Generic functions (monomorphization) |
|
||||||
|
| **Algebraic Enums** | Enums with data (`Result`, `Option`) |
|
||||||
|
| **Pattern Matching** | `match` with guards |
|
||||||
|
| **Methods** | `extend` blocks for struct methods |
|
||||||
|
| **Interfaces** | `interface` + `extend` for trait-like behavior |
|
||||||
|
| **Error Handling** | `Result<T,E>`, `Option<T>`, and the `?` operator |
|
||||||
|
| **Standard Library** | `Io`, `Array`, `String`, `Map` |
|
||||||
|
| **Backend** | C transpiler (bootstrap) |
|
||||||
|
| **Tooling** | `bux new`, `bux build`, `bux run`, `bux check` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
bux/
|
||||||
|
├── src/ # Bootstrap compiler (Nim)
|
||||||
|
├── stdlib/ # Standard library (.bux + .c runtime)
|
||||||
|
├── examples/ # Example programs
|
||||||
|
├── tests/ # Unit tests (Nim)
|
||||||
|
├── docs/ # Documentation
|
||||||
|
├── README.md
|
||||||
|
├── PLAN.md # Roadmap to self-hosting
|
||||||
|
└── Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Build & Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build compiler
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
make test
|
||||||
|
|
||||||
|
# Run example programs
|
||||||
|
make test-examples
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
make clean
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [`docs/LanguageRef.md`](docs/LanguageRef.md) — Language reference
|
||||||
|
- [`docs/Stdlib.md`](docs/Stdlib.md) — Standard library documentation
|
||||||
|
- [`docs/BuildAndTest.md`](docs/BuildAndTest.md) — Build and test guide
|
||||||
|
- [`PLAN.md`](PLAN.md) — Roadmap to self-hosting
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,190 @@
|
|||||||
|
# Build and Test Guide
|
||||||
|
|
||||||
|
This guide covers building the Bux bootstrap compiler, creating projects, and running tests.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
- **Nim** (1.6+) — for building the bootstrap compiler
|
||||||
|
- **C compiler** (`gcc`, `clang`, or `cc`) — for the C backend
|
||||||
|
- **Make**
|
||||||
|
|
||||||
|
On Debian/Ubuntu:
|
||||||
|
```bash
|
||||||
|
sudo apt-get install nim gcc make
|
||||||
|
```
|
||||||
|
|
||||||
|
On macOS:
|
||||||
|
```bash
|
||||||
|
brew install nim gcc make
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building the Compiler
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Release build
|
||||||
|
make build
|
||||||
|
|
||||||
|
# Development build (no optimizations, faster compile)
|
||||||
|
make dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The output is a single binary: `buxc`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Creating a Project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create a new package in a new directory
|
||||||
|
./buxc new myproject
|
||||||
|
cd myproject
|
||||||
|
```
|
||||||
|
|
||||||
|
This generates:
|
||||||
|
```
|
||||||
|
myproject/
|
||||||
|
├── bux.toml
|
||||||
|
└── src/
|
||||||
|
└── Main.bux
|
||||||
|
```
|
||||||
|
|
||||||
|
### bux.toml
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[Package]
|
||||||
|
Name = "myproject"
|
||||||
|
Version = "0.1.0"
|
||||||
|
Type = "bin"
|
||||||
|
|
||||||
|
[Build]
|
||||||
|
Output = "Bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Building and Running
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Type-check without building
|
||||||
|
./buxc check
|
||||||
|
|
||||||
|
# Build
|
||||||
|
./buxc build
|
||||||
|
|
||||||
|
# Build and run
|
||||||
|
./buxc run
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
./buxc clean
|
||||||
|
```
|
||||||
|
|
||||||
|
Build output goes to `build/` by default.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Running Tests
|
||||||
|
|
||||||
|
### Compiler Tests
|
||||||
|
```bash
|
||||||
|
make test
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs:
|
||||||
|
- Lexer unit tests
|
||||||
|
- Parser unit tests
|
||||||
|
- Semantic analysis unit tests
|
||||||
|
- HIR lowering unit tests
|
||||||
|
- Integration tests (`buxc new`, `buxc --version`)
|
||||||
|
|
||||||
|
### Example Programs
|
||||||
|
```bash
|
||||||
|
make test-examples
|
||||||
|
```
|
||||||
|
|
||||||
|
Compiles and runs all programs in `examples/`.
|
||||||
|
|
||||||
|
### Individual Example
|
||||||
|
```bash
|
||||||
|
mkdir -p examples_pkg/hello/src
|
||||||
|
cp examples/hello.bux examples_pkg/hello/src/Main.bux
|
||||||
|
# Create bux.toml manually or use `buxc new`
|
||||||
|
cd examples_pkg/hello && ../../buxc run
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
bux/
|
||||||
|
├── src/ # Bootstrap compiler source (Nim)
|
||||||
|
│ ├── main.nim # Entry point
|
||||||
|
│ ├── cli.nim # CLI commands
|
||||||
|
│ ├── lexer.nim # Tokenizer
|
||||||
|
│ ├── parser.nim # Parser
|
||||||
|
│ ├── sema.nim # Semantic analysis
|
||||||
|
│ ├── hir.nim # High-level IR
|
||||||
|
│ ├── hir_lower.nim # AST → HIR lowering
|
||||||
|
│ ├── c_backend.nim # HIR → C code generation
|
||||||
|
│ └── ...
|
||||||
|
├── stdlib/ # Standard library
|
||||||
|
│ ├── Std/
|
||||||
|
│ │ ├── Io.bux
|
||||||
|
│ │ ├── Array.bux
|
||||||
|
│ │ ├── String.bux
|
||||||
|
│ │ └── Map.bux
|
||||||
|
│ ├── runtime.c # C runtime shim
|
||||||
|
│ └── io.c # C I/O functions
|
||||||
|
├── examples/ # Example programs
|
||||||
|
├── tests/ # Unit tests (Nim)
|
||||||
|
├── docs/ # Documentation
|
||||||
|
└── Makefile
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Verbose Output
|
||||||
|
```bash
|
||||||
|
./buxc build -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inspecting Generated C
|
||||||
|
```bash
|
||||||
|
./buxc build
|
||||||
|
cat build/main.c
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Errors
|
||||||
|
|
||||||
|
| Error | Cause | Fix |
|
||||||
|
|-------|-------|-----|
|
||||||
|
| `stdlib directory not found` | `buxc` can't find `stdlib/` | Run from project root or set correct path |
|
||||||
|
| `duplicate symbol 'bux_alloc'` | Multiple stdlib modules declare same extern | Only declare in one module |
|
||||||
|
| `C compilation failed` | Generated C has errors | Check `build/main.c` for issues |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adding a New Example
|
||||||
|
|
||||||
|
1. Create `examples/myexample.bux`
|
||||||
|
2. Add `myexample` to `EXAMPLES` in `Makefile`
|
||||||
|
3. Run `make test-examples`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After making changes to the compiler:
|
||||||
|
make build
|
||||||
|
make test
|
||||||
|
|
||||||
|
# If tests pass, run examples:
|
||||||
|
make test-examples
|
||||||
|
```
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
# Bux Language Reference
|
||||||
|
|
||||||
|
This document describes the Bux programming language as implemented by the bootstrap compiler.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Lexical Structure](#lexical-structure)
|
||||||
|
2. [Types](#types)
|
||||||
|
3. [Variables](#variables)
|
||||||
|
4. [Functions](#functions)
|
||||||
|
5. [Control Flow](#control-flow)
|
||||||
|
6. [Structs](#structs)
|
||||||
|
7. [Enums](#enums)
|
||||||
|
8. [Pattern Matching](#pattern-matching)
|
||||||
|
9. [Methods and Interfaces](#methods-and-interfaces)
|
||||||
|
10. [Generics](#generics)
|
||||||
|
11. [Error Handling](#error-handling)
|
||||||
|
12. [Modules and Imports](#modules-and-imports)
|
||||||
|
13. [Operators](#operators)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lexical Structure
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
```bux
|
||||||
|
// Single-line comment
|
||||||
|
|
||||||
|
/*
|
||||||
|
Multi-line comment
|
||||||
|
/* Nested comments are supported */
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Identifiers
|
||||||
|
Identifiers start with a letter or underscore, followed by letters, digits, or underscores.
|
||||||
|
|
||||||
|
### Keywords
|
||||||
|
```
|
||||||
|
func, let, var, const, type, struct, enum, union, interface, extend
|
||||||
|
module, import, pub, extern, if, else, while, do, loop, for, in
|
||||||
|
break, continue, return, match, as, is, null, self, super, sizeof
|
||||||
|
```
|
||||||
|
|
||||||
|
### String Literals
|
||||||
|
```bux
|
||||||
|
"Hello" // String (UTF-8)
|
||||||
|
c8"Hello" // *char8 (C string)
|
||||||
|
c16"Hello" // *char16
|
||||||
|
c32"Hello" // *char32
|
||||||
|
```
|
||||||
|
|
||||||
|
### Number Literals
|
||||||
|
```bux
|
||||||
|
42 // int
|
||||||
|
3.14 // float64
|
||||||
|
0x2A // hex
|
||||||
|
0o52 // octal
|
||||||
|
0b101010 // binary
|
||||||
|
32i8 // int8 literal
|
||||||
|
1000u64 // uint64 literal
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Types
|
||||||
|
|
||||||
|
### Primitive Types
|
||||||
|
|
||||||
|
| Type | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `int8`, `int16`, `int32`, `int64`, `int` | Signed integers |
|
||||||
|
| `uint8`, `uint16`, `uint32`, `uint64`, `uint` | Unsigned integers |
|
||||||
|
| `float32`, `float64` | Floating-point |
|
||||||
|
| `bool`, `bool8`, `bool16`, `bool32` | Booleans |
|
||||||
|
| `char8`, `char16`, `char32` | Characters |
|
||||||
|
| `String` | C-compatible string (`const char*`) |
|
||||||
|
|
||||||
|
### Composite Types
|
||||||
|
|
||||||
|
```bux
|
||||||
|
*T // Pointer to T
|
||||||
|
T[] // Slice (unsized)
|
||||||
|
T[N] // Fixed-size array
|
||||||
|
(T1, T2, T3) // Tuple
|
||||||
|
func(T1) -> T2 // Function type
|
||||||
|
```
|
||||||
|
|
||||||
|
### Structs
|
||||||
|
```bux
|
||||||
|
struct Point {
|
||||||
|
x: int;
|
||||||
|
y: int;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enums
|
||||||
|
```bux
|
||||||
|
enum Color {
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Algebraic enum (tagged union)
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unions
|
||||||
|
```bux
|
||||||
|
union Bits {
|
||||||
|
asByte: uint8;
|
||||||
|
asInt: int32;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
```bux
|
||||||
|
let x: int = 42; // Immutable
|
||||||
|
var y: int = 10; // Mutable
|
||||||
|
y = 20; // OK
|
||||||
|
|
||||||
|
const MAX: int = 100; // Compile-time constant
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
```bux
|
||||||
|
func Add(a: int, b: int) -> int {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extern C function
|
||||||
|
extern func printf(fmt: *char8, ...);
|
||||||
|
|
||||||
|
// Generic function
|
||||||
|
func Min<T>(a: T, b: T) -> T {
|
||||||
|
if a < b {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Control Flow
|
||||||
|
|
||||||
|
### If / Else
|
||||||
|
```bux
|
||||||
|
if x > 0 {
|
||||||
|
PrintLine("positive");
|
||||||
|
} else if x < 0 {
|
||||||
|
PrintLine("negative");
|
||||||
|
} else {
|
||||||
|
PrintLine("zero");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Loops
|
||||||
|
```bux
|
||||||
|
while i < 10 {
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
i = i + 1;
|
||||||
|
} while i < 10;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Infinite loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
// Range 0 to 9 (exclusive)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..=10 {
|
||||||
|
// Range 0 to 10 (inclusive)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Break / Continue with Labels
|
||||||
|
```bux
|
||||||
|
outer: loop {
|
||||||
|
loop {
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Structs
|
||||||
|
|
||||||
|
```bux
|
||||||
|
struct Rectangle {
|
||||||
|
width: int;
|
||||||
|
height: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let rect: Rectangle = Rectangle { width: 10, height: 5 };
|
||||||
|
PrintInt(rect.width);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Enums
|
||||||
|
|
||||||
|
### Simple Enums
|
||||||
|
```bux
|
||||||
|
enum Color { Red, Green, Blue }
|
||||||
|
|
||||||
|
let c: Color = Color::Red;
|
||||||
|
if c == Color::Red {
|
||||||
|
PrintLine("red");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Algebraic Enums
|
||||||
|
```bux
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let r: Result = Result { tag: Result_Ok };
|
||||||
|
r.data.Ok_0 = 42;
|
||||||
|
|
||||||
|
if r.tag == Result_Ok {
|
||||||
|
PrintInt(r.data.Ok_0);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pattern Matching
|
||||||
|
|
||||||
|
```bux
|
||||||
|
match opt {
|
||||||
|
Option::Some(value) => PrintInt(value),
|
||||||
|
Option::None => PrintLine("none")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported patterns:
|
||||||
|
- Wildcard: `_`
|
||||||
|
- Literal: `42`, `"hello"`, `true`
|
||||||
|
- Identifier: `name`
|
||||||
|
- Range: `1..9`, `1..=9`
|
||||||
|
- Enum destructuring: `Shape::Circle(r)`
|
||||||
|
- Struct destructuring: `Point { x: 0, y: 0 }`
|
||||||
|
- Tuple: `(a, b, c)`
|
||||||
|
- Guard: `t if t < 0`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Methods and Interfaces
|
||||||
|
|
||||||
|
```bux
|
||||||
|
struct Rectangle {
|
||||||
|
width: int;
|
||||||
|
height: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Drawable {
|
||||||
|
func Draw(self: Rectangle);
|
||||||
|
}
|
||||||
|
|
||||||
|
extend Rectangle for Drawable {
|
||||||
|
func Draw(self: Rectangle) {
|
||||||
|
PrintLine("Drawing rectangle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or extend with standalone methods
|
||||||
|
extend Rectangle {
|
||||||
|
func Area(self: Rectangle) -> int {
|
||||||
|
return self.width * self.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Generics
|
||||||
|
|
||||||
|
Generic functions are monomorphized at compile time:
|
||||||
|
|
||||||
|
```bux
|
||||||
|
func Swap<T>(a: *T, b: *T) {
|
||||||
|
let tmp: T = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let x: int = 1;
|
||||||
|
let y: int = 2;
|
||||||
|
Swap<int>(&x, &y);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Result and Option Types
|
||||||
|
```bux
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Option {
|
||||||
|
Some(int),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### The `?` Operator
|
||||||
|
The `?` operator automatically propagates errors:
|
||||||
|
|
||||||
|
```bux
|
||||||
|
func Divide(a: int, b: int) -> Result {
|
||||||
|
if b == 0 {
|
||||||
|
return Result_NewErr("division by zero");
|
||||||
|
}
|
||||||
|
return Result_NewOk(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compute() -> Result {
|
||||||
|
let x: int = Divide(10, 2)?; // If Err, returns immediately
|
||||||
|
let y: int = Divide(x, 5)?;
|
||||||
|
return Result_NewOk(y);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`?` can be used on `Result` and `Option` types in any expression context.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Modules and Imports
|
||||||
|
|
||||||
|
```bux
|
||||||
|
// Single import
|
||||||
|
import Std::Io::PrintLine;
|
||||||
|
|
||||||
|
// Multiple imports
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
// Wildcard import
|
||||||
|
import Std::Io::*;
|
||||||
|
|
||||||
|
// Module declaration
|
||||||
|
module MyModule;
|
||||||
|
|
||||||
|
pub func PublicFunc() -> int {
|
||||||
|
return 42;
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrivateFunc() -> int {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Operators
|
||||||
|
|
||||||
|
### Arithmetic
|
||||||
|
`+`, `-`, `*`, `/`, `%`, `**` (power)
|
||||||
|
|
||||||
|
### Comparison
|
||||||
|
`==`, `!=`, `<`, `<=`, `>`, `>=`
|
||||||
|
|
||||||
|
### Logical
|
||||||
|
`&&`, `||`, `!`
|
||||||
|
|
||||||
|
### Bitwise
|
||||||
|
`&`, `|`, `^`, `~`, `<<`, `>>`
|
||||||
|
|
||||||
|
### Assignment
|
||||||
|
`=`, `+=`, `-=`, `*=`, `/=`, `%=`, `&=`, `|=`, `^=`, `<<=`, `>>=`
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- `as` — Cast: `expr as Type`
|
||||||
|
- `is` — Type test: `expr is Type`
|
||||||
|
- `?` — Try / error propagation: `expr?`
|
||||||
|
- `&` — Address-of: `&var`
|
||||||
|
- `*` — Dereference: `*ptr`
|
||||||
|
- `::` — Path separator: `Module::Name`
|
||||||
|
- `..` — Range (exclusive): `0..10`
|
||||||
|
- `..=` — Range (inclusive): `0..=10`
|
||||||
|
- `sizeof` — Size of type: `sizeof(Type)`
|
||||||
+176
@@ -0,0 +1,176 @@
|
|||||||
|
# Bux Standard Library
|
||||||
|
|
||||||
|
The Bux standard library provides core functionality for systems programming. It is intentionally minimal for the bootstrap phase and will grow as the language matures.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Io
|
||||||
|
|
||||||
|
Basic input/output operations wrapping C stdio.
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `PrintLine` | `func PrintLine(s: String)` | Print string with newline |
|
||||||
|
| `Print` | `func Print(s: String)` | Print string without newline |
|
||||||
|
| `PrintInt` | `func PrintInt(n: int)` | Print integer |
|
||||||
|
| `PrintFloat` | `func PrintFloat(f: float64)` | Print float |
|
||||||
|
| `PrintBool` | `func PrintBool(b: bool)` | Print boolean |
|
||||||
|
| `ReadLine` | `func ReadLine() -> String` | Read line from stdin |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("Hello, World!");
|
||||||
|
PrintInt(42);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Array
|
||||||
|
|
||||||
|
Dynamic array of integers (currently hardcoded for `int`).
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
```bux
|
||||||
|
struct Array {
|
||||||
|
data: *int,
|
||||||
|
len: uint,
|
||||||
|
cap: uint,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Array_New` | `func Array_New(cap: uint) -> Array` | Create new array with capacity |
|
||||||
|
| `Array_Push` | `func Array_Push(arr: *Array, value: int)` | Append element |
|
||||||
|
| `Array_Get` | `func Array_Get(arr: *Array, index: uint) -> int` | Get element at index |
|
||||||
|
| `Array_Len` | `func Array_Len(arr: *Array) -> uint` | Get length |
|
||||||
|
| `Array_Free` | `func Array_Free(arr: *Array)` | Free memory |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Array::{Array, Array_New, Array_Push, Array_Get};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let arr: Array = Array_New(4);
|
||||||
|
Array_Push(&arr, 10);
|
||||||
|
Array_Push(&arr, 20);
|
||||||
|
PrintInt(Array_Get(&arr, 0)); // 10
|
||||||
|
Array_Free(&arr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::String
|
||||||
|
|
||||||
|
String manipulation utilities for the built-in `String` type (`const char*` in C).
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `String_Len` | `func String_Len(s: String) -> uint` | Length of string (wraps `strlen`) |
|
||||||
|
| `String_Eq` | `func String_Eq(a: String, b: String) -> bool` | Compare strings for equality |
|
||||||
|
| `String_Concat` | `func String_Concat(a: String, b: String) -> String` | Concatenate two strings (allocates) |
|
||||||
|
| `String_Copy` | `func String_Copy(s: String) -> String` | Copy string (allocates) |
|
||||||
|
| `String_StartsWith` | `func String_StartsWith(s: String, prefix: String) -> bool` | Check prefix |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::String::{String_Len, String_Concat, String_Eq};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let hello: String = "Hello";
|
||||||
|
let world: String = "World";
|
||||||
|
let greeting: String = String_Concat(hello, ", ");
|
||||||
|
let full: String = String_Concat(greeting, world);
|
||||||
|
PrintLine(full); // "Hello, World"
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Std::Map
|
||||||
|
|
||||||
|
Hash map with `String` keys and `int` values using linear probing.
|
||||||
|
|
||||||
|
### Types
|
||||||
|
|
||||||
|
```bux
|
||||||
|
struct MapEntry {
|
||||||
|
key: String,
|
||||||
|
value: int,
|
||||||
|
occupied: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Map {
|
||||||
|
entries: *MapEntry,
|
||||||
|
cap: uint,
|
||||||
|
len: uint,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `Map_New` | `func Map_New(cap: uint) -> Map` | Create new map with given capacity |
|
||||||
|
| `Map_Set` | `func Map_Set(m: *Map, key: String, value: int)` | Insert or update key |
|
||||||
|
| `Map_Get` | `func Map_Get(m: *Map, key: String) -> int` | Get value by key (0 if missing) |
|
||||||
|
| `Map_Has` | `func Map_Has(m: *Map, key: String) -> bool` | Check if key exists |
|
||||||
|
| `Map_Len` | `func Map_Len(m: *Map) -> uint` | Number of entries |
|
||||||
|
| `Map_Free` | `func Map_Free(m: *Map)` | Free memory |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
```bux
|
||||||
|
import Std::Map::{Map, Map_New, Map_Set, Map_Get, Map_Has};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let m: Map = Map_New(16);
|
||||||
|
Map_Set(&m, "one", 1);
|
||||||
|
Map_Set(&m, "two", 2);
|
||||||
|
PrintInt(Map_Get(&m, "one")); // 1
|
||||||
|
PrintInt(Map_Get(&m, "three")); // 0
|
||||||
|
Map_Free(&m);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Runtime Functions
|
||||||
|
|
||||||
|
These C functions are provided by `runtime.c` and are available via `extern` declarations.
|
||||||
|
|
||||||
|
| Function | Signature | Description |
|
||||||
|
|----------|-----------|-------------|
|
||||||
|
| `bux_alloc` | `func bux_alloc(size: uint) -> *void` | Allocate memory (wraps `malloc`) |
|
||||||
|
| `bux_realloc` | `func bux_realloc(ptr: *void, size: uint) -> *void` | Reallocate memory |
|
||||||
|
| `bux_free` | `func bux_free(ptr: *void)` | Free memory |
|
||||||
|
| `bux_bounds_check` | `func bux_bounds_check(index: uint, len: uint)` | Panic on out-of-bounds |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Modules
|
||||||
|
|
||||||
|
Planned for future phases:
|
||||||
|
|
||||||
|
- `Std::Result` — Generic `Result<T, E>` with `?` operator support
|
||||||
|
- `Std::Option` — Generic `Option<T>`
|
||||||
|
- `Std::Math` — `Sqrt`, `Pow`, `Min`, `Max`, `Abs`
|
||||||
|
- `Std::Os` — `Args`, `Env`, `Exit`, `Cwd`
|
||||||
|
- `Std::Fmt` — String formatting with interpolation
|
||||||
|
- `Std::Iter` — Iterator trait and combinators
|
||||||
|
- `Std::Task` / `Std::Channel` — Lightweight concurrency
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// Map - Hash map with String keys and int values
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::Map::{Map, Map_New, Map_Set, Map_Get, Map_Has, Map_Len};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let m: Map = Map_New(16);
|
||||||
|
|
||||||
|
PrintLine("Map operations:");
|
||||||
|
|
||||||
|
Map_Set(&m, "one", 1);
|
||||||
|
Map_Set(&m, "two", 2);
|
||||||
|
Map_Set(&m, "three", 3);
|
||||||
|
|
||||||
|
PrintLine("Length:");
|
||||||
|
PrintInt(Map_Len(&m));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
PrintLine("Get 'one':");
|
||||||
|
PrintInt(Map_Get(&m, "one"));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
PrintLine("Get 'two':");
|
||||||
|
PrintInt(Map_Get(&m, "two"));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
PrintLine("Get 'three':");
|
||||||
|
PrintInt(Map_Get(&m, "three"));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
Map_Set(&m, "two", 22);
|
||||||
|
PrintLine("Updated 'two':");
|
||||||
|
PrintInt(Map_Get(&m, "two"));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
if Map_Has(&m, "one") {
|
||||||
|
PrintLine("Has 'one': yes");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Map_Has(&m, "four") {
|
||||||
|
PrintLine("Has 'four': no");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
// Result and Option - Error handling and optional values
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_NewOk(value: int) -> Result {
|
||||||
|
let r: Result = Result { tag: Result_Ok };
|
||||||
|
r.data.Ok_0 = value;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_NewErr(msg: String) -> Result {
|
||||||
|
let r: Result = Result { tag: Result_Err };
|
||||||
|
r.data.Err_0 = msg;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_IsOk(r: Result) -> bool {
|
||||||
|
return r.tag == Result_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_IsErr(r: Result) -> bool {
|
||||||
|
return r.tag == Result_Err;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_Unwrap(r: Result) -> int {
|
||||||
|
if r.tag == Result_Ok {
|
||||||
|
return r.data.Ok_0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_UnwrapOr(r: Result, fallback: int) -> int {
|
||||||
|
if r.tag == Result_Ok {
|
||||||
|
return r.data.Ok_0;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Option {
|
||||||
|
Some(int),
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
func Option_NewSome(value: int) -> Option {
|
||||||
|
let o: Option = Option { tag: Option_Some };
|
||||||
|
o.data.Some_0 = value;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Option_NewNone() -> Option {
|
||||||
|
return Option { tag: Option_None };
|
||||||
|
}
|
||||||
|
|
||||||
|
func Option_IsSome(o: Option) -> bool {
|
||||||
|
return o.tag == Option_Some;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Option_UnwrapOr(o: Option, fallback: int) -> int {
|
||||||
|
if o.tag == Option_Some {
|
||||||
|
return o.data.Some_0;
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Divide(a: int, b: int) -> Result {
|
||||||
|
if b == 0 {
|
||||||
|
return Result_NewErr("division by zero");
|
||||||
|
}
|
||||||
|
return Result_NewOk(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
func SafeDivide(a: int, b: int) -> Option {
|
||||||
|
if b == 0 {
|
||||||
|
return Option_NewNone();
|
||||||
|
}
|
||||||
|
return Option_NewSome(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("Result demo:");
|
||||||
|
|
||||||
|
let r1: Result = Divide(10, 2);
|
||||||
|
if Result_IsOk(r1) {
|
||||||
|
PrintLine("10 / 2 = ");
|
||||||
|
PrintInt(Result_Unwrap(r1));
|
||||||
|
PrintLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
let r2: Result = Divide(10, 0);
|
||||||
|
if Result_IsErr(r2) {
|
||||||
|
PrintLine("10 / 0 failed");
|
||||||
|
}
|
||||||
|
PrintLine("unwrap_or = ");
|
||||||
|
PrintInt(Result_UnwrapOr(r2, -1));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
PrintLine("Option demo:");
|
||||||
|
|
||||||
|
let o1: Option = SafeDivide(20, 4);
|
||||||
|
if Option_IsSome(o1) {
|
||||||
|
PrintLine("20 / 4 = ");
|
||||||
|
PrintInt(Option_UnwrapOr(o1, 0));
|
||||||
|
PrintLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
let o2: Option = SafeDivide(20, 0);
|
||||||
|
PrintLine("20 / 0 fallback = ");
|
||||||
|
PrintInt(Option_UnwrapOr(o2, -1));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Strings - String manipulation with Std::String
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
import Std::String::{String_Len, String_Eq, String_Concat};
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
let hello: String = "Hello";
|
||||||
|
let world: String = "World";
|
||||||
|
|
||||||
|
PrintLine("String operations:");
|
||||||
|
|
||||||
|
// Length
|
||||||
|
PrintLine("Length of 'Hello':");
|
||||||
|
PrintInt(String_Len(hello));
|
||||||
|
PrintLine("");
|
||||||
|
|
||||||
|
// Concatenation
|
||||||
|
let greeting: String = String_Concat(hello, ", ");
|
||||||
|
let greeting2: String = String_Concat(greeting, world);
|
||||||
|
let full: String = String_Concat(greeting2, "!");
|
||||||
|
|
||||||
|
PrintLine("Concatenated:");
|
||||||
|
PrintLine(full);
|
||||||
|
|
||||||
|
// Equality
|
||||||
|
if String_Eq(hello, "Hello") {
|
||||||
|
PrintLine("'Hello' equals 'Hello'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !String_Eq(hello, world) {
|
||||||
|
PrintLine("'Hello' does not equal 'World'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
// Try operator ? - Error propagation
|
||||||
|
import Std::Io::{PrintLine, PrintInt};
|
||||||
|
|
||||||
|
enum Result {
|
||||||
|
Ok(int),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_NewOk(value: int) -> Result {
|
||||||
|
let r: Result = Result { tag: Result_Ok };
|
||||||
|
r.data.Ok_0 = value;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Result_NewErr(msg: String) -> Result {
|
||||||
|
let r: Result = Result { tag: Result_Err };
|
||||||
|
r.data.Err_0 = msg;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Divide(a: int, b: int) -> Result {
|
||||||
|
if b == 0 {
|
||||||
|
return Result_NewErr("division by zero");
|
||||||
|
}
|
||||||
|
return Result_NewOk(a / b);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Compute() -> Result {
|
||||||
|
let x: int = Divide(10, 2)?;
|
||||||
|
let y: int = Divide(x, 5)?;
|
||||||
|
return Result_NewOk(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
func ComputeWithError() -> Result {
|
||||||
|
let x: int = Divide(10, 0)?;
|
||||||
|
let y: int = Divide(x, 5)?;
|
||||||
|
return Result_NewOk(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
func Main() -> int {
|
||||||
|
PrintLine("Try operator demo:");
|
||||||
|
|
||||||
|
let r1: Result = Compute();
|
||||||
|
if r1.tag == Result_Ok {
|
||||||
|
PrintLine("Compute() = ");
|
||||||
|
PrintInt(r1.data.Ok_0);
|
||||||
|
PrintLine("");
|
||||||
|
} else {
|
||||||
|
PrintLine("Compute() failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
let r2: Result = ComputeWithError();
|
||||||
|
if r2.tag == Result_Err {
|
||||||
|
PrintLine("ComputeWithError() failed as expected");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -113,6 +113,7 @@ type
|
|||||||
ekTuple
|
ekTuple
|
||||||
ekCast
|
ekCast
|
||||||
ekIs
|
ekIs
|
||||||
|
ekTry
|
||||||
ekBlock
|
ekBlock
|
||||||
ekMatch
|
ekMatch
|
||||||
|
|
||||||
@@ -186,6 +187,9 @@ type
|
|||||||
of ekIs:
|
of ekIs:
|
||||||
exprIsOperand*: Expr
|
exprIsOperand*: Expr
|
||||||
exprIsType*: TypeExpr
|
exprIsType*: TypeExpr
|
||||||
|
of ekTry:
|
||||||
|
exprTryOperand*: Expr
|
||||||
|
exprTryType*: TypeExpr # nil for Result?, or explicit target type
|
||||||
of ekBlock:
|
of ekBlock:
|
||||||
exprBlock*: Block
|
exprBlock*: Block
|
||||||
of ekMatch:
|
of ekMatch:
|
||||||
|
|||||||
@@ -235,6 +235,10 @@ proc emitExpr(be: var CBackend, node: HirNode): string =
|
|||||||
of hIs:
|
of hIs:
|
||||||
return "true" # TODO: proper type checking
|
return "true" # TODO: proper type checking
|
||||||
|
|
||||||
|
of hSizeOf:
|
||||||
|
let typ = typeToC(node.sizeOfType)
|
||||||
|
return &"sizeof({typ})"
|
||||||
|
|
||||||
of hIf:
|
of hIf:
|
||||||
# Ternary expression
|
# Ternary expression
|
||||||
let cond = be.emitExpr(node.ifCond)
|
let cond = be.emitExpr(node.ifCond)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type
|
|||||||
# Type operations
|
# Type operations
|
||||||
hCast
|
hCast
|
||||||
hIs
|
hIs
|
||||||
|
hSizeOf
|
||||||
# Composite
|
# Composite
|
||||||
hBlock
|
hBlock
|
||||||
hStructInit
|
hStructInit
|
||||||
@@ -104,6 +105,8 @@ type
|
|||||||
of hIs:
|
of hIs:
|
||||||
isOperand*: HirNode
|
isOperand*: HirNode
|
||||||
isType*: Type
|
isType*: Type
|
||||||
|
of hSizeOf:
|
||||||
|
sizeOfType*: Type
|
||||||
of hBlock:
|
of hBlock:
|
||||||
blockStmts*: seq[HirNode]
|
blockStmts*: seq[HirNode]
|
||||||
blockExpr*: HirNode
|
blockExpr*: HirNode
|
||||||
|
|||||||
+124
-48
@@ -9,6 +9,8 @@ type
|
|||||||
currentFuncRetType*: Type
|
currentFuncRetType*: Type
|
||||||
currentFuncDecl*: Decl
|
currentFuncDecl*: Decl
|
||||||
varCounter*: int
|
varCounter*: int
|
||||||
|
tryCounter*: int
|
||||||
|
pendingStmts*: seq[HirNode]
|
||||||
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
typeSubst*: Table[string, Type] # Type parameter substitution for generics
|
||||||
importTable*: Table[string, string] # Local name → fully qualified name for imports
|
importTable*: Table[string, string] # Local name → fully qualified name for imports
|
||||||
|
|
||||||
@@ -16,6 +18,18 @@ proc freshName(ctx: var LowerCtx): string =
|
|||||||
inc ctx.varCounter
|
inc ctx.varCounter
|
||||||
result = "__tmp_" & $ctx.varCounter
|
result = "__tmp_" & $ctx.varCounter
|
||||||
|
|
||||||
|
proc freshTryVar(ctx: var LowerCtx): string =
|
||||||
|
inc ctx.tryCounter
|
||||||
|
result = "__try_" & $ctx.tryCounter
|
||||||
|
|
||||||
|
proc flushPending(ctx: var LowerCtx, node: HirNode): HirNode =
|
||||||
|
if ctx.pendingStmts.len > 0:
|
||||||
|
var stmts = ctx.pendingStmts
|
||||||
|
ctx.pendingStmts = @[]
|
||||||
|
stmts.add(node)
|
||||||
|
return hirBlock(stmts, nil, makeVoid(), node.loc)
|
||||||
|
return node
|
||||||
|
|
||||||
proc lowerMatch(ctx: var LowerCtx, subject: HirNode, arms: seq[HirMatchArm], typ: Type, loc: SourceLocation): HirNode =
|
proc lowerMatch(ctx: var LowerCtx, subject: HirNode, arms: seq[HirMatchArm], typ: Type, loc: SourceLocation): HirNode =
|
||||||
# Lower match expression to a block with if-else chain.
|
# Lower match expression to a block with if-else chain.
|
||||||
# For now, supports enum tag matching and wildcard/ident fallbacks.
|
# For now, supports enum tag matching and wildcard/ident fallbacks.
|
||||||
@@ -100,6 +114,8 @@ proc initLowerCtx*(module: Module, sema: Sema): LowerCtx =
|
|||||||
result.globalScope = sema.globalScope
|
result.globalScope = sema.globalScope
|
||||||
result.methodTable = sema.methodTable
|
result.methodTable = sema.methodTable
|
||||||
result.varCounter = 0
|
result.varCounter = 0
|
||||||
|
result.tryCounter = 0
|
||||||
|
result.pendingStmts = @[]
|
||||||
result.typeSubst = initTable[string, Type]()
|
result.typeSubst = initTable[string, Type]()
|
||||||
result.importTable = initTable[string, string]()
|
result.importTable = initTable[string, string]()
|
||||||
|
|
||||||
@@ -110,14 +126,26 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type =
|
|||||||
case te.typeName
|
case te.typeName
|
||||||
of "void": return makeVoid()
|
of "void": return makeVoid()
|
||||||
of "bool": return makeBool()
|
of "bool": return makeBool()
|
||||||
|
of "bool8": return makeBool8()
|
||||||
|
of "bool16": return makeBool16()
|
||||||
|
of "bool32": return makeBool32()
|
||||||
|
of "char8": return makeChar8()
|
||||||
|
of "char16": return makeChar16()
|
||||||
|
of "char32": return makeChar32()
|
||||||
|
of "String", "str": return makeStr()
|
||||||
of "int": return makeInt()
|
of "int": return makeInt()
|
||||||
of "int32": return makeInt()
|
of "int8": return makeInt8()
|
||||||
|
of "int16": return makeInt16()
|
||||||
|
of "int32": return makeInt32()
|
||||||
of "int64": return makeInt64()
|
of "int64": return makeInt64()
|
||||||
of "float64": return makeFloat64()
|
|
||||||
of "float32": return makeFloat32()
|
|
||||||
of "uint": return makeUInt()
|
of "uint": return makeUInt()
|
||||||
of "uint32": return makeUInt()
|
of "uint8": return makeUInt8()
|
||||||
|
of "uint16": return makeUInt16()
|
||||||
|
of "uint32": return makeUInt32()
|
||||||
of "uint64": return makeUInt64()
|
of "uint64": return makeUInt64()
|
||||||
|
of "float": return makeFloat64()
|
||||||
|
of "float32": return makeFloat32()
|
||||||
|
of "float64": return makeFloat64()
|
||||||
else: return makeNamed(te.typeName)
|
else: return makeNamed(te.typeName)
|
||||||
of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee))
|
of tekPointer: return makePointer(ctx.resolveTypeExpr(te.pointerPointee))
|
||||||
of tekSlice: return makeSlice(ctx.resolveTypeExpr(te.sliceElement))
|
of tekSlice: return makeSlice(ctx.resolveTypeExpr(te.sliceElement))
|
||||||
@@ -241,6 +269,9 @@ proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type =
|
|||||||
if expr.exprCastType != nil:
|
if expr.exprCastType != nil:
|
||||||
return ctx.resolveTypeExpr(expr.exprCastType)
|
return ctx.resolveTypeExpr(expr.exprCastType)
|
||||||
return makeUnknown()
|
return makeUnknown()
|
||||||
|
of ekTry:
|
||||||
|
# For now, assume Result<int, String> -> int or Option<int> -> int
|
||||||
|
return makeInt()
|
||||||
of ekBlock:
|
of ekBlock:
|
||||||
if expr.exprBlock.stmts.len > 0:
|
if expr.exprBlock.stmts.len > 0:
|
||||||
let last = expr.exprBlock.stmts[^1]
|
let last = expr.exprBlock.stmts[^1]
|
||||||
@@ -426,6 +457,60 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return HirNode(kind: hIs, isOperand: operand, isType: isType,
|
return HirNode(kind: hIs, isOperand: operand, isType: isType,
|
||||||
typ: makeBool(), loc: loc)
|
typ: makeBool(), loc: loc)
|
||||||
|
|
||||||
|
of ekTry:
|
||||||
|
let operand = ctx.lowerExpr(expr.exprTryOperand)
|
||||||
|
let operandType = ctx.resolveExprType(expr.exprTryOperand)
|
||||||
|
|
||||||
|
var typeName = ""
|
||||||
|
var errTag = ""
|
||||||
|
var okField = ""
|
||||||
|
if operandType.kind == tkNamed:
|
||||||
|
typeName = operandType.name
|
||||||
|
case typeName
|
||||||
|
of "Result":
|
||||||
|
errTag = "Result_Err"
|
||||||
|
okField = "Ok_0"
|
||||||
|
of "Option":
|
||||||
|
errTag = "Option_None"
|
||||||
|
okField = "Some_0"
|
||||||
|
else:
|
||||||
|
errTag = typeName & "_Err"
|
||||||
|
okField = "Ok_0"
|
||||||
|
else:
|
||||||
|
errTag = "Result_Err"
|
||||||
|
okField = "Ok_0"
|
||||||
|
typeName = "Result"
|
||||||
|
|
||||||
|
let tmpName = ctx.freshTryVar()
|
||||||
|
let tmpAlloca = hirAlloca(tmpName, operandType, loc)
|
||||||
|
let tmpVar = hirVar(tmpName, makePointer(operandType), loc)
|
||||||
|
let tmpStore = hirStore(tmpVar, operand, loc)
|
||||||
|
|
||||||
|
let tagPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "tag",
|
||||||
|
typ: makePointer(makeNamed(typeName & "_Tag")), loc: loc)
|
||||||
|
let tagLoad = HirNode(kind: hLoad, loadPtr: tagPtr,
|
||||||
|
typ: makeNamed(typeName & "_Tag"), loc: loc)
|
||||||
|
let errConst = hirVar(errTag, makeNamed(typeName & "_Tag"), loc)
|
||||||
|
let cond = hirBinary(tkEq, tagLoad, errConst, makeBool(), loc)
|
||||||
|
|
||||||
|
let retNode = hirReturn(tmpVar, loc)
|
||||||
|
let thenBlock = hirBlock(@[retNode], nil, makeVoid(), loc)
|
||||||
|
let ifNode = HirNode(kind: hIf, ifCond: cond, ifThen: thenBlock,
|
||||||
|
ifElse: nil, typ: makeVoid(), loc: loc)
|
||||||
|
|
||||||
|
let dataPtr = HirNode(kind: hFieldPtr, fieldPtrBase: tmpVar, fieldName: "data",
|
||||||
|
typ: makePointer(makeNamed(typeName & "_Data")), loc: loc)
|
||||||
|
let dataLoad = HirNode(kind: hLoad, loadPtr: dataPtr,
|
||||||
|
typ: makeNamed(typeName & "_Data"), loc: loc)
|
||||||
|
let okPtr = HirNode(kind: hFieldPtr, fieldPtrBase: dataLoad, fieldName: okField,
|
||||||
|
typ: makePointer(makeInt()), loc: loc)
|
||||||
|
let okLoad = HirNode(kind: hLoad, loadPtr: okPtr, typ: makeInt(), loc: loc)
|
||||||
|
|
||||||
|
ctx.pendingStmts.add(tmpAlloca)
|
||||||
|
ctx.pendingStmts.add(tmpStore)
|
||||||
|
ctx.pendingStmts.add(ifNode)
|
||||||
|
return okLoad
|
||||||
|
|
||||||
of ekMatch:
|
of ekMatch:
|
||||||
let subject = ctx.lowerExpr(expr.exprMatchSubject)
|
let subject = ctx.lowerExpr(expr.exprMatchSubject)
|
||||||
var arms: seq[HirMatchArm] = @[]
|
var arms: seq[HirMatchArm] = @[]
|
||||||
@@ -434,8 +519,8 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode =
|
|||||||
return lowerMatch(ctx, subject, arms, typ, loc)
|
return lowerMatch(ctx, subject, arms, typ, loc)
|
||||||
|
|
||||||
of ekSizeOf:
|
of ekSizeOf:
|
||||||
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
let ty = ctx.resolveTypeExpr(expr.exprSizeOfType)
|
||||||
typ: makeInt(), loc: loc)
|
return HirNode(kind: hSizeOf, sizeOfType: ty, typ: makeInt(), loc: loc)
|
||||||
|
|
||||||
of ekIntrinsic:
|
of ekIntrinsic:
|
||||||
return HirNode(kind: hLit, litToken: Token(kind: tkStringLiteral, text: "\"\"", loc: loc),
|
return HirNode(kind: hLit, litToken: Token(kind: tkStringLiteral, text: "\"\"", loc: loc),
|
||||||
@@ -451,21 +536,20 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
|
|
||||||
case stmt.kind
|
case stmt.kind
|
||||||
of skExpr:
|
of skExpr:
|
||||||
return ctx.lowerExpr(stmt.stmtExpr)
|
return ctx.flushPending(ctx.lowerExpr(stmt.stmtExpr))
|
||||||
|
|
||||||
of skLet:
|
of skLet:
|
||||||
let initHir = ctx.lowerExpr(stmt.stmtLetInit)
|
let initHir = ctx.lowerExpr(stmt.stmtLetInit)
|
||||||
let allocaType = if stmt.stmtLetType != nil:
|
let allocaType = if stmt.stmtLetType != nil:
|
||||||
case stmt.stmtLetType.kind
|
case stmt.stmtLetType.kind
|
||||||
of tekNamed:
|
of tekNamed:
|
||||||
case stmt.stmtLetType.typeName
|
ctx.resolveTypeExpr(stmt.stmtLetType)
|
||||||
of "int", "int32": makeInt()
|
of tekPointer:
|
||||||
of "int64": makeInt64()
|
let pointeeType = ctx.resolveTypeExpr(stmt.stmtLetType.pointerPointee)
|
||||||
of "float64": makeFloat64()
|
makePointer(pointeeType)
|
||||||
of "float32": makeFloat32()
|
of tekSlice:
|
||||||
of "bool": makeBool()
|
let elemType = ctx.resolveTypeExpr(stmt.stmtLetType.sliceElement)
|
||||||
else: makeNamed(stmt.stmtLetType.typeName)
|
makeSlice(elemType)
|
||||||
of tekPointer: makePointer(makeUnknown())
|
|
||||||
else: makeUnknown()
|
else: makeUnknown()
|
||||||
else:
|
else:
|
||||||
ctx.resolveExprType(stmt.stmtLetInit)
|
ctx.resolveExprType(stmt.stmtLetInit)
|
||||||
@@ -473,12 +557,15 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
let alloca = hirAlloca(stmt.stmtLetName, allocaType, loc)
|
let alloca = hirAlloca(stmt.stmtLetName, allocaType, loc)
|
||||||
let varNode = hirVar(stmt.stmtLetName, makePointer(allocaType), loc)
|
let varNode = hirVar(stmt.stmtLetName, makePointer(allocaType), loc)
|
||||||
let store = hirStore(varNode, initHir, loc)
|
let store = hirStore(varNode, initHir, loc)
|
||||||
return HirNode(kind: hBlock, blockStmts: @[alloca, store],
|
var stmts = ctx.pendingStmts
|
||||||
blockExpr: nil, typ: makeVoid(), loc: loc)
|
ctx.pendingStmts = @[]
|
||||||
|
stmts.add(alloca)
|
||||||
|
stmts.add(store)
|
||||||
|
return hirBlock(stmts, nil, makeVoid(), loc)
|
||||||
|
|
||||||
of skReturn:
|
of skReturn:
|
||||||
let value = if stmt.stmtReturnValue != nil: ctx.lowerExpr(stmt.stmtReturnValue) else: nil
|
let value = if stmt.stmtReturnValue != nil: ctx.lowerExpr(stmt.stmtReturnValue) else: nil
|
||||||
return hirReturn(value, loc)
|
return ctx.flushPending(hirReturn(value, loc))
|
||||||
|
|
||||||
of skIf:
|
of skIf:
|
||||||
let cond = ctx.lowerExpr(stmt.stmtIfCond)
|
let cond = ctx.lowerExpr(stmt.stmtIfCond)
|
||||||
@@ -496,26 +583,26 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
current = HirNode(kind: hIf, ifCond: elifCond, ifThen: elifBlock,
|
current = HirNode(kind: hIf, ifCond: elifCond, ifThen: elifBlock,
|
||||||
ifElse: current, typ: makeVoid(), loc: elifBranch.loc)
|
ifElse: current, typ: makeVoid(), loc: elifBranch.loc)
|
||||||
elseBlock = current
|
elseBlock = current
|
||||||
return HirNode(kind: hIf, ifCond: cond, ifThen: thenBlock, ifElse: elseBlock,
|
return ctx.flushPending(HirNode(kind: hIf, ifCond: cond, ifThen: thenBlock, ifElse: elseBlock,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skWhile:
|
of skWhile:
|
||||||
let cond = ctx.lowerExpr(stmt.stmtWhileCond)
|
let cond = ctx.lowerExpr(stmt.stmtWhileCond)
|
||||||
let body = ctx.lowerBlock(stmt.stmtWhileBody)
|
let body = ctx.lowerBlock(stmt.stmtWhileBody)
|
||||||
return HirNode(kind: hWhile, whileCond: cond, whileBody: body,
|
return ctx.flushPending(HirNode(kind: hWhile, whileCond: cond, whileBody: body,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skLoop:
|
of skLoop:
|
||||||
let body = ctx.lowerBlock(stmt.stmtLoopBody)
|
let body = ctx.lowerBlock(stmt.stmtLoopBody)
|
||||||
return HirNode(kind: hLoop, loopBody: body, typ: makeVoid(), loc: loc)
|
return ctx.flushPending(HirNode(kind: hLoop, loopBody: body, typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skBreak:
|
of skBreak:
|
||||||
return HirNode(kind: hBreak, breakLabel: stmt.stmtBreakLabel,
|
return ctx.flushPending(HirNode(kind: hBreak, breakLabel: stmt.stmtBreakLabel,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skContinue:
|
of skContinue:
|
||||||
return HirNode(kind: hContinue, continueLabel: stmt.stmtContinueLabel,
|
return ctx.flushPending(HirNode(kind: hContinue, continueLabel: stmt.stmtContinueLabel,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skFor:
|
of skFor:
|
||||||
let iterExpr = stmt.stmtForIter
|
let iterExpr = stmt.stmtForIter
|
||||||
@@ -559,28 +646,28 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode =
|
|||||||
|
|
||||||
# Wrap in a block so loop variable doesn't leak into outer scope
|
# Wrap in a block so loop variable doesn't leak into outer scope
|
||||||
let forBlock = hirBlock(@[initStmt, initStore, whileNode], nil, makeVoid(), loc, isScope = true)
|
let forBlock = hirBlock(@[initStmt, initStore, whileNode], nil, makeVoid(), loc, isScope = true)
|
||||||
return forBlock
|
return ctx.flushPending(forBlock)
|
||||||
|
|
||||||
# Generic iterator for loop (simplified - just infinite loop for now)
|
# Generic iterator for loop (simplified - just infinite loop for now)
|
||||||
let loweredIter = ctx.lowerExpr(iterExpr)
|
let loweredIter = ctx.lowerExpr(iterExpr)
|
||||||
let loweredBody = ctx.lowerBlock(body)
|
let loweredBody = ctx.lowerBlock(body)
|
||||||
return HirNode(kind: hLoop, loopBody: loweredBody, typ: makeVoid(), loc: loc)
|
return ctx.flushPending(HirNode(kind: hLoop, loopBody: loweredBody, typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skDoWhile:
|
of skDoWhile:
|
||||||
let body = ctx.lowerBlock(stmt.stmtDoWhileBody)
|
let body = ctx.lowerBlock(stmt.stmtDoWhileBody)
|
||||||
let cond = ctx.lowerExpr(stmt.stmtDoWhileCond)
|
let cond = ctx.lowerExpr(stmt.stmtDoWhileCond)
|
||||||
let whileNode = HirNode(kind: hWhile, whileCond: cond, whileBody: body,
|
let whileNode = HirNode(kind: hWhile, whileCond: cond, whileBody: body,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc)
|
||||||
return HirNode(kind: hBlock, blockStmts: @[body, whileNode],
|
return ctx.flushPending(HirNode(kind: hBlock, blockStmts: @[body, whileNode],
|
||||||
blockExpr: nil, typ: makeVoid(), loc: loc)
|
blockExpr: nil, typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skMatch:
|
of skMatch:
|
||||||
let subject = ctx.lowerExpr(stmt.stmtMatchSubject)
|
let subject = ctx.lowerExpr(stmt.stmtMatchSubject)
|
||||||
var arms: seq[HirMatchArm] = @[]
|
var arms: seq[HirMatchArm] = @[]
|
||||||
for arm in stmt.stmtMatchArms:
|
for arm in stmt.stmtMatchArms:
|
||||||
arms.add(HirMatchArm(pattern: arm.pattern, body: ctx.lowerExpr(arm.body)))
|
arms.add(HirMatchArm(pattern: arm.pattern, body: ctx.lowerExpr(arm.body)))
|
||||||
return HirNode(kind: hMatch, matchSubject: subject, matchArms: arms,
|
return ctx.flushPending(HirNode(kind: hMatch, matchSubject: subject, matchArms: arms,
|
||||||
typ: makeVoid(), loc: loc)
|
typ: makeVoid(), loc: loc))
|
||||||
|
|
||||||
of skDecl:
|
of skDecl:
|
||||||
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc),
|
||||||
@@ -642,14 +729,7 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
if ctx.typeSubst.hasKey(p.ptype.typeName):
|
if ctx.typeSubst.hasKey(p.ptype.typeName):
|
||||||
pType = ctx.typeSubst[p.ptype.typeName]
|
pType = ctx.typeSubst[p.ptype.typeName]
|
||||||
else:
|
else:
|
||||||
case p.ptype.typeName
|
pType = ctx.resolveTypeExpr(p.ptype)
|
||||||
of "int", "int32": pType = makeInt()
|
|
||||||
of "int64": pType = makeInt64()
|
|
||||||
of "float64": pType = makeFloat64()
|
|
||||||
of "float32": pType = makeFloat32()
|
|
||||||
of "bool": pType = makeBool()
|
|
||||||
of "Point", "Self": pType = makeNamed(p.ptype.typeName)
|
|
||||||
else: pType = makeNamed(p.ptype.typeName)
|
|
||||||
of tekPointer:
|
of tekPointer:
|
||||||
let pointeeType = ctx.resolveTypeExpr(p.ptype.pointerPointee)
|
let pointeeType = ctx.resolveTypeExpr(p.ptype.pointerPointee)
|
||||||
pType = makePointer(pointeeType)
|
pType = makePointer(pointeeType)
|
||||||
@@ -664,13 +744,7 @@ proc lowerFunc*(ctx: var LowerCtx, decl: Decl): HirFunc =
|
|||||||
if ctx.typeSubst.hasKey(funcReturnType.typeName):
|
if ctx.typeSubst.hasKey(funcReturnType.typeName):
|
||||||
retType = ctx.typeSubst[funcReturnType.typeName]
|
retType = ctx.typeSubst[funcReturnType.typeName]
|
||||||
else:
|
else:
|
||||||
case funcReturnType.typeName
|
retType = ctx.resolveTypeExpr(funcReturnType)
|
||||||
of "int", "int32": retType = makeInt()
|
|
||||||
of "int64": retType = makeInt64()
|
|
||||||
of "float64": retType = makeFloat64()
|
|
||||||
of "float32": retType = makeFloat32()
|
|
||||||
of "bool": retType = makeBool()
|
|
||||||
else: retType = makeNamed(funcReturnType.typeName)
|
|
||||||
of tekPointer:
|
of tekPointer:
|
||||||
let pointeeType = ctx.resolveTypeExpr(funcReturnType.pointerPointee)
|
let pointeeType = ctx.resolveTypeExpr(funcReturnType.pointerPointee)
|
||||||
retType = makePointer(pointeeType)
|
retType = makePointer(pointeeType)
|
||||||
@@ -751,6 +825,8 @@ proc lowerModule*(module: Module, sema: Sema): HirModule =
|
|||||||
result.add(findGenericCalls(expr.exprBinaryRight))
|
result.add(findGenericCalls(expr.exprBinaryRight))
|
||||||
of ekUnary:
|
of ekUnary:
|
||||||
result.add(findGenericCalls(expr.exprUnaryOperand))
|
result.add(findGenericCalls(expr.exprUnaryOperand))
|
||||||
|
of ekTry:
|
||||||
|
result.add(findGenericCalls(expr.exprTryOperand))
|
||||||
of ekAssign:
|
of ekAssign:
|
||||||
result.add(findGenericCalls(expr.exprAssignTarget))
|
result.add(findGenericCalls(expr.exprAssignTarget))
|
||||||
result.add(findGenericCalls(expr.exprAssignValue))
|
result.add(findGenericCalls(expr.exprAssignValue))
|
||||||
|
|||||||
@@ -499,6 +499,9 @@ proc parsePostfix(p: var Parser): Expr =
|
|||||||
discard p.advance()
|
discard p.advance()
|
||||||
let ty = p.parseType()
|
let ty = p.parseType()
|
||||||
left = Expr(kind: ekIs, loc: loc, exprIsOperand: left, exprIsType: ty)
|
left = Expr(kind: ekIs, loc: loc, exprIsOperand: left, exprIsType: ty)
|
||||||
|
of tkQuestion:
|
||||||
|
discard p.advance()
|
||||||
|
left = Expr(kind: ekTry, loc: loc, exprTryOperand: left, exprTryType: nil)
|
||||||
of tkLBrace:
|
of tkLBrace:
|
||||||
if p.structInitAllowed and left.kind in {ekIdent, ekPath}:
|
if p.structInitAllowed and left.kind in {ekIdent, ekPath}:
|
||||||
discard p.advance()
|
discard p.advance()
|
||||||
|
|||||||
@@ -562,6 +562,11 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type =
|
|||||||
of ekIs:
|
of ekIs:
|
||||||
discard sema.checkExpr(expr.exprIsOperand, scope)
|
discard sema.checkExpr(expr.exprIsOperand, scope)
|
||||||
return makeBool()
|
return makeBool()
|
||||||
|
of ekTry:
|
||||||
|
let operandType = sema.checkExpr(expr.exprTryOperand, scope)
|
||||||
|
# For now, assume Result<int, String> -> int
|
||||||
|
# TODO: check operand is Result/Option and current function returns same type
|
||||||
|
return makeInt()
|
||||||
of ekBlock:
|
of ekBlock:
|
||||||
var blockScope = newScope(scope)
|
var blockScope = newScope(scope)
|
||||||
var lastType = makeVoid()
|
var lastType = makeVoid()
|
||||||
|
|||||||
@@ -0,0 +1,79 @@
|
|||||||
|
module Std::Map {
|
||||||
|
|
||||||
|
extern func bux_hash_string(s: String) -> uint;
|
||||||
|
|
||||||
|
struct MapEntry {
|
||||||
|
key: String,
|
||||||
|
value: int,
|
||||||
|
occupied: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Map {
|
||||||
|
entries: *MapEntry,
|
||||||
|
cap: uint,
|
||||||
|
len: uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_New(cap: uint) -> Map {
|
||||||
|
let total: uint = cap * sizeof(MapEntry);
|
||||||
|
let data: *MapEntry = bux_alloc(total) as *MapEntry;
|
||||||
|
var i: uint = 0;
|
||||||
|
while i < cap {
|
||||||
|
data[i].occupied = false;
|
||||||
|
i = i + 1;
|
||||||
|
}
|
||||||
|
return Map { entries: data, cap: cap, len: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_Set(m: *Map, key: String, value: int) {
|
||||||
|
let hash: uint = bux_hash_string(key);
|
||||||
|
var idx: uint = hash % m.cap;
|
||||||
|
while m.entries[idx].occupied {
|
||||||
|
if String_Eq(m.entries[idx].key, key) {
|
||||||
|
m.entries[idx].value = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % m.cap;
|
||||||
|
}
|
||||||
|
m.entries[idx].key = key;
|
||||||
|
m.entries[idx].value = value;
|
||||||
|
m.entries[idx].occupied = true;
|
||||||
|
m.len = m.len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_Get(m: *Map, key: String) -> int {
|
||||||
|
let hash: uint = bux_hash_string(key);
|
||||||
|
var idx: uint = hash % m.cap;
|
||||||
|
while m.entries[idx].occupied {
|
||||||
|
if String_Eq(m.entries[idx].key, key) {
|
||||||
|
return m.entries[idx].value;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % m.cap;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_Has(m: *Map, key: String) -> bool {
|
||||||
|
let hash: uint = bux_hash_string(key);
|
||||||
|
var idx: uint = hash % m.cap;
|
||||||
|
while m.entries[idx].occupied {
|
||||||
|
if String_Eq(m.entries[idx].key, key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
idx = (idx + 1) % m.cap;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_Len(m: *Map) -> uint {
|
||||||
|
return m.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
func Map_Free(m: *Map) {
|
||||||
|
bux_free(m.entries as *void);
|
||||||
|
m.entries = null as *MapEntry;
|
||||||
|
m.cap = 0;
|
||||||
|
m.len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
module Std::String {
|
||||||
|
|
||||||
|
extern func bux_strlen(s: String) -> uint;
|
||||||
|
extern func bux_strcmp(a: String, b: String) -> int;
|
||||||
|
extern func bux_strncmp(a: String, b: String, n: uint) -> int;
|
||||||
|
extern func bux_strcpy(dest: *char8, src: String) -> *char8;
|
||||||
|
extern func bux_strcat(dest: *char8, src: String) -> *char8;
|
||||||
|
extern func bux_strncpy(dest: *char8, src: String, n: uint) -> *char8;
|
||||||
|
|
||||||
|
func String_Len(s: String) -> uint {
|
||||||
|
return bux_strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
func String_Eq(a: String, b: String) -> bool {
|
||||||
|
return bux_strcmp(a, b) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
func String_Concat(a: String, b: String) -> String {
|
||||||
|
let len_a: uint = bux_strlen(a);
|
||||||
|
let len_b: uint = bux_strlen(b);
|
||||||
|
let total: uint = len_a + len_b + 1;
|
||||||
|
let buf: *char8 = bux_alloc(total) as *char8;
|
||||||
|
bux_strcpy(buf, a);
|
||||||
|
bux_strcat(buf, b);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
func String_Copy(s: String) -> String {
|
||||||
|
let len: uint = bux_strlen(s);
|
||||||
|
let buf: *char8 = bux_alloc(len + 1) as *char8;
|
||||||
|
bux_strcpy(buf, s);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
func String_StartsWith(s: String, prefix: String) -> bool {
|
||||||
|
let s_len: uint = bux_strlen(s);
|
||||||
|
let p_len: uint = bux_strlen(prefix);
|
||||||
|
if p_len > s_len {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let r: int = bux_strncmp(s, prefix, p_len);
|
||||||
|
return r == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+2
-2
@@ -21,8 +21,8 @@ void Print(const char* s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* PrintInt - print integer */
|
/* PrintInt - print integer */
|
||||||
void PrintInt(int64_t n) {
|
void PrintInt(int n) {
|
||||||
printf("%lld", (long long)n);
|
printf("%d", n);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PrintFloat - print float */
|
/* PrintFloat - print float */
|
||||||
|
|||||||
@@ -127,3 +127,38 @@ void bux_bounds_check(size_t index, size_t len) {
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* String wrappers with Bux-compatible signatures */
|
||||||
|
unsigned int bux_strlen(const char* s) {
|
||||||
|
return (unsigned int)strlen(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_strcmp(const char* a, const char* b) {
|
||||||
|
return strcmp(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bux_strncmp(const char* a, const char* b, unsigned int n) {
|
||||||
|
return strncmp(a, b, (size_t)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_strcpy(char* dest, const char* src) {
|
||||||
|
return strcpy(dest, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_strcat(char* dest, const char* src) {
|
||||||
|
return strcat(dest, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* bux_strncpy(char* dest, const char* src, unsigned int n) {
|
||||||
|
return strncpy(dest, src, (size_t)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash function (djb2) for string keys */
|
||||||
|
unsigned int bux_hash_string(const char* s) {
|
||||||
|
unsigned int hash = 5381;
|
||||||
|
int c;
|
||||||
|
while ((c = *s++)) {
|
||||||
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user