Skip to content

Add C-style types, new language features, standard library tools and CI - #2

Merged
csubbdev merged 4 commits into
mainfrom
claude/bugs-language-improvements-3tbrkx
Nov 2, 2026
Merged

csubbdev merged 4 commits into
mainfrom
claude/bugs-language-improvements-3tbrkx

Conversation

@csubbdev

@csubbdev csubbdev commented Nov 2, 2026 •

Copy link
Copy Markdown
Owner

Summary

Adds optional C-style types to Ascend, a batch of language upgrades and new builtins, VS Code syntax highlighting, and a GitHub Actions build.

C-style types

u8 age = 30
f32 ratio = 0.5
char grade = 'A'
string? nickname          // ? allows null; starts as null
i32 count                 // starts at 0
const f64 RATE = 0.07

i32 add(i32 a, i32 b = 1) {
    return a + b
}

void log(string message) {
    print("log:", message)
}
  • Types: i8 i16 i32 i64 u8 u16 u32 u64 byte int f32 f64 float bool char string list map func any, void for return types, and nullable T?.
  • Every number carries its type. Arithmetic stays in that type, literals adapt to the other operand, mixed types widen to a type that holds both, and overflow is an error instead of a silent wrap.
  • Typed variables, parameters and return values are checked. Literal mismatches like u8 x = 300 are reported before the program runs.
  • Untyped code still works (x := 5, func f(a, b) { }). The name: type form is rejected with a hint showing the C-style spelling.

Language

  • const, match, f-strings with format specs (f"{price:>8.2f}"), a char type, Unicode-aware strings
  • Hex/binary/octal literals, type suffixes (255u8), bitwise operators & | ^ ~ << >>
  • Modules via import("file"); errors inside a module point at that module's file

Tools

  • 124 builtins, including file I/O, JSON, format, string/list/map helpers, math, time and system functions
  • ascend --check file.asc finds errors without running
  • VS Code syntax highlighting in editors/vscode, checked with VS Code's TextMate engine

CI

.github/workflows/build.yml builds and tests on Linux (GCC and Clang), macOS and Windows (MSVC), runs the tests under ASan and UBSan, and uploads the built interpreters as artifacts.

Behavior changes

These are listed in CHANGELOG.md:

  • Single quotes now make a char, so strings need double quotes.
  • & and | are now bitwise operators; use and/or for logic.
  • Indexing a string gives a char. s[0] == "a" still works.
  • String lengths and indexes count characters, not bytes.

Windows fix found by CI

The first Windows run crashed in the deep-recursion test. Two commits fix it:

  • The stack guard now measures the real stack on every platform (GetCurrentThreadStackLimits on Windows) and also protects the parser.
  • Errors no longer get caught and rethrown at every evaluation level to attach their location. With MSVC, each rethrow runs on top of the frames still being unwound, so thousands of them overflowed the stack. The location is now recorded in a destructor and applied once. Error messages and locations are unchanged.

The test runner now prints each test's name before running it, so a future crash shows which test caused it.

Testing

  • CI passes on all five jobs: Linux (GCC), Linux (Clang), macOS, Windows (MSVC), and the ASan + UBSan run.
  • 114 unit tests plus all 13 example programs run through ctest, with no compiler warnings on GCC or Clang.

🤖 Generated with Claude Code

https://claude.ai/code/session_014xWEy3pVr7ofMaomGRbiuo

Types
- Optional type annotations: `age: u8 = 30`, `name: string?`, and
  `count: i32` (starts at the zero value). Types: i8 i16 i32 i64 u8 u16
  u32 u64 byte int f32 f64 float bool char string list map func any, plus
  `void` as a return type and `T?` for nullable types.
- Every number carries its type. Arithmetic keeps the type, literals adapt
  to the other operand, mixed types widen to a type holding both, and
  overflow is an error instead of a silent wrap. f32 keeps 32-bit precision.
- Typed parameters and return types (`func f(a: i32) -> f64`), `const`,
  and conversion functions named after each type (`u8(x)`, `f32(x)`, ...).
- Literal type mismatches (`x: u8 = 300`) and wrong return values are
  reported before the program runs.

Language
- `char` type and literals ('a', '\u{1F600}'); single quotes now mean a char.
- Strings are Unicode: len, indexing, slicing and loops count characters.
- f-strings with format specs (f"{price:>8.2f}") and format().
- match statements.
- Hex/binary/octal literals, type suffixes (255u8), and bitwise operators
  & | ^ ~ << >> with compound assignments.
- Modules via import("file"), with errors reported in the module's file.

Tools
- 124 builtins, including file I/O, JSON, string/list/map helpers, math,
  time and system functions.
- `ascend --check file.asc` finds errors without running.
- VS Code syntax highlighting in editors/vscode, checked with the real
  TextMate engine.

Also adds 34 tests (110 total), six examples, a CHANGELOG listing the
behavior changes, and updated docs. Builds without warnings on GCC and
Clang; tests and examples are clean under ASan, UBSan and LSan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014xWEy3pVr7ofMaomGRbiuo
Types are now written before the name, like in C:

    u8 age = 30
    string? nickname
    const f64 RATE = 0.07
    i32 add(i32 a, i32 b = 1) { return a + b }
    void log(string message) { print(message) }

Typed parameters are `type name`, and a typed function can put its return
type in front of its name. Untyped code is unchanged (`x := 5`,
`func f(a, b) { }`), anonymous functions can still use `-> type`, and
`func handler = print` declares a variable holding a function. The old
`name: type` form is rejected with a hint showing the C-style spelling.

CI: .github/workflows/build.yml builds and tests on Linux (GCC and Clang),
macOS and Windows (MSVC), runs the tests under ASan and UBSan, and uploads
the built interpreters as artifacts. MSVC now compiles with /utf-8, since
the tests contain UTF-8 text.

Tests, examples, docs, the changelog and the VS Code grammar are updated
for the new syntax (114 tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014xWEy3pVr7ofMaomGRbiuo
The Windows (MSVC) CI job crashed partway through the unit tests. On
Windows the recursion guard assumed an 8 MB stack instead of measuring
it, and the parser limited nesting only by a level count, so a smaller
stack or larger MSVC stack frames could overflow before either check
fired.

Add StackLimit, which reads the current thread's actual stack bounds
(GetCurrentThreadStackLimits on Windows, the stack rlimit elsewhere) and
keeps a safety margin, and use it in both the interpreter and the parser.
Runaway recursion and deeply nested code are now reported as errors at
any stack size; locally the test suite no longer crashes even with a
256 KB stack.

The test runner now prints the available stack and each test's name
before running it, so a crash in CI shows which test caused it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014xWEy3pVr7ofMaomGRbiuo
The Windows (MSVC) unit tests still crashed, in
interp_recursion_limit_is_an_error_not_a_crash, with the full 8 MB stack
available. The stack guard did fire, but each eval/exec level then caught
the error to attach a source location and rethrew it. With MSVC, a catch
block runs on top of the frames being unwound, so every rethrow starts a
new exception dispatch deeper on the stack. Thousands of nested rethrows
from deep recursion overflowed it. GCC and Clang unwind differently, so
Linux and macOS passed.

Record the location in a destructor instead (ErrorSite): creating a
RuntimeError sets a flag, and the innermost expression or statement being
unwound notes its position and file. The location is applied once at the
top level, and `try` clears it. Error messages and locations are
unchanged; all error-location tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014xWEy3pVr7ofMaomGRbiuo
@csubbdev
csubbdev merged commit 6a53bd2 into main Nov 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants