├── .discontinued ├── example │ ├── herald.yaml │ ├── test.js │ ├── test.ts │ └── main.wrl ├── examples │ ├── WebServer │ │ ├── README.md │ │ ├── .gitignore │ │ ├── source │ │ │ ├── auth.wrl │ │ │ ├── typing.wrl │ │ │ └── main.wrl │ │ └── herald.yaml │ ├── frontend │ │ ├── herald.yaml │ │ ├── packages │ │ │ └── tangent │ │ │ │ ├── herald.yaml │ │ │ │ └── source │ │ │ │ ├── lib.wrl │ │ │ │ └── tangent │ │ │ │ ├── page.wrl │ │ │ │ ├── web.wrl │ │ │ │ └── tangent.wrl │ │ └── source │ │ │ └── app │ │ │ ├── views │ │ │ ├── shared │ │ │ │ └── shared.wrl │ │ │ ├── views.wrl │ │ │ └── Home │ │ │ │ └── Home.wrl │ │ │ ├── utils │ │ │ ├── element.wrl │ │ │ ├── utils.wrl │ │ │ ├── t.wrl │ │ │ └── css.wrl │ │ │ ├── styling │ │ │ └── styling.wrl │ │ │ ├── state │ │ │ └── state.wrl │ │ │ └── app.wrl │ └── randoms │ │ ├── alloc.wrl │ │ ├── randoms.wrl │ │ ├── tangent │ │ ├── page.wrl │ │ ├── web.wrl │ │ └── tangent.wrl │ │ └── config.wrl ├── bytecode │ ├── README.md │ ├── src │ │ ├── injunction.rs │ │ ├── instruction │ │ │ ├── mod.rs │ │ │ └── opcode │ │ │ │ └── generateOpcodeImpl.js │ │ ├── tests │ │ │ ├── main.wrl │ │ │ └── mod.rs │ │ ├── retriever │ │ │ ├── model.rs │ │ │ ├── mod.rs │ │ │ └── function.rs │ │ ├── expressions.md │ │ ├── from_text.rs │ │ ├── constant.rs │ │ ├── registers.rs │ │ ├── lib.rs │ │ ├── branstorm.md │ │ └── reader.rs │ └── Cargo.toml ├── runtime │ ├── src │ │ ├── lib.rs │ │ └── predefined.rs │ ├── benchmarks │ │ ├── test.js │ │ └── test.py │ └── Cargo.toml └── README.md ├── examples ├── hello-world │ ├── README.md │ ├── .gitignore │ ├── source │ │ └── main.wrl │ └── herald.toml ├── self-host │ ├── source │ │ └── main.wrl │ └── herald.toml ├── type-test │ └── herald.toml └── algorithms │ ├── herald.info │ └── source │ └── lib.wrl ├── source ├── cli │ ├── src │ │ ├── globals.rs │ │ ├── help.rs │ │ └── options.rs │ ├── README.md │ └── Cargo.toml ├── herald │ ├── .gitignore │ ├── .npmignore │ ├── source │ │ └── mod.mjs │ ├── jsconfig.json │ ├── bun.lockb │ ├── deno.json │ ├── README.md │ └── package.json ├── library │ ├── .gitignore │ ├── system │ │ ├── os │ │ │ └── os.wrl │ │ ├── system.wrl │ │ ├── net │ │ │ ├── net.wrl │ │ │ └── http │ │ │ │ ├── response.wrl │ │ │ │ ├── http.wrl │ │ │ │ └── request.wrl │ │ ├── path │ │ │ └── path.wrl │ │ ├── process │ │ │ └── process.wrl │ │ ├── fs │ │ │ └── fs.wrl │ │ └── io │ │ │ ├── terminal.wrl │ │ │ └── io.wrl │ ├── README.md │ ├── core │ │ ├── internals │ │ │ ├── gc │ │ │ │ └── gc.wrl │ │ │ ├── internals.wrl │ │ │ └── never.wrl │ │ ├── data │ │ │ ├── serialize.wrl │ │ │ └── data.wrl │ │ ├── collections │ │ │ ├── arrayQueue.wrl │ │ │ ├── collections.wrl │ │ │ ├── ntuple │ │ │ │ ├── ntuple.wrl │ │ │ │ ├── quintuple.wrl │ │ │ │ ├── triple.wrl │ │ │ │ ├── sextuple.wrl │ │ │ │ ├── tuple.wrl │ │ │ │ └── quadruple.wrl │ │ │ └── hashMap.wrl │ │ ├── interfaces │ │ │ ├── interfaces.wrl │ │ │ ├── default.wrl │ │ │ ├── try.wrl │ │ │ └── guaranteed.wrl │ │ ├── time │ │ │ └── time.wrl │ │ ├── ops │ │ │ ├── range.wrl │ │ │ └── ops.wrl │ │ ├── core.wrl │ │ ├── string │ │ │ ├── coloredstring.wrl │ │ │ └── display.wrl │ │ ├── memory │ │ │ ├── ref.wrl │ │ │ └── memory.wrl │ │ ├── concurrent │ │ │ └── concurrent.wrl │ │ ├── prelude │ │ │ └── prelude.wrl │ │ ├── boolean │ │ │ └── boolean.wrl │ │ └── testing │ │ │ └── testing.wrl │ ├── lib.wrl │ ├── .DS_Store │ └── malloc.wat ├── compiler │ ├── utils │ │ ├── src │ │ │ ├── threadpool.rs │ │ │ ├── container.rs │ │ │ ├── lib.rs │ │ │ └── terminal │ │ │ │ └── table.rs │ │ └── Cargo.toml │ ├── pretty │ │ ├── src │ │ │ ├── format.rs │ │ │ ├── lib.rs │ │ │ └── highlight.rs │ │ └── Cargo.toml │ ├── analyzer │ │ ├── src │ │ │ ├── representations │ │ │ │ ├── mod.rs │ │ │ │ └── astrepr.rs │ │ │ ├── typechecker │ │ │ │ ├── mod.rs │ │ │ │ └── checker │ │ │ │ │ └── statements │ │ │ │ │ └── enum_declaration.rs │ │ │ ├── binding │ │ │ │ └── typed_module.rs │ │ │ ├── lib.rs │ │ │ ├── literalmap.rs │ │ │ ├── globals.rs │ │ │ ├── modulemap.rs │ │ │ └── programdiagnostic.rs │ │ └── Cargo.toml │ ├── errors │ │ ├── src │ │ │ ├── runtime_error.rs │ │ │ ├── warning.rs │ │ │ ├── bytecode_error.rs │ │ │ └── lex_error.rs │ │ └── Cargo.toml │ ├── ast │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── lexer │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── lib.rs │ │ │ └── text_lexer.rs │ ├── macros │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ └── parser │ │ ├── src │ │ └── lib.rs │ │ └── Cargo.toml ├── server │ ├── README.md │ ├── src │ │ ├── error.rs │ │ ├── requests.rs │ │ ├── message_store.rs │ │ ├── folding_range.rs │ │ └── diagnostic │ │ │ └── mod.rs │ └── Cargo.toml ├── repl │ ├── src │ │ └── main.rs │ └── Cargo.toml ├── .DS_Store ├── website │ ├── scss │ │ ├── app.scss │ │ ├── animations.scss │ │ ├── font.scss │ │ └── utils.scss │ ├── public │ │ ├── favicon.ico │ │ ├── whirlwind.svg │ │ └── site.webmanifest │ ├── README.md │ ├── assets │ │ ├── fonts │ │ │ ├── Sedan-Italic.ttf │ │ │ └── Sedan-Regular.ttf │ │ └── Icon.svg │ ├── server │ │ └── index.ts │ └── pages │ │ ├── index.scss │ │ └── index.html ├── extensions │ └── vscode │ │ ├── bun.lockb │ │ ├── .env.sample │ │ ├── images │ │ ├── Screenshot.png │ │ ├── Screenshot-2.png │ │ ├── wrl_color_2.svg │ │ └── wrl_color.svg │ │ ├── jsconfig.json │ │ ├── syntaxes │ │ └── whirlwind.json │ │ ├── README.md │ │ ├── snippets │ │ └── whirlwind.code-snippets │ │ ├── package.json │ │ ├── src │ │ └── completion.js │ │ └── pnpm-lock.yaml └── codegen │ ├── Cargo.toml │ └── src │ ├── error_helpers.rs │ └── opcode.rs ├── docs ├── BUILD.md ├── CONTRIBUTING.md ├── CODE_OF_CONDUCT.md ├── roadmap.md ├── README.md └── quick-intro │ └── README.md ├── bun.lockb ├── assets ├── Git Banner.png ├── wrl_color_2.svg ├── herald.svg └── Icon.svg ├── tsconfig.json ├── vite.config.ts ├── .vscode ├── settings.json ├── launch.json └── tasks.json ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── commands.md ├── README.md ├── package.json └── .gitignore /.discontinued/example/herald.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/hello-world/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/cli/src/globals.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /source/herald/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /source/library/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/hello-world/.gitignore: -------------------------------------------------------------------------------- 1 | dependencies -------------------------------------------------------------------------------- /source/compiler/utils/src/threadpool.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/BUILD.md: -------------------------------------------------------------------------------- 1 | # Building Whirlwind from Source. 2 | -------------------------------------------------------------------------------- /docs/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTIBRUTING TO WHIRLWIND 2 | -------------------------------------------------------------------------------- /source/library/system/os/os.wrl: -------------------------------------------------------------------------------- 1 | module os 2 | 3 | -------------------------------------------------------------------------------- /.discontinued/bytecode/README.md: -------------------------------------------------------------------------------- 1 | # discontinued. rip. 2 | -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/.gitignore: -------------------------------------------------------------------------------- 1 | Packages 2 | -------------------------------------------------------------------------------- /source/library/README.md: -------------------------------------------------------------------------------- 1 | # The Whirlwind Standard Library. -------------------------------------------------------------------------------- /source/library/core/internals/gc/gc.wrl: -------------------------------------------------------------------------------- 1 | module gc 2 | 3 | -------------------------------------------------------------------------------- /source/library/lib.wrl: -------------------------------------------------------------------------------- 1 | module lib 2 | 3 | public use core -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/source/auth.wrl: -------------------------------------------------------------------------------- 1 | module auth; -------------------------------------------------------------------------------- /.discontinued/examples/frontend/herald.yaml: -------------------------------------------------------------------------------- 1 | name: frontend 2 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/packages/tangent/herald.yaml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /source/herald/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | .gitignore 3 | jsconfig.json -------------------------------------------------------------------------------- /source/server/README.md: -------------------------------------------------------------------------------- 1 | # The Whirlwind Language Server 2 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/injunction.rs: -------------------------------------------------------------------------------- 1 | pub enum Injunction {} 2 | -------------------------------------------------------------------------------- /docs/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Be nice, respectful and understanding. 2 | -------------------------------------------------------------------------------- /docs/roadmap.md: -------------------------------------------------------------------------------- 1 | # The Whirlwind Roadmap 2 | 3 | ## Highlights 4 | -------------------------------------------------------------------------------- /source/compiler/pretty/src/format.rs: -------------------------------------------------------------------------------- 1 | pub struct Formatter {} 2 | -------------------------------------------------------------------------------- /source/library/core/data/serialize.wrl: -------------------------------------------------------------------------------- 1 | module serialize 2 | 3 | -------------------------------------------------------------------------------- /source/library/core/data/data.wrl: -------------------------------------------------------------------------------- 1 | module data 2 | 3 | public use json -------------------------------------------------------------------------------- /.discontinued/examples/frontend/packages/tangent/source/lib.wrl: -------------------------------------------------------------------------------- 1 | module lib; -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/bun.lockb -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/views/shared/shared.wrl: -------------------------------------------------------------------------------- 1 | module shared; -------------------------------------------------------------------------------- /source/repl/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("Hello, world!"); 3 | } 4 | -------------------------------------------------------------------------------- /source/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/.DS_Store -------------------------------------------------------------------------------- /source/herald/source/mod.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | 3 | console.log(fs); 4 | -------------------------------------------------------------------------------- /.discontinued/examples/randoms/alloc.wrl: -------------------------------------------------------------------------------- 1 | module alloc; 2 | 3 | 4 | function main() { 5 | 6 | } -------------------------------------------------------------------------------- /assets/Git Banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/assets/Git Banner.png -------------------------------------------------------------------------------- /source/herald/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "checkJs": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /source/herald/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/herald/bun.lockb -------------------------------------------------------------------------------- /source/website/scss/app.scss: -------------------------------------------------------------------------------- 1 | @import "./animations"; 2 | @import "./font"; 3 | @import "./utils"; 4 | -------------------------------------------------------------------------------- /source/library/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/library/.DS_Store -------------------------------------------------------------------------------- /.discontinued/example/test.js: -------------------------------------------------------------------------------- 1 | console.log("Hello world"); 2 | function doStuff() { 3 | return a; 4 | } 5 | -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/source/typing.wrl: -------------------------------------------------------------------------------- 1 | module typing; 2 | 3 | 4 | function main() { 5 | 6 | } -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/utils/element.wrl: -------------------------------------------------------------------------------- 1 | module element; 2 | 3 | 4 | public type Component; -------------------------------------------------------------------------------- /.discontinued/example/test.ts: -------------------------------------------------------------------------------- 1 | console.log("Hello world"); 2 | 3 | function doStuff() { 4 | return a; 5 | } 6 | -------------------------------------------------------------------------------- /source/herald/deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@whirlwind/herald", 3 | "version": "0.1.0", 4 | "exports": "./mod.mjs" 5 | } -------------------------------------------------------------------------------- /source/website/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/website/public/favicon.ico -------------------------------------------------------------------------------- /source/extensions/vscode/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/extensions/vscode/bun.lockb -------------------------------------------------------------------------------- /source/compiler/analyzer/src/representations/mod.rs: -------------------------------------------------------------------------------- 1 | mod module; 2 | mod astrepr; 3 | 4 | pub use module::*; 5 | pub use astrepr::*; -------------------------------------------------------------------------------- /source/library/system/system.wrl: -------------------------------------------------------------------------------- 1 | module system 2 | 3 | 4 | public use fs 5 | public use io 6 | public use path 7 | public use net -------------------------------------------------------------------------------- /source/website/README.md: -------------------------------------------------------------------------------- 1 | # Whirlwind Website 2 | 3 | This folder contains the source code for the Whirlwind documentation website. 4 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/instruction/mod.rs: -------------------------------------------------------------------------------- 1 | mod opcode; 2 | 3 | use analyzer::{PathIndex, ScopeId, SymbolIndex}; 4 | pub use opcode::*; 5 | -------------------------------------------------------------------------------- /source/compiler/utils/src/container.rs: -------------------------------------------------------------------------------- 1 | /// A container for an item. 2 | pub trait Container { 3 | fn unwrap(&self) -> Item; 4 | } 5 | -------------------------------------------------------------------------------- /source/extensions/vscode/.env.sample: -------------------------------------------------------------------------------- 1 | WHIRL_LS_PATH="../../../target/debug/server" 2 | CORE_LIBRARY_PATH="../../library/core/core.wrl" 3 | -------------------------------------------------------------------------------- /source/extensions/vscode/images/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/extensions/vscode/images/Screenshot.png -------------------------------------------------------------------------------- /source/website/assets/fonts/Sedan-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/website/assets/fonts/Sedan-Italic.ttf -------------------------------------------------------------------------------- /source/website/assets/fonts/Sedan-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/website/assets/fonts/Sedan-Regular.ttf -------------------------------------------------------------------------------- /source/website/scss/animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes slide_up { 2 | from { 3 | transform: translateY(30%); 4 | opacity: 0; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "jsx": "react-jsx", 5 | "jsxImportSource": "hono/jsx" 6 | } 7 | } -------------------------------------------------------------------------------- /source/extensions/vscode/images/Screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adebola-io/whirlwind/HEAD/source/extensions/vscode/images/Screenshot-2.png -------------------------------------------------------------------------------- /.discontinued/bytecode/src/tests/main.wrl: -------------------------------------------------------------------------------- 1 | module main; 2 | 3 | 4 | public function main() { 5 | 6 | } 7 | 8 | enum EvaluatedType { 9 | 10 | } -------------------------------------------------------------------------------- /source/compiler/pretty/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod format; 2 | mod highlight; 3 | mod writer; 4 | 5 | pub use format::*; 6 | pub use highlight::*; 7 | pub use writer::*; 8 | -------------------------------------------------------------------------------- /source/herald/README.md: -------------------------------------------------------------------------------- 1 | # Herald 2 | 3 | Herald is the package manager and setup tool for development using the Whirlwind language. 4 | 5 | ## Usage 6 | -------------------------------------------------------------------------------- /source/extensions/vscode/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["ESNext"], 4 | "module": "CommonJS", 5 | "checkJs": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /source/library/core/collections/arrayQueue.wrl: -------------------------------------------------------------------------------- 1 | module arrayQueue 2 | 3 | /// An implementation of a queue data structure using an array. 4 | public model ArrayQueue { 5 | 6 | } -------------------------------------------------------------------------------- /.discontinued/examples/randoms/randoms.wrl: -------------------------------------------------------------------------------- 1 | /// Random files ripped out of other repositories and popular projects 2 | /// to see how they would look in Whirlwind. 3 | module randoms; 4 | 5 | -------------------------------------------------------------------------------- /examples/hello-world/source/main.wrl: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | function print(message: T) { 4 | todo() 5 | } 6 | 7 | public function main() { 8 | // .. 9 | } 10 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/utils/utils.wrl: -------------------------------------------------------------------------------- 1 | module utils; 2 | 3 | public use css.{css, CSSStyle}; 4 | public use t; 5 | public use element.{Element, HtmlDivElement, HtmlHeadingElement}; -------------------------------------------------------------------------------- /source/library/core/interfaces/interfaces.wrl: -------------------------------------------------------------------------------- 1 | /// This module defines intrinsic interfaces 2 | module interfaces 3 | 4 | 5 | public use default.Default 6 | public use guaranteed.Guaranteed 7 | public use try.Try -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/views/views.wrl: -------------------------------------------------------------------------------- 1 | module views; 2 | 3 | 4 | public use Home; 5 | public use shared; 6 | 7 | public use super.state; 8 | public use super.styling; 9 | public use super.utils; -------------------------------------------------------------------------------- /.discontinued/runtime/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | mod predefined; 3 | mod sequence; 4 | mod stack; 5 | mod tests; 6 | mod vm; 7 | 8 | pub use sequence::*; 9 | pub use stack::*; 10 | pub use vm::*; 11 | -------------------------------------------------------------------------------- /examples/self-host/source/main.wrl: -------------------------------------------------------------------------------- 1 | module main 2 | 3 | use system.io.print 4 | use system.env.readArgs 5 | 6 | function main { 7 | args := readArgs().skipFirst(2) 8 | print('This is the first step.') 9 | } -------------------------------------------------------------------------------- /source/compiler/errors/src/runtime_error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy)] 2 | pub enum ExecutionError { 3 | MainCrashed, 4 | MainFunctionNotDefined, 5 | StackOverflow, 6 | IllegalMemoryAccess, 7 | } 8 | -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/herald.yaml: -------------------------------------------------------------------------------- 1 | name: WebServer 2 | version: 0.0.0 3 | description: A simple web server implementation in Whirlwind. 4 | 5 | author: Adebola-io 6 | license: MIT 7 | 8 | entry: Source/Main.wrl 9 | -------------------------------------------------------------------------------- /source/repl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "repl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /examples/type-test/herald.toml: -------------------------------------------------------------------------------- 1 | [project.info] 2 | name = "whirlwind-examples.type-test" 3 | type = "library" 4 | version = "0.0.1" 5 | description = "Testing the type system of Whirlwind." 6 | authors = ["The Whirlwind Team"] 7 | -------------------------------------------------------------------------------- /source/compiler/utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "utils" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /source/compiler/pretty/src/highlight.rs: -------------------------------------------------------------------------------- 1 | /// Highlights the syntax of a piece of source code using the base tokenizer and 2 | /// the `utils::Colored` struct. 3 | pub fn highlight(text: &str) -> String { 4 | // TODO 5 | text.to_string() 6 | } 7 | -------------------------------------------------------------------------------- /.discontinued/README.md: -------------------------------------------------------------------------------- 1 | # The Recycle Bin of the Repo. 2 | 3 | This directory contains discarded ideas, failing prototypes and long abandoned dreams. 4 | Nothing in here works. Nothing in here makes sense. But nothing here is to be deleted just yet. 5 | -------------------------------------------------------------------------------- /source/codegen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "codegen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | analyzer = { path = "../compiler/analyzer" } 8 | ast = { path = "../compiler/ast" } 9 | errors = { path = "../compiler/errors" } -------------------------------------------------------------------------------- /source/website/server/index.ts: -------------------------------------------------------------------------------- 1 | import { Hono } from 'hono'; 2 | 3 | const app = new Hono(); 4 | 5 | app.get('/', (c) => { 6 | return c.text('Hello Hono!'); 7 | }); 8 | 9 | export default { 10 | port: 3100, 11 | fetch: app.fetch, 12 | }; 13 | -------------------------------------------------------------------------------- /source/website/scss/font.scss: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Averia+Serif+Libre:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap"); 2 | @font-face { 3 | font-family: Sedan; 4 | src: url(../assets/fonts/Sedan-Regular.ttf); 5 | } 6 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | 3 | export default defineConfig({ 4 | root: './source/website/pages', 5 | publicDir: '../public', 6 | build: { 7 | outDir: '../../../dist', 8 | emptyOutDir: true, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/retriever/model.rs: -------------------------------------------------------------------------------- 1 | use analyzer::{Standpoint, SymbolIndex}; 2 | 3 | /// Returns a type declaration that maps to a type symbol. 4 | pub struct ModelRetriever<'a> { 5 | standpoint: &'a Standpoint, 6 | symbolindex: SymbolIndex, 7 | } 8 | -------------------------------------------------------------------------------- /.discontinued/example/main.wrl: -------------------------------------------------------------------------------- 1 | module main; 2 | 3 | use core.io.prompt; 4 | 5 | 6 | function main() { 7 | name := prompt(some("How do you do?: ")); 8 | ageStr := prompt(some("What is your age?")); 9 | 10 | age = UInt8.parse(ageStr); 11 | } 12 | -------------------------------------------------------------------------------- /source/compiler/ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ast" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | macros = {version="*", path="../macros"} -------------------------------------------------------------------------------- /source/compiler/errors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "errors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ast = {version="*", path="../ast"} -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/styling/styling.wrl: -------------------------------------------------------------------------------- 1 | module styling; 2 | 3 | use super.utils.{Styles, css}; 4 | 5 | public function WindowStyles() -> Styles { 6 | style() 7 | .colors("f: blue, b: red") 8 | .rounded("8px") 9 | 10 | } -------------------------------------------------------------------------------- /.discontinued/runtime/benchmarks/test.js: -------------------------------------------------------------------------------- 1 | // console.time(); 2 | // let s = ""; 3 | // for (let i = 0; i < 100000000; i++) {} 4 | // console.timeEnd(); 5 | 6 | console.time(); 7 | if (10 + 20 == 30) { 8 | } else { 9 | console.log(30); 10 | } 11 | console.timeEnd(); 12 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/views/Home/Home.wrl: -------------------------------------------------------------------------------- 1 | module Home; 2 | 3 | use core.memory.Allocator; 4 | 5 | function main() { 6 | allocator := createAllocator(); 7 | 8 | 9 | 10 | print("In the beginning..."); 11 | 12 | allocator.close(); 13 | } -------------------------------------------------------------------------------- /examples/hello-world/herald.toml: -------------------------------------------------------------------------------- 1 | [project.info] 2 | name = "whirlwind-examples.hello-world" 3 | type = "binary" 4 | version = "0.0.1" 5 | description = "A simple hello world example in Whirlwind." 6 | authors = ["The Whirlwind Team"] 7 | namespace = "system" 8 | entry = "source/main.wrl" 9 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/utils/t.wrl: -------------------------------------------------------------------------------- 1 | module t; 2 | 3 | use element.HtmlDivElement; 4 | use element.Element; 5 | 6 | public function div() -> HtmlDivElement { 7 | return new HtmlDivElement(); 8 | } 9 | 10 | public function root() -> Element { 11 | todo() 12 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "asiter", 4 | "corelib", 5 | "stricjs", 6 | "symbolindex", 7 | "symbollib", 8 | "typecheck", 9 | "typechecker", 10 | "Typechecks" 11 | ], 12 | "svg.preview.background": "dark-transparent" 13 | } 14 | -------------------------------------------------------------------------------- /assets/wrl_color_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/self-host/herald.toml: -------------------------------------------------------------------------------- 1 | [project.info] 2 | name = "whirlwind-examples.self-host" 3 | type = "binary" 4 | version = "0.0.1" 5 | description = "An experimental compiler for Whirlwind, written in Whirlwind." 6 | authors = ["The Whirlwind Team"] 7 | namespace = "system" 8 | entry = "source/main.wrl" 9 | -------------------------------------------------------------------------------- /source/extensions/vscode/images/wrl_color_2.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/typechecker/mod.rs: -------------------------------------------------------------------------------- 1 | mod checker; 2 | mod environment; 3 | mod evaluated; 4 | mod intermediate; 5 | mod tests; 6 | pub mod unify; 7 | pub mod utils; 8 | 9 | pub use checker::*; 10 | pub use environment::*; 11 | pub use evaluated::*; 12 | pub use intermediate::*; 13 | pub use unify::*; 14 | -------------------------------------------------------------------------------- /.discontinued/runtime/src/predefined.rs: -------------------------------------------------------------------------------- 1 | /// Maximum value for the part of a stack that is allocated on (Rust's) heap. 2 | pub const MAX_STACK_SIZE: usize = 2097152; //2mb 3 | /// The start of the instruction stream in the bytecode. 4 | pub const INSTRUCTION_START: usize = 1; // 0 is supposed to be the global return address. 5 | -------------------------------------------------------------------------------- /source/compiler/lexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lexer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ast = {version = "*", path = "../ast"} 10 | errors = {version="*", path="../errors"} -------------------------------------------------------------------------------- /source/compiler/macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "macros" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | proc_macro = true 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | quote = "1.0.33" 13 | syn = "2.0.37" 14 | -------------------------------------------------------------------------------- /source/website/public/whirlwind.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /source/cli/README.md: -------------------------------------------------------------------------------- 1 | # Whirlwindc 2 | 3 | This repo contains the front facing interface for the Whirlwind compiler. 4 | 5 | ## Notes 6 | 7 | --CORELIBPATH in dev mode is `../library/core/core.wrl`. 8 | 9 | For starters, run 10 | 11 | ```sh 12 | cargo run check ../../examples/hello-world/source/main.wrl --CORELIBPATH=../library/core/core.wrl 13 | ``` 14 | -------------------------------------------------------------------------------- /source/compiler/errors/src/warning.rs: -------------------------------------------------------------------------------- 1 | use ast::Span; 2 | 3 | #[derive(PartialEq, Debug)] 4 | pub struct Warning { 5 | pub span: Span, 6 | pub warning_type: WarningType, 7 | } 8 | 9 | #[derive(PartialEq, Debug)] 10 | pub enum WarningType { 11 | UnusedImportSymbol(String), 12 | UnusedModelSymbol(String), 13 | RedundantConstraint, 14 | } 15 | -------------------------------------------------------------------------------- /source/website/scss/utils.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Converts a value in pixels to its representation 3 | * in relative width units, compared to the screen width. 4 | * If no screen width is given, the default screen width of 430px 5 | * is used. 6 | */ 7 | @function relative-width($value, $screen-width: 430) { 8 | @return calc(($value / $screen-width) * 100) + vw; 9 | } 10 | -------------------------------------------------------------------------------- /source/compiler/parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | // #![allow(unused)] 2 | 3 | use std::str::Chars; 4 | 5 | pub use parse::Parser; 6 | 7 | use lexer::{lex_text, TextLexer}; 8 | 9 | mod parse; 10 | mod test; 11 | 12 | /// Returns an iterable parser for text input. 13 | pub fn parse_text(input: &str) -> Parser> { 14 | Parser::from_lexer(lex_text(input)) 15 | } 16 | -------------------------------------------------------------------------------- /.discontinued/runtime/benchmarks/test.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | # start = time.time() 4 | # for x in range(100000000): 5 | # pass 6 | # end = time.time() 7 | # print(f"Program exited in {end - start}s") 8 | 9 | 10 | start = time.time() 11 | if 10 + 20 == 30: 12 | pass 13 | else: 14 | print(30) 15 | end = time.time() 16 | print(f"Program exited in {end - start}s") 17 | -------------------------------------------------------------------------------- /source/library/system/net/net.wrl: -------------------------------------------------------------------------------- 1 | module net 2 | 3 | public use http 4 | use core.prelude.Display 5 | use core.prelude.String 6 | use core.prelude.todo 7 | 8 | 9 | public model Url implements Display { 10 | var scheme: String 11 | var host: String 12 | var path: String 13 | 14 | public function [Display.toString] -> String { 15 | todo() 16 | } 17 | } -------------------------------------------------------------------------------- /examples/algorithms/herald.info: -------------------------------------------------------------------------------- 1 | .info ( 2 | .name "examples/algorithms" 3 | .type "library" 4 | .version "0.0.1" 5 | .description "Implementation of various algorithms in Whirlwind." 6 | .authors "The Whirlwind Team" 7 | .license "Apache-2.0" 8 | ) 9 | 10 | .dependencies ( 11 | .webServer ( 12 | .source "https://github.com/adebola-io/web-server" 13 | ) 14 | ) -------------------------------------------------------------------------------- /source/compiler/parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | lexer = {version = "*", path = "../lexer"} 12 | ast = {version = "*", path = "../ast"} 13 | utils = {version="*", path="../utils"} 14 | errors = {version="*", path="../errors"} -------------------------------------------------------------------------------- /.discontinued/bytecode/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bytecode" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | analyzer = {version = "*", path = "../analyzer"} 10 | ast = {version = "*", path = "../ast"} 11 | errors = {version = "*", path = "../errors"} 12 | utils = {version = "*", path= "../utils"} 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = [ 4 | "source/compiler/ast", 5 | "source/compiler/errors", 6 | "source/compiler/lexer", 7 | "source/compiler/parser", 8 | "source/compiler/pretty", 9 | "source/compiler/analyzer", 10 | "source/compiler/utils", 11 | "source/compiler/macros", 12 | "source/cli", 13 | "source/server", 14 | "source/repl", 15 | "source/codegen", 16 | ] 17 | -------------------------------------------------------------------------------- /source/library/core/time/time.wrl: -------------------------------------------------------------------------------- 1 | module time 2 | 3 | use core.{ 4 | numeric.i32, 5 | sentinels.todo 6 | } 7 | 8 | public type Duration = Second | Millisecond 9 | 10 | public model Second { 11 | 12 | public static function times(number: i32) -> This { 13 | todo() 14 | } 15 | } 16 | 17 | public model Millisecond { 18 | public static function times(number: i32) -> This { 19 | todo() 20 | } 21 | } -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/state/state.wrl: -------------------------------------------------------------------------------- 1 | module state; 2 | 3 | // use tangent.{StateStore, State}; 4 | 5 | interface StateStore {} 6 | type State { 7 | var value: T; 8 | new(value: T) { 9 | this.value = value; 10 | } 11 | } 12 | 13 | public type ClientState implements StateStore { 14 | public var navMobile: State; 15 | 16 | new() { 17 | this.navMobile = new State(false); 18 | } 19 | } -------------------------------------------------------------------------------- /source/compiler/pretty/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pretty" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ast = {version ="*", path="../ast"} 10 | errors = {version="*", path="../errors"} 11 | utils = {version="*", path="../utils"} 12 | analyzer = {version="*", path="../analyzer"} 13 | lexer = {version="*", path="../lexer"} -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "args": [ 6 | "--extensionDevelopmentPath=${workspaceFolder}/source/extensions/vscode", 7 | "--inspect-extensions=6009" 8 | ], 9 | 10 | "name": "Launch Extension", 11 | "outFiles": ["${workspaceFolder}/extensions/code/out/**/*.js"], 12 | "request": "launch", 13 | "type": "extensionHost" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /source/compiler/analyzer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "analyzer" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | parser = {version = "*", path = "../parser"} 10 | ast = {version = "*", path = "../ast"} 11 | lexer = {version = "*", path = "../lexer"} 12 | errors = {version="*", path="../errors"} 13 | utils = {version="*", path="../utils"} 14 | -------------------------------------------------------------------------------- /source/website/public/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "short_name": "", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /source/herald/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@whirlwind/herald", 3 | "version": "0.0.0", 4 | "description": "The Whirlwind setup tool.", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "node source/index.mjs", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "languages" 12 | ], 13 | "author": "Adebola Akomolafe", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@types/node": "^20.11.24" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source/server/src/error.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | use std::{io::Error as IOError, path::PathBuf}; 3 | use url::{ParseError, Url}; 4 | 5 | #[derive(Debug)] 6 | pub enum DocumentError { 7 | IOError(IOError), 8 | NoParentFolder(PathBuf), 9 | WhattheHellHappened, 10 | CouldNotConvert(Url), 11 | CouldNotDetermineMain, 12 | } 13 | 14 | impl From for DocumentError { 15 | fn from(value: IOError) -> Self { 16 | DocumentError::IOError(value) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/binding/typed_module.rs: -------------------------------------------------------------------------------- 1 | use ast::UseTarget; 2 | 3 | use crate::{PathIndex, SymbolIndex, TypedStmnt}; 4 | use std::path::PathBuf; 5 | 6 | /// A semantically contextualized module. 7 | #[derive(Debug)] 8 | pub struct TypedModule { 9 | pub line_lengths: Vec, 10 | pub path_buf: PathBuf, 11 | pub path_idx: PathIndex, 12 | pub symbol_idx: SymbolIndex, 13 | pub statements: Vec, 14 | // todo: ugh. 15 | pub imports: Vec<(UseTarget, Vec)>, 16 | } 17 | -------------------------------------------------------------------------------- /.discontinued/runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | analyzer = {version = "*", path = "../compiler/analyzer"} 10 | bytecode = {version = "*", path = "../compiler/bytecode"} 11 | ast = {version = "*", path = "../compiler/ast"} 12 | utils = {version = "*", path = "../compiler/utils"} 13 | errors = {version = "*", path = "../compiler/errors"} 14 | -------------------------------------------------------------------------------- /source/server/src/requests.rs: -------------------------------------------------------------------------------- 1 | use tower_lsp::lsp_types::request::Request; 2 | 3 | /// A file is being saved in the editor. 4 | pub struct SavingStart(); 5 | 6 | impl Request for SavingStart { 7 | type Params = (); 8 | type Result = (); 9 | const METHOD: &'static str = "saving-start"; 10 | } 11 | 12 | /// A file is being saved in the editor. 13 | pub struct SavingEnd(); 14 | 15 | impl Request for SavingEnd { 16 | type Params = (); 17 | type Result = (); 18 | const METHOD: &'static str = "saving-end"; 19 | } 20 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "command": "cd source/server && cargo build", 7 | "problemMatcher": ["$rustc"], 8 | "group": "build", 9 | "label": "Build Server Binary (Debug)" 10 | }, 11 | { 12 | "type": "shell", 13 | "command": "cd source/server && cargo build --release", 14 | "problemMatcher": ["$rustc"], 15 | "group": "build", 16 | "label": "Build Server Binary (Release)" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /source/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cli" 3 | version = "0.0.1" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ast = {version = "*", path = "../compiler/ast"} 10 | analyzer = {version = "*", path = "../compiler/analyzer"} 11 | utils = {version = "*", path = "../compiler/utils"} 12 | errors = {version = "*", path = "../compiler/errors"} 13 | pretty = {version = "*", path = "../compiler/pretty"} 14 | codegen = {version = "*", path = "../codegen"} -------------------------------------------------------------------------------- /source/library/system/path/path.wrl: -------------------------------------------------------------------------------- 1 | module path 2 | 3 | use super.io.IoError 4 | use core.prelude.{Outcome, boolean, todo, String} 5 | 6 | public model Path { 7 | new() { 8 | 9 | } 10 | /// Returns true if a path exists. 11 | public function exists -> boolean { 12 | todo() 13 | } 14 | public function toAbsolute -> Outcome { 15 | todo() 16 | } 17 | public function extname -> ?String { 18 | todo() 19 | } 20 | public function basename -> ?String { 21 | todo() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Whirlwind Docs 2 | 3 | This folder contains (sparse) notes to get up to speed in aiding Whirlwind's development process. It is mostly geared towards people who either wish to contribute to this repository, or gain a deeper understanding of the language's concepts. 4 | 5 | If you are just looking to learn how to write programs in Whirlwind, a better place to start would be the site, [here](http://whirlwind.lang.vercel.app). 6 | 7 | _Every single file in this folder is subject to change. Nothing here is law, there are only excuses and explanations._ 8 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/retriever/mod.rs: -------------------------------------------------------------------------------- 1 | mod function; 2 | mod type; 3 | 4 | use analyzer::{Standpoint, SymbolIndex}; 5 | pub use function::FunctionRetriever; 6 | 7 | // /// Returns an interface declaration that maps to an interface symbol. 8 | // pub struct InterfaceRetriever<'a> { 9 | // standpoint: &'a Standpoint, 10 | // symbolindex: SymbolIndex, 11 | // } 12 | 13 | // /// Returns a method declaration that maps to a method symbol. 14 | // pub struct MethodRetriever<'a> { 15 | // standpoint: &'a Standpoint, 16 | // symbolindex: SymbolIndex, 17 | // } 18 | -------------------------------------------------------------------------------- /.discontinued/examples/randoms/tangent/page.wrl: -------------------------------------------------------------------------------- 1 | module page; 2 | 3 | use tangent.{Style, Div, Element, Component}; 4 | 5 | 6 | type Page implements Component { 7 | var base: Div; 8 | 9 | public function [Component.base]() -> Element { 10 | this.base 11 | } 12 | 13 | public function render() -> Element { 14 | this.base() 15 | .style(css.Page) 16 | } 17 | } 18 | 19 | type css { 20 | public static function Page() -> Style { 21 | (@Style) 22 | .height("400px") 23 | .width("900px") 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /source/library/core/collections/collections.wrl: -------------------------------------------------------------------------------- 1 | /// This module contains useful, fundamental types and operations 2 | /// for managing collections of data. 3 | module collections 4 | 5 | 6 | public use arrayStack.ArrayStack 7 | public use arrayQueue.ArrayQueue 8 | public use hashMap.{ 9 | HashMap, HashMapKeys, HashMapValues, 10 | Hash, Hasher, ToMap 11 | } 12 | public use ntuple.{ 13 | Tuple, pair, Triple, Quadruple, 14 | Quintuple, Sextuple, NTuple 15 | } 16 | public use set.{Set, SetIterator} 17 | public use sort.{mergeSort, quickSort, insertionSort, bubbleSort, timSort} -------------------------------------------------------------------------------- /.discontinued/examples/frontend/packages/tangent/source/tangent/page.wrl: -------------------------------------------------------------------------------- 1 | module page; 2 | 3 | use tangent.{Style, Div, Element, Component}; 4 | 5 | 6 | type Page implements Component { 7 | var base: Div; 8 | 9 | public function [Component.base]() -> Element { 10 | this.base 11 | } 12 | 13 | public function render() -> Element { 14 | this.base() 15 | .style(css.Page) 16 | } 17 | } 18 | 19 | type css { 20 | public static function Page() -> Style { 21 | (@Style) 22 | .height("400px") 23 | .width("900px") 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /source/compiler/analyzer/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod binding; 2 | mod globals; 3 | mod literalmap; 4 | mod modulemap; 5 | mod programdiagnostic; 6 | mod representations; 7 | mod standpoint; 8 | mod surfacearea; 9 | mod symbollib; 10 | mod symbols; 11 | mod typechecker; 12 | mod visitor; 13 | 14 | pub use binding::*; 15 | pub use globals::*; 16 | pub use literalmap::*; 17 | pub use modulemap::*; 18 | pub use programdiagnostic::*; 19 | pub use representations::*; 20 | pub use standpoint::*; 21 | pub use surfacearea::*; 22 | pub use symbollib::*; 23 | pub use symbols::*; 24 | pub use typechecker::*; 25 | pub use visitor::*; 26 | -------------------------------------------------------------------------------- /source/server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1", features = ["full"] } 10 | tower-lsp = {version = "0.20.0"} 11 | ast = {version = "*", path = "../compiler/ast"} 12 | analyzer = {version = "*", path = "../compiler/analyzer"} 13 | pretty = {version = "*", path = "../compiler/pretty"} 14 | errors = {version="*", path="../compiler/errors"} 15 | utils = {version="*" , path="../compiler/utils"} 16 | url = "2.4.1" 17 | serde_json = "1.0.108" 18 | -------------------------------------------------------------------------------- /source/codegen/src/error_helpers.rs: -------------------------------------------------------------------------------- 1 | use analyzer::TypedFunctionDeclaration; 2 | 3 | const REPORT: &str = 4 | "This is a compiler bug. Please report this at https://github.com/adebola-io/whirlwind/issues."; 5 | pub fn function_mismatch_error(function_symbol: &analyzer::SemanticSymbol) { 6 | unreachable!("A function declaration was matched to a symbol of type {:?} during bytecode generation. {REPORT}", function_symbol.kind) 7 | } 8 | 9 | pub fn function_resolve_error(function_symbol: &TypedFunctionDeclaration) -> ! { 10 | unreachable!("A function declaration {:?} did not resolve to a symbol during bytecode generation. {REPORT}", function_symbol) 11 | } 12 | -------------------------------------------------------------------------------- /source/library/core/internals/internals.wrl: -------------------------------------------------------------------------------- 1 | /// ## Core of the Core. 2 | /// This module is the beating heart of Whirlwind, responsible for defining and orchestrating 3 | /// critical low-level functions and data manipulations. 4 | /// 5 | /// It is **strictly** for internal core library development and optimization 6 | /// purposes. Treat everything within it as unsafe, unstable and unpredictable, unless 7 | /// stated otherwise. Modifying its contents or directly depending on them 8 | /// _will_ have unintended consequences. 9 | /// 10 | /// If you break anything, it's not my fault. 11 | module internals 12 | 13 | 14 | public use never.never 15 | public use wasm 16 | 17 | -------------------------------------------------------------------------------- /commands.md: -------------------------------------------------------------------------------- 1 | # List of Scripts in the Project 2 | 3 | - `build.ls.debug`: Builds the Whirlwind language server binary in debug mode. 4 | - `build.ls.release`: Builds the Whirlwind language server binary in release mode. 5 | - `build.cli.debug`: Builds the Whirlwind CLI in debug mode. 6 | - `build.cli.release`: Builds the Whirlwind CLI in release mode. 7 | - `publish.extensions.code`: Publishes the Whirlwind VSCode extension. 8 | - `cli.example`: Tests the Whirlwind CLI on `examples/hello-world/source/main.wrl` 9 | 10 | - `herald.node`: Runs the language server in Node.js. 11 | - `herald.deno`: Runs the language server in Deno. 12 | - `herald.bun`: Runs the language server in Bun. 13 | -------------------------------------------------------------------------------- /source/compiler/errors/src/bytecode_error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum BytecodeError { 3 | /// The main function is marked as asynchronous. 4 | MainIsAsync, 5 | /// The main function has a return type. 6 | MainReturns, 7 | /// The main function could not be found in the symbol table. 8 | MainNotFound, 9 | /// The main function has parameters. 10 | MainHasParameters, 11 | /// The symbol for the main function could not be reolved from the symbol table. 12 | MainNotResolvable, 13 | /// The main function is not marked as public. 14 | MainNotPublic, 15 | /// The main function is imported from an external library. 16 | MainIsImported, 17 | } 18 | -------------------------------------------------------------------------------- /source/library/core/ops/range.wrl: -------------------------------------------------------------------------------- 1 | module range 2 | 3 | use core.iterate.Iterable 4 | use core.maybe.{some, none} 5 | use core.ops.Sequenced 6 | 7 | public model Range implements Iterable { 8 | public var start: T 9 | var current: ?T 10 | public var end: T 11 | 12 | new(start: T, end: T) { 13 | this.start = start 14 | this.current = some(start) 15 | this.end = end 16 | } 17 | 18 | public function [Iterable.next] -> ?T { 19 | var current = this.current? 20 | if current == this.end { 21 | this.current = none() 22 | } else { 23 | this.current = current.nextItem() 24 | } 25 | return some(current) 26 | } 27 | } -------------------------------------------------------------------------------- /source/compiler/utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod container; 2 | mod fs; 3 | mod partial; 4 | mod stringmutator; 5 | mod threadpool; 6 | mod unorderedmap; 7 | 8 | pub use container::*; 9 | // pub use fs::*; 10 | pub use partial::*; 11 | use std::{ 12 | path::Path, 13 | sync::{Arc, Mutex}, 14 | }; 15 | pub use stringmutator::*; 16 | pub mod terminal; 17 | // pub use threadpool::*; 18 | pub use unorderedmap::*; 19 | 20 | pub fn get_parent_dir(path: &Path) -> Option<&Path> { 21 | path.ancestors().nth(1) 22 | } 23 | 24 | pub fn get_dir_basename(path: &std::path::Path) -> Option<&str> { 25 | path.components() 26 | .last() 27 | .map(|component| component.as_os_str()) 28 | .and_then(|dirname| dirname.to_str()) 29 | } 30 | 31 | pub type Atomic = Arc>; 32 | -------------------------------------------------------------------------------- /source/compiler/macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | extern crate proc_macro; 4 | extern crate quote; 5 | extern crate syn; 6 | 7 | #[proc_macro_derive(Signature)] 8 | pub fn signature(input: TokenStream) -> TokenStream { 9 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 10 | let name = input.ident; 11 | 12 | let expanded = quote::quote! { 13 | impl Signature for #name { 14 | fn name(&self) -> &str { 15 | self.name.name.as_str() 16 | } 17 | fn info(&self) -> Option<&Vec> { 18 | self.info.as_ref() 19 | } 20 | fn is_public(&self) -> bool { 21 | self.is_public 22 | } 23 | } 24 | }; 25 | 26 | TokenStream::from(expanded) 27 | } 28 | -------------------------------------------------------------------------------- /source/extensions/vscode/syntaxes/whirlwind.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | "lineComment": "//", 4 | "docComment": "///", 5 | "blockComment": ["/*", "*/"] 6 | }, 7 | "onEnterRules": [ 8 | { 9 | "beforeText": "^\\s*///\\s*", 10 | "action": { "indent": "none", "appendText": "/// " } 11 | } 12 | ], 13 | "brackets": [ 14 | ["{", "}"], 15 | ["[", "]"], 16 | ["(", ")"] 17 | ], 18 | "autoClosingPairs": [ 19 | ["{", "}"], 20 | ["[", "]"], 21 | ["(", ")"], 22 | ["/*", " */"], 23 | ["\"", "\""], 24 | ["'", "'"] 25 | ], 26 | "surroundingPairs": [ 27 | ["{", "}"], 28 | ["[", "]"], 29 | ["(", ")"], 30 | ["\"", "\""], 31 | ["'", "'"] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /source/library/system/process/process.wrl: -------------------------------------------------------------------------------- 1 | module process 2 | 3 | use core.{ 4 | concurrent.CurrentSequence, 5 | internals.builtin, 6 | } 7 | 8 | 9 | /// Returns the value of an environment variable. 10 | public function getEnv(varName: String) -> ?String { 11 | todo() 12 | } 13 | 14 | public model Error { 15 | 16 | } 17 | 18 | 19 | /// Returns a String value corresponding to the path of the current working directory. 20 | /// ### Usage 21 | /// ``` 22 | /// var currentDir = system.process.cwd() 23 | /// print("The current directory is " + currentDir) 24 | /// ``` 25 | public function cwd -> String { 26 | return builtin.sequence.cwd() 27 | } 28 | 29 | /// Schedules a function to run after a duration elapses. 30 | public function waitThen(callback: fn(), timeout: i32) { 31 | CurrentSequence.sleep(timeout) 32 | callback() 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /source/extensions/vscode/README.md: -------------------------------------------------------------------------------- 1 | # Whirlwind for VS Code. 2 | 3 | This is the official extension for the Whirlwind Programming Language, which provides syntax and semantic highlighting, as well as rich intellisense in Whirlwind files and projects. 4 | 5 | # Setup 6 | 7 | The extension requires at least VSCode v1.52.0. It also requires Whirlwind to be installed for it to work properly. 8 | 9 | For steps on installing Whirlwind, visit [here](https://whirlwind-lang.vercel.app). 10 | 11 | # Features 12 | 13 | - Code completion 14 | - Goto Definition 15 | - Workspace Diagnostics and Messages 16 | - Find All References 17 | - Types and Documentation on Hover 18 | 19 | --- 20 | 21 |

