Skip to content

Releases: chharvey/counterpoint

v0.5.2

Choose a tag to compare

@chharvey chharvey released this 18 Aug 20:21

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

Breaking Changes

  • #112 replace int, nat, and float unary keyword operators with the already-present constructor calls, using Integer, Natural, Float as the base. E.g., int xInteger.(x)

Non-Breaking Changes

External

  • #111 isset and delete for optional variables
    • WARNING: isset will be removed in v0.5.3
  • #113 add new constructor call String.(), which converts its argument to a string
  • add new constructor call Boolean.(), which converts argument to boolean, equivalent to !!arg.

New Interpreter

The old AST.Expression#fold() method is renamed and repurposed into a new interpreter. Unlike a compiler, which emits assembly code to be executed later, the interpreter executes Counterpoint code directly as it analyzes the source code. It uses the same CFG from the compiler, but executes instructions there instead of optimizing or emitting bytecode.

Building an interpreter is a much more manageable task than building a compiler. The compiler hasn’t been cancelled, it just has a lower priority and exists as a longer-term project. The middle- and back- ends of the compiler were the biggest bottleneck to Counterpoint language development, so we decided to move forward and move faster with an interpreter. The interpreter will allow us to speed up design and release new features more quickly so that we can reach Version 1.0.0 sooner. The compiler will still be developed in tandem with the interpreter but any issues that hold it back won’t also delay releases in this project. The compiler is expected to always be in “alpha” mode (unstable and potentially buggy), at least until v1.0.0.

The interpreter is executed on the command line via the new cpli command line tool (analogous to the compiler’s cplc).

Internal

  • use hash function for computing TokenWorth of identifiers
  • clean up constant value analysis
  • update dependencies and engine to Node v26

v0.5.1

Choose a tag to compare

@chharvey chharvey released this 27 Jun 21:54

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

This version focuses solely on naming and documentation. No new features or behavioral changes.

  • rename class and file CLI -> Cli
  • rename type and file {CP => Cpl}Config
  • rename folder validator/{astnode-cp => ast}
    • rename class {AST => Ast}Node and all subclasses {ASTNode => }*, e.g. {ASTNode => }CollectionLiteral
    • merge ASTNodeCP into AstNode and move it into this folder. delete ASTNodeCP after combining
  • rename folders typer/{cp-type => type}, typer/{cp-value => value}
    • rename method .to{CP => Cpl}String()
  • rename folder builder/ -> code-generator/ and class Builder -> CodeGenerator
  • rename folder optimizer/ -> builder/ and class Optimizer -> Builder
  • rename folder builder/ir/ -> builder/op/ and namespace IR -> OP
  • rename methods .lower() -> .build() and interface Lowerable -> Buildable
  • resolve {Reference,Type}Error naming ambiguity
  • docs: rename folder docs/spec/{grammar-cp => grammar}
  • docs: update Identical and Equal algos and move them to types-values.md

v0.5.0

Choose a tag to compare

@chharvey chharvey released this 11 Jun 03:41

In addition to the features mentioned in Milestone v0.5.0, this release contains the following changes:

Breaking Changes

  • minor updates to CLI interface and file extensions.
    • Counterpoint files now use extension .cpls, fully supported by the VSCode extension (counterpoint-lang, unpublished)
    • .cplm & .cplp are future-reserved
    • potentially also: .cplb & .cplx
    • bin file and CLI tool is renamed to cplc
    • Markdown code snippets use id cpl:
      ```cpl
      this is code;
      ```
      
    • see the updated README for details

Constant Folding

  • Constant folding as a compiler option is removed. It was only used as a testing tool, but we’ve updated tests to achieve that better.
  • The method AST.Expression#fold still exists, but has been removed from the type-checking system (with the exception of AST.Constant nodes, whose types are their folded values). We may use it in a future interpreter, where we’ll rename the method to something like interpreterValue.
  • The strict concept of “constant folding” will be integrated into an IR optimization pass.

Non-Breaking Changes

