Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 56 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,25 @@ jobs:
linux-ccache-${{ runner.os }}-

- name: Restore pinned LLVM toolchain
uses: actions/cache@v6
id: llvm
uses: actions/cache/restore@v6
with:
path: .cache/llvm-toolchain
key: llvm-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('metadata/LLVM_TOOLCHAIN', 'scripts/llvm_toolchain.sh') }}

- name: Install dependencies
run: ./scripts/install.sh

- name: Build pinned LLVM toolchain
run: ./scripts/build_llvm.sh native

- name: Save pinned LLVM toolchain
if: steps.llvm.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: .cache/llvm-toolchain
key: ${{ steps.llvm.outputs.cache-primary-key }}

- name: Build and test
run: ./scripts/test.sh --lint=false

Expand Down Expand Up @@ -86,14 +97,25 @@ jobs:
macos-ccache-${{ runner.os }}-

- name: Restore pinned LLVM toolchain
uses: actions/cache@v6
id: llvm
uses: actions/cache/restore@v6
with:
path: .cache/llvm-toolchain
key: llvm-native-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('metadata/LLVM_TOOLCHAIN', 'scripts/llvm_toolchain.sh') }}

- name: Install dependencies
run: ./scripts/install.sh

- name: Build pinned LLVM toolchain
run: ./scripts/build_llvm.sh native

- name: Save pinned LLVM toolchain
if: steps.llvm.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: .cache/llvm-toolchain
key: ${{ steps.llvm.outputs.cache-primary-key }}

- name: Build and test
run: ./scripts/test.sh --lint=false

Expand All @@ -109,7 +131,8 @@ jobs:
submodules: true

- name: Restore pinned LLVM toolchain
uses: actions/cache@v6
id: llvm
uses: actions/cache/restore@v6
with:
path: |
.cache/llvm-toolchain
Expand All @@ -122,12 +145,26 @@ jobs:
run: ./scripts/install.ps1
shell: pwsh

- name: Build pinned LLVM toolchain
run: ./scripts/build_llvm.sh native
shell: bash

- name: Save pinned LLVM toolchain
if: steps.llvm.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: |
.cache/llvm-toolchain
.cache/llvm-mingw
.cache/vcpkg
.cache/vcpkg-installed
key: ${{ steps.llvm.outputs.cache-primary-key }}

- name: Build and test
run: ./scripts/test.sh --lint=false
shell: bash