22 | Image showing syntax highlighting 23 | Image showing hover 24 |

25 | -------------------------------------------------------------------------------- /source/library/core/core.wrl: -------------------------------------------------------------------------------- 1 | /// ## The Whirlwind Core Library 2 | /// The core library is the cornerstone of all development in Whirlwind. 3 | /// It provides a foundational framework that underpins countless aspects of the language's functionality. 4 | /// 5 | /// The Core library is made up of utility functions and methods that streamline common programming tasks, 6 | /// ranging from String manipulation and arrays to networking and file system handling. 7 | module core 8 | 9 | public use array 10 | public use boolean 11 | public use collections 12 | public use concurrent 13 | public use data 14 | public use interfaces 15 | public use internals 16 | public use iterate 17 | public use math 18 | public use maybe 19 | public use memory 20 | public use numeric 21 | public use ops 22 | public use outcome 23 | public use prelude 24 | public use sentinels 25 | public use string 26 | public use testing 27 | public use time 28 | -------------------------------------------------------------------------------- /docs/quick-intro/README.md: -------------------------------------------------------------------------------- 1 | # Quick Intro to Whirlwind 2 | 3 | ## Installation 4 | 5 | ## Hello World 6 | 7 | ## Variables 8 | 9 | ## Comments 10 | 11 | ## Basic Data Types 12 | 13 | ## Control Flow 14 | 15 | ### If-Else 16 | 17 | ### For Loops 18 | 19 | ### While Loops 20 | 21 | ## Functions 22 | 23 | ### Optional Parameters 24 | 25 | ### Return Values 26 | 27 | ### Function Expressions 28 | 29 | ## Collections 30 | 31 | ## Models 32 | 33 | ### Public and Private Properties 34 | 35 | ### Destructuring 36 | 37 | ## Modules and Imports 38 | 39 | ### The `super` Module 40 | 41 | ### The `core` and `system` Libraries 42 | 43 | ## The `Maybe` and `Outcome` models 44 | 45 | ### Unwrapping 46 | 47 | ## Interfaces and Generics 48 | 49 | ### Implementations 50 | 51 | ### Type Constraints and Clauses 52 | 53 | ### Important Interfaces 54 | 55 | ## Union Types 56 | 57 | ## Enums 58 | 59 | ## External Functions 60 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/expressions.md: -------------------------------------------------------------------------------- 1 | # Generating Bytecode from Expressions. 2 | 3 | ## Binary Expressions. 4 | 5 | - Given the expression a + b. 6 | - Load a into a register of its type. Start with r1. If r1 is occupied, use r2. If both registers is occupied, pop the value on the operand stack, empty both r1 and r2 to the accumulator and use r1. 7 | - Load b into a register of its type, same as a. 8 | - Push the operand onto the operand queue. 9 | 10 | ## Assignment Expressions. 11 | 12 | - Transform the lhs from an expression to a stack address. 13 | - Evaluate the RHS. 14 | - If the two intermediate registers are not empty, flush their two intermediate registers with the last operand on the operand stack. 15 | - Empty whatever value is in the mapped accumulator register to the stack address. 16 | - If the value in the stack address is a heap pointer, 17 | - If the pointer has only one reference, update it in place. 18 | - If it has multiple references, clone it and update its clone. 19 | -------------------------------------------------------------------------------- /source/compiler/errors/src/lex_error.rs: -------------------------------------------------------------------------------- 1 | use ast::Span; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub struct LexError { 5 | pub error_type: LexErrorType, 6 | pub position: LexErrorPos, 7 | } 8 | impl LexError { 9 | pub fn unterminated_string(position: LexErrorPos) -> LexError { 10 | LexError { 11 | error_type: LexErrorType::UnterminatedString, 12 | position, 13 | } 14 | } 15 | 16 | pub fn no_value_after_exponent(position: LexErrorPos) -> LexError { 17 | LexError { 18 | error_type: LexErrorType::NoValAfterExponent, 19 | position, 20 | } 21 | } 22 | } 23 | 24 | #[derive(Debug, PartialEq)] 25 | /// The point to mark in an error. 26 | pub enum LexErrorPos { 27 | Point([u32; 2]), 28 | Span(Span), 29 | } 30 | 31 | #[derive(Debug, PartialEq)] 32 | pub enum LexErrorType { 33 | UnexpectedEndOfInput, 34 | UnterminatedString, 35 | InvalidCharacter(char), 36 | NoValAfterExponent, 37 | ExponentforInvalidBase, 38 | } 39 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/from_text.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use crate::{generate_from, BytecodeObject}; 4 | use analyzer::{Module, Standpoint, CORE_LIBRARY_PATH}; 5 | use errors::BytecodeError; 6 | 7 | /// Generates bytecode directly from text. 8 | /// Only for testing purposes. 9 | pub fn bytecode_from_text(text: &str) -> Result { 10 | let corelib_path = Some(PathBuf::from(CORE_LIBRARY_PATH)); 11 | let mut standpoint = Standpoint::new(true, corelib_path); 12 | let mut module = Module::from_text(text); 13 | module.module_path = Some(PathBuf::from("testing://main.wrl")); 14 | standpoint.entry_module = standpoint.add_module(module).unwrap(); 15 | standpoint.validate(); 16 | if standpoint 17 | .diagnostics 18 | .iter() 19 | .filter(|diagnostic| diagnostic.is_error()) 20 | .count() 21 | > 0 22 | { 23 | panic!( 24 | "Non-zero diagnostics: \n\n 25 | {:#?} 26 | ", 27 | standpoint.diagnostics 28 | ) 29 | } 30 | return generate_from(&standpoint); 31 | } 32 | -------------------------------------------------------------------------------- /source/library/core/string/coloredstring.wrl: -------------------------------------------------------------------------------- 1 | module coloredstring 2 | 3 | use core.sentinels.todo 4 | use core.maybe.some 5 | use core.numeric.i32 6 | use string.String 7 | use display.Display 8 | 9 | /// A String intended for display on a console or terminal, 10 | /// that may have custom colors and styles applied to it. 11 | public model ColoredString implements Display { 12 | public var text: String 13 | var codes: []i32 14 | 15 | new(text?: String) { 16 | this.text = text.valueOrDefault() 17 | } 18 | 19 | /// Changes the color of the text to blue. 20 | public function blue -> This { 21 | this.codes.push(34) 22 | return this 23 | } 24 | 25 | /// Changes the color of the text to red. 26 | public function red -> This { 27 | this.codes.push(31) 28 | return this 29 | } 30 | 31 | public function [Display.toString] -> String { 32 | todo() 33 | } 34 | 35 | public static function from(value: T) -> This { 36 | var text = value.toString() 37 | return ColoredString(some(text)) 38 | } 39 | } -------------------------------------------------------------------------------- /source/compiler/analyzer/src/typechecker/checker/statements/enum_declaration.rs: -------------------------------------------------------------------------------- 1 | use crate::TypedEnumDeclaration; 2 | 3 | use super::*; 4 | 5 | /// Typechecks an enum and all its variants. 6 | pub fn typecheck_enum_declaration( 7 | enum_: &mut TypedEnumDeclaration, 8 | checker_ctx: &mut TypecheckerContext, 9 | symbollib: &mut SymbolLibrary, 10 | ) { 11 | let enum_symbol = unwrap_or_return!(symbollib.get(enum_.name)); 12 | if let SemanticSymbolKind::Enum { 13 | generic_params, 14 | variants, 15 | .. 16 | } = &enum_symbol.kind 17 | { 18 | typecheck_generic_params(generic_params, symbollib, checker_ctx); 19 | for variant in variants { 20 | let variant_symbol = unwrap_or_continue!(symbollib.get(*variant)); 21 | if let SemanticSymbolKind::Variant { tagged_types, .. } = &variant_symbol.kind { 22 | for typ in tagged_types { 23 | let span = typ.span(); 24 | evaluate(typ, symbollib, None, &mut checker_ctx.tracker(span), 0); 25 | } 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/library/system/fs/fs.wrl: -------------------------------------------------------------------------------- 1 | module fs 2 | 3 | // use core.prelude.String 4 | // use core.prelude.{todo, boolean} 5 | // use core.prelude.Outcome 6 | 7 | 8 | 9 | // // public type PathLike = String | Buffer | Url 10 | // public type FileHandle = i32 11 | 12 | // public model File { 13 | // public async function open { 14 | 15 | // } 16 | // public async function close { 17 | 18 | // } 19 | // } 20 | 21 | // public model FsError { 22 | 23 | // } 24 | 25 | // /// Opens a file. 26 | // public async function open(filepath: PathLike) -> Outcome { 27 | // return todo() 28 | // } 29 | 30 | 31 | // /// Closes an open file. 32 | // public async function close(handle: FileHandle) -> Outcome { 33 | // return todo() 34 | // } 35 | 36 | // // /// Reads a file, given its filename, and returns its contents. 37 | // // public async function readFile(filename: PathLike) -> Outcome { 38 | // // todo() 39 | // // // return Err("") 40 | // // } 41 | 42 | // public async function readToString(filename: PathLike) -> Outcome { 43 | // todo() 44 | // } -------------------------------------------------------------------------------- /source/compiler/lexer/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod lexer_inner; 2 | mod test; 3 | mod text_lexer; 4 | 5 | use ast::{Token, TokenType}; 6 | use errors::LexError; 7 | use lexer_inner::LexerInner; 8 | pub use text_lexer::{lex_text, TextLexer}; 9 | 10 | pub trait Lexer: LexerInner + Iterator { 11 | fn module_id(&self) -> usize; 12 | /// Lazily lexes and provides the next token in a stream. 13 | fn get_next_token(&mut self) -> Option { 14 | self.next_token_inner() 15 | } 16 | /// Bypass comments and invalid tokens in the stream and return the next syntactic token. 17 | fn next_useful_token(&mut self) -> Option { 18 | loop { 19 | if let Some(token) = self.next_token_inner() { 20 | match token._type { 21 | TokenType::Comment(_) | TokenType::Invalid(_) => {} 22 | _ => return Some(token), 23 | } 24 | } else { 25 | return None; 26 | } 27 | } 28 | } 29 | /// Returns an array of the vectors encountered while parsing. 30 | fn errors(&mut self) -> &mut Vec; 31 | } 32 | -------------------------------------------------------------------------------- /source/library/core/memory/ref.wrl: -------------------------------------------------------------------------------- 1 | module ref 2 | 3 | use core.interfaces.Guaranteed 4 | use core.sentinels.todo 5 | 6 | 7 | 8 | /// An intrinsic model that provides a way to hold a weak reference to a value of type `T`. 9 | /// 10 | /// Unlike regular references, weak references don't prevent the underlying 11 | /// value from being dropped by the runtime. This means that if the 12 | /// object is no longer reachable through other strong references, it will 13 | /// be cleared and the WeakRef will point to empty memory. 14 | public model WeakRef implements Guaranteed { 15 | 16 | new(value: T){ 17 | todo() 18 | } 19 | 20 | /// Implementation of the Guaranteed interface. 21 | function [Guaranteed.guarantee] -> T { 22 | this.value() 23 | } 24 | 25 | /// Forcefully retrieves the value in the reference. 26 | /// 27 | /// It is useful when there is a surety that the value 28 | /// contained has not been dropped by the runtime. 29 | public function value -> T { 30 | this.deref().value() 31 | } 32 | 33 | /// Retrieves the value in the reference. 34 | public function deref -> ?T { 35 | todo() 36 | } 37 | } -------------------------------------------------------------------------------- /source/library/system/io/terminal.wrl: -------------------------------------------------------------------------------- 1 | module terminal 2 | 3 | 4 | 5 | import "system" { 6 | /// Takes a String from memory and prints it to the console. 7 | "startPrint" as function startPrint(start: i32, length: i32) 8 | } 9 | 10 | /// Prints a message or an object to the console, terminal 11 | /// or any predefined output interface. 12 | /// ``` 13 | /// use system.io.print 14 | /// print("Hello from the other side!") 15 | /// ``` 16 | public function print(message: M) { 17 | var messageStr = message.toString() 18 | var offset = todo() 19 | var length = messageStr.length() 20 | startPrint(offset, length) 21 | } 22 | 23 | /// Displays an optional message to the console, terminal or 24 | /// any predefined input interface, prompting the user to enter 25 | /// text. 26 | public function prompt(message?: String) -> String { 27 | todo() 28 | } 29 | 30 | /// Prints a message or an object to the console, terminal, 31 | /// or any predefined output interface. 32 | public function printFmt(message: T) { 33 | var messageStr = message.toFormattedString() 34 | var offset = todo() 35 | var length = messageStr.length() 36 | startPrint(offset, length) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /source/library/system/io/io.wrl: -------------------------------------------------------------------------------- 1 | /// ## Input/Output in Whirlwind 2 | /// 3 | /// The `io` module provides a set of tools for fundamental input/output operations and streams. 4 | /// These tools facilitate reading and writing data to various sources, such as files, 5 | /// network connections, or in-memory buffers. 6 | /// 7 | /// It abstracts the underlying details of the data source, allowing 8 | /// for a consistent interface. 9 | module io 10 | 11 | public use terminal.{print, prompt, printFmt} 12 | 13 | /// This interface is implemented by any entity that can write 14 | /// to streams of data. 15 | public interface Writer { 16 | /// Writes into a byte stream and returns the number of bytes written. 17 | public async function write(bytes: []i32) -> Outcome 18 | } 19 | 20 | /// This interface is implemented by any entity that is capable of 21 | /// reading streams of data. The primary method provided is the 22 | /// `.read` method, which reads bytes into a provided byte array. 23 | public interface Reader { 24 | /// Reads data from a specified source. 25 | public async function read(bytes: []i32, offset?: i32, length?: i32) 26 | } 27 | 28 | public model IoError { 29 | var errorType: IoErrorType 30 | } 31 | enum IoErrorType {} 32 | 33 | -------------------------------------------------------------------------------- /source/library/core/interfaces/default.wrl: -------------------------------------------------------------------------------- 1 | module default 2 | 3 | /// This is a language level interface to be implemented by types that can have default values when they are not initialized. 4 | /// ## Usage 5 | /// Suppose there is a `Position` model that, if not set, should start at the origin (0, 0). A non implemented approach would be: 6 | /// ``` 7 | /// model Position { 8 | /// public var x: number 9 | /// public var y: number 10 | /// new(x: number, y: number) { 11 | /// this.x = x 12 | /// this.y = y 13 | /// } 14 | /// public static function origin -> This { 15 | /// Position(0, 0) 16 | /// } 17 | /// } 18 | /// var p1 = Position.origin() 19 | /// ``` 20 | /// Using the Default interface, it would instead be: 21 | /// ``` 22 | /// model Position implements Default { 23 | /// public var x: number 24 | /// public var y: number 25 | /// new(x: number, y: number) { 26 | /// this.x = x 27 | /// this.y = y 28 | /// } 29 | /// static function [Default.init] -> This { 30 | /// Position(0, 0) 31 | /// } 32 | /// } 33 | /// var p1: Position /// Is automatically assigned (0, 0) 34 | public interface Default { 35 | /// Returns the default instance of the implementing model. 36 | static function init -> This 37 | } -------------------------------------------------------------------------------- /source/library/core/string/display.wrl: -------------------------------------------------------------------------------- 1 | module display 2 | 3 | use string.String 4 | 5 | /// The `Display` interface defines a contract for the conversion of models into String representations. 6 | /// This allows objects of different types to be seamlessly integrated into textual contexts, 7 | /// particularly for user-facing interfaces. 8 | /// 9 | /// ## Usage 10 | /// ``` 11 | /// // Implementing Display on a model. 12 | /// model Rectangle implements Display { 13 | /// var length: i32 14 | /// var width: i32 15 | /// 16 | /// new(length: i32, width: i32) { 17 | /// this.length = length 18 | /// this.width = width 19 | /// } 20 | /// 21 | /// public function [Display.toString] -> String { 22 | /// var tmpl = String.fmt("Rectangle { length: %, breadth: % }") 23 | /// return tmpl.add(this.length).add(this.breadth).finish() 24 | /// } 25 | /// } 26 | /// 27 | /// Rectangle(4, 6).toString() // outputs "Rectangle { length: 4, breadth: 6 }"". 28 | /// ``` 29 | public interface Display { 30 | /// Converts the implementing type to a String. 31 | public function toString -> String 32 | /// Converts the implementing type to a formatted String. 33 | public function toFormattedString -> String { 34 | return this.toString() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/app.wrl: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | public use state; 4 | public use styling; 5 | public use views; 6 | public use utils; 7 | 8 | // use tangent.thread; 9 | 10 | function main() { 11 | 12 | } 13 | 14 | // use tangent.{ 15 | // Button, State, StateStore, Root, Span, View 16 | // }; 17 | 18 | // type ClientState implements StateStore { 19 | // public var count: State 20 | // new() { 21 | // this.count = new State(0); 22 | // this.shouldDisplayButton = new State(false); 23 | // } 24 | // } 25 | 26 | // function createRoot(state: ClientState) -> Root { 27 | // t.root() 28 | // .style(styles.Root) 29 | // .id("app") 30 | // .class("window") 31 | // .append( 32 | // t.button() 33 | // .style(styles.Button) 34 | // .append(State.asText(state.count)) 35 | // .click(fn state.count.update(fn (value) value + 1)) 36 | // .hover(fn(_, btn) btn!.setStyle("color", "red")), 37 | // ) 38 | // .append( 39 | // t.span() 40 | // .style(styles.Span) 41 | // .text("Click the button.") 42 | // ); 43 | // } 44 | 45 | // function main() { 46 | // state := new ClientState(); 47 | // view := new View(createRoot(state)); 48 | // } -------------------------------------------------------------------------------- /source/server/src/message_store.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Debug, Display}; 2 | 3 | use tower_lsp::lsp_types::MessageType; 4 | 5 | /// A list of messages to be logged to the server's client whenever the async shenanigans allows it. 6 | #[derive(Default)] 7 | pub struct MessageStore { 8 | messages: Vec<(MessageType, String)>, 9 | } 10 | 11 | pub type WithMessages = (MessageStore, T); 12 | 13 | impl MessageStore { 14 | /// Creates a new message store. 15 | pub fn new() -> Self { 16 | Self { messages: vec![] } 17 | } 18 | 19 | /// Adds a netral message to the store. 20 | pub fn inform(&mut self, message: T) { 21 | self.messages.push((MessageType::INFO, message.to_string())) 22 | } 23 | 24 | /// Adds an error to the store. 25 | pub fn error(&mut self, message: T) { 26 | self.messages 27 | .push((MessageType::ERROR, message.to_string())) 28 | } 29 | 30 | pub fn _debug(&mut self, message: T) { 31 | self.messages 32 | .push((MessageType::ERROR, format!("{message:?}"))) 33 | } 34 | } 35 | 36 | impl IntoIterator for MessageStore { 37 | type Item = (MessageType, String); 38 | 39 | type IntoIter = std::vec::IntoIter; 40 | 41 | fn into_iter(self) -> Self::IntoIter { 42 | self.messages.into_iter() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /assets/herald.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /source/library/system/net/http/response.wrl: -------------------------------------------------------------------------------- 1 | module response 2 | use request.HttpRequest 3 | 4 | use core.prelude.{String, i32, todo} 5 | use core.data.json.JsonValue 6 | 7 | /// The headers on a 8 | public model ResponseHeaders { 9 | public function set(key: String, value: String) { 10 | todo() 11 | } 12 | public function get(key: String) -> ?String { 13 | todo() 14 | } 15 | } 16 | 17 | public model HttpResponse { 18 | var _status: i32 19 | var headers: ResponseHeaders 20 | var data: String 21 | /// Build a response object from a request. 22 | public static function from(request: HttpRequest) -> This { 23 | return todo() 24 | } 25 | /// Returns the content type set in the response header. 26 | public function contentType -> ?String { 27 | this.headers.get("Content-Type") 28 | } 29 | /// Sets the status code value of the response. 30 | public function status(code: i32) -> This { 31 | this._status = code 32 | return this 33 | } 34 | /// Sets the response type to HTML Text. 35 | public function html(String: String) -> This { 36 | this.headers.set("Content-Type", "text/html") 37 | this.data = String 38 | return this 39 | } 40 | public function json(json: J) -> This { 41 | this.headers.set("Content-Type", "application/json") 42 | return this 43 | } 44 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **THIS PROJECT IS 55% INCOMPLETE. LOOK AWAY.** 2 | 3 |

