Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements for v0.1 #46

Merged
merged 37 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
2227d89
improved 'nil' for Class variables
JohannesMeierSE Dec 6, 2024
1d947b1
fixed and refactored type inference
JohannesMeierSE Dec 6, 2024
183ebb4
fixed comments in LOX
JohannesMeierSE Dec 8, 2024
35630c4
refactoring: turned existing functions into methods, this enables to …
JohannesMeierSE Dec 8, 2024
0540f3f
functions and operators can be annotated with validations now, used f…
JohannesMeierSE Dec 8, 2024
cb8e78e
unified the notation of comments in LOX, added TODO
JohannesMeierSE Dec 8, 2024
f0c940b
refactoring of the Typir-Langium module: makes the customization easi…
JohannesMeierSE Dec 8, 2024
e94a696
removed all dependencies to 'langium/lsp' from 'typir-langium' in ord…
JohannesMeierSE Dec 8, 2024
00bac47
use service/interface with implementation/class design for the integr…
JohannesMeierSE Dec 8, 2024
be5e1b7
started to make TypeScript types from the application domain explicit…
JohannesMeierSE Dec 8, 2024
92015f2
switched to Langium v3.3, simplified API, removed duplicated code for…
JohannesMeierSE Dec 8, 2024
adff6b9
improved testing infrastructure, unified test cases for OX and LOX
JohannesMeierSE Dec 12, 2024
dce7cdc
made the TypeSelector definition more explicit, the additional featur…
JohannesMeierSE Dec 12, 2024
c6db382
renamed factory service for operators according to the type factory s…
JohannesMeierSE Dec 14, 2024
d727fe1
improved comments
JohannesMeierSE Dec 14, 2024
7edbcb1
refactoring: extracted logic to resolve TypeSelectors into its own se…
JohannesMeierSE Dec 14, 2024
7db8bc7
refactoring for LOX and OX: don't merge Langium+Typir services, but p…
JohannesMeierSE Dec 14, 2024
4f7daf0
Service names start with an upper-case letter, groups of services wit…
JohannesMeierSE Dec 16, 2024
c72419b
reworked the grouping of Typir services
JohannesMeierSE Dec 16, 2024
efb1904
removed old type checking approach without Typir
JohannesMeierSE Dec 16, 2024
d6eda7a
removed Lambda examples from LOX
JohannesMeierSE Dec 16, 2024
0d684f4
small fixes
JohannesMeierSE Dec 16, 2024
9b11d3f
added missing READMEs, ChangeLog, added content, added EclipseCon slides
JohannesMeierSE Dec 16, 2024
2c18a7e
new feature: associate domain elements with Typir types
JohannesMeierSE Dec 17, 2024
e66d2ec
removed some more unused methods (to be consistent with the other pre…
JohannesMeierSE Dec 17, 2024
12907db
use Typir already during Linking! (this fixed compiler issues after r…
JohannesMeierSE Dec 17, 2024
e967cbc
refactoring: moved existing test cases for methods of classes into it…
JohannesMeierSE Dec 17, 2024
75537f0
implemented overloaded functions and methods in LOX, more test cases,…
JohannesMeierSE Dec 17, 2024
29c3f1d
'signature' vs 'signatures' properties in the operator API
JohannesMeierSE Dec 17, 2024
66016b7
renaming, improved comments
JohannesMeierSE Dec 17, 2024
2d09d66
refactoring: make the default options of kinds configurable
JohannesMeierSE Dec 17, 2024
479be87
refactoring: just turned functions into methods to enable their custo…
JohannesMeierSE Dec 17, 2024
36a6b8c
improved comments
JohannesMeierSE Dec 17, 2024
4022e05
the module now looks into the kind registry, whether the kind is alre…
JohannesMeierSE Dec 17, 2024
3ff6109
reduced code for the "Tiny Typir" example
JohannesMeierSE Dec 17, 2024
33c454b
forgotten change
JohannesMeierSE Dec 17, 2024
a153b27
improvements according to the review
JohannesMeierSE Dec 19, 2024
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
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Typir Change Log

We roughly follow the ideas of [semantic versioning](https://semver.org/).
Note that the versions "0.x.0" probably will include breaking changes.


## v0.1.0 (December 2024)

This is the first official release of Typir.
It serves as first version to experiment with Typir and to gather feedback to guide and improve the upcoming versions. We are looking forward to your feedback!

- [Linked issues and PRs](https://github.com/TypeFox/typir/milestone/2)
- Core implementations of the following [type-checking services](/packages/typir/src/services/):
- Assignability
- Equality
- Conversion (implicit/coercion and explicit/casting)
- Type inference
- Sub-typing
- Validation
- Caching
- [Predefined types](/packages/typir/src/kinds/) to reuse:
- Primitives
- Functions (with overloading)
- Classes (nominally typed)
- Top, bottom
- (some more are under development)
- Operators (which are mapped to Functions, with overloading)
- Application examples:
- LOX (without lambdas)
- OX
17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
# Contributing

TODO


## Communication

The following communication channels are available:

- [GitHub issues](https://github.com/TypeFox/typir/issues) - for bug reports, feature requests, etc.
- [GitHub discussions](https://github.com/TypeFox/typir/discussions) - for questions, ideas, announcements, etc.
- [Weekly Langium dev meeting](https://github.com/eclipse-langium/langium/discussions/564?sort=new) - While Typir is independent from Langium in general, you might meet some Typir developers at the Langium dev meetings.

In case you have a question, please look into the provided resources and documentations first.
If you don't find any answer there, feel free to use the discussions to get help.


## Release Process

The release process for Typir is described in [RELEASE.md](./RELEASE.md).
78 changes: 74 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,90 @@
# Typir

Engineering types for software languages in the web.
<div id="badges" align="center">

[![npm](https://img.shields.io/npm/v/typir)](https://www.npmjs.com/package/typir)
[![Build](https://github.com/TypeFox/typir/actions/workflows/actions.yml/badge.svg)](https://github.com/TypeFox/typir/actions/workflows/actions.yml)
[![Github Discussions](https://img.shields.io/badge/github-discussions-blue?logo=github)](https://github.com/TypeFox/typir/discussions)
[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-ready--to--code-FFAE33?logo=gitpod)](https://gitpod.io/#https://github.com/TypeFox/typir)

</div>

---

Typir is a library for type systems and type checking for software languages in the web.

Typir is OpenSource, written in TypeScript, and follows pragmatic approaches for easing type checking in practical language engineering projects by providing default implementations for recurring problems.
As a stand-alone library, Typir provides a TypeScript-API for language engineers without an additional, external DSL for formalizing types.


## Core Features

Typir provides these core features:

- Predefined types:
- primitives
- functions (with overloading)
- classes
- top, bottom
- (more are planned)
- Solutions for: circular type definitions, caching, operators
- Meaningful and customizable error messages
- The provided default implementations are customizable by dependency injection

Typir does intentionally _not_ include ...

- rules engines and constraint solving
- formal proofs
- external DSLs for formalizing types


## NPM workspace

This repository is a NPM workspace. It contains the following packages:

- [Typir](./packages/typir/README.md) - the core package of Typir
- [Typir-Langium](./packages/typir-langium/README.md) - a integration of Typir for [Langium](https://github.com/eclipse-langium/langium)
- [Typir](./packages/typir/README.md) - the core package of Typir with default implementations for type checking services and some predefined types
- [Typir-Langium](./packages/typir-langium/README.md) - a binding of Typir for [Langium](https://github.com/eclipse-langium/langium), a language workbench for developing textual DSLs in the web,
in order to ease type checking for Langium-based languages

This repository contains the following stand-alone applications, which demonstrate how to use Typir for type checking:

- [LOX](./examples/lox/README.md) - static type checking for LOX, implemented with Typir-Langium
- [OX](./examples/ox/README.md) - a reduced version of LOX, implemented with Typir-Langium


## Tiny Typir Example

[TODO](/packages/typir/test/api-example.test.ts)


## Resources

Typir is presented in these talks:

- [LangDev'24](https://langdevcon.org/2024/program#26): [Video](https://www.youtube.com/watch?v=CL8EbJYeyTE), [slides](/resources/talks/2024-10-17-LangDev.pdf) (2024-10-17)
- [OCX/EclipseCon'24](https://www.ocxconf.org/event/778b82cc-6834-48a4-a58e-f883c5a7b8c9/agenda?session=23b97df9-0435-4fab-8a01-e0a9cf3e3831&shareLink=true): [Video](https://www.youtube.com/watch?v=WLzXAhcl-aY&list=PLy7t4z5SYNaRRGVdF83feN-_uHLwvGvgw&index=23), [slides](/resources/talks/2024-10-24-EclipseCon.pdf) (2024-10-24)


## Roadmap

The roadmap of Typir is organized with [milestones in GitHub](https://github.com/TypeFox/typir/milestones).

The roadmap include, among other, these features:

- More predefined types: structurally typed classes, lambdas, generics, constrained primitive types (e.g. numbers with upper and lower bound), ...
- Calculate types, e.g. operators whose return types depend on their current input types
- Optimized APIs to register rules for inference and validation

For the released versions of Typir, see the [CHANGELOG.md](/CHANGELOG.md).


## Contributing

Please read the [CONTRIBUTING.md](./CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us.

We also have a release process described in [RELEASE.md](./RELEASE.md).


## License

[MIT License](/LICENSE)
Typir is fully [MIT licensed](/LICENSE).
17 changes: 17 additions & 0 deletions examples/lox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Typir applied to LOX

This package contains an adapted version of [LOX](https://craftinginterpreters.com/the-lox-language.html), [realized with Langium](https://github.com/TypeFox/langium-lox) and statically type-checked with [Typir](https://typir.org/).

Typir is used here to make LOX a statically typed language:

- Variables have one type, which is either explicitly declared (e.g. `var v1: string`) or derived from the initial value (e.g. `var v2: 2 <= 3`).
- Lox supports these types here:
- primitives: boolean, string, number, void
- Classes (nominally typed)
- Lambdas (not yet supported)
- We keep `nil`, but it can be assigned only to variables with a class or lambda as type.
Variables with primitive type and without explicit initial value have the primitive types default value.

For examples written in LOX, look at some [collected examples](./examples/) or the [test cases](./test/).

To compare the current implementation for type checking with Typir with an implementation without Typir, have a look into [this repository](https://github.com/TypeFox/langium-lox/tree/main/langium/src/language-server/type-system).
28 changes: 0 additions & 28 deletions examples/lox/examples/basic.lox
Original file line number Diff line number Diff line change
Expand Up @@ -88,34 +88,6 @@ fun returnSum(a: number, b: number): number {
return a + b;
}

// Closures

fun identity(a: (number, number) => number): (number, number) => number {
return a;
}

print identity(returnSum)(1, 2); // prints "3";

fun outerFunction(): void {
fun localFunction(): void {
print "I'm local!";
}
localFunction();
}

fun returnFunction(): () => void {
var outside = "outside";

fun inner(): void {
print outside;
}

return inner;
}

var fn = returnFunction();
fn();

// Classes WIP

class SuperClass {
Expand Down
6 changes: 3 additions & 3 deletions examples/lox/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
},
"dependencies": {
"commander": "~12.1.0",
"langium": "~3.2.0",
"langium": "~3.3.0",
"typir-langium": "~0.0.2",
"vscode-languageclient": "~9.0.1",
"vscode-languageserver": "~9.0.1"
},
"devDependencies": {
"@types/vscode": "~1.94.0",
"langium-cli": "~3.2.0"
"langium-cli": "~3.3.0"
},
"files": [
"bin",
Expand Down Expand Up @@ -75,6 +75,6 @@
],
"main": "./out/extension/main.cjs",
"bin": {
"ox-cli": "out/cli/main.js"
"lox-cli": "out/cli/main.js"
}
}
63 changes: 63 additions & 0 deletions examples/lox/src/language/lox-linker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/******************************************************************************
* Copyright 2024 TypeFox GmbH
* This program and the accompanying materials are made available under the
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { AstNodeDescription, DefaultLinker, LinkingError, ReferenceInfo } from 'langium';
import { isType } from '../../../../packages/typir/lib/graph/type-node.js';
import { TypirServices } from '../../../../packages/typir/lib/typir.js';
import { isClass, isFunctionDeclaration, isMemberCall, isMethodMember } from './generated/ast.js';
import { LoxServices } from './lox-module.js';

export class LoxLinker extends DefaultLinker {
protected readonly typir: TypirServices;

constructor(services: LoxServices) {
super(services);
this.typir = services.typir;
}

override getCandidate(refInfo: ReferenceInfo): AstNodeDescription | LinkingError {
const container = refInfo.container;
if (isMemberCall(container) && container.explicitOperationCall) {
// handle overloaded functions/methods
const scope = this.scopeProvider.getScope(refInfo);
const calledDescriptions = scope.getAllElements().filter(d => d.name === refInfo.reference.$refText).toArray(); // same name
if (calledDescriptions.length === 1) {
return calledDescriptions[0]; // no overloaded functions/methods
} if (calledDescriptions.length >= 2) {
// in case of overloaded functions/methods, do type inference for given arguments
const argumentTypes = container.arguments.map(arg => this.typir.Inference.inferType(arg)).filter(isType);
if (argumentTypes.length === container.arguments.length) { // for all given arguments, a type is inferred
for (const calledDescription of calledDescriptions) {
const called = this.loadAstNode(calledDescription);
if (isClass(called)) {
// special case: call of the constructur, without any arguments/parameters
return calledDescription; // there is only one constructor without any parameters
}
if ((isMethodMember(called) || isFunctionDeclaration(called)) && called.parameters.length === container.arguments.length) { // same number of arguments
// infer expected types of parameters
const parameterTypes = called.parameters.map(p => this.typir.Inference.inferType(p)).filter(isType);
if (parameterTypes.length === called.parameters.length) { // for all parameters, a type is inferred
if (argumentTypes.every((arg, index) => this.typir.Assignability.isAssignable(arg, parameterTypes[index]))) {
return calledDescription;
}
}
}
}
}
// no matching method is found, return the first found method => linking works + validation issues regarding the wrong parameter values can be shown!
return calledDescriptions[0];

// the following approach does not work, since the container's cross-references are required for type inference, but they are not yet resolved
// const type = this.typir.Inference.inferType(container);
// if (isFunctionType(type)) {
// return type.associatedDomainElement;
// }
}
return this.createLinkingError(refInfo);
}
return super.getCandidate(refInfo);
}
}
45 changes: 25 additions & 20 deletions examples/lox/src/language/lox-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,53 @@
* terms of the MIT License, which is available in the project root.
******************************************************************************/

import { Module, PartialLangiumCoreServices, createDefaultCoreModule, inject } from 'langium';
import { LangiumSharedCoreServices, Module, PartialLangiumCoreServices, createDefaultCoreModule, inject } from 'langium';
import { DefaultSharedModuleContext, LangiumServices, LangiumSharedServices, createDefaultSharedModule } from 'langium/lsp';
import { LangiumServicesForTypirBinding, createLangiumModuleForTypirBinding, initializeLangiumTypirServices } from 'typir-langium';
import { LoxGeneratedModule, LoxGeneratedSharedModule } from './generated/module.js';
import { LoxScopeProvider } from './lox-scope.js';
import { LoxValidationRegistry, LoxValidator } from './lox-validator.js';
import { createLoxTypirModule } from './type-system/lox-type-checking.js';
import { createLoxTypirModule } from './lox-type-checking.js';
import { LoxLinker } from './lox-linker.js';

/**
* Declaration of custom services - add your own service classes here.
*/
export type LoxAddedServices = {
validation: {
LoxValidator: LoxValidator
}
},
typir: LangiumServicesForTypirBinding,
}

/**
* Union of Langium default services and your custom services - use this as constructor parameter
* of custom service classes.
*/
export type LoxServices = LangiumServices & LoxAddedServices & LangiumServicesForTypirBinding
export type LoxServices = LangiumServices & LoxAddedServices

/**
* Dependency injection module that overrides Langium default services and contributes the
* declared custom services. The Langium defaults can be partially specified to override only
* selected services, while the custom services must be fully specified.
*/
export const LoxModule: Module<LoxServices, PartialLangiumCoreServices & LoxAddedServices> = {
validation: {
ValidationRegistry: (services) => new LoxValidationRegistry(services),
LoxValidator: () => new LoxValidator()
},
references: {
ScopeProvider: (services) => new LoxScopeProvider(services)
}
};
export function createLoxModule(shared: LangiumSharedCoreServices): Module<LoxServices, PartialLangiumCoreServices & LoxAddedServices> {
return {
validation: {
ValidationRegistry: (services) => new LoxValidationRegistry(services),
LoxValidator: () => new LoxValidator()
},
// For type checking with Typir, inject and merge these modules:
typir: () => inject(Module.merge(
createLangiumModuleForTypirBinding(shared), // the Typir default services
createLoxTypirModule(shared), // custom Typir services for LOX
)),
references: {
ScopeProvider: (services) => new LoxScopeProvider(services),
Linker: (services) => new LoxLinker(services),
},
};
}

/**
* Create the full set of services required by Langium.
Expand All @@ -53,9 +63,6 @@ export const LoxModule: Module<LoxServices, PartialLangiumCoreServices & LoxAdde
* - Langium default language-specific services
* - Services generated by langium-cli
* - Services specified in this file
* For type checking with Typir, merge two more modules:
* - Typir default services
* - custom services for LOX
*
* @param context Optional module context with the LSP connection
* @returns An object wrapping the shared services and the language-specific services
Expand All @@ -71,11 +78,9 @@ export function createLoxServices(context: DefaultSharedModuleContext): {
const Lox = inject(
createDefaultCoreModule({ shared }),
LoxGeneratedModule,
LoxModule,
createLangiumModuleForTypirBinding(shared),
createLoxTypirModule(shared),
createLoxModule(shared),
);
shared.ServiceRegistry.register(Lox);
initializeLangiumTypirServices(Lox);
initializeLangiumTypirServices(Lox, Lox.typir);
return { shared, Lox };
}
Loading
Loading