Features

  • codegen strings & templates (leaving stringify functions to intrinsics)
  • codegen statements and blocks

Docs

  • Variables declared with the mut keyword (e.g. val mut x: …) are now referred to as “writable” in documentation. Variables without mut are referred to as “read-only”. Avoid the terms “fixed” and “unfixed”.
  • Types with the mut operator (e.g. mut List.<int>) are referred to as “mutable” and those without are “non-mutable”. “Immutable” types are not a distinction, but all value types are immutable since they cannot change. (A reference type object whose type is “non-mutable” can still theoretically be mutated, which is why we don’t say “immutable”.)

External

  • rename operator isnt -> !is. (344e83a). This operator is not yet supported, so no external change in functionality.

Internal

  • optimize performance with tree-sitter parser
  • tree-sitter fields (improve decorating)
  • new and improved test suite — replacing Mocha with Node.JS’s 'node:test' module

v0.4.4

Choose a tag to compare

@chharvey chharvey released this 04 Apr 02:56

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

Breaking Changes

  • revisits #94. Access on a union type where the union constituents are of different types, or where the accessor type does not exist on all constituents, is no longer allowed. With the exception of nullish constituents.
    claim base: A | B;
    
    base.0; % only allowed if A and B are both tuple types
    base.x; % only allowed if A and B are both record types and both have a non-optional `x` key
    base.[‹expression›]; % only allowed if A and B are both the same dynamic type (List/Dict/Set/Map) and ‹expression›’s type is an allowed type for the accessor
    
    % same rules as above, but relaxed. if A or B is nullish, these are allowed:
    base?.0;
    base?.x;
    base?.[‹expression›];
    
    % additionally, if A and B are both record types and both have an `x` key,
    % but `x` is optional for at least one type, then:
    base?.x;
    % is allowed… even required. (`base.x` would be a TypeError since it is optional in one constituent)
    
    % similarly, for tuples,
    base?.3;
    % is allowed if A and B are tuple types and both have indices including `3`, and that index is optional for at least one type.
    

Non-Breaking Changes

Internal

Completely revamps the build system. AST is built into an IR first, consisting of abstract instructions (later to become a control-flow graph). Then the instructions each emit WASM bytecode. In the future, the IR will have a few optimization passes (e.g., moving the constant folding process from the AST to the IR; dead code elimination; static single-assignment; just to name a few) before moving to the code emission phase. Converting the tree-like AST into a linear IR before emitting target code was a necessary step, and made much easier building compound data structures to WASM (+GC) using Binaryen.

v0.4.3

Choose a tag to compare

@chharvey chharvey released this 18 Feb 02:30

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