4 | Whirlwind Icon 5 |

Whirlwind.

6 |

7 | 8 | Whirlwind is an open-source, statically-typed programming language for quickly and easily writing maintainable software. It is designed to be practical, easy to read and efficient. 9 | 10 | ## Documentation 📑 11 | 12 | The [Whirlwind website](http://whirlwind-lang.vercel.app) is still pending. For more information relating to the source code, refer to the [docs](https://github.com/adebola-io/whirlwind/tree/master/docs) folder. 13 | 14 | ## Features ✨ 15 | 16 | - First Class Functions. 17 | - Static, Nominal Typing System. 18 | - Support for Generic Programming. 19 | - Support for Union Types. 20 | - Inbuilt Testing capabilities. 21 | - Modular organization. 22 | 23 | ## Syntax 📐 24 | 25 | Whirlwind is inspired heavily by the syntax of already existing languages. An example of Whirlwind code is: 26 | 27 | ```ts 28 | /// Function to add two numbers. 29 | function add(a: i32, b: i32) -> i32 { 30 | return a + b 31 | } 32 | 33 | /// Entry to the program. 34 | function main() { 35 | add(2, 3) 36 | } 37 | ``` 38 | 39 | ## Roadmap and Implementation Status 🛠️ 40 | 41 | To view the planned and implemented features in the development of Whirlwind, please refer to the [roadmap](./docs/roadmap). 42 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/constant.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fmt::Display}; 2 | 3 | /// A smart list of unique constant values. 4 | #[derive(Debug)] 5 | pub struct ConstantPool { 6 | pub list: Vec, 7 | /// The router redirects LiteralIndex requests to the new unique values. 8 | pub router: HashMap, 9 | } 10 | 11 | impl ConstantPool { 12 | /// Creates a new constant list. 13 | pub fn new() -> Self { 14 | Self { 15 | list: vec![], 16 | router: HashMap::new(), 17 | } 18 | } 19 | /// Adds a new constant and return its index. 20 | // TODO: Interning. 21 | pub fn add>(&mut self, constant: T) -> usize { 22 | let constant = constant.into(); 23 | let index = self.list.len(); 24 | self.list.push(constant); 25 | return index; 26 | } 27 | } 28 | 29 | #[derive(Debug)] 30 | pub enum Constant { 31 | String(String), 32 | Number(f64), 33 | Bool(bool), 34 | } 35 | 36 | impl Display for Constant { 37 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 38 | match self { 39 | Constant::String(s) => write!(f, "{s}"), 40 | Constant::Number(n) => write!(f, "{n}"), 41 | Constant::Bool(b) => write!(f, "{b}"), 42 | } 43 | } 44 | } 45 | 46 | impl From for Constant { 47 | fn from(value: String) -> Self { 48 | Constant::String(value) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/ntuple.wrl: -------------------------------------------------------------------------------- 1 | module ntuple 2 | 3 | use core.internals.never 4 | use core.maybe.none 5 | 6 | public use quadruple.Quadruple 7 | public use quintuple.Quintuple 8 | public use sextuple.Sextuple 9 | public use tuple.{pair, Tuple} 10 | public use triple.Triple 11 | 12 | /// A representation of any collection of elements that maintain a specific order, 13 | /// and can have different types. 14 | /// 15 | /// The number of elements in an `NTuple` implementation can vary, depending 16 | /// on the implementor, but the maximum is six, chosen arbritrarily. 17 | public interface NTuple< 18 | T = never, 19 | U = never, 20 | V = never, 21 | W = never, 22 | X = never, 23 | Y = never 24 | > 25 | { 26 | /// Returns the first item in the set. 27 | public function first -> ?T { 28 | return none() 29 | } 30 | /// Returns the second item in the set. 31 | public function second -> ?U { 32 | return none() 33 | } 34 | /// Returns the third item in the set. 35 | public function third -> ?V { 36 | return none() 37 | } 38 | /// Returns the fourth item in the set. 39 | public function fourth -> ?W { 40 | return none() 41 | } 42 | /// Returns the fifth item in the set. 43 | public function fifth -> ?X { 44 | return none() 45 | } 46 | /// Returns the sixth item in the set. 47 | public function sixth -> ?Y { 48 | return none() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/registers.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fmt::{Debug, Display}, 3 | sync::{Arc, Mutex}, 4 | }; 5 | 6 | use analyzer::SymbolIndex; 7 | 8 | #[derive(Clone, Copy)] 9 | pub struct Register(pub f64); 10 | 11 | /// The smallest representation of a stored value in Whirlwind. 12 | /// 13 | /// The smallest value created by the runtime will have 14 | /// a size of 24 bytes, consequentially. `[sad trumpet noise.]` 15 | /// 16 | /// Forgiveness is requested. It will be optimized later. 17 | #[derive(Debug, Default, Clone)] 18 | pub enum StackValue { 19 | HeapPointer(HeapPointer), 20 | Number(f64), 21 | Boolean(bool), 22 | Constant(usize), 23 | Function(usize), 24 | #[default] 25 | None, 26 | } 27 | 28 | impl Display for StackValue { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | write!( 31 | f, 32 | "{}", 33 | match self { 34 | StackValue::HeapPointer(_) => String::from("HeapPointer"), 35 | StackValue::Number(num) => num.to_string(), 36 | _ => String::from("None"), 37 | } 38 | ) 39 | } 40 | } 41 | impl From for StackValue { 42 | fn from(value: u8) -> Self { 43 | StackValue::Number(value as f64) 44 | } 45 | } 46 | 47 | #[derive(Debug)] 48 | pub struct HeapPointer(pub Arc>>); 49 | impl Clone for HeapPointer { 50 | fn clone(&self) -> Self { 51 | Self(self.0.clone()) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/library/core/concurrent/concurrent.wrl: -------------------------------------------------------------------------------- 1 | /// ## Async Whirlwind. 2 | /// The `concurrent` module contains all primitive constructs in Whirlwind 3 | /// for the creation and utilization of asynchronous processes. 4 | module concurrent 5 | 6 | use core.{ 7 | numeric.i32, 8 | string.String, 9 | sentinels.todo 10 | } 11 | public use eventual.Eventual 12 | public use prospect.Prospect 13 | 14 | /// Runs a prospect immediately, blocking the current execution sequence until it is finished. 15 | /// 16 | /// ### Usage 17 | /// ``` 18 | /// async function asyncSquare(a: i32) -> i32 { 19 | /// return a * a 20 | /// } 21 | /// var num = toSync(asyncSquare(2)) 22 | /// assert(num).equals(4) // 2 * 2. 23 | /// ``` 24 | /// It is the same as running `.run().await()` on the prospect (Its implementation is quite literally that). 25 | public function toSync(prospect: Prospect) -> T { 26 | return prospect.run().await() 27 | } 28 | 29 | /// The current running sequence for an instruction. 30 | public model CurrentSequence { 31 | var handle: Sequence 32 | /// Forces the current running sequence to halt for a duration. 33 | public static function sleep(timeout: i32) { 34 | todo() 35 | } 36 | } 37 | 38 | /// A running set of instructions. 39 | public model Sequence {} 40 | 41 | public model SequenceError { 42 | public var errorType: SequenceErrorType 43 | public var message: ?String 44 | } 45 | 46 | public enum SequenceErrorType { 47 | StackOverflow, 48 | CastingError, 49 | FatalError, 50 | } -------------------------------------------------------------------------------- /source/library/core/interfaces/try.wrl: -------------------------------------------------------------------------------- 1 | module try 2 | 3 | use core.prelude.Outcome 4 | 5 | /// The Try interface allows usage of the `?` operator on expressions 6 | /// that may have uncertain or error-prone values. It propagates the 7 | /// empty or error value if the expression has one, or returns the valid value 8 | /// otherwise. 9 | /// 10 | /// The interface is implemented by the `Maybe` and `Outcome` models. 11 | /// It has a requirement that the enclosing function must have a return type 12 | /// equal or unifiable to the type of the expression. 13 | /// 14 | /// For example, here are two approaches to doubling a stringified integer: 15 | /// 16 | /// #### First Approach: 17 | /// ``` 18 | /// function parseAndDouble1(numberStr: String) -> Maybe { 19 | /// var maybeInteger = i32.fromString(numberStr) 20 | /// if maybeInteger.isNone() { 21 | /// return none() 22 | /// } 23 | /// var integer = maybeInteger.value() 24 | /// return some(integer * integer) 25 | /// } 26 | /// ``` 27 | /// #### Second Approach: 28 | /// ``` 29 | /// function parseAndDouble2(numberStr: String) -> Maybe { 30 | /// // i32.fromString returns a Maybe. If the integer cannot be 31 | /// // parsed, the none() value gotten is immediately 32 | /// // returned from this function. If the parsing is 33 | /// // successful, then the value is passed to the variable 34 | /// // "integer". 35 | /// var integer = i32.fromString(numberStr)? 36 | /// return some(integer * integer) 37 | /// } 38 | /// ``` 39 | public interface Try { 40 | function try -> Outcome 41 | } -------------------------------------------------------------------------------- /source/website/pages/index.scss: -------------------------------------------------------------------------------- 1 | @import '../scss/app'; 2 | 3 | body { 4 | background-color: #131313; 5 | color: #eaeaea; 6 | font-family: 'Sedan', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 7 | margin: 0; 8 | } 9 | .waiting-page { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | flex-direction: column; 14 | height: 100vh; 15 | padding: 0 relative-width(30); 16 | background-image: url(../assets/HeavenStream.svg); 17 | background-size: 250%; 18 | background-position: center; 19 | @media (aspect-ratio >= 0.8) { 20 | background-repeat: repeat; 21 | background-size: 160%; 22 | } 23 | 24 | &__icon { 25 | $size: relative-width(101.6); 26 | width: $size; 27 | height: $size; 28 | animation: slide_up 500ms; 29 | @media (aspect-ratio >= 0.8) { 30 | display: none; 31 | } 32 | } 33 | 34 | &__heading { 35 | font-size: relative-width(39.8); 36 | @media (aspect-ratio >= 0.8) { 37 | animation: slide_up 500ms; 38 | font-size: 4.5rem; 39 | } 40 | font-weight: normal; 41 | } 42 | 43 | p { 44 | font-family: 'Averia Serif Libre', 'Segoe UI', Tahoma, Geneva, Verdana, 45 | sans-serif; 46 | font-size: relative-width(14); 47 | @media (aspect-ratio >= 0.8) { 48 | width: 80%; 49 | font-size: 15pt; 50 | text-align: center; 51 | } 52 | } 53 | 54 | &__greeting { 55 | width: 100%; 56 | } 57 | 58 | &__explanation { 59 | text-align: right; 60 | 61 | &__link { 62 | text-decoration: none; 63 | color: #a4facc; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | mod constant; 3 | mod disassembler; 4 | mod from_text; 5 | mod generator; 6 | mod injunction; 7 | mod instruction; 8 | mod retriever; 9 | #[cfg(test)] 10 | mod tests; 11 | mod reader; 12 | 13 | use analyzer::{ 14 | PathIndex, ScopeId, SemanticSymbolKind, Standpoint, SymbolIndex, TypedFunctionDeclaration, 15 | }; 16 | pub use constant::*; 17 | use errors::BytecodeError; 18 | pub use generator::*; 19 | pub use injunction::*; 20 | pub use instruction::*; 21 | pub use retriever::*; 22 | 23 | /// Generates a list of instructions and data from a standpoint. 24 | pub fn generate_from(standpoint: &Standpoint) -> Result { 25 | let main = standpoint.main(); 26 | let main = match main { 27 | Some(main) => main, 28 | None => return Err(BytecodeError::MainNotFound), 29 | }; 30 | let main_symbol = match standpoint.symbol_library.get(main.name) { 31 | Some(main) => main, 32 | None => return Err(BytecodeError::MainNotFound), 33 | }; 34 | if let SemanticSymbolKind::Function { 35 | is_public, 36 | is_async, 37 | params, 38 | generic_params, 39 | return_type, 40 | } = &main_symbol.kind 41 | { 42 | if *is_async { 43 | return Err(BytecodeError::MainIsAsync); 44 | } 45 | if return_type.is_some() { 46 | return Err(BytecodeError::MainReturns); 47 | } 48 | if !params.is_empty() { 49 | return Err(BytecodeError::MainHasParameters); 50 | } 51 | } 52 | let mut generator = BytecodeGenerator::from(standpoint); 53 | let object = generator.generate()?; 54 | Ok(object) 55 | } 56 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/representations/astrepr.rs: -------------------------------------------------------------------------------- 1 | use ast::{ModuleAmbience, Statement}; 2 | use errors::{LexError, ParseError}; 3 | use lexer::Lexer; 4 | use parser::parse_text; 5 | use std::cell::RefCell; 6 | 7 | pub struct Ast { 8 | pub ambience: RefCell, 9 | pub statements: Vec, 10 | pub lexical_errors: Vec, 11 | pub syntax_errors: Vec, 12 | pub line_lengths: Vec, 13 | } 14 | 15 | impl Ast { 16 | /// Returns the ast ambience. 17 | pub fn ambience(&self) -> &mut ModuleAmbience { 18 | unsafe { &mut *(self.ambience.as_ptr()) } 19 | } 20 | /// lex, parse and build an ast from text. 21 | pub fn from_text(input: &str) -> Ast { 22 | let mut parser = parse_text(input); 23 | let mut syntax_errors = vec![]; 24 | let mut statements = vec![]; 25 | loop { 26 | match parser.next() { 27 | Some(mut partial) => { 28 | syntax_errors.append(&mut partial.errors); 29 | if let Some(statement) = partial.value { 30 | statements.push(statement); 31 | } 32 | } 33 | None => break, 34 | } 35 | } 36 | 37 | let ambience = RefCell::new(std::mem::take(parser.module_ambience())); 38 | let lexical_errors = std::mem::take(parser.lexer.borrow_mut().errors()); 39 | let line_lengths = std::mem::take(&mut parser.lexer.borrow_mut().line_lengths); 40 | 41 | Ast { 42 | statements, 43 | lexical_errors, 44 | syntax_errors, 45 | ambience, 46 | line_lengths, 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/source/app/utils/css.wrl: -------------------------------------------------------------------------------- 1 | module css; 2 | 3 | use core.collections.HashMap; 4 | 5 | /// Encapsulates CSS styles for an element, providing methods for 6 | /// manipulation and application. 7 | public type CSSStyle { 8 | var properties: HashMap 9 | new() { 10 | this.properties = new HashMap(); 11 | } 12 | public function display(value: String) -> This { 13 | return this; 14 | } 15 | public function background(value: String) -> This { 16 | return this; 17 | } 18 | public function backgroundColor(value: String) -> This { 19 | return this; 20 | } 21 | public function justifyContent(value: String) -> This { 22 | return this; 23 | } 24 | public function alignItems(value: String) -> This { 25 | return this; 26 | } 27 | public function transitionDuration(value: String) -> This { 28 | return this; 29 | } 30 | public function textTransform(value: String) -> This { 31 | return this; 32 | } 33 | public function textDecoration(value: String) -> This { 34 | return this; 35 | } 36 | public function borderRadius(value: String) -> This { 37 | return this; 38 | } 39 | public function position(value: String) -> This { 40 | return this; 41 | } 42 | public function height(value: String) -> This { 43 | return this; 44 | } 45 | public function width(value: String) -> This { 46 | return this; 47 | } 48 | public function color(value: String) -> This { 49 | return this; 50 | } 51 | } 52 | 53 | /// Generates a new CSS style instance. 54 | public function css() -> CSSStyle { 55 | return new CSSStyle(); 56 | } -------------------------------------------------------------------------------- /source/library/core/prelude/prelude.wrl: -------------------------------------------------------------------------------- 1 | /// ## The Whirlwind Prelude 2 | /// 3 | /// The `core.prelude` module is a core module that exposes a set of 4 | /// fundamental types, functions and models, automatically available 5 | /// in the global namespace of every other module. 6 | /// 7 | /// These functionalities are considered essential for writing clear, 8 | /// efficient code, and would be tedious to import again and again 9 | /// into other modules. Having a `prelude` reduces boilerplate 10 | /// by predefining these values, increasing code readability, 11 | /// consistency and productivity. 12 | /// 13 | /// ### Note 14 | /// Technically, the prelude is only available _outside_ the core library. 15 | /// The importing rules in Whirlwind are convoluted, and there are several 16 | /// core modules with cyclical dependencies.e.g. `core.io` needs the `Display` 17 | /// interface from `core.prelude`, but `core.prelude` needs the 18 | /// `print` function from `core.io`. 19 | /// 20 | /// Allowing a global scope within `core` would leads to ominous, disappearing errors, 21 | /// because depending on the import resolution path at build time, `core.prelude` might 22 | /// not yet exist. 23 | module prelude 24 | 25 | public use core 26 | public use core.{ 27 | array.Array, 28 | boolean.boolean, 29 | collections.{HashMap, Set, Tuple, pair}, 30 | concurrent.{Eventual, Prospect, toSync}, 31 | interfaces.{ 32 | Default, Guaranteed, Try 33 | }, 34 | iterate.{Iterable, AsIterator}, 35 | maybe.{none, some, Maybe}, 36 | numeric.{number, i32, int, float, i64, f32, f64}, 37 | outcome.{ok, err, Outcome}, 38 | ops.Range, 39 | sentinels.{todo, fatal, unreachable}, 40 | string.{Display, FromStr, String}, 41 | internals.never, 42 | } 43 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/instruction/opcode/generateOpcodeImpl.js: -------------------------------------------------------------------------------- 1 | // Node.js script to generate conversions between opcodes and bytes 2 | // and spacing for the disassembler. 3 | 4 | const fs = require("fs"); 5 | 6 | const data = fs.readFileSync(__dirname + "/opcode.txt").toString(); 7 | const codes = data 8 | .split(/\n/) 9 | .slice(1, -1) 10 | .map((line) => line.trim()) 11 | .filter((line) => !(line.startsWith("//") || line == "")) 12 | .map((line) => line.slice(0, -1)); 13 | 14 | let toByte = ` 15 | impl From for u8 { 16 | fn from(value: Opcode) -> Self { 17 | match value { 18 | ${codes 19 | .map((code, index) => { 20 | return `Opcode::${code} => ${index},\n`; 21 | }) 22 | .join("")} 23 | } 24 | } 25 | } 26 | `; 27 | 28 | let toCode = ` 29 | impl From for Opcode { 30 | fn from(value: u8) -> Self { 31 | match value { 32 | ${codes 33 | .map((code, index) => { 34 | return `${index} => Self::${code},\n`; 35 | }) 36 | .join("")} 37 | _ => panic!("Undefined opcode conversion. No opcode maps to value {value}.") 38 | } 39 | } 40 | } 41 | `; 42 | // console.log(toByte + "\n" + toCode); 43 | 44 | let longestOpcode = codes.reduce((acc, value) => 45 | value.length > acc.length ? value : acc 46 | ); 47 | 48 | let final = ` 49 | ///! This module is auto generated. 50 | use super::Opcode; 51 | 52 | /// The formatting spacing between opcode and operands for the disassembler output. 53 | pub const DISASSEMBLER_SPACING: usize = ${longestOpcode.length + 1}; 54 | 55 | ${toByte} 56 | 57 | ${toCode} 58 | `; 59 | 60 | fs.writeFileSync(__dirname + "/opcode_impl.rs", final); 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@whirlwind/core", 3 | "description": "A programming language for clever computing.", 4 | "type": "module", 5 | "scripts": { 6 | "build:ls:debug": "echo -e '\\033[34mGenerating Language Server Binary in Debug Mode...\\033[0m' && cd source/server && cargo build", 7 | "build:ls:release": "echo -e '\\033[34mGenerating Language Server Binary in Release Mode...\\033[0m' && cd source/server && cargo build --release", 8 | "build:cli:debug": "echo -e '\\033[34mGenerating CLI Binary in Debug Mode...\\033[0m' && cd source/cli && cargo build", 9 | "build:cli:release": "echo -e '\\033[34mGenerating CLI Binary in Release Mode...\\033[0m' && cd source/cli && cargo build --release", 10 | "build:site": "echo -e '\\033[34mBuilding Whirlwind Docs Site...\\033[0m' && vite build", 11 | "dev:site:server": "bun run --hot source/website/server/index.ts", 12 | "dev:site:web": "vite dev", 13 | "publish:extensions:code": "echo 'Publishing the VSCode extension...'", 14 | "cli:example": "echo 'Running CLI example...' && cd source/cli && cargo run run ../../examples/hello-world/source/main.wrl --CORELIBPATH=../library/core/core.wrl", 15 | "herald:node": "echo 'Running Herald in Node.js...' && cd source/herald && node source/mod.mjs", 16 | "herald:deno": "echo 'Running Herald in Deno...' && cd source/herald && deno run --allow-read source/mod.mjs", 17 | "herald:bun": "echo 'Running Herald in Bun...' && cd source/herald && bun run source/mod.mjs" 18 | }, 19 | "devDependencies": { 20 | "sass": "^1.72.0", 21 | "@types/bun": "latest", 22 | "@types/node": "^20.11.24" 23 | }, 24 | "peerDependencies": { 25 | "typescript": "^5.0.0" 26 | }, 27 | "dependencies": { 28 | "vite": "^5.2.3", 29 | "hono": "^4.1.3" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /assets/Icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /source/website/assets/Icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | -------------------------------------------------------------------------------- /source/library/core/internals/never.wrl: -------------------------------------------------------------------------------- 1 | module never 2 | 3 | /// ### The Never Type 4 | /// `never` is the type representing the set of values that _cannot_ occur. 5 | /// 6 | /// Examples of never types are: 7 | /// - the return value of a function that exits the program, 8 | /// - the type of an exception that crashes sequence execution, or 9 | /// - self referential infinitely recursive types (e.g. `type T = T`). 10 | /// 11 | /// The exact definition is that it is the type of a value that cannot be 12 | /// modelled by the type system. Because it is a placeholder for something conceptual, 13 | /// _the `never` type is assignable to all types, including itself._ 14 | /// This makes it useful for writing unfinished code that compiles because 15 | /// it satisfies the typechecker. 16 | /// 17 | /// As a result, the following are all semantically valid: 18 | /// 19 | /// ``` 20 | /// function blackHole -> never { 21 | /// core.sentinels.exit(1) // Exits the program and never returns. 22 | /// } 23 | /// var num: i32 = blackHole() // valid. 24 | /// var str: String = blackHole() // valid. 25 | /// 26 | /// function produceValue -> String { 27 | /// return blackHole() // valid. 28 | /// } 29 | /// ``` 30 | /// However, due to its abstract nature, _no other type is assignable to `never`, except itself._ 31 | /// This means that the following statements will produce errors. 32 | /// 33 | /// ``` 34 | /// var neverNum: never = 0 // will not compile. 35 | /// type Recursive = Recursive 36 | /// var neverBool: Recursive = true // will not compile. 37 | /// 38 | /// function outsideReality -> never { 39 | /// return "Hello, world" // will not compile. 40 | /// } 41 | /// ``` 42 | /// As a matter of fact, `never` types are preemptively disallowed in variable type labels. 43 | /// They are only allowed in return types for functions. 44 | public type never = never 45 | -------------------------------------------------------------------------------- /source/extensions/vscode/images/wrl_color.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /.discontinued/examples/randoms/config.wrl: -------------------------------------------------------------------------------- 1 | /// From https://github.com/aerogo/aero/blob/master/Configuration.go 2 | module config; 3 | 4 | use core.time.{Duration, Millisecond, Second}; 5 | use core.fs; 6 | 7 | /// Configuration represents the data in your config.json file. 8 | public type Configuration { 9 | public var push: []String; 10 | public var gzip: Bool; 11 | public var ports: PortConfiguration; 12 | public var timeouts: TimeoutConfiguration; 13 | 14 | new() { 15 | this.push = []; 16 | this.gzip = true; 17 | this.ports = new PortConfiguration(); 18 | this.timeouts = new TimeoutConfiguration(); 19 | } 20 | 21 | public function reset { 22 | this = new Configuration(); 23 | } 24 | } 25 | /// PortConfiguration lets you configure the ports that Aero will listen on. 26 | public type PortConfiguration { 27 | public var HTTP: Int; 28 | public var HTTPS: Int; 29 | 30 | new() { 31 | this.HTTP = 4000; 32 | this.HTTPS = 4001; 33 | } 34 | } 35 | 36 | /// TimeoutConfiguration lets you configure the different timeout durations. 37 | public type TimeoutConfiguration { 38 | public var idle: Duration; 39 | public var readHeader: Duration; 40 | public var write: Duration; 41 | public var shutdown: Duration; 42 | 43 | new() { 44 | this.idle = Second.times(180); 45 | this.write = Second.times(120); 46 | this.readHeader = Second.times(5); 47 | this.shutdown = Millisecond.times(250); 48 | } 49 | } 50 | 51 | public function loadConfig(path: String) -> Outcome { 52 | file := fs.open(path).run().await()?; 53 | 54 | json := new json.Decoder(file).readToEnd()?.asObject(); 55 | 56 | configuration := Configuration.fromJsonDecoder(decoder)?; 57 | 58 | file.close(); 59 | todo() 60 | } -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/quintuple.wrl: -------------------------------------------------------------------------------- 1 | module quintuple 2 | 3 | use core.maybe.some 4 | use core.string.{Display, String} 5 | use ntuple.NTuple 6 | 7 | 8 | /// A set containing five individual items. 9 | public model Quintuple 10 | implements Display|=( 11 | T implements Display 12 | and U implements Display 13 | and V implements Display 14 | and W implements Display 15 | and X implements Display 16 | ) + NTuple 17 | { 18 | public var a: T 19 | public var b: U 20 | public var c: V 21 | public var d: W 22 | public var e: X 23 | 24 | new(first: T, second: U, third: V, fourth: W, fifth: X) { 25 | this.a = first 26 | this.b = second 27 | this.c = third 28 | this.d = fourth 29 | this.e = fifth 30 | } 31 | 32 | public function [Display.toString]|=( 33 | T implements Display 34 | and U implements Display 35 | and V implements Display 36 | and W implements Display 37 | and X implements Display 38 | ) -> String 39 | { 40 | return String.fmt("Quintuple(%, %, %, %, %)") 41 | .add(this.a.toString()) 42 | .add(this.b.toString()) 43 | .add(this.c.toString()) 44 | .add(this.d.toString()) 45 | .add(this.e.toString()) 46 | .finish() 47 | } 48 | 49 | public function [NTuple.first] -> ?T { 50 | return some(this.a) 51 | } 52 | public function [NTuple.second] -> ?U { 53 | return some(this.b) 54 | } 55 | public function [NTuple.third] -> ?V { 56 | return some(this.c) 57 | } 58 | public function [NTuple.fourth] -> ?W { 59 | return some(this.d) 60 | } 61 | public function [NTuple.fifth] -> ?X { 62 | return some(this.e) 63 | } 64 | } -------------------------------------------------------------------------------- /.discontinued/examples/randoms/tangent/web.wrl: -------------------------------------------------------------------------------- 1 | module web; 2 | 3 | use tangent.{ 4 | Button, State, StateStore, Root, Span, View 5 | }; 6 | 7 | type ClientState implements StateStore { 8 | public var count: State 9 | new() { 10 | this.count = new State(0); 11 | this.shouldDisplayButton = new State(false); 12 | } 13 | } 14 | 15 | function createRoot(state: ClientState) -> Root { 16 | t.root() 17 | .style(styles.Root) 18 | .id("app") 19 | .class("window") 20 | .append( 21 | t.button() 22 | .style(styles.Button) 23 | .append(State.asText(state.count)) 24 | .click(fn state.count.update(fn (value) value + 1)) 25 | .hover(fn(_, btn) btn!.setStyle("color", "red")), 26 | ) 27 | .append( 28 | t.span() 29 | .style(styles.Span) 30 | .text("Click the button.") 31 | ); 32 | } 33 | 34 | 35 | type styles { 36 | public static function Root() -> Style { 37 | css() 38 | .display("flex") 39 | .justifyContent("center") 40 | .alignItems("center") 41 | .height("100h") 42 | .width("100vw") 43 | .background("#fff000") 44 | } 45 | 46 | public static function Button(): Style { 47 | css() 48 | .display("flex") 49 | .justifyContent("center") 50 | .alignItems("center") 51 | .height("100px") 52 | .width("200px") 53 | .color("blue") 54 | .background("white") 55 | .borderRadius("3rem") 56 | .transitionDuration("300ms") 57 | } 58 | 59 | public static function Span(): Style { 60 | css() 61 | .textTransform("uppercase") 62 | .textDecoration("underline") 63 | } 64 | } 65 | 66 | function main() { 67 | state := new ClientState(); 68 | view := new View(createRoot(state)); 69 | } -------------------------------------------------------------------------------- /.discontinued/examples/frontend/packages/tangent/source/tangent/web.wrl: -------------------------------------------------------------------------------- 1 | module web; 2 | 3 | use tangent.{ 4 | Button, State, StateStore, Root, Span, View 5 | }; 6 | 7 | type ClientState implements StateStore { 8 | public var count: State 9 | new() { 10 | this.count = new State(0); 11 | this.shouldDisplayButton = new State(false); 12 | } 13 | } 14 | 15 | function createRoot(state: ClientState) -> Root { 16 | t.root() 17 | .style(styles.Root) 18 | .id("app") 19 | .class("window") 20 | .append( 21 | t.button() 22 | .style(styles.Button) 23 | .append(State.asText(state.count)) 24 | .click(fn state.count.update(fn (value) value + 1)) 25 | .hover(fn(_, btn) btn!.setStyle("color", "red")), 26 | ) 27 | .append( 28 | t.span() 29 | .style(styles.Span) 30 | .text("Click the button.") 31 | ); 32 | } 33 | 34 | 35 | type styles { 36 | public static function Root() -> Style { 37 | css() 38 | .display("flex") 39 | .justifyContent("center") 40 | .alignItems("center") 41 | .height("100h") 42 | .width("100vw") 43 | .background("#fff000") 44 | } 45 | 46 | public static function Button(): Style { 47 | css() 48 | .display("flex") 49 | .justifyContent("center") 50 | .alignItems("center") 51 | .height("100px") 52 | .width("200px") 53 | .color("blue") 54 | .background("white") 55 | .borderRadius("3rem") 56 | .transitionDuration("300ms") 57 | } 58 | 59 | public static function Span(): Style { 60 | css() 61 | .textTransform("uppercase") 62 | .textDecoration("underline") 63 | } 64 | } 65 | 66 | function main() { 67 | state := new ClientState(); 68 | view := new View(createRoot(state)); 69 | } -------------------------------------------------------------------------------- /source/library/core/boolean/boolean.wrl: -------------------------------------------------------------------------------- 1 | /// Contains the primitive `boolean` model for representing 2 | /// boolean values. 3 | module boolean 4 | 5 | use core.maybe.{none, some} 6 | use core.numeric.i32 7 | use core.interfaces.Default 8 | use core.string.{Display, FromStr, String} 9 | 10 | /// A representation of boolean values, `true` or `false`. 11 | public model boolean 12 | implements Display 13 | + Default 14 | + FromStr 15 | { 16 | static function [Default.init] -> This { 17 | return false 18 | } 19 | 20 | public function [Display.toString] -> String { 21 | if this { "true" } else "false" 22 | } 23 | 24 | /// Returns an integer representation of the boolean 25 | /// (1 for `true`, 0 for `false`) 26 | public function toInt -> i32 { 27 | if this { 1 } else { 0 } 28 | } 29 | 30 | public static function [FromStr.fromString](str: String) -> ?This { 31 | if str == "true" { some(true) } 32 | else if str == "false" { some(false) } 33 | else none() 34 | } 35 | /// Creates a `Maybe` based on the value. 36 | /// ### Usage 37 | /// ``` 38 | /// var operationSuccessful = true 39 | /// var message = fn "Operation successful!" 40 | /// 41 | /// var optionalMessage = operationSuccessful.ifTrue(message) 42 | /// 43 | /// assertThat(optionalMessage.isSome()) 44 | /// ``` 45 | public function ifTrue(consequent: fn -> T) -> ?T { 46 | if this { some(consequent()) } else { none() } 47 | } 48 | 49 | /// Creates a `Maybe` based on the value. 50 | /// ### Usage 51 | /// ``` 52 | /// var operationSuccessful = false 53 | /// var message = fn "Operation failed!" 54 | /// 55 | /// var optionalMessage = operationSuccessful.ifFalse(message) 56 | /// 57 | /// assertThat(optionalMessage.isSome()) 58 | /// ``` 59 | public function ifFalse(consequent: fn -> T) -> ?T { 60 | if !this { some(consequent()) } else { none() } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /source/library/core/interfaces/guaranteed.wrl: -------------------------------------------------------------------------------- 1 | module guaranteed 2 | 3 | /// The Guaranteed interface is a language feature that allows usage of 4 | /// the `!` suffix operator on expressions that may have uncertain or 5 | /// error-prone values. 6 | /// 7 | /// The `!` operator assets that the expression has a valid and expected value, 8 | /// and returns the value without more rigorous checks. If the expression does not 9 | /// have a valid value, the `!` operation should, ideally, cause an error. 10 | /// 11 | /// The interface is implemented by some built-in types, such as `Maybe`, `Outcome` 12 | /// and `Eventual`. For these types, the use of the `!` operator is equivalent to calling 13 | /// the `.value()` method. 14 | /// 15 | /// ``` 16 | /// var name: ?String = some("Alice") 17 | /// var greeting = "Hello, " + name! 18 | /// assert(greeting).equals("Hello, Alice") 19 | /// 20 | /// name = none() 21 | /// name! // causes an error. 22 | /// 23 | /// var age: Outcome = ok(25) 24 | /// var message = "You are " + age! " years old" 25 | /// age = err(Error("Invalid age.")) 26 | /// age! // causes an error. 27 | /// ``` 28 | /// 29 | /// The interface is useful when you are confident that the expression has a valid value, 30 | /// and you want to avoid extra checks (or the extra letters it takes to unwrap). 31 | /// It can be implemented by any custom class that defines the internal 32 | /// `guarantee()` method. For example: 33 | /// ``` 34 | /// model BankAccount implements Guaranteed { 35 | /// var balance: number 36 | /// new(balance: number) { 37 | /// this.balance = balance 38 | /// } 39 | /// function [Guaranteed.guarantee] -> number { 40 | /// if this.balance > 0 { 41 | /// fatal("negative balance.") 42 | /// } 43 | /// } 44 | /// } 45 | /// 46 | /// var acc = BackAccount(100) 47 | /// var invalidAcc = BankAccount(-100) 48 | /// 49 | /// acc! // 100. 50 | /// invalidAcc! // causes an error. 51 | /// ``` 52 | public interface Guaranteed { 53 | function guarantee -> T 54 | } -------------------------------------------------------------------------------- /source/website/pages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Whirlwind | A Language for Efficient Software. 7 | 8 | 9 | 10 | 11 | 12 |
13 | 21 | 29 | 38 | 45 | 46 |