web-build-smoke:
if: ${{ vars.DYNLEX_WEB_CI_ENABLED == '1' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v7
Expand All @@ -142,23 +179,26 @@ jobs:
with:
version: 6.0.3

- name: Install pinned WebGPU translator toolchain
working-directory: tools/wgsl-translator
run: |
RUST_VERSION="$(sed -n 's/^channel = "\([^"]*\)"$/\1/p' rust-toolchain.toml)"
RUST_PROFILE="$(sed -n 's/^profile = "\([^"]*\)"$/\1/p' rust-toolchain.toml)"
RUST_TARGET="$(sed -n 's/^targets = \["\([^"]*\)"\]$/\1/p' rust-toolchain.toml)"
test -n "$RUST_VERSION"
test -n "$RUST_PROFILE"
test -n "$RUST_TARGET"
rustup toolchain install "$RUST_VERSION" --profile "$RUST_PROFILE" --target "$RUST_TARGET"
- name: Install dependencies
run: ./scripts/install.sh

- name: Restore pinned LLVM toolchain
uses: actions/cache@v6
id: llvm
uses: actions/cache/restore@v6
with:
path: .cache/llvm-toolchain
key: llvm-web-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('metadata/LLVM_TOOLCHAIN', 'scripts/llvm_toolchain.sh') }}

- name: Build pinned LLVM toolchain
run: ./scripts/build_llvm.sh web

- name: Save pinned LLVM toolchain
if: steps.llvm.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: .cache/llvm-toolchain
key: ${{ steps.llvm.outputs.cache-primary-key }}

- name: Build web compiler and run smoke test
run: ./scripts/test_web_smoke.sh

Expand All @@ -179,4 +219,4 @@ jobs:
run: ./packaging/launchpad/scripts/install-build-deps.sh

- name: Build unsigned Launchpad source package
run: ./packaging/launchpad/scripts/build-source-package.sh --series noble --version 0.1.0+ci${{ github.run_number }}.g${GITHUB_SHA::7}
run: ./packaging/launchpad/scripts/build-source-package.sh --series noble --version "$(cat metadata/VERSION)+ci${{ github.run_number }}.g${GITHUB_SHA::7}"
7 changes: 0 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
name: Release

on:
push:
tags:
- 'v*'
- '*.*.*'
workflow_dispatch:

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
verify-release-ref:
runs-on: ubuntu-24.04
Expand All @@ -26,7 +21,6 @@ jobs:
env:
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
run: bash ./scripts/verify-release-ref.sh

build-linux:
needs: [verify-release-ref]
strategy:
Expand All @@ -50,7 +44,6 @@ jobs:
- uses: actions/checkout@v7
with:
submodules: true

- name: Restore pinned LLVM toolchain
uses: actions/cache@v6
with:
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ else()
endif()
set(CPACK_PACKAGE_NAME "dynlex")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_SOURCE_DIR}/cmake/CPackCalendarVersion.cmake")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "DynLex — a natural-language-like programming language")
set(CPACK_PACKAGE_CONTACT "DynLex maintainers <maintainers@dynlex.org>")
set(CPACK_PACKAGE_VENDOR "DynLex")
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,12 +203,20 @@ when the source passes through the shell.

Compiler/release version source of truth is [`metadata/VERSION`](./metadata/VERSION).

Create and push a release tag with automatic version bump:
Releases use `YEAR.WEEK.REVISION`, with ISO week numbers and a revision starting
at zero each week. Compiler, editor extension and WGSL translator share this
version. Release tags have a `v` prefix. The release clock uses UTC.

Create and push a release tag with the next version for the current week:

```bash
./scripts/release.sh patch # or: minor, major, set X.Y.Z
./scripts/release.sh # or an explicit version: ./scripts/release.sh 2026.45.1
```

Windows MSI stores the year as an offset from 2000 (`26.45.1` for public version
`2026.45.1`) because its first version field is limited to 255. The compiler and
all source package metadata retain the full public version.

## ASan Leak Check (With Third-Party Suppressions)

For sanitizer leak checks that ignore known process-lifetime allocations from
Expand Down
17 changes: 17 additions & 0 deletions cmake/CPackCalendarVersion.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# MSI version fields are 8, 8 and 16 bits. Keep the public calendar version in
# every other package format and encode the MSI year as an offset from 2000.
if(CPACK_GENERATOR STREQUAL "WIX")
if(NOT CPACK_PACKAGE_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
message(FATAL_ERROR "Invalid calendar package version: ${CPACK_PACKAGE_VERSION}")
endif()
math(EXPR CPACK_PACKAGE_VERSION_MAJOR "${CMAKE_MATCH_1} - 2000")
set(CPACK_PACKAGE_VERSION_MINOR "${CMAKE_MATCH_2}")
set(CPACK_PACKAGE_VERSION_PATCH "${CMAKE_MATCH_3}")
if(CPACK_PACKAGE_VERSION_MAJOR LESS 0 OR CPACK_PACKAGE_VERSION_MAJOR GREATER 255
OR CPACK_PACKAGE_VERSION_MINOR LESS 1 OR CPACK_PACKAGE_VERSION_MINOR GREATER 53
OR CPACK_PACKAGE_VERSION_PATCH GREATER 65535)
message(FATAL_ERROR "Calendar package version exceeds MSI fields: ${CPACK_PACKAGE_VERSION}")
endif()
set(CPACK_PACKAGE_VERSION
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
endif()
26 changes: 26 additions & 0 deletions docs/stages.md
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,36 @@ All instantiations of a function have the same operand reordering for each code

We reuse the same strategy (code) for flex functions where possible, keeping it DRY.

Each function body is contained in one source file. Includes at file scope may
join complete definitions; they must not insert statements, case labels or closing
braces into another file's function body.

Node handling, conditional branch merging, overload selection and flex-entry
bookkeeping have separate, non-inlined call boundaries. This keeps their scratch
space out of traversal frames for other expression kinds and recursive callees.
These boundaries preserve the compiler's native stack budget, including the
Windows default stack.

The grouping candidate generator also has a non-inlined entry point. This keeps
coroutine allocation elision from placing its suspended enumeration state in
the recursive inference stack.

Unary arithmetic intrinsics require numeric scalar or vector values. Classes, pointers, arrays, matrices, booleans, and type values are rejected during inference, before code generation. Floating-point vector negation uses the element type to select floating-point instructions.

## Operand Reordering

Binary infix patterns may declare `associativity: left` or `associativity: right`.
This is a syntax-family rule shared by typed overloads and choice spellings;
contradictory declarations are errors. It determines grouping, not algebraic
equivalence or reassociation by an optimizer. The rule applies to directly
connected parent and operand operators: an intervening function starts another
chain. Only binary infix uses of a family participate in its chains; prefix
choice spellings remain capture boundaries. Explicit operand parentheses take
precedence. The standard `+`, `*`, `and` and `or` families group to the left.
Unspecified families retain ambiguity checking. The grouping complexity limit
counts operators with unconstrained relative order, so declared chains can be
long without being classified as ambiguous.

We iterate over all possible operand orders until we find a valid one.

All instantiation types are known when instantiating a function and do not change. We use this fact for reordering.
Expand Down
8 changes: 8 additions & 0 deletions lib/config.dl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ dynlex options:
formatted string: "formatted"
argument pack separator: ","
argument pack conjunction: "and"
associativity:
left: "left"
right: "right"
convert: "convert"
implicitly: "implicitly"
class:
Expand Down Expand Up @@ -45,6 +48,7 @@ dynlex options:
globals: "globals"
before: "before"
after: "after"
associativity: "associativity"
messages:
could not import main file: "couldn't import main file: {path}"
failed to import source file: "failed to import source file: {path}"
Expand Down Expand Up @@ -88,6 +92,10 @@ dynlex options:
type extent invalid: "The requested dimension does not exist on this type"
precedence target not found: "Precedence target not found: {target}"
precedence cycle detected: "Cycle detected in precedence declarations"
invalid associativity: "An associativity declaration requires exactly one direction"
associativity requires binary infix: "Associativity requires a binary infix pattern"
conflicting associativity: "Conflicting associativity declarations in one syntax family"
conflicting associativity related: "The other associativity is declared by this pattern"
invalid pattern parse unmatched bracket: "Unmatched '{character}' in pattern"
invalid pattern parse capture format: "Capture element must have format {name} or {type:name}"
listing section cannot create sections: "you can't create sections in a listing section"
Expand Down
4 changes: 4 additions & 0 deletions lib/std.dl
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ function value shifted right by distance:
return @intrinsic("shift right", value, distance)

function left[ |]+[ |]right:
associativity: left
before: $ or $, $ and $, not $, $ | $, $ ^ $, $ & $, $ < $, $ > $, $ = $, $ != $, $ <= $, $ >= $, $ << $, $ >> $
execute:
return @intrinsic("add", left, right)
Expand All @@ -261,6 +262,7 @@ function left[ |]-[ |]right:
return @intrinsic("subtract", left, right)

function left[ |]*[ |]right:
associativity: left
before: $ + $, $ - $
execute:
return @intrinsic("multiply", left, right)
Expand Down Expand Up @@ -479,6 +481,7 @@ function not value:
@intrinsic("not", value)

function {boolean:left} and {boolean:right}:
associativity: left
before: $ or $
replacement:
@intrinsic("and", left, right)
Expand All @@ -490,6 +493,7 @@ flex function {nothing:left}[ and|.] {nothing:right}:
@intrinsic("discard", right)

function left or right:
associativity: left
before: default
replacement:
@intrinsic("or", left, right)
Expand Down
2 changes: 1 addition & 1 deletion metadata/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.3
2026.45.0
6 changes: 2 additions & 4 deletions scripts/llvm_toolchain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -411,21 +411,19 @@ dynlex_build_lld() {
dynlex_resolve_bootstrap_compilers
dynlex_require_command ninja
local cmake_command=(cmake)
local compiler_arguments=()
if [ "$profile" = web ]; then
dynlex_require_command emcmake
cmake_command=(emcmake cmake)
else
compiler_arguments=(
cmake_command+=(
"-DCMAKE_C_COMPILER=$DYNLEX_LLVM_BOOTSTRAP_CC"
"-DCMAKE_CXX_COMPILER=$DYNLEX_LLVM_BOOTSTRAP_CXX"
)
while IFS= read -r argument; do
compiler_arguments+=("$argument")
cmake_command+=("$argument")
done < <(dynlex_native_llvm_cmake_arguments)
fi
"${cmake_command[@]}" -S "$DYNLEX_LLVM_SOURCE_DIR/lld" -B "$build_directory" -G Ninja \
"${compiler_arguments[@]}" \
-DCMAKE_BUILD_TYPE=Release \
"-DCMAKE_INSTALL_PREFIX=$install_directory" \
-DCMAKE_INSTALL_LIBDIR=lib \
Expand Down
Loading
Loading