Releases: chharvey/counterpoint
Release list
v0.5.2
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, andfloatunary keyword operators with the already-present constructor calls, usingInteger,Natural,Floatas the base. E.g.,int x→Integer.(x)
Non-Breaking Changes
External
- #111
issetanddeletefor optional variables- WARNING:
issetwill be removed in v0.5.3
- WARNING:
- #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
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}Nodeand all subclasses{ASTNode => }*, e.g.{ASTNode => }CollectionLiteral - merge
ASTNodeCPintoAstNodeand move it into this folder. deleteASTNodeCPafter combining
- rename class
- rename folders
typer/{cp-type => type},typer/{cp-value => value}- rename method
.to{CP => Cpl}String()
- rename method
- rename folder
builder/->code-generator/and classBuilder->CodeGenerator - rename folder
optimizer/->builder/and classOptimizer->Builder - rename folder
builder/ir/->builder/op/and namespaceIR->OP - rename methods
.lower()->.build()and interfaceLowerable->Buildable - resolve
{Reference,Type}Errornaming 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
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&.cplpare 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
- Counterpoint files now use extension
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#foldstill exists, but has been removed from the type-checking system (with the exception ofAST.Constantnodes, whose types are their folded values). We may use it in a future interpreter, where we’ll rename the method to something likeinterpreterValue. - 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
mutkeyword (e.g.val mut x: …) are now referred to as “writable” in documentation. Variables withoutmutare referred to as “read-only”. Avoid the terms “fixed” and “unfixed”. - Types with the
mutoperator (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
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
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
symboltype (#59)
- note this requires the
- 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)
- static (tuple and record) access must be dot access:
- (#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 expectingacan acceptbwithout 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.0and-0.0 == 0.0still return true (and4 == 2.0returns 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.
- note that this exception does not abide by the core principle above: if
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?.propis no longer a TypeError, as long as the type ofmy_objis a union with a constituent having apropproperty. It produces the value ofmy_obj.propif it exists, otherwisenull - (#96) optional entries are now
nullinstead 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
v0.4.2
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
neverandunknown(this is only breaking if you had used these as identifiers) (5eb7521) - type
objis no longer a reserved keyword; useObjectinstead, or manually declare a type aliastype obj = Object;.objmay now be used as an arbitrary identifier. - variable declaration keywords have changed. use
valto declare a (fixed) variable, andval mutto 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
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 toA & BorA & 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 constituentsA | BorA | Cneed be assignable. - Inspired by this post.
- Type
- 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'};)
- update relative module paths to contain
- optimize performance & type-checking of unary, equality, and logical operations
- finish implementation of tuple/record compiling (building)
v0.4.1
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
unfixedchanged tovar, i.e.let var x: int = 42;(2b95e17) - keyword
mutablechanged tomut, 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: Solid → Counterpoint
- (#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-sitterfor parser - restructure/reorganize file architecture
- use TS-native decorators
- enable
useDefineForClassFieldscompiler option and update field initializers
Fixes
- fix an issue regarding collection literal assignment
- add some missing tests
v0.4.0
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
buildmethods from some ASTNode classes
v0.3.0
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
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
- languageFeatures
- 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 ^ bis faster whenais0or1 - improve documentation & error reporting
Internal
- Add development flags to internal functionality and tests. Based only on
package.jsonversion 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
CLIclass - reorganize code structures to match specification: from smallest/simplest to largest/most complex
- more structured code builder (mechanism that produces assembly code)