Whirlwind.

47 |

Oh...hi, hello! You're early.

48 |

49 | Thing is, Whirlwind is still (aggressively) being built. No worries, 50 | you'll be the first to see it once it's ready. :)

In the 51 | meantime, you can check out its source, and contribute to its 52 | development 53 | 59 | here. 60 | 61 |

62 |
63 | 64 | 65 | -------------------------------------------------------------------------------- /source/library/malloc.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "env" "memory" (memory 1)) ; Import standard memory 3 | 4 | (type $mem_t (struct (field $ptr i32) (field $size i32))) ; Type representing a block of memory 5 | 6 | (global $heap_base i32 (i32.const 1024)) ; Start of our 'heap' (after initial memory) 7 | (global $last_alloc $mem_t (struct.new $mem_t (i32.const 1024) (i32.const 0))) ; Track last allocation 8 | 9 | (func $malloc (param $size i32) (result $mem_t) 10 | ;; Find a suitable free block 11 | local $current_block $mem_t 12 | local $end i32 13 | (block $search_loop 14 | (loop 15 | (set $current_block (struct.get $last_alloc $mem_t 0)) ; Get pointer to last allocation 16 | (set $end (i32.add $current_block (struct.get $last_alloc $mem_t 1))) ; Calculate block end 17 | 18 | ;; Check if block is free and large enough 19 | (if (and (i32.eq (struct.get $last_alloc $mem_t 1) (i32.const 0)) (i32.ge_u $size (struct.get $last_alloc $mem_t 1))) 20 | (then 21 | ;; Found it! Update allocation status and return. 22 | (struct.set $last_alloc $mem_t 1 $size) ; Mark block as allocated 23 | (return (struct.new $mem_t $current_block $size)) 24 | ) 25 | ) 26 | 27 | ;; Is there more memory we can access? 28 | (if (i32.lt_s (i32.add $end (i32.const 65536)) (memory.size)) ; Check against max 64KB pages 29 | (then 30 | (if (i32.eq (i32.grow_memory (i32.const 1)) (i32.const -1)) ; Attempt to grow by 1 page 31 | (then 32 | (return (struct.new $mem_t (i32.const 0) (i32.const 0))) ; Allocation failed 33 | ) 34 | ) 35 | (set $last_alloc $mem_t (struct.new $mem_t $end (i32.const 0))) ; Set last allocation to the new space 36 | (br $search_loop) ; Try again 37 | ) 38 | (else 39 | (return (struct.new $mem_t (i32.const 0) (i32.const 0))) ; Allocation failed - out of memory 40 | ) 41 | ) 42 | ) 43 | ) 44 | ) 45 | ) 46 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/branstorm.md: -------------------------------------------------------------------------------- 1 | ## Blocking Cyclic Types? 2 | 3 | To simplify reference count in memory management, Whirlwind does not allow cyclic or recursive type types. 4 | This means that: 5 | 6 | ```wrl 7 | type Node { 8 | var parent: Node; 9 | } 10 | ``` 11 | 12 | will not compile. 13 | 14 | ## Arithmetic Operations. 15 | 16 | - Dividing will always yield the type of the two operands. i.e. Int / Int = Int. 17 | 18 | ## Register Allocation. 19 | 20 | There will be 3 registers for every runtime data type in Whirilwind. 21 | 22 | - Boolean registers _boola_, _boolb_ and _boolc_. 23 | - General purpose value registers _vala_, _valb_ and _valc_. 24 | 25 | ## Assignment 26 | 27 | the lhs will take the value in the determined register. 28 | 29 | ## Representation of Opaque Types in Memory 30 | 31 | - An opaque type has the size of its largest possible value. 32 | 33 | ## Creating an Instance. 34 | 35 | - To create an instance of a type: 36 | - Load the arguments, in consecutive order, onto the stack, inside the current frame. 37 | - Create a new call frame, then offset its start index in the stack by the number of arguments, so that the arguments are now values inside the new frame. 38 | - For optional values that have not been initialized, load them as empty Maybe values into the frame. 39 | - Create an instance of the type being created. 40 | - Run the function and store the instance pointer in the ret register. 41 | - Return from the constructor function. 42 | 43 | ## Calling a Named Function. 44 | 45 | - Load the arguments, in consecutive order, onto the stack, inside the current frame. 46 | - Create a new call frame and offset by the number of arguments so that the arguments are within the new frame. 47 | - For optional values that have not been initialized, load them as empty Maybe values into the frame. 48 | - Run the function and store its return value in the ret register. 49 | - Return with [Opcode::Return](./opcode.rs). 50 | 51 | ## Calling a Method. 52 | 53 | - The same as calling a function, but the `this` value is loaded as the first parameter. 54 | 55 | ## Creating an array. 56 | -------------------------------------------------------------------------------- /source/cli/src/help.rs: -------------------------------------------------------------------------------- 1 | use utils::terminal::{Colored, TerminalTable}; 2 | 3 | pub fn print_help() { 4 | let handle = Colored::from("|-").cyan(); 5 | let welcome = Colored::from("🥏 Whirlwind.").cyan().bold(); 6 | let sideline = Colored::from("|>").cyan(); 7 | println!("\n{handle}{welcome}"); 8 | println!("{sideline} Whirlwind is a programming language for creating cool applications."); 9 | blankline(); 10 | println!( 11 | "{sideline} Usage: {} [command] [file.wrl] [arguments]", 12 | Colored::from("whirlwind").italic() 13 | ); 14 | 15 | blankline(); 16 | 17 | let mut commands = TerminalTable::new("Commands:"); 18 | commands 19 | .sideline(&sideline) 20 | .row("build (disabled)", vec!["Build a file to a WASM object."]) 21 | .row("check", vec!["Checks file for syntax and semantic errors."]) 22 | .row("eval (disabled)", vec!["Starts a new repl."]) 23 | .row("format (disabled)", vec!["Formats a file."]) 24 | .row("h, help", vec!["Displays this message."]) 25 | .row("run (disabled)", vec!["Builds and runs a file."]) 26 | .row("test (disabled)", vec!["Runs the test blocks in the file."]) 27 | .row("v, version", vec!["Prints version of runtime installed."]); 28 | println!("{commands}"); 29 | 30 | blankline(); 31 | 32 | let mut args = TerminalTable::new("Arguments:"); 33 | args.sideline(&sideline) 34 | .row("--CORELIBPATH=[path]", vec!["Path to Core Library folder."]) 35 | .row("--OUTDIR=[path]", vec!["Output path for the WASM build."]) 36 | .row( 37 | "--NO-WARNING=[true|false]", 38 | vec!["Disables warning messages."], 39 | ) 40 | .row( 41 | "--NO-SOURCE-LOOKUP=[true|false]", 42 | vec!["Disables error and warning lookup."], 43 | ) 44 | .row( 45 | "--SHOW-ALL-DIAGNOSTICS=[true|false]", 46 | vec!["Shows all diagnostics rather than a max of 30."], 47 | ); 48 | 49 | println!("{args}"); 50 | } 51 | 52 | fn blankline() { 53 | println!("{}", Colored::from("|").cyan()); 54 | } 55 | -------------------------------------------------------------------------------- /examples/algorithms/source/lib.wrl: -------------------------------------------------------------------------------- 1 | module lib 2 | 3 | 4 | /// Implementation of the Fibonacci function 5 | /// ### Usage 6 | /// ``` 7 | /// use algorithms.fibonacci 8 | /// 9 | /// var fifth = fibonacci(5) 10 | /// assert(fifth).equals(5) 11 | /// 12 | /// var tenth = fibonacci(10) 13 | /// assert(tenth).equals(55) 14 | /// 15 | /// var sixth = fibonacci(6) 16 | /// assert(sixth).equals(8) 17 | /// ``` 18 | public function fibonacci(n: i32) -> i32 { 19 | if n == 0 or n == 1 { 20 | return n 21 | } 22 | return fibonacci(n - 1) + fibonacci(n - 2) 23 | } 24 | 25 | /// A function that checks if a string is a palindrome 26 | /// ### Usage 27 | /// ``` 28 | /// use algorithms.isPalindrome 29 | /// 30 | /// var str = "racecar" 31 | /// assertThat(isPalindrome(input)) 32 | /// 33 | /// var str2 = "hello" 34 | /// assertThat(!isPalindrome(input)) 35 | /// ``` 36 | public function isPalindrome(input: String) -> boolean { 37 | return input == input.reverse() 38 | } 39 | 40 | /// Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target. 41 | /// 42 | /// You may assume that each input would have exactly one solution, and you may not use the same element twice. 43 | /// You can return the answer in any order. 44 | /// 45 | /// ## Example 1: 46 | /// Input: nums = `[2, 7, 11, 15]`, target = 9 47 | /// 48 | /// Output: [0, 1] 49 | /// 50 | /// Explanation: Because nums[0] + nums[1] == 9, we return [0, 1]. 51 | /// 52 | /// ## Example 2: 53 | /// Input: nums = `[3, 2, 4]`, target = 6 54 | /// 55 | /// Output: [1, 2] 56 | /// 57 | /// ## Example 3: 58 | /// Input: nums = `[3, 3]`, target = 6 59 | /// 60 | /// Output: [0, 1] 61 | public function twoSum(nums: []i32, target: i32) -> []i32 { 62 | var map = HashMap() 63 | 64 | for { a as i, b as num } in nums.iter().enumerate() { 65 | var complement = target - num; 66 | var maybeSolution: ?i32 = map.get(complement); 67 | if maybeSolution.isSome() { 68 | return [maybeSolution.value(), num] 69 | } 70 | map.set(num, i); 71 | } 72 | core.sentinels.unreachable() 73 | } -------------------------------------------------------------------------------- /.discontinued/examples/randoms/tangent/tangent.wrl: -------------------------------------------------------------------------------- 1 | module tangent; 2 | 3 | use core.collections.HashMap; 4 | 5 | type Style { 6 | var properties: HashMap; 7 | 8 | public function display(value: String) -> This { 9 | return this; 10 | } 11 | public function justifyContent(value: String) -> This { 12 | return this; 13 | } 14 | public function alignItems(value: String) -> This { 15 | return this; 16 | } 17 | public function color(value: String) -> This { 18 | return this; 19 | } 20 | public function borderRadius(value: String) -> This { 21 | return this; 22 | } 23 | public function background(value: String) -> This { 24 | return this; 25 | } 26 | public function height(value: String) -> This { 27 | return this; 28 | } 29 | public function width(value: String) -> This { 30 | return this; 31 | } 32 | public function margin(value: String) -> This { 33 | return this; 34 | } 35 | } 36 | 37 | public type View { 38 | new(root: RootElement) { 39 | this.root = root; 40 | } 41 | } 42 | 43 | type Attributes { 44 | public function set(key: String, value: String) { 45 | todo(); 46 | } 47 | } 48 | 49 | 50 | 51 | public interface Component { 52 | /// Returns the base element of the component. 53 | public function base() -> Element; 54 | 55 | /// Sets the id of the string. 56 | public function id(string: String) -> This { 57 | this.base().setAttribute("id", string); 58 | return this; 59 | } 60 | 61 | /// Sets the class of the string. 62 | public function class(string: String) -> This { 63 | this.base().setAttribute("class", string); 64 | return this; 65 | } 66 | 67 | public function append(child: T) -> This { 68 | this.base().children().push(child); 69 | return this; 70 | } 71 | 72 | public function style(style: Style) -> This { 73 | return this; 74 | } 75 | } 76 | 77 | type Div implements Component { 78 | public function [Component.base]() -> Element { 79 | return this; 80 | } 81 | } 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/literalmap.rs: -------------------------------------------------------------------------------- 1 | use crate::Literal; 2 | use utils::UnorderedMap; 3 | 4 | /// An index into the list of literals in the program. 5 | #[derive(Debug, PartialEq, Clone, Copy)] 6 | pub struct LiteralIndex(pub usize); 7 | 8 | #[derive(Debug)] 9 | pub struct LiteralMap { 10 | values: UnorderedMap, 11 | } 12 | 13 | impl LiteralMap { 14 | /// Create a new literal map. 15 | pub fn new() -> Self { 16 | Self { 17 | values: UnorderedMap::new(), 18 | } 19 | } 20 | /// Returns an iterator over all the literals in the table and their indexes. 21 | pub fn literals(&self) -> impl Iterator { 22 | self.values 23 | .iter() 24 | .enumerate() 25 | .map(|(idx, literal)| (LiteralIndex(idx), literal)) 26 | } 27 | 28 | /// Returns a mutable iterator over all the literals in the table and their indexes. 29 | pub fn literals_mut(&mut self) -> impl Iterator { 30 | self.values 31 | .iter_mut() 32 | .enumerate() 33 | .map(|(idx, literal)| (LiteralIndex(idx), literal)) 34 | } 35 | 36 | /// Reserve an index for a literal to be added later. 37 | pub fn reserve_index(&mut self) -> LiteralIndex { 38 | LiteralIndex(self.values.reserve()) 39 | } 40 | 41 | /// Add a literal to the table and return its index number. 42 | pub fn add(&mut self, literal: Literal) -> LiteralIndex { 43 | LiteralIndex(self.values.insert(literal)) 44 | } 45 | /// Returns the literal at an index. 46 | pub fn get(&self, index: LiteralIndex) -> Option<&Literal> { 47 | self.values.get(index.0) 48 | } 49 | /// Returns the literal at an index. 50 | pub fn get_mut(&mut self, index: LiteralIndex) -> Option<&mut Literal> { 51 | self.values.get_mut(index.0) 52 | } 53 | /// Remove a literal using its index. 54 | pub fn remove(&mut self, index: LiteralIndex) -> Option { 55 | self.values.remove(index.0) 56 | } 57 | /// Returns the number of literals in the table. 58 | pub fn len(&self) -> usize { 59 | self.values.len() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /.discontinued/examples/frontend/packages/tangent/source/tangent/tangent.wrl: -------------------------------------------------------------------------------- 1 | module tangent; 2 | 3 | use core.collections.HashMap; 4 | 5 | type Style { 6 | var properties: HashMap; 7 | 8 | public function display(value: String) -> This { 9 | return this; 10 | } 11 | public function justifyContent(value: String) -> This { 12 | return this; 13 | } 14 | public function alignItems(value: String) -> This { 15 | return this; 16 | } 17 | public function color(value: String) -> This { 18 | return this; 19 | } 20 | public function borderRadius(value: String) -> This { 21 | return this; 22 | } 23 | public function background(value: String) -> This { 24 | return this; 25 | } 26 | public function height(value: String) -> This { 27 | return this; 28 | } 29 | public function width(value: String) -> This { 30 | return this; 31 | } 32 | public function margin(value: String) -> This { 33 | return this; 34 | } 35 | } 36 | 37 | public type View { 38 | new(root: RootElement) { 39 | this.root = root; 40 | } 41 | } 42 | 43 | type Attributes { 44 | public function set(key: String, value: String) { 45 | todo(); 46 | } 47 | } 48 | 49 | 50 | 51 | public interface Component { 52 | /// Returns the base element of the component. 53 | public function base() -> Element; 54 | 55 | /// Sets the id of the string. 56 | public function id(string: String) -> This { 57 | this.base().setAttribute("id", string); 58 | return this; 59 | } 60 | 61 | /// Sets the class of the string. 62 | public function class(string: String) -> This { 63 | this.base().setAttribute("class", string); 64 | return this; 65 | } 66 | 67 | public function append(child: T) -> This { 68 | this.base().children().push(child); 69 | return this; 70 | } 71 | 72 | public function style(style: Style) -> This { 73 | return this; 74 | } 75 | } 76 | 77 | type Div implements Component { 78 | public function [Component.base]() -> Element { 79 | return this; 80 | } 81 | } 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/triple.wrl: -------------------------------------------------------------------------------- 1 | module triple 2 | 3 | use core.maybe.some 4 | use core.string.{Display, String} 5 | use core.interfaces.Default 6 | 7 | use ntuple.NTuple 8 | 9 | 10 | /// A set containing four individual items. 11 | public model Triple 12 | implements Display|=( 13 | T implements Display 14 | and U implements Display 15 | and V implements Display 16 | ) + Default|=(T implements Default 17 | and U implements Default 18 | and V implements Default) 19 | + NTuple 20 | { 21 | public var a: T 22 | public var b: U 23 | public var c: V 24 | 25 | new(first: T, second: U, third: V) { 26 | this.a = first 27 | this.b = second 28 | this.c = third 29 | } 30 | 31 | public function [Display.toString]|=( 32 | T implements Display 33 | and U implements Display 34 | and V implements Display 35 | ) -> String 36 | { 37 | return String.fmt("Triple(%, %, %)") 38 | .add(this.a) 39 | .add(this.b) 40 | .add(this.c) 41 | .finish() 42 | } 43 | 44 | static function [Default.init]|=( 45 | T implements Default 46 | and U implements Default 47 | and V implements Default) -> This { 48 | var a: T; 49 | var b: U; 50 | var c: V; 51 | return Triple(a, b, c) 52 | } 53 | 54 | public function [NTuple.first] -> ?T { 55 | return some(this.a) 56 | } 57 | public function [NTuple.second] -> ?U { 58 | return some(this.b) 59 | } 60 | public function [NTuple.third] -> ?V { 61 | return some(this.c) 62 | } 63 | /// Reverses the triple from `Triple` to 64 | /// `Triple`. 65 | /// ### Usage 66 | /// ``` 67 | /// use core.collections.Triple 68 | /// 69 | /// var quadruple = Triple("hello", false, some(9)) 70 | /// var transposed = triple.transpose() 71 | /// 72 | /// assert(transposed).equals(Triple(some(9), false, "hello")) 73 | /// ``` 74 | public function transpose -> Triple { 75 | return Triple(this.c, this.b, this.a) 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/globals.rs: -------------------------------------------------------------------------------- 1 | use crate::EvaluatedType; 2 | 3 | /// The path to the entry file. 4 | /// This is only used in testing. 5 | /// Its value in production is dependent on the environment. 6 | pub const CORE_LIBRARY_PATH: &'static str = "../../library/core/core.wrl"; 7 | 8 | /// The maximum recursion limit for a type evaluation. 9 | /// If leads to a stack overflow in Windows when its higher than 200, for some reason. 10 | pub const EVALUATION_DEPTH: u64 = 200; 11 | 12 | /// The number of related symbols to show in the hover of a symbol. 13 | pub const RELATION_COUNT: u8 = 5; 14 | 15 | /// Signifies the file being parsed. 16 | /// Useful to pinpoint primitive types without too much build-in into 17 | /// the compiler. 18 | pub enum CurrentModuleType { 19 | Regular, 20 | String, 21 | Array, 22 | Boolean, 23 | Numeric, 24 | Concurrent, 25 | Eventual, 26 | Prospect, 27 | Internal, 28 | Ops, 29 | Interfaces, 30 | Guaranteed, 31 | Try, 32 | Iteration, 33 | Range, 34 | Default, 35 | Maybe, 36 | Never, 37 | } 38 | pub trait IntrinsicPaths { 39 | const PRELUDE: &'static str = "prelude/prelude.wrl"; 40 | const STRING: &'static str = "string/string.wrl"; 41 | const ARRAY: &'static str = "array/array.wrl"; 42 | const BOOL: &'static str = "boolean/boolean.wrl"; 43 | const NUMERIC: &'static str = "numeric/numeric.wrl"; 44 | const CONCURRENT: &'static str = "concurrent/concurrent.wrl"; 45 | const PROSPECT: &'static str = "concurrent/prospect.wrl"; 46 | const EVENTUAL: &'static str = "concurrent/eventual.wrl"; 47 | const INTERNAL: &'static str = "internals/internals.wrl"; 48 | const NEVER: &'static str = "internals/never.wrl"; 49 | const OPS: &'static str = "ops/ops.wrl"; 50 | const INTERFACES: &'static str = "interfaces/interfaces.wrl"; 51 | const RANGE: &'static str = "ops/range.wrl"; 52 | const ITERABLE: &'static str = "iterate/iterate.wrl"; 53 | const TRY: &'static str = "interfaces/try.wrl"; 54 | const GUARANTEED: &'static str = "interfaces/guaranteed.wrl"; 55 | const DEFAULT: &'static str = "interfaces/default.wrl"; 56 | const MAYBE: &'static str = "maybe/maybe.wrl"; 57 | } 58 | 59 | pub const UNKNOWN: EvaluatedType = EvaluatedType::Unknown; 60 | -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/sextuple.wrl: -------------------------------------------------------------------------------- 1 | module sextuple 2 | 3 | use core.maybe.some 4 | use core.string.{Display, String} 5 | 6 | use ntuple.NTuple 7 | 8 | 9 | /// A set containing six individual items. 10 | public model Sextuple 11 | implements Display|=( 12 | T implements Display 13 | and U implements Display 14 | and V implements Display 15 | and W implements Display 16 | and X implements Display 17 | and Y implements Display 18 | ) + NTuple 19 | { 20 | public var a: T 21 | public var b: U 22 | public var c: V 23 | public var d: W 24 | public var e: X 25 | public var f: Y 26 | 27 | new( 28 | first: T, 29 | second: U, 30 | third: V, 31 | fourth: W, 32 | fifth: X, 33 | sixth: Y 34 | ) { 35 | this.a = first 36 | this.b = second 37 | this.c = third 38 | this.d = fourth 39 | this.e = fifth 40 | this.f = sixth 41 | } 42 | 43 | public function [Display.toString]|=( 44 | T implements Display 45 | and U implements Display 46 | and V implements Display 47 | and W implements Display 48 | and X implements Display 49 | and Y implements Display 50 | ) -> String 51 | { 52 | return String.fmt("Sextuple(%, %, %, %, %, %)") 53 | .add(this.a.toString()) 54 | .add(this.b.toString()) 55 | .add(this.c.toString()) 56 | .add(this.d.toString()) 57 | .add(this.e.toString()) 58 | .add(this.f.toString()) 59 | .finish() 60 | } 61 | 62 | public function [NTuple.first] -> ?T { 63 | return some(this.a) 64 | } 65 | public function [NTuple.second] -> ?U { 66 | return some(this.b) 67 | } 68 | public function [NTuple.third] -> ?V { 69 | return some(this.c) 70 | } 71 | public function [NTuple.fourth] -> ?W { 72 | return some(this.d) 73 | } 74 | public function [NTuple.fifth] -> ?X { 75 | return some(this.e) 76 | } 77 | public function [NTuple.sixth] -> ?Y { 78 | return some(this.f) 79 | } 80 | } -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/tuple.wrl: -------------------------------------------------------------------------------- 1 | module tuple 2 | 3 | use ntuple.NTuple 4 | use core.string.{Display, String} 5 | use core.maybe.some 6 | 7 | /// A simple ordered pair of items. Elements within a tuple can be of any type, 8 | /// and the order in which they are defined is preserved. 9 | public model Tuple 10 | implements Display|=(T implements Display and U implements Display) 11 | + NTuple 12 | { 13 | public var a: T 14 | public var b: U 15 | 16 | new(first: T, second: U) { 17 | this.a = first 18 | this.b = second 19 | } 20 | 21 | /// Converts the tuple to a String, provided that its elements implement 22 | /// Display. 23 | /// ### Usage 24 | /// ``` 25 | /// var tuple = pair(true, 123) 26 | /// var str = tuple.toString() 27 | /// 28 | /// assert(str).equals("Tuple(true, 123)") 29 | /// ``` 30 | public function [Display.toString]|=( 31 | T implements Display and U implements Display 32 | ) -> String { 33 | return String.fmt("Tuple(%, %)") 34 | .add(this.a) 35 | .add(this.b) 36 | .finish() 37 | } 38 | 39 | public function [NTuple.first] -> ?T { 40 | return some(this.a) 41 | } 42 | public function [NTuple.second] -> ?U { 43 | return some(this.b) 44 | } 45 | 46 | /// Returns a tuple with the position of the two items in the tuple reversed. 47 | /// ### Usage 48 | /// ``` 49 | /// use core.collections.Tuple 50 | /// 51 | /// var tuple = Tuple(true, "story") 52 | /// var swapped = tuple.swap() 53 | /// assertThat(swapped == Tuple("story", true)) 54 | /// ``` 55 | public function swap -> Tuple { 56 | Tuple(this.b, this.a) 57 | } 58 | } 59 | 60 | /// Takes two arguments of any type and returns a `Tuple` containing them. 61 | /// 62 | /// It is useful for grouping related data or for passing multiple values 63 | /// as a single unit. 64 | /// 65 | /// ### Usage 66 | /// ``` 67 | /// var pair1 = pair(1, "apple") 68 | /// var pair2 = pair(true, [1, 2, 3]) 69 | /// 70 | /// // Accessing elements of the tuple. 71 | /// var first = pair1.a 72 | /// assert(first).equals(1) 73 | /// 74 | /// var second = pair1.b 75 | /// assert(second).equals("apple") 76 | /// ``` 77 | public function pair(a: T, b: U) -> Tuple { 78 | return Tuple(a, b) 79 | } -------------------------------------------------------------------------------- /source/server/src/folding_range.rs: -------------------------------------------------------------------------------- 1 | // use analyzer::{TypedModelPropertyType, TypedModule, TypedVisitorNoArgs}; 2 | // use std::cell::RefCell; 3 | // use tower_lsp::lsp_types::{FoldingRange, FoldingRangeKind}; 4 | 5 | // use crate::diagnostic::to_range; 6 | 7 | // /// Folding range traverser. 8 | // pub struct FoldingRangeFinder<'a> { 9 | // pub ranges: RefCell>, 10 | // module: &'a TypedModule, 11 | // } 12 | 13 | // impl<'a> FoldingRangeFinder<'a> { 14 | // /// Creates a new folding range finder. 15 | // pub fn new(module: &'a TypedModule) -> Self { 16 | // FoldingRangeFinder { 17 | // ranges: RefCell::new(vec![]), 18 | // module, 19 | // } 20 | // } 21 | // /// Traverses the module to gather the folding ranges. 22 | // pub fn gather(&self) { 23 | // for statement in &self.module.statements { 24 | // self.statement(statement); 25 | // } 26 | // } 27 | // fn add_range(&self, span: ast::Span) { 28 | // let range = to_range(span); 29 | // let folding_range = FoldingRange { 30 | // start_line: range.start.line, 31 | // start_character: Some(range.start.character), 32 | // end_line: range.end.line, 33 | // end_character: Some(range.end.character), 34 | // kind: Some(FoldingRangeKind::Region), 35 | // collapsed_text: None, 36 | // }; 37 | // self.ranges.borrow_mut().push(folding_range) 38 | // } 39 | // } 40 | 41 | // impl TypedVisitorNoArgs for FoldingRangeFinder<'_> { 42 | // fn block(&self, block: &analyzer::TypedBlock) { 43 | // self.add_range(block.span); 44 | // for statement in &block.statements { 45 | // self.statement(&statement) 46 | // } 47 | // } 48 | 49 | // fn model_decl(&self, model: &analyzer::TypedModelDeclaration) -> () { 50 | // self.add_range(model.body.span); 51 | // if let Some(constructor) = &model.body.constructor { 52 | // self.block(constructor) 53 | // } 54 | // for property in &model.body.properties { 55 | // match &property._type { 56 | // TypedModelPropertyType::TypedMethod { body } 57 | // | TypedModelPropertyType::InterfaceImpl { body, .. } => self.block(&body), 58 | // _ => {} 59 | // } 60 | // } 61 | // } 62 | // } 63 | -------------------------------------------------------------------------------- /source/library/core/ops/ops.wrl: -------------------------------------------------------------------------------- 1 | module ops 2 | 3 | public use range.Range 4 | 5 | /// This interface allows the definition of models that can be added together 6 | /// by overloading the `+` operator, such as strings and numbers. 7 | public interface Addition { 8 | /// Adds two items together. 9 | public function add(other: This) -> This 10 | } 11 | 12 | public interface Subtraction { 13 | /// Subtracts a value from the current value. 14 | public function sub(other: This) -> This 15 | } 16 | 17 | public interface Multiplication { 18 | /// Multiplies this value by another. 19 | public function mul(other: This) -> This 20 | } 21 | 22 | public interface Division { 23 | /// Divides this value by another. 24 | public function div(other: This) -> This 25 | } 26 | 27 | // public interface Index { 28 | // public function index(indexer: I) -> R 29 | // } 30 | 31 | /// The Ordering enum type represents the possible results of comparing two values. 32 | /// 33 | /// It is conceptually similar to the standard comparisons used 34 | /// in other programming languages, making it intuitive and useful 35 | /// in various sorting or comparison-related algorithms. 36 | public enum Ordering { 37 | Less, 38 | Greater, 39 | Equal, 40 | } 41 | 42 | /// This interface should be implemented by models that can be ordered and compared 43 | /// relative to each other. 44 | /// 45 | /// It allows overloading of the `<`, `>`, `<=` and `>=` operators. 46 | public interface Orderable { 47 | /// Compares two values together. 48 | public function compare(other: This) -> Ordering 49 | } 50 | 51 | /// Provides a set of methods to enable overloading of bitwise operators (`|`, `&`, `>>` and `<<`) for custom models. 52 | /// It allows instances of a model to behave like integers in bitwise operations. 53 | public interface Bitwise { 54 | /// Performs the bitwise OR operation. 55 | public function bitor(other: This) -> This 56 | /// Performs the bitwise AND operation. 57 | public function bitand(other: This) -> This 58 | /// Performs the bitwise XOR operation. 59 | public function bitxor(other: This) -> This 60 | /// Performs the bitwise left shift operation. 61 | public function lshift(other: This) -> This 62 | /// Performs the bitwise right shift operation. 63 | public function rshift(other: This) -> This 64 | } 65 | 66 | /// This interface concerns all items that can exist in a sequence, 67 | /// most notably letters and numbers. 68 | public interface Sequenced { 69 | /// Returns the next item in a sequence. 70 | public function nextItem -> ?This 71 | } 72 | 73 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/modulemap.rs: -------------------------------------------------------------------------------- 1 | use crate::TypedModule; 2 | use utils::UnorderedMap; 3 | 4 | /// An index into the paths stored. 5 | #[derive(Debug, PartialEq, PartialOrd, Ord, Clone, Copy, Hash, Eq)] 6 | pub struct PathIndex(pub u32); 7 | 8 | #[derive(Debug)] 9 | pub struct ModuleMap { 10 | values: UnorderedMap, 11 | } 12 | 13 | impl ModuleMap { 14 | /// Create a new Path table. 15 | pub fn new() -> Self { 16 | Self { 17 | values: UnorderedMap::new(), 18 | } 19 | } 20 | /// Returns an iterator over all the paths in the table and their indexes. 21 | pub fn paths(&self) -> impl Iterator { 22 | self.values 23 | .iter() 24 | .enumerate() 25 | .map(|(idx, module)| (PathIndex(idx as u32), module)) 26 | } 27 | 28 | /// Returns a mutable iterator over all the paths in the table and their indexes. 29 | pub fn paths_mut(&mut self) -> impl Iterator { 30 | self.values 31 | .iter_mut() 32 | .enumerate() 33 | .map(|(idx, module)| (PathIndex(idx as u32), module)) 34 | } 35 | 36 | /// Reserve an index for a module to be added later. 37 | pub fn reserve_index(&mut self) -> PathIndex { 38 | PathIndex(self.values.reserve() as u32) 39 | } 40 | 41 | /// Add a module to the table and return its index number. 42 | pub fn add(&mut self, module: TypedModule) -> PathIndex { 43 | PathIndex(self.values.insert(module) as u32) 44 | } 45 | /// Returns the module at an index. 46 | pub fn get(&self, index: PathIndex) -> Option<&TypedModule> { 47 | self.values.get(index.0 as usize) 48 | } 49 | /// Returns the module at an index. 50 | pub fn get_mut(&mut self, index: PathIndex) -> Option<&mut TypedModule> { 51 | self.values.get_mut(index.0 as usize) 52 | } 53 | /// Remove a module using its index. 54 | pub fn remove(&mut self, index: PathIndex) -> Option { 55 | self.values.remove(index.0 as usize) 56 | } 57 | /// Returns the number of modules in the table. 58 | pub fn len(&self) -> usize { 59 | self.values.len() 60 | } 61 | /// Returns true if a module is contained in the table. 62 | pub fn has(&self, module: &TypedModule) -> bool { 63 | self.get(module.path_idx).is_some() 64 | } 65 | /// Find the index for a path. 66 | pub fn map_path_to_index(&self, path: &std::path::PathBuf) -> Option { 67 | Some(self.paths().find(|tuple| tuple.1.path_buf == *path)?.0) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /source/compiler/ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod ambience; 2 | mod expression; 3 | mod scope; 4 | mod signature; 5 | mod span; 6 | mod statement; 7 | 8 | mod token; 9 | mod types; 10 | mod visitor; 11 | 12 | use std::{ 13 | fs::File, 14 | io::{Error, Read, Seek, SeekFrom}, 15 | }; 16 | 17 | pub use ambience::*; 18 | pub use expression::*; 19 | pub use scope::*; 20 | pub use signature::*; 21 | pub use span::*; 22 | pub use statement::*; 23 | pub use token::*; 24 | pub use types::*; 25 | pub use visitor::*; 26 | 27 | /// Exits a function and returns an option value if it is Some. 28 | /// Basically the direct opposite of `impl Try for Option`. 29 | #[macro_export] 30 | macro_rules! maybe { 31 | ($exp: expr) => { 32 | if let Some(exp) = $exp { 33 | return Some(exp); 34 | } 35 | }; 36 | } 37 | 38 | /// Exits a visition function if a position is not contained within a span. 39 | #[macro_export] 40 | macro_rules! within { 41 | ($span: expr, $self: expr) => { 42 | if !$span.contains($self.pos) { 43 | return None; 44 | } 45 | }; 46 | } 47 | 48 | /// Helper macro that unwraps an Option, or continues in a for loop. 49 | #[macro_export] 50 | macro_rules! unwrap_or_continue { 51 | ($expr: expr) => {{ 52 | match $expr { 53 | Some(value) => value, 54 | None => continue, 55 | } 56 | }}; 57 | } 58 | 59 | /// Helper macro that unwraps an Option, or returns from the function. 60 | #[macro_export] 61 | macro_rules! unwrap_or_return { 62 | ($expr: expr) => {{ 63 | match $expr { 64 | Some(value) => value, 65 | None => return, 66 | } 67 | }}; 68 | } 69 | 70 | /// Returns a string containing the documentation comments from a particular range in a file. 71 | pub fn get_documentation_at( 72 | path: &std::path::Path, 73 | line_lengths: &[u32], 74 | docs_span: Span, 75 | ) -> Result { 76 | let mut file = File::open(path)?; 77 | let range = Span::to_range(docs_span, line_lengths); 78 | 79 | file.seek(SeekFrom::Start(range.start as u64))?; 80 | let mut buffer = vec![0; range.end - range.start]; 81 | 82 | file.read_exact(&mut buffer)?; 83 | 84 | let raw_str = String::from_utf8(buffer).unwrap_or_else(|_| String::new()); 85 | let doc_str = raw_str 86 | .lines() 87 | .map(|line| { 88 | let trim = line.trim(); 89 | if trim.ends_with("///") { 90 | return String::from("\n\n"); 91 | } else { 92 | return String::from(trim.split_at(3).1) + "\n"; 93 | } 94 | }) 95 | .collect::(); 96 | Ok(doc_str) 97 | } 98 | -------------------------------------------------------------------------------- /source/codegen/src/opcode.rs: -------------------------------------------------------------------------------- 1 | use analyzer::{EvaluatedType, SemanticSymbolKind, SymbolIndex, SymbolLibrary}; 2 | 3 | pub enum WasmSectionId { 4 | TypeSectionId = 0x01, 5 | ImportSectionId = 0x02, 6 | FunctionSectionId = 0x03, 7 | TableSectionId = 0x04, 8 | MemorySectionId = 0x05, 9 | GlobalSectionId = 0x06, 10 | ExportSectionId = 0x07, 11 | StartSectionId = 0x08, 12 | ElementSectionId = 0x09, 13 | CodeSectionId = 0x10, 14 | DataSectionId = 0x11, 15 | DataCountSectionId = 0x12, 16 | } 17 | 18 | impl Into for WasmSectionId { 19 | fn into(self) -> u8 { 20 | self as u8 21 | } 22 | } 23 | 24 | pub enum WasmType { 25 | Function = 0x60, 26 | 27 | I32 = 0x7f, 28 | I64 = 0x7e, 29 | F32 = 0x7d, 30 | F64 = 0x7c, 31 | } 32 | 33 | impl Into for WasmType { 34 | fn into(self) -> u8 { 35 | self as u8 36 | } 37 | } 38 | 39 | impl WasmType { 40 | pub fn from_parameter(parameter: SymbolIndex, symbollib: &SymbolLibrary) -> Self { 41 | let parameter_type = symbollib 42 | .get(parameter) 43 | .map(|symbol| match &symbol.kind { 44 | SemanticSymbolKind::Parameter { inferred_type, .. } => Some(inferred_type), 45 | _ => None, 46 | }) 47 | .flatten(); 48 | 49 | match parameter_type { 50 | Some(evaled_typed) => WasmType::from_evaluated_type(evaled_typed, symbollib), 51 | _ => todo!(), 52 | } 53 | } 54 | 55 | pub fn from_evaluated_type(evaluated_type: &EvaluatedType, symbollib: &SymbolLibrary) -> Self { 56 | match evaluated_type { 57 | EvaluatedType::ModelInstance { model, .. } => { 58 | let index = *model; 59 | if symbollib 60 | .i32 61 | .is_some_and(|symbol_index| index == symbol_index) 62 | { 63 | WasmType::I32 64 | } else if symbollib 65 | .i64 66 | .is_some_and(|symbol_index| index == symbol_index) 67 | { 68 | WasmType::I64 69 | } else if symbollib 70 | .f32 71 | .is_some_and(|symbol_index| index == symbol_index) 72 | { 73 | WasmType::F32 74 | } else if symbollib 75 | .f64 76 | .is_some_and(|symbol_index| index == symbol_index) 77 | { 78 | WasmType::F64 79 | } else { 80 | WasmType::I32 81 | } 82 | } 83 | _ => todo!(), 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /source/library/core/collections/ntuple/quadruple.wrl: -------------------------------------------------------------------------------- 1 | module quadruple 2 | 3 | use core.maybe.some 4 | use core.string.{Display, String} 5 | 6 | use ntuple.NTuple 7 | use tuple.{Tuple, pair} 8 | 9 | 10 | /// A set containing four individual items. 11 | public model Quadruple 12 | implements Display|=( 13 | T implements Display 14 | and U implements Display 15 | and V implements Display 16 | and W implements Display 17 | ) + NTuple 18 | { 19 | public var a: T 20 | public var b: U 21 | public var c: V 22 | public var d: W 23 | 24 | new(first: T, second: U, third: V, fourth: W) { 25 | this.a = first 26 | this.b = second 27 | this.c = third 28 | this.d = fourth 29 | } 30 | 31 | public function [Display.toString]|=( 32 | T implements Display 33 | and U implements Display 34 | and V implements Display 35 | and W implements Display 36 | ) -> String 37 | { 38 | return String.fmt("Quadruple(%, %, %, %)") 39 | .add(this.a.toString()) 40 | .add(this.b.toString()) 41 | .add(this.c.toString()) 42 | .add(this.d.toString()) 43 | .finish() 44 | } 45 | 46 | public function [NTuple.first] -> ?T { 47 | return some(this.a) 48 | } 49 | public function [NTuple.second] -> ?U { 50 | return some(this.b) 51 | } 52 | public function [NTuple.third] -> ?V { 53 | return some(this.c) 54 | } 55 | public function [NTuple.fourth] -> ?W { 56 | return some(this.d) 57 | } 58 | 59 | /// Groups the items in the quadruple to two tuples. 60 | /// ### Usage 61 | /// ``` 62 | /// use core.collections.{Quadruple, Tuple} 63 | /// 64 | /// var quadruple = Quadruple(1, "Sefunmi", true, [35.5]) 65 | /// var { a as first, b as second } = quadruple.toTuple() 66 | /// 67 | /// assert(first).equals(Tuple(1, "Sefunmi")) 68 | /// assert(second).equals(Tuple(true, [35.5])) 69 | /// ``` 70 | public function toTuple -> Tuple, Tuple> { 71 | return pair(pair(this.a, this.b), pair(this.c, this.d)) 72 | } 73 | /// Reverses the quadruple from `Quadruple` to 74 | /// `Quadruple`. 75 | /// ### Usage 76 | /// ``` 77 | /// use core.collections.Quadruple 78 | /// 79 | /// var quadruple = Quadruple("hello", false, some(9), 1.44) 80 | /// var transposed = quadruple.transpose() 81 | /// 82 | /// assert(transposed).equals(Quadruple(1.44, some(9), false, "hello")) 83 | /// ``` 84 | public function transpose -> Quadruple { 85 | return Quadruple(this.d, this.c, this.b, this.a) 86 | } 87 | } -------------------------------------------------------------------------------- /source/extensions/vscode/snippets/whirlwind.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Implements Keyword": { 3 | "prefix": "imp", 4 | "body": "implements " 5 | }, 6 | "Model Definition": { 7 | "prefix": "model", 8 | "isFileTemplate": true, 9 | "body": [ 10 | "model ${1:name} {", 11 | "\tnew(${2:parameters}) {", 12 | "\t\t$0", 13 | "\t}", 14 | "}" 15 | ], 16 | "description": "Model Definition" 17 | }, 18 | "Public Function Definition": { 19 | "prefix": "public func", 20 | "body": ["/// $2", "public function ${1:name}() {", "\t$0", "}"], 21 | "description": "Public Function Definition" 22 | }, 23 | "Use Statement": { 24 | "prefix": "use ", 25 | "body": ["use $0"], 26 | "description": "Use external module" 27 | }, 28 | "Fatal": { 29 | "prefix": "fatal", 30 | "body": ["fatal(some(\"$1\"))", "$0"], 31 | "description": "fatal expression" 32 | }, 33 | "For-In Loop": { 34 | "prefix": "for", 35 | "body": [ 36 | "for ${1:element} in ${2:iterator} {", 37 | "\t$TM_SELECTED_TEXT$0", 38 | "}" 39 | ], 40 | "description": "For-In Loop" 41 | }, 42 | "Function Statement": { 43 | "prefix": "func", 44 | "body": [ 45 | "function ${1:name}(${2:params}:${3:Paramtype}) {", 46 | "\t$TM_SELECTED_TEXT$0", 47 | "}" 48 | ], 49 | "description": "Function Statement" 50 | }, 51 | "If Expression": { 52 | "prefix": "if", 53 | "body": ["if ${1:condition} {", "\t$TM_SELECTED_TEXT$0", "}"], 54 | "description": "If Expression" 55 | }, 56 | "If-Else Statement": { 57 | "prefix": "ifelse", 58 | "body": [ 59 | "if ${1:condition} {", 60 | "\t$TM_SELECTED_TEXT$0", 61 | "} else {", 62 | "\t", 63 | "}" 64 | ], 65 | "description": "If-Else Expression" 66 | }, 67 | "Return Statement": { 68 | "prefix": "ret", 69 | "body": "return ${1}" 70 | }, 71 | "Variable Declaration": { 72 | "prefix": "var ", 73 | "body": "var ${1:name} = ${2:value}" 74 | }, 75 | "While Statement": { 76 | "prefix": "while", 77 | "body": ["while ${1:condition} {", "\t$TM_SELECTED_TEXT$0", "}"], 78 | "description": "While Statement" 79 | }, 80 | "Async Function Statement": { 81 | "prefix": "async func", 82 | "body": [ 83 | "async function ${1:name}(${2:params}:${3:type}) {", 84 | "\t$TM_SELECTED_TEXT$0", 85 | "}" 86 | ], 87 | "description": "Async Function Statement" 88 | }, 89 | "Async Function Expression": { 90 | "prefix": "async fn exp", 91 | "body": [ 92 | "async fn (${1:params}:${2:type}) {", 93 | "\t$TM_SELECTED_TEXT$0", 94 | "}" 95 | ], 96 | "description": "Async Function Expression" 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /source/server/src/diagnostic/mod.rs: -------------------------------------------------------------------------------- 1 | use analyzer::{DiagnosticType, Error, ProgramDiagnostic}; 2 | use ast::Span; 3 | use errors::LexErrorPos; 4 | use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range}; 5 | 6 | /// Convert a program diagnostic to an LSP diagnostic. 7 | pub fn progdiagnostic_to_diagnostic(diagnostic: &ProgramDiagnostic) -> Diagnostic { 8 | let (message, range, severity) = match &diagnostic._type { 9 | DiagnosticType::Error(error) => { 10 | let (message, range) = match error { 11 | Error::Lexical(lex_error) => ( 12 | lex_error.error_type.to_string(), 13 | to_range(match lex_error.position { 14 | LexErrorPos::Point(pos) => Span::at(pos), 15 | LexErrorPos::Span(span) => span, 16 | }), 17 | ), 18 | Error::Syntax(parse_error) => { 19 | (parse_error._type.to_string(), to_range(parse_error.span)) 20 | } 21 | Error::Typing(type_error) => { 22 | (type_error._type.to_string(), to_range(type_error.span)) 23 | } 24 | Error::Importing(resolve_error) => ( 25 | resolve_error._type.to_string(), 26 | to_range(resolve_error.span.unwrap_or_default()), 27 | ), 28 | Error::Context(context_error) => ( 29 | context_error._type.to_string(), 30 | to_range(context_error.span), 31 | ), 32 | }; 33 | (message, range, Some(DiagnosticSeverity::ERROR)) 34 | } 35 | DiagnosticType::Warning(warning) => { 36 | let range = to_range(warning.span); 37 | let message = warning.warning_type.to_string(); 38 | (message, range, Some(DiagnosticSeverity::WARNING)) 39 | } 40 | }; 41 | Diagnostic { 42 | range, 43 | severity, 44 | code: None, 45 | code_description: None, 46 | source: Some(format!("whirlwind")), 47 | message, 48 | related_information: None, 49 | tags: None, 50 | data: None, 51 | } 52 | } 53 | 54 | /// Convert a span to a range. 55 | pub fn to_range(mut span: Span) -> Range { 56 | if span.start[0] > 0 { 57 | span.start[0] -= 1 58 | } 59 | if span.start[1] > 0 { 60 | span.start[1] -= 1 61 | } 62 | if span.end[0] > 0 { 63 | span.end[0] -= 1 64 | } 65 | if span.end[1] > 0 { 66 | span.end[1] -= 1 67 | } 68 | Range { 69 | start: Position { 70 | line: span.start[0], 71 | character: span.start[1], 72 | }, 73 | end: Position { 74 | line: span.end[0], 75 | character: span.end[1], 76 | }, 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/retriever/function.rs: -------------------------------------------------------------------------------- 1 | use analyzer::{ 2 | span_of_typed_statement, SemanticSymbol, ShortcircuitTypedVisitorNoArgs, Standpoint, 3 | TypedFunctionDeclaration, TypedStmnt, 4 | }; 5 | 6 | /// Returns a function declaration that maps to a function symbol. 7 | pub struct FunctionRetriever<'a> { 8 | standpoint: &'a Standpoint, 9 | // Symbol of the function to the retrieved. 10 | symbol: &'a SemanticSymbol, 11 | } 12 | 13 | impl<'a> ShortcircuitTypedVisitorNoArgs<'a, &'a TypedFunctionDeclaration> 14 | for FunctionRetriever<'a> 15 | { 16 | fn statement(&self, statement: &'a TypedStmnt) -> Option<&'a TypedFunctionDeclaration> { 17 | let symbollib = &self.standpoint.symbol_library; 18 | let literals = &self.standpoint.literals; 19 | let span = span_of_typed_statement(statement, symbollib, literals); 20 | if !span.encloses(self.symbol.origin_span) { 21 | return None; 22 | } 23 | match statement { 24 | TypedStmnt::TestDeclaration(test) => self.test_declaration(test), 25 | TypedStmnt::VariableDeclaration(var_decl) => self.var_decl(var_decl), 26 | TypedStmnt::ShorthandVariableDeclaration(shorthand) => { 27 | self.shorthand_var_decl(shorthand) 28 | } 29 | TypedStmnt::ConstantDeclaration(constant) => self.constant(constant), 30 | TypedStmnt::ModelDeclaration(type) => self.model_decl(type), 31 | TypedStmnt::FunctionDeclaration(function) => { 32 | let symbol = symbollib.get(function.name)?; 33 | if std::ptr::eq(symbol, self.symbol) { 34 | return Some(function); 35 | } 36 | self.function(function) 37 | } 38 | TypedStmnt::InterfaceDeclaration(interface) => self.interface_declaration(interface), 39 | TypedStmnt::ExpressionStatement(expr) | TypedStmnt::FreeExpression(expr) => { 40 | self.expr(expr) 41 | } 42 | TypedStmnt::ReturnStatement(rettye) => self.return_statement(rettye), 43 | TypedStmnt::ForStatement(forstat) => self.for_statement(forstat), 44 | TypedStmnt::WhileStatement(whilestat) => self.while_statement(whilestat), 45 | _ => None, 46 | } 47 | } 48 | } 49 | impl<'a> FunctionRetriever<'a> { 50 | pub fn retrieve(&self) -> Option<&'a TypedFunctionDeclaration> { 51 | let module_idx = self.symbol.references.first()?.module_path; 52 | let module = self.standpoint.module_map.get(module_idx)?; 53 | for statement in &module.statements { 54 | ast::maybe!(self.statement(statement)) 55 | } 56 | return None; 57 | } 58 | /// Creates a new retriever. 59 | pub fn new(standpoint: &'a Standpoint, symbol: &'a SemanticSymbol) -> Self { 60 | Self { standpoint, symbol } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /source/library/core/testing/testing.wrl: -------------------------------------------------------------------------------- 1 | module testing 2 | 3 | use core.{ 4 | boolean.boolean, 5 | maybe.some, 6 | ops.{Orderable}, 7 | sentinels.fatal, 8 | string.{Display, String} 9 | } 10 | 11 | /// Creates an assertion object. 12 | public function assert(value: T) -> Assertion { 13 | return Assertion(value) 14 | } 15 | 16 | /// Tests that a condition is true. 17 | public function assertThat(proposition: boolean) { 18 | if !proposition { 19 | var {} = fatal(some("Assertion failed due to false proposition.")) 20 | } 21 | } 22 | 23 | /// Generates a String of an assertion error. 24 | function assertionMessage(mainMessage: String, left: T, right: T) -> String { 25 | var template = String.fmt(mainMessage) 26 | template.extend("Contained value: %\nOther: %") 27 | return template.add(left).add(right).finish() 28 | } 29 | 30 | /// This model represents an assertion object, 31 | /// which is the base object for many testing operations. 32 | public model Assertion { 33 | var value: T 34 | new(value: T) { 35 | this.value = value 36 | } 37 | /// Checks if the contained value equals another. 38 | public function equals(other: T) -> This { 39 | if other != this.value { 40 | var message = assertionMessage( 41 | "Assertion failed on equality.", 42 | this.value, 43 | other) 44 | fatal(some(message)) 45 | } 46 | return this 47 | } 48 | /// Checks that the contained value does not equal another. 49 | public function doesNotEqual(other: T) -> This { 50 | if other == this.value { 51 | var message = assertionMessage( 52 | "Assertion failed on inequality.", 53 | this.value, 54 | other) 55 | fatal(some(message)) 56 | } 57 | return this 58 | } 59 | /// Checks that the contained value is less than another. 60 | public function isLessThan|=(T implements Orderable)(other: T) -> This { 61 | if other <= this.value { 62 | var message = assertionMessage( 63 | "Assertion failed for less than.", 64 | this.value, 65 | other) 66 | fatal(some(message)) 67 | } 68 | return this 69 | } 70 | /// Checks that the contained value is greater than another. 71 | public function isGreaterThan|=(T implements Orderable)(other: T) -> This { 72 | if other >= this.value { 73 | var message = assertionMessage( 74 | "Assertion failed for greater than.", 75 | this.value, 76 | other) 77 | fatal(some(message)) 78 | } 79 | return this 80 | } 81 | } -------------------------------------------------------------------------------- /.discontinued/examples/WebServer/source/main.wrl: -------------------------------------------------------------------------------- 1 | // Define the main module. 2 | module main; 3 | 4 | // Import necessary modules from the Core library. 5 | use core.{ 6 | net.http.{ 7 | HttpServer, ServerOutcome, HttpRequest, HttpResponse, 8 | Middleware, MiddlewareResponse, 9 | }, 10 | io.{fmt, print}, 11 | crypto.JwtEncryptor, 12 | process.getEnv 13 | }; 14 | 15 | /// Entry point into the program. 16 | public function main() { 17 | // Set up server configurations. 18 | name := "simple-server"; 19 | port := 8080; 20 | app := new HttpServer(name, port); 21 | middleware := new Middleware(); 22 | printMessage := fn() { 23 | template := fmt("% is listening on %."); 24 | message := template.add(name).add(port).finish(); 25 | print(message) 26 | }; 27 | // Register authentication. 28 | middleware.addHandler(authenticationHandler); 29 | // Apply middleware to the server. 30 | app.useMiddleware(middleware) 31 | .get("/hello", helloHandler) 32 | .get("/greet/:name", greetHandler) 33 | .get("/static/::filename", staticFileHandler) 34 | .start() 35 | .then(printMessage) 36 | .run(); 37 | } 38 | 39 | /// Handler function for the "/hello" endpoint. 40 | async function helloHandler(request: HttpRequest): ServerOutcome { 41 | response := HttpResponse.from(request); 42 | response.status(200).html(`