Breaking Changes

  • (#108) New Tuple and Record Syntax
  • (#96) remove Void type
  • access syntax must match type:
    • static (tuple and record) access must be dot access: tup.0, tup.1, rec.a, rec.b
    • dynamic (list, dict, set, map) access must be bracket access: list.[0], list.[1], dict.[@a], dict.[@b], 'set'.[elem], map.[ant]
      • note this requires the symbol type (#59)
    • to access a tuple/record dynamically, convert it to a list/dict first, e.g. List.<T0 | T1 | T2>(tuple), Dict.<T_a | T_b | T_c>(record)
  • (#94) for static collections (tuple, record), access kind must match entry optionality. For dynamic collections (list, dict), both . and ?. access are allowed for all entries. Claim access is now disallowed (will be added back in in v0.5+).
    claim tup:  [int, ?: float];
    claim rec:  [b?: float, a: int];
    claim list: int[];
    claim dict: [: int];
    
    % | ok:        | type errors:    | syntax errors:
    tup.0;           tup?.0;           tup!.0;
    tup?.1;          tup.1;            tup!.1;
    rec.a;           rec?.a;           rec!.a;
    rec?.b;          rec.b;            rec!.b;
    list.[0];                          list!.[0];
    list?.[0];
    dict.[@a];                         dict!.[@a];
    dict?.[@a];
    

Type-Sensitive Equality

  • rules for equality (==) are updated. Equality now considers the type of its operands when comparing them, and returns false for operands with different types, with one exception. This change is breaking because, e.g., lists are not equal to tuples even if they have the same items. (e.g., List.<int>([1, 2, 3]) == (1, 2, 3) now returns false when it previously returned true). This change is for the better: a function expecting a List would not accept a Tuple, so it wouldn’t make sense for them to be equal.
  • as a core principle: Equality should imply substitutability within the language’s type system. If a == b, then any context expecting a can accept b without semantic change.
  • there is one very major exception, which is not a breaking change: numbers of different types can still be compared by equality. In this sense, numeric values (integer, float, a future natural type, etc.) that are mathematically equal will still obey the equality operator. This means 42 == 42.0 and -0.0 == 0.0 still return true (and 4 == 2.0 returns false but is still valid). When comparing mixed numeric types by equality, if one of the operands is a float, the int will be implicitly converted to a float and then they will be compared by floating-point value.
    • note that this exception does not abide by the core principle above: if 42 == 42.0, then a context that expects an int should not accept a float, and giving it a float will result in a type-error. This drawback is outweighed by the benefit of == emulating mathematical equality for numeric types.

Non-Breaking Changes

Deprecations

  • ints and floats’ interoperability is changing. See the v0.5 milestone for details. TLDR:
    • int values should not be assigned to float types and vice versa
    • int and float values should not be intermixed in arithmetic operations ^, *, /, +, -
    • ints/floats may still be compared by comparison operators <, >, <=, >=, !<, !> and identity ===, !==, and equality ==, !=, though identity will always return false for different types
    • these rules will be enforced starting in v0.5

External

  • (#59) new Symbol (sym) type: global keys @a, @b, etc.
  • (#94) my_obj?.prop is no longer a TypeError, as long as the type of my_obj is a union with a constituent having a prop property. It produces the value of my_obj.prop if it exists, otherwise null
  • (#96) optional entries are now null instead of having no value
  • (#96) unfixed variables may be uninitialized, but have a default value of null
  • the List, Set, and Map constructors may now take Set arguments (and that includes set literal syntax)
  • the Dict constructor may now take tuple, List, and Set arguments (including literal syntax)
  • the Map constructor may now take a Map argument (including map literal syntax)

Internal

  • optimize static analysis of static (dot) access with integers
  • generalize subtyping algorithm of compound types using variance
  • more consistent access/assignment typing (using read and write types instead of ad-hoc typing based on operator)

Fixes

  • fix merge conflicts with keyword unfixed => var (bca01e3)
  • fix merge conflicts with keyword mutable => mut (47a7efb)
  • fix subtyping issue with types having enumerated values
  • fix parsing issue where reserved keywords were allowed as identifiers (upgraded tree-sitter)

v0.4.2

Choose a tag to compare

@chharvey chharvey released this 18 Feb 01:29

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

Breaking Changes

  • (#93) tuples and records are value types (cannot be mutable)
  • add type constant keywords never and unknown (this is only breaking if you had used these as identifiers) (5eb7521)
  • type obj is no longer a reserved keyword; use Object instead, or manually declare a type alias type obj = Object;. obj may now be used as an arbitrary identifier.
  • variable declaration keywords have changed. use val to declare a (fixed) variable, and val mut to declare an unfixed variable.
    val text1: str = "hello";
    text1 = "world"; %> AssignmentError: Reassignment of fixed variable `text1`.
    val mut text2: str = "hello";
    text2 = "world"; % ok
    

Non-Breaking Changes

  • (#90, #92) value types and reference types

External

Internal

  • restructure error codes
  • normalize type operators — intersections/unions try to convert to the other as needed.
    • Type A & (B | C) will attempt to convert to (A & B) | (A & C) when being assigned a value. A type union in this case is more performant, as only one constituent of the union is sufficient — the value only need be assignable to A & B or A & C, which allows for short-circuiting.
    • Likewise, when a value is of type A | (B & C), it will attempt to convert to (A | B) & (A | C) when being assigned to a target. In this scenario, a type intersection can allow for short-circuiting — only one of the constituents A | B or A | C need be assignable.
    • Inspired by this post.
  • improve method decorators
  • memoize expensive operations
  • upgrade eslint & update rules
  • upgrade TypeScript to 5.8
    • update relative module paths to contain *.ts, e.g. import Foo from './Foo.ts';
    • update Node built-in module paths to start with node:, e.g. import * as assert from 'node:assert';
    • use native ES module imports with import attributes (import foo from './foo.json' with {type: 'json'};)
  • optimize performance & type-checking of unary, equality, and logical operations
  • finish implementation of tuple/record compiling (building)

v0.4.1

Choose a tag to compare

@chharvey chharvey released this 12 Dec 20:20

Warning: This release is not SemVer-compliant! Only the “patch” number is incremented, but it contains the following changes:

Breaking Changes

  • forbid duplicate keys in record literals
  • (#88) strings now use "double-quotes" (U+0022) instead of 'single-quotes' (U+0027)
  • (#88) quoted identifiers now use 'single-quotes' (U+0027) instead of `back-ticks` (U+0060)
  • keyword unfixed changed to var, i.e. let var x: int = 42; (2b95e17)
  • keyword mutable changed to mut, i.e. type T = mut S; (94ef7b7)
  • (#89) _ (a single underscore) is no longer a valid identifier reference

Non-Breaking Changes

External

  • Rename project and language: SolidCounterpoint
  • (#89) blank identifiers (_) can be duplicately assigned
  • (#95) base 6 integer support

Internal

  • improve performance of compiler output for logical operators NOT, AND, and OR

Project Stuff

  • upgrade to npm v8+
  • upgrade to TypeScript v5+
  • update TS target to ES2022
  • add eslint to project
  • use tree-sitter for parser
  • restructure/reorganize file architecture
  • use TS-native decorators
  • enable useDefineForClassFields compiler option and update field initializers

Fixes

  • fix an issue regarding collection literal assignment
  • add some missing tests

v0.4.0

Choose a tag to compare

@chharvey chharvey released this 06 Jun 18:32

In addition to the features mentioned in Milestone v0.4.0, this release contains the following changes:

Non-Breaking Changes

External

  • better error-reporting (multiple errors, better messages, etc.)

Internal

Project Stuff

  • upgrade to npm 7
  • upgrade to ES Modules
  • development tasks no longer depend on gulp
  • dev toggles no longer version-based

Specifics

  • ASTNode constructors no longer take arrays for enumerated properties
  • remove unused build methods from some ASTNode classes

v0.3.0

Choose a tag to compare

@chharvey chharvey released this 11 Apr 04:30

In addition to the features mentioned in Milestone v0.3.0, this release contains the following changes:

Non-Breaking Changes

External

  • add GitHub Issue templates

Internal

  • refactor source files

v0.2.0

Choose a tag to compare

@chharvey chharvey released this 10 Sep 06:51

In addition to the features mentioned in Milestone v0.2.0, this release contains the following changes:

Non-Breaking Changes

External

  • Add compiler feature toggles to enable programming language features. The end user (programmer) has control over these.
    • languageFeatures
      • comments
      • integerRadices
      • numericSeparators
    • compilerOptions
      • constantFolding
      • intCoercion
  • add type-checking and improve constant-folding of expressions
  • improve int coercion for arithmetic operations when constant-folding is turned off
  • optimize exponentiation operation: a ^ b is faster when a is 0 or 1
  • improve documentation & error reporting

Internal

  • Add development flags to internal functionality and tests. Based only on package.json version number. Flags for a version should be removed & fully enabled before that version is released.
  • replace Jest testing suite with Mocha
  • abstract out cli scripting functionality into a CLI class
  • reorganize code structures to match specification: from smallest/simplest to largest/most complex
  • more structured code builder (mechanism that produces assembly code)