Skip to content

Latest commit

 

History

History
114 lines (87 loc) · 7.01 KB

File metadata and controls

114 lines (87 loc) · 7.01 KB

Voyd Programming Language

This repository contains the implementation of the voyd programming language. voyd is a level between rust and typescript in terms of abstraction. It compiles to webassembly.

Directory Index

  • apps/cli: voyd / vt command line entrypoints.
  • apps/smoke: End-to-end smoke tests (prefer adding public API tests here).
  • apps/site: voyd.dev docs/playground site.
  • apps/vscode: VSCode extension and language client wiring.
  • packages/compiler: Parser, semantics, and Wasm codegen pipeline.
  • packages/language-server: LSP server built on compiler + std.
  • packages/sdk: Public Node/browser/deno APIs for compile/run/test flows.
  • packages/lib: Shared runtime/tooling utilities (CLI helpers, binaryen helpers).
  • packages/vx-dom: Browser/server VX DOM renderer and hydration APIs.
  • packages/js-host: JS host runtime used for executing compiled modules.
  • packages/std: Standard library source bundle (Voyd source).
  • packages/reference: Language reference source/build scripts.
  • docs/architecture: Design constraints and cross-module contracts.

Architecture Overview

  • Monorepo layout: product surfaces live in apps/*, reusable language/runtime components live in packages/*.
  • Compilation flow (authoritative path): parser -> semantics (typing/binding/lowering) -> ProgramCodegenView -> Wasm codegen.
  • Boundary rule: packages/compiler/src/semantics/codegen-view is the contract; codegen should consume this view, not typing internals.
  • Runtime split: compiler emits Wasm, while @voyd-lang/js-host + @voyd-lang/lib provide JS-side execution and interop helpers.
  • Public integration point: @voyd-lang/sdk composes compiler + host + std and is the preferred API for tests and external tooling.
  • Developer tooling stack: CLI (apps/cli), language server (packages/language-server), and VSCode extension (apps/vscode) all build on shared packages.

Guide

  • Always build with long term maintainability in mind. Avoid short term hacks.
  • If you encounter code or an architecture that could benefit from a refactor, report on it and suggest direction in your final response.
  • When diagnosing bugs, prefer implementation-level root-cause fixes over call-site/type-annotation workarounds unless a workaround is explicitly requested.
  • Always be mindful about clear code boundaries and contracts. Avoid introducing any unnecessary coupling.
  • When you encounter a bug or important missing feature that is likely to have a real impact on users and is unrelated to your current task, file a ticket in linear tagged with codex.

Required Pre-Reads

  • Before contributing to packages/compiler or packages/std, read docs/agent-voyd-quick-reference.md. This is required for compiler/std work because those areas often need precise, idiomatic Voyd syntax in std sources, fixtures, smoke tests, and reference examples.

Debugging

A cli is available after npm link

Helpful commands:

  • vt --emit-parser-ast <path-to-voyd-file>
  • vt --run <path-to-voyd-file> // runs the pub fn main of the file
  • vt --emit-wasm-text --opt <path-to-voyd-file> // Careful, this can be large

Testing

  • npm test (runs vitest suite). Always confirm this passes before finishing.
  • npm run typecheck.
  • npx vitest <path-to-test>

You should generally add unit tests when they protect new behavior or a regression. Do not add tests just to mirror coverage that already exists at another layer.

E2E Unit tests should go in apps/smoke (unless strictly scoped to the compiler). Always prefer the public API

Test Placement Guidance

Before adding tests, read docs/testing/test-layer-ownership.md.

  • packages/compiler: parser/typing/lowering/codegen internals and compiler-only contracts.
  • packages/sdk: public compile/run/test APIs and runtime adapter contracts.
  • apps/cli: argument parsing, command wiring, process UX, and exit/reporting behavior.
  • apps/smoke: end-to-end user-visible flows via public APIs (cross-package integration).
  • Avoid duplicating semantic assertions across CLI/SDK/smoke unless it protects a boundary; if duplicated, document the boundary and keep exactly one canonical layer.

Test Hygiene

  • Prefer batching expensive compile/run setup: one Voyd fixture with several focused entrypoints is usually better than several tiny fixtures that each compile the std/runtime stack.
  • Keep compile-heavy smoke tests at the highest useful boundary only. If compiler or std unit tests already own the semantics, smoke should assert a narrow public integration signal.
  • Before adding a new .test.ts smoke file or .test.voyd module, check whether an existing fixture can host the scenario without mixing unrelated ownership.
  • Avoid brute-force sweeps in the default suite. Use representative edge cases in normal tests, and put benchmark/perf loops behind an explicit perf script or keep them intentionally small.
  • When adding or expanding a slow test, note the boundary it protects and the expected runtime cost in the PR or final handoff.

TS Style Guide

  • Keep functions small
  • Prefer early returns to else ifs
  • Use const whenever possible
  • Use ternary conditionals for conditional variable init
  • Prefer functional control flow (map, filter, etc) to imperative loop constructs.
  • Files should be ordered by importance. The main export of a file at the top.
  • Use a single parameter object for functions containing more than three params to name the parameters on call.
  • Avoid reaching across module boundaries.

Voyd Style Guide

Guide for writing voyd code and APIs. Voyd APIs should share a similar spirit to Swift API Design Guidelines

  • Snake case for functions, variables, and effect ops. UpperCamelCase for types and effects
  • Treat labels as required when a function has more than two non-self parameters, unless there is a clear readability reason not to.
  • For two non-self parameters, prefer labels when they improve clarity or prevent ambiguity.
  • Make use of function / method overloading when it makes semantic sense.
  • Prefer positional parameters when a label does not add clarity (for example push(value: x) should be push(x)).
  • Avoid labels that only repeat the function name or obvious role (contains_key({ key }) should prefer contains(key)).
  • Use labels to distinguish overload families when argument types overlap (contains(key: ...), contains(value: ...), contains(where: ...)).
  • Prefer semantic base function names over type-encoded names. Use labels to describe input role/source instead of suffixes like _bytes when practical (for example ascii_string_from(bytes: source)).
  • Any public/api function that accepts StringSlice should also provide a String overload with equivalent behavior. Implement one overload as a thin forwarder to avoid duplicated logic.
  • Use stable effect ids with dotted capability naming: @effect(id: "<owner>.<package>.<capability>") (for example voyd.std.fs). IDs are API contracts: do not change them for refactors.