Hello, world!

`); 43 | ok(response) 44 | } 45 | 46 | /// Handler function for authenticating users. 47 | async function authenticationHandler(req: HttpRequest): MiddlewareResponse { 48 | if req.url().startsWith("/user/") { 49 | auth := req.headers().getOrErr("auth")?; 50 | token := if !auth.startsWith("Bearer ") { 51 | return err(req.createError("Invalid authentication header.")); 52 | } else { 53 | auth.slice(7..auth.length()).unwrap() 54 | }; 55 | maybeSecret := getEnv("JWT_SECRET"); 56 | if maybeSecret.isNone() { 57 | return err(req.createError("Unauthorized Request.")); 58 | }; 59 | secret := maybeSecret.unwrap(); 60 | decoded := JwtEncryptor.decode(token, secret); 61 | req.storeData("user-id", decoded); 62 | return ok(req); 63 | } 64 | return ok(req); 65 | } 66 | 67 | /// Handler function for the "/greet/:name" endpoint. 68 | async function greetHandler(req: HttpRequest): ServerOutcome { 69 | name := req.pathParams().getOrErr("name")?; 70 | response := req.respond(); 71 | response.status(200).html(`

Hello, ` + name + `!

`); 72 | return ok(response); 73 | } 74 | 75 | // Handler function for serving static files. 76 | async function staticFileHandler(req: HttpRequest): ServerOutcome { 77 | filename := req.pathParams().getOrErr("filename")?; 78 | // Implement logic to read and serve the specified static file. 79 | response := req.respond(); 80 | return ok(response); 81 | } 82 | -------------------------------------------------------------------------------- /source/extensions/vscode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@whirlwind/vscode", 3 | "displayName": "Whirlwind", 4 | "description": "Rich Support for the Whirlwind Programming Language", 5 | "publisher": "adebola-io", 6 | "author": { 7 | "name": "Adebola Akomolafe", 8 | "email": "adebolaakomolafe@gmail.com" 9 | }, 10 | "icon": "./images/wrl-color.svg", 11 | "galleryBanner": { 12 | "color": "#000", 13 | "theme": "dark" 14 | }, 15 | "homepage": "https://whirlwind-lang.vercel.app", 16 | "version": "0.0.0", 17 | "engines": { 18 | "vscode": "^1.54.0" 19 | }, 20 | "categories": [ 21 | "Programming Languages", 22 | "Snippets", 23 | "Linters", 24 | "Formatters", 25 | "Other" 26 | ], 27 | "main": "./src/extension.js", 28 | "contributes": { 29 | "languages": [ 30 | { 31 | "id": "wrl", 32 | "icon": { 33 | "dark": "./icons/wrl-color.svg", 34 | "light": "./icons/wrl-color.svg" 35 | }, 36 | "firstLine": "public\\s+model|module\\s+\\w+;", 37 | "aliases": [ 38 | "Whirlwind", 39 | "wrl" 40 | ], 41 | "extensions": [ 42 | ".wrl" 43 | ], 44 | "configuration": "./syntaxes/whirlwind.json" 45 | } 46 | ], 47 | "commands": [ 48 | { 49 | "command": "whirlwind-server-start", 50 | "title": "Start Server", 51 | "category": "Whirlwind" 52 | }, 53 | { 54 | "command": "whirlwind-server-stop", 55 | "title": "Stop Server", 56 | "category": "Whirlwind" 57 | }, 58 | { 59 | "command": "whirlwind-server-restart", 60 | "title": "Restart Sever", 61 | "category": "Whirlwind" 62 | } 63 | ], 64 | "configuration": { 65 | "type": "object", 66 | "title": "Whirlwind", 67 | "properties": { 68 | "pbls.maxNumberOfProblems": { 69 | "scope": "resource", 70 | "type": "number", 71 | "default": 100, 72 | "description": "Controls the maximum number of problems produced by the server." 73 | } 74 | } 75 | }, 76 | "grammars": [ 77 | { 78 | "language": "wrl", 79 | "scopeName": "source.wrl", 80 | "path": "./syntaxes/whirlwind.tmlLanguage.json" 81 | } 82 | ], 83 | "snippets": [ 84 | { 85 | "language": "wrl", 86 | "path": "./snippets/whirlwind.code-snippets" 87 | } 88 | ] 89 | }, 90 | "dependencies": { 91 | "dotenv": "^16.3.1", 92 | "vscode-languageclient": "^8.0.2" 93 | }, 94 | "devDependencies": { 95 | "@types/node": "^20.5.7" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /source/compiler/utils/src/terminal/table.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use crate::terminal::Colored; 4 | 5 | pub type TermnalTableEntry = (String, Vec); 6 | pub struct TerminalTable { 7 | heading: String, 8 | sidebar: Option, 9 | rows: Vec, 10 | } 11 | 12 | impl TerminalTable { 13 | /// Creates a new Terminal table. 14 | pub fn new(heading: T) -> Self { 15 | TerminalTable { 16 | heading: heading.to_string(), 17 | sidebar: None, 18 | rows: vec![], 19 | } 20 | } 21 | /// Add a new row to the table. The tag is the leftmost field in the row. 22 | pub fn row(&mut self, tag: &str, data: Vec<&str>) -> &mut Self { 23 | self.rows.push(( 24 | tag.to_string(), 25 | data.into_iter().map(|str| str.to_string()).collect(), 26 | )); 27 | return self; 28 | } 29 | /// Add a line demarcation to the front of each row. 30 | pub fn sideline(&mut self, sidebar: &Colored) -> &mut Self { 31 | self.sidebar = Some(sidebar.clone()); 32 | return self; 33 | } 34 | 35 | /// Prints the sidebar into the string. 36 | fn print_sidebar(&self, string: &mut String) { 37 | if let Some(sidebar) = &self.sidebar { 38 | string.push_str(&sidebar.to_string()); 39 | string.push_str(" "); 40 | } 41 | } 42 | } 43 | 44 | impl Display for TerminalTable { 45 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 46 | let mut string = String::new(); 47 | self.print_sidebar(&mut string); 48 | let heading = Colored::from(&self.heading).underline().bold(); 49 | string.push_str(&heading.to_string()); 50 | string.push_str("\n"); 51 | 52 | let longest_tag_len = self 53 | .rows 54 | .iter() 55 | .map(|(tag, _)| tag.len()) 56 | .reduce(|acc, e| if acc < e { e } else { acc }) 57 | .unwrap(); 58 | 59 | for (idx, (tag, data)) in self.rows.iter().enumerate() { 60 | self.print_sidebar(&mut string); 61 | string.push_str(" "); 62 | string.push_str(tag); 63 | let mut i = tag.len(); 64 | while i < longest_tag_len { 65 | string.push(' '); 66 | i += 1; 67 | } 68 | if data.len() == 0 { 69 | string.push_str("\n"); 70 | continue; 71 | } 72 | let data_fields = data.len() as f64; 73 | let space = (1 as f64 / data_fields).ceil() as i64 * 4; 74 | for _ in 0..space { 75 | string.push(' '); 76 | } 77 | for data in data { 78 | string.push_str(data); 79 | for _ in 0..space { 80 | string.push(' '); 81 | } 82 | } 83 | if idx + 1 != self.rows.len() { 84 | string.push_str("\n"); 85 | } 86 | } 87 | write!(f, "{string}") 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /source/extensions/vscode/src/completion.js: -------------------------------------------------------------------------------- 1 | const vscode = require("vscode"); 2 | const { CompletionTriggerKind } = require("vscode-languageclient"); 3 | 4 | /** 5 | * Middleware completion function. 6 | * @param {vscode.TextDocument} document 7 | * @param {vscode.Position} position 8 | * @param {vscode.CompletionContext} context 9 | * @param {vscode.CancellationToken} token 10 | * @param {import("vscode-languageclient").ProvideCompletionItemsSignature} next 11 | */ 12 | module.exports.provideCompletionItem = ( 13 | document, 14 | position, 15 | context, 16 | token, 17 | next 18 | ) => { 19 | // Ignore comments and strings while providing completions. 20 | const line = document.lineAt(position.line).text; 21 | let beforePosition = line.slice(0, position.character); 22 | const isInComment = beforePosition.includes("//"); 23 | // todo: also disable for multiline strings. 24 | const doubleQuoteBefore = 25 | beforePosition.includes('"') && 26 | beforePosition.indexOf('"') === beforePosition.lastIndexOf('"'); 27 | const singleQuoteBefore = 28 | beforePosition.includes("'") && 29 | beforePosition.indexOf("'") === beforePosition.lastIndexOf("'"); 30 | const isInString = doubleQuoteBefore || singleQuoteBefore; 31 | if (isInComment || isInString) { 32 | return null; 33 | } 34 | let newContext = { 35 | triggerKind: CompletionTriggerKind.TriggerCharacter, 36 | triggerCharacter: context.triggerCharacter, 37 | }; 38 | const whitespaceTrigger = context.triggerCharacter === " "; 39 | beforePosition = beforePosition.trimEnd(); 40 | if (beforePosition.endsWith(".")) { 41 | newContext = { 42 | ...newContext, 43 | triggerCharacter: ".", 44 | }; 45 | } 46 | // Customized completion for use imports. 47 | const beforeIsUse = 48 | beforePosition.endsWith(" use") || beforePosition === "use"; 49 | if (beforeIsUse && whitespaceTrigger) { 50 | newContext = { 51 | ...newContext, 52 | triggerCharacter: "use ", 53 | }; 54 | } 55 | // Customized completion for new model instances. 56 | else if (beforePosition.endsWith("new") && whitespaceTrigger) { 57 | newContext = { 58 | ...newContext, 59 | triggerCharacter: "new ", 60 | }; 61 | // return null; 62 | } 63 | // Customized completion for type labels. 64 | else if (beforePosition.endsWith(":") && whitespaceTrigger) { 65 | newContext = { 66 | ...newContext, 67 | triggerCharacter: ": ", 68 | }; 69 | } 70 | // Customized completion for implementations. 71 | else if (beforePosition.endsWith("implements") && whitespaceTrigger) { 72 | newContext = { 73 | ...newContext, 74 | triggerCharacter: "implements ", 75 | }; 76 | } else if (beforePosition.endsWith("module") && whitespaceTrigger) { 77 | newContext = { 78 | ...newContext, 79 | triggerCharacter: "module ", 80 | }; 81 | } 82 | return next(document, position, newContext, token); 83 | }; 84 | -------------------------------------------------------------------------------- /source/library/system/net/http/http.wrl: -------------------------------------------------------------------------------- 1 | module http 2 | 3 | use core.prelude.{ 4 | boolean, todo, Outcome, Prospect, String, 5 | i32, some, fatal, 6 | } 7 | public use request.{ 8 | RequestParams, HttpRequest, HttpMethod, 9 | HttpRequestError, 10 | } 11 | public use response.HttpResponse 12 | 13 | model Endpoints { 14 | new() { 15 | 16 | } 17 | /// Registers a GET route. 18 | public function register( 19 | method: HttpMethod, 20 | route: String, 21 | handler: fn(request: HttpRequest) -> Prospect 22 | ) { 23 | todo() 24 | } 25 | } 26 | 27 | public type ServerOutcome = Outcome 28 | 29 | /// A simple model for an HTTP server. 30 | /// 31 | /// ## Constructor Arguments. 32 | /// - @param (name: String) The name to use to represent this server. 33 | /// - @param (port: i32) The port to listen on. 34 | public model HttpServer { 35 | var endpoints: Endpoints 36 | var middleware: []Middleware 37 | var isRunning: boolean 38 | var port: i32 39 | new(name: String, port: i32) { 40 | this.endpoints = Endpoints() 41 | this.middleware = [] 42 | this.isRunning = false 43 | this.port = port 44 | } 45 | /// Registers a route for a request with a `GET` method. 46 | public function get( 47 | route: String, 48 | handler: fn(request: HttpRequest) -> Prospect 49 | ) -> This { 50 | this.assertInert() 51 | this.endpoints.register(HttpMethod.Get, route, handler) 52 | return this 53 | } 54 | /// Registers a route for a request with a `POST` method. 55 | public function post( 56 | route: String, 57 | handler: fn(request: HttpRequest) -> Prospect 58 | ) -> This { 59 | this.assertInert() 60 | this.endpoints.register(HttpMethod.Post, route, handler) 61 | return this 62 | } 63 | /// Asserts that the server is not yet running. 64 | function assertInert { 65 | if this.isRunning { 66 | var {} = fatal(some("Server is already running.")) 67 | } 68 | } 69 | /// Starts the server by listening at the specified port for changes. 70 | public async function start { 71 | this.assertInert() 72 | this.isRunning = true 73 | while true { 74 | todo() 75 | } 76 | } 77 | /// Registers a middleware for use in the server. 78 | /// Middlewares are executed in the order in which they are registered. 79 | public function useMiddleware(middleware: Middleware) -> This { 80 | this.middleware.push(middleware) 81 | return this 82 | } 83 | } 84 | 85 | 86 | public type MiddlewareResponse = Outcome 87 | public model Middleware { 88 | var handlers: []fn(request: HttpRequest) -> Prospect 89 | new() { 90 | this.handlers = [] 91 | } 92 | /// Registers a middleware handler. 93 | public function addHandler(handler: fn(request: HttpRequest) -> Prospect) { 94 | this.handlers.push(handler) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /.discontinued/bytecode/src/reader.rs: -------------------------------------------------------------------------------- 1 | /// A uniform interface for any struct that can iteratively read 2 | /// the instruction stream while advancing an internal counter. 3 | /// 4 | /// The counter is post incremented, meaning that it always points 5 | /// to the next instruction byte in the stream. 6 | pub trait BytecodeReader { 7 | /// Returns the position of the bytecode reader in the stream. 8 | fn position(&self) -> usize; 9 | /// Returns the next byte in the stream, advancing the reader by 1. 10 | fn next_byte(&mut self) -> u8; 11 | /// Returns the next two bytes in the stream, advancing the reader by 2. 12 | fn next_two_bytes(&mut self) -> [u8; 2]; 13 | /// Returns the next four bytes in the stream, advancing the reader by 4. 14 | fn next_four_bytes(&mut self) -> [u8; 4]; 15 | /// Returns the next eight bytes in the stream, advancing the reader by 8. 16 | fn next_eight_bytes(&mut self) -> [u8; 8]; 17 | } 18 | 19 | /// A bytecode reader for disassembling bytecode objects. 20 | pub struct DisAsmBytecodeReader<'inst> { 21 | pub bytes: &'inst [u8], 22 | /// The instruction pointer. 23 | pub pc: usize, 24 | } 25 | 26 | impl<'inst> From<&'inst [u8]> for DisAsmBytecodeReader<'inst> { 27 | fn from(value: &'inst [u8]) -> Self { 28 | DisAsmBytecodeReader { 29 | bytes: value, 30 | pc: 0, 31 | } 32 | } 33 | } 34 | impl<'inst> BytecodeReader for DisAsmBytecodeReader<'inst> { 35 | fn position(&self) -> usize { 36 | self.pc 37 | } 38 | 39 | #[inline] 40 | fn next_byte(&mut self) -> u8 { 41 | let byte = self.bytes[self.pc]; 42 | self.pc += 1; 43 | return byte; 44 | } 45 | 46 | #[inline] 47 | fn next_two_bytes(&mut self) -> [u8; 2] { 48 | let blob = self.bytes; 49 | let range = (self.pc)..(self.pc + 2); 50 | let range_ptr = blob[range].as_ptr(); 51 | let bytes = unsafe { *(range_ptr as *const [u8; 2]) }; 52 | self.pc += 2; 53 | return bytes; 54 | } 55 | 56 | #[inline] 57 | fn next_four_bytes(&mut self) -> [u8; 4] { 58 | let blob = self.bytes; 59 | let range = (self.pc)..(self.pc + 4); 60 | let range_ptr = blob[range].as_ptr(); 61 | let bytes = unsafe { *(range_ptr as *const [u8; 4]) }; 62 | self.pc += 4; 63 | return bytes; 64 | } 65 | 66 | #[inline] 67 | fn next_eight_bytes(&mut self) -> [u8; 8] { 68 | let blob = self.bytes; 69 | let range = (self.pc)..(self.pc + 8); 70 | let range_ptr = blob[range].as_ptr(); 71 | let bytes = unsafe { *(range_ptr as *const [u8; 8]) }; 72 | self.pc += 8; 73 | return bytes; 74 | } 75 | } 76 | 77 | #[test] 78 | fn test_disassembler_reader() { 79 | let stream: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 80 | let mut reader = DisAsmBytecodeReader::from(stream.as_slice()); 81 | 82 | assert!(reader.next_byte() == 1); 83 | assert!(reader.next_two_bytes() == [2, 3]); 84 | assert!(reader.next_four_bytes() == [4, 5, 6, 7]); 85 | assert!(reader.next_eight_bytes() == [8, 9, 10, 11, 12, 13, 14, 15]) 86 | } 87 | -------------------------------------------------------------------------------- /source/extensions/vscode/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | specifiers: 4 | '@types/node': ^20.5.7 5 | dotenv: ^16.3.1 6 | vscode-languageclient: ^8.0.2 7 | 8 | dependencies: 9 | dotenv: 16.3.1 10 | vscode-languageclient: 8.1.0 11 | 12 | devDependencies: 13 | '@types/node': 20.8.3 14 | 15 | packages: 16 | 17 | /@types/node/20.8.3: 18 | resolution: {integrity: sha512-jxiZQFpb+NlH5kjW49vXxvxTjeeqlbsnTAdBTKpzEdPs9itay7MscYXz3Fo9VYFEsfQ6LJFitHad3faerLAjCw==} 19 | dev: true 20 | 21 | /balanced-match/1.0.2: 22 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 23 | dev: false 24 | 25 | /brace-expansion/2.0.1: 26 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 27 | dependencies: 28 | balanced-match: 1.0.2 29 | dev: false 30 | 31 | /dotenv/16.3.1: 32 | resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} 33 | engines: {node: '>=12'} 34 | dev: false 35 | 36 | /lru-cache/6.0.0: 37 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 38 | engines: {node: '>=10'} 39 | dependencies: 40 | yallist: 4.0.0 41 | dev: false 42 | 43 | /minimatch/5.1.6: 44 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 45 | engines: {node: '>=10'} 46 | dependencies: 47 | brace-expansion: 2.0.1 48 | dev: false 49 | 50 | /semver/7.5.4: 51 | resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} 52 | engines: {node: '>=10'} 53 | hasBin: true 54 | dependencies: 55 | lru-cache: 6.0.0 56 | dev: false 57 | 58 | /vscode-jsonrpc/8.1.0: 59 | resolution: {integrity: sha512-6TDy/abTQk+zDGYazgbIPc+4JoXdwC8NHU9Pbn4UJP1fehUyZmM4RHp5IthX7A6L5KS30PRui+j+tbbMMMafdw==} 60 | engines: {node: '>=14.0.0'} 61 | dev: false 62 | 63 | /vscode-languageclient/8.1.0: 64 | resolution: {integrity: sha512-GL4QdbYUF/XxQlAsvYWZRV3V34kOkpRlvV60/72ghHfsYFnS/v2MANZ9P6sHmxFcZKOse8O+L9G7Czg0NUWing==} 65 | engines: {vscode: ^1.67.0} 66 | dependencies: 67 | minimatch: 5.1.6 68 | semver: 7.5.4 69 | vscode-languageserver-protocol: 3.17.3 70 | dev: false 71 | 72 | /vscode-languageserver-protocol/3.17.3: 73 | resolution: {integrity: sha512-924/h0AqsMtA5yK22GgMtCYiMdCOtWTSGgUOkgEDX+wk2b0x4sAfLiO4NxBxqbiVtz7K7/1/RgVrVI0NClZwqA==} 74 | dependencies: 75 | vscode-jsonrpc: 8.1.0 76 | vscode-languageserver-types: 3.17.3 77 | dev: false 78 | 79 | /vscode-languageserver-types/3.17.3: 80 | resolution: {integrity: sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==} 81 | dev: false 82 | 83 | /yallist/4.0.0: 84 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 85 | dev: false 86 | -------------------------------------------------------------------------------- /source/library/system/net/http/request.wrl: -------------------------------------------------------------------------------- 1 | module request 2 | 3 | use core.prelude.{todo, String, boolean, i32, Outcome, Display} 4 | use core.collections.HashMap 5 | use response.HttpResponse 6 | 7 | public enum HttpMethod { 8 | Get, 9 | Post, 10 | Put, 11 | Delete 12 | } 13 | 14 | model Buffer { 15 | 16 | } 17 | 18 | public model HttpRequest { 19 | var _url: String 20 | var data: HashMap 21 | new(url: String) { 22 | this._url = url 23 | this.data = HashMap() 24 | } 25 | public function pathParams -> RequestParams { 26 | todo() 27 | } 28 | /// Returns the method of the request, 29 | public function method -> String { 30 | todo() 31 | } 32 | /// Returns the URL route of the request. 33 | public function url -> String { 34 | return this._url 35 | } 36 | /// Gets the complete body buffer of the String asynchronously. 37 | public async function body -> Buffer { 38 | todo() 39 | } 40 | /// Returns true if the request was completed successfully. 41 | public function isCompleted -> boolean { 42 | todo() 43 | } 44 | /// Returns the headers stored in the request. 45 | public function headers -> RequestHeaders { 46 | todo() 47 | } 48 | /// Stores data on the request object. 49 | public function storeData(key: String, value: String) { 50 | var {} = this.data.set(key, value) 51 | } 52 | /// Creates a new response model from the request. 53 | public function respond -> HttpResponse { 54 | return HttpResponse.from(this) 55 | } 56 | /// Returns an error based on this request. 57 | public function createError( 58 | message: String, 59 | statusCode?: i32 60 | ) -> HttpRequestError { 61 | return HttpRequestError(message, this, statusCode) 62 | } 63 | } 64 | 65 | public model RequestHeaders { 66 | function get(name: String) -> ?String { 67 | todo() 68 | } 69 | function has(name: String) -> boolean { 70 | todo() 71 | } 72 | /// Returns the 73 | public function getOrErr(name: String) -> Outcome 74 | { 75 | todo() 76 | } 77 | } 78 | public model RequestParams { 79 | public function get(name: String) -> ?String { 80 | todo() 81 | } 82 | public function getOrErr(name: String) -> Outcome 83 | { 84 | todo() 85 | } 86 | function has(name: String) -> boolean { 87 | todo() 88 | } 89 | } 90 | 91 | 92 | /// An error that occurs on the HTTP server while it is active. 93 | /// 94 | /// The error is non-fatal, meaning that it does not lead to the 95 | /// crashing of the server when it happens. 96 | public model HttpRequestError implements Display { 97 | var message: String 98 | var route: String 99 | var statusCode: i32 100 | new(message: String, request: HttpRequest, statusCode?: i32) { 101 | this.message = message 102 | this.route = request.url() 103 | this.statusCode = statusCode.valueOr(400) 104 | } 105 | public function [Display.toString] -> String { 106 | return "Error: " + this.message 107 | } 108 | } -------------------------------------------------------------------------------- /source/cli/src/options.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, fmt::Display, path::PathBuf}; 2 | 3 | pub struct CliObject { 4 | pub command: Option, 5 | pub file: Option, 6 | pub arguments: HashMap, 7 | } 8 | 9 | pub enum CliCommand { 10 | Run, 11 | Build, 12 | Check, 13 | Test, 14 | Eval, 15 | Help, 16 | Format, 17 | Version, 18 | Custom(String), 19 | } 20 | 21 | pub enum CliError { 22 | InvalidArgument(String), 23 | DuplicateOption(String), 24 | EntryFileNotSpecified, 25 | } 26 | 27 | impl Display for CliError { 28 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 29 | let message = match self { 30 | CliError::InvalidArgument(option) => format!("Unknown option: {}", option), 31 | CliError::DuplicateOption(option) => format!( 32 | "Duplicate option: {}. The option {} is defined more than once.", 33 | option, option 34 | ), 35 | CliError::EntryFileNotSpecified => format!("Entry file not specified."), 36 | }; 37 | write!(f, "{message}",) 38 | } 39 | } 40 | 41 | /// Parses the command line arguments to setup Whirlwind. 42 | pub fn parse_cli() -> Result { 43 | let mut args = std::env::args().skip(1); 44 | 45 | let mut arguments = HashMap::new(); 46 | let mut file = None; 47 | let command = match args.next().as_ref().map(|a| a.as_str()) { 48 | Some("run") => CliCommand::Run, 49 | Some("check") => CliCommand::Check, 50 | Some("help" | "h") => CliCommand::Help, 51 | Some("version" | "v") => CliCommand::Version, 52 | Some("build") => CliCommand::Build, 53 | Some("format") => CliCommand::Format, 54 | Some("eval") => CliCommand::Eval, 55 | Some("test") => CliCommand::Test, 56 | Some(string) => CliCommand::Custom(string.to_string()), 57 | None => { 58 | return Ok(CliObject { 59 | command: None, 60 | file: None, 61 | arguments, 62 | }) 63 | } 64 | }; 65 | 66 | if matches!( 67 | command, 68 | CliCommand::Run 69 | | CliCommand::Build 70 | | CliCommand::Eval 71 | | CliCommand::Test 72 | | CliCommand::Check 73 | ) { 74 | let entry_file = args.next(); 75 | if entry_file.is_none() { 76 | return Err(CliError::EntryFileNotSpecified); 77 | } 78 | let entry_file = entry_file.unwrap(); 79 | let path = PathBuf::from(entry_file); 80 | file = Some(path); 81 | } 82 | 83 | for arg in args { 84 | let tuples = arg.split("=").collect::>(); 85 | if tuples.len() != 2 { 86 | return Err(CliError::InvalidArgument(arg)); 87 | } 88 | let option = tuples[0]; 89 | let value = tuples[1]; 90 | if arguments.get(option).is_some() { 91 | return Err(CliError::DuplicateOption(arg)); 92 | } 93 | arguments.insert(option.to_string(), value.to_string()); 94 | } 95 | Ok(CliObject { 96 | command: Some(command), 97 | file, 98 | arguments, 99 | }) 100 | } 101 | -------------------------------------------------------------------------------- /source/library/core/collections/hashMap.wrl: -------------------------------------------------------------------------------- 1 | module hashMap 2 | 3 | use core.sentinels.todo 4 | use core.interfaces.Default 5 | use core.iterate.{AsIterator, Iterable} 6 | use core.numeric.i32 7 | use ntuple.Tuple 8 | 9 | public interface Hasher { 10 | 11 | } 12 | 13 | public interface Hash { 14 | /// Feeds this value into a given hasher. 15 | // public function hash(state: H) 16 | } 17 | 18 | import "internal:map" { 19 | /// Creates a new Hashmap and returns its unique identifier. 20 | "newMap" as function newMap() -> i32; 21 | } 22 | 23 | /// An interface to be implemented by models that can be transformed into 24 | /// a hashmap of sorts. 25 | public interface ToMap { 26 | public function toMap -> HashMap 27 | } 28 | 29 | /// A hashmap implementation in Whirlwind. 30 | /// 31 | /// Hashmaps are used to store key-value pairs. 32 | public model HashMap 33 | implements AsIterator, HashMapIterable> 34 | + Default 35 | { 36 | var id: i32 37 | 38 | new(){ 39 | this.id = newMap() 40 | } 41 | 42 | public function [AsIterator.iter] -> HashMapIterable { 43 | return HashMapIterable(this) 44 | } 45 | static function [Default.init] -> This { 46 | return HashMap() 47 | } 48 | /// Inserts a key-value pair into the hashmap. 49 | /// 50 | /// If the key is already present in the map, then it will be replaced. 51 | /// ### Usage 52 | /// ``` 53 | /// var map = HashMap() 54 | /// map.set("hello", "world") 55 | /// 56 | /// assert(map.get("hello")).equals(some("world")) 57 | /// ``` 58 | public function set(key: K, value: V) -> V { 59 | todo() 60 | } 61 | /// Retrieves a value from the hashmap. 62 | /// ### Usage 63 | /// ``` 64 | /// var map = HashMap() 65 | /// map.set(10, "greetings") 66 | /// 67 | /// var value = map.get(10) 68 | /// assert(value).equals(some("greetings")) 69 | /// ``` 70 | public function get(key: K) -> ?V { 71 | todo() 72 | } 73 | /// Removes a key-value pair from the hashmap. 74 | /// ### Usage 75 | /// ``` 76 | /// 77 | /// ``` 78 | public function remove(key: K) -> ?V { 79 | todo() 80 | } 81 | /// Returns the number of elements in the map. 82 | public function length -> i32 { 83 | todo() 84 | } 85 | /// Returns an iterator over the keys in the map. 86 | public function keys -> HashMapKeys { 87 | todo() 88 | } 89 | /// Returns an iterator over the values in the map. 90 | public function values -> HashMapValues { 91 | todo() 92 | } 93 | } 94 | 95 | public model HashMapKeys implements Iterable { 96 | 97 | public function [Iterable.next] -> ?K { 98 | todo() 99 | } 100 | } 101 | 102 | public model HashMapValues implements Iterable { 103 | 104 | public function [Iterable.next] -> ?V { 105 | todo() 106 | } 107 | } 108 | 109 | public model HashMapIterable implements Iterable> { 110 | var _map: HashMap 111 | 112 | new(map: HashMap) { 113 | this._map = map 114 | } 115 | 116 | public function [Iterable.next] -> ?Tuple { 117 | todo() 118 | } 119 | } 120 | 121 | 122 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | release 3 | node_modules 4 | .Deps 5 | .env 6 | .VSCodeCounter 7 | vault 8 | 9 | 10 | 11 | # Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore 12 | 13 | # Logs 14 | 15 | logs 16 | _.log 17 | npm-debug.log_ 18 | yarn-debug.log* 19 | yarn-error.log* 20 | lerna-debug.log* 21 | .pnpm-debug.log* 22 | 23 | # Caches 24 | 25 | .cache 26 | 27 | # Diagnostic reports (https://nodejs.org/api/report.html) 28 | 29 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 30 | 31 | # Runtime data 32 | 33 | pids 34 | _.pid 35 | _.seed 36 | *.pid.lock 37 | 38 | # Directory for instrumented libs generated by jscoverage/JSCover 39 | 40 | lib-cov 41 | 42 | # Coverage directory used by tools like istanbul 43 | 44 | coverage 45 | *.lcov 46 | 47 | # nyc test coverage 48 | 49 | .nyc_output 50 | 51 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 52 | 53 | .grunt 54 | 55 | # Bower dependency directory (https://bower.io/) 56 | 57 | bower_components 58 | 59 | # node-waf configuration 60 | 61 | .lock-wscript 62 | 63 | # Compiled binary addons (https://nodejs.org/api/addons.html) 64 | 65 | build/Release 66 | 67 | # Dependency directories 68 | 69 | node_modules/ 70 | jspm_packages/ 71 | 72 | # Snowpack dependency directory (https://snowpack.dev/) 73 | 74 | web_modules/ 75 | 76 | # TypeScript cache 77 | 78 | *.tsbuildinfo 79 | 80 | # Optional npm cache directory 81 | 82 | .npm 83 | 84 | # Optional eslint cache 85 | 86 | .eslintcache 87 | 88 | # Optional stylelint cache 89 | 90 | .stylelintcache 91 | 92 | # Microbundle cache 93 | 94 | .rpt2_cache/ 95 | .rts2_cache_cjs/ 96 | .rts2_cache_es/ 97 | .rts2_cache_umd/ 98 | 99 | # Optional REPL history 100 | 101 | .node_repl_history 102 | 103 | # Output of 'npm pack' 104 | 105 | *.tgz 106 | 107 | # Yarn Integrity file 108 | 109 | .yarn-integrity 110 | 111 | # dotenv environment variable files 112 | 113 | .env 114 | .env.development.local 115 | .env.test.local 116 | .env.production.local 117 | .env.local 118 | 119 | # parcel-bundler cache (https://parceljs.org/) 120 | 121 | .parcel-cache 122 | 123 | # Next.js build output 124 | 125 | .next 126 | out 127 | 128 | # Nuxt.js build / generate output 129 | 130 | .nuxt 131 | dist 132 | 133 | # Gatsby files 134 | 135 | # Comment in the public line in if your project uses Gatsby and not Next.js 136 | 137 | # https://nextjs.org/blog/next-9-1#public-directory-support 138 | 139 | # public 140 | 141 | # vuepress build output 142 | 143 | .vuepress/dist 144 | 145 | # vuepress v2.x temp and cache directory 146 | 147 | .temp 148 | 149 | # Docusaurus cache and generated files 150 | 151 | .docusaurus 152 | 153 | # Serverless directories 154 | 155 | .serverless/ 156 | 157 | # FuseBox cache 158 | 159 | .fusebox/ 160 | 161 | # DynamoDB Local files 162 | 163 | .dynamodb/ 164 | 165 | # TernJS port file 166 | 167 | .tern-port 168 | 169 | # Stores VSCode versions used for testing VSCode extensions 170 | 171 | .vscode-test 172 | 173 | # yarn v2 174 | 175 | .yarn/cache 176 | .yarn/unplugged 177 | .yarn/build-state.yml 178 | .yarn/install-state.gz 179 | .pnp.* 180 | 181 | # IntelliJ based IDEs 182 | .idea 183 | 184 | # Finder (MacOS) folder config 185 | .DS_Store 186 | -------------------------------------------------------------------------------- /source/compiler/analyzer/src/programdiagnostic.rs: -------------------------------------------------------------------------------- 1 | use crate::PathIndex; 2 | use errors::{ContextError, ImportError, LexError, ParseError, TypeError, Warning}; 3 | 4 | #[derive(PartialEq, Debug)] 5 | pub struct ProgramDiagnostic { 6 | pub offending_file: PathIndex, 7 | pub _type: DiagnosticType, 8 | } 9 | 10 | #[derive(PartialEq, Debug)] 11 | pub enum DiagnosticType { 12 | Error(Error), 13 | Warning(Warning), 14 | } 15 | 16 | impl ProgramDiagnostic { 17 | pub fn span(&self) -> ast::Span { 18 | match &self._type { 19 | DiagnosticType::Error(error) => match error { 20 | Error::Lexical(le) => match le.position { 21 | errors::LexErrorPos::Point(point) => ast::Span::at(point), 22 | errors::LexErrorPos::Span(span) => span, 23 | }, 24 | Error::Syntax(syntax) => syntax.span, 25 | Error::Context(ctx) => ctx.span, 26 | Error::Importing(import) => import.span.unwrap_or_default(), 27 | Error::Typing(typing) => typing.span, 28 | }, 29 | DiagnosticType::Warning(warning) => warning.span, 30 | } 31 | } 32 | 33 | pub fn is_error(&self) -> bool { 34 | matches!(self._type, DiagnosticType::Error(_)) 35 | } 36 | 37 | pub fn is_warning(&self) -> bool { 38 | matches!(self._type, DiagnosticType::Warning(_)) 39 | } 40 | 41 | pub fn contextual(offending_file: PathIndex, error: ContextError) -> Self { 42 | ProgramDiagnostic { 43 | offending_file, 44 | _type: DiagnosticType::Error(Error::Context(error)), 45 | } 46 | } 47 | 48 | pub fn typing( 49 | offending_file: PathIndex, 50 | errortype: errors::TypeErrorType, 51 | span: ast::Span, 52 | ) -> ProgramDiagnostic { 53 | ProgramDiagnostic { 54 | offending_file, 55 | _type: DiagnosticType::Error(Error::Typing(TypeError { 56 | _type: errortype, 57 | span, 58 | })), 59 | } 60 | } 61 | } 62 | 63 | #[derive(PartialEq, Debug)] 64 | pub enum Error { 65 | Lexical(LexError), 66 | Syntax(ParseError), 67 | Context(ContextError), 68 | Importing(ImportError), 69 | Typing(TypeError), 70 | } 71 | 72 | impl std::fmt::Display for Error { 73 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 74 | write!( 75 | f, 76 | "{}", 77 | match self { 78 | Error::Context(context_err) => context_err._type.to_string(), 79 | Error::Importing(import_err) => import_err._type.to_string(), 80 | Error::Lexical(lex_err) => lex_err.error_type.to_string(), 81 | Error::Syntax(syntax) => syntax._type.to_string(), 82 | Error::Typing(type_error) => type_error._type.to_string(), 83 | } 84 | ) 85 | } 86 | } 87 | 88 | impl std::fmt::Display for ProgramDiagnostic { 89 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 90 | write!( 91 | f, 92 | "{}", 93 | match &self._type { 94 | DiagnosticType::Error(error) => error.to_string(), 95 | DiagnosticType::Warning(warning) => warning.warning_type.to_string(), 96 | } 97 | ) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /source/library/core/memory/memory.wrl: -------------------------------------------------------------------------------- 1 | module memory 2 | 3 | use core.boolean.boolean 4 | use core.internals.never 5 | use core.numeric.i32 6 | use core.sentinels.todo 7 | 8 | public use ref.WeakRef 9 | 10 | public model Ptr { 11 | public var address: i32 12 | 13 | /// Determines whether two variables point to the same location in memory 14 | /// by comparing their pointer values. 15 | /// 16 | /// This function is unlike regular equality with the `==` operator, which 17 | /// compares values by matching their bytes. 18 | /// ### Usage 19 | /// ``` 20 | /// use core.memory.Ptr 21 | /// var arr1 = [] 22 | /// var arr2 = arr1 23 | /// 24 | /// assertThat(Ptr.same(arr1, arr2)) 25 | /// assertThat(Ptr.same(arr2, arr1)) 26 | /// ``` 27 | public static function same(left: T, right: T) -> boolean { 28 | return Ptr.getOffset(left) == Ptr.getOffset(right) 29 | } 30 | 31 | 32 | /// Returns the address of an object instance in linear memory. 33 | public static function getOffset(value: T) -> i32 { 34 | // False recursion. The implementation of this function 35 | // is built into the compiler. 36 | return Ptr.getOffset(value) 37 | } 38 | } 39 | 40 | 41 | /// Given a number of bytes, `malloc` allocates a block of memory on the heap, 42 | /// returning a pointer to the 43 | /// start of the block. 44 | /// 45 | /// The memory is not initialized, so the contents are undefined. 46 | /// Use `calloc` if you want the memory to be initialized to zero. 47 | /// 48 | /// @param size - The number of bytes to allocate. 49 | /// @returns A pointer to the start of the newly allocated block of memory. 50 | /// 51 | /// ### Usage 52 | /// ``` 53 | /// use core.memory.malloc 54 | /// 55 | /// var ptr = malloc(1024) 56 | /// assertThat(ptr.address > 0) 57 | /// ``` 58 | public function malloc(size: i32) -> Ptr { 59 | todo() 60 | } 61 | 62 | 63 | /// Given a number of bytes, `calloc` allocates a block of memory on the heap, 64 | /// returning a pointer to the start of the block. 65 | /// 66 | /// Unlike with `malloc`, the memory is initialized to zero. 67 | /// 68 | /// @param size - The number of bytes to allocate. 69 | /// @returns A pointer to the start of the newly allocated block of memory. 70 | /// 71 | /// ### Usage 72 | /// ``` 73 | /// use core.memory.calloc 74 | /// 75 | /// var ptr = calloc(1024) 76 | /// assertThat(ptr.address > 0) 77 | /// ``` 78 | public function calloc(size: i32) -> Ptr { 79 | todo() 80 | } 81 | 82 | 83 | /// Frees a block of memory previously allocated with `malloc` or `calloc`. 84 | /// 85 | /// ### Usage 86 | /// ``` 87 | /// use core.memory.free 88 | /// 89 | /// var ptr = malloc(1024) 90 | /// free(ptr) 91 | /// ``` 92 | public function free(p: Ptr) { 93 | todo() 94 | } 95 | 96 | 97 | 98 | 99 | /// Returns the last value on the call stack. 100 | /// 101 | /// It will crash the program if it is called when 102 | /// the call stack has more than one value in it, 103 | /// or when the call stack is empty. 104 | public function stackLast -> never { 105 | // False recursion. The implementation of this function 106 | // is built into the compiler. 107 | return stackLast() 108 | } 109 | 110 | 111 | /// Swaps the memory address of two values. 112 | /// # Usage 113 | /// ``` 114 | /// use core.memory.swap 115 | /// 116 | /// var a = [1, 2, 3] 117 | /// var b = [4, 5, 6] 118 | /// swap(a, b) 119 | /// 120 | /// assert(a).equals([4, 5, 6]) 121 | /// assert(b).equals([1, 2, 3]) 122 | /// ``` 123 | public function swap(a: T, b: T) { 124 | todo() 125 | } -------------------------------------------------------------------------------- /.discontinued/bytecode/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use analyzer::{Module, Standpoint, CORE_LIBRARY_PATH}; 4 | 5 | use crate::{from_text::bytecode_from_text, CallablePtr, Opcode}; 6 | 7 | #[test] 8 | fn retrieves_main_module() { 9 | let text = String::from( 10 | " 11 | module Main; 12 | 13 | public function main() { 14 | 15 | } 16 | ", 17 | ); 18 | let mut module = Module::from_text(&text); 19 | module.module_path = Some(PathBuf::from("testing://Main.wrl")); 20 | let corelib_path = PathBuf::from(CORE_LIBRARY_PATH); 21 | let mut standpoint = Standpoint::new(true, Some(corelib_path)); 22 | let idx = standpoint.add_module(module).unwrap(); 23 | standpoint.validate(); 24 | standpoint.entry_module = idx; 25 | assert!( 26 | standpoint 27 | .diagnostics 28 | .iter() 29 | .filter(|d| d.is_error()) 30 | .count() 31 | == 0 32 | ); 33 | 34 | println!("The main function is {:#?}", standpoint.main()); 35 | } 36 | 37 | // #[test] 38 | fn simple_empty_main() { 39 | let object = bytecode_from_text( 40 | " 41 | module main; 42 | 43 | function main() { 44 | 45 | } 46 | ", 47 | ) 48 | .unwrap(); 49 | assert_eq!( 50 | object.callables, 51 | vec![CallablePtr { 52 | name: String::from("main"), 53 | param_count: 0, 54 | start: 10, 55 | end: 0, 56 | calls: 0 57 | }] 58 | ); 59 | assert_eq!( 60 | object.instructions, 61 | vec![ 62 | Opcode::LoadFunctionPtr.into(), 63 | 0, 64 | 0, 65 | 0, 66 | 0, // address of main. 67 | ] 68 | ) 69 | } 70 | 71 | // #[test] 72 | fn function_call() { 73 | let object = bytecode_from_text( 74 | " 75 | module main; 76 | 77 | function main() { 78 | anotherFunction() 79 | } 80 | function anotherFunction() { 81 | 82 | } 83 | ", 84 | ) 85 | .unwrap(); 86 | assert_eq!( 87 | object.callables, 88 | vec![ 89 | CallablePtr { 90 | name: String::from("main"), 91 | param_count: 0, 92 | start: 7, // After pad + call + index + exit. 93 | end: 0, 94 | calls: 0 95 | }, 96 | CallablePtr { 97 | name: String::from("anotherFunction"), 98 | param_count: 0, 99 | start: 15, 100 | end: 0, 101 | calls: 0 102 | } 103 | ] 104 | ) 105 | } 106 | 107 | // #[test] 108 | fn recursive_function_call() { 109 | let object = bytecode_from_text( 110 | " 111 | module main; 112 | 113 | function main() { 114 | anotherFunction() 115 | } 116 | function anotherFunction() { 117 | main(); 118 | anotherFunction(); 119 | } 120 | ", 121 | ) 122 | .unwrap(); 123 | assert_eq!( 124 | object.callables, 125 | vec![ 126 | CallablePtr { 127 | name: String::from("main"), 128 | param_count: 0, 129 | start: 7, // After pad + call + index + exit. 130 | end: 0, 131 | calls: 0 132 | }, 133 | CallablePtr { 134 | name: String::from("anotherFunction"), 135 | param_count: 0, 136 | start: 15, 137 | end: 0, 138 | calls: 0 139 | } 140 | ] 141 | ) 142 | } 143 | -------------------------------------------------------------------------------- /source/compiler/lexer/src/text_lexer.rs: -------------------------------------------------------------------------------- 1 | use std::str::Chars; 2 | 3 | use ast::{Span, Token}; 4 | use errors::LexError; 5 | 6 | use crate::{lexer_inner::LexerInner, Lexer}; 7 | 8 | /// A generic lexer for Whirlwind text. 9 | pub struct TextLexer> { 10 | chars: I, 11 | position: u32, 12 | line: u32, 13 | span_start: [u32; 2], 14 | errors: Vec, 15 | stash: Option, 16 | /// A vector containing the lengths of each line in the string. 17 | /// It is useful for recomputing the number of characters in the string. 18 | /// It does not contain the terminators at the end of the line. 19 | pub line_lengths: Vec, 20 | module_id: usize, 21 | saved: Option, 22 | } 23 | 24 | impl> From for TextLexer { 25 | fn from(value: I) -> Self { 26 | TextLexer { 27 | module_id: 0, 28 | chars: value, 29 | position: 1, 30 | line: 1, 31 | span_start: [1, 1], 32 | errors: vec![], 33 | stash: None, 34 | line_lengths: vec![], 35 | saved: None, 36 | } 37 | } 38 | } 39 | 40 | impl> LexerInner for TextLexer { 41 | fn next_char(&mut self) -> Option { 42 | if let Some(ch) = self.chars.next() { 43 | if ch == '\n' { 44 | // Store line length and move to next. 45 | self.line_lengths.push(self.position - 1); 46 | self.position = 1; 47 | self.line += 1; 48 | } else { 49 | self.position += 1; 50 | } 51 | Some(ch) 52 | } else { 53 | // Store the length of the last line. 54 | self.line_lengths.push(self.position - 1); 55 | None 56 | } 57 | } 58 | 59 | fn start_span(&mut self) { 60 | self.span_start = [self.line, self.position - 1]; 61 | } 62 | 63 | fn report_span(&self) -> Span { 64 | Span { 65 | start: self.span_start, 66 | end: self.current_pos(), 67 | } 68 | } 69 | 70 | fn current_pos(&self) -> [u32; 2] { 71 | [self.line, self.position] 72 | } 73 | 74 | fn add_error(&mut self, error: LexError) { 75 | self.errors.push(error) 76 | } 77 | 78 | fn remove_stashed(&mut self) -> Option { 79 | self.stash.take() 80 | } 81 | 82 | fn stash(&mut self, ch: char) { 83 | self.stash = Some(ch) 84 | } 85 | 86 | fn line_lengths(&self) -> &[u32] { 87 | &self.line_lengths 88 | } 89 | 90 | fn save_token(&mut self, token: Token) { 91 | self.saved = Some(token) 92 | } 93 | 94 | fn remove_saved(&mut self) -> Option { 95 | self.saved.take() 96 | } 97 | } 98 | 99 | impl> Lexer for TextLexer { 100 | fn errors(&mut self) -> &mut Vec { 101 | &mut self.errors 102 | } 103 | 104 | fn module_id(&self) -> usize { 105 | self.module_id 106 | } 107 | } 108 | 109 | impl> Iterator for TextLexer { 110 | type Item = Token; 111 | 112 | fn next(&mut self) -> Option { 113 | self.get_next_token() 114 | } 115 | } 116 | /// Lexes valid code and returns a token Iterator. 117 | pub fn lex_text(input: &str) -> TextLexer { 118 | TextLexer { 119 | chars: input.chars(), 120 | position: 1, 121 | line: 1, 122 | span_start: [1, 1], 123 | errors: vec![], 124 | stash: None, 125 | line_lengths: vec![], 126 | saved: None, 127 | module_id: 0, 128 | } 129 | } 130 | --------------------------------------------------------------------------------