From 6afe3ec5ce68ea4c08d7fe65e8a2df00c3c213d5 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 16:04:00 +0000 Subject: [PATCH 01/37] Setup nix This is for my development and not a fully planned part of the change --- flake.lock | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 39 ++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8d6c6a0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,96 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1759199574, + "narHash": "sha256-w24RYly3VSVKp98rVfCI1nFYfQ0VoWmShtKPCbXgK6A=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "381776b12d0d125edd7c1930c2041a1471e586c0", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..39d3adb --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +{ + description = "Environment for amber-lsp."; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + nixpkgs, + rust-overlay, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { inherit system overlays; }; + + rust-build = pkgs.rust-bin.stable.latest.default.override { + extensions = [ "rust-src" ]; + }; + in + { + devShells.default = + with pkgs; + mkShell { + buildInputs = [ + rust-build + bacon + amber-lang + ]; + }; + } + ); +} From 9146ddc7e44bd7193eb5d810b67ce5e8fb05e1ce Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 17:24:39 +0000 Subject: [PATCH 02/37] Changed to workspace --- Cargo.toml | 36 +++++++++---------- lsp/Cargo.toml | 30 ++++++++++++++++ .../resources}/alpha034/std/main.ab | 0 .../resources}/alpha035/std/array.ab | 0 .../resources}/alpha035/std/date.ab | 0 .../resources}/alpha035/std/env.ab | 0 .../resources}/alpha035/std/fs.ab | 0 .../resources}/alpha035/std/http.ab | 0 .../resources}/alpha035/std/math.ab | 0 .../resources}/alpha035/std/text.ab | 0 .../resources}/alpha040/builtin.ab | 0 .../resources}/alpha040/std/array.ab | 0 .../resources}/alpha040/std/date.ab | 0 .../resources}/alpha040/std/env.ab | 0 .../resources}/alpha040/std/fs.ab | 0 .../resources}/alpha040/std/http.ab | 0 .../resources}/alpha040/std/math.ab | 0 .../resources}/alpha040/std/text.ab | 0 {src => lsp/src}/analysis/alpha034/exp.rs | 0 {src => lsp/src}/analysis/alpha034/global.rs | 0 {src => lsp/src}/analysis/alpha034/mod.rs | 0 {src => lsp/src}/analysis/alpha034/stmnts.rs | 0 {src => lsp/src}/analysis/alpha035/exp.rs | 0 {src => lsp/src}/analysis/alpha035/global.rs | 0 {src => lsp/src}/analysis/alpha035/mod.rs | 0 {src => lsp/src}/analysis/alpha035/stmnts.rs | 0 {src => lsp/src}/analysis/alpha040/exp.rs | 0 {src => lsp/src}/analysis/alpha040/global.rs | 0 {src => lsp/src}/analysis/alpha040/mod.rs | 0 {src => lsp/src}/analysis/alpha040/stmnts.rs | 0 {src => lsp/src}/analysis/mod.rs | 0 {src => lsp/src}/analysis/types.rs | 0 {src => lsp/src}/backend.rs | 0 {src => lsp/src}/files.rs | 0 {src => lsp/src}/fs.rs | 0 .../src}/grammar/alpha034/expressions/and.rs | 0 .../alpha034/expressions/atom/array.rs | 0 .../grammar/alpha034/expressions/atom/bool.rs | 0 .../grammar/alpha034/expressions/atom/call.rs | 0 .../alpha034/expressions/atom/command.rs | 0 .../grammar/alpha034/expressions/atom/mod.rs | 0 .../grammar/alpha034/expressions/atom/null.rs | 0 .../alpha034/expressions/atom/number.rs | 0 .../alpha034/expressions/atom/parentheses.rs | 0 .../alpha034/expressions/atom/status.rs | 0 .../grammar/alpha034/expressions/atom/text.rs | 0 .../grammar/alpha034/expressions/atom/var.rs | 0 .../src}/grammar/alpha034/expressions/cast.rs | 0 .../alpha034/expressions/comparison.rs | 0 .../src}/grammar/alpha034/expressions/is.rs | 0 .../src}/grammar/alpha034/expressions/mod.rs | 0 .../src}/grammar/alpha034/expressions/or.rs | 0 .../grammar/alpha034/expressions/product.rs | 0 .../grammar/alpha034/expressions/range.rs | 0 .../src}/grammar/alpha034/expressions/sum.rs | 0 .../grammar/alpha034/expressions/ternary.rs | 0 .../grammar/alpha034/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha034/global.rs | 0 {src => lsp/src}/grammar/alpha034/lexer.rs | 0 {src => lsp/src}/grammar/alpha034/mod.rs | 0 {src => lsp/src}/grammar/alpha034/parser.rs | 0 .../src}/grammar/alpha034/semantic_tokens.rs | 0 .../src}/grammar/alpha034/statements/block.rs | 0 .../grammar/alpha034/statements/comment.rs | 0 .../grammar/alpha034/statements/const_init.rs | 0 .../grammar/alpha034/statements/failed.rs | 0 .../grammar/alpha034/statements/if_cond.rs | 0 .../grammar/alpha034/statements/keywords.rs | 0 .../src}/grammar/alpha034/statements/loops.rs | 0 .../src}/grammar/alpha034/statements/mod.rs | 0 .../grammar/alpha034/statements/modifiers.rs | 0 .../grammar/alpha034/statements/shebang.rs | 0 .../grammar/alpha034/statements/shorthands.rs | 0 .../grammar/alpha034/statements/var_init.rs | 0 .../grammar/alpha034/statements/var_set.rs | 0 .../src}/grammar/alpha035/expressions/and.rs | 0 .../alpha035/expressions/atom/array.rs | 0 .../grammar/alpha035/expressions/atom/bool.rs | 0 .../grammar/alpha035/expressions/atom/call.rs | 0 .../alpha035/expressions/atom/command.rs | 0 .../grammar/alpha035/expressions/atom/mod.rs | 0 .../grammar/alpha035/expressions/atom/null.rs | 0 .../alpha035/expressions/atom/number.rs | 0 .../alpha035/expressions/atom/parentheses.rs | 0 .../alpha035/expressions/atom/status.rs | 0 .../grammar/alpha035/expressions/atom/text.rs | 0 .../grammar/alpha035/expressions/atom/var.rs | 0 .../src}/grammar/alpha035/expressions/cast.rs | 0 .../alpha035/expressions/comparison.rs | 0 .../src}/grammar/alpha035/expressions/is.rs | 0 .../src}/grammar/alpha035/expressions/mod.rs | 0 .../src}/grammar/alpha035/expressions/or.rs | 0 .../grammar/alpha035/expressions/product.rs | 0 .../grammar/alpha035/expressions/range.rs | 0 .../src}/grammar/alpha035/expressions/sum.rs | 0 .../grammar/alpha035/expressions/ternary.rs | 0 .../grammar/alpha035/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha035/global.rs | 0 {src => lsp/src}/grammar/alpha035/lexer.rs | 0 {src => lsp/src}/grammar/alpha035/mod.rs | 0 {src => lsp/src}/grammar/alpha035/parser.rs | 0 .../src}/grammar/alpha035/semantic_tokens.rs | 0 .../src}/grammar/alpha035/statements/block.rs | 0 .../grammar/alpha035/statements/comment.rs | 0 .../grammar/alpha035/statements/const_init.rs | 0 .../grammar/alpha035/statements/failed.rs | 0 .../grammar/alpha035/statements/if_cond.rs | 0 .../grammar/alpha035/statements/keywords.rs | 0 .../src}/grammar/alpha035/statements/loops.rs | 0 .../src}/grammar/alpha035/statements/mod.rs | 0 .../grammar/alpha035/statements/modifiers.rs | 0 .../grammar/alpha035/statements/move_files.rs | 0 .../grammar/alpha035/statements/shebang.rs | 0 .../grammar/alpha035/statements/shorthands.rs | 0 .../grammar/alpha035/statements/var_init.rs | 0 .../grammar/alpha035/statements/var_set.rs | 0 .../src}/grammar/alpha040/expressions/and.rs | 0 .../alpha040/expressions/atom/array.rs | 0 .../grammar/alpha040/expressions/atom/bool.rs | 0 .../grammar/alpha040/expressions/atom/call.rs | 0 .../alpha040/expressions/atom/command.rs | 0 .../grammar/alpha040/expressions/atom/exit.rs | 0 .../grammar/alpha040/expressions/atom/mod.rs | 0 .../grammar/alpha040/expressions/atom/null.rs | 0 .../alpha040/expressions/atom/number.rs | 0 .../alpha040/expressions/atom/parentheses.rs | 0 .../alpha040/expressions/atom/status.rs | 0 .../grammar/alpha040/expressions/atom/text.rs | 0 .../grammar/alpha040/expressions/atom/var.rs | 0 .../src}/grammar/alpha040/expressions/cast.rs | 0 .../alpha040/expressions/comparison.rs | 0 .../src}/grammar/alpha040/expressions/is.rs | 0 .../src}/grammar/alpha040/expressions/mod.rs | 0 .../src}/grammar/alpha040/expressions/or.rs | 0 .../grammar/alpha040/expressions/product.rs | 0 .../grammar/alpha040/expressions/range.rs | 0 .../src}/grammar/alpha040/expressions/sum.rs | 0 .../grammar/alpha040/expressions/ternary.rs | 0 .../grammar/alpha040/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha040/global.rs | 0 {src => lsp/src}/grammar/alpha040/lexer.rs | 0 {src => lsp/src}/grammar/alpha040/mod.rs | 0 {src => lsp/src}/grammar/alpha040/parser.rs | 0 .../src}/grammar/alpha040/semantic_tokens.rs | 0 .../src}/grammar/alpha040/statements/block.rs | 0 .../grammar/alpha040/statements/comment.rs | 0 .../grammar/alpha040/statements/const_init.rs | 0 .../grammar/alpha040/statements/failed.rs | 0 .../grammar/alpha040/statements/if_cond.rs | 0 .../grammar/alpha040/statements/keywords.rs | 0 .../src}/grammar/alpha040/statements/loops.rs | 0 .../src}/grammar/alpha040/statements/mod.rs | 0 .../grammar/alpha040/statements/modifiers.rs | 0 .../grammar/alpha040/statements/move_files.rs | 0 .../grammar/alpha040/statements/shebang.rs | 0 .../grammar/alpha040/statements/shorthands.rs | 0 .../grammar/alpha040/statements/var_init.rs | 0 .../grammar/alpha040/statements/var_set.rs | 0 {src => lsp/src}/grammar/mod.rs | 0 {src => lsp/src}/lib.rs | 0 {src => lsp/src}/main.rs | 0 {src => lsp/src}/paths.rs | 0 {src => lsp/src}/stdlib.rs | 0 {src => lsp/src}/utils.rs | 0 164 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 lsp/Cargo.toml rename {resources => lsp/resources}/alpha034/std/main.ab (100%) rename {resources => lsp/resources}/alpha035/std/array.ab (100%) rename {resources => lsp/resources}/alpha035/std/date.ab (100%) rename {resources => lsp/resources}/alpha035/std/env.ab (100%) rename {resources => lsp/resources}/alpha035/std/fs.ab (100%) rename {resources => lsp/resources}/alpha035/std/http.ab (100%) rename {resources => lsp/resources}/alpha035/std/math.ab (100%) rename {resources => lsp/resources}/alpha035/std/text.ab (100%) rename {resources => lsp/resources}/alpha040/builtin.ab (100%) rename {resources => lsp/resources}/alpha040/std/array.ab (100%) rename {resources => lsp/resources}/alpha040/std/date.ab (100%) rename {resources => lsp/resources}/alpha040/std/env.ab (100%) rename {resources => lsp/resources}/alpha040/std/fs.ab (100%) rename {resources => lsp/resources}/alpha040/std/http.ab (100%) rename {resources => lsp/resources}/alpha040/std/math.ab (100%) rename {resources => lsp/resources}/alpha040/std/text.ab (100%) rename {src => lsp/src}/analysis/alpha034/exp.rs (100%) rename {src => lsp/src}/analysis/alpha034/global.rs (100%) rename {src => lsp/src}/analysis/alpha034/mod.rs (100%) rename {src => lsp/src}/analysis/alpha034/stmnts.rs (100%) rename {src => lsp/src}/analysis/alpha035/exp.rs (100%) rename {src => lsp/src}/analysis/alpha035/global.rs (100%) rename {src => lsp/src}/analysis/alpha035/mod.rs (100%) rename {src => lsp/src}/analysis/alpha035/stmnts.rs (100%) rename {src => lsp/src}/analysis/alpha040/exp.rs (100%) rename {src => lsp/src}/analysis/alpha040/global.rs (100%) rename {src => lsp/src}/analysis/alpha040/mod.rs (100%) rename {src => lsp/src}/analysis/alpha040/stmnts.rs (100%) rename {src => lsp/src}/analysis/mod.rs (100%) rename {src => lsp/src}/analysis/types.rs (100%) rename {src => lsp/src}/backend.rs (100%) rename {src => lsp/src}/files.rs (100%) rename {src => lsp/src}/fs.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha034/global.rs (100%) rename {src => lsp/src}/grammar/alpha034/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha034/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/parser.rs (100%) rename {src => lsp/src}/grammar/alpha034/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha035/global.rs (100%) rename {src => lsp/src}/grammar/alpha035/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha035/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/parser.rs (100%) rename {src => lsp/src}/grammar/alpha035/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/move_files.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/exit.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha040/global.rs (100%) rename {src => lsp/src}/grammar/alpha040/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha040/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/parser.rs (100%) rename {src => lsp/src}/grammar/alpha040/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/move_files.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/mod.rs (100%) rename {src => lsp/src}/lib.rs (100%) rename {src => lsp/src}/main.rs (100%) rename {src => lsp/src}/paths.rs (100%) rename {src => lsp/src}/stdlib.rs (100%) rename {src => lsp/src}/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 06c8ab1..7b64cc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,19 @@ -[package] -name = "amber-lsp" -version = "0.1.10" -edition = "2021" -repository = "https://github.com/KrosFire/amber-lsp" +[workspace] +resolver = "2" +members = ["lsp"] -[dependencies] -tokio = { version = "1.39.1", features = ["full"] } +[workspace.dependencies] +tokio = "1.39.1" tower-lsp-server = "0.22.0" -phf = { version = "0.11", features = ["macros"] } +phf = "0.11" dashmap = "6.0.1" ropey = "1.6.1" -chumsky = { git = "https://github.com/KrosFire/chumsky", rev = "406ea80", features = [ - "default", - "label", -] } +chumsky = { git = "https://github.com/KrosFire/chumsky", rev = "406ea80" } heraclitus-compiler = "1.8.2" serde_json = "1.0.128" rangemap = "1.5.1" indexmap = "2.6.0" -clap = { version = "4.5.21", features = ["derive"] } +clap = "4.5.21" include_dir = "0.7.4" tracing = "0.1" tracing-subscriber = "0.3" @@ -26,11 +21,8 @@ rustc-hash = "2.1.1" tracing-appender = "0.2.3" thiserror = "2.0.12" -[dev-dependencies] -insta = { version = "1.39.0", features = ["yaml"] } - -[build-dependencies] -fs_extra = "1.3.0" +# Dev +insta = "1.39.0" # The profile that 'cargo dist' will build with [profile.dist] @@ -46,7 +38,13 @@ ci = "github" # The installers to generate for each app installers = [] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] +targets = [ + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-pc-windows-msvc", +] # Publish jobs to run in CI pr-run-mode = "plan" # Skip checking whether the specified configuration files are up to date diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml new file mode 100644 index 0000000..8f55eac --- /dev/null +++ b/lsp/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "amber-lsp" +version = "0.1.10" +edition = "2021" +repository = "https://github.com/KrosFire/amber-lsp" + +[dependencies] +tokio = { workspace = true, features = ["full"] } +tower-lsp-server = { workspace = true } +phf = { workspace = true, features = ["macros"] } +dashmap = { workspace = true } +ropey = { workspace = true } +chumsky = { workspace = true, features = ["default", "label"] } +heraclitus-compiler = { workspace = true } +serde_json = { workspace = true } +rangemap = { workspace = true } +indexmap = { workspace = true } +clap = { workspace = true, features = ["derive"] } +include_dir = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +rustc-hash = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +insta = { workspace = true, features = ["yaml"] } + +[build-dependencies] +fs_extra = "1.3.0" diff --git a/resources/alpha034/std/main.ab b/lsp/resources/alpha034/std/main.ab similarity index 100% rename from resources/alpha034/std/main.ab rename to lsp/resources/alpha034/std/main.ab diff --git a/resources/alpha035/std/array.ab b/lsp/resources/alpha035/std/array.ab similarity index 100% rename from resources/alpha035/std/array.ab rename to lsp/resources/alpha035/std/array.ab diff --git a/resources/alpha035/std/date.ab b/lsp/resources/alpha035/std/date.ab similarity index 100% rename from resources/alpha035/std/date.ab rename to lsp/resources/alpha035/std/date.ab diff --git a/resources/alpha035/std/env.ab b/lsp/resources/alpha035/std/env.ab similarity index 100% rename from resources/alpha035/std/env.ab rename to lsp/resources/alpha035/std/env.ab diff --git a/resources/alpha035/std/fs.ab b/lsp/resources/alpha035/std/fs.ab similarity index 100% rename from resources/alpha035/std/fs.ab rename to lsp/resources/alpha035/std/fs.ab diff --git a/resources/alpha035/std/http.ab b/lsp/resources/alpha035/std/http.ab similarity index 100% rename from resources/alpha035/std/http.ab rename to lsp/resources/alpha035/std/http.ab diff --git a/resources/alpha035/std/math.ab b/lsp/resources/alpha035/std/math.ab similarity index 100% rename from resources/alpha035/std/math.ab rename to lsp/resources/alpha035/std/math.ab diff --git a/resources/alpha035/std/text.ab b/lsp/resources/alpha035/std/text.ab similarity index 100% rename from resources/alpha035/std/text.ab rename to lsp/resources/alpha035/std/text.ab diff --git a/resources/alpha040/builtin.ab b/lsp/resources/alpha040/builtin.ab similarity index 100% rename from resources/alpha040/builtin.ab rename to lsp/resources/alpha040/builtin.ab diff --git a/resources/alpha040/std/array.ab b/lsp/resources/alpha040/std/array.ab similarity index 100% rename from resources/alpha040/std/array.ab rename to lsp/resources/alpha040/std/array.ab diff --git a/resources/alpha040/std/date.ab b/lsp/resources/alpha040/std/date.ab similarity index 100% rename from resources/alpha040/std/date.ab rename to lsp/resources/alpha040/std/date.ab diff --git a/resources/alpha040/std/env.ab b/lsp/resources/alpha040/std/env.ab similarity index 100% rename from resources/alpha040/std/env.ab rename to lsp/resources/alpha040/std/env.ab diff --git a/resources/alpha040/std/fs.ab b/lsp/resources/alpha040/std/fs.ab similarity index 100% rename from resources/alpha040/std/fs.ab rename to lsp/resources/alpha040/std/fs.ab diff --git a/resources/alpha040/std/http.ab b/lsp/resources/alpha040/std/http.ab similarity index 100% rename from resources/alpha040/std/http.ab rename to lsp/resources/alpha040/std/http.ab diff --git a/resources/alpha040/std/math.ab b/lsp/resources/alpha040/std/math.ab similarity index 100% rename from resources/alpha040/std/math.ab rename to lsp/resources/alpha040/std/math.ab diff --git a/resources/alpha040/std/text.ab b/lsp/resources/alpha040/std/text.ab similarity index 100% rename from resources/alpha040/std/text.ab rename to lsp/resources/alpha040/std/text.ab diff --git a/src/analysis/alpha034/exp.rs b/lsp/src/analysis/alpha034/exp.rs similarity index 100% rename from src/analysis/alpha034/exp.rs rename to lsp/src/analysis/alpha034/exp.rs diff --git a/src/analysis/alpha034/global.rs b/lsp/src/analysis/alpha034/global.rs similarity index 100% rename from src/analysis/alpha034/global.rs rename to lsp/src/analysis/alpha034/global.rs diff --git a/src/analysis/alpha034/mod.rs b/lsp/src/analysis/alpha034/mod.rs similarity index 100% rename from src/analysis/alpha034/mod.rs rename to lsp/src/analysis/alpha034/mod.rs diff --git a/src/analysis/alpha034/stmnts.rs b/lsp/src/analysis/alpha034/stmnts.rs similarity index 100% rename from src/analysis/alpha034/stmnts.rs rename to lsp/src/analysis/alpha034/stmnts.rs diff --git a/src/analysis/alpha035/exp.rs b/lsp/src/analysis/alpha035/exp.rs similarity index 100% rename from src/analysis/alpha035/exp.rs rename to lsp/src/analysis/alpha035/exp.rs diff --git a/src/analysis/alpha035/global.rs b/lsp/src/analysis/alpha035/global.rs similarity index 100% rename from src/analysis/alpha035/global.rs rename to lsp/src/analysis/alpha035/global.rs diff --git a/src/analysis/alpha035/mod.rs b/lsp/src/analysis/alpha035/mod.rs similarity index 100% rename from src/analysis/alpha035/mod.rs rename to lsp/src/analysis/alpha035/mod.rs diff --git a/src/analysis/alpha035/stmnts.rs b/lsp/src/analysis/alpha035/stmnts.rs similarity index 100% rename from src/analysis/alpha035/stmnts.rs rename to lsp/src/analysis/alpha035/stmnts.rs diff --git a/src/analysis/alpha040/exp.rs b/lsp/src/analysis/alpha040/exp.rs similarity index 100% rename from src/analysis/alpha040/exp.rs rename to lsp/src/analysis/alpha040/exp.rs diff --git a/src/analysis/alpha040/global.rs b/lsp/src/analysis/alpha040/global.rs similarity index 100% rename from src/analysis/alpha040/global.rs rename to lsp/src/analysis/alpha040/global.rs diff --git a/src/analysis/alpha040/mod.rs b/lsp/src/analysis/alpha040/mod.rs similarity index 100% rename from src/analysis/alpha040/mod.rs rename to lsp/src/analysis/alpha040/mod.rs diff --git a/src/analysis/alpha040/stmnts.rs b/lsp/src/analysis/alpha040/stmnts.rs similarity index 100% rename from src/analysis/alpha040/stmnts.rs rename to lsp/src/analysis/alpha040/stmnts.rs diff --git a/src/analysis/mod.rs b/lsp/src/analysis/mod.rs similarity index 100% rename from src/analysis/mod.rs rename to lsp/src/analysis/mod.rs diff --git a/src/analysis/types.rs b/lsp/src/analysis/types.rs similarity index 100% rename from src/analysis/types.rs rename to lsp/src/analysis/types.rs diff --git a/src/backend.rs b/lsp/src/backend.rs similarity index 100% rename from src/backend.rs rename to lsp/src/backend.rs diff --git a/src/files.rs b/lsp/src/files.rs similarity index 100% rename from src/files.rs rename to lsp/src/files.rs diff --git a/src/fs.rs b/lsp/src/fs.rs similarity index 100% rename from src/fs.rs rename to lsp/src/fs.rs diff --git a/src/grammar/alpha034/expressions/and.rs b/lsp/src/grammar/alpha034/expressions/and.rs similarity index 100% rename from src/grammar/alpha034/expressions/and.rs rename to lsp/src/grammar/alpha034/expressions/and.rs diff --git a/src/grammar/alpha034/expressions/atom/array.rs b/lsp/src/grammar/alpha034/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/array.rs rename to lsp/src/grammar/alpha034/expressions/atom/array.rs diff --git a/src/grammar/alpha034/expressions/atom/bool.rs b/lsp/src/grammar/alpha034/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/bool.rs rename to lsp/src/grammar/alpha034/expressions/atom/bool.rs diff --git a/src/grammar/alpha034/expressions/atom/call.rs b/lsp/src/grammar/alpha034/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/call.rs rename to lsp/src/grammar/alpha034/expressions/atom/call.rs diff --git a/src/grammar/alpha034/expressions/atom/command.rs b/lsp/src/grammar/alpha034/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/command.rs rename to lsp/src/grammar/alpha034/expressions/atom/command.rs diff --git a/src/grammar/alpha034/expressions/atom/mod.rs b/lsp/src/grammar/alpha034/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/mod.rs rename to lsp/src/grammar/alpha034/expressions/atom/mod.rs diff --git a/src/grammar/alpha034/expressions/atom/null.rs b/lsp/src/grammar/alpha034/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/null.rs rename to lsp/src/grammar/alpha034/expressions/atom/null.rs diff --git a/src/grammar/alpha034/expressions/atom/number.rs b/lsp/src/grammar/alpha034/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/number.rs rename to lsp/src/grammar/alpha034/expressions/atom/number.rs diff --git a/src/grammar/alpha034/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha034/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha034/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha034/expressions/atom/status.rs b/lsp/src/grammar/alpha034/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/status.rs rename to lsp/src/grammar/alpha034/expressions/atom/status.rs diff --git a/src/grammar/alpha034/expressions/atom/text.rs b/lsp/src/grammar/alpha034/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/text.rs rename to lsp/src/grammar/alpha034/expressions/atom/text.rs diff --git a/src/grammar/alpha034/expressions/atom/var.rs b/lsp/src/grammar/alpha034/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/var.rs rename to lsp/src/grammar/alpha034/expressions/atom/var.rs diff --git a/src/grammar/alpha034/expressions/cast.rs b/lsp/src/grammar/alpha034/expressions/cast.rs similarity index 100% rename from src/grammar/alpha034/expressions/cast.rs rename to lsp/src/grammar/alpha034/expressions/cast.rs diff --git a/src/grammar/alpha034/expressions/comparison.rs b/lsp/src/grammar/alpha034/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha034/expressions/comparison.rs rename to lsp/src/grammar/alpha034/expressions/comparison.rs diff --git a/src/grammar/alpha034/expressions/is.rs b/lsp/src/grammar/alpha034/expressions/is.rs similarity index 100% rename from src/grammar/alpha034/expressions/is.rs rename to lsp/src/grammar/alpha034/expressions/is.rs diff --git a/src/grammar/alpha034/expressions/mod.rs b/lsp/src/grammar/alpha034/expressions/mod.rs similarity index 100% rename from src/grammar/alpha034/expressions/mod.rs rename to lsp/src/grammar/alpha034/expressions/mod.rs diff --git a/src/grammar/alpha034/expressions/or.rs b/lsp/src/grammar/alpha034/expressions/or.rs similarity index 100% rename from src/grammar/alpha034/expressions/or.rs rename to lsp/src/grammar/alpha034/expressions/or.rs diff --git a/src/grammar/alpha034/expressions/product.rs b/lsp/src/grammar/alpha034/expressions/product.rs similarity index 100% rename from src/grammar/alpha034/expressions/product.rs rename to lsp/src/grammar/alpha034/expressions/product.rs diff --git a/src/grammar/alpha034/expressions/range.rs b/lsp/src/grammar/alpha034/expressions/range.rs similarity index 100% rename from src/grammar/alpha034/expressions/range.rs rename to lsp/src/grammar/alpha034/expressions/range.rs diff --git a/src/grammar/alpha034/expressions/sum.rs b/lsp/src/grammar/alpha034/expressions/sum.rs similarity index 100% rename from src/grammar/alpha034/expressions/sum.rs rename to lsp/src/grammar/alpha034/expressions/sum.rs diff --git a/src/grammar/alpha034/expressions/ternary.rs b/lsp/src/grammar/alpha034/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha034/expressions/ternary.rs rename to lsp/src/grammar/alpha034/expressions/ternary.rs diff --git a/src/grammar/alpha034/expressions/unary.rs b/lsp/src/grammar/alpha034/expressions/unary.rs similarity index 100% rename from src/grammar/alpha034/expressions/unary.rs rename to lsp/src/grammar/alpha034/expressions/unary.rs diff --git a/src/grammar/alpha034/global.rs b/lsp/src/grammar/alpha034/global.rs similarity index 100% rename from src/grammar/alpha034/global.rs rename to lsp/src/grammar/alpha034/global.rs diff --git a/src/grammar/alpha034/lexer.rs b/lsp/src/grammar/alpha034/lexer.rs similarity index 100% rename from src/grammar/alpha034/lexer.rs rename to lsp/src/grammar/alpha034/lexer.rs diff --git a/src/grammar/alpha034/mod.rs b/lsp/src/grammar/alpha034/mod.rs similarity index 100% rename from src/grammar/alpha034/mod.rs rename to lsp/src/grammar/alpha034/mod.rs diff --git a/src/grammar/alpha034/parser.rs b/lsp/src/grammar/alpha034/parser.rs similarity index 100% rename from src/grammar/alpha034/parser.rs rename to lsp/src/grammar/alpha034/parser.rs diff --git a/src/grammar/alpha034/semantic_tokens.rs b/lsp/src/grammar/alpha034/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha034/semantic_tokens.rs rename to lsp/src/grammar/alpha034/semantic_tokens.rs diff --git a/src/grammar/alpha034/statements/block.rs b/lsp/src/grammar/alpha034/statements/block.rs similarity index 100% rename from src/grammar/alpha034/statements/block.rs rename to lsp/src/grammar/alpha034/statements/block.rs diff --git a/src/grammar/alpha034/statements/comment.rs b/lsp/src/grammar/alpha034/statements/comment.rs similarity index 100% rename from src/grammar/alpha034/statements/comment.rs rename to lsp/src/grammar/alpha034/statements/comment.rs diff --git a/src/grammar/alpha034/statements/const_init.rs b/lsp/src/grammar/alpha034/statements/const_init.rs similarity index 100% rename from src/grammar/alpha034/statements/const_init.rs rename to lsp/src/grammar/alpha034/statements/const_init.rs diff --git a/src/grammar/alpha034/statements/failed.rs b/lsp/src/grammar/alpha034/statements/failed.rs similarity index 100% rename from src/grammar/alpha034/statements/failed.rs rename to lsp/src/grammar/alpha034/statements/failed.rs diff --git a/src/grammar/alpha034/statements/if_cond.rs b/lsp/src/grammar/alpha034/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha034/statements/if_cond.rs rename to lsp/src/grammar/alpha034/statements/if_cond.rs diff --git a/src/grammar/alpha034/statements/keywords.rs b/lsp/src/grammar/alpha034/statements/keywords.rs similarity index 100% rename from src/grammar/alpha034/statements/keywords.rs rename to lsp/src/grammar/alpha034/statements/keywords.rs diff --git a/src/grammar/alpha034/statements/loops.rs b/lsp/src/grammar/alpha034/statements/loops.rs similarity index 100% rename from src/grammar/alpha034/statements/loops.rs rename to lsp/src/grammar/alpha034/statements/loops.rs diff --git a/src/grammar/alpha034/statements/mod.rs b/lsp/src/grammar/alpha034/statements/mod.rs similarity index 100% rename from src/grammar/alpha034/statements/mod.rs rename to lsp/src/grammar/alpha034/statements/mod.rs diff --git a/src/grammar/alpha034/statements/modifiers.rs b/lsp/src/grammar/alpha034/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha034/statements/modifiers.rs rename to lsp/src/grammar/alpha034/statements/modifiers.rs diff --git a/src/grammar/alpha034/statements/shebang.rs b/lsp/src/grammar/alpha034/statements/shebang.rs similarity index 100% rename from src/grammar/alpha034/statements/shebang.rs rename to lsp/src/grammar/alpha034/statements/shebang.rs diff --git a/src/grammar/alpha034/statements/shorthands.rs b/lsp/src/grammar/alpha034/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha034/statements/shorthands.rs rename to lsp/src/grammar/alpha034/statements/shorthands.rs diff --git a/src/grammar/alpha034/statements/var_init.rs b/lsp/src/grammar/alpha034/statements/var_init.rs similarity index 100% rename from src/grammar/alpha034/statements/var_init.rs rename to lsp/src/grammar/alpha034/statements/var_init.rs diff --git a/src/grammar/alpha034/statements/var_set.rs b/lsp/src/grammar/alpha034/statements/var_set.rs similarity index 100% rename from src/grammar/alpha034/statements/var_set.rs rename to lsp/src/grammar/alpha034/statements/var_set.rs diff --git a/src/grammar/alpha035/expressions/and.rs b/lsp/src/grammar/alpha035/expressions/and.rs similarity index 100% rename from src/grammar/alpha035/expressions/and.rs rename to lsp/src/grammar/alpha035/expressions/and.rs diff --git a/src/grammar/alpha035/expressions/atom/array.rs b/lsp/src/grammar/alpha035/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/array.rs rename to lsp/src/grammar/alpha035/expressions/atom/array.rs diff --git a/src/grammar/alpha035/expressions/atom/bool.rs b/lsp/src/grammar/alpha035/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/bool.rs rename to lsp/src/grammar/alpha035/expressions/atom/bool.rs diff --git a/src/grammar/alpha035/expressions/atom/call.rs b/lsp/src/grammar/alpha035/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/call.rs rename to lsp/src/grammar/alpha035/expressions/atom/call.rs diff --git a/src/grammar/alpha035/expressions/atom/command.rs b/lsp/src/grammar/alpha035/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/command.rs rename to lsp/src/grammar/alpha035/expressions/atom/command.rs diff --git a/src/grammar/alpha035/expressions/atom/mod.rs b/lsp/src/grammar/alpha035/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/mod.rs rename to lsp/src/grammar/alpha035/expressions/atom/mod.rs diff --git a/src/grammar/alpha035/expressions/atom/null.rs b/lsp/src/grammar/alpha035/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/null.rs rename to lsp/src/grammar/alpha035/expressions/atom/null.rs diff --git a/src/grammar/alpha035/expressions/atom/number.rs b/lsp/src/grammar/alpha035/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/number.rs rename to lsp/src/grammar/alpha035/expressions/atom/number.rs diff --git a/src/grammar/alpha035/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha035/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha035/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha035/expressions/atom/status.rs b/lsp/src/grammar/alpha035/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/status.rs rename to lsp/src/grammar/alpha035/expressions/atom/status.rs diff --git a/src/grammar/alpha035/expressions/atom/text.rs b/lsp/src/grammar/alpha035/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/text.rs rename to lsp/src/grammar/alpha035/expressions/atom/text.rs diff --git a/src/grammar/alpha035/expressions/atom/var.rs b/lsp/src/grammar/alpha035/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/var.rs rename to lsp/src/grammar/alpha035/expressions/atom/var.rs diff --git a/src/grammar/alpha035/expressions/cast.rs b/lsp/src/grammar/alpha035/expressions/cast.rs similarity index 100% rename from src/grammar/alpha035/expressions/cast.rs rename to lsp/src/grammar/alpha035/expressions/cast.rs diff --git a/src/grammar/alpha035/expressions/comparison.rs b/lsp/src/grammar/alpha035/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha035/expressions/comparison.rs rename to lsp/src/grammar/alpha035/expressions/comparison.rs diff --git a/src/grammar/alpha035/expressions/is.rs b/lsp/src/grammar/alpha035/expressions/is.rs similarity index 100% rename from src/grammar/alpha035/expressions/is.rs rename to lsp/src/grammar/alpha035/expressions/is.rs diff --git a/src/grammar/alpha035/expressions/mod.rs b/lsp/src/grammar/alpha035/expressions/mod.rs similarity index 100% rename from src/grammar/alpha035/expressions/mod.rs rename to lsp/src/grammar/alpha035/expressions/mod.rs diff --git a/src/grammar/alpha035/expressions/or.rs b/lsp/src/grammar/alpha035/expressions/or.rs similarity index 100% rename from src/grammar/alpha035/expressions/or.rs rename to lsp/src/grammar/alpha035/expressions/or.rs diff --git a/src/grammar/alpha035/expressions/product.rs b/lsp/src/grammar/alpha035/expressions/product.rs similarity index 100% rename from src/grammar/alpha035/expressions/product.rs rename to lsp/src/grammar/alpha035/expressions/product.rs diff --git a/src/grammar/alpha035/expressions/range.rs b/lsp/src/grammar/alpha035/expressions/range.rs similarity index 100% rename from src/grammar/alpha035/expressions/range.rs rename to lsp/src/grammar/alpha035/expressions/range.rs diff --git a/src/grammar/alpha035/expressions/sum.rs b/lsp/src/grammar/alpha035/expressions/sum.rs similarity index 100% rename from src/grammar/alpha035/expressions/sum.rs rename to lsp/src/grammar/alpha035/expressions/sum.rs diff --git a/src/grammar/alpha035/expressions/ternary.rs b/lsp/src/grammar/alpha035/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha035/expressions/ternary.rs rename to lsp/src/grammar/alpha035/expressions/ternary.rs diff --git a/src/grammar/alpha035/expressions/unary.rs b/lsp/src/grammar/alpha035/expressions/unary.rs similarity index 100% rename from src/grammar/alpha035/expressions/unary.rs rename to lsp/src/grammar/alpha035/expressions/unary.rs diff --git a/src/grammar/alpha035/global.rs b/lsp/src/grammar/alpha035/global.rs similarity index 100% rename from src/grammar/alpha035/global.rs rename to lsp/src/grammar/alpha035/global.rs diff --git a/src/grammar/alpha035/lexer.rs b/lsp/src/grammar/alpha035/lexer.rs similarity index 100% rename from src/grammar/alpha035/lexer.rs rename to lsp/src/grammar/alpha035/lexer.rs diff --git a/src/grammar/alpha035/mod.rs b/lsp/src/grammar/alpha035/mod.rs similarity index 100% rename from src/grammar/alpha035/mod.rs rename to lsp/src/grammar/alpha035/mod.rs diff --git a/src/grammar/alpha035/parser.rs b/lsp/src/grammar/alpha035/parser.rs similarity index 100% rename from src/grammar/alpha035/parser.rs rename to lsp/src/grammar/alpha035/parser.rs diff --git a/src/grammar/alpha035/semantic_tokens.rs b/lsp/src/grammar/alpha035/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha035/semantic_tokens.rs rename to lsp/src/grammar/alpha035/semantic_tokens.rs diff --git a/src/grammar/alpha035/statements/block.rs b/lsp/src/grammar/alpha035/statements/block.rs similarity index 100% rename from src/grammar/alpha035/statements/block.rs rename to lsp/src/grammar/alpha035/statements/block.rs diff --git a/src/grammar/alpha035/statements/comment.rs b/lsp/src/grammar/alpha035/statements/comment.rs similarity index 100% rename from src/grammar/alpha035/statements/comment.rs rename to lsp/src/grammar/alpha035/statements/comment.rs diff --git a/src/grammar/alpha035/statements/const_init.rs b/lsp/src/grammar/alpha035/statements/const_init.rs similarity index 100% rename from src/grammar/alpha035/statements/const_init.rs rename to lsp/src/grammar/alpha035/statements/const_init.rs diff --git a/src/grammar/alpha035/statements/failed.rs b/lsp/src/grammar/alpha035/statements/failed.rs similarity index 100% rename from src/grammar/alpha035/statements/failed.rs rename to lsp/src/grammar/alpha035/statements/failed.rs diff --git a/src/grammar/alpha035/statements/if_cond.rs b/lsp/src/grammar/alpha035/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha035/statements/if_cond.rs rename to lsp/src/grammar/alpha035/statements/if_cond.rs diff --git a/src/grammar/alpha035/statements/keywords.rs b/lsp/src/grammar/alpha035/statements/keywords.rs similarity index 100% rename from src/grammar/alpha035/statements/keywords.rs rename to lsp/src/grammar/alpha035/statements/keywords.rs diff --git a/src/grammar/alpha035/statements/loops.rs b/lsp/src/grammar/alpha035/statements/loops.rs similarity index 100% rename from src/grammar/alpha035/statements/loops.rs rename to lsp/src/grammar/alpha035/statements/loops.rs diff --git a/src/grammar/alpha035/statements/mod.rs b/lsp/src/grammar/alpha035/statements/mod.rs similarity index 100% rename from src/grammar/alpha035/statements/mod.rs rename to lsp/src/grammar/alpha035/statements/mod.rs diff --git a/src/grammar/alpha035/statements/modifiers.rs b/lsp/src/grammar/alpha035/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha035/statements/modifiers.rs rename to lsp/src/grammar/alpha035/statements/modifiers.rs diff --git a/src/grammar/alpha035/statements/move_files.rs b/lsp/src/grammar/alpha035/statements/move_files.rs similarity index 100% rename from src/grammar/alpha035/statements/move_files.rs rename to lsp/src/grammar/alpha035/statements/move_files.rs diff --git a/src/grammar/alpha035/statements/shebang.rs b/lsp/src/grammar/alpha035/statements/shebang.rs similarity index 100% rename from src/grammar/alpha035/statements/shebang.rs rename to lsp/src/grammar/alpha035/statements/shebang.rs diff --git a/src/grammar/alpha035/statements/shorthands.rs b/lsp/src/grammar/alpha035/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha035/statements/shorthands.rs rename to lsp/src/grammar/alpha035/statements/shorthands.rs diff --git a/src/grammar/alpha035/statements/var_init.rs b/lsp/src/grammar/alpha035/statements/var_init.rs similarity index 100% rename from src/grammar/alpha035/statements/var_init.rs rename to lsp/src/grammar/alpha035/statements/var_init.rs diff --git a/src/grammar/alpha035/statements/var_set.rs b/lsp/src/grammar/alpha035/statements/var_set.rs similarity index 100% rename from src/grammar/alpha035/statements/var_set.rs rename to lsp/src/grammar/alpha035/statements/var_set.rs diff --git a/src/grammar/alpha040/expressions/and.rs b/lsp/src/grammar/alpha040/expressions/and.rs similarity index 100% rename from src/grammar/alpha040/expressions/and.rs rename to lsp/src/grammar/alpha040/expressions/and.rs diff --git a/src/grammar/alpha040/expressions/atom/array.rs b/lsp/src/grammar/alpha040/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/array.rs rename to lsp/src/grammar/alpha040/expressions/atom/array.rs diff --git a/src/grammar/alpha040/expressions/atom/bool.rs b/lsp/src/grammar/alpha040/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/bool.rs rename to lsp/src/grammar/alpha040/expressions/atom/bool.rs diff --git a/src/grammar/alpha040/expressions/atom/call.rs b/lsp/src/grammar/alpha040/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/call.rs rename to lsp/src/grammar/alpha040/expressions/atom/call.rs diff --git a/src/grammar/alpha040/expressions/atom/command.rs b/lsp/src/grammar/alpha040/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/command.rs rename to lsp/src/grammar/alpha040/expressions/atom/command.rs diff --git a/src/grammar/alpha040/expressions/atom/exit.rs b/lsp/src/grammar/alpha040/expressions/atom/exit.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/exit.rs rename to lsp/src/grammar/alpha040/expressions/atom/exit.rs diff --git a/src/grammar/alpha040/expressions/atom/mod.rs b/lsp/src/grammar/alpha040/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/mod.rs rename to lsp/src/grammar/alpha040/expressions/atom/mod.rs diff --git a/src/grammar/alpha040/expressions/atom/null.rs b/lsp/src/grammar/alpha040/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/null.rs rename to lsp/src/grammar/alpha040/expressions/atom/null.rs diff --git a/src/grammar/alpha040/expressions/atom/number.rs b/lsp/src/grammar/alpha040/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/number.rs rename to lsp/src/grammar/alpha040/expressions/atom/number.rs diff --git a/src/grammar/alpha040/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha040/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha040/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha040/expressions/atom/status.rs b/lsp/src/grammar/alpha040/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/status.rs rename to lsp/src/grammar/alpha040/expressions/atom/status.rs diff --git a/src/grammar/alpha040/expressions/atom/text.rs b/lsp/src/grammar/alpha040/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/text.rs rename to lsp/src/grammar/alpha040/expressions/atom/text.rs diff --git a/src/grammar/alpha040/expressions/atom/var.rs b/lsp/src/grammar/alpha040/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/var.rs rename to lsp/src/grammar/alpha040/expressions/atom/var.rs diff --git a/src/grammar/alpha040/expressions/cast.rs b/lsp/src/grammar/alpha040/expressions/cast.rs similarity index 100% rename from src/grammar/alpha040/expressions/cast.rs rename to lsp/src/grammar/alpha040/expressions/cast.rs diff --git a/src/grammar/alpha040/expressions/comparison.rs b/lsp/src/grammar/alpha040/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha040/expressions/comparison.rs rename to lsp/src/grammar/alpha040/expressions/comparison.rs diff --git a/src/grammar/alpha040/expressions/is.rs b/lsp/src/grammar/alpha040/expressions/is.rs similarity index 100% rename from src/grammar/alpha040/expressions/is.rs rename to lsp/src/grammar/alpha040/expressions/is.rs diff --git a/src/grammar/alpha040/expressions/mod.rs b/lsp/src/grammar/alpha040/expressions/mod.rs similarity index 100% rename from src/grammar/alpha040/expressions/mod.rs rename to lsp/src/grammar/alpha040/expressions/mod.rs diff --git a/src/grammar/alpha040/expressions/or.rs b/lsp/src/grammar/alpha040/expressions/or.rs similarity index 100% rename from src/grammar/alpha040/expressions/or.rs rename to lsp/src/grammar/alpha040/expressions/or.rs diff --git a/src/grammar/alpha040/expressions/product.rs b/lsp/src/grammar/alpha040/expressions/product.rs similarity index 100% rename from src/grammar/alpha040/expressions/product.rs rename to lsp/src/grammar/alpha040/expressions/product.rs diff --git a/src/grammar/alpha040/expressions/range.rs b/lsp/src/grammar/alpha040/expressions/range.rs similarity index 100% rename from src/grammar/alpha040/expressions/range.rs rename to lsp/src/grammar/alpha040/expressions/range.rs diff --git a/src/grammar/alpha040/expressions/sum.rs b/lsp/src/grammar/alpha040/expressions/sum.rs similarity index 100% rename from src/grammar/alpha040/expressions/sum.rs rename to lsp/src/grammar/alpha040/expressions/sum.rs diff --git a/src/grammar/alpha040/expressions/ternary.rs b/lsp/src/grammar/alpha040/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha040/expressions/ternary.rs rename to lsp/src/grammar/alpha040/expressions/ternary.rs diff --git a/src/grammar/alpha040/expressions/unary.rs b/lsp/src/grammar/alpha040/expressions/unary.rs similarity index 100% rename from src/grammar/alpha040/expressions/unary.rs rename to lsp/src/grammar/alpha040/expressions/unary.rs diff --git a/src/grammar/alpha040/global.rs b/lsp/src/grammar/alpha040/global.rs similarity index 100% rename from src/grammar/alpha040/global.rs rename to lsp/src/grammar/alpha040/global.rs diff --git a/src/grammar/alpha040/lexer.rs b/lsp/src/grammar/alpha040/lexer.rs similarity index 100% rename from src/grammar/alpha040/lexer.rs rename to lsp/src/grammar/alpha040/lexer.rs diff --git a/src/grammar/alpha040/mod.rs b/lsp/src/grammar/alpha040/mod.rs similarity index 100% rename from src/grammar/alpha040/mod.rs rename to lsp/src/grammar/alpha040/mod.rs diff --git a/src/grammar/alpha040/parser.rs b/lsp/src/grammar/alpha040/parser.rs similarity index 100% rename from src/grammar/alpha040/parser.rs rename to lsp/src/grammar/alpha040/parser.rs diff --git a/src/grammar/alpha040/semantic_tokens.rs b/lsp/src/grammar/alpha040/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha040/semantic_tokens.rs rename to lsp/src/grammar/alpha040/semantic_tokens.rs diff --git a/src/grammar/alpha040/statements/block.rs b/lsp/src/grammar/alpha040/statements/block.rs similarity index 100% rename from src/grammar/alpha040/statements/block.rs rename to lsp/src/grammar/alpha040/statements/block.rs diff --git a/src/grammar/alpha040/statements/comment.rs b/lsp/src/grammar/alpha040/statements/comment.rs similarity index 100% rename from src/grammar/alpha040/statements/comment.rs rename to lsp/src/grammar/alpha040/statements/comment.rs diff --git a/src/grammar/alpha040/statements/const_init.rs b/lsp/src/grammar/alpha040/statements/const_init.rs similarity index 100% rename from src/grammar/alpha040/statements/const_init.rs rename to lsp/src/grammar/alpha040/statements/const_init.rs diff --git a/src/grammar/alpha040/statements/failed.rs b/lsp/src/grammar/alpha040/statements/failed.rs similarity index 100% rename from src/grammar/alpha040/statements/failed.rs rename to lsp/src/grammar/alpha040/statements/failed.rs diff --git a/src/grammar/alpha040/statements/if_cond.rs b/lsp/src/grammar/alpha040/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha040/statements/if_cond.rs rename to lsp/src/grammar/alpha040/statements/if_cond.rs diff --git a/src/grammar/alpha040/statements/keywords.rs b/lsp/src/grammar/alpha040/statements/keywords.rs similarity index 100% rename from src/grammar/alpha040/statements/keywords.rs rename to lsp/src/grammar/alpha040/statements/keywords.rs diff --git a/src/grammar/alpha040/statements/loops.rs b/lsp/src/grammar/alpha040/statements/loops.rs similarity index 100% rename from src/grammar/alpha040/statements/loops.rs rename to lsp/src/grammar/alpha040/statements/loops.rs diff --git a/src/grammar/alpha040/statements/mod.rs b/lsp/src/grammar/alpha040/statements/mod.rs similarity index 100% rename from src/grammar/alpha040/statements/mod.rs rename to lsp/src/grammar/alpha040/statements/mod.rs diff --git a/src/grammar/alpha040/statements/modifiers.rs b/lsp/src/grammar/alpha040/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha040/statements/modifiers.rs rename to lsp/src/grammar/alpha040/statements/modifiers.rs diff --git a/src/grammar/alpha040/statements/move_files.rs b/lsp/src/grammar/alpha040/statements/move_files.rs similarity index 100% rename from src/grammar/alpha040/statements/move_files.rs rename to lsp/src/grammar/alpha040/statements/move_files.rs diff --git a/src/grammar/alpha040/statements/shebang.rs b/lsp/src/grammar/alpha040/statements/shebang.rs similarity index 100% rename from src/grammar/alpha040/statements/shebang.rs rename to lsp/src/grammar/alpha040/statements/shebang.rs diff --git a/src/grammar/alpha040/statements/shorthands.rs b/lsp/src/grammar/alpha040/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha040/statements/shorthands.rs rename to lsp/src/grammar/alpha040/statements/shorthands.rs diff --git a/src/grammar/alpha040/statements/var_init.rs b/lsp/src/grammar/alpha040/statements/var_init.rs similarity index 100% rename from src/grammar/alpha040/statements/var_init.rs rename to lsp/src/grammar/alpha040/statements/var_init.rs diff --git a/src/grammar/alpha040/statements/var_set.rs b/lsp/src/grammar/alpha040/statements/var_set.rs similarity index 100% rename from src/grammar/alpha040/statements/var_set.rs rename to lsp/src/grammar/alpha040/statements/var_set.rs diff --git a/src/grammar/mod.rs b/lsp/src/grammar/mod.rs similarity index 100% rename from src/grammar/mod.rs rename to lsp/src/grammar/mod.rs diff --git a/src/lib.rs b/lsp/src/lib.rs similarity index 100% rename from src/lib.rs rename to lsp/src/lib.rs diff --git a/src/main.rs b/lsp/src/main.rs similarity index 100% rename from src/main.rs rename to lsp/src/main.rs diff --git a/src/paths.rs b/lsp/src/paths.rs similarity index 100% rename from src/paths.rs rename to lsp/src/paths.rs diff --git a/src/stdlib.rs b/lsp/src/stdlib.rs similarity index 100% rename from src/stdlib.rs rename to lsp/src/stdlib.rs diff --git a/src/utils.rs b/lsp/src/utils.rs similarity index 100% rename from src/utils.rs rename to lsp/src/utils.rs From 42723b4b5e5205ec35a8e025d663413b4d75e30e Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 17:38:59 +0000 Subject: [PATCH 03/37] Separated into library and binary --- Cargo.toml | 3 +- lib/Cargo.toml | 29 ++ lib/resources/alpha034/std/main.ab | 268 ++++++++++++++++++ lib/resources/alpha035/std/array.ab | 27 ++ lib/resources/alpha035/std/date.ab | 109 +++++++ lib/resources/alpha035/std/env.ab | 160 +++++++++++ lib/resources/alpha035/std/fs.ab | 74 +++++ lib/resources/alpha035/std/http.ab | 25 ++ lib/resources/alpha035/std/math.ab | 34 +++ lib/resources/alpha035/std/text.ab | 164 +++++++++++ lib/resources/alpha040/builtin.ab | 38 +++ lib/resources/alpha040/std/array.ab | 80 ++++++ lib/resources/alpha040/std/date.ab | 126 ++++++++ lib/resources/alpha040/std/env.ab | 164 +++++++++++ lib/resources/alpha040/std/fs.ab | 140 +++++++++ lib/resources/alpha040/std/http.ab | 24 ++ lib/resources/alpha040/std/math.ab | 34 +++ lib/resources/alpha040/std/text.ab | 241 ++++++++++++++++ {lsp => lib}/src/analysis/alpha034/exp.rs | 0 {lsp => lib}/src/analysis/alpha034/global.rs | 0 {lsp => lib}/src/analysis/alpha034/mod.rs | 0 {lsp => lib}/src/analysis/alpha034/stmnts.rs | 0 {lsp => lib}/src/analysis/alpha035/exp.rs | 0 {lsp => lib}/src/analysis/alpha035/global.rs | 0 {lsp => lib}/src/analysis/alpha035/mod.rs | 0 {lsp => lib}/src/analysis/alpha035/stmnts.rs | 0 {lsp => lib}/src/analysis/alpha040/exp.rs | 0 {lsp => lib}/src/analysis/alpha040/global.rs | 0 {lsp => lib}/src/analysis/alpha040/mod.rs | 0 {lsp => lib}/src/analysis/alpha040/stmnts.rs | 0 {lsp => lib}/src/analysis/mod.rs | 0 {lsp => lib}/src/analysis/types.rs | 0 {lsp => lib}/src/backend.rs | 0 {lsp => lib}/src/files.rs | 0 {lsp => lib}/src/fs.rs | 0 .../src/grammar/alpha034/expressions/and.rs | 0 .../alpha034/expressions/atom/array.rs | 0 .../grammar/alpha034/expressions/atom/bool.rs | 0 .../grammar/alpha034/expressions/atom/call.rs | 0 .../alpha034/expressions/atom/command.rs | 0 .../grammar/alpha034/expressions/atom/mod.rs | 0 .../grammar/alpha034/expressions/atom/null.rs | 0 .../alpha034/expressions/atom/number.rs | 0 .../alpha034/expressions/atom/parentheses.rs | 0 .../alpha034/expressions/atom/status.rs | 0 .../grammar/alpha034/expressions/atom/text.rs | 0 .../grammar/alpha034/expressions/atom/var.rs | 0 .../src/grammar/alpha034/expressions/cast.rs | 0 .../alpha034/expressions/comparison.rs | 0 .../src/grammar/alpha034/expressions/is.rs | 0 .../src/grammar/alpha034/expressions/mod.rs | 0 .../src/grammar/alpha034/expressions/or.rs | 0 .../grammar/alpha034/expressions/product.rs | 0 .../src/grammar/alpha034/expressions/range.rs | 0 .../src/grammar/alpha034/expressions/sum.rs | 0 .../grammar/alpha034/expressions/ternary.rs | 0 .../src/grammar/alpha034/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha034/global.rs | 0 {lsp => lib}/src/grammar/alpha034/lexer.rs | 0 {lsp => lib}/src/grammar/alpha034/mod.rs | 0 {lsp => lib}/src/grammar/alpha034/parser.rs | 0 .../src/grammar/alpha034/semantic_tokens.rs | 0 .../src/grammar/alpha034/statements/block.rs | 0 .../grammar/alpha034/statements/comment.rs | 0 .../grammar/alpha034/statements/const_init.rs | 0 .../src/grammar/alpha034/statements/failed.rs | 0 .../grammar/alpha034/statements/if_cond.rs | 0 .../grammar/alpha034/statements/keywords.rs | 0 .../src/grammar/alpha034/statements/loops.rs | 0 .../src/grammar/alpha034/statements/mod.rs | 0 .../grammar/alpha034/statements/modifiers.rs | 0 .../grammar/alpha034/statements/shebang.rs | 0 .../grammar/alpha034/statements/shorthands.rs | 0 .../grammar/alpha034/statements/var_init.rs | 0 .../grammar/alpha034/statements/var_set.rs | 0 .../src/grammar/alpha035/expressions/and.rs | 0 .../alpha035/expressions/atom/array.rs | 0 .../grammar/alpha035/expressions/atom/bool.rs | 0 .../grammar/alpha035/expressions/atom/call.rs | 0 .../alpha035/expressions/atom/command.rs | 0 .../grammar/alpha035/expressions/atom/mod.rs | 0 .../grammar/alpha035/expressions/atom/null.rs | 0 .../alpha035/expressions/atom/number.rs | 0 .../alpha035/expressions/atom/parentheses.rs | 0 .../alpha035/expressions/atom/status.rs | 0 .../grammar/alpha035/expressions/atom/text.rs | 0 .../grammar/alpha035/expressions/atom/var.rs | 0 .../src/grammar/alpha035/expressions/cast.rs | 0 .../alpha035/expressions/comparison.rs | 0 .../src/grammar/alpha035/expressions/is.rs | 0 .../src/grammar/alpha035/expressions/mod.rs | 0 .../src/grammar/alpha035/expressions/or.rs | 0 .../grammar/alpha035/expressions/product.rs | 0 .../src/grammar/alpha035/expressions/range.rs | 0 .../src/grammar/alpha035/expressions/sum.rs | 0 .../grammar/alpha035/expressions/ternary.rs | 0 .../src/grammar/alpha035/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha035/global.rs | 0 {lsp => lib}/src/grammar/alpha035/lexer.rs | 0 {lsp => lib}/src/grammar/alpha035/mod.rs | 0 {lsp => lib}/src/grammar/alpha035/parser.rs | 0 .../src/grammar/alpha035/semantic_tokens.rs | 0 .../src/grammar/alpha035/statements/block.rs | 0 .../grammar/alpha035/statements/comment.rs | 0 .../grammar/alpha035/statements/const_init.rs | 0 .../src/grammar/alpha035/statements/failed.rs | 0 .../grammar/alpha035/statements/if_cond.rs | 0 .../grammar/alpha035/statements/keywords.rs | 0 .../src/grammar/alpha035/statements/loops.rs | 0 .../src/grammar/alpha035/statements/mod.rs | 0 .../grammar/alpha035/statements/modifiers.rs | 0 .../grammar/alpha035/statements/move_files.rs | 0 .../grammar/alpha035/statements/shebang.rs | 0 .../grammar/alpha035/statements/shorthands.rs | 0 .../grammar/alpha035/statements/var_init.rs | 0 .../grammar/alpha035/statements/var_set.rs | 0 .../src/grammar/alpha040/expressions/and.rs | 0 .../alpha040/expressions/atom/array.rs | 0 .../grammar/alpha040/expressions/atom/bool.rs | 0 .../grammar/alpha040/expressions/atom/call.rs | 0 .../alpha040/expressions/atom/command.rs | 0 .../grammar/alpha040/expressions/atom/exit.rs | 0 .../grammar/alpha040/expressions/atom/mod.rs | 0 .../grammar/alpha040/expressions/atom/null.rs | 0 .../alpha040/expressions/atom/number.rs | 0 .../alpha040/expressions/atom/parentheses.rs | 0 .../alpha040/expressions/atom/status.rs | 0 .../grammar/alpha040/expressions/atom/text.rs | 0 .../grammar/alpha040/expressions/atom/var.rs | 0 .../src/grammar/alpha040/expressions/cast.rs | 0 .../alpha040/expressions/comparison.rs | 0 .../src/grammar/alpha040/expressions/is.rs | 0 .../src/grammar/alpha040/expressions/mod.rs | 0 .../src/grammar/alpha040/expressions/or.rs | 0 .../grammar/alpha040/expressions/product.rs | 0 .../src/grammar/alpha040/expressions/range.rs | 0 .../src/grammar/alpha040/expressions/sum.rs | 0 .../grammar/alpha040/expressions/ternary.rs | 0 .../src/grammar/alpha040/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha040/global.rs | 0 {lsp => lib}/src/grammar/alpha040/lexer.rs | 0 {lsp => lib}/src/grammar/alpha040/mod.rs | 0 {lsp => lib}/src/grammar/alpha040/parser.rs | 0 .../src/grammar/alpha040/semantic_tokens.rs | 0 .../src/grammar/alpha040/statements/block.rs | 0 .../grammar/alpha040/statements/comment.rs | 0 .../grammar/alpha040/statements/const_init.rs | 0 .../src/grammar/alpha040/statements/failed.rs | 0 .../grammar/alpha040/statements/if_cond.rs | 0 .../grammar/alpha040/statements/keywords.rs | 0 .../src/grammar/alpha040/statements/loops.rs | 0 .../src/grammar/alpha040/statements/mod.rs | 0 .../grammar/alpha040/statements/modifiers.rs | 0 .../grammar/alpha040/statements/move_files.rs | 0 .../grammar/alpha040/statements/shebang.rs | 0 .../grammar/alpha040/statements/shorthands.rs | 0 .../grammar/alpha040/statements/var_init.rs | 0 .../grammar/alpha040/statements/var_set.rs | 0 {lsp => lib}/src/grammar/mod.rs | 0 {lsp => lib}/src/lib.rs | 0 {lsp => lib}/src/paths.rs | 0 {lsp => lib}/src/stdlib.rs | 0 {lsp => lib}/src/utils.rs | 0 lsp/Cargo.toml | 2 + lsp/src/main.rs | 2 +- 165 files changed, 1742 insertions(+), 2 deletions(-) create mode 100644 lib/Cargo.toml create mode 100644 lib/resources/alpha034/std/main.ab create mode 100644 lib/resources/alpha035/std/array.ab create mode 100644 lib/resources/alpha035/std/date.ab create mode 100644 lib/resources/alpha035/std/env.ab create mode 100644 lib/resources/alpha035/std/fs.ab create mode 100644 lib/resources/alpha035/std/http.ab create mode 100644 lib/resources/alpha035/std/math.ab create mode 100644 lib/resources/alpha035/std/text.ab create mode 100644 lib/resources/alpha040/builtin.ab create mode 100644 lib/resources/alpha040/std/array.ab create mode 100644 lib/resources/alpha040/std/date.ab create mode 100644 lib/resources/alpha040/std/env.ab create mode 100644 lib/resources/alpha040/std/fs.ab create mode 100644 lib/resources/alpha040/std/http.ab create mode 100644 lib/resources/alpha040/std/math.ab create mode 100644 lib/resources/alpha040/std/text.ab rename {lsp => lib}/src/analysis/alpha034/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha034/global.rs (100%) rename {lsp => lib}/src/analysis/alpha034/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha034/stmnts.rs (100%) rename {lsp => lib}/src/analysis/alpha035/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha035/global.rs (100%) rename {lsp => lib}/src/analysis/alpha035/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha035/stmnts.rs (100%) rename {lsp => lib}/src/analysis/alpha040/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha040/global.rs (100%) rename {lsp => lib}/src/analysis/alpha040/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha040/stmnts.rs (100%) rename {lsp => lib}/src/analysis/mod.rs (100%) rename {lsp => lib}/src/analysis/types.rs (100%) rename {lsp => lib}/src/backend.rs (100%) rename {lsp => lib}/src/files.rs (100%) rename {lsp => lib}/src/fs.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha034/global.rs (100%) rename {lsp => lib}/src/grammar/alpha034/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha034/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha034/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha035/global.rs (100%) rename {lsp => lib}/src/grammar/alpha035/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha035/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha035/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/move_files.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/exit.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha040/global.rs (100%) rename {lsp => lib}/src/grammar/alpha040/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha040/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha040/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/move_files.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/mod.rs (100%) rename {lsp => lib}/src/lib.rs (100%) rename {lsp => lib}/src/paths.rs (100%) rename {lsp => lib}/src/stdlib.rs (100%) rename {lsp => lib}/src/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 7b64cc4..6e91637 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ [workspace] resolver = "2" -members = ["lsp"] +members = ["lsp", "lib"] [workspace.dependencies] +lib = { path = "./lib" } tokio = "1.39.1" tower-lsp-server = "0.22.0" phf = "0.11" diff --git a/lib/Cargo.toml b/lib/Cargo.toml new file mode 100644 index 0000000..1c31ded --- /dev/null +++ b/lib/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "lib" +version = "0.1.10" +edition = "2021" + +[dependencies] +tokio = { workspace = true, features = ["full"] } +tower-lsp-server = { workspace = true } +phf = { workspace = true, features = ["macros"] } +dashmap = { workspace = true } +ropey = { workspace = true } +chumsky = { workspace = true, features = ["default", "label"] } +heraclitus-compiler = { workspace = true } +serde_json = { workspace = true } +rangemap = { workspace = true } +indexmap = { workspace = true } +clap = { workspace = true, features = ["derive"] } +include_dir = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +rustc-hash = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +insta = { workspace = true, features = ["yaml"] } + +[build-dependencies] +fs_extra = "1.3.0" diff --git a/lib/resources/alpha034/std/main.ab b/lib/resources/alpha034/std/main.ab new file mode 100644 index 0000000..7c428f0 --- /dev/null +++ b/lib/resources/alpha034/std/main.ab @@ -0,0 +1,268 @@ +pub fun input(prompt: Text): Text { + unsafe $printf "\${nameof prompt}"$ + unsafe $read$ + return "$REPLY" +} + +pub fun replace_once(source, pattern, replacement) { + return unsafe $echo "\$\{source/{pattern}/{replacement}}"$ +} + +pub fun replace(source, pattern, replacement) { + return unsafe $echo "\$\{source//{pattern}/{replacement}}"$ +} + +pub fun replace_regex(source: Text, pattern: Text, replacement: Text): Text { + return unsafe $echo "{source}" | sed -e "s/{pattern}/{replacement}/g"$ +} + +pub fun dir_exist(path) { + $[ -d "{path}" ]$ failed { + return false + } + return true +} + +pub fun file_exist(path) { + $[ -f "{path}" ]$ failed { + return false + } + return true +} + +pub fun file_read(path) { + return $< "{path}"$? +} + +pub fun file_write(path, content) { + return $echo "{content}" > "{path}"$? +} + +pub fun file_append(path, content) { + return $echo "{content}" >> "{path}"$? +} + +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + unsafe $IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}")$ + return result +} + +pub fun lines(text: Text): [Text] { + let result = [Text] + unsafe $IFS=\$'\n' read -rd '' -a {nameof result} <<<"\${nameof text}"$ + return result +} + +pub fun words(text: Text): [Text] { + return split(text, " ") +} + +pub fun join(list: [Text], delimiter: Text): Text { + return unsafe $IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}"$ +} + +pub fun trim_left(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/^[[:space:]]*//'$ +} + +pub fun trim_right(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/[[:space:]]*\$//'$ +} + +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +pub fun lower(text: Text): Text { + return unsafe $echo "{text}" | tr '[:upper:]' '[:lower:]'$ +} + +pub fun upper(text: Text): Text { + return unsafe $echo "{text}" | tr '[:lower:]' '[:upper:]'$ +} + +#[allow_absurd_cast] +pub fun len(value): Num { + unsafe { + if value is Text: + return $echo "\$\{#{nameof value}}"$ as Num + else: + return $echo "\$\{#{nameof value}[@]}"$ as Num + } +} + +#[allow_absurd_cast] +pub fun parse(text: Text): Num { + $[ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null$? + return text as Num +} + +pub fun chars(text: Text): [Text] { + let chars = [Text] + unsafe $for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done;$ + return chars +} + +#[allow_absurd_cast] +pub fun sum(list: [Num]): Num { + return unsafe $echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}'$ as Num +} + +pub fun array_first_index(array, value): Num { + loop index, element in array { + if(value as Text == element as Text) { + return index + } + } + return -1 +} + +pub fun array_search(array, value): [Num] { + let result = [Num] + loop index, element in array { + if(value as Text == element as Text) { + result += [index] + } + } + return result +} + +pub fun in_array(array, value): Bool { + let result = array_first_index(array, value) + return result >= 0 +} + +pub fun has_failed(command: Text): Bool { + unsafe silent $eval {command}$ + return status != 0 +} + +pub fun exit(code: Num): Null { + unsafe $exit "{code}"$ +} + +pub fun includes(arr, value) { + loop v in arr { + if v == value { + return true + } + } + return false +} + +pub fun is_command(command: Text): Bool { + $[ -x "\$(command -v {command})" ]$ failed { + return false + } + return true +} + +pub fun create_symbolic_link(origin: Text, destination: Text): Bool { + if file_exist(origin) { + unsafe $ln -s "{origin}" "{destination}"$ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +pub fun create_dir(path: Text): Null { + if not dir_exist(path) { + unsafe $mkdir -p "{path}"$ + } +} + +pub fun make_executable(path: Text): Bool { + if file_exist(path) { + unsafe $chmod +x "{path}"$ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +pub fun change_owner(user: Text, path: Text): Bool { + if file_exist(path) or dir_exist(path) { + unsafe $chown -R "{user}" "{path}"$ + return true + } + + return false +} + +pub fun download(url: Text, path: Text): Bool { + if { + is_command("curl") { + unsafe $curl -L -o "{path}" "{url}"$ + } + is_command("wget") { + unsafe $wget "{url}" -P "{path}"$ + } + is_command("aria2c") { + unsafe $aria2c "{url}" -d "{path}"$ + } + else { + return false + } + } + + return true +} + +pub fun is_root(): Bool { + if (unsafe $id -u$ == "0") { + return true + } + + return false +} + +pub fun get_env_var(var: Text): Text { + let _var = unsafe $echo "\$\{!var}"$ + if _var != "" { + return _var + } + + if file_exist(".env") { + unsafe $source ".env"$ + return unsafe $echo "\$\{!var}"$ + } + + return "" +} + +pub fun load_env_file(): Null { + unsafe $export "\$(xargs < .env)" > /dev/null$ +} + +pub fun shell_isset(name: Text): Bool { + $[[ ! -z \$\{!{nameof name}+z} ]]$ failed { + return false + } + return true +} + +pub fun shell_constant_set(name: Text, val: Text): Null { + $readonly \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +pub fun shell_constant_get(name: Text): Text { + return $echo \$\{!{nameof name}}$? +} + +pub fun shell_var_set(name: Text, val: Text): Null { + $export \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +pub fun shell_var_get(name: Text): Text { + return $echo \$\{!{nameof name}}$? +} + +pub fun shell_unset(name: Text): Null { + $unset {name}$? +} diff --git a/lib/resources/alpha035/std/array.ab b/lib/resources/alpha035/std/array.ab new file mode 100644 index 0000000..4dd2b54 --- /dev/null +++ b/lib/resources/alpha035/std/array.ab @@ -0,0 +1,27 @@ +/// Returns index of the first value found in the specified array +/// If the value is not found, the function returns -1 +pub fun array_first_index(array, value): Num { + loop index, element in array { + if value as Text == element as Text { + return index + } + } + return -1 +} + +/// Search the value in array and return an array with the index of the various items +pub fun array_search(array, value): [Num] { + let result = [Num] + loop index, element in array { + if value as Text == element as Text { + result += [index] + } + } + return result +} + +/// Check if the value is in the array +pub fun includes(array, value) { + let result = array_first_index(array, value) + return result >= 0 +} diff --git a/lib/resources/alpha035/std/date.ab b/lib/resources/alpha035/std/date.ab new file mode 100644 index 0000000..27d01f5 --- /dev/null +++ b/lib/resources/alpha035/std/date.ab @@ -0,0 +1,109 @@ +/// EXPERIMENTAL +/// Format a date with a special format +/// If no date is specified, the current date is used +/// If no format is specified, "%FT%T%Z" format is used +/// For more info about format type "man date" on your shell or go to https://www.gnu.org/software/coreutils/date +/// Format : +/// %% a literal % +/// %a locale's abbreviated weekday name (e.g., Sun) +/// %A locale's full weekday name (e.g., Sunday) +/// %b locale's abbreviated month name (e.g., Jan) +/// %B locale's full month name (e.g., January) +/// %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005) +/// %C century; like %Y, except omit last two digits (e.g., 20) +/// %d day of month (e.g., 01) +/// %D date; same as %m/%d/%y +/// %e day of month, space padded; same as %_d +/// %F full date; like %+4Y-%m-%d +/// %g last two digits of year of ISO week number (see %G) +/// %G year of ISO week number (see %V); normally useful only with %V +/// %h same as %b +/// %H hour (00..23) +/// %I hour (01..12) +/// %j day of year (001..366) +/// %k hour, space padded ( 0..23); same as %_H +/// %l hour, space padded ( 1..12); same as %_I +/// %m month (01..12) +/// %M minute (00..59) +/// %n a newline +/// %N nanoseconds (000000000..999999999) +/// %p locale's equivalent of either AM or PM; blank if not known +/// %P like %p, but lower case +/// %q quarter of year (1..4) +/// %r locale's 12-hour clock time (e.g., 11:11:04 PM) +/// %R 24-hour hour and minute; same as %H:%M +/// %s seconds since the Epoch (1970-01-01 00:00 UTC) +/// %S second (00..60) +/// %t a tab +/// %T time; same as %H:%M:%S +/// %u day of week (1..7); 1 is Monday +/// %U week number of year, with Sunday as first day of week (00..53) +/// %V ISO week number, with Monday as first day of week (01..53) +/// %w day of week (0..6); 0 is Sunday +/// %W week number of year, with Monday as first day of week (00..53) +/// %x locale's date representation (e.g., 12/31/99) +/// %X locale's time representation (e.g., 23:13:48) +/// %y last two digits of year (00..99) +/// %Y year +/// %z +hhmm numeric time zone (e.g., -0400) +/// %:z +hh:mm numeric time zone (e.g., -04:00) +/// %::z +hh:mm:ss numeric time zone (e.g., -04:00:00) +/// %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30) +/// %Z alphabetic time zone abbreviation (e.g., EDT) +/// +/// By default, date pads numeric fields with zeroes. The following optional flags may follow '%': +/// +/// - (hyphen) do not pad the field +/// _ (underscore) pad with spaces +/// 0 (zero) pad with zeros +/// + pad with zeros, and put '+' before future years with >4 digits +/// ^ use upper case if possible +/// # use opposite case if possible +pub fun date_posix(format: Text = "", date: Text = "", utc: Bool = false): Text? { + if format == "": format = "%FT%T%Z" + if date == "": date = unsafe $date +"%FT%T%Z"$ + if (utc) { + return $date --utc -d "{date}" +"{format}"$? + } else { + return $date -d "{date}" +"{format}"$? + } +} + +/// Return current timestamp (seconds since the Epoch (1970-01-01 00:00 UTC)) +#[allow_absurd_cast] +pub fun now(): Num { + return unsafe $date +%s$ as Num +} + +/// EXPERIMENTAL +/// Add value to date. +/// If no date is specified, the current date is used +/// Ex : date_add("+3 days") +/// You can use : +/// (+/-) +/// years +/// months +/// days +/// hours +/// minutes +/// seconds +pub fun date_add(add:Text, date:Text = "", utc: Bool = false): Text? { + if date == "": date = unsafe $date +"%FT%T%Z"$ + return date_posix("", "{date_posix("%F", date, utc)?} {add} {date_posix("%T", date, utc)?}", utc)? +} + +/// EXPERIMENTAL +/// Compare 2 date +/// Return 1 if date_a is after date_b +/// Return 0 if date_a and date_b is the same +/// Return -1 if date_b is after date_a +/// If date_b is not provided, current date will be used +#[allow_absurd_cast] +pub fun date_compare(date_a: Text, date_b: Text = "", utc: Bool = false): Num? { + if date_b == "": date_b = unsafe date_posix("", "", utc) + let timestamp_a = date_posix("%s", date_a, utc)? as Num + let timestamp_b = date_posix("%s", date_b, utc)? as Num + if timestamp_a > timestamp_b: return 1 + if timestamp_a == timestamp_b: return 0 + if timestamp_a < timestamp_b: return -1 +} \ No newline at end of file diff --git a/lib/resources/alpha035/std/env.ab b/lib/resources/alpha035/std/env.ab new file mode 100644 index 0000000..3d1b997 --- /dev/null +++ b/lib/resources/alpha035/std/env.ab @@ -0,0 +1,160 @@ +import * from "std/fs" +import * from "std/text" + +/// Retrieves the value of an environment variable, optionally sourcing it from a file if not already set. +pub fun get_env_var(var: Text, file: Text = ".env"): Text { + let _var = unsafe $echo "\$\{!var}"$ + if _var != "" { + return _var + } + + if file_exist(file) { + unsafe $source "{file}"$ + return unsafe $echo "\$\{!var}"$ + } + + return "" +} + +/// Load the env file in the environment, using `xargs` +pub fun load_env_file(file: Text = ".env"): Null { + unsafe $export "\$(xargs < {file})" > /dev/null$ +} + +/// Check if a variable inside the Shell session exist +pub fun shell_isset(name: Text): Bool { + $[[ ! -z \$\{!{nameof name}+z} ]]$ failed { + return false + } + return true +} + +/// Set a constant inside the Shell session +pub fun shell_constant_set(name: Text, val: Text): Null? { + $readonly \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +/// Get a constant inside the Shell session +pub fun shell_constant_get(name: Text): Text? { + return $echo \$\{!{nameof name}}$? +} + +/// Set a constant inside the Shell session +pub fun shell_var_set(name: Text, val: Text): Null? { + $export \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +/// Get a constant inside the Shell session +pub fun shell_var_get(name: Text): Text? { + return $echo \$\{!{nameof name}}$? +} + +/// Remove a variable inside the Shell session +pub fun shell_unset(name: Text): Null? { + $unset {name}$? +} + +/// Check if the command exist +pub fun is_command(command: Text): Bool { + $[ -x "\$(command -v {command})" ]$ failed { + return false + } + return true +} + +/// Create a prompt and return the value +pub fun input(prompt: Text): Text { + unsafe $printf "\${nameof prompt}"$ + unsafe $read$ + return "\$REPLY" +} + +/// Confirm prompt (Yes/No), return true if choice is Yes +/// "No" is the default choice, set default_yes to true for "Yes" as default choice +pub fun confirm(prompt: Text, default_yes: Bool = false): Bool { + let choice_default = default_yes then " [\x1b[1mY/\x1b[0mn]" else " [y/\x1b[1mN\x1b[0m]" + unsafe { + $printf "\x1b[1m{prompt}\x1b[0m{choice_default}"$ + $read -s -n 1$ + $printf "\n"$ + } + let result = lower(unsafe $echo \$REPLY$) + return result == "y" or (result == "" and default_yes) +} + +/// Checks if the command has failed +pub fun has_failed(command: Text): Bool { + unsafe silent $eval {command}$ + return status != 0 +} + +/// Close the script +pub fun exit(code: Num): Null { + unsafe $exit "{code}"$ +} + +/// Check if the script is running with a user with root permission +pub fun is_root(): Bool { + if unsafe $id -u$ == "0" { + return true + } + + return false +} + +/// `printf` the text following the arguments +pub fun printf(format: Text, args: [Text] = [""]): Null { + unsafe ${nameof args}=("{format}" "\$\{{nameof args}[@]}")$ + unsafe $printf "\$\{{nameof args}[@]}"$ +} + +/// Escape the text to be used with `printf` +pub fun printf_escape(text: Text): Text { + return unsafe $echo \${nameof text} | sed -e 's/\\\\/\\\\\\\\/g' -e "s/%/%%/g"$ +} + +/// Prepare a text with formatting options for `printf` +pub fun text_shell(message: Text, style: Num, fg: Num, bg: Num): Text { + return "\x1b[{style};{fg};{bg}m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as bold +pub fun text_bold(message: Text): Text { + return "\x1b[1m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as italic +pub fun text_italic(message: Text): Text { + return "\x1b[3m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as underlined +pub fun text_underlined(message: Text): Text { + return "\x1b[4m{printf_escape(message)}\x1b[0m" +} + +/// Print a text with a specified color +pub fun color_echo(message: Text, color: Num): Null { + printf("\x1b[{color as Text}m%s\x1b[0m\n", [message]) +} + +/// Print a text as Info +pub fun echo_info(message: Text): Null { + printf("\x1b[1;3;97;44m %s \x1b[0m\n", [message]) +} + +/// Print a text as Success +pub fun echo_success(message: Text): Null { + printf("\x1b[1;3;97;42m %s \x1b[0m\n", [message]) +} + +/// Print a text as Warning +pub fun echo_warning(message: Text): Null { + printf("\x1b[1;3;97;43m %s \x1b[0m\n", [message]) +} + +/// Print a text as Error and exit if the status code is greater than 0 +pub fun error(message: Text, exit_code: Num = 1): Null { + printf("\x1b[1;3;97;41m %s \x1b[0m\n", [message]) + if exit_code > 0 : exit(exit_code) +} diff --git a/lib/resources/alpha035/std/fs.ab b/lib/resources/alpha035/std/fs.ab new file mode 100644 index 0000000..dd14918 --- /dev/null +++ b/lib/resources/alpha035/std/fs.ab @@ -0,0 +1,74 @@ +/// Check if directory exists +pub fun dir_exist(path) { + $[ -d "{path}" ]$ failed { + return false + } + return true +} + +/// Check if file exists +pub fun file_exist(path) { + $[ -f "{path}" ]$ failed { + return false + } + return true +} + +/// Get the file content +pub fun file_read(path) { + return $< "{path}"$? +} + +/// Write the content to the file +/// Doesn't check if the file exist +pub fun file_write(path, content) { + return $echo "{content}" > "{path}"$? +} + +/// Append the content to the file +/// Doesn't check if the file exist +pub fun file_append(path, content) { + return $echo "{content}" >> "{path}"$? +} + +/// Create a symbolic link +/// If the file doens't exist return a boolean and print a message +pub fun create_symbolic_link(origin: Text, destination: Text): Bool { + if file_exist(origin) { + unsafe $ln -s "{origin}" "{destination}"$ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +/// Create a directory with all intermediate directories as required +pub fun create_dir(path: Text): Null { + if not dir_exist(path) { + unsafe $mkdir -p "{path}"$ + } +} + +/// Set the file as executable +/// If the file doesn't exist return a boolean and print a message +pub fun make_executable(path: Text): Bool { + if file_exist(path) { + unsafe $chmod +x "{path}"$ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Change the owner of the file +/// If the file doesn't exist return false +pub fun change_owner(user: Text, path: Text): Bool { + if file_exist(path) or dir_exist(path) { + unsafe $chown -R "{user}" "{path}"$ + return true + } + + return false +} diff --git a/lib/resources/alpha035/std/http.ab b/lib/resources/alpha035/std/http.ab new file mode 100644 index 0000000..439285f --- /dev/null +++ b/lib/resources/alpha035/std/http.ab @@ -0,0 +1,25 @@ +import * from "std/env" + +/// Downloads a file from a given URL and saves it to a specified path using available command-line tools. +/// +/// This function attempts to download a file from the provided URL and save it to the specified path. +/// It checks for the availability of common command-line tools (`curl`, `wget`, and `aria2c`) and uses the first available tool to perform the download. +/// If none of the tools are available, the function returns `false`. +pub fun download(url: Text, path: Text): Bool { + if { + is_command("curl") { + unsafe $curl -L -o "{path}" "{url}"$ + } + is_command("wget") { + unsafe $wget "{url}" -P "{path}"$ + } + is_command("aria2c") { + unsafe $aria2c "{url}" -d "{path}"$ + } + else { + return false + } + } + + return true +} diff --git a/lib/resources/alpha035/std/math.ab b/lib/resources/alpha035/std/math.ab new file mode 100644 index 0000000..db3f6b9 --- /dev/null +++ b/lib/resources/alpha035/std/math.ab @@ -0,0 +1,34 @@ +/// Sum the array content +#[allow_absurd_cast] +pub fun sum(list: [Num]): Num { + return unsafe $echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}'$ as Num +} + +/// Returns the number rounded to the nearest integer +#[allow_absurd_cast] +pub fun round(number: Num): Num { + if number > 0 { + return unsafe $echo "({number}+0.5)/1" | bc$ as Num + } + + return unsafe $echo "({number}-0.5)/1" | bc$ as Num +} + +/// Returns the largest integer less than or equal to the number +#[allow_absurd_cast] +pub fun floor(number: Num): Num { + return unsafe $echo "{number}" | awk '\{printf "%d", (\$1 < 0 ? int(\$1) - 1 : int(\$1))}'$ as Num +} + +/// Returns the smallest integer greater than or equal to the number +#[allow_absurd_cast] +pub fun ceil(number: Num): Num { + return floor(number) + 1 +} + +/// Returns the absolute value of the number +#[allow_absurd_cast] +pub fun abs(number: Num): Num { + if number < 0: return -number + return number +} diff --git a/lib/resources/alpha035/std/text.ab b/lib/resources/alpha035/std/text.ab new file mode 100644 index 0000000..27b1b0d --- /dev/null +++ b/lib/resources/alpha035/std/text.ab @@ -0,0 +1,164 @@ +/// Finds the first occurrence of a pettern in the content and replaces it with provided replacement text +pub fun replace_once(source, pattern, replacement) { + return "\$\{source/{pattern}/{replacement}}" +} + +/// Replaces all occurences of a pattern in the content with provided replacement text +pub fun replace(source, pattern, replacement) { + return "\$\{source//{pattern}/{replacement}}" +} + +/// Replaces all occurences of a regex pattern in the content with provided replacement text +/// +/// Function uses `sed` +pub fun replace_regex(source: Text, pattern: Text, replacement: Text): Text { + return unsafe $echo "{source}" | sed -e "s/{pattern}/{replacement}/g"$ +} + +/// This function splits the input `text` into an array of substrings using the specified `delimiter`. +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + unsafe $IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}")$ + return result +} + +/// Splits a `text` into an array of substrings based on newline characters. +pub fun lines(text: Text): [Text] { + return split(text, "\n") +} + +/// Splits a `text` into an array of substrings based on space character. +pub fun words(text: Text): [Text] { + return split(text, " ") +} + +/// Merge a text using the delimeter specified +pub fun join(list: [Text], delimiter: Text): Text { + return unsafe $IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}"$ +} + +/// Trim the spaces at top of the text using `sed` +pub fun trim_left(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/^[[:space:]]*//'$ +} + +/// Trim the spaces at end of the text using `sed` +pub fun trim_right(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/[[:space:]]*\$//'$ +} + +/// Trim the spaces from the text input +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +/// Lowercase the text input using `tr` +pub fun lower(text: Text): Text { + return unsafe $echo "{text}" | tr '[:upper:]' '[:lower:]'$ +} + +/// Lowercase the text input using `tr` +pub fun upper(text: Text): Text { + return unsafe $echo "{text}" | tr '[:lower:]' '[:upper:]'$ +} + +/// Attempts to parse a given text into a number, returning the parsed number or zero if parsing fails. +#[allow_absurd_cast] +pub fun parse(text: Text): Num? { + $[ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null$? + return text as Num +} + +/// Splits a text into an array of individual characters. +pub fun chars(text: Text): [Text] { + let chars = [Text] + unsafe $for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done;$ + return chars +} + +/// Get the text or array length +#[allow_absurd_cast] +pub fun len(value): Num { + unsafe { + if value is Text: + return $echo "\$\{#{nameof value}}"$ as Num + else: + return $echo "\$\{#{nameof value}[@]}"$ as Num + } +} + +/// Check if text contain the value +pub fun contains(text: Text, phrase: Text): Bool { + let result = unsafe $if [[ "{text}" == *"{phrase}"* ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Reverse a text using `rev` +pub fun reverse(text: Text): Text { + return unsafe $echo "{text}" | rev$ +} + +/// Check if text starts with a value +pub fun starts_with(text: Text, prefix: Text): Bool { + let result = unsafe $if [[ "{text}" == "{prefix}"* ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Check if text ends with a value +pub fun ends_with(text: Text, suffix: Text): Bool { + let result = unsafe $if [[ "{text}" == *"{suffix}" ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Returns a substring from `text` starting at the given `index` (0-based). +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +/// If `length` is provided, the substring will include `length` characters; otherwise, it slices to the end of `text`. +/// If `length` is negative, an empty string is returned. +pub fun slice(text: Text, index: Num, length: Num = 0): Text { + if length == 0: length = len(text) - index + if length <= 0: return "" + return unsafe $printf "%.{length}s" "\$\{text:{index}}"$ +} + +/// Returns the character from `text` at the specified `index` (0-based). +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +pub fun char_at(text: Text, index: Num): Text { + return unsafe $printf "%.1s" "\$\{text:{index}}"$ +} + +/// Capitalize the first letter of the given `text` +pub fun capitalize(text: Text): Text { + return unsafe $echo "{text}" | sed "s/^\(.\)/\U\1/"$ +} + +/// Pads `text` with the specified `pad` character on left until it reaches the desired `length`. +pub fun lpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = unsafe $printf "%{length}s" "" | tr " " "{pad}"$ + return pad + text +} + +/// Pads `text` with the specified `pad` character on the right until it reaches the desired `length`. +pub fun rpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = unsafe $printf "%{length}s" "" | tr " " "{pad}"$ + return text + pad +} + +/// Pads `text` with zeros on the left until it reaches the desired `length`. +pub fun zfill(text: Text, length: Num): Text { + return lpad(text, "0", length) +} diff --git a/lib/resources/alpha040/builtin.ab b/lib/resources/alpha040/builtin.ab new file mode 100644 index 0000000..6f310fe --- /dev/null +++ b/lib/resources/alpha040/builtin.ab @@ -0,0 +1,38 @@ +/// This builtin reads one line at a time from a text file. +/// It can be used in place of an array in an iterative for loop (with or without an index). +/// This is efficient because each line is read into memory, and processed before the next line is read: +/// +/// ```amber +/// for line in lines("foo.txt") { +/// echo line +/// } +/// +/// for index, line in lines("bar.txt") { +/// echo "\#{index} {line}" +/// } +/// ``` +/// +/// Alternatively, it can be used as the right hand side of an array assignment. +/// This is inefficient because the entire file is read into memory in one go: +/// +/// ```amber +/// let lines = lines("foo.txt") +/// lines += lines("bar.txt") +/// echo len(lines) +/// ``` +pub fun lines(path: Text): [Text] {} + +/// For a `Text` value, this builtin calculates and returns the length (in ASCII characters) as a Num type. +/// It is transpiled to `${#TEXT}`: +/// ```amber +/// // Returns 37 +/// echo len("Jackdaws love my big sphinx of quartz") +/// ``` +/// For an `Array` `[]` value, it calculates and returns the length of the array as a Num type. +/// It is transpiled to `${#ARRAY[@]}`: +/// +/// ```amber +/// // Returns 5 +/// echo len(["one", "two", "three", "four", "five"]) +/// ``` +pub fun len(value): Num {} \ No newline at end of file diff --git a/lib/resources/alpha040/std/array.ab b/lib/resources/alpha040/std/array.ab new file mode 100644 index 0000000..d349b7a --- /dev/null +++ b/lib/resources/alpha040/std/array.ab @@ -0,0 +1,80 @@ +/// Returns index of the first value found in the specified array. +/// +/// If the value is not found, the function returns -1. +pub fun array_find(array, value): Num { + for index, element in array { + if value as Text == element as Text { + return index + } + } + return -1 +} + +/// Searches for a value in an array and returns an array with the index of the various items. +pub fun array_find_all(array, value): [Num] { + let result = [Num] + for index, element in array { + if value as Text == element as Text { + result += [index] + } + } + return result +} + +/// Checks if a value is in the array. +pub fun array_contains(array, value) { + let result = array_find(array, value) + return result >= 0 +} + +/// Returns the first element in the array; if the array is empty, the return +/// value is undefined. +pub fun array_first(array) { + return array[0] +} + +/// Returns the last element in the array; if the array is empty, the return +/// value is undefined. +pub fun array_last(array) { + let index = len(array) - 1 + return array[index] +} + +/// Removes an element at the index from the array; if the index is negative +/// or beyond the end, the return value is undefined, but the array will be +/// unchanged. +pub fun array_remove_at(ref array: [], index: Num): Null { + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] +} + +/// Removes an element at the index from the array, and returns it; if the +/// index is negative or beyond the end, the return value is undefined, but +/// the array will be unchanged. +pub fun array_extract_at(ref array, index) { + let element = array[index] + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] + return element +} + +/// Removes the last element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun array_pop(ref array) { + let length = len(array) + let index = length - 1 + let element = array[index] + array = array[0..index] + return element +} + +/// Removes the first element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun array_shift(ref array) { + let length = len(array) + let element = array[0] + array = array[1..length] + return element +} diff --git a/lib/resources/alpha040/std/date.ab b/lib/resources/alpha040/std/date.ab new file mode 100644 index 0000000..15c8d58 --- /dev/null +++ b/lib/resources/alpha040/std/date.ab @@ -0,0 +1,126 @@ +/// ### EXPERIMENTAL +/// +/// Formats a date with a special format. +/// +/// If no date is specified, the current date is used. +/// +/// If no format is specified, "%FT%T%Z" format is used. +/// +/// For more info about format type "man date" on your shell or go to . +/// +/// Format : +/// ``` +/// %% a literal % +/// %a locale's abbreviated weekday name (e.g., Sun) +/// %A locale's full weekday name (e.g., Sunday) +/// %b locale's abbreviated month name (e.g., Jan) +/// %B locale's full month name (e.g., January) +/// %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005) +/// %C century; like %Y, except omit last two digits (e.g., 20) +/// %d day of month (e.g., 01) +/// %D date; same as %m/%d/%y +/// %e day of month, space padded; same as %_d +/// %F full date; like %+4Y-%m-%d +/// %g last two digits of year of ISO week number (see %G) +/// %G year of ISO week number (see %V); normally useful only with %V +/// %h same as %b +/// %H hour (00..23) +/// %I hour (01..12) +/// %j day of year (001..366) +/// %k hour, space padded ( 0..23); same as %_H +/// %l hour, space padded ( 1..12); same as %_I +/// %m month (01..12) +/// %M minute (00..59) +/// %n a newline +/// %N nanoseconds (000000000..999999999) +/// %p locale's equivalent of either AM or PM; blank if not known +/// %P like %p, but lower case +/// %q quarter of year (1..4) +/// %r locale's 12-hour clock time (e.g., 11:11:04 PM) +/// %R 24-hour hour and minute; same as %H:%M +/// %s seconds since the Epoch (1970-01-01 00:00 UTC) +/// %S second (00..60) +/// %t a tab +/// %T time; same as %H:%M:%S +/// %u day of week (1..7); 1 is Monday +/// %U week number of year, with Sunday as first day of week (00..53) +/// %V ISO week number, with Monday as first day of week (01..53) +/// %w day of week (0..6); 0 is Sunday +/// %W week number of year, with Monday as first day of week (00..53) +/// %x locale's date representation (e.g., 12/31/99) +/// %X locale's time representation (e.g., 23:13:48) +/// %y last two digits of year (00..99) +/// %Y year +/// %z +hhmm numeric time zone (e.g., -0400) +/// %:z +hh:mm numeric time zone (e.g., -04:00) +/// %::z +hh:mm:ss numeric time zone (e.g., -04:00:00) +/// %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30) +/// %Z alphabetic time zone abbreviation (e.g., EDT) +/// ``` +/// +/// By default, date pads numeric fields with zeroes. The following optional flags may follow '%': +/// +/// ``` +/// - (hyphen) do not pad the field +/// _ (underscore) pad with spaces +/// 0 (zero) pad with zeros +/// + pad with zeros, and put '+' before future years with >4 digits +/// ^ use upper case if possible +/// # use opposite case if possible +/// ``` +pub fun date_posix(format: Text = "", date: Text = "", utc: Bool = false): Text? { + if format == "": format = "%FT%T%Z" + if date == "": date = trust $ date +"%FT%T%Z" $ + if (utc) { + return $ date --utc -d "{date}" +"{format}" $? + } else { + return $ date -d "{date}" +"{format}" $? + } +} + +/// Returns the current timestamp (seconds since the Epoch (1970-01-01 00:00 UTC)). +#[allow_absurd_cast] +pub fun date_now(): Num { + return trust $ date +%s $ as Num +} + +/// ### EXPERIMENTAL +/// +/// Adds a value to a date. +/// +/// If no date is specified, the current date is used. +/// +/// Example : `date_add("+3 days")` +/// +/// You can use (+/-): +/// +/// - years +/// - months +/// - days +/// - hours +/// - minutes +/// - seconds +pub fun date_add(add:Text, date:Text = "", utc: Bool = false): Text? { + if date == "": date = trust $ date +"%FT%T%Z" $ + return date_posix("", "{date_posix("%F", date, utc)?} {add} {date_posix("%T", date, utc)?}", utc)? +} + +/// ### EXPERIMENTAL +/// Compares two dates. +/// +/// Returns 1 if date_a is after date_b. +/// +/// Returns 0 if date_a and date_b is the same. +/// +/// Returns -1 if date_b is after date_a. +/// +/// If date_b is not provided, current date will be used. +#[allow_absurd_cast] +pub fun date_compare(date_a: Text, date_b: Text = "", utc: Bool = false): Num? { + if date_b == "": date_b = trust date_posix("", "", utc) + let timestamp_a = date_posix("%s", date_a, utc)? as Num + let timestamp_b = date_posix("%s", date_b, utc)? as Num + if timestamp_a > timestamp_b: return 1 + if timestamp_a == timestamp_b: return 0 + if timestamp_a < timestamp_b: return -1 +} diff --git a/lib/resources/alpha040/std/env.ab b/lib/resources/alpha040/std/env.ab new file mode 100644 index 0000000..b24b0b7 --- /dev/null +++ b/lib/resources/alpha040/std/env.ab @@ -0,0 +1,164 @@ +import * from "std/fs" +import * from "std/text" + +/// Retrieves the value of an environment variable, optionally sourcing it from a file if not already set. +pub fun env_var_load(var: Text, file: Text = ".env"): Text { + let _var = trust $ echo "\$\{!var}" $ + if _var != "" { + return _var + } + + if file_exists(file) { + trust $ source "{file}" $ + return trust $ echo "\$\{!var}" $ + } + + return "" +} + +/// Loads the env file in the environment, using `xargs`. +pub fun env_file_load(file: Text = ".env"): Null { + trust $ export "\$(xargs < {file})" > /dev/null $ +} + +/// Checks if a variable inside the shell session exists. +pub fun env_var_test(name: Text): Bool { + $ [[ ! -z \$\{!{nameof name}+z} ]] $ failed { + return false + } + return true +} + +/// Sets a constant inside the shell session. +pub fun env_const_set(name: Text, val: Text): Null? { + $ readonly \${nameof name}="\${nameof val}" 2> /dev/null $? +} + +/// Gets a constant inside the shell session. +pub fun env_const_get(name: Text): Text? { + return $ echo \$\{!{nameof name}} $? +} + +/// Sets a constant inside the shell session. +pub fun env_var_set(name: Text, val: Text): Null? { + $ export \${nameof name}="\${nameof val}" 2> /dev/null $? +} + +/// Gets a constant inside the shell session. +pub fun env_var_get(name: Text): Text? { + return $ echo \$\{!{nameof name}} $? +} + +/// Removes a variable inside the shell session. +pub fun env_var_unset(name: Text): Null? { + $ unset {name} $? +} + +/// Checks if a command exists. +pub fun is_command(command: Text): Bool { + $ [ -x "\$(command -v {command})" ] $ failed { + return false + } + return true +} + +/// Creates a prompt and returns the value. +pub fun input_prompt(prompt: Text): Text { + trust $ read -p "\${nameof prompt}" $ + return "\$REPLY" +} + +/// Creates a prompt, hides any user input and returns the value. +pub fun input_hidden(prompt: Text): Text { + trust { + $ read -s -p "\${nameof prompt}" $ + $ echo "" >&2 $ + } + return "\$REPLY" +} + +/// Creates a confirm prompt (Yes/No), and returns true if the choice is Yes. +/// +/// "No" is the default choice, set default_yes to true for "Yes" as default choice. +pub fun input_confirm(prompt: Text, default_yes: Bool = false): Bool { + let choice_default = default_yes then " [\x1b[1mY/\x1b[0mn]" else " [y/\x1b[1mN\x1b[0m]" + trust { + $ printf "\x1b[1m{prompt}\x1b[0m{choice_default}" $ + $ read -s -n 1 $ + $ printf "\n" $ + } + let result = lowercase(trust $ echo \$REPLY $) + return result == "y" or (result == "" and default_yes) +} + +/// Checks if the command has failed. +pub fun has_failed(command: Text): Bool { + trust silent $ eval {command} $ + return status != 0 +} + +/// Checks if the script is running with a user with root permission. +pub fun is_root(): Bool { + if trust $ id -u $ == "0" { + return true + } + + return false +} + +/// `printf` the text following the arguments. +pub fun printf(format: Text, args: [Text] = [""]): Null { + trust $ {nameof args}=("{format}" "\$\{{nameof args}[@]}") $ + trust $ printf "\$\{{nameof args}[@]}" $ +} + +/// Escapes the text to be used with `printf`. +pub fun escaped(text: Text): Text { + return trust $ echo \${nameof text} | sed -e 's/\\\\/\\\\\\\\/g' -e "s/%/%%/g" $ +} + +/// Prepares a text with formatting options for `printf`. +pub fun styled(message: Text, style: Num, fg: Num, bg: Num): Text { + return "\x1b[{style};{fg};{bg}m{escaped(message)}\x1b[0m" +} + +/// Returns a text as bold. +pub fun bold(message: Text): Text { + return "\x1b[1m{escaped(message)}\x1b[0m" +} + +/// Returns a text as italic. +pub fun italic(message: Text): Text { + return "\x1b[3m{escaped(message)}\x1b[0m" +} + +/// Returns a text as underlined. +pub fun underlined(message: Text): Text { + return "\x1b[4m{escaped(message)}\x1b[0m" +} + +/// Prints a text with a specified color. +pub fun echo_colored(message: Text, color: Num): Null { + printf("\x1b[{color as Text}m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a info message. +pub fun echo_info(message: Text): Null { + printf("\x1b[1;3;97;44m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a success message. +pub fun echo_success(message: Text): Null { + printf("\x1b[1;3;97;42m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a warning message. +pub fun echo_warning(message: Text): Null { + printf("\x1b[1;3;97;43m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a error and exits if the status code is greater than 0. +pub fun echo_error(message: Text, exit_code: Num = 1): Null { + printf("\x1b[1;3;97;41m%s\x1b[0m\n", [message]) + if exit_code > 0 : exit(exit_code) +} diff --git a/lib/resources/alpha040/std/fs.ab b/lib/resources/alpha040/std/fs.ab new file mode 100644 index 0000000..47235b8 --- /dev/null +++ b/lib/resources/alpha040/std/fs.ab @@ -0,0 +1,140 @@ +import { match_regex, join, replace_regex, split } from "std/text" + +/// Checks if a directory exists. +pub fun dir_exists(path) { + $ [ -d "{path}" ] $ failed { + return false + } + return true +} + +/// Checks if a file exists. +pub fun file_exists(path) { + $ [ -f "{path}" ] $ failed { + return false + } + return true +} + +/// Gets file contents from a path. +pub fun file_read(path) { + return $ < "{path}" $? +} + +/// Writes content to a file. +/// Doesn't check if the file exist +pub fun file_write(path, content) { + return $ echo "{content}" > "{path}" $? +} + +/// Appends content to a file. +/// +/// Doesn't check if the file exists. +pub fun file_append(path, content) { + return $ echo "{content}" >> "{path}" $? +} + +/// Creates a symbolic link. +/// +/// If the file doesn't exist, it returns a boolean and prints a message. +pub fun symlink_create(origin: Text, destination: Text): Bool { + if file_exists(origin) { + trust $ ln -s "{origin}" "{destination}" $ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +/// Creates a directory with all parent directories as required. +pub fun dir_create(path: Text): Null { + if not dir_exists(path) { + trust $ mkdir -p "{path}" $ + } +} + +/// Sets a file as executable. +/// +/// If the file doesn't exist, it returns a boolean and prints a message. +pub fun file_chmod(path: Text, mode: Text): Bool { + if file_exists(path) { + trust $ chmod "{mode}" "{path}" $ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Changes the owner of a file. +/// +/// If the file doesn't exist, it returns `false` +pub fun file_chown(path: Text, user: Text): Bool { + if file_exists(path) or dir_exists(path) { + trust $ chown -R "{user}" "{path}" $ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Escapes all characters in the passed-in glob except "*", "?" and "/", +/// to prevent injection attacks. +fun escape_non_glob_chars(path: Text): Text { + return replace_regex(path, "\([^*?/]\)", "\\\\\1") +} + +/// Finds all files or directories matching multiple file globs. When +/// we have union types, this functionality can be merged into the main +/// `glob` function. +pub fun file_glob_all(paths: [Text]): [Text]? { + let combined = "" + if len(paths) == 1 { + combined = escape_non_glob_chars(paths[0]) + } else { + let items = [Text] + for item in paths { + item = escape_non_glob_chars(item) + items += [item] + } + combined = join(items, " ") + } + let files = $ eval "for file in {combined}; do [ -e \\\"\\\$file\\\" ] && echo \\\"\\\$file\\\"; done" $? + return split(files, "\n") +} + +/// Finds all files or directories matching a file glob. +pub fun file_glob(path: Text): [Text]? { + return file_glob_all([path])? +} + +/// Extract the file detecting from the filename the extension +/// Supports: bz2, gz, xz, bz2, deb, rar, rpm, tar(gz/xz/bz), zip(war/jar), 7z +/// Note: Not all the commands supports the output folder path +pub fun file_extract(path: Text, target: Text): Null? { + if file_exists(path) { + if { + match_regex(path, "\.\(tar\.bz2\|tbz\|tbz2\)$"): $ tar xvjf "{path}" -C "{target}" $? + match_regex(path, "\.\(tar\.gz\|tgz\)$"): $ tar xzf "{path}" -C "{target}" $? + match_regex(path, "\.\(tar\.xz\|txz$\)$"): $ tar xJf "{path}" -C "{target}" $? + match_regex(path, "\.bz2$"): $ bunzip2 "{path}" $? + match_regex(path, "\.deb$"): $ dpkg-deb -xv "{path}" "{target}" $? + match_regex(path, "\.gz$"): $ gunzip "{path}" $? + match_regex(path, "\.rar$"): $ unrar x "{path}" "{target}" $? + match_regex(path, "\.rpm$"): $ rpm2cpio "{path}" | cpio -idm $? + match_regex(path, "\.tar$"): $ tar xf "{path}" -C "{target}" $? + match_regex(path, "\.xz$"): $ xz --decompress "{path}" $? + match_regex(path, "\.7z$"): $ 7z -y "{path}" -o "{target}" $? + match_regex(path, "\.\(zip\|war\|jar\)$"): $ unzip "{path}" -d "{target}" $? + else { + echo "Error: Unsupported file type" + fail 3 + } + } + } else { + echo "Error: File not found" + fail 2 + } +} diff --git a/lib/resources/alpha040/std/http.ab b/lib/resources/alpha040/std/http.ab new file mode 100644 index 0000000..c1dbfa8 --- /dev/null +++ b/lib/resources/alpha040/std/http.ab @@ -0,0 +1,24 @@ +import * from "std/env" + +/// Downloads a file from a given URL and saves it to a specified path using available command-line tools. +/// +/// It checks for the availability of common command-line tools (`curl`, `wget`, and `aria2c`, in order) and uses the first available tool to perform the download. +/// If none of the tools are available, the function returns `false`. +pub fun file_download(url: Text, path: Text): Bool { + if { + is_command("curl") { + trust $ curl -L -o "{path}" "{url}" $ + } + is_command("wget") { + trust $ wget "{url}" -P "{path}" $ + } + is_command("aria2c") { + trust $ aria2c "{url}" -d "{path}" $ + } + else { + return false + } + } + + return true +} diff --git a/lib/resources/alpha040/std/math.ab b/lib/resources/alpha040/std/math.ab new file mode 100644 index 0000000..793b312 --- /dev/null +++ b/lib/resources/alpha040/std/math.ab @@ -0,0 +1,34 @@ +/// Sums an array's contents +#[allow_absurd_cast] +pub fun math_sum(list: [Num]): Num { + return trust $ echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}' $ as Num +} + +/// Returns a number, rounded to the nearest integer +#[allow_absurd_cast] +pub fun math_round(number: Num): Num { + if number > 0 { + return trust $ echo "({number}+0.5)/1" | bc $ as Num + } + + return trust $ echo "({number}-0.5)/1" | bc $ as Num +} + +/// Returns the largest integer less than or equal to a number +#[allow_absurd_cast] +pub fun math_floor(number: Num): Num { + return trust $ echo "{number}" | awk '\{printf "%d", (\$1 < 0 ? int(\$1) - 1 : int(\$1))}' $ as Num +} + +/// Returns the smallest integer greater than or equal to a number +#[allow_absurd_cast] +pub fun math_ceil(number: Num): Num { + return math_floor(number) + 1 +} + +/// Returns the absolute value of a number +#[allow_absurd_cast] +pub fun math_abs(number: Num): Num { + if number < 0: return -number + return number +} diff --git a/lib/resources/alpha040/std/text.ab b/lib/resources/alpha040/std/text.ab new file mode 100644 index 0000000..fe071e3 --- /dev/null +++ b/lib/resources/alpha040/std/text.ab @@ -0,0 +1,241 @@ +/// Replaces all occurences of a pattern in the content with the provided replace text. +pub fun replace(source, search, replace) { + return "\$\{source//{search}/{replace}}" +} + +/// Replaces the first occurence of a pattern in the content with the provided replace text. +pub fun replace_one(source, search, replace) { + return "\$\{source/{search}/{replace}}" +} + +/// Replaces all occurences of a regex pattern in the content with the provided replace text. +/// +/// Function uses `sed` +pub fun replace_regex(source: Text, search: Text, replace: Text, extended: Bool = false): Text { + trust { + search = replace(search, "/", "\/") + replace = replace(replace, "/", "\/") + if extended { + // GNU sed versions 4.0 through 4.2 support extended regex syntax, + // but only via the "-r" option; use that if the version information + // contains "GNU sed". + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + let flag = status == 0 then "-r" else "-E" + return $ echo "{source}" | sed "{flag}" -e "s/{search}/{replace}/g" $ + } else { + return $ echo "{source}" | sed -e "s/{search}/{replace}/g" $ + } + } +} + +/// Splits the input `text` into an array of substrings using the specified `delimiter`. +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + trust $ IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}") $ + return result +} + +/// Splits a `text` into an array of substrings based on newline characters. +pub fun split_lines(text: Text): [Text] { + return split(text, "\n") +} + +/// Splits a `text` into an array of substrings based on space character. +pub fun split_words(text: Text): [Text] { + return split(text, " ") +} + +/// Merges text using the delimeter specified. +pub fun join(list: [Text], delimiter: Text): Text { + return trust $ IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}" $ +} + +/// Trims the spaces at top of the text using `sed`. +pub fun trim_left(text: Text): Text { + return trust $ echo "{text}" | sed -e 's/^[[:space:]]*//' $ +} + +/// Trims the spaces at end of the text using `sed`. +pub fun trim_right(text: Text): Text { + return trust $ echo "{text}" | sed -e 's/[[:space:]]*\$//' $ +} + +/// Trims the spaces from the text input. +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +/// Makes the text input lowercase using `tr`. +pub fun lowercase(text: Text): Text { + return trust $ echo "{text}" | tr '[:upper:]' '[:lower:]' $ +} + +/// Makes the text input uppercase using `tr`. +pub fun uppercase(text: Text): Text { + return trust $ echo "{text}" | tr '[:lower:]' '[:upper:]' $ +} + +/// Attempts to parse a given text into a number, returning the parsed number or zero if parsing fails. +#[allow_absurd_cast] +pub fun parse_number(text: Text): Num? { + $ [ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null $? + return text as Num +} + +/// Splits a text into an array of individual characters. +pub fun split_chars(text: Text): [Text] { + let chars = [Text] + trust $ for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done $ + return chars +} + +/// Checks if some text contains a value. +pub fun text_contains(text: Text, phrase: Text): Bool { + let result = trust $ if [[ "{text}" == *"{phrase}"* ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Checks if an array value is in the text. +pub fun text_contains_any(text: Text, terms: [Text]): Bool { + for term in terms { + if text_contains(text, term) { + return true + } + } + + return false +} + +/// Checks if all the arrays values are in the string +pub fun text_contains_all(text: Text, terms: [Text]): Bool { + for term in terms { + if not text_contains(text, term) { + return false + } + } + + return true +} + +/// Match all occurences of a regex pattern. +/// +/// Function uses `sed` +pub fun match_regex(source: Text, search: Text, extended: Bool = false): Bool { + trust { + search = replace(search, "/", "\/") + let output = "" + if extended { + // GNU sed versions 4.0 through 4.2 support extended regex syntax, + // but only via the "-r" option; use that if the version information + // contains "GNU sed". + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + let flag = status == 0 then "-r" else "-E" + output = $ echo "{source}" | sed "{flag}" -ne "/{search}/p" $ + } else { + output = $ echo "{source}" | sed -ne "/{search}/p" $ + } + if output != "" { + return true + } + } + return false +} + +/// Checks if an array value (with regular expression) is in the text. +pub fun match_regex_any(text: Text, terms: [Text]): Bool { + for term in terms { + if match_regex(text, term, false) { + return true + } + } + + return false +} + +/// Reverses text using `rev`. +pub fun reversed(text: Text): Text { + return trust $ echo "{text}" | rev $ +} + +/// Checks if text starts with a value. +pub fun starts_with(text: Text, prefix: Text): Bool { + let result = trust $ if [[ "{text}" == "{prefix}"* ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Checks if text ends with a value. +pub fun ends_with(text: Text, suffix: Text): Bool { + let result = trust $ if [[ "{text}" == *"{suffix}" ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Returns a substring from `text` starting at the given `index` (0-based). +/// +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +/// If `length` is provided, the substring will include `length` characters; otherwise, it slices to the end of `text`. +/// If `length` is negative, an empty string is returned. +pub fun slice(text: Text, index: Num, length: Num = 0): Text { + if length == 0: length = len(text) - index + if length <= 0: return "" + return trust $ printf "%.{length}s" "\$\{text:{index}}" $ +} + +/// Returns the character from `text` at the specified `index` (0-based). +/// +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +pub fun char_at(text: Text, index: Num): Text { + return trust $ printf "%.1s" "\$\{text:{index}}" $ +} + +/// Capitalize the first letter of the given `text`. +#[allow_absurd_cast] +pub fun capitalized(text: Text): Text { + trust { + if len(text) == 0 { + return text + } + const bash_version = $ echo \"\$\{BASH_VERSINFO[0]}\" $ as Num + if bash_version >= 4 { + return $ echo \"\$\{text^}\" $ + } + // GNU sed supports \U + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + if status == 0 { + return $ echo "{text}" | sed "s/^\(.\)/\U\1/" $ + } + const first_letter = uppercase(char_at(text, 0)) + return first_letter + slice(text, 1) + } +} + +/// Pads `text` with the specified `pad` character on left until it reaches the desired `length`. +pub fun lpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = trust $ printf "%{length}s" "" | tr " " "{pad}" $ + return pad + text +} + +/// Pads `text` with the specified `pad` character on the right until it reaches the desired `length`. +pub fun rpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = trust $ printf "%{length}s" "" | tr " " "{pad}" $ + return text + pad +} + +/// Pads `text` with zeros on the left until it reaches the desired `length`. +pub fun zfill(text: Text, length: Num): Text { + return lpad(text, "0", length) +} diff --git a/lsp/src/analysis/alpha034/exp.rs b/lib/src/analysis/alpha034/exp.rs similarity index 100% rename from lsp/src/analysis/alpha034/exp.rs rename to lib/src/analysis/alpha034/exp.rs diff --git a/lsp/src/analysis/alpha034/global.rs b/lib/src/analysis/alpha034/global.rs similarity index 100% rename from lsp/src/analysis/alpha034/global.rs rename to lib/src/analysis/alpha034/global.rs diff --git a/lsp/src/analysis/alpha034/mod.rs b/lib/src/analysis/alpha034/mod.rs similarity index 100% rename from lsp/src/analysis/alpha034/mod.rs rename to lib/src/analysis/alpha034/mod.rs diff --git a/lsp/src/analysis/alpha034/stmnts.rs b/lib/src/analysis/alpha034/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha034/stmnts.rs rename to lib/src/analysis/alpha034/stmnts.rs diff --git a/lsp/src/analysis/alpha035/exp.rs b/lib/src/analysis/alpha035/exp.rs similarity index 100% rename from lsp/src/analysis/alpha035/exp.rs rename to lib/src/analysis/alpha035/exp.rs diff --git a/lsp/src/analysis/alpha035/global.rs b/lib/src/analysis/alpha035/global.rs similarity index 100% rename from lsp/src/analysis/alpha035/global.rs rename to lib/src/analysis/alpha035/global.rs diff --git a/lsp/src/analysis/alpha035/mod.rs b/lib/src/analysis/alpha035/mod.rs similarity index 100% rename from lsp/src/analysis/alpha035/mod.rs rename to lib/src/analysis/alpha035/mod.rs diff --git a/lsp/src/analysis/alpha035/stmnts.rs b/lib/src/analysis/alpha035/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha035/stmnts.rs rename to lib/src/analysis/alpha035/stmnts.rs diff --git a/lsp/src/analysis/alpha040/exp.rs b/lib/src/analysis/alpha040/exp.rs similarity index 100% rename from lsp/src/analysis/alpha040/exp.rs rename to lib/src/analysis/alpha040/exp.rs diff --git a/lsp/src/analysis/alpha040/global.rs b/lib/src/analysis/alpha040/global.rs similarity index 100% rename from lsp/src/analysis/alpha040/global.rs rename to lib/src/analysis/alpha040/global.rs diff --git a/lsp/src/analysis/alpha040/mod.rs b/lib/src/analysis/alpha040/mod.rs similarity index 100% rename from lsp/src/analysis/alpha040/mod.rs rename to lib/src/analysis/alpha040/mod.rs diff --git a/lsp/src/analysis/alpha040/stmnts.rs b/lib/src/analysis/alpha040/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha040/stmnts.rs rename to lib/src/analysis/alpha040/stmnts.rs diff --git a/lsp/src/analysis/mod.rs b/lib/src/analysis/mod.rs similarity index 100% rename from lsp/src/analysis/mod.rs rename to lib/src/analysis/mod.rs diff --git a/lsp/src/analysis/types.rs b/lib/src/analysis/types.rs similarity index 100% rename from lsp/src/analysis/types.rs rename to lib/src/analysis/types.rs diff --git a/lsp/src/backend.rs b/lib/src/backend.rs similarity index 100% rename from lsp/src/backend.rs rename to lib/src/backend.rs diff --git a/lsp/src/files.rs b/lib/src/files.rs similarity index 100% rename from lsp/src/files.rs rename to lib/src/files.rs diff --git a/lsp/src/fs.rs b/lib/src/fs.rs similarity index 100% rename from lsp/src/fs.rs rename to lib/src/fs.rs diff --git a/lsp/src/grammar/alpha034/expressions/and.rs b/lib/src/grammar/alpha034/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/and.rs rename to lib/src/grammar/alpha034/expressions/and.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/array.rs b/lib/src/grammar/alpha034/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/array.rs rename to lib/src/grammar/alpha034/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/bool.rs b/lib/src/grammar/alpha034/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/bool.rs rename to lib/src/grammar/alpha034/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/call.rs b/lib/src/grammar/alpha034/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/call.rs rename to lib/src/grammar/alpha034/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/command.rs b/lib/src/grammar/alpha034/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/command.rs rename to lib/src/grammar/alpha034/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/mod.rs b/lib/src/grammar/alpha034/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/mod.rs rename to lib/src/grammar/alpha034/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/null.rs b/lib/src/grammar/alpha034/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/null.rs rename to lib/src/grammar/alpha034/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/number.rs b/lib/src/grammar/alpha034/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/number.rs rename to lib/src/grammar/alpha034/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/parentheses.rs b/lib/src/grammar/alpha034/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha034/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/status.rs b/lib/src/grammar/alpha034/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/status.rs rename to lib/src/grammar/alpha034/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/text.rs b/lib/src/grammar/alpha034/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/text.rs rename to lib/src/grammar/alpha034/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/var.rs b/lib/src/grammar/alpha034/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/var.rs rename to lib/src/grammar/alpha034/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha034/expressions/cast.rs b/lib/src/grammar/alpha034/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/cast.rs rename to lib/src/grammar/alpha034/expressions/cast.rs diff --git a/lsp/src/grammar/alpha034/expressions/comparison.rs b/lib/src/grammar/alpha034/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/comparison.rs rename to lib/src/grammar/alpha034/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha034/expressions/is.rs b/lib/src/grammar/alpha034/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/is.rs rename to lib/src/grammar/alpha034/expressions/is.rs diff --git a/lsp/src/grammar/alpha034/expressions/mod.rs b/lib/src/grammar/alpha034/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/mod.rs rename to lib/src/grammar/alpha034/expressions/mod.rs diff --git a/lsp/src/grammar/alpha034/expressions/or.rs b/lib/src/grammar/alpha034/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/or.rs rename to lib/src/grammar/alpha034/expressions/or.rs diff --git a/lsp/src/grammar/alpha034/expressions/product.rs b/lib/src/grammar/alpha034/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/product.rs rename to lib/src/grammar/alpha034/expressions/product.rs diff --git a/lsp/src/grammar/alpha034/expressions/range.rs b/lib/src/grammar/alpha034/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/range.rs rename to lib/src/grammar/alpha034/expressions/range.rs diff --git a/lsp/src/grammar/alpha034/expressions/sum.rs b/lib/src/grammar/alpha034/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/sum.rs rename to lib/src/grammar/alpha034/expressions/sum.rs diff --git a/lsp/src/grammar/alpha034/expressions/ternary.rs b/lib/src/grammar/alpha034/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/ternary.rs rename to lib/src/grammar/alpha034/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha034/expressions/unary.rs b/lib/src/grammar/alpha034/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/unary.rs rename to lib/src/grammar/alpha034/expressions/unary.rs diff --git a/lsp/src/grammar/alpha034/global.rs b/lib/src/grammar/alpha034/global.rs similarity index 100% rename from lsp/src/grammar/alpha034/global.rs rename to lib/src/grammar/alpha034/global.rs diff --git a/lsp/src/grammar/alpha034/lexer.rs b/lib/src/grammar/alpha034/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha034/lexer.rs rename to lib/src/grammar/alpha034/lexer.rs diff --git a/lsp/src/grammar/alpha034/mod.rs b/lib/src/grammar/alpha034/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/mod.rs rename to lib/src/grammar/alpha034/mod.rs diff --git a/lsp/src/grammar/alpha034/parser.rs b/lib/src/grammar/alpha034/parser.rs similarity index 100% rename from lsp/src/grammar/alpha034/parser.rs rename to lib/src/grammar/alpha034/parser.rs diff --git a/lsp/src/grammar/alpha034/semantic_tokens.rs b/lib/src/grammar/alpha034/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha034/semantic_tokens.rs rename to lib/src/grammar/alpha034/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha034/statements/block.rs b/lib/src/grammar/alpha034/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/block.rs rename to lib/src/grammar/alpha034/statements/block.rs diff --git a/lsp/src/grammar/alpha034/statements/comment.rs b/lib/src/grammar/alpha034/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/comment.rs rename to lib/src/grammar/alpha034/statements/comment.rs diff --git a/lsp/src/grammar/alpha034/statements/const_init.rs b/lib/src/grammar/alpha034/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/const_init.rs rename to lib/src/grammar/alpha034/statements/const_init.rs diff --git a/lsp/src/grammar/alpha034/statements/failed.rs b/lib/src/grammar/alpha034/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/failed.rs rename to lib/src/grammar/alpha034/statements/failed.rs diff --git a/lsp/src/grammar/alpha034/statements/if_cond.rs b/lib/src/grammar/alpha034/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/if_cond.rs rename to lib/src/grammar/alpha034/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha034/statements/keywords.rs b/lib/src/grammar/alpha034/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/keywords.rs rename to lib/src/grammar/alpha034/statements/keywords.rs diff --git a/lsp/src/grammar/alpha034/statements/loops.rs b/lib/src/grammar/alpha034/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/loops.rs rename to lib/src/grammar/alpha034/statements/loops.rs diff --git a/lsp/src/grammar/alpha034/statements/mod.rs b/lib/src/grammar/alpha034/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/mod.rs rename to lib/src/grammar/alpha034/statements/mod.rs diff --git a/lsp/src/grammar/alpha034/statements/modifiers.rs b/lib/src/grammar/alpha034/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/modifiers.rs rename to lib/src/grammar/alpha034/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha034/statements/shebang.rs b/lib/src/grammar/alpha034/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/shebang.rs rename to lib/src/grammar/alpha034/statements/shebang.rs diff --git a/lsp/src/grammar/alpha034/statements/shorthands.rs b/lib/src/grammar/alpha034/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/shorthands.rs rename to lib/src/grammar/alpha034/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha034/statements/var_init.rs b/lib/src/grammar/alpha034/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/var_init.rs rename to lib/src/grammar/alpha034/statements/var_init.rs diff --git a/lsp/src/grammar/alpha034/statements/var_set.rs b/lib/src/grammar/alpha034/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/var_set.rs rename to lib/src/grammar/alpha034/statements/var_set.rs diff --git a/lsp/src/grammar/alpha035/expressions/and.rs b/lib/src/grammar/alpha035/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/and.rs rename to lib/src/grammar/alpha035/expressions/and.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/array.rs b/lib/src/grammar/alpha035/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/array.rs rename to lib/src/grammar/alpha035/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/bool.rs b/lib/src/grammar/alpha035/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/bool.rs rename to lib/src/grammar/alpha035/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/call.rs b/lib/src/grammar/alpha035/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/call.rs rename to lib/src/grammar/alpha035/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/command.rs b/lib/src/grammar/alpha035/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/command.rs rename to lib/src/grammar/alpha035/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/mod.rs b/lib/src/grammar/alpha035/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/mod.rs rename to lib/src/grammar/alpha035/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/null.rs b/lib/src/grammar/alpha035/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/null.rs rename to lib/src/grammar/alpha035/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/number.rs b/lib/src/grammar/alpha035/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/number.rs rename to lib/src/grammar/alpha035/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/parentheses.rs b/lib/src/grammar/alpha035/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha035/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/status.rs b/lib/src/grammar/alpha035/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/status.rs rename to lib/src/grammar/alpha035/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/text.rs b/lib/src/grammar/alpha035/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/text.rs rename to lib/src/grammar/alpha035/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/var.rs b/lib/src/grammar/alpha035/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/var.rs rename to lib/src/grammar/alpha035/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha035/expressions/cast.rs b/lib/src/grammar/alpha035/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/cast.rs rename to lib/src/grammar/alpha035/expressions/cast.rs diff --git a/lsp/src/grammar/alpha035/expressions/comparison.rs b/lib/src/grammar/alpha035/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/comparison.rs rename to lib/src/grammar/alpha035/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha035/expressions/is.rs b/lib/src/grammar/alpha035/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/is.rs rename to lib/src/grammar/alpha035/expressions/is.rs diff --git a/lsp/src/grammar/alpha035/expressions/mod.rs b/lib/src/grammar/alpha035/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/mod.rs rename to lib/src/grammar/alpha035/expressions/mod.rs diff --git a/lsp/src/grammar/alpha035/expressions/or.rs b/lib/src/grammar/alpha035/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/or.rs rename to lib/src/grammar/alpha035/expressions/or.rs diff --git a/lsp/src/grammar/alpha035/expressions/product.rs b/lib/src/grammar/alpha035/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/product.rs rename to lib/src/grammar/alpha035/expressions/product.rs diff --git a/lsp/src/grammar/alpha035/expressions/range.rs b/lib/src/grammar/alpha035/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/range.rs rename to lib/src/grammar/alpha035/expressions/range.rs diff --git a/lsp/src/grammar/alpha035/expressions/sum.rs b/lib/src/grammar/alpha035/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/sum.rs rename to lib/src/grammar/alpha035/expressions/sum.rs diff --git a/lsp/src/grammar/alpha035/expressions/ternary.rs b/lib/src/grammar/alpha035/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/ternary.rs rename to lib/src/grammar/alpha035/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha035/expressions/unary.rs b/lib/src/grammar/alpha035/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/unary.rs rename to lib/src/grammar/alpha035/expressions/unary.rs diff --git a/lsp/src/grammar/alpha035/global.rs b/lib/src/grammar/alpha035/global.rs similarity index 100% rename from lsp/src/grammar/alpha035/global.rs rename to lib/src/grammar/alpha035/global.rs diff --git a/lsp/src/grammar/alpha035/lexer.rs b/lib/src/grammar/alpha035/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha035/lexer.rs rename to lib/src/grammar/alpha035/lexer.rs diff --git a/lsp/src/grammar/alpha035/mod.rs b/lib/src/grammar/alpha035/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/mod.rs rename to lib/src/grammar/alpha035/mod.rs diff --git a/lsp/src/grammar/alpha035/parser.rs b/lib/src/grammar/alpha035/parser.rs similarity index 100% rename from lsp/src/grammar/alpha035/parser.rs rename to lib/src/grammar/alpha035/parser.rs diff --git a/lsp/src/grammar/alpha035/semantic_tokens.rs b/lib/src/grammar/alpha035/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha035/semantic_tokens.rs rename to lib/src/grammar/alpha035/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha035/statements/block.rs b/lib/src/grammar/alpha035/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/block.rs rename to lib/src/grammar/alpha035/statements/block.rs diff --git a/lsp/src/grammar/alpha035/statements/comment.rs b/lib/src/grammar/alpha035/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/comment.rs rename to lib/src/grammar/alpha035/statements/comment.rs diff --git a/lsp/src/grammar/alpha035/statements/const_init.rs b/lib/src/grammar/alpha035/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/const_init.rs rename to lib/src/grammar/alpha035/statements/const_init.rs diff --git a/lsp/src/grammar/alpha035/statements/failed.rs b/lib/src/grammar/alpha035/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/failed.rs rename to lib/src/grammar/alpha035/statements/failed.rs diff --git a/lsp/src/grammar/alpha035/statements/if_cond.rs b/lib/src/grammar/alpha035/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/if_cond.rs rename to lib/src/grammar/alpha035/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha035/statements/keywords.rs b/lib/src/grammar/alpha035/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/keywords.rs rename to lib/src/grammar/alpha035/statements/keywords.rs diff --git a/lsp/src/grammar/alpha035/statements/loops.rs b/lib/src/grammar/alpha035/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/loops.rs rename to lib/src/grammar/alpha035/statements/loops.rs diff --git a/lsp/src/grammar/alpha035/statements/mod.rs b/lib/src/grammar/alpha035/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/mod.rs rename to lib/src/grammar/alpha035/statements/mod.rs diff --git a/lsp/src/grammar/alpha035/statements/modifiers.rs b/lib/src/grammar/alpha035/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/modifiers.rs rename to lib/src/grammar/alpha035/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha035/statements/move_files.rs b/lib/src/grammar/alpha035/statements/move_files.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/move_files.rs rename to lib/src/grammar/alpha035/statements/move_files.rs diff --git a/lsp/src/grammar/alpha035/statements/shebang.rs b/lib/src/grammar/alpha035/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/shebang.rs rename to lib/src/grammar/alpha035/statements/shebang.rs diff --git a/lsp/src/grammar/alpha035/statements/shorthands.rs b/lib/src/grammar/alpha035/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/shorthands.rs rename to lib/src/grammar/alpha035/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha035/statements/var_init.rs b/lib/src/grammar/alpha035/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/var_init.rs rename to lib/src/grammar/alpha035/statements/var_init.rs diff --git a/lsp/src/grammar/alpha035/statements/var_set.rs b/lib/src/grammar/alpha035/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/var_set.rs rename to lib/src/grammar/alpha035/statements/var_set.rs diff --git a/lsp/src/grammar/alpha040/expressions/and.rs b/lib/src/grammar/alpha040/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/and.rs rename to lib/src/grammar/alpha040/expressions/and.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/array.rs b/lib/src/grammar/alpha040/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/array.rs rename to lib/src/grammar/alpha040/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/bool.rs b/lib/src/grammar/alpha040/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/bool.rs rename to lib/src/grammar/alpha040/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/call.rs b/lib/src/grammar/alpha040/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/call.rs rename to lib/src/grammar/alpha040/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/command.rs b/lib/src/grammar/alpha040/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/command.rs rename to lib/src/grammar/alpha040/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/exit.rs b/lib/src/grammar/alpha040/expressions/atom/exit.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/exit.rs rename to lib/src/grammar/alpha040/expressions/atom/exit.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/mod.rs b/lib/src/grammar/alpha040/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/mod.rs rename to lib/src/grammar/alpha040/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/null.rs b/lib/src/grammar/alpha040/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/null.rs rename to lib/src/grammar/alpha040/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/number.rs b/lib/src/grammar/alpha040/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/number.rs rename to lib/src/grammar/alpha040/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/parentheses.rs b/lib/src/grammar/alpha040/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha040/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/status.rs b/lib/src/grammar/alpha040/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/status.rs rename to lib/src/grammar/alpha040/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/text.rs b/lib/src/grammar/alpha040/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/text.rs rename to lib/src/grammar/alpha040/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/var.rs b/lib/src/grammar/alpha040/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/var.rs rename to lib/src/grammar/alpha040/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha040/expressions/cast.rs b/lib/src/grammar/alpha040/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/cast.rs rename to lib/src/grammar/alpha040/expressions/cast.rs diff --git a/lsp/src/grammar/alpha040/expressions/comparison.rs b/lib/src/grammar/alpha040/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/comparison.rs rename to lib/src/grammar/alpha040/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha040/expressions/is.rs b/lib/src/grammar/alpha040/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/is.rs rename to lib/src/grammar/alpha040/expressions/is.rs diff --git a/lsp/src/grammar/alpha040/expressions/mod.rs b/lib/src/grammar/alpha040/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/mod.rs rename to lib/src/grammar/alpha040/expressions/mod.rs diff --git a/lsp/src/grammar/alpha040/expressions/or.rs b/lib/src/grammar/alpha040/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/or.rs rename to lib/src/grammar/alpha040/expressions/or.rs diff --git a/lsp/src/grammar/alpha040/expressions/product.rs b/lib/src/grammar/alpha040/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/product.rs rename to lib/src/grammar/alpha040/expressions/product.rs diff --git a/lsp/src/grammar/alpha040/expressions/range.rs b/lib/src/grammar/alpha040/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/range.rs rename to lib/src/grammar/alpha040/expressions/range.rs diff --git a/lsp/src/grammar/alpha040/expressions/sum.rs b/lib/src/grammar/alpha040/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/sum.rs rename to lib/src/grammar/alpha040/expressions/sum.rs diff --git a/lsp/src/grammar/alpha040/expressions/ternary.rs b/lib/src/grammar/alpha040/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/ternary.rs rename to lib/src/grammar/alpha040/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha040/expressions/unary.rs b/lib/src/grammar/alpha040/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/unary.rs rename to lib/src/grammar/alpha040/expressions/unary.rs diff --git a/lsp/src/grammar/alpha040/global.rs b/lib/src/grammar/alpha040/global.rs similarity index 100% rename from lsp/src/grammar/alpha040/global.rs rename to lib/src/grammar/alpha040/global.rs diff --git a/lsp/src/grammar/alpha040/lexer.rs b/lib/src/grammar/alpha040/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha040/lexer.rs rename to lib/src/grammar/alpha040/lexer.rs diff --git a/lsp/src/grammar/alpha040/mod.rs b/lib/src/grammar/alpha040/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/mod.rs rename to lib/src/grammar/alpha040/mod.rs diff --git a/lsp/src/grammar/alpha040/parser.rs b/lib/src/grammar/alpha040/parser.rs similarity index 100% rename from lsp/src/grammar/alpha040/parser.rs rename to lib/src/grammar/alpha040/parser.rs diff --git a/lsp/src/grammar/alpha040/semantic_tokens.rs b/lib/src/grammar/alpha040/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha040/semantic_tokens.rs rename to lib/src/grammar/alpha040/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha040/statements/block.rs b/lib/src/grammar/alpha040/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/block.rs rename to lib/src/grammar/alpha040/statements/block.rs diff --git a/lsp/src/grammar/alpha040/statements/comment.rs b/lib/src/grammar/alpha040/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/comment.rs rename to lib/src/grammar/alpha040/statements/comment.rs diff --git a/lsp/src/grammar/alpha040/statements/const_init.rs b/lib/src/grammar/alpha040/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/const_init.rs rename to lib/src/grammar/alpha040/statements/const_init.rs diff --git a/lsp/src/grammar/alpha040/statements/failed.rs b/lib/src/grammar/alpha040/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/failed.rs rename to lib/src/grammar/alpha040/statements/failed.rs diff --git a/lsp/src/grammar/alpha040/statements/if_cond.rs b/lib/src/grammar/alpha040/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/if_cond.rs rename to lib/src/grammar/alpha040/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha040/statements/keywords.rs b/lib/src/grammar/alpha040/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/keywords.rs rename to lib/src/grammar/alpha040/statements/keywords.rs diff --git a/lsp/src/grammar/alpha040/statements/loops.rs b/lib/src/grammar/alpha040/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/loops.rs rename to lib/src/grammar/alpha040/statements/loops.rs diff --git a/lsp/src/grammar/alpha040/statements/mod.rs b/lib/src/grammar/alpha040/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/mod.rs rename to lib/src/grammar/alpha040/statements/mod.rs diff --git a/lsp/src/grammar/alpha040/statements/modifiers.rs b/lib/src/grammar/alpha040/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/modifiers.rs rename to lib/src/grammar/alpha040/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha040/statements/move_files.rs b/lib/src/grammar/alpha040/statements/move_files.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/move_files.rs rename to lib/src/grammar/alpha040/statements/move_files.rs diff --git a/lsp/src/grammar/alpha040/statements/shebang.rs b/lib/src/grammar/alpha040/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/shebang.rs rename to lib/src/grammar/alpha040/statements/shebang.rs diff --git a/lsp/src/grammar/alpha040/statements/shorthands.rs b/lib/src/grammar/alpha040/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/shorthands.rs rename to lib/src/grammar/alpha040/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha040/statements/var_init.rs b/lib/src/grammar/alpha040/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/var_init.rs rename to lib/src/grammar/alpha040/statements/var_init.rs diff --git a/lsp/src/grammar/alpha040/statements/var_set.rs b/lib/src/grammar/alpha040/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/var_set.rs rename to lib/src/grammar/alpha040/statements/var_set.rs diff --git a/lsp/src/grammar/mod.rs b/lib/src/grammar/mod.rs similarity index 100% rename from lsp/src/grammar/mod.rs rename to lib/src/grammar/mod.rs diff --git a/lsp/src/lib.rs b/lib/src/lib.rs similarity index 100% rename from lsp/src/lib.rs rename to lib/src/lib.rs diff --git a/lsp/src/paths.rs b/lib/src/paths.rs similarity index 100% rename from lsp/src/paths.rs rename to lib/src/paths.rs diff --git a/lsp/src/stdlib.rs b/lib/src/stdlib.rs similarity index 100% rename from lsp/src/stdlib.rs rename to lib/src/stdlib.rs diff --git a/lsp/src/utils.rs b/lib/src/utils.rs similarity index 100% rename from lsp/src/utils.rs rename to lib/src/utils.rs diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml index 8f55eac..5bdcccb 100644 --- a/lsp/Cargo.toml +++ b/lsp/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" repository = "https://github.com/KrosFire/amber-lsp" [dependencies] +lib = { workspace = true } + tokio = { workspace = true, features = ["full"] } tower-lsp-server = { workspace = true } phf = { workspace = true, features = ["macros"] } diff --git a/lsp/src/main.rs b/lsp/src/main.rs index 25478bf..aa33f42 100644 --- a/lsp/src/main.rs +++ b/lsp/src/main.rs @@ -3,8 +3,8 @@ use std::{ process::{Command, Stdio}, }; -use amber_lsp::backend::{AmberVersion, Backend}; use clap::{builder::PossibleValue, Parser, ValueEnum}; +use lib::backend::{AmberVersion, Backend}; use tower_lsp_server::{LspService, Server}; use tracing::subscriber; use tracing_subscriber::fmt::format::FmtSpan; From 765a5259feb2a077b4e60a49c6638a66257401f5 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 29 Oct 2025 16:29:19 +0000 Subject: [PATCH 04/37] Moved amber version detection into library --- Cargo.toml | 2 +- formatter/Cargo.toml | 31 +++++++++++++++++ formatter/src/main.rs | 51 +++++++++++++++++++++++++++ lib/src/amber_version.rs | 75 ++++++++++++++++++++++++++++++++++++++++ lib/src/analysis/mod.rs | 3 +- lib/src/backend.rs | 8 +---- lib/src/lib.rs | 3 ++ lib/src/stdlib.rs | 2 +- lsp/src/main.rs | 72 ++------------------------------------ 9 files changed, 168 insertions(+), 79 deletions(-) create mode 100644 formatter/Cargo.toml create mode 100644 formatter/src/main.rs create mode 100644 lib/src/amber_version.rs diff --git a/Cargo.toml b/Cargo.toml index 6e91637..00a4153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["lsp", "lib"] +members = ["lsp", "lib", "formatter"] [workspace.dependencies] lib = { path = "./lib" } diff --git a/formatter/Cargo.toml b/formatter/Cargo.toml new file mode 100644 index 0000000..2720426 --- /dev/null +++ b/formatter/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "amber-fmt" +version = "0.1.10" +edition = "2024" + +[dependencies] +lib = { workspace = true } +clap = { workspace = true, features = ["derive"] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +# tokio = { workspace = true, features = ["full"] } +# tower-lsp-server = { workspace = true } +# phf = { workspace = true, features = ["macros"] } +# dashmap = { workspace = true } +# ropey = { workspace = true } +# chumsky = { workspace = true, features = ["default", "label"] } +# heraclitus-compiler = { workspace = true } +# serde_json = { workspace = true } +# rangemap = { workspace = true } +# indexmap = { workspace = true } +# include_dir = { workspace = true } +# rustc-hash = { workspace = true } + +# [dev-dependencies] +# insta = { workspace = true, features = ["yaml"] } + +# [build-dependencies] +# fs_extra = "1.3.0" diff --git a/formatter/src/main.rs b/formatter/src/main.rs new file mode 100644 index 0000000..2c5ff04 --- /dev/null +++ b/formatter/src/main.rs @@ -0,0 +1,51 @@ +use clap::Parser; +use lib::{CliAmberVersion, detect_amber_version}; +use std::env::temp_dir; +use tracing::subscriber; +use tracing_subscriber::fmt::format::FmtSpan; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Version of the Amber language to use. + #[arg(value_enum, long, short, default_value = "auto")] + amber_version: CliAmberVersion, +} + +fn main() { + let cache_dir = temp_dir().join("amber-lsp"); + let file_appender = tracing_appender::rolling::hourly(cache_dir, "amber-lsp.log"); + let (non_blocking_writer, _guard) = tracing_appender::non_blocking(file_appender); + + // construct a subscriber that prints formatted traces to stdout + let subscriber = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .compact() + // Display source code file paths + .with_file(true) + // Display source code line numbers + .with_line_number(true) + // Don't display the thread ID an event was recorded on + .with_thread_ids(false) + // Don't display the event's target (module path) + .with_target(false) + // Log when entering and exiting spans + .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) + // log to a file + .with_writer(non_blocking_writer) + // Disabled ANSI color codes for better compatibility with some terminals + .with_ansi(false) + // Build the subscriber + .finish(); + + // use that subscriber to process traces emitted after this point + subscriber::set_global_default(subscriber).expect("Could not set global default subscriber"); + + let args = Args::parse(); + + let amber_version = if args.amber_version == CliAmberVersion::Auto { + detect_amber_version() + } else { + args.amber_version.into() + }; +} diff --git a/lib/src/amber_version.rs b/lib/src/amber_version.rs new file mode 100644 index 0000000..31b12c1 --- /dev/null +++ b/lib/src/amber_version.rs @@ -0,0 +1,75 @@ +use clap::{builder::PossibleValue, ValueEnum}; +use std::process::{Command, Stdio}; + +/// The version of amber programming language that the amber source is compiliable in. +#[derive(Clone, Debug, PartialEq)] +pub enum AmberVersion { + Alpha034, + Alpha035, + Alpha040, +} + +/// Used for clap parsing. +/// Allows for the user to manually override the detected amber version. +#[derive(Clone, Debug, PartialEq)] +pub enum CliAmberVersion { + Auto, + Alpha034, + Alpha035, + Alpha040, +} + +impl From for AmberVersion { + fn from(val: CliAmberVersion) -> Self { + match val { + CliAmberVersion::Auto => AmberVersion::Alpha034, + CliAmberVersion::Alpha034 => AmberVersion::Alpha034, + CliAmberVersion::Alpha035 => AmberVersion::Alpha035, + CliAmberVersion::Alpha040 => AmberVersion::Alpha040, + } + } +} + +impl ValueEnum for CliAmberVersion { + fn value_variants<'a>() -> &'a [CliAmberVersion] { + &[ + CliAmberVersion::Auto, + CliAmberVersion::Alpha034, + CliAmberVersion::Alpha035, + CliAmberVersion::Alpha040, + ] + } + + fn to_possible_value(&self) -> Option { + match self { + CliAmberVersion::Auto => Some(PossibleValue::new("auto")), + CliAmberVersion::Alpha034 => Some(PossibleValue::new("0.3.4-alpha")), + CliAmberVersion::Alpha035 => Some(PossibleValue::new("0.3.5-alpha")), + CliAmberVersion::Alpha040 => Some(PossibleValue::new("0.4.0-alpha")), + } + } +} + +/// Detects the amber version by invoking the amber command. +#[tracing::instrument(skip_all)] +pub fn detect_amber_version() -> AmberVersion { + let output = Command::new("amber") + .arg("-V") + .stdout(Stdio::piped()) + .output(); + + let version = match output { + Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), + Err(e) => { + tracing::error!("Failed to execute amber command: {}", e); + return AmberVersion::Alpha040; // Default to the latest version if detection fails + } + }; + + match version.split_whitespace().last() { + Some("0.3.4-alpha") => AmberVersion::Alpha034, + Some("0.3.5-alpha") => AmberVersion::Alpha035, + Some("0.4.0-alpha") => AmberVersion::Alpha040, + _ => AmberVersion::Alpha040, + } +} diff --git a/lib/src/analysis/mod.rs b/lib/src/analysis/mod.rs index b3a64dd..8169dab 100644 --- a/lib/src/analysis/mod.rs +++ b/lib/src/analysis/mod.rs @@ -4,7 +4,8 @@ use tower_lsp_server::{lsp_types::Uri, UriExt}; use types::{DataType, GenericsMap}; use crate::{ - backend::{AmberVersion, Backend}, + amber_version::AmberVersion, + backend::Backend, files::{FileVersion, Files}, grammar::{CommandModifier, CompilerFlag, Span, Spanned}, paths::FileId, diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 0c4ceb6..ef7c589 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -10,6 +10,7 @@ use tower_lsp_server::lsp_types::*; use tower_lsp_server::UriExt; use tower_lsp_server::{Client, LanguageServer}; +use crate::amber_version::AmberVersion; use crate::analysis::{ self, get_symbol_definition_info, Context, FunctionSymbol, SymbolInfo, SymbolTable, SymbolType, VariableSymbol, @@ -22,13 +23,6 @@ use crate::stdlib::{find_in_stdlib, save_resources}; type PinnedFuture<'a, T> = Pin> + Send + 'a>>; -#[derive(Clone, Debug, PartialEq)] -pub enum AmberVersion { - Alpha034, - Alpha035, - Alpha040, -} - #[derive(Debug)] pub struct Backend { pub client: Client, diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2d5e0ef..854af4c 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,3 +1,4 @@ +pub mod amber_version; pub mod analysis; pub mod backend; pub mod files; @@ -6,3 +7,5 @@ pub mod grammar; pub mod paths; pub mod stdlib; pub mod utils; + +pub use amber_version::{detect_amber_version, AmberVersion, CliAmberVersion}; diff --git a/lib/src/stdlib.rs b/lib/src/stdlib.rs index 2b91f1d..4fb5cda 100644 --- a/lib/src/stdlib.rs +++ b/lib/src/stdlib.rs @@ -10,7 +10,7 @@ use include_dir::{include_dir, Dir, DirEntry}; use tower_lsp_server::{lsp_types::Uri, UriExt}; use tracing::warn; -use crate::backend::{AmberVersion, Backend}; +use crate::{amber_version::AmberVersion, backend::Backend}; pub const STDLIB: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources/"); diff --git a/lsp/src/main.rs b/lsp/src/main.rs index aa33f42..6eb7847 100644 --- a/lsp/src/main.rs +++ b/lsp/src/main.rs @@ -1,53 +1,10 @@ -use std::{ - env::temp_dir, - process::{Command, Stdio}, -}; - -use clap::{builder::PossibleValue, Parser, ValueEnum}; -use lib::backend::{AmberVersion, Backend}; +use clap::Parser; +use lib::{backend::Backend, detect_amber_version, CliAmberVersion}; +use std::env::temp_dir; use tower_lsp_server::{LspService, Server}; use tracing::subscriber; use tracing_subscriber::fmt::format::FmtSpan; -#[derive(Clone, Debug, PartialEq)] -enum CliAmberVersion { - Auto, - Alpha034, - Alpha035, - Alpha040, -} - -impl From for AmberVersion { - fn from(val: CliAmberVersion) -> Self { - match val { - CliAmberVersion::Auto => AmberVersion::Alpha034, - CliAmberVersion::Alpha034 => AmberVersion::Alpha034, - CliAmberVersion::Alpha035 => AmberVersion::Alpha035, - CliAmberVersion::Alpha040 => AmberVersion::Alpha040, - } - } -} - -impl ValueEnum for CliAmberVersion { - fn value_variants<'a>() -> &'a [CliAmberVersion] { - &[ - CliAmberVersion::Auto, - CliAmberVersion::Alpha034, - CliAmberVersion::Alpha035, - CliAmberVersion::Alpha040, - ] - } - - fn to_possible_value(&self) -> Option { - match self { - CliAmberVersion::Auto => Some(PossibleValue::new("auto")), - CliAmberVersion::Alpha034 => Some(PossibleValue::new("0.3.4-alpha")), - CliAmberVersion::Alpha035 => Some(PossibleValue::new("0.3.5-alpha")), - CliAmberVersion::Alpha040 => Some(PossibleValue::new("0.4.0-alpha")), - } - } -} - #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { @@ -100,26 +57,3 @@ async fn main() { let (service, socket) = LspService::new(|client| Backend::new(client, amber_version, None)); Server::new(stdin, stdout, socket).serve(service).await; } - -#[tracing::instrument(skip_all)] -fn detect_amber_version() -> AmberVersion { - let output = Command::new("amber") - .arg("-V") - .stdout(Stdio::piped()) - .output(); - - let version = match output { - Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), - Err(e) => { - tracing::error!("Failed to execute amber command: {}", e); - return AmberVersion::Alpha040; // Default to the latest version if detection fails - } - }; - - match version.split_whitespace().last() { - Some("0.3.4-alpha") => AmberVersion::Alpha034, - Some("0.3.5-alpha") => AmberVersion::Alpha035, - Some("0.4.0-alpha") => AmberVersion::Alpha040, - _ => AmberVersion::Alpha040, - } -} From 8678455b7d40acc536123b7b532e728fe225402e Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 16:04:00 +0000 Subject: [PATCH 05/37] Setup nix This is for my development and not a fully planned part of the change --- flake.lock | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 39 ++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..8d6c6a0 --- /dev/null +++ b/flake.lock @@ -0,0 +1,96 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1744536153, + "narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "rust-overlay": "rust-overlay" + } + }, + "rust-overlay": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1759199574, + "narHash": "sha256-w24RYly3VSVKp98rVfCI1nFYfQ0VoWmShtKPCbXgK6A=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "381776b12d0d125edd7c1930c2041a1471e586c0", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..39d3adb --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +{ + description = "Environment for amber-lsp."; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + rust-overlay.url = "github:oxalica/rust-overlay"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { + nixpkgs, + rust-overlay, + flake-utils, + ... + }: + flake-utils.lib.eachDefaultSystem ( + system: + let + overlays = [ (import rust-overlay) ]; + pkgs = import nixpkgs { inherit system overlays; }; + + rust-build = pkgs.rust-bin.stable.latest.default.override { + extensions = [ "rust-src" ]; + }; + in + { + devShells.default = + with pkgs; + mkShell { + buildInputs = [ + rust-build + bacon + amber-lang + ]; + }; + } + ); +} From 605994f4e226c473d23c9562a8edf5bf9e16693b Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 17:24:39 +0000 Subject: [PATCH 06/37] Changed to workspace --- Cargo.toml | 36 +++++++++---------- lsp/Cargo.toml | 30 ++++++++++++++++ .../resources}/alpha034/std/main.ab | 0 .../resources}/alpha035/std/array.ab | 0 .../resources}/alpha035/std/date.ab | 0 .../resources}/alpha035/std/env.ab | 0 .../resources}/alpha035/std/fs.ab | 0 .../resources}/alpha035/std/http.ab | 0 .../resources}/alpha035/std/math.ab | 0 .../resources}/alpha035/std/text.ab | 0 .../resources}/alpha040/builtin.ab | 0 .../resources}/alpha040/std/array.ab | 0 .../resources}/alpha040/std/date.ab | 0 .../resources}/alpha040/std/env.ab | 0 .../resources}/alpha040/std/fs.ab | 0 .../resources}/alpha040/std/http.ab | 0 .../resources}/alpha040/std/math.ab | 0 .../resources}/alpha040/std/text.ab | 0 {src => lsp/src}/analysis/alpha034/exp.rs | 0 {src => lsp/src}/analysis/alpha034/global.rs | 0 {src => lsp/src}/analysis/alpha034/mod.rs | 0 {src => lsp/src}/analysis/alpha034/stmnts.rs | 0 {src => lsp/src}/analysis/alpha035/exp.rs | 0 {src => lsp/src}/analysis/alpha035/global.rs | 0 {src => lsp/src}/analysis/alpha035/mod.rs | 0 {src => lsp/src}/analysis/alpha035/stmnts.rs | 0 {src => lsp/src}/analysis/alpha040/exp.rs | 0 {src => lsp/src}/analysis/alpha040/global.rs | 0 {src => lsp/src}/analysis/alpha040/mod.rs | 0 {src => lsp/src}/analysis/alpha040/stmnts.rs | 0 {src => lsp/src}/analysis/mod.rs | 0 {src => lsp/src}/analysis/types.rs | 0 {src => lsp/src}/backend.rs | 0 {src => lsp/src}/files.rs | 0 {src => lsp/src}/fs.rs | 0 .../src}/grammar/alpha034/expressions/and.rs | 0 .../alpha034/expressions/atom/array.rs | 0 .../grammar/alpha034/expressions/atom/bool.rs | 0 .../grammar/alpha034/expressions/atom/call.rs | 0 .../alpha034/expressions/atom/command.rs | 0 .../grammar/alpha034/expressions/atom/mod.rs | 0 .../grammar/alpha034/expressions/atom/null.rs | 0 .../alpha034/expressions/atom/number.rs | 0 .../alpha034/expressions/atom/parentheses.rs | 0 .../alpha034/expressions/atom/status.rs | 0 .../grammar/alpha034/expressions/atom/text.rs | 0 .../grammar/alpha034/expressions/atom/var.rs | 0 .../src}/grammar/alpha034/expressions/cast.rs | 0 .../alpha034/expressions/comparison.rs | 0 .../src}/grammar/alpha034/expressions/is.rs | 0 .../src}/grammar/alpha034/expressions/mod.rs | 0 .../src}/grammar/alpha034/expressions/or.rs | 0 .../grammar/alpha034/expressions/product.rs | 0 .../grammar/alpha034/expressions/range.rs | 0 .../src}/grammar/alpha034/expressions/sum.rs | 0 .../grammar/alpha034/expressions/ternary.rs | 0 .../grammar/alpha034/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha034/global.rs | 0 {src => lsp/src}/grammar/alpha034/lexer.rs | 0 {src => lsp/src}/grammar/alpha034/mod.rs | 0 {src => lsp/src}/grammar/alpha034/parser.rs | 0 .../src}/grammar/alpha034/semantic_tokens.rs | 0 .../src}/grammar/alpha034/statements/block.rs | 0 .../grammar/alpha034/statements/comment.rs | 0 .../grammar/alpha034/statements/const_init.rs | 0 .../grammar/alpha034/statements/failed.rs | 0 .../grammar/alpha034/statements/if_cond.rs | 0 .../grammar/alpha034/statements/keywords.rs | 0 .../src}/grammar/alpha034/statements/loops.rs | 0 .../src}/grammar/alpha034/statements/mod.rs | 0 .../grammar/alpha034/statements/modifiers.rs | 0 .../grammar/alpha034/statements/shebang.rs | 0 .../grammar/alpha034/statements/shorthands.rs | 0 .../grammar/alpha034/statements/var_init.rs | 0 .../grammar/alpha034/statements/var_set.rs | 0 .../src}/grammar/alpha035/expressions/and.rs | 0 .../alpha035/expressions/atom/array.rs | 0 .../grammar/alpha035/expressions/atom/bool.rs | 0 .../grammar/alpha035/expressions/atom/call.rs | 0 .../alpha035/expressions/atom/command.rs | 0 .../grammar/alpha035/expressions/atom/mod.rs | 0 .../grammar/alpha035/expressions/atom/null.rs | 0 .../alpha035/expressions/atom/number.rs | 0 .../alpha035/expressions/atom/parentheses.rs | 0 .../alpha035/expressions/atom/status.rs | 0 .../grammar/alpha035/expressions/atom/text.rs | 0 .../grammar/alpha035/expressions/atom/var.rs | 0 .../src}/grammar/alpha035/expressions/cast.rs | 0 .../alpha035/expressions/comparison.rs | 0 .../src}/grammar/alpha035/expressions/is.rs | 0 .../src}/grammar/alpha035/expressions/mod.rs | 0 .../src}/grammar/alpha035/expressions/or.rs | 0 .../grammar/alpha035/expressions/product.rs | 0 .../grammar/alpha035/expressions/range.rs | 0 .../src}/grammar/alpha035/expressions/sum.rs | 0 .../grammar/alpha035/expressions/ternary.rs | 0 .../grammar/alpha035/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha035/global.rs | 0 {src => lsp/src}/grammar/alpha035/lexer.rs | 0 {src => lsp/src}/grammar/alpha035/mod.rs | 0 {src => lsp/src}/grammar/alpha035/parser.rs | 0 .../src}/grammar/alpha035/semantic_tokens.rs | 0 .../src}/grammar/alpha035/statements/block.rs | 0 .../grammar/alpha035/statements/comment.rs | 0 .../grammar/alpha035/statements/const_init.rs | 0 .../grammar/alpha035/statements/failed.rs | 0 .../grammar/alpha035/statements/if_cond.rs | 0 .../grammar/alpha035/statements/keywords.rs | 0 .../src}/grammar/alpha035/statements/loops.rs | 0 .../src}/grammar/alpha035/statements/mod.rs | 0 .../grammar/alpha035/statements/modifiers.rs | 0 .../grammar/alpha035/statements/move_files.rs | 0 .../grammar/alpha035/statements/shebang.rs | 0 .../grammar/alpha035/statements/shorthands.rs | 0 .../grammar/alpha035/statements/var_init.rs | 0 .../grammar/alpha035/statements/var_set.rs | 0 .../src}/grammar/alpha040/expressions/and.rs | 0 .../alpha040/expressions/atom/array.rs | 0 .../grammar/alpha040/expressions/atom/bool.rs | 0 .../grammar/alpha040/expressions/atom/call.rs | 0 .../alpha040/expressions/atom/command.rs | 0 .../grammar/alpha040/expressions/atom/exit.rs | 0 .../grammar/alpha040/expressions/atom/mod.rs | 0 .../grammar/alpha040/expressions/atom/null.rs | 0 .../alpha040/expressions/atom/number.rs | 0 .../alpha040/expressions/atom/parentheses.rs | 0 .../alpha040/expressions/atom/status.rs | 0 .../grammar/alpha040/expressions/atom/text.rs | 0 .../grammar/alpha040/expressions/atom/var.rs | 0 .../src}/grammar/alpha040/expressions/cast.rs | 0 .../alpha040/expressions/comparison.rs | 0 .../src}/grammar/alpha040/expressions/is.rs | 0 .../src}/grammar/alpha040/expressions/mod.rs | 0 .../src}/grammar/alpha040/expressions/or.rs | 0 .../grammar/alpha040/expressions/product.rs | 0 .../grammar/alpha040/expressions/range.rs | 0 .../src}/grammar/alpha040/expressions/sum.rs | 0 .../grammar/alpha040/expressions/ternary.rs | 0 .../grammar/alpha040/expressions/unary.rs | 0 {src => lsp/src}/grammar/alpha040/global.rs | 0 {src => lsp/src}/grammar/alpha040/lexer.rs | 0 {src => lsp/src}/grammar/alpha040/mod.rs | 0 {src => lsp/src}/grammar/alpha040/parser.rs | 0 .../src}/grammar/alpha040/semantic_tokens.rs | 0 .../src}/grammar/alpha040/statements/block.rs | 0 .../grammar/alpha040/statements/comment.rs | 0 .../grammar/alpha040/statements/const_init.rs | 0 .../grammar/alpha040/statements/failed.rs | 0 .../grammar/alpha040/statements/if_cond.rs | 0 .../grammar/alpha040/statements/keywords.rs | 0 .../src}/grammar/alpha040/statements/loops.rs | 0 .../src}/grammar/alpha040/statements/mod.rs | 0 .../grammar/alpha040/statements/modifiers.rs | 0 .../grammar/alpha040/statements/move_files.rs | 0 .../grammar/alpha040/statements/shebang.rs | 0 .../grammar/alpha040/statements/shorthands.rs | 0 .../grammar/alpha040/statements/var_init.rs | 0 .../grammar/alpha040/statements/var_set.rs | 0 {src => lsp/src}/grammar/mod.rs | 0 {src => lsp/src}/lib.rs | 0 {src => lsp/src}/main.rs | 0 {src => lsp/src}/paths.rs | 0 {src => lsp/src}/stdlib.rs | 0 {src => lsp/src}/utils.rs | 0 164 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 lsp/Cargo.toml rename {resources => lsp/resources}/alpha034/std/main.ab (100%) rename {resources => lsp/resources}/alpha035/std/array.ab (100%) rename {resources => lsp/resources}/alpha035/std/date.ab (100%) rename {resources => lsp/resources}/alpha035/std/env.ab (100%) rename {resources => lsp/resources}/alpha035/std/fs.ab (100%) rename {resources => lsp/resources}/alpha035/std/http.ab (100%) rename {resources => lsp/resources}/alpha035/std/math.ab (100%) rename {resources => lsp/resources}/alpha035/std/text.ab (100%) rename {resources => lsp/resources}/alpha040/builtin.ab (100%) rename {resources => lsp/resources}/alpha040/std/array.ab (100%) rename {resources => lsp/resources}/alpha040/std/date.ab (100%) rename {resources => lsp/resources}/alpha040/std/env.ab (100%) rename {resources => lsp/resources}/alpha040/std/fs.ab (100%) rename {resources => lsp/resources}/alpha040/std/http.ab (100%) rename {resources => lsp/resources}/alpha040/std/math.ab (100%) rename {resources => lsp/resources}/alpha040/std/text.ab (100%) rename {src => lsp/src}/analysis/alpha034/exp.rs (100%) rename {src => lsp/src}/analysis/alpha034/global.rs (100%) rename {src => lsp/src}/analysis/alpha034/mod.rs (100%) rename {src => lsp/src}/analysis/alpha034/stmnts.rs (100%) rename {src => lsp/src}/analysis/alpha035/exp.rs (100%) rename {src => lsp/src}/analysis/alpha035/global.rs (100%) rename {src => lsp/src}/analysis/alpha035/mod.rs (100%) rename {src => lsp/src}/analysis/alpha035/stmnts.rs (100%) rename {src => lsp/src}/analysis/alpha040/exp.rs (100%) rename {src => lsp/src}/analysis/alpha040/global.rs (100%) rename {src => lsp/src}/analysis/alpha040/mod.rs (100%) rename {src => lsp/src}/analysis/alpha040/stmnts.rs (100%) rename {src => lsp/src}/analysis/mod.rs (100%) rename {src => lsp/src}/analysis/types.rs (100%) rename {src => lsp/src}/backend.rs (100%) rename {src => lsp/src}/files.rs (100%) rename {src => lsp/src}/fs.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha034/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha034/global.rs (100%) rename {src => lsp/src}/grammar/alpha034/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha034/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/parser.rs (100%) rename {src => lsp/src}/grammar/alpha034/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha034/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha035/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha035/global.rs (100%) rename {src => lsp/src}/grammar/alpha035/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha035/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/parser.rs (100%) rename {src => lsp/src}/grammar/alpha035/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/move_files.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha035/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/and.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/array.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/bool.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/call.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/command.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/exit.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/null.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/number.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/parentheses.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/status.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/text.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/atom/var.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/cast.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/comparison.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/is.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/or.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/product.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/range.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/sum.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/ternary.rs (100%) rename {src => lsp/src}/grammar/alpha040/expressions/unary.rs (100%) rename {src => lsp/src}/grammar/alpha040/global.rs (100%) rename {src => lsp/src}/grammar/alpha040/lexer.rs (100%) rename {src => lsp/src}/grammar/alpha040/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/parser.rs (100%) rename {src => lsp/src}/grammar/alpha040/semantic_tokens.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/block.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/comment.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/const_init.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/failed.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/if_cond.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/keywords.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/loops.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/mod.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/modifiers.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/move_files.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/shebang.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/shorthands.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/var_init.rs (100%) rename {src => lsp/src}/grammar/alpha040/statements/var_set.rs (100%) rename {src => lsp/src}/grammar/mod.rs (100%) rename {src => lsp/src}/lib.rs (100%) rename {src => lsp/src}/main.rs (100%) rename {src => lsp/src}/paths.rs (100%) rename {src => lsp/src}/stdlib.rs (100%) rename {src => lsp/src}/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 06c8ab1..7b64cc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,19 @@ -[package] -name = "amber-lsp" -version = "0.1.10" -edition = "2021" -repository = "https://github.com/KrosFire/amber-lsp" +[workspace] +resolver = "2" +members = ["lsp"] -[dependencies] -tokio = { version = "1.39.1", features = ["full"] } +[workspace.dependencies] +tokio = "1.39.1" tower-lsp-server = "0.22.0" -phf = { version = "0.11", features = ["macros"] } +phf = "0.11" dashmap = "6.0.1" ropey = "1.6.1" -chumsky = { git = "https://github.com/KrosFire/chumsky", rev = "406ea80", features = [ - "default", - "label", -] } +chumsky = { git = "https://github.com/KrosFire/chumsky", rev = "406ea80" } heraclitus-compiler = "1.8.2" serde_json = "1.0.128" rangemap = "1.5.1" indexmap = "2.6.0" -clap = { version = "4.5.21", features = ["derive"] } +clap = "4.5.21" include_dir = "0.7.4" tracing = "0.1" tracing-subscriber = "0.3" @@ -26,11 +21,8 @@ rustc-hash = "2.1.1" tracing-appender = "0.2.3" thiserror = "2.0.12" -[dev-dependencies] -insta = { version = "1.39.0", features = ["yaml"] } - -[build-dependencies] -fs_extra = "1.3.0" +# Dev +insta = "1.39.0" # The profile that 'cargo dist' will build with [profile.dist] @@ -46,7 +38,13 @@ ci = "github" # The installers to generate for each app installers = [] # Target platforms to build apps for (Rust target-triple syntax) -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl", "x86_64-pc-windows-msvc"] +targets = [ + "aarch64-apple-darwin", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "x86_64-unknown-linux-musl", + "x86_64-pc-windows-msvc", +] # Publish jobs to run in CI pr-run-mode = "plan" # Skip checking whether the specified configuration files are up to date diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml new file mode 100644 index 0000000..8f55eac --- /dev/null +++ b/lsp/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "amber-lsp" +version = "0.1.10" +edition = "2021" +repository = "https://github.com/KrosFire/amber-lsp" + +[dependencies] +tokio = { workspace = true, features = ["full"] } +tower-lsp-server = { workspace = true } +phf = { workspace = true, features = ["macros"] } +dashmap = { workspace = true } +ropey = { workspace = true } +chumsky = { workspace = true, features = ["default", "label"] } +heraclitus-compiler = { workspace = true } +serde_json = { workspace = true } +rangemap = { workspace = true } +indexmap = { workspace = true } +clap = { workspace = true, features = ["derive"] } +include_dir = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +rustc-hash = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +insta = { workspace = true, features = ["yaml"] } + +[build-dependencies] +fs_extra = "1.3.0" diff --git a/resources/alpha034/std/main.ab b/lsp/resources/alpha034/std/main.ab similarity index 100% rename from resources/alpha034/std/main.ab rename to lsp/resources/alpha034/std/main.ab diff --git a/resources/alpha035/std/array.ab b/lsp/resources/alpha035/std/array.ab similarity index 100% rename from resources/alpha035/std/array.ab rename to lsp/resources/alpha035/std/array.ab diff --git a/resources/alpha035/std/date.ab b/lsp/resources/alpha035/std/date.ab similarity index 100% rename from resources/alpha035/std/date.ab rename to lsp/resources/alpha035/std/date.ab diff --git a/resources/alpha035/std/env.ab b/lsp/resources/alpha035/std/env.ab similarity index 100% rename from resources/alpha035/std/env.ab rename to lsp/resources/alpha035/std/env.ab diff --git a/resources/alpha035/std/fs.ab b/lsp/resources/alpha035/std/fs.ab similarity index 100% rename from resources/alpha035/std/fs.ab rename to lsp/resources/alpha035/std/fs.ab diff --git a/resources/alpha035/std/http.ab b/lsp/resources/alpha035/std/http.ab similarity index 100% rename from resources/alpha035/std/http.ab rename to lsp/resources/alpha035/std/http.ab diff --git a/resources/alpha035/std/math.ab b/lsp/resources/alpha035/std/math.ab similarity index 100% rename from resources/alpha035/std/math.ab rename to lsp/resources/alpha035/std/math.ab diff --git a/resources/alpha035/std/text.ab b/lsp/resources/alpha035/std/text.ab similarity index 100% rename from resources/alpha035/std/text.ab rename to lsp/resources/alpha035/std/text.ab diff --git a/resources/alpha040/builtin.ab b/lsp/resources/alpha040/builtin.ab similarity index 100% rename from resources/alpha040/builtin.ab rename to lsp/resources/alpha040/builtin.ab diff --git a/resources/alpha040/std/array.ab b/lsp/resources/alpha040/std/array.ab similarity index 100% rename from resources/alpha040/std/array.ab rename to lsp/resources/alpha040/std/array.ab diff --git a/resources/alpha040/std/date.ab b/lsp/resources/alpha040/std/date.ab similarity index 100% rename from resources/alpha040/std/date.ab rename to lsp/resources/alpha040/std/date.ab diff --git a/resources/alpha040/std/env.ab b/lsp/resources/alpha040/std/env.ab similarity index 100% rename from resources/alpha040/std/env.ab rename to lsp/resources/alpha040/std/env.ab diff --git a/resources/alpha040/std/fs.ab b/lsp/resources/alpha040/std/fs.ab similarity index 100% rename from resources/alpha040/std/fs.ab rename to lsp/resources/alpha040/std/fs.ab diff --git a/resources/alpha040/std/http.ab b/lsp/resources/alpha040/std/http.ab similarity index 100% rename from resources/alpha040/std/http.ab rename to lsp/resources/alpha040/std/http.ab diff --git a/resources/alpha040/std/math.ab b/lsp/resources/alpha040/std/math.ab similarity index 100% rename from resources/alpha040/std/math.ab rename to lsp/resources/alpha040/std/math.ab diff --git a/resources/alpha040/std/text.ab b/lsp/resources/alpha040/std/text.ab similarity index 100% rename from resources/alpha040/std/text.ab rename to lsp/resources/alpha040/std/text.ab diff --git a/src/analysis/alpha034/exp.rs b/lsp/src/analysis/alpha034/exp.rs similarity index 100% rename from src/analysis/alpha034/exp.rs rename to lsp/src/analysis/alpha034/exp.rs diff --git a/src/analysis/alpha034/global.rs b/lsp/src/analysis/alpha034/global.rs similarity index 100% rename from src/analysis/alpha034/global.rs rename to lsp/src/analysis/alpha034/global.rs diff --git a/src/analysis/alpha034/mod.rs b/lsp/src/analysis/alpha034/mod.rs similarity index 100% rename from src/analysis/alpha034/mod.rs rename to lsp/src/analysis/alpha034/mod.rs diff --git a/src/analysis/alpha034/stmnts.rs b/lsp/src/analysis/alpha034/stmnts.rs similarity index 100% rename from src/analysis/alpha034/stmnts.rs rename to lsp/src/analysis/alpha034/stmnts.rs diff --git a/src/analysis/alpha035/exp.rs b/lsp/src/analysis/alpha035/exp.rs similarity index 100% rename from src/analysis/alpha035/exp.rs rename to lsp/src/analysis/alpha035/exp.rs diff --git a/src/analysis/alpha035/global.rs b/lsp/src/analysis/alpha035/global.rs similarity index 100% rename from src/analysis/alpha035/global.rs rename to lsp/src/analysis/alpha035/global.rs diff --git a/src/analysis/alpha035/mod.rs b/lsp/src/analysis/alpha035/mod.rs similarity index 100% rename from src/analysis/alpha035/mod.rs rename to lsp/src/analysis/alpha035/mod.rs diff --git a/src/analysis/alpha035/stmnts.rs b/lsp/src/analysis/alpha035/stmnts.rs similarity index 100% rename from src/analysis/alpha035/stmnts.rs rename to lsp/src/analysis/alpha035/stmnts.rs diff --git a/src/analysis/alpha040/exp.rs b/lsp/src/analysis/alpha040/exp.rs similarity index 100% rename from src/analysis/alpha040/exp.rs rename to lsp/src/analysis/alpha040/exp.rs diff --git a/src/analysis/alpha040/global.rs b/lsp/src/analysis/alpha040/global.rs similarity index 100% rename from src/analysis/alpha040/global.rs rename to lsp/src/analysis/alpha040/global.rs diff --git a/src/analysis/alpha040/mod.rs b/lsp/src/analysis/alpha040/mod.rs similarity index 100% rename from src/analysis/alpha040/mod.rs rename to lsp/src/analysis/alpha040/mod.rs diff --git a/src/analysis/alpha040/stmnts.rs b/lsp/src/analysis/alpha040/stmnts.rs similarity index 100% rename from src/analysis/alpha040/stmnts.rs rename to lsp/src/analysis/alpha040/stmnts.rs diff --git a/src/analysis/mod.rs b/lsp/src/analysis/mod.rs similarity index 100% rename from src/analysis/mod.rs rename to lsp/src/analysis/mod.rs diff --git a/src/analysis/types.rs b/lsp/src/analysis/types.rs similarity index 100% rename from src/analysis/types.rs rename to lsp/src/analysis/types.rs diff --git a/src/backend.rs b/lsp/src/backend.rs similarity index 100% rename from src/backend.rs rename to lsp/src/backend.rs diff --git a/src/files.rs b/lsp/src/files.rs similarity index 100% rename from src/files.rs rename to lsp/src/files.rs diff --git a/src/fs.rs b/lsp/src/fs.rs similarity index 100% rename from src/fs.rs rename to lsp/src/fs.rs diff --git a/src/grammar/alpha034/expressions/and.rs b/lsp/src/grammar/alpha034/expressions/and.rs similarity index 100% rename from src/grammar/alpha034/expressions/and.rs rename to lsp/src/grammar/alpha034/expressions/and.rs diff --git a/src/grammar/alpha034/expressions/atom/array.rs b/lsp/src/grammar/alpha034/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/array.rs rename to lsp/src/grammar/alpha034/expressions/atom/array.rs diff --git a/src/grammar/alpha034/expressions/atom/bool.rs b/lsp/src/grammar/alpha034/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/bool.rs rename to lsp/src/grammar/alpha034/expressions/atom/bool.rs diff --git a/src/grammar/alpha034/expressions/atom/call.rs b/lsp/src/grammar/alpha034/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/call.rs rename to lsp/src/grammar/alpha034/expressions/atom/call.rs diff --git a/src/grammar/alpha034/expressions/atom/command.rs b/lsp/src/grammar/alpha034/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/command.rs rename to lsp/src/grammar/alpha034/expressions/atom/command.rs diff --git a/src/grammar/alpha034/expressions/atom/mod.rs b/lsp/src/grammar/alpha034/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/mod.rs rename to lsp/src/grammar/alpha034/expressions/atom/mod.rs diff --git a/src/grammar/alpha034/expressions/atom/null.rs b/lsp/src/grammar/alpha034/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/null.rs rename to lsp/src/grammar/alpha034/expressions/atom/null.rs diff --git a/src/grammar/alpha034/expressions/atom/number.rs b/lsp/src/grammar/alpha034/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/number.rs rename to lsp/src/grammar/alpha034/expressions/atom/number.rs diff --git a/src/grammar/alpha034/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha034/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha034/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha034/expressions/atom/status.rs b/lsp/src/grammar/alpha034/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/status.rs rename to lsp/src/grammar/alpha034/expressions/atom/status.rs diff --git a/src/grammar/alpha034/expressions/atom/text.rs b/lsp/src/grammar/alpha034/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/text.rs rename to lsp/src/grammar/alpha034/expressions/atom/text.rs diff --git a/src/grammar/alpha034/expressions/atom/var.rs b/lsp/src/grammar/alpha034/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha034/expressions/atom/var.rs rename to lsp/src/grammar/alpha034/expressions/atom/var.rs diff --git a/src/grammar/alpha034/expressions/cast.rs b/lsp/src/grammar/alpha034/expressions/cast.rs similarity index 100% rename from src/grammar/alpha034/expressions/cast.rs rename to lsp/src/grammar/alpha034/expressions/cast.rs diff --git a/src/grammar/alpha034/expressions/comparison.rs b/lsp/src/grammar/alpha034/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha034/expressions/comparison.rs rename to lsp/src/grammar/alpha034/expressions/comparison.rs diff --git a/src/grammar/alpha034/expressions/is.rs b/lsp/src/grammar/alpha034/expressions/is.rs similarity index 100% rename from src/grammar/alpha034/expressions/is.rs rename to lsp/src/grammar/alpha034/expressions/is.rs diff --git a/src/grammar/alpha034/expressions/mod.rs b/lsp/src/grammar/alpha034/expressions/mod.rs similarity index 100% rename from src/grammar/alpha034/expressions/mod.rs rename to lsp/src/grammar/alpha034/expressions/mod.rs diff --git a/src/grammar/alpha034/expressions/or.rs b/lsp/src/grammar/alpha034/expressions/or.rs similarity index 100% rename from src/grammar/alpha034/expressions/or.rs rename to lsp/src/grammar/alpha034/expressions/or.rs diff --git a/src/grammar/alpha034/expressions/product.rs b/lsp/src/grammar/alpha034/expressions/product.rs similarity index 100% rename from src/grammar/alpha034/expressions/product.rs rename to lsp/src/grammar/alpha034/expressions/product.rs diff --git a/src/grammar/alpha034/expressions/range.rs b/lsp/src/grammar/alpha034/expressions/range.rs similarity index 100% rename from src/grammar/alpha034/expressions/range.rs rename to lsp/src/grammar/alpha034/expressions/range.rs diff --git a/src/grammar/alpha034/expressions/sum.rs b/lsp/src/grammar/alpha034/expressions/sum.rs similarity index 100% rename from src/grammar/alpha034/expressions/sum.rs rename to lsp/src/grammar/alpha034/expressions/sum.rs diff --git a/src/grammar/alpha034/expressions/ternary.rs b/lsp/src/grammar/alpha034/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha034/expressions/ternary.rs rename to lsp/src/grammar/alpha034/expressions/ternary.rs diff --git a/src/grammar/alpha034/expressions/unary.rs b/lsp/src/grammar/alpha034/expressions/unary.rs similarity index 100% rename from src/grammar/alpha034/expressions/unary.rs rename to lsp/src/grammar/alpha034/expressions/unary.rs diff --git a/src/grammar/alpha034/global.rs b/lsp/src/grammar/alpha034/global.rs similarity index 100% rename from src/grammar/alpha034/global.rs rename to lsp/src/grammar/alpha034/global.rs diff --git a/src/grammar/alpha034/lexer.rs b/lsp/src/grammar/alpha034/lexer.rs similarity index 100% rename from src/grammar/alpha034/lexer.rs rename to lsp/src/grammar/alpha034/lexer.rs diff --git a/src/grammar/alpha034/mod.rs b/lsp/src/grammar/alpha034/mod.rs similarity index 100% rename from src/grammar/alpha034/mod.rs rename to lsp/src/grammar/alpha034/mod.rs diff --git a/src/grammar/alpha034/parser.rs b/lsp/src/grammar/alpha034/parser.rs similarity index 100% rename from src/grammar/alpha034/parser.rs rename to lsp/src/grammar/alpha034/parser.rs diff --git a/src/grammar/alpha034/semantic_tokens.rs b/lsp/src/grammar/alpha034/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha034/semantic_tokens.rs rename to lsp/src/grammar/alpha034/semantic_tokens.rs diff --git a/src/grammar/alpha034/statements/block.rs b/lsp/src/grammar/alpha034/statements/block.rs similarity index 100% rename from src/grammar/alpha034/statements/block.rs rename to lsp/src/grammar/alpha034/statements/block.rs diff --git a/src/grammar/alpha034/statements/comment.rs b/lsp/src/grammar/alpha034/statements/comment.rs similarity index 100% rename from src/grammar/alpha034/statements/comment.rs rename to lsp/src/grammar/alpha034/statements/comment.rs diff --git a/src/grammar/alpha034/statements/const_init.rs b/lsp/src/grammar/alpha034/statements/const_init.rs similarity index 100% rename from src/grammar/alpha034/statements/const_init.rs rename to lsp/src/grammar/alpha034/statements/const_init.rs diff --git a/src/grammar/alpha034/statements/failed.rs b/lsp/src/grammar/alpha034/statements/failed.rs similarity index 100% rename from src/grammar/alpha034/statements/failed.rs rename to lsp/src/grammar/alpha034/statements/failed.rs diff --git a/src/grammar/alpha034/statements/if_cond.rs b/lsp/src/grammar/alpha034/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha034/statements/if_cond.rs rename to lsp/src/grammar/alpha034/statements/if_cond.rs diff --git a/src/grammar/alpha034/statements/keywords.rs b/lsp/src/grammar/alpha034/statements/keywords.rs similarity index 100% rename from src/grammar/alpha034/statements/keywords.rs rename to lsp/src/grammar/alpha034/statements/keywords.rs diff --git a/src/grammar/alpha034/statements/loops.rs b/lsp/src/grammar/alpha034/statements/loops.rs similarity index 100% rename from src/grammar/alpha034/statements/loops.rs rename to lsp/src/grammar/alpha034/statements/loops.rs diff --git a/src/grammar/alpha034/statements/mod.rs b/lsp/src/grammar/alpha034/statements/mod.rs similarity index 100% rename from src/grammar/alpha034/statements/mod.rs rename to lsp/src/grammar/alpha034/statements/mod.rs diff --git a/src/grammar/alpha034/statements/modifiers.rs b/lsp/src/grammar/alpha034/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha034/statements/modifiers.rs rename to lsp/src/grammar/alpha034/statements/modifiers.rs diff --git a/src/grammar/alpha034/statements/shebang.rs b/lsp/src/grammar/alpha034/statements/shebang.rs similarity index 100% rename from src/grammar/alpha034/statements/shebang.rs rename to lsp/src/grammar/alpha034/statements/shebang.rs diff --git a/src/grammar/alpha034/statements/shorthands.rs b/lsp/src/grammar/alpha034/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha034/statements/shorthands.rs rename to lsp/src/grammar/alpha034/statements/shorthands.rs diff --git a/src/grammar/alpha034/statements/var_init.rs b/lsp/src/grammar/alpha034/statements/var_init.rs similarity index 100% rename from src/grammar/alpha034/statements/var_init.rs rename to lsp/src/grammar/alpha034/statements/var_init.rs diff --git a/src/grammar/alpha034/statements/var_set.rs b/lsp/src/grammar/alpha034/statements/var_set.rs similarity index 100% rename from src/grammar/alpha034/statements/var_set.rs rename to lsp/src/grammar/alpha034/statements/var_set.rs diff --git a/src/grammar/alpha035/expressions/and.rs b/lsp/src/grammar/alpha035/expressions/and.rs similarity index 100% rename from src/grammar/alpha035/expressions/and.rs rename to lsp/src/grammar/alpha035/expressions/and.rs diff --git a/src/grammar/alpha035/expressions/atom/array.rs b/lsp/src/grammar/alpha035/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/array.rs rename to lsp/src/grammar/alpha035/expressions/atom/array.rs diff --git a/src/grammar/alpha035/expressions/atom/bool.rs b/lsp/src/grammar/alpha035/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/bool.rs rename to lsp/src/grammar/alpha035/expressions/atom/bool.rs diff --git a/src/grammar/alpha035/expressions/atom/call.rs b/lsp/src/grammar/alpha035/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/call.rs rename to lsp/src/grammar/alpha035/expressions/atom/call.rs diff --git a/src/grammar/alpha035/expressions/atom/command.rs b/lsp/src/grammar/alpha035/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/command.rs rename to lsp/src/grammar/alpha035/expressions/atom/command.rs diff --git a/src/grammar/alpha035/expressions/atom/mod.rs b/lsp/src/grammar/alpha035/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/mod.rs rename to lsp/src/grammar/alpha035/expressions/atom/mod.rs diff --git a/src/grammar/alpha035/expressions/atom/null.rs b/lsp/src/grammar/alpha035/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/null.rs rename to lsp/src/grammar/alpha035/expressions/atom/null.rs diff --git a/src/grammar/alpha035/expressions/atom/number.rs b/lsp/src/grammar/alpha035/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/number.rs rename to lsp/src/grammar/alpha035/expressions/atom/number.rs diff --git a/src/grammar/alpha035/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha035/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha035/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha035/expressions/atom/status.rs b/lsp/src/grammar/alpha035/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/status.rs rename to lsp/src/grammar/alpha035/expressions/atom/status.rs diff --git a/src/grammar/alpha035/expressions/atom/text.rs b/lsp/src/grammar/alpha035/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/text.rs rename to lsp/src/grammar/alpha035/expressions/atom/text.rs diff --git a/src/grammar/alpha035/expressions/atom/var.rs b/lsp/src/grammar/alpha035/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha035/expressions/atom/var.rs rename to lsp/src/grammar/alpha035/expressions/atom/var.rs diff --git a/src/grammar/alpha035/expressions/cast.rs b/lsp/src/grammar/alpha035/expressions/cast.rs similarity index 100% rename from src/grammar/alpha035/expressions/cast.rs rename to lsp/src/grammar/alpha035/expressions/cast.rs diff --git a/src/grammar/alpha035/expressions/comparison.rs b/lsp/src/grammar/alpha035/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha035/expressions/comparison.rs rename to lsp/src/grammar/alpha035/expressions/comparison.rs diff --git a/src/grammar/alpha035/expressions/is.rs b/lsp/src/grammar/alpha035/expressions/is.rs similarity index 100% rename from src/grammar/alpha035/expressions/is.rs rename to lsp/src/grammar/alpha035/expressions/is.rs diff --git a/src/grammar/alpha035/expressions/mod.rs b/lsp/src/grammar/alpha035/expressions/mod.rs similarity index 100% rename from src/grammar/alpha035/expressions/mod.rs rename to lsp/src/grammar/alpha035/expressions/mod.rs diff --git a/src/grammar/alpha035/expressions/or.rs b/lsp/src/grammar/alpha035/expressions/or.rs similarity index 100% rename from src/grammar/alpha035/expressions/or.rs rename to lsp/src/grammar/alpha035/expressions/or.rs diff --git a/src/grammar/alpha035/expressions/product.rs b/lsp/src/grammar/alpha035/expressions/product.rs similarity index 100% rename from src/grammar/alpha035/expressions/product.rs rename to lsp/src/grammar/alpha035/expressions/product.rs diff --git a/src/grammar/alpha035/expressions/range.rs b/lsp/src/grammar/alpha035/expressions/range.rs similarity index 100% rename from src/grammar/alpha035/expressions/range.rs rename to lsp/src/grammar/alpha035/expressions/range.rs diff --git a/src/grammar/alpha035/expressions/sum.rs b/lsp/src/grammar/alpha035/expressions/sum.rs similarity index 100% rename from src/grammar/alpha035/expressions/sum.rs rename to lsp/src/grammar/alpha035/expressions/sum.rs diff --git a/src/grammar/alpha035/expressions/ternary.rs b/lsp/src/grammar/alpha035/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha035/expressions/ternary.rs rename to lsp/src/grammar/alpha035/expressions/ternary.rs diff --git a/src/grammar/alpha035/expressions/unary.rs b/lsp/src/grammar/alpha035/expressions/unary.rs similarity index 100% rename from src/grammar/alpha035/expressions/unary.rs rename to lsp/src/grammar/alpha035/expressions/unary.rs diff --git a/src/grammar/alpha035/global.rs b/lsp/src/grammar/alpha035/global.rs similarity index 100% rename from src/grammar/alpha035/global.rs rename to lsp/src/grammar/alpha035/global.rs diff --git a/src/grammar/alpha035/lexer.rs b/lsp/src/grammar/alpha035/lexer.rs similarity index 100% rename from src/grammar/alpha035/lexer.rs rename to lsp/src/grammar/alpha035/lexer.rs diff --git a/src/grammar/alpha035/mod.rs b/lsp/src/grammar/alpha035/mod.rs similarity index 100% rename from src/grammar/alpha035/mod.rs rename to lsp/src/grammar/alpha035/mod.rs diff --git a/src/grammar/alpha035/parser.rs b/lsp/src/grammar/alpha035/parser.rs similarity index 100% rename from src/grammar/alpha035/parser.rs rename to lsp/src/grammar/alpha035/parser.rs diff --git a/src/grammar/alpha035/semantic_tokens.rs b/lsp/src/grammar/alpha035/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha035/semantic_tokens.rs rename to lsp/src/grammar/alpha035/semantic_tokens.rs diff --git a/src/grammar/alpha035/statements/block.rs b/lsp/src/grammar/alpha035/statements/block.rs similarity index 100% rename from src/grammar/alpha035/statements/block.rs rename to lsp/src/grammar/alpha035/statements/block.rs diff --git a/src/grammar/alpha035/statements/comment.rs b/lsp/src/grammar/alpha035/statements/comment.rs similarity index 100% rename from src/grammar/alpha035/statements/comment.rs rename to lsp/src/grammar/alpha035/statements/comment.rs diff --git a/src/grammar/alpha035/statements/const_init.rs b/lsp/src/grammar/alpha035/statements/const_init.rs similarity index 100% rename from src/grammar/alpha035/statements/const_init.rs rename to lsp/src/grammar/alpha035/statements/const_init.rs diff --git a/src/grammar/alpha035/statements/failed.rs b/lsp/src/grammar/alpha035/statements/failed.rs similarity index 100% rename from src/grammar/alpha035/statements/failed.rs rename to lsp/src/grammar/alpha035/statements/failed.rs diff --git a/src/grammar/alpha035/statements/if_cond.rs b/lsp/src/grammar/alpha035/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha035/statements/if_cond.rs rename to lsp/src/grammar/alpha035/statements/if_cond.rs diff --git a/src/grammar/alpha035/statements/keywords.rs b/lsp/src/grammar/alpha035/statements/keywords.rs similarity index 100% rename from src/grammar/alpha035/statements/keywords.rs rename to lsp/src/grammar/alpha035/statements/keywords.rs diff --git a/src/grammar/alpha035/statements/loops.rs b/lsp/src/grammar/alpha035/statements/loops.rs similarity index 100% rename from src/grammar/alpha035/statements/loops.rs rename to lsp/src/grammar/alpha035/statements/loops.rs diff --git a/src/grammar/alpha035/statements/mod.rs b/lsp/src/grammar/alpha035/statements/mod.rs similarity index 100% rename from src/grammar/alpha035/statements/mod.rs rename to lsp/src/grammar/alpha035/statements/mod.rs diff --git a/src/grammar/alpha035/statements/modifiers.rs b/lsp/src/grammar/alpha035/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha035/statements/modifiers.rs rename to lsp/src/grammar/alpha035/statements/modifiers.rs diff --git a/src/grammar/alpha035/statements/move_files.rs b/lsp/src/grammar/alpha035/statements/move_files.rs similarity index 100% rename from src/grammar/alpha035/statements/move_files.rs rename to lsp/src/grammar/alpha035/statements/move_files.rs diff --git a/src/grammar/alpha035/statements/shebang.rs b/lsp/src/grammar/alpha035/statements/shebang.rs similarity index 100% rename from src/grammar/alpha035/statements/shebang.rs rename to lsp/src/grammar/alpha035/statements/shebang.rs diff --git a/src/grammar/alpha035/statements/shorthands.rs b/lsp/src/grammar/alpha035/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha035/statements/shorthands.rs rename to lsp/src/grammar/alpha035/statements/shorthands.rs diff --git a/src/grammar/alpha035/statements/var_init.rs b/lsp/src/grammar/alpha035/statements/var_init.rs similarity index 100% rename from src/grammar/alpha035/statements/var_init.rs rename to lsp/src/grammar/alpha035/statements/var_init.rs diff --git a/src/grammar/alpha035/statements/var_set.rs b/lsp/src/grammar/alpha035/statements/var_set.rs similarity index 100% rename from src/grammar/alpha035/statements/var_set.rs rename to lsp/src/grammar/alpha035/statements/var_set.rs diff --git a/src/grammar/alpha040/expressions/and.rs b/lsp/src/grammar/alpha040/expressions/and.rs similarity index 100% rename from src/grammar/alpha040/expressions/and.rs rename to lsp/src/grammar/alpha040/expressions/and.rs diff --git a/src/grammar/alpha040/expressions/atom/array.rs b/lsp/src/grammar/alpha040/expressions/atom/array.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/array.rs rename to lsp/src/grammar/alpha040/expressions/atom/array.rs diff --git a/src/grammar/alpha040/expressions/atom/bool.rs b/lsp/src/grammar/alpha040/expressions/atom/bool.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/bool.rs rename to lsp/src/grammar/alpha040/expressions/atom/bool.rs diff --git a/src/grammar/alpha040/expressions/atom/call.rs b/lsp/src/grammar/alpha040/expressions/atom/call.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/call.rs rename to lsp/src/grammar/alpha040/expressions/atom/call.rs diff --git a/src/grammar/alpha040/expressions/atom/command.rs b/lsp/src/grammar/alpha040/expressions/atom/command.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/command.rs rename to lsp/src/grammar/alpha040/expressions/atom/command.rs diff --git a/src/grammar/alpha040/expressions/atom/exit.rs b/lsp/src/grammar/alpha040/expressions/atom/exit.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/exit.rs rename to lsp/src/grammar/alpha040/expressions/atom/exit.rs diff --git a/src/grammar/alpha040/expressions/atom/mod.rs b/lsp/src/grammar/alpha040/expressions/atom/mod.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/mod.rs rename to lsp/src/grammar/alpha040/expressions/atom/mod.rs diff --git a/src/grammar/alpha040/expressions/atom/null.rs b/lsp/src/grammar/alpha040/expressions/atom/null.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/null.rs rename to lsp/src/grammar/alpha040/expressions/atom/null.rs diff --git a/src/grammar/alpha040/expressions/atom/number.rs b/lsp/src/grammar/alpha040/expressions/atom/number.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/number.rs rename to lsp/src/grammar/alpha040/expressions/atom/number.rs diff --git a/src/grammar/alpha040/expressions/atom/parentheses.rs b/lsp/src/grammar/alpha040/expressions/atom/parentheses.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/parentheses.rs rename to lsp/src/grammar/alpha040/expressions/atom/parentheses.rs diff --git a/src/grammar/alpha040/expressions/atom/status.rs b/lsp/src/grammar/alpha040/expressions/atom/status.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/status.rs rename to lsp/src/grammar/alpha040/expressions/atom/status.rs diff --git a/src/grammar/alpha040/expressions/atom/text.rs b/lsp/src/grammar/alpha040/expressions/atom/text.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/text.rs rename to lsp/src/grammar/alpha040/expressions/atom/text.rs diff --git a/src/grammar/alpha040/expressions/atom/var.rs b/lsp/src/grammar/alpha040/expressions/atom/var.rs similarity index 100% rename from src/grammar/alpha040/expressions/atom/var.rs rename to lsp/src/grammar/alpha040/expressions/atom/var.rs diff --git a/src/grammar/alpha040/expressions/cast.rs b/lsp/src/grammar/alpha040/expressions/cast.rs similarity index 100% rename from src/grammar/alpha040/expressions/cast.rs rename to lsp/src/grammar/alpha040/expressions/cast.rs diff --git a/src/grammar/alpha040/expressions/comparison.rs b/lsp/src/grammar/alpha040/expressions/comparison.rs similarity index 100% rename from src/grammar/alpha040/expressions/comparison.rs rename to lsp/src/grammar/alpha040/expressions/comparison.rs diff --git a/src/grammar/alpha040/expressions/is.rs b/lsp/src/grammar/alpha040/expressions/is.rs similarity index 100% rename from src/grammar/alpha040/expressions/is.rs rename to lsp/src/grammar/alpha040/expressions/is.rs diff --git a/src/grammar/alpha040/expressions/mod.rs b/lsp/src/grammar/alpha040/expressions/mod.rs similarity index 100% rename from src/grammar/alpha040/expressions/mod.rs rename to lsp/src/grammar/alpha040/expressions/mod.rs diff --git a/src/grammar/alpha040/expressions/or.rs b/lsp/src/grammar/alpha040/expressions/or.rs similarity index 100% rename from src/grammar/alpha040/expressions/or.rs rename to lsp/src/grammar/alpha040/expressions/or.rs diff --git a/src/grammar/alpha040/expressions/product.rs b/lsp/src/grammar/alpha040/expressions/product.rs similarity index 100% rename from src/grammar/alpha040/expressions/product.rs rename to lsp/src/grammar/alpha040/expressions/product.rs diff --git a/src/grammar/alpha040/expressions/range.rs b/lsp/src/grammar/alpha040/expressions/range.rs similarity index 100% rename from src/grammar/alpha040/expressions/range.rs rename to lsp/src/grammar/alpha040/expressions/range.rs diff --git a/src/grammar/alpha040/expressions/sum.rs b/lsp/src/grammar/alpha040/expressions/sum.rs similarity index 100% rename from src/grammar/alpha040/expressions/sum.rs rename to lsp/src/grammar/alpha040/expressions/sum.rs diff --git a/src/grammar/alpha040/expressions/ternary.rs b/lsp/src/grammar/alpha040/expressions/ternary.rs similarity index 100% rename from src/grammar/alpha040/expressions/ternary.rs rename to lsp/src/grammar/alpha040/expressions/ternary.rs diff --git a/src/grammar/alpha040/expressions/unary.rs b/lsp/src/grammar/alpha040/expressions/unary.rs similarity index 100% rename from src/grammar/alpha040/expressions/unary.rs rename to lsp/src/grammar/alpha040/expressions/unary.rs diff --git a/src/grammar/alpha040/global.rs b/lsp/src/grammar/alpha040/global.rs similarity index 100% rename from src/grammar/alpha040/global.rs rename to lsp/src/grammar/alpha040/global.rs diff --git a/src/grammar/alpha040/lexer.rs b/lsp/src/grammar/alpha040/lexer.rs similarity index 100% rename from src/grammar/alpha040/lexer.rs rename to lsp/src/grammar/alpha040/lexer.rs diff --git a/src/grammar/alpha040/mod.rs b/lsp/src/grammar/alpha040/mod.rs similarity index 100% rename from src/grammar/alpha040/mod.rs rename to lsp/src/grammar/alpha040/mod.rs diff --git a/src/grammar/alpha040/parser.rs b/lsp/src/grammar/alpha040/parser.rs similarity index 100% rename from src/grammar/alpha040/parser.rs rename to lsp/src/grammar/alpha040/parser.rs diff --git a/src/grammar/alpha040/semantic_tokens.rs b/lsp/src/grammar/alpha040/semantic_tokens.rs similarity index 100% rename from src/grammar/alpha040/semantic_tokens.rs rename to lsp/src/grammar/alpha040/semantic_tokens.rs diff --git a/src/grammar/alpha040/statements/block.rs b/lsp/src/grammar/alpha040/statements/block.rs similarity index 100% rename from src/grammar/alpha040/statements/block.rs rename to lsp/src/grammar/alpha040/statements/block.rs diff --git a/src/grammar/alpha040/statements/comment.rs b/lsp/src/grammar/alpha040/statements/comment.rs similarity index 100% rename from src/grammar/alpha040/statements/comment.rs rename to lsp/src/grammar/alpha040/statements/comment.rs diff --git a/src/grammar/alpha040/statements/const_init.rs b/lsp/src/grammar/alpha040/statements/const_init.rs similarity index 100% rename from src/grammar/alpha040/statements/const_init.rs rename to lsp/src/grammar/alpha040/statements/const_init.rs diff --git a/src/grammar/alpha040/statements/failed.rs b/lsp/src/grammar/alpha040/statements/failed.rs similarity index 100% rename from src/grammar/alpha040/statements/failed.rs rename to lsp/src/grammar/alpha040/statements/failed.rs diff --git a/src/grammar/alpha040/statements/if_cond.rs b/lsp/src/grammar/alpha040/statements/if_cond.rs similarity index 100% rename from src/grammar/alpha040/statements/if_cond.rs rename to lsp/src/grammar/alpha040/statements/if_cond.rs diff --git a/src/grammar/alpha040/statements/keywords.rs b/lsp/src/grammar/alpha040/statements/keywords.rs similarity index 100% rename from src/grammar/alpha040/statements/keywords.rs rename to lsp/src/grammar/alpha040/statements/keywords.rs diff --git a/src/grammar/alpha040/statements/loops.rs b/lsp/src/grammar/alpha040/statements/loops.rs similarity index 100% rename from src/grammar/alpha040/statements/loops.rs rename to lsp/src/grammar/alpha040/statements/loops.rs diff --git a/src/grammar/alpha040/statements/mod.rs b/lsp/src/grammar/alpha040/statements/mod.rs similarity index 100% rename from src/grammar/alpha040/statements/mod.rs rename to lsp/src/grammar/alpha040/statements/mod.rs diff --git a/src/grammar/alpha040/statements/modifiers.rs b/lsp/src/grammar/alpha040/statements/modifiers.rs similarity index 100% rename from src/grammar/alpha040/statements/modifiers.rs rename to lsp/src/grammar/alpha040/statements/modifiers.rs diff --git a/src/grammar/alpha040/statements/move_files.rs b/lsp/src/grammar/alpha040/statements/move_files.rs similarity index 100% rename from src/grammar/alpha040/statements/move_files.rs rename to lsp/src/grammar/alpha040/statements/move_files.rs diff --git a/src/grammar/alpha040/statements/shebang.rs b/lsp/src/grammar/alpha040/statements/shebang.rs similarity index 100% rename from src/grammar/alpha040/statements/shebang.rs rename to lsp/src/grammar/alpha040/statements/shebang.rs diff --git a/src/grammar/alpha040/statements/shorthands.rs b/lsp/src/grammar/alpha040/statements/shorthands.rs similarity index 100% rename from src/grammar/alpha040/statements/shorthands.rs rename to lsp/src/grammar/alpha040/statements/shorthands.rs diff --git a/src/grammar/alpha040/statements/var_init.rs b/lsp/src/grammar/alpha040/statements/var_init.rs similarity index 100% rename from src/grammar/alpha040/statements/var_init.rs rename to lsp/src/grammar/alpha040/statements/var_init.rs diff --git a/src/grammar/alpha040/statements/var_set.rs b/lsp/src/grammar/alpha040/statements/var_set.rs similarity index 100% rename from src/grammar/alpha040/statements/var_set.rs rename to lsp/src/grammar/alpha040/statements/var_set.rs diff --git a/src/grammar/mod.rs b/lsp/src/grammar/mod.rs similarity index 100% rename from src/grammar/mod.rs rename to lsp/src/grammar/mod.rs diff --git a/src/lib.rs b/lsp/src/lib.rs similarity index 100% rename from src/lib.rs rename to lsp/src/lib.rs diff --git a/src/main.rs b/lsp/src/main.rs similarity index 100% rename from src/main.rs rename to lsp/src/main.rs diff --git a/src/paths.rs b/lsp/src/paths.rs similarity index 100% rename from src/paths.rs rename to lsp/src/paths.rs diff --git a/src/stdlib.rs b/lsp/src/stdlib.rs similarity index 100% rename from src/stdlib.rs rename to lsp/src/stdlib.rs diff --git a/src/utils.rs b/lsp/src/utils.rs similarity index 100% rename from src/utils.rs rename to lsp/src/utils.rs From 84b0aad9ea94a2781de23575902cc4bf3b14a046 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 27 Oct 2025 17:38:59 +0000 Subject: [PATCH 07/37] Separated into library and binary --- Cargo.toml | 3 +- lib/Cargo.toml | 29 ++ lib/resources/alpha034/std/main.ab | 268 ++++++++++++++++++ lib/resources/alpha035/std/array.ab | 27 ++ lib/resources/alpha035/std/date.ab | 109 +++++++ lib/resources/alpha035/std/env.ab | 160 +++++++++++ lib/resources/alpha035/std/fs.ab | 74 +++++ lib/resources/alpha035/std/http.ab | 25 ++ lib/resources/alpha035/std/math.ab | 34 +++ lib/resources/alpha035/std/text.ab | 164 +++++++++++ lib/resources/alpha040/builtin.ab | 38 +++ lib/resources/alpha040/std/array.ab | 80 ++++++ lib/resources/alpha040/std/date.ab | 126 ++++++++ lib/resources/alpha040/std/env.ab | 164 +++++++++++ lib/resources/alpha040/std/fs.ab | 140 +++++++++ lib/resources/alpha040/std/http.ab | 24 ++ lib/resources/alpha040/std/math.ab | 34 +++ lib/resources/alpha040/std/text.ab | 241 ++++++++++++++++ {lsp => lib}/src/analysis/alpha034/exp.rs | 0 {lsp => lib}/src/analysis/alpha034/global.rs | 0 {lsp => lib}/src/analysis/alpha034/mod.rs | 0 {lsp => lib}/src/analysis/alpha034/stmnts.rs | 0 {lsp => lib}/src/analysis/alpha035/exp.rs | 0 {lsp => lib}/src/analysis/alpha035/global.rs | 0 {lsp => lib}/src/analysis/alpha035/mod.rs | 0 {lsp => lib}/src/analysis/alpha035/stmnts.rs | 0 {lsp => lib}/src/analysis/alpha040/exp.rs | 0 {lsp => lib}/src/analysis/alpha040/global.rs | 0 {lsp => lib}/src/analysis/alpha040/mod.rs | 0 {lsp => lib}/src/analysis/alpha040/stmnts.rs | 0 {lsp => lib}/src/analysis/mod.rs | 0 {lsp => lib}/src/analysis/types.rs | 0 {lsp => lib}/src/backend.rs | 0 {lsp => lib}/src/files.rs | 0 {lsp => lib}/src/fs.rs | 0 .../src/grammar/alpha034/expressions/and.rs | 0 .../alpha034/expressions/atom/array.rs | 0 .../grammar/alpha034/expressions/atom/bool.rs | 0 .../grammar/alpha034/expressions/atom/call.rs | 0 .../alpha034/expressions/atom/command.rs | 0 .../grammar/alpha034/expressions/atom/mod.rs | 0 .../grammar/alpha034/expressions/atom/null.rs | 0 .../alpha034/expressions/atom/number.rs | 0 .../alpha034/expressions/atom/parentheses.rs | 0 .../alpha034/expressions/atom/status.rs | 0 .../grammar/alpha034/expressions/atom/text.rs | 0 .../grammar/alpha034/expressions/atom/var.rs | 0 .../src/grammar/alpha034/expressions/cast.rs | 0 .../alpha034/expressions/comparison.rs | 0 .../src/grammar/alpha034/expressions/is.rs | 0 .../src/grammar/alpha034/expressions/mod.rs | 0 .../src/grammar/alpha034/expressions/or.rs | 0 .../grammar/alpha034/expressions/product.rs | 0 .../src/grammar/alpha034/expressions/range.rs | 0 .../src/grammar/alpha034/expressions/sum.rs | 0 .../grammar/alpha034/expressions/ternary.rs | 0 .../src/grammar/alpha034/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha034/global.rs | 0 {lsp => lib}/src/grammar/alpha034/lexer.rs | 0 {lsp => lib}/src/grammar/alpha034/mod.rs | 0 {lsp => lib}/src/grammar/alpha034/parser.rs | 0 .../src/grammar/alpha034/semantic_tokens.rs | 0 .../src/grammar/alpha034/statements/block.rs | 0 .../grammar/alpha034/statements/comment.rs | 0 .../grammar/alpha034/statements/const_init.rs | 0 .../src/grammar/alpha034/statements/failed.rs | 0 .../grammar/alpha034/statements/if_cond.rs | 0 .../grammar/alpha034/statements/keywords.rs | 0 .../src/grammar/alpha034/statements/loops.rs | 0 .../src/grammar/alpha034/statements/mod.rs | 0 .../grammar/alpha034/statements/modifiers.rs | 0 .../grammar/alpha034/statements/shebang.rs | 0 .../grammar/alpha034/statements/shorthands.rs | 0 .../grammar/alpha034/statements/var_init.rs | 0 .../grammar/alpha034/statements/var_set.rs | 0 .../src/grammar/alpha035/expressions/and.rs | 0 .../alpha035/expressions/atom/array.rs | 0 .../grammar/alpha035/expressions/atom/bool.rs | 0 .../grammar/alpha035/expressions/atom/call.rs | 0 .../alpha035/expressions/atom/command.rs | 0 .../grammar/alpha035/expressions/atom/mod.rs | 0 .../grammar/alpha035/expressions/atom/null.rs | 0 .../alpha035/expressions/atom/number.rs | 0 .../alpha035/expressions/atom/parentheses.rs | 0 .../alpha035/expressions/atom/status.rs | 0 .../grammar/alpha035/expressions/atom/text.rs | 0 .../grammar/alpha035/expressions/atom/var.rs | 0 .../src/grammar/alpha035/expressions/cast.rs | 0 .../alpha035/expressions/comparison.rs | 0 .../src/grammar/alpha035/expressions/is.rs | 0 .../src/grammar/alpha035/expressions/mod.rs | 0 .../src/grammar/alpha035/expressions/or.rs | 0 .../grammar/alpha035/expressions/product.rs | 0 .../src/grammar/alpha035/expressions/range.rs | 0 .../src/grammar/alpha035/expressions/sum.rs | 0 .../grammar/alpha035/expressions/ternary.rs | 0 .../src/grammar/alpha035/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha035/global.rs | 0 {lsp => lib}/src/grammar/alpha035/lexer.rs | 0 {lsp => lib}/src/grammar/alpha035/mod.rs | 0 {lsp => lib}/src/grammar/alpha035/parser.rs | 0 .../src/grammar/alpha035/semantic_tokens.rs | 0 .../src/grammar/alpha035/statements/block.rs | 0 .../grammar/alpha035/statements/comment.rs | 0 .../grammar/alpha035/statements/const_init.rs | 0 .../src/grammar/alpha035/statements/failed.rs | 0 .../grammar/alpha035/statements/if_cond.rs | 0 .../grammar/alpha035/statements/keywords.rs | 0 .../src/grammar/alpha035/statements/loops.rs | 0 .../src/grammar/alpha035/statements/mod.rs | 0 .../grammar/alpha035/statements/modifiers.rs | 0 .../grammar/alpha035/statements/move_files.rs | 0 .../grammar/alpha035/statements/shebang.rs | 0 .../grammar/alpha035/statements/shorthands.rs | 0 .../grammar/alpha035/statements/var_init.rs | 0 .../grammar/alpha035/statements/var_set.rs | 0 .../src/grammar/alpha040/expressions/and.rs | 0 .../alpha040/expressions/atom/array.rs | 0 .../grammar/alpha040/expressions/atom/bool.rs | 0 .../grammar/alpha040/expressions/atom/call.rs | 0 .../alpha040/expressions/atom/command.rs | 0 .../grammar/alpha040/expressions/atom/exit.rs | 0 .../grammar/alpha040/expressions/atom/mod.rs | 0 .../grammar/alpha040/expressions/atom/null.rs | 0 .../alpha040/expressions/atom/number.rs | 0 .../alpha040/expressions/atom/parentheses.rs | 0 .../alpha040/expressions/atom/status.rs | 0 .../grammar/alpha040/expressions/atom/text.rs | 0 .../grammar/alpha040/expressions/atom/var.rs | 0 .../src/grammar/alpha040/expressions/cast.rs | 0 .../alpha040/expressions/comparison.rs | 0 .../src/grammar/alpha040/expressions/is.rs | 0 .../src/grammar/alpha040/expressions/mod.rs | 0 .../src/grammar/alpha040/expressions/or.rs | 0 .../grammar/alpha040/expressions/product.rs | 0 .../src/grammar/alpha040/expressions/range.rs | 0 .../src/grammar/alpha040/expressions/sum.rs | 0 .../grammar/alpha040/expressions/ternary.rs | 0 .../src/grammar/alpha040/expressions/unary.rs | 0 {lsp => lib}/src/grammar/alpha040/global.rs | 0 {lsp => lib}/src/grammar/alpha040/lexer.rs | 0 {lsp => lib}/src/grammar/alpha040/mod.rs | 0 {lsp => lib}/src/grammar/alpha040/parser.rs | 0 .../src/grammar/alpha040/semantic_tokens.rs | 0 .../src/grammar/alpha040/statements/block.rs | 0 .../grammar/alpha040/statements/comment.rs | 0 .../grammar/alpha040/statements/const_init.rs | 0 .../src/grammar/alpha040/statements/failed.rs | 0 .../grammar/alpha040/statements/if_cond.rs | 0 .../grammar/alpha040/statements/keywords.rs | 0 .../src/grammar/alpha040/statements/loops.rs | 0 .../src/grammar/alpha040/statements/mod.rs | 0 .../grammar/alpha040/statements/modifiers.rs | 0 .../grammar/alpha040/statements/move_files.rs | 0 .../grammar/alpha040/statements/shebang.rs | 0 .../grammar/alpha040/statements/shorthands.rs | 0 .../grammar/alpha040/statements/var_init.rs | 0 .../grammar/alpha040/statements/var_set.rs | 0 {lsp => lib}/src/grammar/mod.rs | 0 {lsp => lib}/src/lib.rs | 0 {lsp => lib}/src/paths.rs | 0 {lsp => lib}/src/stdlib.rs | 0 {lsp => lib}/src/utils.rs | 0 lsp/Cargo.toml | 2 + lsp/src/main.rs | 2 +- 165 files changed, 1742 insertions(+), 2 deletions(-) create mode 100644 lib/Cargo.toml create mode 100644 lib/resources/alpha034/std/main.ab create mode 100644 lib/resources/alpha035/std/array.ab create mode 100644 lib/resources/alpha035/std/date.ab create mode 100644 lib/resources/alpha035/std/env.ab create mode 100644 lib/resources/alpha035/std/fs.ab create mode 100644 lib/resources/alpha035/std/http.ab create mode 100644 lib/resources/alpha035/std/math.ab create mode 100644 lib/resources/alpha035/std/text.ab create mode 100644 lib/resources/alpha040/builtin.ab create mode 100644 lib/resources/alpha040/std/array.ab create mode 100644 lib/resources/alpha040/std/date.ab create mode 100644 lib/resources/alpha040/std/env.ab create mode 100644 lib/resources/alpha040/std/fs.ab create mode 100644 lib/resources/alpha040/std/http.ab create mode 100644 lib/resources/alpha040/std/math.ab create mode 100644 lib/resources/alpha040/std/text.ab rename {lsp => lib}/src/analysis/alpha034/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha034/global.rs (100%) rename {lsp => lib}/src/analysis/alpha034/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha034/stmnts.rs (100%) rename {lsp => lib}/src/analysis/alpha035/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha035/global.rs (100%) rename {lsp => lib}/src/analysis/alpha035/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha035/stmnts.rs (100%) rename {lsp => lib}/src/analysis/alpha040/exp.rs (100%) rename {lsp => lib}/src/analysis/alpha040/global.rs (100%) rename {lsp => lib}/src/analysis/alpha040/mod.rs (100%) rename {lsp => lib}/src/analysis/alpha040/stmnts.rs (100%) rename {lsp => lib}/src/analysis/mod.rs (100%) rename {lsp => lib}/src/analysis/types.rs (100%) rename {lsp => lib}/src/backend.rs (100%) rename {lsp => lib}/src/files.rs (100%) rename {lsp => lib}/src/fs.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha034/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha034/global.rs (100%) rename {lsp => lib}/src/grammar/alpha034/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha034/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha034/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha034/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha035/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha035/global.rs (100%) rename {lsp => lib}/src/grammar/alpha035/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha035/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha035/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/move_files.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha035/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/and.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/array.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/bool.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/call.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/command.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/exit.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/null.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/number.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/parentheses.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/status.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/text.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/atom/var.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/cast.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/comparison.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/is.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/or.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/product.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/range.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/sum.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/ternary.rs (100%) rename {lsp => lib}/src/grammar/alpha040/expressions/unary.rs (100%) rename {lsp => lib}/src/grammar/alpha040/global.rs (100%) rename {lsp => lib}/src/grammar/alpha040/lexer.rs (100%) rename {lsp => lib}/src/grammar/alpha040/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/parser.rs (100%) rename {lsp => lib}/src/grammar/alpha040/semantic_tokens.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/block.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/comment.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/const_init.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/failed.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/if_cond.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/keywords.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/loops.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/mod.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/modifiers.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/move_files.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/shebang.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/shorthands.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/var_init.rs (100%) rename {lsp => lib}/src/grammar/alpha040/statements/var_set.rs (100%) rename {lsp => lib}/src/grammar/mod.rs (100%) rename {lsp => lib}/src/lib.rs (100%) rename {lsp => lib}/src/paths.rs (100%) rename {lsp => lib}/src/stdlib.rs (100%) rename {lsp => lib}/src/utils.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 7b64cc4..6e91637 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,9 @@ [workspace] resolver = "2" -members = ["lsp"] +members = ["lsp", "lib"] [workspace.dependencies] +lib = { path = "./lib" } tokio = "1.39.1" tower-lsp-server = "0.22.0" phf = "0.11" diff --git a/lib/Cargo.toml b/lib/Cargo.toml new file mode 100644 index 0000000..1c31ded --- /dev/null +++ b/lib/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "lib" +version = "0.1.10" +edition = "2021" + +[dependencies] +tokio = { workspace = true, features = ["full"] } +tower-lsp-server = { workspace = true } +phf = { workspace = true, features = ["macros"] } +dashmap = { workspace = true } +ropey = { workspace = true } +chumsky = { workspace = true, features = ["default", "label"] } +heraclitus-compiler = { workspace = true } +serde_json = { workspace = true } +rangemap = { workspace = true } +indexmap = { workspace = true } +clap = { workspace = true, features = ["derive"] } +include_dir = { workspace = true } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +rustc-hash = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +[dev-dependencies] +insta = { workspace = true, features = ["yaml"] } + +[build-dependencies] +fs_extra = "1.3.0" diff --git a/lib/resources/alpha034/std/main.ab b/lib/resources/alpha034/std/main.ab new file mode 100644 index 0000000..7c428f0 --- /dev/null +++ b/lib/resources/alpha034/std/main.ab @@ -0,0 +1,268 @@ +pub fun input(prompt: Text): Text { + unsafe $printf "\${nameof prompt}"$ + unsafe $read$ + return "$REPLY" +} + +pub fun replace_once(source, pattern, replacement) { + return unsafe $echo "\$\{source/{pattern}/{replacement}}"$ +} + +pub fun replace(source, pattern, replacement) { + return unsafe $echo "\$\{source//{pattern}/{replacement}}"$ +} + +pub fun replace_regex(source: Text, pattern: Text, replacement: Text): Text { + return unsafe $echo "{source}" | sed -e "s/{pattern}/{replacement}/g"$ +} + +pub fun dir_exist(path) { + $[ -d "{path}" ]$ failed { + return false + } + return true +} + +pub fun file_exist(path) { + $[ -f "{path}" ]$ failed { + return false + } + return true +} + +pub fun file_read(path) { + return $< "{path}"$? +} + +pub fun file_write(path, content) { + return $echo "{content}" > "{path}"$? +} + +pub fun file_append(path, content) { + return $echo "{content}" >> "{path}"$? +} + +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + unsafe $IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}")$ + return result +} + +pub fun lines(text: Text): [Text] { + let result = [Text] + unsafe $IFS=\$'\n' read -rd '' -a {nameof result} <<<"\${nameof text}"$ + return result +} + +pub fun words(text: Text): [Text] { + return split(text, " ") +} + +pub fun join(list: [Text], delimiter: Text): Text { + return unsafe $IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}"$ +} + +pub fun trim_left(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/^[[:space:]]*//'$ +} + +pub fun trim_right(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/[[:space:]]*\$//'$ +} + +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +pub fun lower(text: Text): Text { + return unsafe $echo "{text}" | tr '[:upper:]' '[:lower:]'$ +} + +pub fun upper(text: Text): Text { + return unsafe $echo "{text}" | tr '[:lower:]' '[:upper:]'$ +} + +#[allow_absurd_cast] +pub fun len(value): Num { + unsafe { + if value is Text: + return $echo "\$\{#{nameof value}}"$ as Num + else: + return $echo "\$\{#{nameof value}[@]}"$ as Num + } +} + +#[allow_absurd_cast] +pub fun parse(text: Text): Num { + $[ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null$? + return text as Num +} + +pub fun chars(text: Text): [Text] { + let chars = [Text] + unsafe $for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done;$ + return chars +} + +#[allow_absurd_cast] +pub fun sum(list: [Num]): Num { + return unsafe $echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}'$ as Num +} + +pub fun array_first_index(array, value): Num { + loop index, element in array { + if(value as Text == element as Text) { + return index + } + } + return -1 +} + +pub fun array_search(array, value): [Num] { + let result = [Num] + loop index, element in array { + if(value as Text == element as Text) { + result += [index] + } + } + return result +} + +pub fun in_array(array, value): Bool { + let result = array_first_index(array, value) + return result >= 0 +} + +pub fun has_failed(command: Text): Bool { + unsafe silent $eval {command}$ + return status != 0 +} + +pub fun exit(code: Num): Null { + unsafe $exit "{code}"$ +} + +pub fun includes(arr, value) { + loop v in arr { + if v == value { + return true + } + } + return false +} + +pub fun is_command(command: Text): Bool { + $[ -x "\$(command -v {command})" ]$ failed { + return false + } + return true +} + +pub fun create_symbolic_link(origin: Text, destination: Text): Bool { + if file_exist(origin) { + unsafe $ln -s "{origin}" "{destination}"$ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +pub fun create_dir(path: Text): Null { + if not dir_exist(path) { + unsafe $mkdir -p "{path}"$ + } +} + +pub fun make_executable(path: Text): Bool { + if file_exist(path) { + unsafe $chmod +x "{path}"$ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +pub fun change_owner(user: Text, path: Text): Bool { + if file_exist(path) or dir_exist(path) { + unsafe $chown -R "{user}" "{path}"$ + return true + } + + return false +} + +pub fun download(url: Text, path: Text): Bool { + if { + is_command("curl") { + unsafe $curl -L -o "{path}" "{url}"$ + } + is_command("wget") { + unsafe $wget "{url}" -P "{path}"$ + } + is_command("aria2c") { + unsafe $aria2c "{url}" -d "{path}"$ + } + else { + return false + } + } + + return true +} + +pub fun is_root(): Bool { + if (unsafe $id -u$ == "0") { + return true + } + + return false +} + +pub fun get_env_var(var: Text): Text { + let _var = unsafe $echo "\$\{!var}"$ + if _var != "" { + return _var + } + + if file_exist(".env") { + unsafe $source ".env"$ + return unsafe $echo "\$\{!var}"$ + } + + return "" +} + +pub fun load_env_file(): Null { + unsafe $export "\$(xargs < .env)" > /dev/null$ +} + +pub fun shell_isset(name: Text): Bool { + $[[ ! -z \$\{!{nameof name}+z} ]]$ failed { + return false + } + return true +} + +pub fun shell_constant_set(name: Text, val: Text): Null { + $readonly \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +pub fun shell_constant_get(name: Text): Text { + return $echo \$\{!{nameof name}}$? +} + +pub fun shell_var_set(name: Text, val: Text): Null { + $export \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +pub fun shell_var_get(name: Text): Text { + return $echo \$\{!{nameof name}}$? +} + +pub fun shell_unset(name: Text): Null { + $unset {name}$? +} diff --git a/lib/resources/alpha035/std/array.ab b/lib/resources/alpha035/std/array.ab new file mode 100644 index 0000000..4dd2b54 --- /dev/null +++ b/lib/resources/alpha035/std/array.ab @@ -0,0 +1,27 @@ +/// Returns index of the first value found in the specified array +/// If the value is not found, the function returns -1 +pub fun array_first_index(array, value): Num { + loop index, element in array { + if value as Text == element as Text { + return index + } + } + return -1 +} + +/// Search the value in array and return an array with the index of the various items +pub fun array_search(array, value): [Num] { + let result = [Num] + loop index, element in array { + if value as Text == element as Text { + result += [index] + } + } + return result +} + +/// Check if the value is in the array +pub fun includes(array, value) { + let result = array_first_index(array, value) + return result >= 0 +} diff --git a/lib/resources/alpha035/std/date.ab b/lib/resources/alpha035/std/date.ab new file mode 100644 index 0000000..27d01f5 --- /dev/null +++ b/lib/resources/alpha035/std/date.ab @@ -0,0 +1,109 @@ +/// EXPERIMENTAL +/// Format a date with a special format +/// If no date is specified, the current date is used +/// If no format is specified, "%FT%T%Z" format is used +/// For more info about format type "man date" on your shell or go to https://www.gnu.org/software/coreutils/date +/// Format : +/// %% a literal % +/// %a locale's abbreviated weekday name (e.g., Sun) +/// %A locale's full weekday name (e.g., Sunday) +/// %b locale's abbreviated month name (e.g., Jan) +/// %B locale's full month name (e.g., January) +/// %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005) +/// %C century; like %Y, except omit last two digits (e.g., 20) +/// %d day of month (e.g., 01) +/// %D date; same as %m/%d/%y +/// %e day of month, space padded; same as %_d +/// %F full date; like %+4Y-%m-%d +/// %g last two digits of year of ISO week number (see %G) +/// %G year of ISO week number (see %V); normally useful only with %V +/// %h same as %b +/// %H hour (00..23) +/// %I hour (01..12) +/// %j day of year (001..366) +/// %k hour, space padded ( 0..23); same as %_H +/// %l hour, space padded ( 1..12); same as %_I +/// %m month (01..12) +/// %M minute (00..59) +/// %n a newline +/// %N nanoseconds (000000000..999999999) +/// %p locale's equivalent of either AM or PM; blank if not known +/// %P like %p, but lower case +/// %q quarter of year (1..4) +/// %r locale's 12-hour clock time (e.g., 11:11:04 PM) +/// %R 24-hour hour and minute; same as %H:%M +/// %s seconds since the Epoch (1970-01-01 00:00 UTC) +/// %S second (00..60) +/// %t a tab +/// %T time; same as %H:%M:%S +/// %u day of week (1..7); 1 is Monday +/// %U week number of year, with Sunday as first day of week (00..53) +/// %V ISO week number, with Monday as first day of week (01..53) +/// %w day of week (0..6); 0 is Sunday +/// %W week number of year, with Monday as first day of week (00..53) +/// %x locale's date representation (e.g., 12/31/99) +/// %X locale's time representation (e.g., 23:13:48) +/// %y last two digits of year (00..99) +/// %Y year +/// %z +hhmm numeric time zone (e.g., -0400) +/// %:z +hh:mm numeric time zone (e.g., -04:00) +/// %::z +hh:mm:ss numeric time zone (e.g., -04:00:00) +/// %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30) +/// %Z alphabetic time zone abbreviation (e.g., EDT) +/// +/// By default, date pads numeric fields with zeroes. The following optional flags may follow '%': +/// +/// - (hyphen) do not pad the field +/// _ (underscore) pad with spaces +/// 0 (zero) pad with zeros +/// + pad with zeros, and put '+' before future years with >4 digits +/// ^ use upper case if possible +/// # use opposite case if possible +pub fun date_posix(format: Text = "", date: Text = "", utc: Bool = false): Text? { + if format == "": format = "%FT%T%Z" + if date == "": date = unsafe $date +"%FT%T%Z"$ + if (utc) { + return $date --utc -d "{date}" +"{format}"$? + } else { + return $date -d "{date}" +"{format}"$? + } +} + +/// Return current timestamp (seconds since the Epoch (1970-01-01 00:00 UTC)) +#[allow_absurd_cast] +pub fun now(): Num { + return unsafe $date +%s$ as Num +} + +/// EXPERIMENTAL +/// Add value to date. +/// If no date is specified, the current date is used +/// Ex : date_add("+3 days") +/// You can use : +/// (+/-) +/// years +/// months +/// days +/// hours +/// minutes +/// seconds +pub fun date_add(add:Text, date:Text = "", utc: Bool = false): Text? { + if date == "": date = unsafe $date +"%FT%T%Z"$ + return date_posix("", "{date_posix("%F", date, utc)?} {add} {date_posix("%T", date, utc)?}", utc)? +} + +/// EXPERIMENTAL +/// Compare 2 date +/// Return 1 if date_a is after date_b +/// Return 0 if date_a and date_b is the same +/// Return -1 if date_b is after date_a +/// If date_b is not provided, current date will be used +#[allow_absurd_cast] +pub fun date_compare(date_a: Text, date_b: Text = "", utc: Bool = false): Num? { + if date_b == "": date_b = unsafe date_posix("", "", utc) + let timestamp_a = date_posix("%s", date_a, utc)? as Num + let timestamp_b = date_posix("%s", date_b, utc)? as Num + if timestamp_a > timestamp_b: return 1 + if timestamp_a == timestamp_b: return 0 + if timestamp_a < timestamp_b: return -1 +} \ No newline at end of file diff --git a/lib/resources/alpha035/std/env.ab b/lib/resources/alpha035/std/env.ab new file mode 100644 index 0000000..3d1b997 --- /dev/null +++ b/lib/resources/alpha035/std/env.ab @@ -0,0 +1,160 @@ +import * from "std/fs" +import * from "std/text" + +/// Retrieves the value of an environment variable, optionally sourcing it from a file if not already set. +pub fun get_env_var(var: Text, file: Text = ".env"): Text { + let _var = unsafe $echo "\$\{!var}"$ + if _var != "" { + return _var + } + + if file_exist(file) { + unsafe $source "{file}"$ + return unsafe $echo "\$\{!var}"$ + } + + return "" +} + +/// Load the env file in the environment, using `xargs` +pub fun load_env_file(file: Text = ".env"): Null { + unsafe $export "\$(xargs < {file})" > /dev/null$ +} + +/// Check if a variable inside the Shell session exist +pub fun shell_isset(name: Text): Bool { + $[[ ! -z \$\{!{nameof name}+z} ]]$ failed { + return false + } + return true +} + +/// Set a constant inside the Shell session +pub fun shell_constant_set(name: Text, val: Text): Null? { + $readonly \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +/// Get a constant inside the Shell session +pub fun shell_constant_get(name: Text): Text? { + return $echo \$\{!{nameof name}}$? +} + +/// Set a constant inside the Shell session +pub fun shell_var_set(name: Text, val: Text): Null? { + $export \${nameof name}="\${nameof val}" 2> /dev/null$? +} + +/// Get a constant inside the Shell session +pub fun shell_var_get(name: Text): Text? { + return $echo \$\{!{nameof name}}$? +} + +/// Remove a variable inside the Shell session +pub fun shell_unset(name: Text): Null? { + $unset {name}$? +} + +/// Check if the command exist +pub fun is_command(command: Text): Bool { + $[ -x "\$(command -v {command})" ]$ failed { + return false + } + return true +} + +/// Create a prompt and return the value +pub fun input(prompt: Text): Text { + unsafe $printf "\${nameof prompt}"$ + unsafe $read$ + return "\$REPLY" +} + +/// Confirm prompt (Yes/No), return true if choice is Yes +/// "No" is the default choice, set default_yes to true for "Yes" as default choice +pub fun confirm(prompt: Text, default_yes: Bool = false): Bool { + let choice_default = default_yes then " [\x1b[1mY/\x1b[0mn]" else " [y/\x1b[1mN\x1b[0m]" + unsafe { + $printf "\x1b[1m{prompt}\x1b[0m{choice_default}"$ + $read -s -n 1$ + $printf "\n"$ + } + let result = lower(unsafe $echo \$REPLY$) + return result == "y" or (result == "" and default_yes) +} + +/// Checks if the command has failed +pub fun has_failed(command: Text): Bool { + unsafe silent $eval {command}$ + return status != 0 +} + +/// Close the script +pub fun exit(code: Num): Null { + unsafe $exit "{code}"$ +} + +/// Check if the script is running with a user with root permission +pub fun is_root(): Bool { + if unsafe $id -u$ == "0" { + return true + } + + return false +} + +/// `printf` the text following the arguments +pub fun printf(format: Text, args: [Text] = [""]): Null { + unsafe ${nameof args}=("{format}" "\$\{{nameof args}[@]}")$ + unsafe $printf "\$\{{nameof args}[@]}"$ +} + +/// Escape the text to be used with `printf` +pub fun printf_escape(text: Text): Text { + return unsafe $echo \${nameof text} | sed -e 's/\\\\/\\\\\\\\/g' -e "s/%/%%/g"$ +} + +/// Prepare a text with formatting options for `printf` +pub fun text_shell(message: Text, style: Num, fg: Num, bg: Num): Text { + return "\x1b[{style};{fg};{bg}m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as bold +pub fun text_bold(message: Text): Text { + return "\x1b[1m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as italic +pub fun text_italic(message: Text): Text { + return "\x1b[3m{printf_escape(message)}\x1b[0m" +} + +/// Return a text as underlined +pub fun text_underlined(message: Text): Text { + return "\x1b[4m{printf_escape(message)}\x1b[0m" +} + +/// Print a text with a specified color +pub fun color_echo(message: Text, color: Num): Null { + printf("\x1b[{color as Text}m%s\x1b[0m\n", [message]) +} + +/// Print a text as Info +pub fun echo_info(message: Text): Null { + printf("\x1b[1;3;97;44m %s \x1b[0m\n", [message]) +} + +/// Print a text as Success +pub fun echo_success(message: Text): Null { + printf("\x1b[1;3;97;42m %s \x1b[0m\n", [message]) +} + +/// Print a text as Warning +pub fun echo_warning(message: Text): Null { + printf("\x1b[1;3;97;43m %s \x1b[0m\n", [message]) +} + +/// Print a text as Error and exit if the status code is greater than 0 +pub fun error(message: Text, exit_code: Num = 1): Null { + printf("\x1b[1;3;97;41m %s \x1b[0m\n", [message]) + if exit_code > 0 : exit(exit_code) +} diff --git a/lib/resources/alpha035/std/fs.ab b/lib/resources/alpha035/std/fs.ab new file mode 100644 index 0000000..dd14918 --- /dev/null +++ b/lib/resources/alpha035/std/fs.ab @@ -0,0 +1,74 @@ +/// Check if directory exists +pub fun dir_exist(path) { + $[ -d "{path}" ]$ failed { + return false + } + return true +} + +/// Check if file exists +pub fun file_exist(path) { + $[ -f "{path}" ]$ failed { + return false + } + return true +} + +/// Get the file content +pub fun file_read(path) { + return $< "{path}"$? +} + +/// Write the content to the file +/// Doesn't check if the file exist +pub fun file_write(path, content) { + return $echo "{content}" > "{path}"$? +} + +/// Append the content to the file +/// Doesn't check if the file exist +pub fun file_append(path, content) { + return $echo "{content}" >> "{path}"$? +} + +/// Create a symbolic link +/// If the file doens't exist return a boolean and print a message +pub fun create_symbolic_link(origin: Text, destination: Text): Bool { + if file_exist(origin) { + unsafe $ln -s "{origin}" "{destination}"$ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +/// Create a directory with all intermediate directories as required +pub fun create_dir(path: Text): Null { + if not dir_exist(path) { + unsafe $mkdir -p "{path}"$ + } +} + +/// Set the file as executable +/// If the file doesn't exist return a boolean and print a message +pub fun make_executable(path: Text): Bool { + if file_exist(path) { + unsafe $chmod +x "{path}"$ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Change the owner of the file +/// If the file doesn't exist return false +pub fun change_owner(user: Text, path: Text): Bool { + if file_exist(path) or dir_exist(path) { + unsafe $chown -R "{user}" "{path}"$ + return true + } + + return false +} diff --git a/lib/resources/alpha035/std/http.ab b/lib/resources/alpha035/std/http.ab new file mode 100644 index 0000000..439285f --- /dev/null +++ b/lib/resources/alpha035/std/http.ab @@ -0,0 +1,25 @@ +import * from "std/env" + +/// Downloads a file from a given URL and saves it to a specified path using available command-line tools. +/// +/// This function attempts to download a file from the provided URL and save it to the specified path. +/// It checks for the availability of common command-line tools (`curl`, `wget`, and `aria2c`) and uses the first available tool to perform the download. +/// If none of the tools are available, the function returns `false`. +pub fun download(url: Text, path: Text): Bool { + if { + is_command("curl") { + unsafe $curl -L -o "{path}" "{url}"$ + } + is_command("wget") { + unsafe $wget "{url}" -P "{path}"$ + } + is_command("aria2c") { + unsafe $aria2c "{url}" -d "{path}"$ + } + else { + return false + } + } + + return true +} diff --git a/lib/resources/alpha035/std/math.ab b/lib/resources/alpha035/std/math.ab new file mode 100644 index 0000000..db3f6b9 --- /dev/null +++ b/lib/resources/alpha035/std/math.ab @@ -0,0 +1,34 @@ +/// Sum the array content +#[allow_absurd_cast] +pub fun sum(list: [Num]): Num { + return unsafe $echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}'$ as Num +} + +/// Returns the number rounded to the nearest integer +#[allow_absurd_cast] +pub fun round(number: Num): Num { + if number > 0 { + return unsafe $echo "({number}+0.5)/1" | bc$ as Num + } + + return unsafe $echo "({number}-0.5)/1" | bc$ as Num +} + +/// Returns the largest integer less than or equal to the number +#[allow_absurd_cast] +pub fun floor(number: Num): Num { + return unsafe $echo "{number}" | awk '\{printf "%d", (\$1 < 0 ? int(\$1) - 1 : int(\$1))}'$ as Num +} + +/// Returns the smallest integer greater than or equal to the number +#[allow_absurd_cast] +pub fun ceil(number: Num): Num { + return floor(number) + 1 +} + +/// Returns the absolute value of the number +#[allow_absurd_cast] +pub fun abs(number: Num): Num { + if number < 0: return -number + return number +} diff --git a/lib/resources/alpha035/std/text.ab b/lib/resources/alpha035/std/text.ab new file mode 100644 index 0000000..27b1b0d --- /dev/null +++ b/lib/resources/alpha035/std/text.ab @@ -0,0 +1,164 @@ +/// Finds the first occurrence of a pettern in the content and replaces it with provided replacement text +pub fun replace_once(source, pattern, replacement) { + return "\$\{source/{pattern}/{replacement}}" +} + +/// Replaces all occurences of a pattern in the content with provided replacement text +pub fun replace(source, pattern, replacement) { + return "\$\{source//{pattern}/{replacement}}" +} + +/// Replaces all occurences of a regex pattern in the content with provided replacement text +/// +/// Function uses `sed` +pub fun replace_regex(source: Text, pattern: Text, replacement: Text): Text { + return unsafe $echo "{source}" | sed -e "s/{pattern}/{replacement}/g"$ +} + +/// This function splits the input `text` into an array of substrings using the specified `delimiter`. +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + unsafe $IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}")$ + return result +} + +/// Splits a `text` into an array of substrings based on newline characters. +pub fun lines(text: Text): [Text] { + return split(text, "\n") +} + +/// Splits a `text` into an array of substrings based on space character. +pub fun words(text: Text): [Text] { + return split(text, " ") +} + +/// Merge a text using the delimeter specified +pub fun join(list: [Text], delimiter: Text): Text { + return unsafe $IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}"$ +} + +/// Trim the spaces at top of the text using `sed` +pub fun trim_left(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/^[[:space:]]*//'$ +} + +/// Trim the spaces at end of the text using `sed` +pub fun trim_right(text: Text): Text { + return unsafe $echo "{text}" | sed -e 's/[[:space:]]*\$//'$ +} + +/// Trim the spaces from the text input +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +/// Lowercase the text input using `tr` +pub fun lower(text: Text): Text { + return unsafe $echo "{text}" | tr '[:upper:]' '[:lower:]'$ +} + +/// Lowercase the text input using `tr` +pub fun upper(text: Text): Text { + return unsafe $echo "{text}" | tr '[:lower:]' '[:upper:]'$ +} + +/// Attempts to parse a given text into a number, returning the parsed number or zero if parsing fails. +#[allow_absurd_cast] +pub fun parse(text: Text): Num? { + $[ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null$? + return text as Num +} + +/// Splits a text into an array of individual characters. +pub fun chars(text: Text): [Text] { + let chars = [Text] + unsafe $for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done;$ + return chars +} + +/// Get the text or array length +#[allow_absurd_cast] +pub fun len(value): Num { + unsafe { + if value is Text: + return $echo "\$\{#{nameof value}}"$ as Num + else: + return $echo "\$\{#{nameof value}[@]}"$ as Num + } +} + +/// Check if text contain the value +pub fun contains(text: Text, phrase: Text): Bool { + let result = unsafe $if [[ "{text}" == *"{phrase}"* ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Reverse a text using `rev` +pub fun reverse(text: Text): Text { + return unsafe $echo "{text}" | rev$ +} + +/// Check if text starts with a value +pub fun starts_with(text: Text, prefix: Text): Bool { + let result = unsafe $if [[ "{text}" == "{prefix}"* ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Check if text ends with a value +pub fun ends_with(text: Text, suffix: Text): Bool { + let result = unsafe $if [[ "{text}" == *"{suffix}" ]]; then + echo 1 + fi$ + + return result == "1" +} + +/// Returns a substring from `text` starting at the given `index` (0-based). +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +/// If `length` is provided, the substring will include `length` characters; otherwise, it slices to the end of `text`. +/// If `length` is negative, an empty string is returned. +pub fun slice(text: Text, index: Num, length: Num = 0): Text { + if length == 0: length = len(text) - index + if length <= 0: return "" + return unsafe $printf "%.{length}s" "\$\{text:{index}}"$ +} + +/// Returns the character from `text` at the specified `index` (0-based). +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +pub fun char_at(text: Text, index: Num): Text { + return unsafe $printf "%.1s" "\$\{text:{index}}"$ +} + +/// Capitalize the first letter of the given `text` +pub fun capitalize(text: Text): Text { + return unsafe $echo "{text}" | sed "s/^\(.\)/\U\1/"$ +} + +/// Pads `text` with the specified `pad` character on left until it reaches the desired `length`. +pub fun lpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = unsafe $printf "%{length}s" "" | tr " " "{pad}"$ + return pad + text +} + +/// Pads `text` with the specified `pad` character on the right until it reaches the desired `length`. +pub fun rpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = unsafe $printf "%{length}s" "" | tr " " "{pad}"$ + return text + pad +} + +/// Pads `text` with zeros on the left until it reaches the desired `length`. +pub fun zfill(text: Text, length: Num): Text { + return lpad(text, "0", length) +} diff --git a/lib/resources/alpha040/builtin.ab b/lib/resources/alpha040/builtin.ab new file mode 100644 index 0000000..6f310fe --- /dev/null +++ b/lib/resources/alpha040/builtin.ab @@ -0,0 +1,38 @@ +/// This builtin reads one line at a time from a text file. +/// It can be used in place of an array in an iterative for loop (with or without an index). +/// This is efficient because each line is read into memory, and processed before the next line is read: +/// +/// ```amber +/// for line in lines("foo.txt") { +/// echo line +/// } +/// +/// for index, line in lines("bar.txt") { +/// echo "\#{index} {line}" +/// } +/// ``` +/// +/// Alternatively, it can be used as the right hand side of an array assignment. +/// This is inefficient because the entire file is read into memory in one go: +/// +/// ```amber +/// let lines = lines("foo.txt") +/// lines += lines("bar.txt") +/// echo len(lines) +/// ``` +pub fun lines(path: Text): [Text] {} + +/// For a `Text` value, this builtin calculates and returns the length (in ASCII characters) as a Num type. +/// It is transpiled to `${#TEXT}`: +/// ```amber +/// // Returns 37 +/// echo len("Jackdaws love my big sphinx of quartz") +/// ``` +/// For an `Array` `[]` value, it calculates and returns the length of the array as a Num type. +/// It is transpiled to `${#ARRAY[@]}`: +/// +/// ```amber +/// // Returns 5 +/// echo len(["one", "two", "three", "four", "five"]) +/// ``` +pub fun len(value): Num {} \ No newline at end of file diff --git a/lib/resources/alpha040/std/array.ab b/lib/resources/alpha040/std/array.ab new file mode 100644 index 0000000..d349b7a --- /dev/null +++ b/lib/resources/alpha040/std/array.ab @@ -0,0 +1,80 @@ +/// Returns index of the first value found in the specified array. +/// +/// If the value is not found, the function returns -1. +pub fun array_find(array, value): Num { + for index, element in array { + if value as Text == element as Text { + return index + } + } + return -1 +} + +/// Searches for a value in an array and returns an array with the index of the various items. +pub fun array_find_all(array, value): [Num] { + let result = [Num] + for index, element in array { + if value as Text == element as Text { + result += [index] + } + } + return result +} + +/// Checks if a value is in the array. +pub fun array_contains(array, value) { + let result = array_find(array, value) + return result >= 0 +} + +/// Returns the first element in the array; if the array is empty, the return +/// value is undefined. +pub fun array_first(array) { + return array[0] +} + +/// Returns the last element in the array; if the array is empty, the return +/// value is undefined. +pub fun array_last(array) { + let index = len(array) - 1 + return array[index] +} + +/// Removes an element at the index from the array; if the index is negative +/// or beyond the end, the return value is undefined, but the array will be +/// unchanged. +pub fun array_remove_at(ref array: [], index: Num): Null { + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] +} + +/// Removes an element at the index from the array, and returns it; if the +/// index is negative or beyond the end, the return value is undefined, but +/// the array will be unchanged. +pub fun array_extract_at(ref array, index) { + let element = array[index] + let offset = index + 1 + let length = len(array) + array = array[0..index] + array[offset..length] + return element +} + +/// Removes the last element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun array_pop(ref array) { + let length = len(array) + let index = length - 1 + let element = array[index] + array = array[0..index] + return element +} + +/// Removes the first element from the array, and returns it; if the array +/// is empty, the return value is undefined, but the array will be unchanged. +pub fun array_shift(ref array) { + let length = len(array) + let element = array[0] + array = array[1..length] + return element +} diff --git a/lib/resources/alpha040/std/date.ab b/lib/resources/alpha040/std/date.ab new file mode 100644 index 0000000..15c8d58 --- /dev/null +++ b/lib/resources/alpha040/std/date.ab @@ -0,0 +1,126 @@ +/// ### EXPERIMENTAL +/// +/// Formats a date with a special format. +/// +/// If no date is specified, the current date is used. +/// +/// If no format is specified, "%FT%T%Z" format is used. +/// +/// For more info about format type "man date" on your shell or go to . +/// +/// Format : +/// ``` +/// %% a literal % +/// %a locale's abbreviated weekday name (e.g., Sun) +/// %A locale's full weekday name (e.g., Sunday) +/// %b locale's abbreviated month name (e.g., Jan) +/// %B locale's full month name (e.g., January) +/// %c locale's date and time (e.g., Thu Mar 3 23:05:25 2005) +/// %C century; like %Y, except omit last two digits (e.g., 20) +/// %d day of month (e.g., 01) +/// %D date; same as %m/%d/%y +/// %e day of month, space padded; same as %_d +/// %F full date; like %+4Y-%m-%d +/// %g last two digits of year of ISO week number (see %G) +/// %G year of ISO week number (see %V); normally useful only with %V +/// %h same as %b +/// %H hour (00..23) +/// %I hour (01..12) +/// %j day of year (001..366) +/// %k hour, space padded ( 0..23); same as %_H +/// %l hour, space padded ( 1..12); same as %_I +/// %m month (01..12) +/// %M minute (00..59) +/// %n a newline +/// %N nanoseconds (000000000..999999999) +/// %p locale's equivalent of either AM or PM; blank if not known +/// %P like %p, but lower case +/// %q quarter of year (1..4) +/// %r locale's 12-hour clock time (e.g., 11:11:04 PM) +/// %R 24-hour hour and minute; same as %H:%M +/// %s seconds since the Epoch (1970-01-01 00:00 UTC) +/// %S second (00..60) +/// %t a tab +/// %T time; same as %H:%M:%S +/// %u day of week (1..7); 1 is Monday +/// %U week number of year, with Sunday as first day of week (00..53) +/// %V ISO week number, with Monday as first day of week (01..53) +/// %w day of week (0..6); 0 is Sunday +/// %W week number of year, with Monday as first day of week (00..53) +/// %x locale's date representation (e.g., 12/31/99) +/// %X locale's time representation (e.g., 23:13:48) +/// %y last two digits of year (00..99) +/// %Y year +/// %z +hhmm numeric time zone (e.g., -0400) +/// %:z +hh:mm numeric time zone (e.g., -04:00) +/// %::z +hh:mm:ss numeric time zone (e.g., -04:00:00) +/// %:::z numeric time zone with : to necessary precision (e.g., -04, +05:30) +/// %Z alphabetic time zone abbreviation (e.g., EDT) +/// ``` +/// +/// By default, date pads numeric fields with zeroes. The following optional flags may follow '%': +/// +/// ``` +/// - (hyphen) do not pad the field +/// _ (underscore) pad with spaces +/// 0 (zero) pad with zeros +/// + pad with zeros, and put '+' before future years with >4 digits +/// ^ use upper case if possible +/// # use opposite case if possible +/// ``` +pub fun date_posix(format: Text = "", date: Text = "", utc: Bool = false): Text? { + if format == "": format = "%FT%T%Z" + if date == "": date = trust $ date +"%FT%T%Z" $ + if (utc) { + return $ date --utc -d "{date}" +"{format}" $? + } else { + return $ date -d "{date}" +"{format}" $? + } +} + +/// Returns the current timestamp (seconds since the Epoch (1970-01-01 00:00 UTC)). +#[allow_absurd_cast] +pub fun date_now(): Num { + return trust $ date +%s $ as Num +} + +/// ### EXPERIMENTAL +/// +/// Adds a value to a date. +/// +/// If no date is specified, the current date is used. +/// +/// Example : `date_add("+3 days")` +/// +/// You can use (+/-): +/// +/// - years +/// - months +/// - days +/// - hours +/// - minutes +/// - seconds +pub fun date_add(add:Text, date:Text = "", utc: Bool = false): Text? { + if date == "": date = trust $ date +"%FT%T%Z" $ + return date_posix("", "{date_posix("%F", date, utc)?} {add} {date_posix("%T", date, utc)?}", utc)? +} + +/// ### EXPERIMENTAL +/// Compares two dates. +/// +/// Returns 1 if date_a is after date_b. +/// +/// Returns 0 if date_a and date_b is the same. +/// +/// Returns -1 if date_b is after date_a. +/// +/// If date_b is not provided, current date will be used. +#[allow_absurd_cast] +pub fun date_compare(date_a: Text, date_b: Text = "", utc: Bool = false): Num? { + if date_b == "": date_b = trust date_posix("", "", utc) + let timestamp_a = date_posix("%s", date_a, utc)? as Num + let timestamp_b = date_posix("%s", date_b, utc)? as Num + if timestamp_a > timestamp_b: return 1 + if timestamp_a == timestamp_b: return 0 + if timestamp_a < timestamp_b: return -1 +} diff --git a/lib/resources/alpha040/std/env.ab b/lib/resources/alpha040/std/env.ab new file mode 100644 index 0000000..b24b0b7 --- /dev/null +++ b/lib/resources/alpha040/std/env.ab @@ -0,0 +1,164 @@ +import * from "std/fs" +import * from "std/text" + +/// Retrieves the value of an environment variable, optionally sourcing it from a file if not already set. +pub fun env_var_load(var: Text, file: Text = ".env"): Text { + let _var = trust $ echo "\$\{!var}" $ + if _var != "" { + return _var + } + + if file_exists(file) { + trust $ source "{file}" $ + return trust $ echo "\$\{!var}" $ + } + + return "" +} + +/// Loads the env file in the environment, using `xargs`. +pub fun env_file_load(file: Text = ".env"): Null { + trust $ export "\$(xargs < {file})" > /dev/null $ +} + +/// Checks if a variable inside the shell session exists. +pub fun env_var_test(name: Text): Bool { + $ [[ ! -z \$\{!{nameof name}+z} ]] $ failed { + return false + } + return true +} + +/// Sets a constant inside the shell session. +pub fun env_const_set(name: Text, val: Text): Null? { + $ readonly \${nameof name}="\${nameof val}" 2> /dev/null $? +} + +/// Gets a constant inside the shell session. +pub fun env_const_get(name: Text): Text? { + return $ echo \$\{!{nameof name}} $? +} + +/// Sets a constant inside the shell session. +pub fun env_var_set(name: Text, val: Text): Null? { + $ export \${nameof name}="\${nameof val}" 2> /dev/null $? +} + +/// Gets a constant inside the shell session. +pub fun env_var_get(name: Text): Text? { + return $ echo \$\{!{nameof name}} $? +} + +/// Removes a variable inside the shell session. +pub fun env_var_unset(name: Text): Null? { + $ unset {name} $? +} + +/// Checks if a command exists. +pub fun is_command(command: Text): Bool { + $ [ -x "\$(command -v {command})" ] $ failed { + return false + } + return true +} + +/// Creates a prompt and returns the value. +pub fun input_prompt(prompt: Text): Text { + trust $ read -p "\${nameof prompt}" $ + return "\$REPLY" +} + +/// Creates a prompt, hides any user input and returns the value. +pub fun input_hidden(prompt: Text): Text { + trust { + $ read -s -p "\${nameof prompt}" $ + $ echo "" >&2 $ + } + return "\$REPLY" +} + +/// Creates a confirm prompt (Yes/No), and returns true if the choice is Yes. +/// +/// "No" is the default choice, set default_yes to true for "Yes" as default choice. +pub fun input_confirm(prompt: Text, default_yes: Bool = false): Bool { + let choice_default = default_yes then " [\x1b[1mY/\x1b[0mn]" else " [y/\x1b[1mN\x1b[0m]" + trust { + $ printf "\x1b[1m{prompt}\x1b[0m{choice_default}" $ + $ read -s -n 1 $ + $ printf "\n" $ + } + let result = lowercase(trust $ echo \$REPLY $) + return result == "y" or (result == "" and default_yes) +} + +/// Checks if the command has failed. +pub fun has_failed(command: Text): Bool { + trust silent $ eval {command} $ + return status != 0 +} + +/// Checks if the script is running with a user with root permission. +pub fun is_root(): Bool { + if trust $ id -u $ == "0" { + return true + } + + return false +} + +/// `printf` the text following the arguments. +pub fun printf(format: Text, args: [Text] = [""]): Null { + trust $ {nameof args}=("{format}" "\$\{{nameof args}[@]}") $ + trust $ printf "\$\{{nameof args}[@]}" $ +} + +/// Escapes the text to be used with `printf`. +pub fun escaped(text: Text): Text { + return trust $ echo \${nameof text} | sed -e 's/\\\\/\\\\\\\\/g' -e "s/%/%%/g" $ +} + +/// Prepares a text with formatting options for `printf`. +pub fun styled(message: Text, style: Num, fg: Num, bg: Num): Text { + return "\x1b[{style};{fg};{bg}m{escaped(message)}\x1b[0m" +} + +/// Returns a text as bold. +pub fun bold(message: Text): Text { + return "\x1b[1m{escaped(message)}\x1b[0m" +} + +/// Returns a text as italic. +pub fun italic(message: Text): Text { + return "\x1b[3m{escaped(message)}\x1b[0m" +} + +/// Returns a text as underlined. +pub fun underlined(message: Text): Text { + return "\x1b[4m{escaped(message)}\x1b[0m" +} + +/// Prints a text with a specified color. +pub fun echo_colored(message: Text, color: Num): Null { + printf("\x1b[{color as Text}m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a info message. +pub fun echo_info(message: Text): Null { + printf("\x1b[1;3;97;44m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a success message. +pub fun echo_success(message: Text): Null { + printf("\x1b[1;3;97;42m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a warning message. +pub fun echo_warning(message: Text): Null { + printf("\x1b[1;3;97;43m%s\x1b[0m\n", [message]) +} + +/// Prints a text as a error and exits if the status code is greater than 0. +pub fun echo_error(message: Text, exit_code: Num = 1): Null { + printf("\x1b[1;3;97;41m%s\x1b[0m\n", [message]) + if exit_code > 0 : exit(exit_code) +} diff --git a/lib/resources/alpha040/std/fs.ab b/lib/resources/alpha040/std/fs.ab new file mode 100644 index 0000000..47235b8 --- /dev/null +++ b/lib/resources/alpha040/std/fs.ab @@ -0,0 +1,140 @@ +import { match_regex, join, replace_regex, split } from "std/text" + +/// Checks if a directory exists. +pub fun dir_exists(path) { + $ [ -d "{path}" ] $ failed { + return false + } + return true +} + +/// Checks if a file exists. +pub fun file_exists(path) { + $ [ -f "{path}" ] $ failed { + return false + } + return true +} + +/// Gets file contents from a path. +pub fun file_read(path) { + return $ < "{path}" $? +} + +/// Writes content to a file. +/// Doesn't check if the file exist +pub fun file_write(path, content) { + return $ echo "{content}" > "{path}" $? +} + +/// Appends content to a file. +/// +/// Doesn't check if the file exists. +pub fun file_append(path, content) { + return $ echo "{content}" >> "{path}" $? +} + +/// Creates a symbolic link. +/// +/// If the file doesn't exist, it returns a boolean and prints a message. +pub fun symlink_create(origin: Text, destination: Text): Bool { + if file_exists(origin) { + trust $ ln -s "{origin}" "{destination}" $ + return true + } + + echo "The file {origin} doesn't exist!" + return false +} + +/// Creates a directory with all parent directories as required. +pub fun dir_create(path: Text): Null { + if not dir_exists(path) { + trust $ mkdir -p "{path}" $ + } +} + +/// Sets a file as executable. +/// +/// If the file doesn't exist, it returns a boolean and prints a message. +pub fun file_chmod(path: Text, mode: Text): Bool { + if file_exists(path) { + trust $ chmod "{mode}" "{path}" $ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Changes the owner of a file. +/// +/// If the file doesn't exist, it returns `false` +pub fun file_chown(path: Text, user: Text): Bool { + if file_exists(path) or dir_exists(path) { + trust $ chown -R "{user}" "{path}" $ + return true + } + + echo "The file {path} doesn't exist!" + return false +} + +/// Escapes all characters in the passed-in glob except "*", "?" and "/", +/// to prevent injection attacks. +fun escape_non_glob_chars(path: Text): Text { + return replace_regex(path, "\([^*?/]\)", "\\\\\1") +} + +/// Finds all files or directories matching multiple file globs. When +/// we have union types, this functionality can be merged into the main +/// `glob` function. +pub fun file_glob_all(paths: [Text]): [Text]? { + let combined = "" + if len(paths) == 1 { + combined = escape_non_glob_chars(paths[0]) + } else { + let items = [Text] + for item in paths { + item = escape_non_glob_chars(item) + items += [item] + } + combined = join(items, " ") + } + let files = $ eval "for file in {combined}; do [ -e \\\"\\\$file\\\" ] && echo \\\"\\\$file\\\"; done" $? + return split(files, "\n") +} + +/// Finds all files or directories matching a file glob. +pub fun file_glob(path: Text): [Text]? { + return file_glob_all([path])? +} + +/// Extract the file detecting from the filename the extension +/// Supports: bz2, gz, xz, bz2, deb, rar, rpm, tar(gz/xz/bz), zip(war/jar), 7z +/// Note: Not all the commands supports the output folder path +pub fun file_extract(path: Text, target: Text): Null? { + if file_exists(path) { + if { + match_regex(path, "\.\(tar\.bz2\|tbz\|tbz2\)$"): $ tar xvjf "{path}" -C "{target}" $? + match_regex(path, "\.\(tar\.gz\|tgz\)$"): $ tar xzf "{path}" -C "{target}" $? + match_regex(path, "\.\(tar\.xz\|txz$\)$"): $ tar xJf "{path}" -C "{target}" $? + match_regex(path, "\.bz2$"): $ bunzip2 "{path}" $? + match_regex(path, "\.deb$"): $ dpkg-deb -xv "{path}" "{target}" $? + match_regex(path, "\.gz$"): $ gunzip "{path}" $? + match_regex(path, "\.rar$"): $ unrar x "{path}" "{target}" $? + match_regex(path, "\.rpm$"): $ rpm2cpio "{path}" | cpio -idm $? + match_regex(path, "\.tar$"): $ tar xf "{path}" -C "{target}" $? + match_regex(path, "\.xz$"): $ xz --decompress "{path}" $? + match_regex(path, "\.7z$"): $ 7z -y "{path}" -o "{target}" $? + match_regex(path, "\.\(zip\|war\|jar\)$"): $ unzip "{path}" -d "{target}" $? + else { + echo "Error: Unsupported file type" + fail 3 + } + } + } else { + echo "Error: File not found" + fail 2 + } +} diff --git a/lib/resources/alpha040/std/http.ab b/lib/resources/alpha040/std/http.ab new file mode 100644 index 0000000..c1dbfa8 --- /dev/null +++ b/lib/resources/alpha040/std/http.ab @@ -0,0 +1,24 @@ +import * from "std/env" + +/// Downloads a file from a given URL and saves it to a specified path using available command-line tools. +/// +/// It checks for the availability of common command-line tools (`curl`, `wget`, and `aria2c`, in order) and uses the first available tool to perform the download. +/// If none of the tools are available, the function returns `false`. +pub fun file_download(url: Text, path: Text): Bool { + if { + is_command("curl") { + trust $ curl -L -o "{path}" "{url}" $ + } + is_command("wget") { + trust $ wget "{url}" -P "{path}" $ + } + is_command("aria2c") { + trust $ aria2c "{url}" -d "{path}" $ + } + else { + return false + } + } + + return true +} diff --git a/lib/resources/alpha040/std/math.ab b/lib/resources/alpha040/std/math.ab new file mode 100644 index 0000000..793b312 --- /dev/null +++ b/lib/resources/alpha040/std/math.ab @@ -0,0 +1,34 @@ +/// Sums an array's contents +#[allow_absurd_cast] +pub fun math_sum(list: [Num]): Num { + return trust $ echo "{list}" | awk '\{s=0; for (i=1; i<=NF; i++) s+=\$i; print s}' $ as Num +} + +/// Returns a number, rounded to the nearest integer +#[allow_absurd_cast] +pub fun math_round(number: Num): Num { + if number > 0 { + return trust $ echo "({number}+0.5)/1" | bc $ as Num + } + + return trust $ echo "({number}-0.5)/1" | bc $ as Num +} + +/// Returns the largest integer less than or equal to a number +#[allow_absurd_cast] +pub fun math_floor(number: Num): Num { + return trust $ echo "{number}" | awk '\{printf "%d", (\$1 < 0 ? int(\$1) - 1 : int(\$1))}' $ as Num +} + +/// Returns the smallest integer greater than or equal to a number +#[allow_absurd_cast] +pub fun math_ceil(number: Num): Num { + return math_floor(number) + 1 +} + +/// Returns the absolute value of a number +#[allow_absurd_cast] +pub fun math_abs(number: Num): Num { + if number < 0: return -number + return number +} diff --git a/lib/resources/alpha040/std/text.ab b/lib/resources/alpha040/std/text.ab new file mode 100644 index 0000000..fe071e3 --- /dev/null +++ b/lib/resources/alpha040/std/text.ab @@ -0,0 +1,241 @@ +/// Replaces all occurences of a pattern in the content with the provided replace text. +pub fun replace(source, search, replace) { + return "\$\{source//{search}/{replace}}" +} + +/// Replaces the first occurence of a pattern in the content with the provided replace text. +pub fun replace_one(source, search, replace) { + return "\$\{source/{search}/{replace}}" +} + +/// Replaces all occurences of a regex pattern in the content with the provided replace text. +/// +/// Function uses `sed` +pub fun replace_regex(source: Text, search: Text, replace: Text, extended: Bool = false): Text { + trust { + search = replace(search, "/", "\/") + replace = replace(replace, "/", "\/") + if extended { + // GNU sed versions 4.0 through 4.2 support extended regex syntax, + // but only via the "-r" option; use that if the version information + // contains "GNU sed". + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + let flag = status == 0 then "-r" else "-E" + return $ echo "{source}" | sed "{flag}" -e "s/{search}/{replace}/g" $ + } else { + return $ echo "{source}" | sed -e "s/{search}/{replace}/g" $ + } + } +} + +/// Splits the input `text` into an array of substrings using the specified `delimiter`. +pub fun split(text: Text, delimiter: Text): [Text] { + let result = [Text] + trust $ IFS="{delimiter}" read -rd '' -a {nameof result} < <(printf %s "\${nameof text}") $ + return result +} + +/// Splits a `text` into an array of substrings based on newline characters. +pub fun split_lines(text: Text): [Text] { + return split(text, "\n") +} + +/// Splits a `text` into an array of substrings based on space character. +pub fun split_words(text: Text): [Text] { + return split(text, " ") +} + +/// Merges text using the delimeter specified. +pub fun join(list: [Text], delimiter: Text): Text { + return trust $ IFS="{delimiter}" ; echo "\$\{{nameof list}[*]}" $ +} + +/// Trims the spaces at top of the text using `sed`. +pub fun trim_left(text: Text): Text { + return trust $ echo "{text}" | sed -e 's/^[[:space:]]*//' $ +} + +/// Trims the spaces at end of the text using `sed`. +pub fun trim_right(text: Text): Text { + return trust $ echo "{text}" | sed -e 's/[[:space:]]*\$//' $ +} + +/// Trims the spaces from the text input. +pub fun trim(text: Text): Text { + return trim_left(trim_right(text)) +} + +/// Makes the text input lowercase using `tr`. +pub fun lowercase(text: Text): Text { + return trust $ echo "{text}" | tr '[:upper:]' '[:lower:]' $ +} + +/// Makes the text input uppercase using `tr`. +pub fun uppercase(text: Text): Text { + return trust $ echo "{text}" | tr '[:lower:]' '[:upper:]' $ +} + +/// Attempts to parse a given text into a number, returning the parsed number or zero if parsing fails. +#[allow_absurd_cast] +pub fun parse_number(text: Text): Num? { + $ [ -n "{text}" ] && [ "{text}" -eq "{text}" ] 2>/dev/null $? + return text as Num +} + +/// Splits a text into an array of individual characters. +pub fun split_chars(text: Text): [Text] { + let chars = [Text] + trust $ for ((i=0; i<\$\{#{nameof text}}; i++)); do + {nameof chars}+=( "\$\{{nameof text}:\$i:1}" ); + done $ + return chars +} + +/// Checks if some text contains a value. +pub fun text_contains(text: Text, phrase: Text): Bool { + let result = trust $ if [[ "{text}" == *"{phrase}"* ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Checks if an array value is in the text. +pub fun text_contains_any(text: Text, terms: [Text]): Bool { + for term in terms { + if text_contains(text, term) { + return true + } + } + + return false +} + +/// Checks if all the arrays values are in the string +pub fun text_contains_all(text: Text, terms: [Text]): Bool { + for term in terms { + if not text_contains(text, term) { + return false + } + } + + return true +} + +/// Match all occurences of a regex pattern. +/// +/// Function uses `sed` +pub fun match_regex(source: Text, search: Text, extended: Bool = false): Bool { + trust { + search = replace(search, "/", "\/") + let output = "" + if extended { + // GNU sed versions 4.0 through 4.2 support extended regex syntax, + // but only via the "-r" option; use that if the version information + // contains "GNU sed". + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + let flag = status == 0 then "-r" else "-E" + output = $ echo "{source}" | sed "{flag}" -ne "/{search}/p" $ + } else { + output = $ echo "{source}" | sed -ne "/{search}/p" $ + } + if output != "" { + return true + } + } + return false +} + +/// Checks if an array value (with regular expression) is in the text. +pub fun match_regex_any(text: Text, terms: [Text]): Bool { + for term in terms { + if match_regex(text, term, false) { + return true + } + } + + return false +} + +/// Reverses text using `rev`. +pub fun reversed(text: Text): Text { + return trust $ echo "{text}" | rev $ +} + +/// Checks if text starts with a value. +pub fun starts_with(text: Text, prefix: Text): Bool { + let result = trust $ if [[ "{text}" == "{prefix}"* ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Checks if text ends with a value. +pub fun ends_with(text: Text, suffix: Text): Bool { + let result = trust $ if [[ "{text}" == *"{suffix}" ]]; then + echo 1 + fi $ + + return result == "1" +} + +/// Returns a substring from `text` starting at the given `index` (0-based). +/// +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +/// If `length` is provided, the substring will include `length` characters; otherwise, it slices to the end of `text`. +/// If `length` is negative, an empty string is returned. +pub fun slice(text: Text, index: Num, length: Num = 0): Text { + if length == 0: length = len(text) - index + if length <= 0: return "" + return trust $ printf "%.{length}s" "\$\{text:{index}}" $ +} + +/// Returns the character from `text` at the specified `index` (0-based). +/// +/// If `index` is negative, the substring starts from the end of `text` based on the absolute value of `index`. +pub fun char_at(text: Text, index: Num): Text { + return trust $ printf "%.1s" "\$\{text:{index}}" $ +} + +/// Capitalize the first letter of the given `text`. +#[allow_absurd_cast] +pub fun capitalized(text: Text): Text { + trust { + if len(text) == 0 { + return text + } + const bash_version = $ echo \"\$\{BASH_VERSINFO[0]}\" $ as Num + if bash_version >= 4 { + return $ echo \"\$\{text^}\" $ + } + // GNU sed supports \U + $ re='\bCopyright\b.+\bFree Software Foundation\b'; [[ \$(sed --version 2>/dev/null) =~ \$re ]] $ + if status == 0 { + return $ echo "{text}" | sed "s/^\(.\)/\U\1/" $ + } + const first_letter = uppercase(char_at(text, 0)) + return first_letter + slice(text, 1) + } +} + +/// Pads `text` with the specified `pad` character on left until it reaches the desired `length`. +pub fun lpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = trust $ printf "%{length}s" "" | tr " " "{pad}" $ + return pad + text +} + +/// Pads `text` with the specified `pad` character on the right until it reaches the desired `length`. +pub fun rpad(text: Text, pad: Text, length: Num): Text { + if length <= len(text): return text + length = len(text) - length + pad = trust $ printf "%{length}s" "" | tr " " "{pad}" $ + return text + pad +} + +/// Pads `text` with zeros on the left until it reaches the desired `length`. +pub fun zfill(text: Text, length: Num): Text { + return lpad(text, "0", length) +} diff --git a/lsp/src/analysis/alpha034/exp.rs b/lib/src/analysis/alpha034/exp.rs similarity index 100% rename from lsp/src/analysis/alpha034/exp.rs rename to lib/src/analysis/alpha034/exp.rs diff --git a/lsp/src/analysis/alpha034/global.rs b/lib/src/analysis/alpha034/global.rs similarity index 100% rename from lsp/src/analysis/alpha034/global.rs rename to lib/src/analysis/alpha034/global.rs diff --git a/lsp/src/analysis/alpha034/mod.rs b/lib/src/analysis/alpha034/mod.rs similarity index 100% rename from lsp/src/analysis/alpha034/mod.rs rename to lib/src/analysis/alpha034/mod.rs diff --git a/lsp/src/analysis/alpha034/stmnts.rs b/lib/src/analysis/alpha034/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha034/stmnts.rs rename to lib/src/analysis/alpha034/stmnts.rs diff --git a/lsp/src/analysis/alpha035/exp.rs b/lib/src/analysis/alpha035/exp.rs similarity index 100% rename from lsp/src/analysis/alpha035/exp.rs rename to lib/src/analysis/alpha035/exp.rs diff --git a/lsp/src/analysis/alpha035/global.rs b/lib/src/analysis/alpha035/global.rs similarity index 100% rename from lsp/src/analysis/alpha035/global.rs rename to lib/src/analysis/alpha035/global.rs diff --git a/lsp/src/analysis/alpha035/mod.rs b/lib/src/analysis/alpha035/mod.rs similarity index 100% rename from lsp/src/analysis/alpha035/mod.rs rename to lib/src/analysis/alpha035/mod.rs diff --git a/lsp/src/analysis/alpha035/stmnts.rs b/lib/src/analysis/alpha035/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha035/stmnts.rs rename to lib/src/analysis/alpha035/stmnts.rs diff --git a/lsp/src/analysis/alpha040/exp.rs b/lib/src/analysis/alpha040/exp.rs similarity index 100% rename from lsp/src/analysis/alpha040/exp.rs rename to lib/src/analysis/alpha040/exp.rs diff --git a/lsp/src/analysis/alpha040/global.rs b/lib/src/analysis/alpha040/global.rs similarity index 100% rename from lsp/src/analysis/alpha040/global.rs rename to lib/src/analysis/alpha040/global.rs diff --git a/lsp/src/analysis/alpha040/mod.rs b/lib/src/analysis/alpha040/mod.rs similarity index 100% rename from lsp/src/analysis/alpha040/mod.rs rename to lib/src/analysis/alpha040/mod.rs diff --git a/lsp/src/analysis/alpha040/stmnts.rs b/lib/src/analysis/alpha040/stmnts.rs similarity index 100% rename from lsp/src/analysis/alpha040/stmnts.rs rename to lib/src/analysis/alpha040/stmnts.rs diff --git a/lsp/src/analysis/mod.rs b/lib/src/analysis/mod.rs similarity index 100% rename from lsp/src/analysis/mod.rs rename to lib/src/analysis/mod.rs diff --git a/lsp/src/analysis/types.rs b/lib/src/analysis/types.rs similarity index 100% rename from lsp/src/analysis/types.rs rename to lib/src/analysis/types.rs diff --git a/lsp/src/backend.rs b/lib/src/backend.rs similarity index 100% rename from lsp/src/backend.rs rename to lib/src/backend.rs diff --git a/lsp/src/files.rs b/lib/src/files.rs similarity index 100% rename from lsp/src/files.rs rename to lib/src/files.rs diff --git a/lsp/src/fs.rs b/lib/src/fs.rs similarity index 100% rename from lsp/src/fs.rs rename to lib/src/fs.rs diff --git a/lsp/src/grammar/alpha034/expressions/and.rs b/lib/src/grammar/alpha034/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/and.rs rename to lib/src/grammar/alpha034/expressions/and.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/array.rs b/lib/src/grammar/alpha034/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/array.rs rename to lib/src/grammar/alpha034/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/bool.rs b/lib/src/grammar/alpha034/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/bool.rs rename to lib/src/grammar/alpha034/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/call.rs b/lib/src/grammar/alpha034/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/call.rs rename to lib/src/grammar/alpha034/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/command.rs b/lib/src/grammar/alpha034/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/command.rs rename to lib/src/grammar/alpha034/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/mod.rs b/lib/src/grammar/alpha034/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/mod.rs rename to lib/src/grammar/alpha034/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/null.rs b/lib/src/grammar/alpha034/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/null.rs rename to lib/src/grammar/alpha034/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/number.rs b/lib/src/grammar/alpha034/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/number.rs rename to lib/src/grammar/alpha034/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/parentheses.rs b/lib/src/grammar/alpha034/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha034/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/status.rs b/lib/src/grammar/alpha034/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/status.rs rename to lib/src/grammar/alpha034/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/text.rs b/lib/src/grammar/alpha034/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/text.rs rename to lib/src/grammar/alpha034/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha034/expressions/atom/var.rs b/lib/src/grammar/alpha034/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/atom/var.rs rename to lib/src/grammar/alpha034/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha034/expressions/cast.rs b/lib/src/grammar/alpha034/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/cast.rs rename to lib/src/grammar/alpha034/expressions/cast.rs diff --git a/lsp/src/grammar/alpha034/expressions/comparison.rs b/lib/src/grammar/alpha034/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/comparison.rs rename to lib/src/grammar/alpha034/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha034/expressions/is.rs b/lib/src/grammar/alpha034/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/is.rs rename to lib/src/grammar/alpha034/expressions/is.rs diff --git a/lsp/src/grammar/alpha034/expressions/mod.rs b/lib/src/grammar/alpha034/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/mod.rs rename to lib/src/grammar/alpha034/expressions/mod.rs diff --git a/lsp/src/grammar/alpha034/expressions/or.rs b/lib/src/grammar/alpha034/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/or.rs rename to lib/src/grammar/alpha034/expressions/or.rs diff --git a/lsp/src/grammar/alpha034/expressions/product.rs b/lib/src/grammar/alpha034/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/product.rs rename to lib/src/grammar/alpha034/expressions/product.rs diff --git a/lsp/src/grammar/alpha034/expressions/range.rs b/lib/src/grammar/alpha034/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/range.rs rename to lib/src/grammar/alpha034/expressions/range.rs diff --git a/lsp/src/grammar/alpha034/expressions/sum.rs b/lib/src/grammar/alpha034/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/sum.rs rename to lib/src/grammar/alpha034/expressions/sum.rs diff --git a/lsp/src/grammar/alpha034/expressions/ternary.rs b/lib/src/grammar/alpha034/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/ternary.rs rename to lib/src/grammar/alpha034/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha034/expressions/unary.rs b/lib/src/grammar/alpha034/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha034/expressions/unary.rs rename to lib/src/grammar/alpha034/expressions/unary.rs diff --git a/lsp/src/grammar/alpha034/global.rs b/lib/src/grammar/alpha034/global.rs similarity index 100% rename from lsp/src/grammar/alpha034/global.rs rename to lib/src/grammar/alpha034/global.rs diff --git a/lsp/src/grammar/alpha034/lexer.rs b/lib/src/grammar/alpha034/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha034/lexer.rs rename to lib/src/grammar/alpha034/lexer.rs diff --git a/lsp/src/grammar/alpha034/mod.rs b/lib/src/grammar/alpha034/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/mod.rs rename to lib/src/grammar/alpha034/mod.rs diff --git a/lsp/src/grammar/alpha034/parser.rs b/lib/src/grammar/alpha034/parser.rs similarity index 100% rename from lsp/src/grammar/alpha034/parser.rs rename to lib/src/grammar/alpha034/parser.rs diff --git a/lsp/src/grammar/alpha034/semantic_tokens.rs b/lib/src/grammar/alpha034/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha034/semantic_tokens.rs rename to lib/src/grammar/alpha034/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha034/statements/block.rs b/lib/src/grammar/alpha034/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/block.rs rename to lib/src/grammar/alpha034/statements/block.rs diff --git a/lsp/src/grammar/alpha034/statements/comment.rs b/lib/src/grammar/alpha034/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/comment.rs rename to lib/src/grammar/alpha034/statements/comment.rs diff --git a/lsp/src/grammar/alpha034/statements/const_init.rs b/lib/src/grammar/alpha034/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/const_init.rs rename to lib/src/grammar/alpha034/statements/const_init.rs diff --git a/lsp/src/grammar/alpha034/statements/failed.rs b/lib/src/grammar/alpha034/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/failed.rs rename to lib/src/grammar/alpha034/statements/failed.rs diff --git a/lsp/src/grammar/alpha034/statements/if_cond.rs b/lib/src/grammar/alpha034/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/if_cond.rs rename to lib/src/grammar/alpha034/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha034/statements/keywords.rs b/lib/src/grammar/alpha034/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/keywords.rs rename to lib/src/grammar/alpha034/statements/keywords.rs diff --git a/lsp/src/grammar/alpha034/statements/loops.rs b/lib/src/grammar/alpha034/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/loops.rs rename to lib/src/grammar/alpha034/statements/loops.rs diff --git a/lsp/src/grammar/alpha034/statements/mod.rs b/lib/src/grammar/alpha034/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/mod.rs rename to lib/src/grammar/alpha034/statements/mod.rs diff --git a/lsp/src/grammar/alpha034/statements/modifiers.rs b/lib/src/grammar/alpha034/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/modifiers.rs rename to lib/src/grammar/alpha034/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha034/statements/shebang.rs b/lib/src/grammar/alpha034/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/shebang.rs rename to lib/src/grammar/alpha034/statements/shebang.rs diff --git a/lsp/src/grammar/alpha034/statements/shorthands.rs b/lib/src/grammar/alpha034/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/shorthands.rs rename to lib/src/grammar/alpha034/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha034/statements/var_init.rs b/lib/src/grammar/alpha034/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/var_init.rs rename to lib/src/grammar/alpha034/statements/var_init.rs diff --git a/lsp/src/grammar/alpha034/statements/var_set.rs b/lib/src/grammar/alpha034/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha034/statements/var_set.rs rename to lib/src/grammar/alpha034/statements/var_set.rs diff --git a/lsp/src/grammar/alpha035/expressions/and.rs b/lib/src/grammar/alpha035/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/and.rs rename to lib/src/grammar/alpha035/expressions/and.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/array.rs b/lib/src/grammar/alpha035/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/array.rs rename to lib/src/grammar/alpha035/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/bool.rs b/lib/src/grammar/alpha035/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/bool.rs rename to lib/src/grammar/alpha035/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/call.rs b/lib/src/grammar/alpha035/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/call.rs rename to lib/src/grammar/alpha035/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/command.rs b/lib/src/grammar/alpha035/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/command.rs rename to lib/src/grammar/alpha035/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/mod.rs b/lib/src/grammar/alpha035/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/mod.rs rename to lib/src/grammar/alpha035/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/null.rs b/lib/src/grammar/alpha035/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/null.rs rename to lib/src/grammar/alpha035/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/number.rs b/lib/src/grammar/alpha035/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/number.rs rename to lib/src/grammar/alpha035/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/parentheses.rs b/lib/src/grammar/alpha035/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha035/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/status.rs b/lib/src/grammar/alpha035/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/status.rs rename to lib/src/grammar/alpha035/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/text.rs b/lib/src/grammar/alpha035/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/text.rs rename to lib/src/grammar/alpha035/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha035/expressions/atom/var.rs b/lib/src/grammar/alpha035/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/atom/var.rs rename to lib/src/grammar/alpha035/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha035/expressions/cast.rs b/lib/src/grammar/alpha035/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/cast.rs rename to lib/src/grammar/alpha035/expressions/cast.rs diff --git a/lsp/src/grammar/alpha035/expressions/comparison.rs b/lib/src/grammar/alpha035/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/comparison.rs rename to lib/src/grammar/alpha035/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha035/expressions/is.rs b/lib/src/grammar/alpha035/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/is.rs rename to lib/src/grammar/alpha035/expressions/is.rs diff --git a/lsp/src/grammar/alpha035/expressions/mod.rs b/lib/src/grammar/alpha035/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/mod.rs rename to lib/src/grammar/alpha035/expressions/mod.rs diff --git a/lsp/src/grammar/alpha035/expressions/or.rs b/lib/src/grammar/alpha035/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/or.rs rename to lib/src/grammar/alpha035/expressions/or.rs diff --git a/lsp/src/grammar/alpha035/expressions/product.rs b/lib/src/grammar/alpha035/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/product.rs rename to lib/src/grammar/alpha035/expressions/product.rs diff --git a/lsp/src/grammar/alpha035/expressions/range.rs b/lib/src/grammar/alpha035/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/range.rs rename to lib/src/grammar/alpha035/expressions/range.rs diff --git a/lsp/src/grammar/alpha035/expressions/sum.rs b/lib/src/grammar/alpha035/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/sum.rs rename to lib/src/grammar/alpha035/expressions/sum.rs diff --git a/lsp/src/grammar/alpha035/expressions/ternary.rs b/lib/src/grammar/alpha035/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/ternary.rs rename to lib/src/grammar/alpha035/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha035/expressions/unary.rs b/lib/src/grammar/alpha035/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha035/expressions/unary.rs rename to lib/src/grammar/alpha035/expressions/unary.rs diff --git a/lsp/src/grammar/alpha035/global.rs b/lib/src/grammar/alpha035/global.rs similarity index 100% rename from lsp/src/grammar/alpha035/global.rs rename to lib/src/grammar/alpha035/global.rs diff --git a/lsp/src/grammar/alpha035/lexer.rs b/lib/src/grammar/alpha035/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha035/lexer.rs rename to lib/src/grammar/alpha035/lexer.rs diff --git a/lsp/src/grammar/alpha035/mod.rs b/lib/src/grammar/alpha035/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/mod.rs rename to lib/src/grammar/alpha035/mod.rs diff --git a/lsp/src/grammar/alpha035/parser.rs b/lib/src/grammar/alpha035/parser.rs similarity index 100% rename from lsp/src/grammar/alpha035/parser.rs rename to lib/src/grammar/alpha035/parser.rs diff --git a/lsp/src/grammar/alpha035/semantic_tokens.rs b/lib/src/grammar/alpha035/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha035/semantic_tokens.rs rename to lib/src/grammar/alpha035/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha035/statements/block.rs b/lib/src/grammar/alpha035/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/block.rs rename to lib/src/grammar/alpha035/statements/block.rs diff --git a/lsp/src/grammar/alpha035/statements/comment.rs b/lib/src/grammar/alpha035/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/comment.rs rename to lib/src/grammar/alpha035/statements/comment.rs diff --git a/lsp/src/grammar/alpha035/statements/const_init.rs b/lib/src/grammar/alpha035/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/const_init.rs rename to lib/src/grammar/alpha035/statements/const_init.rs diff --git a/lsp/src/grammar/alpha035/statements/failed.rs b/lib/src/grammar/alpha035/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/failed.rs rename to lib/src/grammar/alpha035/statements/failed.rs diff --git a/lsp/src/grammar/alpha035/statements/if_cond.rs b/lib/src/grammar/alpha035/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/if_cond.rs rename to lib/src/grammar/alpha035/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha035/statements/keywords.rs b/lib/src/grammar/alpha035/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/keywords.rs rename to lib/src/grammar/alpha035/statements/keywords.rs diff --git a/lsp/src/grammar/alpha035/statements/loops.rs b/lib/src/grammar/alpha035/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/loops.rs rename to lib/src/grammar/alpha035/statements/loops.rs diff --git a/lsp/src/grammar/alpha035/statements/mod.rs b/lib/src/grammar/alpha035/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/mod.rs rename to lib/src/grammar/alpha035/statements/mod.rs diff --git a/lsp/src/grammar/alpha035/statements/modifiers.rs b/lib/src/grammar/alpha035/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/modifiers.rs rename to lib/src/grammar/alpha035/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha035/statements/move_files.rs b/lib/src/grammar/alpha035/statements/move_files.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/move_files.rs rename to lib/src/grammar/alpha035/statements/move_files.rs diff --git a/lsp/src/grammar/alpha035/statements/shebang.rs b/lib/src/grammar/alpha035/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/shebang.rs rename to lib/src/grammar/alpha035/statements/shebang.rs diff --git a/lsp/src/grammar/alpha035/statements/shorthands.rs b/lib/src/grammar/alpha035/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/shorthands.rs rename to lib/src/grammar/alpha035/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha035/statements/var_init.rs b/lib/src/grammar/alpha035/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/var_init.rs rename to lib/src/grammar/alpha035/statements/var_init.rs diff --git a/lsp/src/grammar/alpha035/statements/var_set.rs b/lib/src/grammar/alpha035/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha035/statements/var_set.rs rename to lib/src/grammar/alpha035/statements/var_set.rs diff --git a/lsp/src/grammar/alpha040/expressions/and.rs b/lib/src/grammar/alpha040/expressions/and.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/and.rs rename to lib/src/grammar/alpha040/expressions/and.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/array.rs b/lib/src/grammar/alpha040/expressions/atom/array.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/array.rs rename to lib/src/grammar/alpha040/expressions/atom/array.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/bool.rs b/lib/src/grammar/alpha040/expressions/atom/bool.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/bool.rs rename to lib/src/grammar/alpha040/expressions/atom/bool.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/call.rs b/lib/src/grammar/alpha040/expressions/atom/call.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/call.rs rename to lib/src/grammar/alpha040/expressions/atom/call.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/command.rs b/lib/src/grammar/alpha040/expressions/atom/command.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/command.rs rename to lib/src/grammar/alpha040/expressions/atom/command.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/exit.rs b/lib/src/grammar/alpha040/expressions/atom/exit.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/exit.rs rename to lib/src/grammar/alpha040/expressions/atom/exit.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/mod.rs b/lib/src/grammar/alpha040/expressions/atom/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/mod.rs rename to lib/src/grammar/alpha040/expressions/atom/mod.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/null.rs b/lib/src/grammar/alpha040/expressions/atom/null.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/null.rs rename to lib/src/grammar/alpha040/expressions/atom/null.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/number.rs b/lib/src/grammar/alpha040/expressions/atom/number.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/number.rs rename to lib/src/grammar/alpha040/expressions/atom/number.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/parentheses.rs b/lib/src/grammar/alpha040/expressions/atom/parentheses.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/parentheses.rs rename to lib/src/grammar/alpha040/expressions/atom/parentheses.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/status.rs b/lib/src/grammar/alpha040/expressions/atom/status.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/status.rs rename to lib/src/grammar/alpha040/expressions/atom/status.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/text.rs b/lib/src/grammar/alpha040/expressions/atom/text.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/text.rs rename to lib/src/grammar/alpha040/expressions/atom/text.rs diff --git a/lsp/src/grammar/alpha040/expressions/atom/var.rs b/lib/src/grammar/alpha040/expressions/atom/var.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/atom/var.rs rename to lib/src/grammar/alpha040/expressions/atom/var.rs diff --git a/lsp/src/grammar/alpha040/expressions/cast.rs b/lib/src/grammar/alpha040/expressions/cast.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/cast.rs rename to lib/src/grammar/alpha040/expressions/cast.rs diff --git a/lsp/src/grammar/alpha040/expressions/comparison.rs b/lib/src/grammar/alpha040/expressions/comparison.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/comparison.rs rename to lib/src/grammar/alpha040/expressions/comparison.rs diff --git a/lsp/src/grammar/alpha040/expressions/is.rs b/lib/src/grammar/alpha040/expressions/is.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/is.rs rename to lib/src/grammar/alpha040/expressions/is.rs diff --git a/lsp/src/grammar/alpha040/expressions/mod.rs b/lib/src/grammar/alpha040/expressions/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/mod.rs rename to lib/src/grammar/alpha040/expressions/mod.rs diff --git a/lsp/src/grammar/alpha040/expressions/or.rs b/lib/src/grammar/alpha040/expressions/or.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/or.rs rename to lib/src/grammar/alpha040/expressions/or.rs diff --git a/lsp/src/grammar/alpha040/expressions/product.rs b/lib/src/grammar/alpha040/expressions/product.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/product.rs rename to lib/src/grammar/alpha040/expressions/product.rs diff --git a/lsp/src/grammar/alpha040/expressions/range.rs b/lib/src/grammar/alpha040/expressions/range.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/range.rs rename to lib/src/grammar/alpha040/expressions/range.rs diff --git a/lsp/src/grammar/alpha040/expressions/sum.rs b/lib/src/grammar/alpha040/expressions/sum.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/sum.rs rename to lib/src/grammar/alpha040/expressions/sum.rs diff --git a/lsp/src/grammar/alpha040/expressions/ternary.rs b/lib/src/grammar/alpha040/expressions/ternary.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/ternary.rs rename to lib/src/grammar/alpha040/expressions/ternary.rs diff --git a/lsp/src/grammar/alpha040/expressions/unary.rs b/lib/src/grammar/alpha040/expressions/unary.rs similarity index 100% rename from lsp/src/grammar/alpha040/expressions/unary.rs rename to lib/src/grammar/alpha040/expressions/unary.rs diff --git a/lsp/src/grammar/alpha040/global.rs b/lib/src/grammar/alpha040/global.rs similarity index 100% rename from lsp/src/grammar/alpha040/global.rs rename to lib/src/grammar/alpha040/global.rs diff --git a/lsp/src/grammar/alpha040/lexer.rs b/lib/src/grammar/alpha040/lexer.rs similarity index 100% rename from lsp/src/grammar/alpha040/lexer.rs rename to lib/src/grammar/alpha040/lexer.rs diff --git a/lsp/src/grammar/alpha040/mod.rs b/lib/src/grammar/alpha040/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/mod.rs rename to lib/src/grammar/alpha040/mod.rs diff --git a/lsp/src/grammar/alpha040/parser.rs b/lib/src/grammar/alpha040/parser.rs similarity index 100% rename from lsp/src/grammar/alpha040/parser.rs rename to lib/src/grammar/alpha040/parser.rs diff --git a/lsp/src/grammar/alpha040/semantic_tokens.rs b/lib/src/grammar/alpha040/semantic_tokens.rs similarity index 100% rename from lsp/src/grammar/alpha040/semantic_tokens.rs rename to lib/src/grammar/alpha040/semantic_tokens.rs diff --git a/lsp/src/grammar/alpha040/statements/block.rs b/lib/src/grammar/alpha040/statements/block.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/block.rs rename to lib/src/grammar/alpha040/statements/block.rs diff --git a/lsp/src/grammar/alpha040/statements/comment.rs b/lib/src/grammar/alpha040/statements/comment.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/comment.rs rename to lib/src/grammar/alpha040/statements/comment.rs diff --git a/lsp/src/grammar/alpha040/statements/const_init.rs b/lib/src/grammar/alpha040/statements/const_init.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/const_init.rs rename to lib/src/grammar/alpha040/statements/const_init.rs diff --git a/lsp/src/grammar/alpha040/statements/failed.rs b/lib/src/grammar/alpha040/statements/failed.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/failed.rs rename to lib/src/grammar/alpha040/statements/failed.rs diff --git a/lsp/src/grammar/alpha040/statements/if_cond.rs b/lib/src/grammar/alpha040/statements/if_cond.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/if_cond.rs rename to lib/src/grammar/alpha040/statements/if_cond.rs diff --git a/lsp/src/grammar/alpha040/statements/keywords.rs b/lib/src/grammar/alpha040/statements/keywords.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/keywords.rs rename to lib/src/grammar/alpha040/statements/keywords.rs diff --git a/lsp/src/grammar/alpha040/statements/loops.rs b/lib/src/grammar/alpha040/statements/loops.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/loops.rs rename to lib/src/grammar/alpha040/statements/loops.rs diff --git a/lsp/src/grammar/alpha040/statements/mod.rs b/lib/src/grammar/alpha040/statements/mod.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/mod.rs rename to lib/src/grammar/alpha040/statements/mod.rs diff --git a/lsp/src/grammar/alpha040/statements/modifiers.rs b/lib/src/grammar/alpha040/statements/modifiers.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/modifiers.rs rename to lib/src/grammar/alpha040/statements/modifiers.rs diff --git a/lsp/src/grammar/alpha040/statements/move_files.rs b/lib/src/grammar/alpha040/statements/move_files.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/move_files.rs rename to lib/src/grammar/alpha040/statements/move_files.rs diff --git a/lsp/src/grammar/alpha040/statements/shebang.rs b/lib/src/grammar/alpha040/statements/shebang.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/shebang.rs rename to lib/src/grammar/alpha040/statements/shebang.rs diff --git a/lsp/src/grammar/alpha040/statements/shorthands.rs b/lib/src/grammar/alpha040/statements/shorthands.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/shorthands.rs rename to lib/src/grammar/alpha040/statements/shorthands.rs diff --git a/lsp/src/grammar/alpha040/statements/var_init.rs b/lib/src/grammar/alpha040/statements/var_init.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/var_init.rs rename to lib/src/grammar/alpha040/statements/var_init.rs diff --git a/lsp/src/grammar/alpha040/statements/var_set.rs b/lib/src/grammar/alpha040/statements/var_set.rs similarity index 100% rename from lsp/src/grammar/alpha040/statements/var_set.rs rename to lib/src/grammar/alpha040/statements/var_set.rs diff --git a/lsp/src/grammar/mod.rs b/lib/src/grammar/mod.rs similarity index 100% rename from lsp/src/grammar/mod.rs rename to lib/src/grammar/mod.rs diff --git a/lsp/src/lib.rs b/lib/src/lib.rs similarity index 100% rename from lsp/src/lib.rs rename to lib/src/lib.rs diff --git a/lsp/src/paths.rs b/lib/src/paths.rs similarity index 100% rename from lsp/src/paths.rs rename to lib/src/paths.rs diff --git a/lsp/src/stdlib.rs b/lib/src/stdlib.rs similarity index 100% rename from lsp/src/stdlib.rs rename to lib/src/stdlib.rs diff --git a/lsp/src/utils.rs b/lib/src/utils.rs similarity index 100% rename from lsp/src/utils.rs rename to lib/src/utils.rs diff --git a/lsp/Cargo.toml b/lsp/Cargo.toml index 8f55eac..5bdcccb 100644 --- a/lsp/Cargo.toml +++ b/lsp/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" repository = "https://github.com/KrosFire/amber-lsp" [dependencies] +lib = { workspace = true } + tokio = { workspace = true, features = ["full"] } tower-lsp-server = { workspace = true } phf = { workspace = true, features = ["macros"] } diff --git a/lsp/src/main.rs b/lsp/src/main.rs index 25478bf..aa33f42 100644 --- a/lsp/src/main.rs +++ b/lsp/src/main.rs @@ -3,8 +3,8 @@ use std::{ process::{Command, Stdio}, }; -use amber_lsp::backend::{AmberVersion, Backend}; use clap::{builder::PossibleValue, Parser, ValueEnum}; +use lib::backend::{AmberVersion, Backend}; use tower_lsp_server::{LspService, Server}; use tracing::subscriber; use tracing_subscriber::fmt::format::FmtSpan; From f9cfb069e5e2eca40149a61445365400338a4a67 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 29 Oct 2025 16:29:19 +0000 Subject: [PATCH 08/37] Moved amber version detection into library --- Cargo.toml | 2 +- formatter/Cargo.toml | 31 +++++++++++++++++ formatter/src/main.rs | 51 +++++++++++++++++++++++++++ lib/src/amber_version.rs | 75 ++++++++++++++++++++++++++++++++++++++++ lib/src/analysis/mod.rs | 3 +- lib/src/backend.rs | 8 +---- lib/src/lib.rs | 3 ++ lib/src/stdlib.rs | 2 +- lsp/src/main.rs | 72 ++------------------------------------ 9 files changed, 168 insertions(+), 79 deletions(-) create mode 100644 formatter/Cargo.toml create mode 100644 formatter/src/main.rs create mode 100644 lib/src/amber_version.rs diff --git a/Cargo.toml b/Cargo.toml index 6e91637..00a4153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["lsp", "lib"] +members = ["lsp", "lib", "formatter"] [workspace.dependencies] lib = { path = "./lib" } diff --git a/formatter/Cargo.toml b/formatter/Cargo.toml new file mode 100644 index 0000000..2720426 --- /dev/null +++ b/formatter/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "amber-fmt" +version = "0.1.10" +edition = "2024" + +[dependencies] +lib = { workspace = true } +clap = { workspace = true, features = ["derive"] } +tracing = { workspace = true } +tracing-subscriber = { workspace = true } +tracing-appender = { workspace = true } +thiserror = { workspace = true } + +# tokio = { workspace = true, features = ["full"] } +# tower-lsp-server = { workspace = true } +# phf = { workspace = true, features = ["macros"] } +# dashmap = { workspace = true } +# ropey = { workspace = true } +# chumsky = { workspace = true, features = ["default", "label"] } +# heraclitus-compiler = { workspace = true } +# serde_json = { workspace = true } +# rangemap = { workspace = true } +# indexmap = { workspace = true } +# include_dir = { workspace = true } +# rustc-hash = { workspace = true } + +# [dev-dependencies] +# insta = { workspace = true, features = ["yaml"] } + +# [build-dependencies] +# fs_extra = "1.3.0" diff --git a/formatter/src/main.rs b/formatter/src/main.rs new file mode 100644 index 0000000..2c5ff04 --- /dev/null +++ b/formatter/src/main.rs @@ -0,0 +1,51 @@ +use clap::Parser; +use lib::{CliAmberVersion, detect_amber_version}; +use std::env::temp_dir; +use tracing::subscriber; +use tracing_subscriber::fmt::format::FmtSpan; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Version of the Amber language to use. + #[arg(value_enum, long, short, default_value = "auto")] + amber_version: CliAmberVersion, +} + +fn main() { + let cache_dir = temp_dir().join("amber-lsp"); + let file_appender = tracing_appender::rolling::hourly(cache_dir, "amber-lsp.log"); + let (non_blocking_writer, _guard) = tracing_appender::non_blocking(file_appender); + + // construct a subscriber that prints formatted traces to stdout + let subscriber = tracing_subscriber::fmt() + // Use a more compact, abbreviated log format + .compact() + // Display source code file paths + .with_file(true) + // Display source code line numbers + .with_line_number(true) + // Don't display the thread ID an event was recorded on + .with_thread_ids(false) + // Don't display the event's target (module path) + .with_target(false) + // Log when entering and exiting spans + .with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE) + // log to a file + .with_writer(non_blocking_writer) + // Disabled ANSI color codes for better compatibility with some terminals + .with_ansi(false) + // Build the subscriber + .finish(); + + // use that subscriber to process traces emitted after this point + subscriber::set_global_default(subscriber).expect("Could not set global default subscriber"); + + let args = Args::parse(); + + let amber_version = if args.amber_version == CliAmberVersion::Auto { + detect_amber_version() + } else { + args.amber_version.into() + }; +} diff --git a/lib/src/amber_version.rs b/lib/src/amber_version.rs new file mode 100644 index 0000000..31b12c1 --- /dev/null +++ b/lib/src/amber_version.rs @@ -0,0 +1,75 @@ +use clap::{builder::PossibleValue, ValueEnum}; +use std::process::{Command, Stdio}; + +/// The version of amber programming language that the amber source is compiliable in. +#[derive(Clone, Debug, PartialEq)] +pub enum AmberVersion { + Alpha034, + Alpha035, + Alpha040, +} + +/// Used for clap parsing. +/// Allows for the user to manually override the detected amber version. +#[derive(Clone, Debug, PartialEq)] +pub enum CliAmberVersion { + Auto, + Alpha034, + Alpha035, + Alpha040, +} + +impl From for AmberVersion { + fn from(val: CliAmberVersion) -> Self { + match val { + CliAmberVersion::Auto => AmberVersion::Alpha034, + CliAmberVersion::Alpha034 => AmberVersion::Alpha034, + CliAmberVersion::Alpha035 => AmberVersion::Alpha035, + CliAmberVersion::Alpha040 => AmberVersion::Alpha040, + } + } +} + +impl ValueEnum for CliAmberVersion { + fn value_variants<'a>() -> &'a [CliAmberVersion] { + &[ + CliAmberVersion::Auto, + CliAmberVersion::Alpha034, + CliAmberVersion::Alpha035, + CliAmberVersion::Alpha040, + ] + } + + fn to_possible_value(&self) -> Option { + match self { + CliAmberVersion::Auto => Some(PossibleValue::new("auto")), + CliAmberVersion::Alpha034 => Some(PossibleValue::new("0.3.4-alpha")), + CliAmberVersion::Alpha035 => Some(PossibleValue::new("0.3.5-alpha")), + CliAmberVersion::Alpha040 => Some(PossibleValue::new("0.4.0-alpha")), + } + } +} + +/// Detects the amber version by invoking the amber command. +#[tracing::instrument(skip_all)] +pub fn detect_amber_version() -> AmberVersion { + let output = Command::new("amber") + .arg("-V") + .stdout(Stdio::piped()) + .output(); + + let version = match output { + Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), + Err(e) => { + tracing::error!("Failed to execute amber command: {}", e); + return AmberVersion::Alpha040; // Default to the latest version if detection fails + } + }; + + match version.split_whitespace().last() { + Some("0.3.4-alpha") => AmberVersion::Alpha034, + Some("0.3.5-alpha") => AmberVersion::Alpha035, + Some("0.4.0-alpha") => AmberVersion::Alpha040, + _ => AmberVersion::Alpha040, + } +} diff --git a/lib/src/analysis/mod.rs b/lib/src/analysis/mod.rs index b3a64dd..8169dab 100644 --- a/lib/src/analysis/mod.rs +++ b/lib/src/analysis/mod.rs @@ -4,7 +4,8 @@ use tower_lsp_server::{lsp_types::Uri, UriExt}; use types::{DataType, GenericsMap}; use crate::{ - backend::{AmberVersion, Backend}, + amber_version::AmberVersion, + backend::Backend, files::{FileVersion, Files}, grammar::{CommandModifier, CompilerFlag, Span, Spanned}, paths::FileId, diff --git a/lib/src/backend.rs b/lib/src/backend.rs index 0c4ceb6..ef7c589 100644 --- a/lib/src/backend.rs +++ b/lib/src/backend.rs @@ -10,6 +10,7 @@ use tower_lsp_server::lsp_types::*; use tower_lsp_server::UriExt; use tower_lsp_server::{Client, LanguageServer}; +use crate::amber_version::AmberVersion; use crate::analysis::{ self, get_symbol_definition_info, Context, FunctionSymbol, SymbolInfo, SymbolTable, SymbolType, VariableSymbol, @@ -22,13 +23,6 @@ use crate::stdlib::{find_in_stdlib, save_resources}; type PinnedFuture<'a, T> = Pin> + Send + 'a>>; -#[derive(Clone, Debug, PartialEq)] -pub enum AmberVersion { - Alpha034, - Alpha035, - Alpha040, -} - #[derive(Debug)] pub struct Backend { pub client: Client, diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 2d5e0ef..854af4c 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,3 +1,4 @@ +pub mod amber_version; pub mod analysis; pub mod backend; pub mod files; @@ -6,3 +7,5 @@ pub mod grammar; pub mod paths; pub mod stdlib; pub mod utils; + +pub use amber_version::{detect_amber_version, AmberVersion, CliAmberVersion}; diff --git a/lib/src/stdlib.rs b/lib/src/stdlib.rs index 2b91f1d..4fb5cda 100644 --- a/lib/src/stdlib.rs +++ b/lib/src/stdlib.rs @@ -10,7 +10,7 @@ use include_dir::{include_dir, Dir, DirEntry}; use tower_lsp_server::{lsp_types::Uri, UriExt}; use tracing::warn; -use crate::backend::{AmberVersion, Backend}; +use crate::{amber_version::AmberVersion, backend::Backend}; pub const STDLIB: Dir = include_dir!("$CARGO_MANIFEST_DIR/resources/"); diff --git a/lsp/src/main.rs b/lsp/src/main.rs index aa33f42..6eb7847 100644 --- a/lsp/src/main.rs +++ b/lsp/src/main.rs @@ -1,53 +1,10 @@ -use std::{ - env::temp_dir, - process::{Command, Stdio}, -}; - -use clap::{builder::PossibleValue, Parser, ValueEnum}; -use lib::backend::{AmberVersion, Backend}; +use clap::Parser; +use lib::{backend::Backend, detect_amber_version, CliAmberVersion}; +use std::env::temp_dir; use tower_lsp_server::{LspService, Server}; use tracing::subscriber; use tracing_subscriber::fmt::format::FmtSpan; -#[derive(Clone, Debug, PartialEq)] -enum CliAmberVersion { - Auto, - Alpha034, - Alpha035, - Alpha040, -} - -impl From for AmberVersion { - fn from(val: CliAmberVersion) -> Self { - match val { - CliAmberVersion::Auto => AmberVersion::Alpha034, - CliAmberVersion::Alpha034 => AmberVersion::Alpha034, - CliAmberVersion::Alpha035 => AmberVersion::Alpha035, - CliAmberVersion::Alpha040 => AmberVersion::Alpha040, - } - } -} - -impl ValueEnum for CliAmberVersion { - fn value_variants<'a>() -> &'a [CliAmberVersion] { - &[ - CliAmberVersion::Auto, - CliAmberVersion::Alpha034, - CliAmberVersion::Alpha035, - CliAmberVersion::Alpha040, - ] - } - - fn to_possible_value(&self) -> Option { - match self { - CliAmberVersion::Auto => Some(PossibleValue::new("auto")), - CliAmberVersion::Alpha034 => Some(PossibleValue::new("0.3.4-alpha")), - CliAmberVersion::Alpha035 => Some(PossibleValue::new("0.3.5-alpha")), - CliAmberVersion::Alpha040 => Some(PossibleValue::new("0.4.0-alpha")), - } - } -} - #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { @@ -100,26 +57,3 @@ async fn main() { let (service, socket) = LspService::new(|client| Backend::new(client, amber_version, None)); Server::new(stdin, stdout, socket).serve(service).await; } - -#[tracing::instrument(skip_all)] -fn detect_amber_version() -> AmberVersion { - let output = Command::new("amber") - .arg("-V") - .stdout(Stdio::piped()) - .output(); - - let version = match output { - Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(), - Err(e) => { - tracing::error!("Failed to execute amber command: {}", e); - return AmberVersion::Alpha040; // Default to the latest version if detection fails - } - }; - - match version.split_whitespace().last() { - Some("0.3.4-alpha") => AmberVersion::Alpha034, - Some("0.3.5-alpha") => AmberVersion::Alpha035, - Some("0.4.0-alpha") => AmberVersion::Alpha040, - _ => AmberVersion::Alpha040, - } -} From 94d19e7b6164f3f7c5e3869ffe0e685613f80618 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 5 Nov 2025 22:31:21 +0000 Subject: [PATCH 09/37] First attempt at formatting This only has basic support for some text current. This is to gather feedback on approaches and methodology --- Cargo.lock | 40 ++++++++++- formatter/src/alpha040/mod.rs | 128 ++++++++++++++++++++++++++++++++++ formatter/src/lib.rs | 123 ++++++++++++++++++++++++++++++++ formatter/src/main.rs | 49 +++++++++++-- 4 files changed, 332 insertions(+), 8 deletions(-) create mode 100644 formatter/src/alpha040/mod.rs create mode 100644 formatter/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index b3c640b..faa3110 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,9 +44,21 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "amber-fmt" +version = "0.1.10" +dependencies = [ + "clap", + "lib", + "thiserror 2.0.17", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "amber-lsp" -version = "0.1.9" +version = "0.1.10" dependencies = [ "chumsky", "clap", @@ -56,6 +68,7 @@ dependencies = [ "include_dir", "indexmap", "insta", + "lib", "phf", "rangemap", "ropey", @@ -523,6 +536,31 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lib" +version = "0.1.10" +dependencies = [ + "chumsky", + "clap", + "dashmap", + "fs_extra", + "heraclitus-compiler", + "include_dir", + "indexmap", + "insta", + "phf", + "rangemap", + "ropey", + "rustc-hash", + "serde_json", + "thiserror 2.0.17", + "tokio", + "tower-lsp-server", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + [[package]] name = "libc" version = "0.2.176" diff --git a/formatter/src/alpha040/mod.rs b/formatter/src/alpha040/mod.rs new file mode 100644 index 0000000..cd7b785 --- /dev/null +++ b/formatter/src/alpha040/mod.rs @@ -0,0 +1,128 @@ +use crate::SpanTextOutput; +use lib::{ + analysis::types::DataType, + grammar::{ + CompilerFlag, + alpha040::{FunctionArgument, GlobalStatement, ImportContent}, + }, +}; + +use crate::{Output, TextOutput}; + +impl TextOutput for GlobalStatement { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + match self { + GlobalStatement::Import(public, import, content, from, path) => { + if public.0 { + output.push_text("pub "); + } + + output.push_output(import); + output.push_space(); + output.push_output(content); + output.push_space(); + output.push_output(from); + output.push_space(); + output.push_output(path); + output.push_newline(); + } + GlobalStatement::FunctionDefinition( + compiler_flags, + public, + function_keyword, + name, + args, + return_type, + content, + ) => { + for flag in compiler_flags { + output.push_output(flag); + output.push_newline(); + } + + if public.0 { + output.push_text("pub "); + } + + output.push_output(function_keyword); + output.push_space(); + output.push_output(name); + + output.push_char('('); + // Handle adding variables with proper spacing + { + for arg in args.iter().take(args.len().saturating_sub(1)) { + output.push_output(arg); + output.push_char(','); + output.push_space(); + } + + if let Some(arg) = args.last() { + output.push_output(arg); + } + } + output.push_char(')'); + + if let Some(returns) = return_type { + output.push_char(':'); + output.push_space(); + output.push_output(returns); + } + + output.push_text("{ Null }"); + output.push_newline(); + } + GlobalStatement::Main(_, _, items) => {} + GlobalStatement::Statement(_) => {} + } + } +} + +impl TextOutput for ImportContent { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + match self { + ImportContent::ImportAll => output.push_char('*'), + ImportContent::ImportSpecific(items) => { + output.push_text("{ "); + for identifier in items { + output.push_output(identifier); + output.push_space(); + } + output.push_char('}'); + } + } + } +} + +impl TextOutput for FunctionArgument { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + fn push_arg(output: &mut Output, is_ref: bool, text: &impl SpanTextOutput) { + if is_ref { + output.push_text("ref"); + output.push_space(); + } + output.push_output(text); + } + + match self { + FunctionArgument::Generic(is_ref, text) => push_arg(output, is_ref.0, text), + FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, is_ref.0, text), + FunctionArgument::Typed(is_ref, text, _) => push_arg(output, is_ref.0, text), + FunctionArgument::Error => output.push_span(span), + } + } +} + +impl TextOutput for CompilerFlag { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.push_text(format!("#[{self}]")); + } +} + +impl TextOutput for String { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.push_text(self.clone()); + } +} + +impl TextOutput for DataType {} diff --git a/formatter/src/lib.rs b/formatter/src/lib.rs new file mode 100644 index 0000000..48295ae --- /dev/null +++ b/formatter/src/lib.rs @@ -0,0 +1,123 @@ +use lib::grammar::{Span, Spanned}; +use std::string::FromUtf8Error; + +mod alpha040; + +#[derive(Default)] +pub struct Output { + buffer: Vec, +} + +pub enum Fragment { + Space, + Newline, + Indentation, + Text(Box), + Span { + /// Start byte offset into source file. + start_offset: usize, + /// End byte offset into source file. + end_offset: usize, + }, +} + +pub trait SpanTextOutput { + fn output(&self, output: &mut Output); +} + +pub trait TextOutput { + /// Gets the formatted string representation of an AST element. + /// The string representation should be written to the output buffer. + /// + /// It is the responsibility of the caller to ensure that the buffer is in the correct state to + /// have text appended. E.G. The buffer is at the start of a new line. + /// + /// It is the responsibility of the function implementation to add a space to the end of the + /// buffer before returning. + fn output(&self, span: &Span, output: &mut Output) { + output.push_span(span); + } +} + +#[derive(thiserror::Error, Debug)] +pub enum FormattingError { + /// The span does not exist within the source file. + #[error("Invalid span. Starts: {start}; Ends: {end}")] + SpanDoesntExist { start: usize, end: usize }, + /// The span cannot be converted into UTF8 text. + #[error(transparent)] + InvalidSpan(#[from] FromUtf8Error), +} + +impl SpanTextOutput for Spanned { + fn output(&self, output: &mut Output) { + self.0.output(&self.1, output); + } +} + +impl Output { + fn push_space(&mut self) { + self.buffer.push(Fragment::Space); + } + + fn push_newline(&mut self) { + self.buffer.push(Fragment::Newline); + } + + fn push_indentation(&mut self) { + todo!() + } + + fn push_text(&mut self, text: impl Into>) { + self.buffer.push(Fragment::Text(text.into())); + } + + fn push_char(&mut self, character: char) { + self.buffer + .push(Fragment::Text(character.to_string().into_boxed_str())); + } + + fn push_output(&mut self, output: &TOutput) + where + TOutput: SpanTextOutput, + { + output.output(self); + } + + fn push_span(&mut self, span: &Span) { + self.buffer.push(Fragment::Span { + start_offset: span.start, + end_offset: span.end, + }); + } + + pub fn format(self, file_content: &str) -> Result { + let mut text = String::new(); + + for fragment in self.buffer { + match fragment { + Fragment::Space => text.push(' '), + Fragment::Newline => text.push('\n'), + Fragment::Indentation => text.push_str(" "), + Fragment::Text(frag_text) => text.push_str(&frag_text), + Fragment::Span { + start_offset, + end_offset, + } => { + let start = start_offset.min(end_offset); + let end = start_offset.max(end_offset); + + let span = file_content + .as_bytes() + .get(start..=end) + .ok_or_else(|| FormattingError::SpanDoesntExist { start, end })?; + + let span_text = String::from_utf8(span.to_vec())?; + text.push_str(&span_text); + } + } + } + + Ok(text) + } +} diff --git a/formatter/src/main.rs b/formatter/src/main.rs index 2c5ff04..a9d8fde 100644 --- a/formatter/src/main.rs +++ b/formatter/src/main.rs @@ -1,5 +1,9 @@ +use amber_fmt::Output; use clap::Parser; -use lib::{CliAmberVersion, detect_amber_version}; +use lib::{ + CliAmberVersion, + grammar::{Grammar, LSPAnalysis, alpha040::AmberCompiler}, +}; use std::env::temp_dir; use tracing::subscriber; use tracing_subscriber::fmt::format::FmtSpan; @@ -41,11 +45,42 @@ fn main() { // use that subscriber to process traces emitted after this point subscriber::set_global_default(subscriber).expect("Could not set global default subscriber"); - let args = Args::parse(); + // let args = Args::parse(); - let amber_version = if args.amber_version == CliAmberVersion::Auto { - detect_amber_version() - } else { - args.amber_version.into() - }; + // let amber_version = if args.amber_version == CliAmberVersion::Auto { + // detect_amber_version() + // } else { + // args.amber_version.into() + // }; + + // let mut stdin = std::io::stdin(); + // let mut data = String::new(); + // stdin.read_to_string(&mut data).unwrap(); + + // For temporary testing + let data = include_str!("../../lib/resources/alpha040/std/array.ab"); + + let amber_compiler = AmberCompiler::new(); + let tokenize = amber_compiler.tokenize(data); + let parse = amber_compiler.parse(&tokenize); + + println!("{:?}", parse.ast); + match parse.ast { + Grammar::Alpha034(items) => todo!(), + Grammar::Alpha035(items) => todo!(), + Grammar::Alpha040(items) => { + if let Some(items) = items { + { + let mut output = Output::default(); + for item in items { + use amber_fmt::SpanTextOutput; + (&item).output(&mut output); + } + let format = output.format(data).expect("Able to parse"); + println!("{format}"); + } + } + } + Grammar::Alpha040(items) => {} + } } From 80c04d11d12ad408f638bf1abbbe5b7d760c5c45 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 23 Nov 2025 15:55:23 +0000 Subject: [PATCH 10/37] Add display for command modifier --- lib/src/grammar/alpha040/mod.rs | 4 ++++ lib/src/grammar/mod.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/src/grammar/alpha040/mod.rs b/lib/src/grammar/alpha040/mod.rs index 7f208ec..db4f200 100644 --- a/lib/src/grammar/alpha040/mod.rs +++ b/lib/src/grammar/alpha040/mod.rs @@ -116,13 +116,17 @@ pub enum ImportContent { #[derive(PartialEq, Debug, Clone)] pub enum FunctionArgument { + /// First arg is whether this is a reference. + /// The second is generic variable text. Generic(Spanned, Spanned), + /// First arg is whether this is a reference. Optional( Spanned, Spanned, Option>, Spanned, ), + /// First arg is whether this is a reference. Typed(Spanned, Spanned, Spanned), Error, } diff --git a/lib/src/grammar/mod.rs b/lib/src/grammar/mod.rs index 934d4bd..6a4cfea 100644 --- a/lib/src/grammar/mod.rs +++ b/lib/src/grammar/mod.rs @@ -63,6 +63,16 @@ pub enum CommandModifier { Silent, } +impl fmt::Display for CommandModifier { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CommandModifier::Unsafe => write!(f, "unsafe"), + CommandModifier::Trust => write!(f, "trust"), + CommandModifier::Silent => write!(f, "silent"), + } + } +} + #[derive(PartialEq, Debug, Clone, Eq)] pub enum CompilerFlag { AllowNestedIfElse, From 719d9814d09d45d5d76e2b752bcfb703b0b94979 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 23 Nov 2025 15:55:38 +0000 Subject: [PATCH 11/37] Can parse files without error Still very much experimental, but can parse the local run_coverage file without panicking --- formatter/src/alpha040/mod.rs | 524 +++++++++++++++++++++++++++++++--- formatter/src/lib.rs | 121 +++++++- formatter/src/main.rs | 3 +- 3 files changed, 590 insertions(+), 58 deletions(-) diff --git a/formatter/src/alpha040/mod.rs b/formatter/src/alpha040/mod.rs index cd7b785..5ff5d0e 100644 --- a/formatter/src/alpha040/mod.rs +++ b/formatter/src/alpha040/mod.rs @@ -2,8 +2,12 @@ use crate::SpanTextOutput; use lib::{ analysis::types::DataType, grammar::{ - CompilerFlag, - alpha040::{FunctionArgument, GlobalStatement, ImportContent}, + CommandModifier, CompilerFlag, + alpha040::{ + Block, Comment, ElseCondition, Expression, FailureHandler, FunctionArgument, + GlobalStatement, IfChainContent, IfCondition, ImportContent, InterpolatedCommand, + InterpolatedText, IterLoopVars, Statement, VariableInitType, + }, }, }; @@ -14,17 +18,17 @@ impl TextOutput for GlobalStatement { match self { GlobalStatement::Import(public, import, content, from, path) => { if public.0 { - output.push_text("pub "); + output.text("pub "); } - output.push_output(import); - output.push_space(); - output.push_output(content); - output.push_space(); - output.push_output(from); - output.push_space(); - output.push_output(path); - output.push_newline(); + output.output(import); + output.space(); + output.output(content); + output.space(); + output.output(from); + output.space(); + output.output(path); + output.newline(); } GlobalStatement::FunctionDefinition( compiler_flags, @@ -33,47 +37,63 @@ impl TextOutput for GlobalStatement { name, args, return_type, - content, + contents, ) => { for flag in compiler_flags { - output.push_output(flag); - output.push_newline(); + output.output(flag); + output.newline(); } if public.0 { - output.push_text("pub "); + output.text("pub "); } - output.push_output(function_keyword); - output.push_space(); - output.push_output(name); + output.output(function_keyword); + output.space(); + output.output(name); - output.push_char('('); + output.char('('); // Handle adding variables with proper spacing { for arg in args.iter().take(args.len().saturating_sub(1)) { - output.push_output(arg); - output.push_char(','); - output.push_space(); + output.output(arg); + output.char(','); + output.space(); } if let Some(arg) = args.last() { - output.push_output(arg); + output.output(arg); } } - output.push_char(')'); + output.char(')'); if let Some(returns) = return_type { - output.push_char(':'); - output.push_space(); - output.push_output(returns); + output.char(':'); + output.space(); + output.output(returns); } - output.push_text("{ Null }"); - output.push_newline(); + output.char('{').increase_indentation(); + for content in contents { + dbg!(content); + output.newline().end_output(content); + } + output.char('}').newline().decrease_indentation(); } - GlobalStatement::Main(_, _, items) => {} - GlobalStatement::Statement(_) => {} + GlobalStatement::Main(main, args, statements) => { + output.output(main); + + output.char('('); + if let Some(args) = args { + output.output(args); + } + output.char(')'); + + for statement in statements { + output.output(statement); + } + } + GlobalStatement::Statement(statement) => output.end_output(statement), } } } @@ -81,14 +101,16 @@ impl TextOutput for GlobalStatement { impl TextOutput for ImportContent { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { match self { - ImportContent::ImportAll => output.push_char('*'), + ImportContent::ImportAll => { + output.char('*'); + } ImportContent::ImportSpecific(items) => { - output.push_text("{ "); + output.text("{ "); for identifier in items { - output.push_output(identifier); - output.push_space(); + output.output(identifier); + output.space(); } - output.push_char('}'); + output.char('}'); } } } @@ -98,31 +120,449 @@ impl TextOutput for FunctionArgument { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { fn push_arg(output: &mut Output, is_ref: bool, text: &impl SpanTextOutput) { if is_ref { - output.push_text("ref"); - output.push_space(); + output.text("ref"); + output.space(); } - output.push_output(text); + output.output(text); } match self { FunctionArgument::Generic(is_ref, text) => push_arg(output, is_ref.0, text), FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, is_ref.0, text), FunctionArgument::Typed(is_ref, text, _) => push_arg(output, is_ref.0, text), - FunctionArgument::Error => output.push_span(span), + FunctionArgument::Error => { + output.span(span); + } } } } impl TextOutput for CompilerFlag { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.push_text(format!("#[{self}]")); + output.text(format!("#[{self}]")); } } impl TextOutput for String { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.push_text(self.clone()); + output.text(self.clone()); } } impl TextOutput for DataType {} + +impl TextOutput for Statement { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + fn shorthand( + output: &mut Output, + variable: &impl SpanTextOutput, + separator: &str, + expression: &impl SpanTextOutput, + ) { + output + .output(variable) + .space() + .text(separator) + .space() + .output(expression); + } + + match self { + Statement::Expression(expression) => { + output.output(expression); + } + Statement::VariableInit(keyword, name, init) => { + output + .output(keyword) + .space() + .output(name) + .space() + .char('=') + .space() + .output(init); + } + Statement::ConstInit(keyword, name, init) => { + output + .output(keyword) + .space() + .output(name) + .space() + .char('=') + .space() + .output(init); + } + Statement::VariableSet(name, new_value) => { + output + .output(name) + .space() + .char('=') + .space() + .output(new_value); + } + Statement::IfCondition(r#if, condition, items, else_condition) => { + output + .output(r#if) + .space() + .output(condition) + .space() + .debug_point("If condition items"); + + for ele in items { + output.output(ele); + } + + if let Some(else_condition) = else_condition { + output.output(else_condition); + } + } + Statement::IfChain(r#if, items) => { + output.output(r#if); + for ele in items { + output.output(ele); + } + } + Statement::ShorthandAdd(variable, expr) => shorthand(output, variable, "+=", expr), + Statement::ShorthandSub(variable, expr) => shorthand(output, variable, "-=", expr), + Statement::ShorthandMul(variable, expr) => shorthand(output, variable, "*=", expr), + Statement::ShorthandDiv(variable, expr) => shorthand(output, variable, "/=", expr), + Statement::ShorthandModulo(variable, expr) => shorthand(output, variable, "%=", expr), + Statement::InfiniteLoop(r#loop, block) => { + output.output(r#loop).space().output(block); + } + Statement::IterLoop(r#for, element, r#in, expr, block) => { + output + .output(r#for) + .space() + .output(element) + .space() + .output(r#in) + .space() + .output(expr) + .space() + .end_output(block); + } + Statement::Break => output.end_text("break"), + Statement::Continue => output.end_text("continue"), + Statement::Return(r#return, expr) => { + output.end_output(r#return); + + if let Some(expr) = expr { + output.space().end_output(expr); + } + } + Statement::Fail(fail, expr) => { + output.end_output(fail); + + if let Some(expr) = expr { + output.space().end_output(expr); + } + } + Statement::Echo(echo, text) => output.output(echo).space().end_output(text), + Statement::Cd(cd, text) => output.output(cd).space().end_output(text), + Statement::MoveFiles(modifiers, mv, source, destination, failure_handler) => { + for modifier in modifiers { + output.output(modifier).space(); + } + + output + .output(mv) + .space() + .output(source) + .space() + .output(destination); + + if let Some(failure_handler) = failure_handler { + output.space().output(failure_handler); + } + } + Statement::Block(block) => output.end_output(block), + Statement::Comment(comment) => output.end_output(comment), + Statement::Shebang(shebang) => output.end_text(shebang.as_str()), + Statement::Error => output.end_span(span), + } + } +} + +impl TextOutput for IterLoopVars { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for Block { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.debug_point("Block").span(span); + // match self { + // Block::Block(modifiers, statements) => { + // output.char('{').end_newline(); + + // for modifier in modifiers { + // output.output(modifier).end_space(); + // } + // for statement in statements { + // output.output(statement).end_newline(); + // } + + // output.char('}').end_newline(); + // } + // Block::Error => output.end_span(span), + // } + } +} + +impl TextOutput for IfChainContent { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for ElseCondition { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for Comment { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for IfCondition { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for VariableInitType { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + match self { + VariableInitType::Expression(expr) => output.end_output(expr), + VariableInitType::DataType(r#type) => output.end_output(r#type), + VariableInitType::Error => output.end_span(span), + } + } +} + +impl TextOutput for Expression { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + fn char_separated( + output: &mut Output, + rhs: &impl SpanTextOutput, + middle: char, + lhs: &impl SpanTextOutput, + ) { + output.output(rhs); + output.space(); + output.char(middle); + output.space(); + output.output(lhs); + } + + fn string_separated( + output: &mut Output, + rhs: &impl SpanTextOutput, + middle: &str, + lhs: &impl SpanTextOutput, + ) { + output.output(rhs); + output.space(); + output.text(middle); + output.space(); + output.output(lhs); + } + + fn output_separated( + output: &mut Output, + rhs: &impl SpanTextOutput, + middle: &impl SpanTextOutput, + lhs: &impl SpanTextOutput, + ) { + output.output(rhs); + output.space(); + output.output(middle); + output.space(); + output.output(lhs); + } + + match self { + Expression::Number(num) => { + output.output(num); + } + Expression::Boolean(boolean) => { + output.output(boolean); + } + Expression::Text(texts) => { + for text in texts { + output.output(text); + } + } + Expression::Parentheses(parentheses) => { + output.output(parentheses); + } + Expression::Var(var) => { + output.output(var); + } + Expression::Add(rhs, lhs) => char_separated(output, rhs, '+', lhs), + Expression::Subtract(rhs, lhs) => char_separated(output, rhs, '-', lhs), + Expression::Multiply(rhs, lhs) => char_separated(output, rhs, '*', lhs), + Expression::Divide(rhs, lhs) => char_separated(output, rhs, '/', lhs), + Expression::Modulo(rhs, lhs) => char_separated(output, rhs, '%', lhs), + Expression::Neg(neg, lhs) => { + output.output(neg); + output.output(lhs); + } + Expression::And(rhs, and, lhs) => output_separated(output, rhs, and, lhs), + Expression::Or(rhs, or, lhs) => output_separated(output, rhs, or, lhs), + Expression::Gt(rhs, lhs) => char_separated(output, rhs, '>', lhs), + Expression::Ge(rhs, lhs) => string_separated(output, rhs, ">=", lhs), + Expression::Lt(rhs, lhs) => char_separated(output, rhs, '<', lhs), + Expression::Le(rhs, lhs) => string_separated(output, rhs, ">=", lhs), + Expression::Eq(rhs, lhs) => string_separated(output, rhs, "==", lhs), + Expression::Neq(rhs, lhs) => string_separated(output, rhs, "!=", lhs), + Expression::Not(not, lhs) => { + output.output(not); + output.space(); + output.output(lhs); + } + Expression::Ternary(condition, then, if_then, r#else, if_else) => { + // TODO(tye-exe): Allow single line ternary if short enough. Use given span to measure length? + output.increase_indentation(); + output.output(condition); + output.newline(); + output.output(then); + output.space(); + output.output(if_then); + output.newline(); + output.output(r#else); + output.space(); + output.output(if_else); + output.decrease_indentation(); + } + Expression::FunctionInvocation(modifiers, function_name, args, failure_handler) => { + for modifier in modifiers { + output.output(modifier); + output.space(); + } + + output.output(function_name).char('('); + + for arg in args.iter().take(args.len().saturating_sub(1)) { + output.output(arg).char(',').space(); + } + if let Some(arg) = args.last() { + output.output(arg); + } + + output.char(')'); + + if let Some(failure_handler) = failure_handler { + output.output(failure_handler); + } + } + Expression::Command(modifiers, commands, failure_handler) => { + for modifier in modifiers { + output.output(modifier); + output.space(); + } + + for command in commands { + output.output(command); + output.space(); + } + + if let Some(failure_handler) = failure_handler { + output.output(failure_handler); + } + } + Expression::Array(array) => { + output.char('['); + for expression in array { + output.output(expression); + output.char(','); + output.space(); + } + output.remove_space(); + output.char(']'); + } + Expression::Range(lhs, rhs) => { + output.output(lhs); + output.text(".."); + output.output(rhs); + } + Expression::Null => { + output.text("Null"); + } + Expression::Cast(lhs, r#as, rhs) => string_separated(output, rhs, &r#as.0, lhs), + Expression::Status => { + output.text("status"); + } + Expression::Nameof(name_of, variable) => { + output.output(name_of); + output.space(); + output.output(variable); + } + Expression::Is(lhs, is, rhs) => { + output.output(lhs).space().output(is).space().output(rhs); + } + Expression::ArrayIndex(array, index) => { + output.output(array).char('[').output(index).end_char(']') + } + Expression::Exit(exit, value) => { + output.output(exit); + + if let Some(value) = value { + output.space().output(value); + } + } + Expression::Error => { + output.span(span); + } + } + } +} + +impl TextOutput for FailureHandler { + fn output(&self, _span: &lib::grammar::Span, output: &mut Output) { + match self { + FailureHandler::Propagate => { + output.char('?'); + } + FailureHandler::Handle(failed, statements) => { + output.output(failed); + output.space(); + output.char('{'); + output.increase_indentation(); + output.newline(); + + for statement in statements { + output.output(statement); + output.newline(); + } + + output.remove_newline(); + output.decrease_indentation(); + output.newline(); + + output.char('}'); + } + } + } +} + +impl TextOutput for CommandModifier {} + +impl TextOutput for InterpolatedText { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for InterpolatedCommand { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.span(span); + } +} + +impl TextOutput for f32 {} +impl TextOutput for bool {} diff --git a/formatter/src/lib.rs b/formatter/src/lib.rs index 48295ae..5475036 100644 --- a/formatter/src/lib.rs +++ b/formatter/src/lib.rs @@ -1,8 +1,11 @@ use lib::grammar::{Span, Spanned}; -use std::string::FromUtf8Error; +use std::{panic::Location, string::FromUtf8Error}; mod alpha040; +/// The content of an indentation. +const INDENT: &str = " "; + #[derive(Default)] pub struct Output { buffer: Vec, @@ -11,7 +14,9 @@ pub struct Output { pub enum Fragment { Space, Newline, - Indentation, + /// Denotes an indentation change. + /// This has no output in its current position, but will change the indentation after every newline. + IndentationChange(Indentation), Text(Box), Span { /// Start byte offset into source file. @@ -21,6 +26,11 @@ pub enum Fragment { }, } +enum Indentation { + Increase, + Decrease, +} + pub trait SpanTextOutput { fn output(&self, output: &mut Output); } @@ -32,10 +42,10 @@ pub trait TextOutput { /// It is the responsibility of the caller to ensure that the buffer is in the correct state to /// have text appended. E.G. The buffer is at the start of a new line. /// - /// It is the responsibility of the function implementation to add a space to the end of the - /// buffer before returning. + /// It is the responsibilirt of the caller to append a space to the buffer if required. + /// An implementation should not end with adding a space. fn output(&self, span: &Span, output: &mut Output) { - output.push_span(span); + output.span(span); } } @@ -55,50 +65,131 @@ impl SpanTextOutput for Spanned { } } +impl SpanTextOutput for Box> { + fn output(&self, output: &mut Output) { + self.0.output(&self.1, output); + } +} + impl Output { - fn push_space(&mut self) { + #[track_caller] + fn debug_point(&mut self, info: &str) -> &mut Self { + self.text(format!( + "%%Debug point : {info} : at {}%%", + Location::caller() + )) + } + + fn space(&mut self) -> &mut Self { self.buffer.push(Fragment::Space); + self } - fn push_newline(&mut self) { + fn newline(&mut self) -> &mut Self { self.buffer.push(Fragment::Newline); + self } - fn push_indentation(&mut self) { - todo!() + fn text(&mut self, text: impl Into>) -> &mut Self { + self.buffer.push(Fragment::Text(text.into())); + self } - fn push_text(&mut self, text: impl Into>) { + fn char(&mut self, character: char) -> &mut Self { + self.buffer + .push(Fragment::Text(character.to_string().into_boxed_str())); + self + } + + fn output(&mut self, output: &TOutput) -> &mut Self + where + TOutput: SpanTextOutput, + { + output.output(self); + self + } + + fn span(&mut self, span: &Span) -> &mut Self { + self.buffer.push(Fragment::Span { + start_offset: span.start, + end_offset: span.end, + }); + self + } + + fn end_space(&mut self) { + self.buffer.push(Fragment::Space); + } + + fn end_newline(&mut self) { + self.buffer.push(Fragment::Newline); + } + + fn end_text(&mut self, text: impl Into>) { self.buffer.push(Fragment::Text(text.into())); } - fn push_char(&mut self, character: char) { + fn end_char(&mut self, character: char) { self.buffer .push(Fragment::Text(character.to_string().into_boxed_str())); } - fn push_output(&mut self, output: &TOutput) + fn end_output(&mut self, output: &TOutput) where TOutput: SpanTextOutput, { output.output(self); } - fn push_span(&mut self, span: &Span) { + fn end_span(&mut self, span: &Span) { self.buffer.push(Fragment::Span { start_offset: span.start, end_offset: span.end, }); } + fn increase_indentation(&mut self) -> &mut Self { + self.buffer + .push(Fragment::IndentationChange(Indentation::Increase)); + self + } + + fn decrease_indentation(&mut self) -> &mut Self { + self.buffer + .push(Fragment::IndentationChange(Indentation::Decrease)); + self + } + + /// Removes the last fragment from the buffer if it is a space. + fn remove_space(&mut self) -> &mut Self { + self.buffer.pop_if(|last| !matches!(last, Fragment::Space)); + self + } + + /// Removes the last fragment from the buffer if it is a newline. + fn remove_newline(&mut self) -> &mut Self { + self.buffer + .pop_if(|last| !matches!(last, Fragment::Newline)); + self + } + pub fn format(self, file_content: &str) -> Result { let mut text = String::new(); + let mut indentation = String::new(); for fragment in self.buffer { match fragment { Fragment::Space => text.push(' '), - Fragment::Newline => text.push('\n'), - Fragment::Indentation => text.push_str(" "), + Fragment::Newline => { + text.push('\n'); + text.push_str(&indentation); + } + Fragment::IndentationChange(indent) => match indent { + Indentation::Increase => indentation += INDENT, + Indentation::Decrease => { + indentation.truncate(indentation.len().saturating_sub(INDENT.len())); + } + }, Fragment::Text(frag_text) => text.push_str(&frag_text), Fragment::Span { start_offset, diff --git a/formatter/src/main.rs b/formatter/src/main.rs index a9d8fde..22cd01e 100644 --- a/formatter/src/main.rs +++ b/formatter/src/main.rs @@ -58,7 +58,8 @@ fn main() { // stdin.read_to_string(&mut data).unwrap(); // For temporary testing - let data = include_str!("../../lib/resources/alpha040/std/array.ab"); + // let data = include_str!("../../lib/resources/alpha040/std/array.ab"); + let data = include_str!("../../run_coverage.ab"); let amber_compiler = AmberCompiler::new(); let tokenize = amber_compiler.tokenize(data); From 69ef28956883360bb8ed1e2050d95e6eda096474 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 23 Nov 2025 17:06:37 +0000 Subject: [PATCH 12/37] Function parsing Function parsing works from run_coverage --- formatter/src/alpha040/mod.rs | 75 +++++++++++++++++++++++++------ formatter/src/lib.rs | 85 +++++++++++++++++++++++++---------- 2 files changed, 123 insertions(+), 37 deletions(-) diff --git a/formatter/src/alpha040/mod.rs b/formatter/src/alpha040/mod.rs index 5ff5d0e..0f39f38 100644 --- a/formatter/src/alpha040/mod.rs +++ b/formatter/src/alpha040/mod.rs @@ -39,6 +39,8 @@ impl TextOutput for GlobalStatement { return_type, contents, ) => { + output.newline(); + for flag in compiler_flags { output.output(flag); output.newline(); @@ -78,9 +80,10 @@ impl TextOutput for GlobalStatement { dbg!(content); output.newline().end_output(content); } - output.char('}').newline().decrease_indentation(); + output.decrease_indentation().newline().char('}'); } GlobalStatement::Main(main, args, statements) => { + output.newline(); output.output(main); output.char('('); @@ -131,7 +134,7 @@ impl TextOutput for FunctionArgument { FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, is_ref.0, text), FunctionArgument::Typed(is_ref, text, _) => push_arg(output, is_ref.0, text), FunctionArgument::Error => { - output.span(span); + output.error(span); } } } @@ -216,10 +219,17 @@ impl TextOutput for Statement { } } Statement::IfChain(r#if, items) => { - output.output(r#if); + output.output(r#if).space().char('{').increase_indentation(); + for ele in items { - output.output(ele); + output.newline().output(ele); } + + output.decrease_indentation(); + if items.len() > 0 { + output.newline(); + } + output.char('}'); } Statement::ShorthandAdd(variable, expr) => shorthand(output, variable, "+=", expr), Statement::ShorthandSub(variable, expr) => shorthand(output, variable, "-=", expr), @@ -278,7 +288,7 @@ impl TextOutput for Statement { Statement::Block(block) => output.end_output(block), Statement::Comment(comment) => output.end_output(comment), Statement::Shebang(shebang) => output.end_text(shebang.as_str()), - Statement::Error => output.end_span(span), + Statement::Error => output.error(span), } } } @@ -324,13 +334,27 @@ impl TextOutput for ElseCondition { impl TextOutput for Comment { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.span(span); + match self { + Comment::Comment(comment) => output.text("//").space().end_text(comment.as_str()), + Comment::DocString(doc_comment) => { + output.text("///").space().end_text(doc_comment.as_str()) + } + } } } impl TextOutput for IfCondition { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.span(span); + match self { + IfCondition::IfCondition(condition, block) => { + output.output(condition).space().end_output(block); + } + IfCondition::InlineIfCondition(condition, statement) => { + output.output(condition).char(':').space().output(statement); + } + IfCondition::Comment(comment) => output.end_output(comment), + IfCondition::Error => output.error(span), + } } } @@ -339,7 +363,7 @@ impl TextOutput for VariableInitType { match self { VariableInitType::Expression(expr) => output.end_output(expr), VariableInitType::DataType(r#type) => output.end_output(r#type), - VariableInitType::Error => output.end_span(span), + VariableInitType::Error => output.error(span), } } } @@ -393,15 +417,21 @@ impl TextOutput for Expression { output.output(boolean); } Expression::Text(texts) => { + if texts.len() > 0 { + output.char('"'); + } for text in texts { output.output(text); } + if texts.len() > 0 { + output.char('"'); + } } Expression::Parentheses(parentheses) => { output.output(parentheses); } Expression::Var(var) => { - output.output(var); + output.char('{').output(var).end_char('}'); } Expression::Add(rhs, lhs) => char_separated(output, rhs, '+', lhs), Expression::Subtract(rhs, lhs) => char_separated(output, rhs, '-', lhs), @@ -516,7 +546,7 @@ impl TextOutput for Expression { } } Expression::Error => { - output.span(span); + output.error(span); } } } @@ -550,17 +580,36 @@ impl TextOutput for FailureHandler { } } -impl TextOutput for CommandModifier {} +impl TextOutput for CommandModifier { + fn output(&self, span: &lib::grammar::Span, output: &mut Output) { + output.text(format!("{}", self)); + } +} impl TextOutput for InterpolatedText { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.span(span); + match self { + InterpolatedText::Escape(escape) => output + .debug_point("InterpolatedText escape") + .end_output(escape), + InterpolatedText::Expression(expr) => output.end_output(expr), + InterpolatedText::Text(text) => output.output(text).end_space(), + } } } impl TextOutput for InterpolatedCommand { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.span(span); + match self { + InterpolatedCommand::Escape(escape) => output + .debug_point("InterpolatedCommand escape") + .end_text(escape.as_str()), + InterpolatedCommand::CommandOption(option) => output + .debug_point("InterpolatedCommand command options") + .end_text(option.as_str()), + InterpolatedCommand::Expression(expr) => output.end_output(expr), + InterpolatedCommand::Text(text) => output.end_text(text.as_str()), + } } } diff --git a/formatter/src/lib.rs b/formatter/src/lib.rs index 5475036..318bbf9 100644 --- a/formatter/src/lib.rs +++ b/formatter/src/lib.rs @@ -4,13 +4,46 @@ use std::{panic::Location, string::FromUtf8Error}; mod alpha040; /// The content of an indentation. -const INDENT: &str = " "; +const INDENT: &str = " "; #[derive(Default)] pub struct Output { buffer: Vec, } +pub struct FragmentSpan { + /// Start byte offset into source file. + start_offset: usize, + /// End byte offset into source file. + end_offset: usize, +} + +impl FragmentSpan { + pub fn new(start_offset: usize, end_offset: usize) -> Self { + Self { + start_offset: start_offset.min(end_offset), + end_offset: start_offset.max(end_offset), + } + } + + pub fn end_offset(&self) -> usize { + self.end_offset + } + + pub fn start_offset(&self) -> usize { + self.start_offset + } +} + +impl From<&Span> for FragmentSpan { + fn from(value: &Span) -> Self { + FragmentSpan { + start_offset: value.start, + end_offset: value.end.saturating_sub(1), + } + } +} + pub enum Fragment { Space, Newline, @@ -18,15 +51,11 @@ pub enum Fragment { /// This has no output in its current position, but will change the indentation after every newline. IndentationChange(Indentation), Text(Box), - Span { - /// Start byte offset into source file. - start_offset: usize, - /// End byte offset into source file. - end_offset: usize, - }, + Span(FragmentSpan), + ParseError(FragmentSpan), } -enum Indentation { +pub enum Indentation { Increase, Decrease, } @@ -80,6 +109,10 @@ impl Output { )) } + fn error(&mut self, span: &Span) { + self.span(span); + } + fn space(&mut self) -> &mut Self { self.buffer.push(Fragment::Space); self @@ -110,10 +143,7 @@ impl Output { } fn span(&mut self, span: &Span) -> &mut Self { - self.buffer.push(Fragment::Span { - start_offset: span.start, - end_offset: span.end, - }); + self.buffer.push(Fragment::Span(span.into())); self } @@ -142,10 +172,7 @@ impl Output { } fn end_span(&mut self, span: &Span) { - self.buffer.push(Fragment::Span { - start_offset: span.start, - end_offset: span.end, - }); + self.span(span); } fn increase_indentation(&mut self) -> &mut Self { @@ -191,19 +218,29 @@ impl Output { } }, Fragment::Text(frag_text) => text.push_str(&frag_text), - Fragment::Span { - start_offset, - end_offset, - } => { - let start = start_offset.min(end_offset); - let end = start_offset.max(end_offset); + Fragment::Span(span) => { + let span = file_content + .as_bytes() + .get(span.start_offset()..=span.end_offset()) + .ok_or_else(|| FormattingError::SpanDoesntExist { + start: span.start_offset(), + end: span.end_offset(), + })?; + let span_text = String::from_utf8(span.to_vec())?; + text.push_str(&span_text); + } + Fragment::ParseError(span) => { let span = file_content .as_bytes() - .get(start..=end) - .ok_or_else(|| FormattingError::SpanDoesntExist { start, end })?; + .get(span.start_offset()..=span.end_offset()) + .ok_or_else(|| FormattingError::SpanDoesntExist { + start: span.start_offset(), + end: span.end_offset(), + })?; let span_text = String::from_utf8(span.to_vec())?; + eprintln!("Unable to parse '{span_text}'. Failing back to sourcefile content"); text.push_str(&span_text); } } From 80ebc9458e340a338c1bc53bfc89a7ede0ef251e Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 30 Nov 2025 18:16:27 +0000 Subject: [PATCH 13/37] Fix fragment remove method implementations --- formatter/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/formatter/src/lib.rs b/formatter/src/lib.rs index 318bbf9..a4dcbe9 100644 --- a/formatter/src/lib.rs +++ b/formatter/src/lib.rs @@ -189,14 +189,13 @@ impl Output { /// Removes the last fragment from the buffer if it is a space. fn remove_space(&mut self) -> &mut Self { - self.buffer.pop_if(|last| !matches!(last, Fragment::Space)); + self.buffer.pop_if(|last| matches!(last, Fragment::Space)); self } /// Removes the last fragment from the buffer if it is a newline. fn remove_newline(&mut self) -> &mut Self { - self.buffer - .pop_if(|last| !matches!(last, Fragment::Newline)); + self.buffer.pop_if(|last| matches!(last, Fragment::Newline)); self } From 253d414fafa69d600787652302ce127d6d21e273 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 30 Nov 2025 18:18:13 +0000 Subject: [PATCH 14/37] Use file content for text type The formatter should not modify program strings. --- formatter/src/alpha040/mod.rs | 86 ++++++++++++++--------------------- 1 file changed, 34 insertions(+), 52 deletions(-) diff --git a/formatter/src/alpha040/mod.rs b/formatter/src/alpha040/mod.rs index 0f39f38..deeab83 100644 --- a/formatter/src/alpha040/mod.rs +++ b/formatter/src/alpha040/mod.rs @@ -6,7 +6,7 @@ use lib::{ alpha040::{ Block, Comment, ElseCondition, Expression, FailureHandler, FunctionArgument, GlobalStatement, IfChainContent, IfCondition, ImportContent, InterpolatedCommand, - InterpolatedText, IterLoopVars, Statement, VariableInitType, + IterLoopVars, Statement, VariableInitType, }, }, }; @@ -77,7 +77,7 @@ impl TextOutput for GlobalStatement { output.char('{').increase_indentation(); for content in contents { - dbg!(content); + // dbg!(content); output.newline().end_output(content); } output.decrease_indentation().newline().char('}'); @@ -90,11 +90,14 @@ impl TextOutput for GlobalStatement { if let Some(args) = args { output.output(args); } - output.char(')'); + output.char(')').space().char('{').increase_indentation(); for statement in statements { - output.output(statement); + dbg!(statement); + output.newline().output(statement); } + + output.decrease_indentation().newline().end_char('}'); } GlobalStatement::Statement(statement) => output.end_output(statement), } @@ -203,12 +206,8 @@ impl TextOutput for Statement { .output(new_value); } Statement::IfCondition(r#if, condition, items, else_condition) => { - output - .output(r#if) - .space() - .output(condition) - .space() - .debug_point("If condition items"); + output.output(r#if).space().output(condition).end_space(); + // .debug_point("If condition items"); for ele in items { output.output(ele); @@ -301,22 +300,26 @@ impl TextOutput for IterLoopVars { impl TextOutput for Block { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - output.debug_point("Block").span(span); - // match self { - // Block::Block(modifiers, statements) => { - // output.char('{').end_newline(); - - // for modifier in modifiers { - // output.output(modifier).end_space(); - // } - // for statement in statements { - // output.output(statement).end_newline(); - // } - - // output.char('}').end_newline(); - // } - // Block::Error => output.end_span(span), - // } + // output.debug_point("Block").span(span); + match self { + Block::Block(modifiers, statements) => { + output.char('{').increase_indentation().end_newline(); + + for modifier in modifiers { + output.output(modifier).end_space(); + } + for statement in statements { + output.output(statement).end_newline(); + } + + output + .remove_newline() + .decrease_indentation() + .newline() + .char('}'); + } + Block::Error => output.end_span(span), + } } } @@ -347,7 +350,7 @@ impl TextOutput for IfCondition { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { match self { IfCondition::IfCondition(condition, block) => { - output.output(condition).space().end_output(block); + output.output(condition).space().output(block).newline(); } IfCondition::InlineIfCondition(condition, statement) => { output.output(condition).char(':').space().output(statement); @@ -416,16 +419,9 @@ impl TextOutput for Expression { Expression::Boolean(boolean) => { output.output(boolean); } - Expression::Text(texts) => { - if texts.len() > 0 { - output.char('"'); - } - for text in texts { - output.output(text); - } - if texts.len() > 0 { - output.char('"'); - } + Expression::Text(_) => { + // Take raw text from file, as string content should not be modified + output.end_span(span); } Expression::Parentheses(parentheses) => { output.output(parentheses); @@ -586,27 +582,13 @@ impl TextOutput for CommandModifier { } } -impl TextOutput for InterpolatedText { - fn output(&self, span: &lib::grammar::Span, output: &mut Output) { - match self { - InterpolatedText::Escape(escape) => output - .debug_point("InterpolatedText escape") - .end_output(escape), - InterpolatedText::Expression(expr) => output.end_output(expr), - InterpolatedText::Text(text) => output.output(text).end_space(), - } - } -} - impl TextOutput for InterpolatedCommand { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { match self { InterpolatedCommand::Escape(escape) => output .debug_point("InterpolatedCommand escape") .end_text(escape.as_str()), - InterpolatedCommand::CommandOption(option) => output - .debug_point("InterpolatedCommand command options") - .end_text(option.as_str()), + InterpolatedCommand::CommandOption(option) => output.end_text(option.as_str()), InterpolatedCommand::Expression(expr) => output.end_output(expr), InterpolatedCommand::Text(text) => output.end_text(text.as_str()), } From 3bd90c8091daebe3af77d4304addd0b5c9ad79d5 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Thu, 11 Dec 2025 21:38:10 +0000 Subject: [PATCH 15/37] Allow same-line comments --- formatter/src/alpha040/mod.rs | 7 ++- formatter/src/lib.rs | 90 ++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/formatter/src/alpha040/mod.rs b/formatter/src/alpha040/mod.rs index deeab83..8c67c33 100644 --- a/formatter/src/alpha040/mod.rs +++ b/formatter/src/alpha040/mod.rs @@ -338,9 +338,12 @@ impl TextOutput for ElseCondition { impl TextOutput for Comment { fn output(&self, span: &lib::grammar::Span, output: &mut Output) { match self { - Comment::Comment(comment) => output.text("//").space().end_text(comment.as_str()), + Comment::Comment(comment) => output.end_comment("//", comment.as_str(), span), Comment::DocString(doc_comment) => { - output.text("///").space().end_text(doc_comment.as_str()) + output + .text("///") + .space() + .end_comment("///", doc_comment.as_str(), span) } } } diff --git a/formatter/src/lib.rs b/formatter/src/lib.rs index a4dcbe9..b983dee 100644 --- a/formatter/src/lib.rs +++ b/formatter/src/lib.rs @@ -6,6 +6,19 @@ mod alpha040; /// The content of an indentation. const INDENT: &str = " "; +const WHITESPACE: [char; 4] = [' ', '\n', '\r', '\t']; +const WHITESPACE_BYTES: [u8; 4] = { + let mut array = [0; WHITESPACE.len()]; + let mut index = 0; + + while index < WHITESPACE.len() { + array[index] = WHITESPACE[index] as u8; + index += 1; + } + + array +}; + #[derive(Default)] pub struct Output { buffer: Vec, @@ -51,6 +64,13 @@ pub enum Fragment { /// This has no output in its current position, but will change the indentation after every newline. IndentationChange(Indentation), Text(Box), + Comment { + /// Dentoes the start of a comment. E.G "//" or "///". + marker: Box, + text: Box, + /// Byte index of the source file where the comment starts. + start_index: usize, + }, Span(FragmentSpan), ParseError(FragmentSpan), } @@ -128,6 +148,20 @@ impl Output { self } + fn comment( + &mut self, + marker: impl Into>, + text: impl Into>, + span: &Span, + ) -> &mut Self { + self.buffer.push(Fragment::Comment { + marker: marker.into(), + text: text.into(), + start_index: span.start, + }); + self + } + fn char(&mut self, character: char) -> &mut Self { self.buffer .push(Fragment::Text(character.to_string().into_boxed_str())); @@ -159,6 +193,14 @@ impl Output { self.buffer.push(Fragment::Text(text.into())); } + fn end_comment(&mut self, marker: impl Into>, text: impl Into>, span: &Span) { + self.buffer.push(Fragment::Comment { + marker: marker.into(), + text: text.into(), + start_index: span.start, + }); + } + fn end_char(&mut self, character: char) { self.buffer .push(Fragment::Text(character.to_string().into_boxed_str())); @@ -203,10 +245,40 @@ impl Output { let mut text = String::new(); let mut indentation = String::new(); - for fragment in self.buffer { + let mut iter = self.buffer.into_iter().peekable(); + while let Some(fragment) = iter.next() { match fragment { Fragment::Space => text.push(' '), Fragment::Newline => { + // Don't add newline if comment is on sameline + if let Some(Fragment::Comment { + marker: _, + text: _, + start_index, + }) = iter.peek() + { + let mut index = *start_index; + let mut on_newline = false; + + while let Some(character) = file_content.as_bytes().get(index).cloned() { + index -= 1; + let character: char = character.into(); + + if character == '\n' || character == '\r' { + on_newline = true; + break; + } + + if ![' ', '\t', '/'].contains(&character) { + break; + } + } + + if !on_newline { + continue; + } + } + text.push('\n'); text.push_str(&indentation); } @@ -242,6 +314,22 @@ impl Output { eprintln!("Unable to parse '{span_text}'. Failing back to sourcefile content"); text.push_str(&span_text); } + Fragment::Comment { + marker, + text: comment, + start_index: _, + } => { + // Ensure that there is a space between the comment and previous code + if let Some(previous) = text.as_bytes().last() + && !WHITESPACE_BYTES.contains(previous) + { + text.push(' '); + } + + text.push_str(&marker); + text.push(' '); + text.push_str(&comment); + } } } From d64d4146d42ebff38a5204f0713d52e988f14eff Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 14 Dec 2025 16:34:33 +0000 Subject: [PATCH 16/37] Do not format bash commands --- crates/formatter/src/alpha040/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 4f451cf..6587f6f 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -492,13 +492,13 @@ impl TextOutput for Expression { output.space(); } + // Do not format bash commands for command in commands { - output.output(command); - output.space(); + output.end_span(&command.1); } if let Some(failure_handler) = failure_handler { - output.output(failure_handler); + output.space().output(failure_handler); } } Expression::Array(array) => { From 618013694e12b3e4bf1f868a62815fab9b36c140 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 14 Dec 2025 16:40:07 +0000 Subject: [PATCH 17/37] Ignore unused variables --- crates/formatter/src/alpha040/mod.rs | 12 ++++++------ crates/formatter/src/main.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 6587f6f..8291b10 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -11,7 +11,7 @@ use crate::SpanTextOutput; use crate::{Output, TextOutput}; impl TextOutput for GlobalStatement { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { match self { GlobalStatement::Import(public, import, content, from, path) => { if public.0 { @@ -102,7 +102,7 @@ impl TextOutput for GlobalStatement { } impl TextOutput for ImportContent { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { match self { ImportContent::ImportAll => { output.char('*'); @@ -141,13 +141,13 @@ impl TextOutput for FunctionArgument { } impl TextOutput for CompilerFlag { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { output.text(format!("#[{self}]")); } } impl TextOutput for String { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { output.text(self.clone()); } } @@ -577,7 +577,7 @@ impl TextOutput for FailureHandler { } impl TextOutput for CommandModifier { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { match self { CommandModifier::Unsafe => output.end_text("unsafe"), CommandModifier::Trust => output.end_text("trust"), @@ -588,7 +588,7 @@ impl TextOutput for CommandModifier { } impl TextOutput for InterpolatedCommand { - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, _span: &Span, output: &mut Output) { match self { InterpolatedCommand::Escape(escape) => output .debug_point("InterpolatedCommand escape") diff --git a/crates/formatter/src/main.rs b/crates/formatter/src/main.rs index 48dfd36..a868365 100644 --- a/crates/formatter/src/main.rs +++ b/crates/formatter/src/main.rs @@ -10,8 +10,8 @@ fn main() { println!("{:?}", parse.ast); match parse.ast { - Grammar::Alpha034(items) => todo!(), - Grammar::Alpha035(items) => todo!(), + Grammar::Alpha034(_items) => todo!(), + Grammar::Alpha035(_items) => todo!(), Grammar::Alpha040(items) => { if let Some(items) = items { { @@ -25,6 +25,6 @@ fn main() { } } } - Grammar::Alpha050(items) => todo!(), + Grammar::Alpha050(_items) => todo!(), } } From a09154bddbf066074d50389c7f5433c2d5199f60 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 14 Dec 2025 16:44:38 +0000 Subject: [PATCH 18/37] Do not surround variables with {} --- crates/formatter/src/alpha040/mod.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 8291b10..55bbb6f 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -426,9 +426,7 @@ impl TextOutput for Expression { Expression::Parentheses(parentheses) => { output.output(parentheses); } - Expression::Var(var) => { - output.char('{').output(var).end_char('}'); - } + Expression::Var(var) => output.end_output(var), Expression::Add(rhs, lhs) => char_separated(output, rhs, '+', lhs), Expression::Subtract(rhs, lhs) => char_separated(output, rhs, '-', lhs), Expression::Multiply(rhs, lhs) => char_separated(output, rhs, '*', lhs), From 9f115a4a84efff31c22414b92f988b1aef7ef3df Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 14 Dec 2025 16:55:34 +0000 Subject: [PATCH 19/37] Properly handle imports --- crates/formatter/src/alpha040/mod.rs | 29 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 55bbb6f..2a65246 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -18,14 +18,17 @@ impl TextOutput for GlobalStatement { output.text("pub "); } - output.output(import); - output.space(); - output.output(content); - output.space(); - output.output(from); - output.space(); - output.output(path); - output.newline(); + output + .output(import) + .space() + .output(content) + .space() + .output(from) + .space() + .char('"') + .output(path) + .char('"') + .newline(); } GlobalStatement::FunctionDefinition( compiler_flags, @@ -109,10 +112,14 @@ impl TextOutput for ImportContent { } ImportContent::ImportSpecific(items) => { output.text("{ "); - for identifier in items { - output.output(identifier); - output.space(); + + for identifier in items.iter().take(items.len().saturating_sub(1)) { + output.output(identifier).char(',').space(); + } + if let Some(item) = items.last() { + output.output(item).end_space(); } + output.char('}'); } } From a3b5337b2c148e69014f7d025e6f913d54f3ef8c Mon Sep 17 00:00:00 2001 From: tye-exe Date: Mon, 29 Dec 2025 19:57:58 +0000 Subject: [PATCH 20/37] Correctly handle doc comments --- crates/formatter/src/alpha040/mod.rs | 13 +++----- crates/formatter/src/lib.rs | 47 ++++++++++++++++++++++------ crates/formatter/src/main.rs | 5 +-- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 2a65246..8dc96e1 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -7,7 +7,7 @@ use amber_grammar::{CommandModifier, CompilerFlag}; use amber_types::DataType; use amber_types::token::Span; -use crate::SpanTextOutput; +use crate::{CommentVariant, SpanTextOutput}; use crate::{Output, TextOutput}; impl TextOutput for GlobalStatement { @@ -77,7 +77,6 @@ impl TextOutput for GlobalStatement { output.char('{').increase_indentation(); for content in contents { - // dbg!(content); output.newline().end_output(content); } output.decrease_indentation().newline().char('}'); @@ -93,7 +92,6 @@ impl TextOutput for GlobalStatement { output.char(')').space().char('{').increase_indentation(); for statement in statements { - dbg!(statement); output.newline().output(statement); } @@ -342,12 +340,11 @@ impl TextOutput for ElseCondition { impl TextOutput for Comment { fn output(&self, span: &Span, output: &mut Output) { match self { - Comment::Comment(comment) => output.end_comment("//", comment.as_str(), span), + Comment::Comment(comment) => { + output.end_comment(CommentVariant::Regular, comment.as_str(), span) + } Comment::DocString(doc_comment) => { - output - .text("///") - .space() - .end_comment("///", doc_comment.as_str(), span) + output.end_comment(CommentVariant::Doc, doc_comment.as_str(), span) } } } diff --git a/crates/formatter/src/lib.rs b/crates/formatter/src/lib.rs index 171891e..a7f7dc2 100644 --- a/crates/formatter/src/lib.rs +++ b/crates/formatter/src/lib.rs @@ -19,11 +19,12 @@ const WHITESPACE_BYTES: [u8; 4] = { array }; -#[derive(Default)] +#[derive(Default, Debug)] pub struct Output { buffer: Vec, } +#[derive(Debug)] pub struct FragmentSpan { /// Start byte offset into source file. start_offset: usize, @@ -57,6 +58,7 @@ impl From<&Span> for FragmentSpan { } } +#[derive(Debug)] pub enum Fragment { Space, Newline, @@ -66,7 +68,7 @@ pub enum Fragment { Text(Box), Comment { /// Dentoes the start of a comment. E.G "//" or "///". - marker: Box, + variant: CommentVariant, text: Box, /// Byte index of the source file where the comment starts. start_index: usize, @@ -75,11 +77,31 @@ pub enum Fragment { ParseError(FragmentSpan), } +#[derive(Debug)] pub enum Indentation { Increase, Decrease, } +/// Comments can either be doc comments or regular comments. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CommentVariant { + /// Doc comment + Doc, + /// Regular comment + Regular, +} + +impl CommentVariant { + /// Returns the string that this comment variant is denoted by. + fn denoted_by(self) -> &'static str { + match self { + CommentVariant::Doc => "///", + CommentVariant::Regular => "//", + } + } +} + pub trait SpanTextOutput { fn output(&self, output: &mut Output); } @@ -150,12 +172,12 @@ impl Output { fn comment( &mut self, - marker: impl Into>, + variant: CommentVariant, text: impl Into>, span: &Span, ) -> &mut Self { self.buffer.push(Fragment::Comment { - marker: marker.into(), + variant, text: text.into(), start_index: span.start, }); @@ -193,9 +215,9 @@ impl Output { self.buffer.push(Fragment::Text(text.into())); } - fn end_comment(&mut self, marker: impl Into>, text: impl Into>, span: &Span) { + fn end_comment(&mut self, variant: CommentVariant, text: impl Into>, span: &Span) { self.buffer.push(Fragment::Comment { - marker: marker.into(), + variant, text: text.into(), start_index: span.start, }); @@ -252,7 +274,7 @@ impl Output { Fragment::Newline => { // Don't add newline if comment is on sameline if let Some(Fragment::Comment { - marker: _, + variant: _, text: _, start_index, }) = iter.peek() @@ -315,7 +337,7 @@ impl Output { text.push_str(&span_text); } Fragment::Comment { - marker, + variant, text: comment, start_index: _, } => { @@ -326,9 +348,16 @@ impl Output { text.push(' '); } - text.push_str(&marker); + text.push_str(variant.denoted_by()); text.push(' '); text.push_str(&comment); + + if let Some(Fragment::Comment { variant, .. }) = iter.peek() + && *variant == CommentVariant::Doc + && matches!(variant, CommentVariant::Doc) + { + text.push('\n'); + } } } } diff --git a/crates/formatter/src/main.rs b/crates/formatter/src/main.rs index a868365..2b7d241 100644 --- a/crates/formatter/src/main.rs +++ b/crates/formatter/src/main.rs @@ -2,13 +2,13 @@ use amber_fmt::Output; use amber_grammar::{Grammar, LSPAnalysis as _, alpha040::AmberCompiler}; fn main() { - let data = include_str!("../../../run_coverage.ab"); + // let data = include_str!("../../../run_coverage.ab"); + let data = include_str!("../../../resources/alpha040/std/fs.ab"); let amber_compiler = AmberCompiler::new(); let tokenize = amber_compiler.tokenize(data); let parse = amber_compiler.parse(&tokenize); - println!("{:?}", parse.ast); match parse.ast { Grammar::Alpha034(_items) => todo!(), Grammar::Alpha035(_items) => todo!(), @@ -20,6 +20,7 @@ fn main() { use amber_fmt::SpanTextOutput; (&item).output(&mut output); } + eprintln!("{output:?}"); let format = output.format(data).expect("Able to parse"); println!("{format}"); } From c26376f7e0e69ab41ef76f98bb03fc8b6f92dca1 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 4 Jan 2026 21:00:51 +0000 Subject: [PATCH 21/37] Give formatter program context --- crates/formatter/src/alpha040/mod.rs | 402 +++++++++++++++------------ crates/formatter/src/lib.rs | 82 ++++-- crates/formatter/src/main.rs | 13 +- 3 files changed, 295 insertions(+), 202 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 8dc96e1..9c7cb11 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -7,11 +7,13 @@ use amber_grammar::{CommandModifier, CompilerFlag}; use amber_types::DataType; use amber_types::token::Span; -use crate::{CommentVariant, SpanTextOutput}; +use crate::{CommentVariant, FmtContext, SpanTextOutput}; use crate::{Output, TextOutput}; -impl TextOutput for GlobalStatement { - fn output(&self, _span: &Span, output: &mut Output) { +type Gen = (GlobalStatement, Span); + +impl TextOutput for GlobalStatement { + fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { GlobalStatement::Import(public, import, content, from, path) => { if public.0 { @@ -19,14 +21,14 @@ impl TextOutput for GlobalStatement { } output - .output(import) + .output(ctx, import) .space() - .output(content) + .output(ctx, content) .space() - .output(from) + .output(ctx, from) .space() .char('"') - .output(path) + .output(ctx, path) .char('"') .newline(); } @@ -39,10 +41,10 @@ impl TextOutput for GlobalStatement { return_type, contents, ) => { - output.newline(); + // output.newline(); for flag in compiler_flags { - output.output(flag); + output.output(ctx, flag); output.newline(); } @@ -50,21 +52,21 @@ impl TextOutput for GlobalStatement { output.text("pub "); } - output.output(function_keyword); + output.output(ctx, function_keyword); output.space(); - output.output(name); + output.output(ctx, name); output.char('('); // Handle adding variables with proper spacing { for arg in args.iter().take(args.len().saturating_sub(1)) { - output.output(arg); + output.output(ctx, arg); output.char(','); output.space(); } if let Some(arg) = args.last() { - output.output(arg); + output.output(ctx, arg); } } output.char(')'); @@ -72,38 +74,50 @@ impl TextOutput for GlobalStatement { if let Some(returns) = return_type { output.char(':'); output.space(); - output.output(returns); + output.output(ctx, returns); } output.char('{').increase_indentation(); for content in contents { - output.newline().end_output(content); + output.newline().end_output(ctx, content); } - output.decrease_indentation().newline().char('}'); + output + .decrease_indentation() + .newline() + .char('}') + .newline() + .newline(); } GlobalStatement::Main(main, args, statements) => { - output.newline(); - output.output(main); + // output.newline(); + output.output(ctx, main); output.char('('); if let Some(args) = args { - output.output(args); + output.output(ctx, args); } output.char(')').space().char('{').increase_indentation(); for statement in statements { - output.newline().output(statement); + output.newline().output(ctx, statement); } - output.decrease_indentation().newline().end_char('}'); + output + .decrease_indentation() + .newline() + .char('}') + .newline() + .newline(); + } + GlobalStatement::Statement(statement) => { + output.output(ctx, statement).newline(); } - GlobalStatement::Statement(statement) => output.end_output(statement), } } } -impl TextOutput for ImportContent { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for ImportContent { + fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { ImportContent::ImportAll => { output.char('*'); @@ -112,10 +126,10 @@ impl TextOutput for ImportContent { output.text("{ "); for identifier in items.iter().take(items.len().saturating_sub(1)) { - output.output(identifier).char(',').space(); + output.output(ctx, identifier).char(',').space(); } if let Some(item) = items.last() { - output.output(item).end_space(); + output.output(ctx, item).end_space(); } output.char('}'); @@ -124,20 +138,25 @@ impl TextOutput for ImportContent { } } -impl TextOutput for FunctionArgument { - fn output(&self, span: &Span, output: &mut Output) { - fn push_arg(output: &mut Output, is_ref: bool, text: &impl SpanTextOutput) { +impl TextOutput for FunctionArgument { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn push_arg( + output: &mut Output, + ctx: &mut FmtContext, + is_ref: bool, + text: &impl SpanTextOutput, + ) { if is_ref { output.text("ref"); output.space(); } - output.output(text); + output.output(ctx, text); } match self { - FunctionArgument::Generic(is_ref, text) => push_arg(output, is_ref.0, text), - FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, is_ref.0, text), - FunctionArgument::Typed(is_ref, text, _) => push_arg(output, is_ref.0, text), + FunctionArgument::Generic(is_ref, text) => push_arg(output, ctx, is_ref.0, text), + FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, ctx, is_ref.0, text), + FunctionArgument::Typed(is_ref, text, _) => push_arg(output, ctx, is_ref.0, text), FunctionArgument::Error => { output.error(span); } @@ -145,85 +164,94 @@ impl TextOutput for FunctionArgument { } } -impl TextOutput for CompilerFlag { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for CompilerFlag { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.text(format!("#[{self}]")); } } -impl TextOutput for String { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for String { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.text(self.clone()); } } -impl TextOutput for DataType {} +impl TextOutput for DataType {} -impl TextOutput for Statement { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for Statement { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { fn shorthand( output: &mut Output, - variable: &impl SpanTextOutput, + ctx: &mut FmtContext, + variable: &impl SpanTextOutput, separator: &str, - expression: &impl SpanTextOutput, + expression: &impl SpanTextOutput, ) { output - .output(variable) + .output(ctx, variable) .space() .text(separator) .space() - .output(expression); + .output(ctx, expression); } match self { Statement::Expression(expression) => { - output.output(expression); + output.output(ctx, expression); } Statement::VariableInit(keyword, name, init) => { output - .output(keyword) + .output(ctx, keyword) .space() - .output(name) + .output(ctx, name) .space() .char('=') .space() - .output(init); + .output(ctx, init); } Statement::ConstInit(keyword, name, init) => { output - .output(keyword) + .output(ctx, keyword) .space() - .output(name) + .output(ctx, name) .space() .char('=') .space() - .output(init); + .output(ctx, init); } Statement::VariableSet(name, new_value) => { output - .output(name) + .output(ctx, name) .space() .char('=') .space() - .output(new_value); + .output(ctx, new_value); } Statement::IfCondition(r#if, condition, items, else_condition) => { - output.output(r#if).space().output(condition).end_space(); + output + .output(ctx, r#if) + .space() + .output(ctx, condition) + .end_space(); // .debug_point("If condition items"); for ele in items { - output.output(ele); + output.output(ctx, ele); } if let Some(else_condition) = else_condition { - output.output(else_condition); + output.output(ctx, else_condition); } } Statement::IfChain(r#if, items) => { - output.output(r#if).space().char('{').increase_indentation(); + output + .output(ctx, r#if) + .space() + .char('{') + .increase_indentation(); for ele in items { - output.newline().output(ele); + output.newline().output(ctx, ele); } output.decrease_indentation(); @@ -232,86 +260,88 @@ impl TextOutput for Statement { } output.char('}'); } - Statement::ShorthandAdd(variable, expr) => shorthand(output, variable, "+=", expr), - Statement::ShorthandSub(variable, expr) => shorthand(output, variable, "-=", expr), - Statement::ShorthandMul(variable, expr) => shorthand(output, variable, "*=", expr), - Statement::ShorthandDiv(variable, expr) => shorthand(output, variable, "/=", expr), - Statement::ShorthandModulo(variable, expr) => shorthand(output, variable, "%=", expr), + Statement::ShorthandAdd(variable, expr) => shorthand(output, ctx, variable, "+=", expr), + Statement::ShorthandSub(variable, expr) => shorthand(output, ctx, variable, "-=", expr), + Statement::ShorthandMul(variable, expr) => shorthand(output, ctx, variable, "*=", expr), + Statement::ShorthandDiv(variable, expr) => shorthand(output, ctx, variable, "/=", expr), + Statement::ShorthandModulo(variable, expr) => { + shorthand(output, ctx, variable, "%=", expr) + } Statement::InfiniteLoop(r#loop, block) => { - output.output(r#loop).space().output(block); + output.output(ctx, r#loop).space().output(ctx, block); } Statement::IterLoop(r#for, element, r#in, expr, block) => { output - .output(r#for) + .output(ctx, r#for) .space() - .output(element) + .output(ctx, element) .space() - .output(r#in) + .output(ctx, r#in) .space() - .output(expr) + .output(ctx, expr) .space() - .end_output(block); + .end_output(ctx, block); } Statement::Break => output.end_text("break"), Statement::Continue => output.end_text("continue"), Statement::Return(r#return, expr) => { - output.end_output(r#return); + output.end_output(ctx, r#return); if let Some(expr) = expr { - output.space().end_output(expr); + output.space().end_output(ctx, expr); } } Statement::Fail(fail, expr) => { - output.end_output(fail); + output.end_output(ctx, fail); if let Some(expr) = expr { - output.space().end_output(expr); + output.space().end_output(ctx, expr); } } - Statement::Echo(echo, text) => output.output(echo).space().end_output(text), - Statement::Cd(cd, text) => output.output(cd).space().end_output(text), + Statement::Echo(echo, text) => output.output(ctx, echo).space().end_output(ctx, text), + Statement::Cd(cd, text) => output.output(ctx, cd).space().end_output(ctx, text), Statement::MoveFiles(modifiers, mv, source, destination, failure_handler) => { for modifier in modifiers { - output.output(modifier).space(); + output.output(ctx, modifier).space(); } output - .output(mv) + .output(ctx, mv) .space() - .output(source) + .output(ctx, source) .space() - .output(destination); + .output(ctx, destination); if let Some(failure_handler) = failure_handler { - output.space().output(failure_handler); + output.space().output(ctx, failure_handler); } } - Statement::Block(block) => output.end_output(block), - Statement::Comment(comment) => output.end_output(comment), + Statement::Block(block) => output.end_output(ctx, block), + Statement::Comment(comment) => output.end_output(ctx, comment), Statement::Shebang(shebang) => output.end_text(shebang.as_str()), Statement::Error => output.error(span), } } } -impl TextOutput for IterLoopVars { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for IterLoopVars { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.span(span); } } -impl TextOutput for Block { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for Block { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { // output.debug_point("Block").span(span); match self { Block::Block(modifiers, statements) => { output.char('{').increase_indentation().end_newline(); for modifier in modifiers { - output.output(modifier).end_space(); + output.output(ctx, modifier).end_space(); } for statement in statements { - output.output(statement).end_newline(); + output.output(ctx, statement).end_newline(); } output @@ -325,20 +355,20 @@ impl TextOutput for Block { } } -impl TextOutput for IfChainContent { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for IfChainContent { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.span(span); } } -impl TextOutput for ElseCondition { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for ElseCondition { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.span(span); } } -impl TextOutput for Comment { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for Comment { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { Comment::Comment(comment) => { output.end_comment(CommentVariant::Regular, comment.as_str(), span) @@ -350,147 +380,158 @@ impl TextOutput for Comment { } } -impl TextOutput for IfCondition { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for IfCondition { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { IfCondition::IfCondition(condition, block) => { - output.output(condition).space().output(block).newline(); + output + .output(ctx, condition) + .space() + .output(ctx, block) + .newline(); } IfCondition::InlineIfCondition(condition, statement) => { - output.output(condition).char(':').space().output(statement); + output + .output(ctx, condition) + .char(':') + .space() + .output(ctx, statement); } - IfCondition::Comment(comment) => output.end_output(comment), + IfCondition::Comment(comment) => output.end_output(ctx, comment), IfCondition::Error => output.error(span), } } } -impl TextOutput for VariableInitType { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for VariableInitType { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { - VariableInitType::Expression(expr) => output.end_output(expr), - VariableInitType::DataType(r#type) => output.end_output(r#type), + VariableInitType::Expression(expr) => output.end_output(ctx, expr), + VariableInitType::DataType(r#type) => output.end_output(ctx, r#type), VariableInitType::Error => output.error(span), } } } -impl TextOutput for Expression { - fn output(&self, span: &Span, output: &mut Output) { +impl TextOutput for Expression { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { fn char_separated( output: &mut Output, - rhs: &impl SpanTextOutput, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, middle: char, - lhs: &impl SpanTextOutput, + lhs: &impl SpanTextOutput, ) { - output.output(rhs); + output.output(ctx, rhs); output.space(); output.char(middle); output.space(); - output.output(lhs); + output.output(ctx, lhs); } fn string_separated( output: &mut Output, - rhs: &impl SpanTextOutput, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, middle: &str, - lhs: &impl SpanTextOutput, + lhs: &impl SpanTextOutput, ) { - output.output(rhs); + output.output(ctx, rhs); output.space(); output.text(middle); output.space(); - output.output(lhs); + output.output(ctx, lhs); } fn output_separated( output: &mut Output, - rhs: &impl SpanTextOutput, - middle: &impl SpanTextOutput, - lhs: &impl SpanTextOutput, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, + middle: &impl SpanTextOutput, + lhs: &impl SpanTextOutput, ) { - output.output(rhs); + output.output(ctx, rhs); output.space(); - output.output(middle); + output.output(ctx, middle); output.space(); - output.output(lhs); + output.output(ctx, lhs); } match self { Expression::Number(num) => { - output.output(num); + output.output(ctx, num); } Expression::Boolean(boolean) => { - output.output(boolean); + output.output(ctx, boolean); } Expression::Text(_) => { // Take raw text from file, as string content should not be modified output.end_span(span); } Expression::Parentheses(parentheses) => { - output.output(parentheses); - } - Expression::Var(var) => output.end_output(var), - Expression::Add(rhs, lhs) => char_separated(output, rhs, '+', lhs), - Expression::Subtract(rhs, lhs) => char_separated(output, rhs, '-', lhs), - Expression::Multiply(rhs, lhs) => char_separated(output, rhs, '*', lhs), - Expression::Divide(rhs, lhs) => char_separated(output, rhs, '/', lhs), - Expression::Modulo(rhs, lhs) => char_separated(output, rhs, '%', lhs), + output.output(ctx, parentheses); + } + Expression::Var(var) => output.end_output(ctx, var), + Expression::Add(rhs, lhs) => char_separated(output, ctx, rhs, '+', lhs), + Expression::Subtract(rhs, lhs) => char_separated(output, ctx, rhs, '-', lhs), + Expression::Multiply(rhs, lhs) => char_separated(output, ctx, rhs, '*', lhs), + Expression::Divide(rhs, lhs) => char_separated(output, ctx, rhs, '/', lhs), + Expression::Modulo(rhs, lhs) => char_separated(output, ctx, rhs, '%', lhs), Expression::Neg(neg, lhs) => { - output.output(neg); - output.output(lhs); - } - Expression::And(rhs, and, lhs) => output_separated(output, rhs, and, lhs), - Expression::Or(rhs, or, lhs) => output_separated(output, rhs, or, lhs), - Expression::Gt(rhs, lhs) => char_separated(output, rhs, '>', lhs), - Expression::Ge(rhs, lhs) => string_separated(output, rhs, ">=", lhs), - Expression::Lt(rhs, lhs) => char_separated(output, rhs, '<', lhs), - Expression::Le(rhs, lhs) => string_separated(output, rhs, ">=", lhs), - Expression::Eq(rhs, lhs) => string_separated(output, rhs, "==", lhs), - Expression::Neq(rhs, lhs) => string_separated(output, rhs, "!=", lhs), + output.output(ctx, neg); + output.output(ctx, lhs); + } + Expression::And(rhs, and, lhs) => output_separated(output, ctx, rhs, and, lhs), + Expression::Or(rhs, or, lhs) => output_separated(output, ctx, rhs, or, lhs), + Expression::Gt(rhs, lhs) => char_separated(output, ctx, rhs, '>', lhs), + Expression::Ge(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), + Expression::Lt(rhs, lhs) => char_separated(output, ctx, rhs, '<', lhs), + Expression::Le(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), + Expression::Eq(rhs, lhs) => string_separated(output, ctx, rhs, "==", lhs), + Expression::Neq(rhs, lhs) => string_separated(output, ctx, rhs, "!=", lhs), Expression::Not(not, lhs) => { - output.output(not); + output.output(ctx, not); output.space(); - output.output(lhs); + output.output(ctx, lhs); } Expression::Ternary(condition, then, if_then, r#else, if_else) => { // TODO(tye-exe): Allow single line ternary if short enough. Use given span to measure length? output.increase_indentation(); - output.output(condition); + output.output(ctx, condition); output.newline(); - output.output(then); + output.output(ctx, then); output.space(); - output.output(if_then); + output.output(ctx, if_then); output.newline(); - output.output(r#else); + output.output(ctx, r#else); output.space(); - output.output(if_else); + output.output(ctx, if_else); output.decrease_indentation(); } Expression::FunctionInvocation(modifiers, function_name, args, failure_handler) => { for modifier in modifiers { - output.output(modifier); + output.output(ctx, modifier); output.space(); } - output.output(function_name).char('('); + output.output(ctx, function_name).char('('); for arg in args.iter().take(args.len().saturating_sub(1)) { - output.output(arg).char(',').space(); + output.output(ctx, arg).char(',').space(); } if let Some(arg) = args.last() { - output.output(arg); + output.output(ctx, arg); } output.char(')'); if let Some(failure_handler) = failure_handler { - output.output(failure_handler); + output.output(ctx, failure_handler); } } Expression::Command(modifiers, commands, failure_handler) => { for modifier in modifiers { - output.output(modifier); + output.output(ctx, modifier); output.space(); } @@ -500,13 +541,13 @@ impl TextOutput for Expression { } if let Some(failure_handler) = failure_handler { - output.space().output(failure_handler); + output.space().output(ctx, failure_handler); } } Expression::Array(array) => { output.char('['); for expression in array { - output.output(expression); + output.output(ctx, expression); output.char(','); output.space(); } @@ -514,33 +555,40 @@ impl TextOutput for Expression { output.char(']'); } Expression::Range(lhs, rhs) => { - output.output(lhs); + output.output(ctx, lhs); output.text(".."); - output.output(rhs); + output.output(ctx, rhs); } Expression::Null => { output.text("Null"); } - Expression::Cast(lhs, r#as, rhs) => string_separated(output, rhs, &r#as.0, lhs), + Expression::Cast(lhs, r#as, rhs) => string_separated(output, ctx, rhs, &r#as.0, lhs), Expression::Status => { output.text("status"); } Expression::Nameof(name_of, variable) => { - output.output(name_of); + output.output(ctx, name_of); output.space(); - output.output(variable); + output.output(ctx, variable); } Expression::Is(lhs, is, rhs) => { - output.output(lhs).space().output(is).space().output(rhs); - } - Expression::ArrayIndex(array, index) => { - output.output(array).char('[').output(index).end_char(']') + output + .output(ctx, lhs) + .space() + .output(ctx, is) + .space() + .output(ctx, rhs); } + Expression::ArrayIndex(array, index) => output + .output(ctx, array) + .char('[') + .output(ctx, index) + .end_char(']'), Expression::Exit(exit, value) => { - output.output(exit); + output.output(ctx, exit); if let Some(value) = value { - output.space().output(value); + output.space().output(ctx, value); } } Expression::Error => { @@ -550,21 +598,21 @@ impl TextOutput for Expression { } } -impl TextOutput for FailureHandler { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for FailureHandler { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { FailureHandler::Propagate => { output.char('?'); } FailureHandler::Handle(failed, statements) => { - output.output(failed); + output.output(ctx, failed); output.space(); output.char('{'); output.increase_indentation(); output.newline(); for statement in statements { - output.output(statement); + output.output(ctx, statement); output.newline(); } @@ -578,8 +626,8 @@ impl TextOutput for FailureHandler { } } -impl TextOutput for CommandModifier { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for CommandModifier { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { CommandModifier::Unsafe => output.end_text("unsafe"), CommandModifier::Trust => output.end_text("trust"), @@ -589,18 +637,18 @@ impl TextOutput for CommandModifier { } } -impl TextOutput for InterpolatedCommand { - fn output(&self, _span: &Span, output: &mut Output) { +impl TextOutput for InterpolatedCommand { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { InterpolatedCommand::Escape(escape) => output .debug_point("InterpolatedCommand escape") .end_text(escape.as_str()), InterpolatedCommand::CommandOption(option) => output.end_text(option.as_str()), - InterpolatedCommand::Expression(expr) => output.end_output(expr), + InterpolatedCommand::Expression(expr) => output.end_output(ctx, expr), InterpolatedCommand::Text(text) => output.end_text(text.as_str()), } } } -impl TextOutput for f32 {} -impl TextOutput for bool {} +impl TextOutput for f32 {} +impl TextOutput for bool {} diff --git a/crates/formatter/src/lib.rs b/crates/formatter/src/lib.rs index a7f7dc2..8f6856b 100644 --- a/crates/formatter/src/lib.rs +++ b/crates/formatter/src/lib.rs @@ -1,3 +1,4 @@ +use amber_grammar::alpha040::GlobalStatement; use amber_types::{Spanned, token::Span}; use std::{panic::Location, string::FromUtf8Error}; @@ -19,11 +20,37 @@ const WHITESPACE_BYTES: [u8; 4] = { array }; +/// Contains string fragments of the code ready for finial formatting into a string. #[derive(Default, Debug)] pub struct Output { buffer: Vec, } +/// Context about the general structure of the program to use during formatting. +pub struct FmtContext<'a, T> { + /// The parsed top level statements of the program. + items: &'a [T], + /// The location of the statement currently being formatted. + index: usize, +} + +impl<'a, T> FmtContext<'a, T> { + /// Moves the context to the next top level statement. + fn increment(&mut self) { + self.index += 1; + } + + /// Returns the previous top level statement if it exists. + pub fn previous_global(&self) -> Option<&T> { + self.items.get(self.index.checked_sub(1)?) + } + + /// Returns the next top level statement if it exists. + pub fn next_global(&self) -> Option<&T> { + self.items.get(self.index.checked_add(1)?) + } +} + #[derive(Debug)] pub struct FragmentSpan { /// Start byte offset into source file. @@ -102,11 +129,11 @@ impl CommentVariant { } } -pub trait SpanTextOutput { - fn output(&self, output: &mut Output); +pub trait SpanTextOutput { + fn output(&self, output: &mut Output, ctx: &mut FmtContext); } -pub trait TextOutput { +pub trait TextOutput { /// Gets the formatted string representation of an AST element. /// The string representation should be written to the output buffer. /// @@ -115,11 +142,23 @@ pub trait TextOutput { /// /// It is the responsibilirt of the caller to append a space to the buffer if required. /// An implementation should not end with adding a space. - fn output(&self, span: &Span, output: &mut Output) { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.span(span); } } +impl> SpanTextOutput for Spanned { + fn output(&self, output: &mut Output, ctx: &mut FmtContext) { + self.0.output(&self.1, output, ctx); + } +} + +impl> SpanTextOutput for Box> { + fn output(&self, output: &mut Output, ctx: &mut FmtContext) { + self.0.output(&self.1, output, ctx); + } +} + #[derive(thiserror::Error, Debug)] pub enum FormattingError { /// The span does not exist within the source file. @@ -130,19 +169,22 @@ pub enum FormattingError { InvalidSpan(#[from] FromUtf8Error), } -impl SpanTextOutput for Spanned { - fn output(&self, output: &mut Output) { - self.0.output(&self.1, output); - } -} +impl Output { + pub fn parse(items: &[(GlobalStatement, Span)]) -> Self { + let mut index = 0; -impl SpanTextOutput for Box> { - fn output(&self, output: &mut Output) { - self.0.output(&self.1, output); + let mut output = Output::default(); + let mut ctx = FmtContext { items, index }; + + while let Some(item) = items.get(index) { + index += 1; + item.output(&mut output, &mut ctx); + ctx.increment(); + } + + output } -} -impl Output { #[track_caller] fn debug_point(&mut self, info: &str) -> &mut Self { self.text(format!( @@ -190,11 +232,11 @@ impl Output { self } - fn output(&mut self, output: &TOutput) -> &mut Self + fn output(&mut self, ctx: &mut FmtContext, output: &TOutput) -> &mut Self where - TOutput: SpanTextOutput, + TOutput: SpanTextOutput, { - output.output(self); + output.output(self, ctx); self } @@ -228,11 +270,11 @@ impl Output { .push(Fragment::Text(character.to_string().into_boxed_str())); } - fn end_output(&mut self, output: &TOutput) + fn end_output(&mut self, ctx: &mut FmtContext, output: &TOutput) where - TOutput: SpanTextOutput, + TOutput: SpanTextOutput, { - output.output(self); + output.output(self, ctx); } fn end_span(&mut self, span: &Span) { diff --git a/crates/formatter/src/main.rs b/crates/formatter/src/main.rs index 2b7d241..bd79c65 100644 --- a/crates/formatter/src/main.rs +++ b/crates/formatter/src/main.rs @@ -14,12 +14,15 @@ fn main() { Grammar::Alpha035(_items) => todo!(), Grammar::Alpha040(items) => { if let Some(items) = items { + // eprintln!("{items:?}"); + { - let mut output = Output::default(); - for item in items { - use amber_fmt::SpanTextOutput; - (&item).output(&mut output); - } + // let mut output = Output::default(); + // for item in items { + // use amber_fmt::SpanTextOutput; + // (&item).output(&mut output); + // } + let output = Output::parse(&items); eprintln!("{output:?}"); let format = output.format(data).expect("Able to parse"); println!("{format}"); From 1cfcc21e7dd767d00cb1fce595d17f10d13e65ba Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 4 Jan 2026 21:02:21 +0000 Subject: [PATCH 22/37] Fix global statement spacing --- crates/formatter/src/alpha040/mod.rs | 29 ++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 9c7cb11..d50612e 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -31,6 +31,13 @@ impl TextOutput for GlobalStatement { .output(ctx, path) .char('"') .newline(); + + if ctx + .next_global() + .is_some_and(|next| !matches!(next.0, GlobalStatement::Import(..))) + { + output.newline(); + } } GlobalStatement::FunctionDefinition( compiler_flags, @@ -81,12 +88,11 @@ impl TextOutput for GlobalStatement { for content in contents { output.newline().end_output(ctx, content); } - output - .decrease_indentation() - .newline() - .char('}') - .newline() - .newline(); + output.decrease_indentation().newline().char('}'); + + if ctx.next_global().is_some() { + output.newline().newline(); + } } GlobalStatement::Main(main, args, statements) => { // output.newline(); @@ -102,12 +108,11 @@ impl TextOutput for GlobalStatement { output.newline().output(ctx, statement); } - output - .decrease_indentation() - .newline() - .char('}') - .newline() - .newline(); + output.decrease_indentation().newline().char('}'); + + if ctx.next_global().is_some() { + output.newline().newline(); + } } GlobalStatement::Statement(statement) => { output.output(ctx, statement).newline(); From b149730342f73f0a9f2d5a51612497de4717d772 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 4 Jan 2026 21:26:27 +0000 Subject: [PATCH 23/37] Minor refactor --- crates/formatter/src/alpha040/mod.rs | 37 ++- crates/formatter/src/format.rs | 302 ++++++++++++++++++++++ crates/formatter/src/fragments.rs | 79 ++++++ crates/formatter/src/lib.rs | 372 +-------------------------- crates/formatter/src/main.rs | 12 +- 5 files changed, 406 insertions(+), 396 deletions(-) create mode 100644 crates/formatter/src/format.rs create mode 100644 crates/formatter/src/fragments.rs diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index d50612e..3102e4b 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -1,14 +1,13 @@ -use amber_grammar::alpha040::{ - Block, Comment, ElseCondition, Expression, FailureHandler, FunctionArgument, GlobalStatement, - IfChainContent, IfCondition, ImportContent, InterpolatedCommand, IterLoopVars, Statement, - VariableInitType, +use crate::{FmtContext, Output, SpanTextOutput, TextOutput, fragments::CommentVariant}; +use amber_grammar::{ + CommandModifier, CompilerFlag, + alpha040::{ + Block, Comment, ElseCondition, Expression, FailureHandler, FunctionArgument, + GlobalStatement, IfChainContent, IfCondition, ImportContent, InterpolatedCommand, + IterLoopVars, Statement, VariableInitType, + }, }; -use amber_grammar::{CommandModifier, CompilerFlag}; -use amber_types::DataType; -use amber_types::token::Span; - -use crate::{CommentVariant, FmtContext, SpanTextOutput}; -use crate::{Output, TextOutput}; +use amber_types::{DataType, token::Span}; type Gen = (GlobalStatement, Span); @@ -170,13 +169,13 @@ impl TextOutput for FunctionArgument { } impl TextOutput for CompilerFlag { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, _span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.text(format!("#[{self}]")); } } impl TextOutput for String { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, _span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.text(self.clone()); } } @@ -330,7 +329,7 @@ impl TextOutput for Statement { } impl TextOutput for IterLoopVars { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.span(span); } } @@ -361,19 +360,19 @@ impl TextOutput for Block { } impl TextOutput for IfChainContent { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.span(span); } } impl TextOutput for ElseCondition { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.span(span); } } impl TextOutput for Comment { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, span: &Span, output: &mut Output, _ctx: &mut FmtContext) { match self { Comment::Comment(comment) => { output.end_comment(CommentVariant::Regular, comment.as_str(), span) @@ -604,7 +603,7 @@ impl TextOutput for Expression { } impl TextOutput for FailureHandler { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { FailureHandler::Propagate => { output.char('?'); @@ -632,7 +631,7 @@ impl TextOutput for FailureHandler { } impl TextOutput for CommandModifier { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, _span: &Span, output: &mut Output, _ctx: &mut FmtContext) { match self { CommandModifier::Unsafe => output.end_text("unsafe"), CommandModifier::Trust => output.end_text("trust"), @@ -643,7 +642,7 @@ impl TextOutput for CommandModifier { } impl TextOutput for InterpolatedCommand { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { InterpolatedCommand::Escape(escape) => output .debug_point("InterpolatedCommand escape") diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs new file mode 100644 index 0000000..bb5cf63 --- /dev/null +++ b/crates/formatter/src/format.rs @@ -0,0 +1,302 @@ +use super::{INDENT, SpanTextOutput, WHITESPACE_BYTES}; +use crate::fragments::{CommentVariant, Fragment, Indentation}; +use amber_grammar::alpha040::GlobalStatement; +use amber_types::token::Span; +use std::{panic::Location, string::FromUtf8Error}; + +/// Formats the file. +/// +/// items is the parsed file content. +/// file_content is the raw file content. +pub fn format( + items: &[(GlobalStatement, Span)], + file_content: &str, +) -> Result { + let mut index = 0; + + let mut output = Output::default(); + let mut ctx = FmtContext { items, index }; + + while let Some(item) = items.get(index) { + index += 1; + item.output(&mut output, &mut ctx); + ctx.increment(); + } + + eprintln!("{output:?}"); + output.format(file_content) +} + +/// Contains string fragments of the code ready for finial formatting into a string. +#[derive(Default, Debug)] +pub struct Output { + buffer: Vec, +} + +/// Context about the general structure of the program to use during formatting. +pub struct FmtContext<'a, T> { + /// The parsed top level statements of the program. + items: &'a [T], + /// The location of the statement currently being formatted. + index: usize, +} + +impl<'a, T> FmtContext<'a, T> { + /// Moves the context to the next top level statement. + fn increment(&mut self) { + self.index += 1; + } + + /// Returns the previous top level statement if it exists. + pub fn previous_global(&self) -> Option<&T> { + self.items.get(self.index.checked_sub(1)?) + } + + /// Returns the next top level statement if it exists. + pub fn next_global(&self) -> Option<&T> { + self.items.get(self.index.checked_add(1)?) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum FormattingError { + /// The span does not exist within the source file. + #[error("Invalid span. Starts: {start}; Ends: {end}")] + SpanDoesntExist { start: usize, end: usize }, + /// The span cannot be converted into UTF8 text. + #[error(transparent)] + InvalidSpan(#[from] FromUtf8Error), +} + +impl Output { + #[track_caller] + pub(crate) fn debug_point(&mut self, info: &str) -> &mut Self { + self.text(format!( + "%%Debug point : {info} : at {}%%", + Location::caller() + )) + } + + pub(crate) fn error(&mut self, span: &Span) { + self.span(span); + } + + pub(crate) fn space(&mut self) -> &mut Self { + self.buffer.push(Fragment::Space); + self + } + + pub(crate) fn newline(&mut self) -> &mut Self { + self.buffer.push(Fragment::Newline); + self + } + + pub(crate) fn text(&mut self, text: impl Into>) -> &mut Self { + self.buffer.push(Fragment::Text(text.into())); + self + } + + pub(crate) fn comment( + &mut self, + variant: CommentVariant, + text: impl Into>, + span: &Span, + ) -> &mut Self { + self.buffer.push(Fragment::Comment { + variant, + text: text.into(), + start_index: span.start, + }); + self + } + + pub(crate) fn char(&mut self, character: char) -> &mut Self { + self.buffer + .push(Fragment::Text(character.to_string().into_boxed_str())); + self + } + + pub(crate) fn output( + &mut self, + ctx: &mut FmtContext, + output: &TOutput, + ) -> &mut Self + where + TOutput: SpanTextOutput, + { + output.output(self, ctx); + self + } + + pub(crate) fn span(&mut self, span: &Span) -> &mut Self { + self.buffer.push(Fragment::Span(span.into())); + self + } + + pub(crate) fn end_space(&mut self) { + self.buffer.push(Fragment::Space); + } + + pub(crate) fn end_newline(&mut self) { + self.buffer.push(Fragment::Newline); + } + + pub(crate) fn end_text(&mut self, text: impl Into>) { + self.buffer.push(Fragment::Text(text.into())); + } + + pub(crate) fn end_comment( + &mut self, + variant: CommentVariant, + text: impl Into>, + span: &Span, + ) { + self.buffer.push(Fragment::Comment { + variant, + text: text.into(), + start_index: span.start, + }); + } + + pub(crate) fn end_char(&mut self, character: char) { + self.buffer + .push(Fragment::Text(character.to_string().into_boxed_str())); + } + + pub(crate) fn end_output(&mut self, ctx: &mut FmtContext, output: &TOutput) + where + TOutput: SpanTextOutput, + { + output.output(self, ctx); + } + + pub(crate) fn end_span(&mut self, span: &Span) { + self.span(span); + } + + pub(crate) fn increase_indentation(&mut self) -> &mut Self { + self.buffer + .push(Fragment::IndentationChange(Indentation::Increase)); + self + } + + pub(crate) fn decrease_indentation(&mut self) -> &mut Self { + self.buffer + .push(Fragment::IndentationChange(Indentation::Decrease)); + self + } + + /// Removes the last fragment from the buffer if it is a space. + pub(crate) fn remove_space(&mut self) -> &mut Self { + self.buffer.pop_if(|last| matches!(last, Fragment::Space)); + self + } + + /// Removes the last fragment from the buffer if it is a newline. + pub(crate) fn remove_newline(&mut self) -> &mut Self { + self.buffer.pop_if(|last| matches!(last, Fragment::Newline)); + self + } + + fn format(self, file_content: &str) -> Result { + let mut text = String::new(); + let mut indentation = String::new(); + + let mut iter = self.buffer.into_iter().peekable(); + while let Some(fragment) = iter.next() { + match fragment { + Fragment::Space => text.push(' '), + Fragment::Newline => { + // Don't add newline if comment is on sameline + if let Some(Fragment::Comment { + variant: _, + text: _, + start_index, + }) = iter.peek() + { + let mut index = *start_index; + let mut on_newline = false; + + while let Some(character) = file_content.as_bytes().get(index).cloned() { + index -= 1; + let character: char = character.into(); + + if character == '\n' || character == '\r' { + on_newline = true; + break; + } + + if ![' ', '\t', '/'].contains(&character) { + break; + } + } + + if !on_newline { + continue; + } + } + + text.push('\n'); + text.push_str(&indentation); + } + Fragment::IndentationChange(indent) => match indent { + Indentation::Increase => indentation += INDENT, + Indentation::Decrease => { + indentation.truncate(indentation.len().saturating_sub(INDENT.len())); + } + }, + Fragment::Text(frag_text) => text.push_str(&frag_text), + Fragment::Span(span) => { + let span = file_content + .as_bytes() + .get(span.start_offset()..=span.end_offset()) + .ok_or_else(|| FormattingError::SpanDoesntExist { + start: span.start_offset(), + end: span.end_offset(), + })?; + + let span_text = String::from_utf8(span.to_vec())?; + text.push_str(&span_text); + } + Fragment::ParseError(span) => { + let span = file_content + .as_bytes() + .get(span.start_offset()..=span.end_offset()) + .ok_or_else(|| FormattingError::SpanDoesntExist { + start: span.start_offset(), + end: span.end_offset(), + })?; + + let span_text = String::from_utf8(span.to_vec())?; + eprintln!("Unable to parse '{span_text}'. Failing back to sourcefile content"); + text.push_str(&span_text); + } + Fragment::Comment { + variant, + text: comment, + start_index: _, + } => { + // Ensure that there is a space between the comment and previous code + if let Some(previous) = text.as_bytes().last() + && !WHITESPACE_BYTES.contains(previous) + { + text.push(' '); + } + + text.push_str(variant.denoted_by()); + text.push(' '); + text.push_str(&comment); + + if let Some(Fragment::Comment { variant, .. }) = iter.peek() + && *variant == CommentVariant::Doc + && matches!(variant, CommentVariant::Doc) + { + text.push('\n'); + } + } + } + } + + Ok(text) + } +} diff --git a/crates/formatter/src/fragments.rs b/crates/formatter/src/fragments.rs new file mode 100644 index 0000000..667f3e2 --- /dev/null +++ b/crates/formatter/src/fragments.rs @@ -0,0 +1,79 @@ +use amber_types::token::Span; + +#[derive(Debug)] +pub struct FragmentSpan { + /// Start byte offset into source file. + pub(crate) start_offset: usize, + /// End byte offset into source file. + pub(crate) end_offset: usize, +} + +impl FragmentSpan { + pub fn new(start_offset: usize, end_offset: usize) -> Self { + Self { + start_offset: start_offset.min(end_offset), + end_offset: start_offset.max(end_offset), + } + } + + pub fn end_offset(&self) -> usize { + self.end_offset + } + + pub fn start_offset(&self) -> usize { + self.start_offset + } +} + +impl From<&Span> for FragmentSpan { + fn from(value: &Span) -> Self { + FragmentSpan { + start_offset: value.start, + end_offset: value.end.saturating_sub(1), + } + } +} + +#[derive(Debug)] +pub enum Fragment { + Space, + Newline, + /// Denotes an indentation change. + /// This has no output in its current position, but will change the indentation after every newline. + IndentationChange(Indentation), + Text(Box), + Comment { + /// Dentoes the start of a comment. E.G "//" or "///". + variant: CommentVariant, + text: Box, + /// Byte index of the source file where the comment starts. + start_index: usize, + }, + Span(FragmentSpan), + ParseError(FragmentSpan), +} + +#[derive(Debug)] +pub enum Indentation { + Increase, + Decrease, +} + +/// Comments can either be doc comments or regular comments. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CommentVariant { + /// Doc comment + Doc, + /// Regular comment + Regular, +} + +impl CommentVariant { + /// Returns the string that this comment variant is denoted by. + pub fn denoted_by(self) -> &'static str { + match self { + CommentVariant::Doc => "///", + CommentVariant::Regular => "//", + } + } +} diff --git a/crates/formatter/src/lib.rs b/crates/formatter/src/lib.rs index 8f6856b..6ba66e8 100644 --- a/crates/formatter/src/lib.rs +++ b/crates/formatter/src/lib.rs @@ -1,8 +1,11 @@ -use amber_grammar::alpha040::GlobalStatement; +use crate::format::{FmtContext, Output}; use amber_types::{Spanned, token::Span}; -use std::{panic::Location, string::FromUtf8Error}; mod alpha040; +mod format; +mod fragments; + +pub use format::format; /// The content of an indentation. const INDENT: &str = " "; @@ -20,115 +23,6 @@ const WHITESPACE_BYTES: [u8; 4] = { array }; -/// Contains string fragments of the code ready for finial formatting into a string. -#[derive(Default, Debug)] -pub struct Output { - buffer: Vec, -} - -/// Context about the general structure of the program to use during formatting. -pub struct FmtContext<'a, T> { - /// The parsed top level statements of the program. - items: &'a [T], - /// The location of the statement currently being formatted. - index: usize, -} - -impl<'a, T> FmtContext<'a, T> { - /// Moves the context to the next top level statement. - fn increment(&mut self) { - self.index += 1; - } - - /// Returns the previous top level statement if it exists. - pub fn previous_global(&self) -> Option<&T> { - self.items.get(self.index.checked_sub(1)?) - } - - /// Returns the next top level statement if it exists. - pub fn next_global(&self) -> Option<&T> { - self.items.get(self.index.checked_add(1)?) - } -} - -#[derive(Debug)] -pub struct FragmentSpan { - /// Start byte offset into source file. - start_offset: usize, - /// End byte offset into source file. - end_offset: usize, -} - -impl FragmentSpan { - pub fn new(start_offset: usize, end_offset: usize) -> Self { - Self { - start_offset: start_offset.min(end_offset), - end_offset: start_offset.max(end_offset), - } - } - - pub fn end_offset(&self) -> usize { - self.end_offset - } - - pub fn start_offset(&self) -> usize { - self.start_offset - } -} - -impl From<&Span> for FragmentSpan { - fn from(value: &Span) -> Self { - FragmentSpan { - start_offset: value.start, - end_offset: value.end.saturating_sub(1), - } - } -} - -#[derive(Debug)] -pub enum Fragment { - Space, - Newline, - /// Denotes an indentation change. - /// This has no output in its current position, but will change the indentation after every newline. - IndentationChange(Indentation), - Text(Box), - Comment { - /// Dentoes the start of a comment. E.G "//" or "///". - variant: CommentVariant, - text: Box, - /// Byte index of the source file where the comment starts. - start_index: usize, - }, - Span(FragmentSpan), - ParseError(FragmentSpan), -} - -#[derive(Debug)] -pub enum Indentation { - Increase, - Decrease, -} - -/// Comments can either be doc comments or regular comments. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum CommentVariant { - /// Doc comment - Doc, - /// Regular comment - Regular, -} - -impl CommentVariant { - /// Returns the string that this comment variant is denoted by. - fn denoted_by(self) -> &'static str { - match self { - CommentVariant::Doc => "///", - CommentVariant::Regular => "//", - } - } -} - pub trait SpanTextOutput { fn output(&self, output: &mut Output, ctx: &mut FmtContext); } @@ -136,12 +30,7 @@ pub trait SpanTextOutput { pub trait TextOutput { /// Gets the formatted string representation of an AST element. /// The string representation should be written to the output buffer. - /// - /// It is the responsibility of the caller to ensure that the buffer is in the correct state to - /// have text appended. E.G. The buffer is at the start of a new line. - /// - /// It is the responsibilirt of the caller to append a space to the buffer if required. - /// An implementation should not end with adding a space. + #[allow(unused_variables)] // Not used in default impl. fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { output.span(span); } @@ -158,252 +47,3 @@ impl> SpanTextOutput for Box> { self.0.output(&self.1, output, ctx); } } - -#[derive(thiserror::Error, Debug)] -pub enum FormattingError { - /// The span does not exist within the source file. - #[error("Invalid span. Starts: {start}; Ends: {end}")] - SpanDoesntExist { start: usize, end: usize }, - /// The span cannot be converted into UTF8 text. - #[error(transparent)] - InvalidSpan(#[from] FromUtf8Error), -} - -impl Output { - pub fn parse(items: &[(GlobalStatement, Span)]) -> Self { - let mut index = 0; - - let mut output = Output::default(); - let mut ctx = FmtContext { items, index }; - - while let Some(item) = items.get(index) { - index += 1; - item.output(&mut output, &mut ctx); - ctx.increment(); - } - - output - } - - #[track_caller] - fn debug_point(&mut self, info: &str) -> &mut Self { - self.text(format!( - "%%Debug point : {info} : at {}%%", - Location::caller() - )) - } - - fn error(&mut self, span: &Span) { - self.span(span); - } - - fn space(&mut self) -> &mut Self { - self.buffer.push(Fragment::Space); - self - } - - fn newline(&mut self) -> &mut Self { - self.buffer.push(Fragment::Newline); - self - } - - fn text(&mut self, text: impl Into>) -> &mut Self { - self.buffer.push(Fragment::Text(text.into())); - self - } - - fn comment( - &mut self, - variant: CommentVariant, - text: impl Into>, - span: &Span, - ) -> &mut Self { - self.buffer.push(Fragment::Comment { - variant, - text: text.into(), - start_index: span.start, - }); - self - } - - fn char(&mut self, character: char) -> &mut Self { - self.buffer - .push(Fragment::Text(character.to_string().into_boxed_str())); - self - } - - fn output(&mut self, ctx: &mut FmtContext, output: &TOutput) -> &mut Self - where - TOutput: SpanTextOutput, - { - output.output(self, ctx); - self - } - - fn span(&mut self, span: &Span) -> &mut Self { - self.buffer.push(Fragment::Span(span.into())); - self - } - - fn end_space(&mut self) { - self.buffer.push(Fragment::Space); - } - - fn end_newline(&mut self) { - self.buffer.push(Fragment::Newline); - } - - fn end_text(&mut self, text: impl Into>) { - self.buffer.push(Fragment::Text(text.into())); - } - - fn end_comment(&mut self, variant: CommentVariant, text: impl Into>, span: &Span) { - self.buffer.push(Fragment::Comment { - variant, - text: text.into(), - start_index: span.start, - }); - } - - fn end_char(&mut self, character: char) { - self.buffer - .push(Fragment::Text(character.to_string().into_boxed_str())); - } - - fn end_output(&mut self, ctx: &mut FmtContext, output: &TOutput) - where - TOutput: SpanTextOutput, - { - output.output(self, ctx); - } - - fn end_span(&mut self, span: &Span) { - self.span(span); - } - - fn increase_indentation(&mut self) -> &mut Self { - self.buffer - .push(Fragment::IndentationChange(Indentation::Increase)); - self - } - - fn decrease_indentation(&mut self) -> &mut Self { - self.buffer - .push(Fragment::IndentationChange(Indentation::Decrease)); - self - } - - /// Removes the last fragment from the buffer if it is a space. - fn remove_space(&mut self) -> &mut Self { - self.buffer.pop_if(|last| matches!(last, Fragment::Space)); - self - } - - /// Removes the last fragment from the buffer if it is a newline. - fn remove_newline(&mut self) -> &mut Self { - self.buffer.pop_if(|last| matches!(last, Fragment::Newline)); - self - } - - pub fn format(self, file_content: &str) -> Result { - let mut text = String::new(); - let mut indentation = String::new(); - - let mut iter = self.buffer.into_iter().peekable(); - while let Some(fragment) = iter.next() { - match fragment { - Fragment::Space => text.push(' '), - Fragment::Newline => { - // Don't add newline if comment is on sameline - if let Some(Fragment::Comment { - variant: _, - text: _, - start_index, - }) = iter.peek() - { - let mut index = *start_index; - let mut on_newline = false; - - while let Some(character) = file_content.as_bytes().get(index).cloned() { - index -= 1; - let character: char = character.into(); - - if character == '\n' || character == '\r' { - on_newline = true; - break; - } - - if ![' ', '\t', '/'].contains(&character) { - break; - } - } - - if !on_newline { - continue; - } - } - - text.push('\n'); - text.push_str(&indentation); - } - Fragment::IndentationChange(indent) => match indent { - Indentation::Increase => indentation += INDENT, - Indentation::Decrease => { - indentation.truncate(indentation.len().saturating_sub(INDENT.len())); - } - }, - Fragment::Text(frag_text) => text.push_str(&frag_text), - Fragment::Span(span) => { - let span = file_content - .as_bytes() - .get(span.start_offset()..=span.end_offset()) - .ok_or_else(|| FormattingError::SpanDoesntExist { - start: span.start_offset(), - end: span.end_offset(), - })?; - - let span_text = String::from_utf8(span.to_vec())?; - text.push_str(&span_text); - } - Fragment::ParseError(span) => { - let span = file_content - .as_bytes() - .get(span.start_offset()..=span.end_offset()) - .ok_or_else(|| FormattingError::SpanDoesntExist { - start: span.start_offset(), - end: span.end_offset(), - })?; - - let span_text = String::from_utf8(span.to_vec())?; - eprintln!("Unable to parse '{span_text}'. Failing back to sourcefile content"); - text.push_str(&span_text); - } - Fragment::Comment { - variant, - text: comment, - start_index: _, - } => { - // Ensure that there is a space between the comment and previous code - if let Some(previous) = text.as_bytes().last() - && !WHITESPACE_BYTES.contains(previous) - { - text.push(' '); - } - - text.push_str(variant.denoted_by()); - text.push(' '); - text.push_str(&comment); - - if let Some(Fragment::Comment { variant, .. }) = iter.peek() - && *variant == CommentVariant::Doc - && matches!(variant, CommentVariant::Doc) - { - text.push('\n'); - } - } - } - } - - Ok(text) - } -} diff --git a/crates/formatter/src/main.rs b/crates/formatter/src/main.rs index bd79c65..e30cd4a 100644 --- a/crates/formatter/src/main.rs +++ b/crates/formatter/src/main.rs @@ -1,4 +1,3 @@ -use amber_fmt::Output; use amber_grammar::{Grammar, LSPAnalysis as _, alpha040::AmberCompiler}; fn main() { @@ -14,17 +13,8 @@ fn main() { Grammar::Alpha035(_items) => todo!(), Grammar::Alpha040(items) => { if let Some(items) = items { - // eprintln!("{items:?}"); - { - // let mut output = Output::default(); - // for item in items { - // use amber_fmt::SpanTextOutput; - // (&item).output(&mut output); - // } - let output = Output::parse(&items); - eprintln!("{output:?}"); - let format = output.format(data).expect("Able to parse"); + let format = amber_fmt::format(&items, data).expect("Able to parse"); println!("{format}"); } } From 45875b37351af66d2886e66649fa660d6079f11d Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 4 Jan 2026 22:20:22 +0000 Subject: [PATCH 24/37] Remove trailing whitespace inside functions --- crates/formatter/src/alpha040/mod.rs | 15 ++++++++++----- crates/formatter/src/format.rs | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 3102e4b..5f3cfb4 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -47,8 +47,6 @@ impl TextOutput for GlobalStatement { return_type, contents, ) => { - // output.newline(); - for flag in compiler_flags { output.output(ctx, flag); output.newline(); @@ -87,14 +85,17 @@ impl TextOutput for GlobalStatement { for content in contents { output.newline().end_output(ctx, content); } - output.decrease_indentation().newline().char('}'); + output + .remove_trailing_whitespace() + .decrease_indentation() + .newline() + .char('}'); if ctx.next_global().is_some() { output.newline().newline(); } } GlobalStatement::Main(main, args, statements) => { - // output.newline(); output.output(ctx, main); output.char('('); @@ -107,7 +108,11 @@ impl TextOutput for GlobalStatement { output.newline().output(ctx, statement); } - output.decrease_indentation().newline().char('}'); + output + .remove_trailing_whitespace() + .decrease_indentation() + .newline() + .char('}'); if ctx.next_global().is_some() { output.newline().newline(); diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs index bb5cf63..97e3acd 100644 --- a/crates/formatter/src/format.rs +++ b/crates/formatter/src/format.rs @@ -198,6 +198,29 @@ impl Output { self } + /// Removes the any trailing whitespace fragments. + /// + /// Indentation changes are ignored but still preserved. + pub(crate) fn remove_trailing_whitespace(&mut self) -> &mut Self { + let mut indentation = Vec::new(); + + let is_text = |last: &mut Fragment| { + matches!( + last, + Fragment::IndentationChange(..) | Fragment::Space | Fragment::Newline + ) + }; + + while let Some(fragment) = self.buffer.pop_if(is_text) { + if matches!(fragment, Fragment::IndentationChange(..)) { + indentation.push(fragment); + } + } + + self.buffer.append(&mut indentation); + self + } + fn format(self, file_content: &str) -> Result { let mut text = String::new(); let mut indentation = String::new(); From 5d203d235fd3a82f1624430a7ea82ff963fc1ab5 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 14:32:38 +0000 Subject: [PATCH 25/37] Preserve newlines from input files --- crates/formatter/src/alpha040/mod.rs | 61 ++++++++--- crates/formatter/src/format.rs | 151 +++++++++++++++++++++------ crates/formatter/src/lib.rs | 10 ++ 3 files changed, 174 insertions(+), 48 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 5f3cfb4..a702982 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -73,7 +73,7 @@ impl TextOutput for GlobalStatement { output.output(ctx, arg); } } - output.char(')'); + output.char(')').space(); if let Some(returns) = return_type { output.char(':'); @@ -81,10 +81,24 @@ impl TextOutput for GlobalStatement { output.output(ctx, returns); } - output.char('{').increase_indentation(); + output.char('{').increase_indentation().newline(); + + let mut last_span_end = None; for content in contents { - output.newline().end_output(ctx, content); + if let Some(last_span_end) = last_span_end { + if !matches!(content.0, Statement::Comment(..)) + || ctx.source_has_newline(last_span_end..=content.1.start) + { + output.end_newline() + } + + ctx.allow_newline(output, last_span_end..=content.1.start); + } + + output.end_output(ctx, content); + last_span_end = Some(content.1.end); } + output .remove_trailing_whitespace() .decrease_indentation() @@ -102,10 +116,27 @@ impl TextOutput for GlobalStatement { if let Some(args) = args { output.output(ctx, args); } - output.char(')').space().char('{').increase_indentation(); + output + .char(')') + .space() + .char('{') + .increase_indentation() + .newline(); + let mut last_span_end = None; for statement in statements { - output.newline().output(ctx, statement); + if let Some(last_span_end) = last_span_end { + if !matches!(statement.0, Statement::Comment(..)) + || ctx.source_has_newline(last_span_end..=statement.1.start) + { + output.end_newline() + } + + ctx.allow_newline(output, last_span_end..=statement.1.start); + } + + output.output(ctx, statement); + last_span_end = Some(statement.1.end); } output @@ -236,20 +267,20 @@ impl TextOutput for Statement { .space() .output(ctx, new_value); } - Statement::IfCondition(r#if, condition, items, else_condition) => { - output - .output(ctx, r#if) - .space() - .output(ctx, condition) - .end_space(); - // .debug_point("If condition items"); + Statement::IfCondition(r#if, condition, comments, else_condition) => { + output.output(ctx, r#if).space().output(ctx, condition); - for ele in items { - output.output(ctx, ele); + if let Some(comment) = comments.first() { + ctx.allow_newline(output, condition.1.end..=comment.1.start); + output.output(ctx, comment); + } + + for comment in comments.iter().skip(1) { + output.output(ctx, comment); } if let Some(else_condition) = else_condition { - output.output(ctx, else_condition); + output.space().output(ctx, else_condition); } } Statement::IfChain(r#if, items) => { diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs index 97e3acd..008c463 100644 --- a/crates/formatter/src/format.rs +++ b/crates/formatter/src/format.rs @@ -1,8 +1,11 @@ use super::{INDENT, SpanTextOutput, WHITESPACE_BYTES}; -use crate::fragments::{CommentVariant, Fragment, Indentation}; +use crate::{ + WHITESPACE, + fragments::{CommentVariant, Fragment, Indentation}, +}; use amber_grammar::alpha040::GlobalStatement; use amber_types::token::Span; -use std::{panic::Location, string::FromUtf8Error}; +use std::{ops::RangeBounds, panic::Location, string::FromUtf8Error}; /// Formats the file. /// @@ -12,10 +15,24 @@ pub fn format( items: &[(GlobalStatement, Span)], file_content: &str, ) -> Result { + // eprintln!("{items:?}"); let mut index = 0; - let mut output = Output::default(); - let mut ctx = FmtContext { items, index }; + let mut output = Output::new(); + let consecutive_newlines = consecutive_newlines(file_content); + let source_newlines = file_content + .bytes() + .enumerate() + .filter_map(|(index, byte)| if byte == b'\n' { Some(index) } else { None }) + .collect(); + + // eprintln!("{consecutive_newlines:?}"); + let mut ctx = FmtContext { + items, + index, + consecutive_newlines, + source_newlines, + }; while let Some(item) = items.get(index) { index += 1; @@ -23,10 +40,47 @@ pub fn format( ctx.increment(); } - eprintln!("{output:?}"); + // eprintln!("{output:?}"); output.format(file_content) } +/// Index of newlines in the content only separated by whitepspace (ignoring the first newline in a consecutive sequence). +fn consecutive_newlines(file_content: &str) -> Vec { + // amber-lsp parses by bytes, not by chars + let char_indices: Vec = file_content.bytes().collect(); + let mut newlines = Vec::new(); + let mut index = 0; + + while index < char_indices.len() { + let character = char_indices.get(index).unwrap(); + index += 1; + + if *character != b'\n' { + continue; + } + + // Finds subsequent newline chars + while index < char_indices.len() { + let character = char_indices.get(index).unwrap(); + index += 1; + + match *character { + b' ' | b'\t' | b'\r' => { + continue; + } + b'\n' => { + newlines.push(index - 1); + } + _ => { + break; + } + } + } + } + + newlines +} + /// Contains string fragments of the code ready for finial formatting into a string. #[derive(Default, Debug)] pub struct Output { @@ -39,6 +93,10 @@ pub struct FmtContext<'a, T> { items: &'a [T], /// The location of the statement currently being formatted. index: usize, + /// The byte index of consecutive newlines in the source file. + consecutive_newlines: Vec, + /// Byte index of newlines in the source file. + source_newlines: Box<[usize]>, } impl<'a, T> FmtContext<'a, T> { @@ -56,6 +114,22 @@ impl<'a, T> FmtContext<'a, T> { pub fn next_global(&self) -> Option<&T> { self.items.get(self.index.checked_add(1)?) } + + pub fn allow_newline>(&self, output: &mut Output, range: R) { + if self + .consecutive_newlines + .iter() + .any(|newline| range.contains(&(newline + 2))) + { + output.end_newline(); + } + } + + pub fn source_has_newline>(&self, range: R) -> bool { + self.source_newlines + .iter() + .any(|newline| range.contains(newline)) + } } #[derive(thiserror::Error, Debug)] @@ -69,6 +143,10 @@ pub enum FormattingError { } impl Output { + pub fn new() -> Self { + Self { buffer: Vec::new() } + } + #[track_caller] pub(crate) fn debug_point(&mut self, info: &str) -> &mut Self { self.text(format!( @@ -224,41 +302,22 @@ impl Output { fn format(self, file_content: &str) -> Result { let mut text = String::new(); let mut indentation = String::new(); + let mut consecutive_newlines = 0; let mut iter = self.buffer.into_iter().peekable(); while let Some(fragment) = iter.next() { + if let Fragment::Newline = fragment { + consecutive_newlines += 1; + } else { + consecutive_newlines = 0; + } + match fragment { Fragment::Space => text.push(' '), Fragment::Newline => { - // Don't add newline if comment is on sameline - if let Some(Fragment::Comment { - variant: _, - text: _, - start_index, - }) = iter.peek() - { - let mut index = *start_index; - let mut on_newline = false; - - while let Some(character) = file_content.as_bytes().get(index).cloned() { - index -= 1; - let character: char = character.into(); - - if character == '\n' || character == '\r' { - on_newline = true; - break; - } - - if ![' ', '\t', '/'].contains(&character) { - break; - } - } - - if !on_newline { - continue; - } + if consecutive_newlines > 2 { + continue; } - text.push('\n'); text.push_str(&indentation); } @@ -323,3 +382,29 @@ impl Output { Ok(text) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn single_newline() { + let newlines = "Some\nText\nWith\n\nNewlines"; + let consecutive_newlines = consecutive_newlines(newlines); + assert_eq!(consecutive_newlines.as_slice(), &[15]); + } + + #[test] + fn multiple_newline() { + let newlines = "Some\nText\nWith\n\n\nNewlines\n\nMultiple"; + let consecutive_newlines = consecutive_newlines(newlines); + assert_eq!(consecutive_newlines.as_slice(), &[15, 16, 26]); + } + + #[test] + fn multiple_newline_whitespace() { + let newlines = "Some\nText\nWith\n \n \n Newlines\n\t\nMultiple"; + let consecutive_newlines = consecutive_newlines(newlines); + assert_eq!(consecutive_newlines.as_slice(), &[17, 20, 33]); + } +} diff --git a/crates/formatter/src/lib.rs b/crates/formatter/src/lib.rs index 6ba66e8..506bbb8 100644 --- a/crates/formatter/src/lib.rs +++ b/crates/formatter/src/lib.rs @@ -25,6 +25,8 @@ const WHITESPACE_BYTES: [u8; 4] = { pub trait SpanTextOutput { fn output(&self, output: &mut Output, ctx: &mut FmtContext); + + fn span(&self) -> Span; } pub trait TextOutput { @@ -40,10 +42,18 @@ impl> SpanTextOutput for Spanned { fn output(&self, output: &mut Output, ctx: &mut FmtContext) { self.0.output(&self.1, output, ctx); } + + fn span(&self) -> Span { + self.1 + } } impl> SpanTextOutput for Box> { fn output(&self, output: &mut Output, ctx: &mut FmtContext) { self.0.output(&self.1, output, ctx); } + + fn span(&self) -> Span { + self.1 + } } From a78cbe353f63217c6963407a80c9f46b1817b33d Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 15:19:52 +0000 Subject: [PATCH 26/37] Allow import statements to be separated with a newline --- crates/formatter/src/alpha040/mod.rs | 8 +++++++- crates/formatter/src/format.rs | 9 +++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index a702982..8e16ba2 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -12,7 +12,7 @@ use amber_types::{DataType, token::Span}; type Gen = (GlobalStatement, Span); impl TextOutput for GlobalStatement { - fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { GlobalStatement::Import(public, import, content, from, path) => { if public.0 { @@ -37,6 +37,12 @@ impl TextOutput for GlobalStatement { { output.newline(); } + + if let Some(next_global) = ctx.next_global() + && matches!(next_global.0, GlobalStatement::Import(..)) + { + ctx.allow_newline(output, span.end..=next_global.1.start); + } } GlobalStatement::FunctionDefinition( compiler_flags, diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs index 008c463..8891dc3 100644 --- a/crates/formatter/src/format.rs +++ b/crates/formatter/src/format.rs @@ -16,8 +16,6 @@ pub fn format( file_content: &str, ) -> Result { // eprintln!("{items:?}"); - let mut index = 0; - let mut output = Output::new(); let consecutive_newlines = consecutive_newlines(file_content); let source_newlines = file_content @@ -29,13 +27,12 @@ pub fn format( // eprintln!("{consecutive_newlines:?}"); let mut ctx = FmtContext { items, - index, + index: 0, consecutive_newlines, source_newlines, }; - while let Some(item) = items.get(index) { - index += 1; + for item in items { item.output(&mut output, &mut ctx); ctx.increment(); } @@ -119,7 +116,7 @@ impl<'a, T> FmtContext<'a, T> { if self .consecutive_newlines .iter() - .any(|newline| range.contains(&(newline + 2))) + .any(|newline| range.contains(&newline)) { output.end_newline(); } From c4a79b2ba8a43c722fe3103a7f6c3badf948a270 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 15:29:26 +0000 Subject: [PATCH 27/37] Prepare format method to be generic over amber versions --- crates/formatter/src/format.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs index 8891dc3..6c77c10 100644 --- a/crates/formatter/src/format.rs +++ b/crates/formatter/src/format.rs @@ -1,9 +1,8 @@ use super::{INDENT, SpanTextOutput, WHITESPACE_BYTES}; use crate::{ - WHITESPACE, + TextOutput, fragments::{CommentVariant, Fragment, Indentation}, }; -use amber_grammar::alpha040::GlobalStatement; use amber_types::token::Span; use std::{ops::RangeBounds, panic::Location, string::FromUtf8Error}; @@ -11,10 +10,13 @@ use std::{ops::RangeBounds, panic::Location, string::FromUtf8Error}; /// /// items is the parsed file content. /// file_content is the raw file content. -pub fn format( - items: &[(GlobalStatement, Span)], +pub fn format( + items: &[(AmberStatement, Span)], file_content: &str, -) -> Result { +) -> Result +where + AmberStatement: TextOutput<(AmberStatement, Span)>, +{ // eprintln!("{items:?}"); let mut output = Output::new(); let consecutive_newlines = consecutive_newlines(file_content); From af49cf1be2ab2709881cf96d97b9903349627d53 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 15:29:43 +0000 Subject: [PATCH 28/37] Read file from cli args --- crates/formatter/src/main.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/formatter/src/main.rs b/crates/formatter/src/main.rs index e30cd4a..e798387 100644 --- a/crates/formatter/src/main.rs +++ b/crates/formatter/src/main.rs @@ -1,11 +1,19 @@ use amber_grammar::{Grammar, LSPAnalysis as _, alpha040::AmberCompiler}; fn main() { - // let data = include_str!("../../../run_coverage.ab"); - let data = include_str!("../../../resources/alpha040/std/fs.ab"); + let Some(file_path) = std::env::args().skip(1).next() else { + eprintln!("No files to format"); + return; + }; + + let Ok(data) = std::fs::read_to_string(&file_path) + .inspect_err(|err| eprintln!("Unable to read file '{file_path}' err: {err}")) + else { + return; + }; let amber_compiler = AmberCompiler::new(); - let tokenize = amber_compiler.tokenize(data); + let tokenize = amber_compiler.tokenize(&data); let parse = amber_compiler.parse(&tokenize); match parse.ast { @@ -14,7 +22,7 @@ fn main() { Grammar::Alpha040(items) => { if let Some(items) = items { { - let format = amber_fmt::format(&items, data).expect("Able to parse"); + let format = amber_fmt::format(&items, &data).expect("Able to parse"); println!("{format}"); } } From 3ae901a7f18bb36e14cca785cb0519f0370e7917 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 15:48:46 +0000 Subject: [PATCH 29/37] Split 040 formatting code into multiple modules --- crates/formatter/src/alpha040/expression.rs | 191 +++++++ .../src/alpha040/global_statement.rs | 160 ++++++ crates/formatter/src/alpha040/mod.rs | 495 +----------------- crates/formatter/src/alpha040/statement.rs | 152 ++++++ 4 files changed, 511 insertions(+), 487 deletions(-) create mode 100644 crates/formatter/src/alpha040/expression.rs create mode 100644 crates/formatter/src/alpha040/global_statement.rs create mode 100644 crates/formatter/src/alpha040/statement.rs diff --git a/crates/formatter/src/alpha040/expression.rs b/crates/formatter/src/alpha040/expression.rs new file mode 100644 index 0000000..519e070 --- /dev/null +++ b/crates/formatter/src/alpha040/expression.rs @@ -0,0 +1,191 @@ +use crate::{ + SpanTextOutput, TextOutput, + alpha040::Gen, + format::{FmtContext, Output}, +}; +use amber_grammar::{Span, alpha040::Expression}; + +impl TextOutput for Expression { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn char_separated( + output: &mut Output, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, + middle: char, + lhs: &impl SpanTextOutput, + ) { + output.output(ctx, rhs); + output.space(); + output.char(middle); + output.space(); + output.output(ctx, lhs); + } + + fn string_separated( + output: &mut Output, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, + middle: &str, + lhs: &impl SpanTextOutput, + ) { + output.output(ctx, rhs); + output.space(); + output.text(middle); + output.space(); + output.output(ctx, lhs); + } + + fn output_separated( + output: &mut Output, + ctx: &mut FmtContext, + rhs: &impl SpanTextOutput, + middle: &impl SpanTextOutput, + lhs: &impl SpanTextOutput, + ) { + output.output(ctx, rhs); + output.space(); + output.output(ctx, middle); + output.space(); + output.output(ctx, lhs); + } + + match self { + Expression::Number(num) => { + output.output(ctx, num); + } + Expression::Boolean(boolean) => { + output.output(ctx, boolean); + } + Expression::Text(_) => { + // Take raw text from file, as string content should not be modified + output.end_span(span); + } + Expression::Parentheses(parentheses) => { + output.output(ctx, parentheses); + } + Expression::Var(var) => output.end_output(ctx, var), + Expression::Add(rhs, lhs) => char_separated(output, ctx, rhs, '+', lhs), + Expression::Subtract(rhs, lhs) => char_separated(output, ctx, rhs, '-', lhs), + Expression::Multiply(rhs, lhs) => char_separated(output, ctx, rhs, '*', lhs), + Expression::Divide(rhs, lhs) => char_separated(output, ctx, rhs, '/', lhs), + Expression::Modulo(rhs, lhs) => char_separated(output, ctx, rhs, '%', lhs), + Expression::Neg(neg, lhs) => { + output.output(ctx, neg); + output.output(ctx, lhs); + } + Expression::And(rhs, and, lhs) => output_separated(output, ctx, rhs, and, lhs), + Expression::Or(rhs, or, lhs) => output_separated(output, ctx, rhs, or, lhs), + Expression::Gt(rhs, lhs) => char_separated(output, ctx, rhs, '>', lhs), + Expression::Ge(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), + Expression::Lt(rhs, lhs) => char_separated(output, ctx, rhs, '<', lhs), + Expression::Le(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), + Expression::Eq(rhs, lhs) => string_separated(output, ctx, rhs, "==", lhs), + Expression::Neq(rhs, lhs) => string_separated(output, ctx, rhs, "!=", lhs), + Expression::Not(not, lhs) => { + output.output(ctx, not); + output.space(); + output.output(ctx, lhs); + } + Expression::Ternary(condition, then, if_then, r#else, if_else) => { + // TODO(tye-exe): Allow single line ternary if short enough. Use given span to measure length? + output.increase_indentation(); + output.output(ctx, condition); + output.newline(); + output.output(ctx, then); + output.space(); + output.output(ctx, if_then); + output.newline(); + output.output(ctx, r#else); + output.space(); + output.output(ctx, if_else); + output.decrease_indentation(); + } + Expression::FunctionInvocation(modifiers, function_name, args, failure_handler) => { + for modifier in modifiers { + output.output(ctx, modifier); + output.space(); + } + + output.output(ctx, function_name).char('('); + + for arg in args.iter().take(args.len().saturating_sub(1)) { + output.output(ctx, arg).char(',').space(); + } + if let Some(arg) = args.last() { + output.output(ctx, arg); + } + + output.char(')'); + + if let Some(failure_handler) = failure_handler { + output.output(ctx, failure_handler); + } + } + Expression::Command(modifiers, commands, failure_handler) => { + for modifier in modifiers { + output.output(ctx, modifier); + output.space(); + } + + // Do not format bash commands + for command in commands { + output.end_span(&command.1); + } + + if let Some(failure_handler) = failure_handler { + output.space().output(ctx, failure_handler); + } + } + Expression::Array(array) => { + output.char('['); + for expression in array { + output.output(ctx, expression); + output.char(','); + output.space(); + } + output.remove_space(); + output.char(']'); + } + Expression::Range(lhs, rhs) => { + output.output(ctx, lhs); + output.text(".."); + output.output(ctx, rhs); + } + Expression::Null => { + output.text("Null"); + } + Expression::Cast(lhs, r#as, rhs) => string_separated(output, ctx, rhs, &r#as.0, lhs), + Expression::Status => { + output.text("status"); + } + Expression::Nameof(name_of, variable) => { + output.output(ctx, name_of); + output.space(); + output.output(ctx, variable); + } + Expression::Is(lhs, is, rhs) => { + output + .output(ctx, lhs) + .space() + .output(ctx, is) + .space() + .output(ctx, rhs); + } + Expression::ArrayIndex(array, index) => output + .output(ctx, array) + .char('[') + .output(ctx, index) + .end_char(']'), + Expression::Exit(exit, value) => { + output.output(ctx, exit); + + if let Some(value) = value { + output.space().output(ctx, value); + } + } + Expression::Error => { + output.error(span); + } + } + } +} diff --git a/crates/formatter/src/alpha040/global_statement.rs b/crates/formatter/src/alpha040/global_statement.rs new file mode 100644 index 0000000..e736b39 --- /dev/null +++ b/crates/formatter/src/alpha040/global_statement.rs @@ -0,0 +1,160 @@ +use crate::{ + TextOutput, + alpha040::Gen, + format::{FmtContext, Output}, +}; +use amber_grammar::{ + Span, + alpha040::{GlobalStatement, Statement}, +}; + +impl TextOutput for GlobalStatement { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + match self { + GlobalStatement::Import(public, import, content, from, path) => { + if public.0 { + output.text("pub "); + } + + output + .output(ctx, import) + .space() + .output(ctx, content) + .space() + .output(ctx, from) + .space() + .char('"') + .output(ctx, path) + .char('"') + .newline(); + + if ctx + .next_global() + .is_some_and(|next| !matches!(next.0, GlobalStatement::Import(..))) + { + output.newline(); + } + + if let Some(next_global) = ctx.next_global() + && matches!(next_global.0, GlobalStatement::Import(..)) + { + ctx.allow_newline(output, span.end..=next_global.1.start); + } + } + GlobalStatement::FunctionDefinition( + compiler_flags, + public, + function_keyword, + name, + args, + return_type, + contents, + ) => { + for flag in compiler_flags { + output.output(ctx, flag); + output.newline(); + } + + if public.0 { + output.text("pub "); + } + + output.output(ctx, function_keyword); + output.space(); + output.output(ctx, name); + + output.char('('); + // Handle adding variables with proper spacing + { + for arg in args.iter().take(args.len().saturating_sub(1)) { + output.output(ctx, arg); + output.char(','); + output.space(); + } + + if let Some(arg) = args.last() { + output.output(ctx, arg); + } + } + output.char(')').space(); + + if let Some(returns) = return_type { + output.char(':'); + output.space(); + output.output(ctx, returns); + } + + output.char('{').increase_indentation().newline(); + + let mut last_span_end = None; + for content in contents { + if let Some(last_span_end) = last_span_end { + if !matches!(content.0, Statement::Comment(..)) + || ctx.source_has_newline(last_span_end..=content.1.start) + { + output.end_newline() + } + + ctx.allow_newline(output, last_span_end..=content.1.start); + } + + output.end_output(ctx, content); + last_span_end = Some(content.1.end); + } + + output + .remove_trailing_whitespace() + .decrease_indentation() + .newline() + .char('}'); + + if ctx.next_global().is_some() { + output.newline().newline(); + } + } + GlobalStatement::Main(main, args, statements) => { + output.output(ctx, main); + + output.char('('); + if let Some(args) = args { + output.output(ctx, args); + } + output + .char(')') + .space() + .char('{') + .increase_indentation() + .newline(); + + let mut last_span_end = None; + for statement in statements { + if let Some(last_span_end) = last_span_end { + if !matches!(statement.0, Statement::Comment(..)) + || ctx.source_has_newline(last_span_end..=statement.1.start) + { + output.end_newline() + } + + ctx.allow_newline(output, last_span_end..=statement.1.start); + } + + output.output(ctx, statement); + last_span_end = Some(statement.1.end); + } + + output + .remove_trailing_whitespace() + .decrease_indentation() + .newline() + .char('}'); + + if ctx.next_global().is_some() { + output.newline().newline(); + } + } + GlobalStatement::Statement(statement) => { + output.output(ctx, statement).newline(); + } + } + } +} diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 8e16ba2..c25a2a3 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -2,165 +2,18 @@ use crate::{FmtContext, Output, SpanTextOutput, TextOutput, fragments::CommentVa use amber_grammar::{ CommandModifier, CompilerFlag, alpha040::{ - Block, Comment, ElseCondition, Expression, FailureHandler, FunctionArgument, - GlobalStatement, IfChainContent, IfCondition, ImportContent, InterpolatedCommand, - IterLoopVars, Statement, VariableInitType, + Block, Comment, ElseCondition, FailureHandler, FunctionArgument, GlobalStatement, + IfChainContent, IfCondition, ImportContent, InterpolatedCommand, IterLoopVars, + VariableInitType, }, }; use amber_types::{DataType, token::Span}; -type Gen = (GlobalStatement, Span); - -impl TextOutput for GlobalStatement { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { - match self { - GlobalStatement::Import(public, import, content, from, path) => { - if public.0 { - output.text("pub "); - } - - output - .output(ctx, import) - .space() - .output(ctx, content) - .space() - .output(ctx, from) - .space() - .char('"') - .output(ctx, path) - .char('"') - .newline(); - - if ctx - .next_global() - .is_some_and(|next| !matches!(next.0, GlobalStatement::Import(..))) - { - output.newline(); - } - - if let Some(next_global) = ctx.next_global() - && matches!(next_global.0, GlobalStatement::Import(..)) - { - ctx.allow_newline(output, span.end..=next_global.1.start); - } - } - GlobalStatement::FunctionDefinition( - compiler_flags, - public, - function_keyword, - name, - args, - return_type, - contents, - ) => { - for flag in compiler_flags { - output.output(ctx, flag); - output.newline(); - } - - if public.0 { - output.text("pub "); - } - - output.output(ctx, function_keyword); - output.space(); - output.output(ctx, name); - - output.char('('); - // Handle adding variables with proper spacing - { - for arg in args.iter().take(args.len().saturating_sub(1)) { - output.output(ctx, arg); - output.char(','); - output.space(); - } - - if let Some(arg) = args.last() { - output.output(ctx, arg); - } - } - output.char(')').space(); +mod expression; +mod global_statement; +mod statement; - if let Some(returns) = return_type { - output.char(':'); - output.space(); - output.output(ctx, returns); - } - - output.char('{').increase_indentation().newline(); - - let mut last_span_end = None; - for content in contents { - if let Some(last_span_end) = last_span_end { - if !matches!(content.0, Statement::Comment(..)) - || ctx.source_has_newline(last_span_end..=content.1.start) - { - output.end_newline() - } - - ctx.allow_newline(output, last_span_end..=content.1.start); - } - - output.end_output(ctx, content); - last_span_end = Some(content.1.end); - } - - output - .remove_trailing_whitespace() - .decrease_indentation() - .newline() - .char('}'); - - if ctx.next_global().is_some() { - output.newline().newline(); - } - } - GlobalStatement::Main(main, args, statements) => { - output.output(ctx, main); - - output.char('('); - if let Some(args) = args { - output.output(ctx, args); - } - output - .char(')') - .space() - .char('{') - .increase_indentation() - .newline(); - - let mut last_span_end = None; - for statement in statements { - if let Some(last_span_end) = last_span_end { - if !matches!(statement.0, Statement::Comment(..)) - || ctx.source_has_newline(last_span_end..=statement.1.start) - { - output.end_newline() - } - - ctx.allow_newline(output, last_span_end..=statement.1.start); - } - - output.output(ctx, statement); - last_span_end = Some(statement.1.end); - } - - output - .remove_trailing_whitespace() - .decrease_indentation() - .newline() - .char('}'); - - if ctx.next_global().is_some() { - output.newline().newline(); - } - } - GlobalStatement::Statement(statement) => { - output.output(ctx, statement).newline(); - } - } - } -} +type Gen = (GlobalStatement, Span); impl TextOutput for ImportContent { fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { @@ -222,154 +75,6 @@ impl TextOutput for String { } } -impl TextOutput for DataType {} - -impl TextOutput for Statement { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { - fn shorthand( - output: &mut Output, - ctx: &mut FmtContext, - variable: &impl SpanTextOutput, - separator: &str, - expression: &impl SpanTextOutput, - ) { - output - .output(ctx, variable) - .space() - .text(separator) - .space() - .output(ctx, expression); - } - - match self { - Statement::Expression(expression) => { - output.output(ctx, expression); - } - Statement::VariableInit(keyword, name, init) => { - output - .output(ctx, keyword) - .space() - .output(ctx, name) - .space() - .char('=') - .space() - .output(ctx, init); - } - Statement::ConstInit(keyword, name, init) => { - output - .output(ctx, keyword) - .space() - .output(ctx, name) - .space() - .char('=') - .space() - .output(ctx, init); - } - Statement::VariableSet(name, new_value) => { - output - .output(ctx, name) - .space() - .char('=') - .space() - .output(ctx, new_value); - } - Statement::IfCondition(r#if, condition, comments, else_condition) => { - output.output(ctx, r#if).space().output(ctx, condition); - - if let Some(comment) = comments.first() { - ctx.allow_newline(output, condition.1.end..=comment.1.start); - output.output(ctx, comment); - } - - for comment in comments.iter().skip(1) { - output.output(ctx, comment); - } - - if let Some(else_condition) = else_condition { - output.space().output(ctx, else_condition); - } - } - Statement::IfChain(r#if, items) => { - output - .output(ctx, r#if) - .space() - .char('{') - .increase_indentation(); - - for ele in items { - output.newline().output(ctx, ele); - } - - output.decrease_indentation(); - if items.len() > 0 { - output.newline(); - } - output.char('}'); - } - Statement::ShorthandAdd(variable, expr) => shorthand(output, ctx, variable, "+=", expr), - Statement::ShorthandSub(variable, expr) => shorthand(output, ctx, variable, "-=", expr), - Statement::ShorthandMul(variable, expr) => shorthand(output, ctx, variable, "*=", expr), - Statement::ShorthandDiv(variable, expr) => shorthand(output, ctx, variable, "/=", expr), - Statement::ShorthandModulo(variable, expr) => { - shorthand(output, ctx, variable, "%=", expr) - } - Statement::InfiniteLoop(r#loop, block) => { - output.output(ctx, r#loop).space().output(ctx, block); - } - Statement::IterLoop(r#for, element, r#in, expr, block) => { - output - .output(ctx, r#for) - .space() - .output(ctx, element) - .space() - .output(ctx, r#in) - .space() - .output(ctx, expr) - .space() - .end_output(ctx, block); - } - Statement::Break => output.end_text("break"), - Statement::Continue => output.end_text("continue"), - Statement::Return(r#return, expr) => { - output.end_output(ctx, r#return); - - if let Some(expr) = expr { - output.space().end_output(ctx, expr); - } - } - Statement::Fail(fail, expr) => { - output.end_output(ctx, fail); - - if let Some(expr) = expr { - output.space().end_output(ctx, expr); - } - } - Statement::Echo(echo, text) => output.output(ctx, echo).space().end_output(ctx, text), - Statement::Cd(cd, text) => output.output(ctx, cd).space().end_output(ctx, text), - Statement::MoveFiles(modifiers, mv, source, destination, failure_handler) => { - for modifier in modifiers { - output.output(ctx, modifier).space(); - } - - output - .output(ctx, mv) - .space() - .output(ctx, source) - .space() - .output(ctx, destination); - - if let Some(failure_handler) = failure_handler { - output.space().output(ctx, failure_handler); - } - } - Statement::Block(block) => output.end_output(ctx, block), - Statement::Comment(comment) => output.end_output(ctx, comment), - Statement::Shebang(shebang) => output.end_text(shebang.as_str()), - Statement::Error => output.error(span), - } - } -} - impl TextOutput for IterLoopVars { fn output(&self, span: &Span, output: &mut Output, _ctx: &mut FmtContext) { output.span(span); @@ -459,191 +164,6 @@ impl TextOutput for VariableInitType { } } -impl TextOutput for Expression { - fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { - fn char_separated( - output: &mut Output, - ctx: &mut FmtContext, - rhs: &impl SpanTextOutput, - middle: char, - lhs: &impl SpanTextOutput, - ) { - output.output(ctx, rhs); - output.space(); - output.char(middle); - output.space(); - output.output(ctx, lhs); - } - - fn string_separated( - output: &mut Output, - ctx: &mut FmtContext, - rhs: &impl SpanTextOutput, - middle: &str, - lhs: &impl SpanTextOutput, - ) { - output.output(ctx, rhs); - output.space(); - output.text(middle); - output.space(); - output.output(ctx, lhs); - } - - fn output_separated( - output: &mut Output, - ctx: &mut FmtContext, - rhs: &impl SpanTextOutput, - middle: &impl SpanTextOutput, - lhs: &impl SpanTextOutput, - ) { - output.output(ctx, rhs); - output.space(); - output.output(ctx, middle); - output.space(); - output.output(ctx, lhs); - } - - match self { - Expression::Number(num) => { - output.output(ctx, num); - } - Expression::Boolean(boolean) => { - output.output(ctx, boolean); - } - Expression::Text(_) => { - // Take raw text from file, as string content should not be modified - output.end_span(span); - } - Expression::Parentheses(parentheses) => { - output.output(ctx, parentheses); - } - Expression::Var(var) => output.end_output(ctx, var), - Expression::Add(rhs, lhs) => char_separated(output, ctx, rhs, '+', lhs), - Expression::Subtract(rhs, lhs) => char_separated(output, ctx, rhs, '-', lhs), - Expression::Multiply(rhs, lhs) => char_separated(output, ctx, rhs, '*', lhs), - Expression::Divide(rhs, lhs) => char_separated(output, ctx, rhs, '/', lhs), - Expression::Modulo(rhs, lhs) => char_separated(output, ctx, rhs, '%', lhs), - Expression::Neg(neg, lhs) => { - output.output(ctx, neg); - output.output(ctx, lhs); - } - Expression::And(rhs, and, lhs) => output_separated(output, ctx, rhs, and, lhs), - Expression::Or(rhs, or, lhs) => output_separated(output, ctx, rhs, or, lhs), - Expression::Gt(rhs, lhs) => char_separated(output, ctx, rhs, '>', lhs), - Expression::Ge(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), - Expression::Lt(rhs, lhs) => char_separated(output, ctx, rhs, '<', lhs), - Expression::Le(rhs, lhs) => string_separated(output, ctx, rhs, ">=", lhs), - Expression::Eq(rhs, lhs) => string_separated(output, ctx, rhs, "==", lhs), - Expression::Neq(rhs, lhs) => string_separated(output, ctx, rhs, "!=", lhs), - Expression::Not(not, lhs) => { - output.output(ctx, not); - output.space(); - output.output(ctx, lhs); - } - Expression::Ternary(condition, then, if_then, r#else, if_else) => { - // TODO(tye-exe): Allow single line ternary if short enough. Use given span to measure length? - output.increase_indentation(); - output.output(ctx, condition); - output.newline(); - output.output(ctx, then); - output.space(); - output.output(ctx, if_then); - output.newline(); - output.output(ctx, r#else); - output.space(); - output.output(ctx, if_else); - output.decrease_indentation(); - } - Expression::FunctionInvocation(modifiers, function_name, args, failure_handler) => { - for modifier in modifiers { - output.output(ctx, modifier); - output.space(); - } - - output.output(ctx, function_name).char('('); - - for arg in args.iter().take(args.len().saturating_sub(1)) { - output.output(ctx, arg).char(',').space(); - } - if let Some(arg) = args.last() { - output.output(ctx, arg); - } - - output.char(')'); - - if let Some(failure_handler) = failure_handler { - output.output(ctx, failure_handler); - } - } - Expression::Command(modifiers, commands, failure_handler) => { - for modifier in modifiers { - output.output(ctx, modifier); - output.space(); - } - - // Do not format bash commands - for command in commands { - output.end_span(&command.1); - } - - if let Some(failure_handler) = failure_handler { - output.space().output(ctx, failure_handler); - } - } - Expression::Array(array) => { - output.char('['); - for expression in array { - output.output(ctx, expression); - output.char(','); - output.space(); - } - output.remove_space(); - output.char(']'); - } - Expression::Range(lhs, rhs) => { - output.output(ctx, lhs); - output.text(".."); - output.output(ctx, rhs); - } - Expression::Null => { - output.text("Null"); - } - Expression::Cast(lhs, r#as, rhs) => string_separated(output, ctx, rhs, &r#as.0, lhs), - Expression::Status => { - output.text("status"); - } - Expression::Nameof(name_of, variable) => { - output.output(ctx, name_of); - output.space(); - output.output(ctx, variable); - } - Expression::Is(lhs, is, rhs) => { - output - .output(ctx, lhs) - .space() - .output(ctx, is) - .space() - .output(ctx, rhs); - } - Expression::ArrayIndex(array, index) => output - .output(ctx, array) - .char('[') - .output(ctx, index) - .end_char(']'), - Expression::Exit(exit, value) => { - output.output(ctx, exit); - - if let Some(value) = value { - output.space().output(ctx, value); - } - } - Expression::Error => { - output.error(span); - } - } - } -} - impl TextOutput for FailureHandler { fn output(&self, _span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { @@ -698,3 +218,4 @@ impl TextOutput for InterpolatedCommand { impl TextOutput for f32 {} impl TextOutput for bool {} +impl TextOutput for DataType {} diff --git a/crates/formatter/src/alpha040/statement.rs b/crates/formatter/src/alpha040/statement.rs new file mode 100644 index 0000000..61c840b --- /dev/null +++ b/crates/formatter/src/alpha040/statement.rs @@ -0,0 +1,152 @@ +use crate::{ + SpanTextOutput, TextOutput, + alpha040::Gen, + format::{FmtContext, Output}, +}; +use amber_grammar::{Span, alpha040::Statement}; + +impl TextOutput for Statement { + fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { + fn shorthand( + output: &mut Output, + ctx: &mut FmtContext, + variable: &impl SpanTextOutput, + separator: &str, + expression: &impl SpanTextOutput, + ) { + output + .output(ctx, variable) + .space() + .text(separator) + .space() + .output(ctx, expression); + } + + match self { + Statement::Expression(expression) => { + output.output(ctx, expression); + } + Statement::VariableInit(keyword, name, init) => { + output + .output(ctx, keyword) + .space() + .output(ctx, name) + .space() + .char('=') + .space() + .output(ctx, init); + } + Statement::ConstInit(keyword, name, init) => { + output + .output(ctx, keyword) + .space() + .output(ctx, name) + .space() + .char('=') + .space() + .output(ctx, init); + } + Statement::VariableSet(name, new_value) => { + output + .output(ctx, name) + .space() + .char('=') + .space() + .output(ctx, new_value); + } + Statement::IfCondition(r#if, condition, comments, else_condition) => { + output.output(ctx, r#if).space().output(ctx, condition); + + if let Some(comment) = comments.first() { + ctx.allow_newline(output, condition.1.end..=comment.1.start); + output.output(ctx, comment); + } + + for comment in comments.iter().skip(1) { + output.output(ctx, comment); + } + + if let Some(else_condition) = else_condition { + output.space().output(ctx, else_condition); + } + } + Statement::IfChain(r#if, items) => { + output + .output(ctx, r#if) + .space() + .char('{') + .increase_indentation(); + + for ele in items { + output.newline().output(ctx, ele); + } + + output.decrease_indentation(); + if items.len() > 0 { + output.newline(); + } + output.char('}'); + } + Statement::ShorthandAdd(variable, expr) => shorthand(output, ctx, variable, "+=", expr), + Statement::ShorthandSub(variable, expr) => shorthand(output, ctx, variable, "-=", expr), + Statement::ShorthandMul(variable, expr) => shorthand(output, ctx, variable, "*=", expr), + Statement::ShorthandDiv(variable, expr) => shorthand(output, ctx, variable, "/=", expr), + Statement::ShorthandModulo(variable, expr) => { + shorthand(output, ctx, variable, "%=", expr) + } + Statement::InfiniteLoop(r#loop, block) => { + output.output(ctx, r#loop).space().output(ctx, block); + } + Statement::IterLoop(r#for, element, r#in, expr, block) => { + output + .output(ctx, r#for) + .space() + .output(ctx, element) + .space() + .output(ctx, r#in) + .space() + .output(ctx, expr) + .space() + .end_output(ctx, block); + } + Statement::Break => output.end_text("break"), + Statement::Continue => output.end_text("continue"), + Statement::Return(r#return, expr) => { + output.end_output(ctx, r#return); + + if let Some(expr) = expr { + output.space().end_output(ctx, expr); + } + } + Statement::Fail(fail, expr) => { + output.end_output(ctx, fail); + + if let Some(expr) = expr { + output.space().end_output(ctx, expr); + } + } + Statement::Echo(echo, text) => output.output(ctx, echo).space().end_output(ctx, text), + Statement::Cd(cd, text) => output.output(ctx, cd).space().end_output(ctx, text), + Statement::MoveFiles(modifiers, mv, source, destination, failure_handler) => { + for modifier in modifiers { + output.output(ctx, modifier).space(); + } + + output + .output(ctx, mv) + .space() + .output(ctx, source) + .space() + .output(ctx, destination); + + if let Some(failure_handler) = failure_handler { + output.space().output(ctx, failure_handler); + } + } + Statement::Block(block) => output.end_output(ctx, block), + Statement::Comment(comment) => output.end_output(ctx, comment), + Statement::Shebang(shebang) => output.end_text(shebang.as_str()), + Statement::Error => output.error(span), + } + } +} From 8c111836bdd3295c7543ac93be905d769f8c0352 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 16:01:53 +0000 Subject: [PATCH 30/37] Test import statement formatting --- crates/formatter/tests/alpha040.rs | 80 ++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 crates/formatter/tests/alpha040.rs diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs new file mode 100644 index 0000000..f7fc83c --- /dev/null +++ b/crates/formatter/tests/alpha040.rs @@ -0,0 +1,80 @@ +use amber_fmt::format; +use amber_grammar::{ + Grammar, LSPAnalysis as _, Span, + alpha040::{AmberCompiler, GlobalStatement}, +}; + +/// Parse the input string through the amber compiler. +fn parse(data: &str) -> Vec<(GlobalStatement, Span)> { + let amber_compiler = AmberCompiler::new(); + let tokenize = amber_compiler.tokenize(&data); + let parse = amber_compiler.parse(&tokenize); + + let Grammar::Alpha040(Some(items)) = parse.ast else { + panic!("Cannot parse test text"); + }; + + items +} + +/// Asserts that the input string matches the output string after formatting. +fn test_format(input: &str, output: &str) { + let items = parse(input); + let formatted = format(&items, input).expect("Able to format amber file"); + assert_eq!(formatted, output); +} + +#[test] +fn import_allow_newlines() { + let data = r#"import { function } from "std/array" +import { function } from "std/array" + +import { function } from "std/array" +"#; + + test_format(data, data); +} + +#[test] +fn imports_singleline() { + let input = r#"import { function } from "std/array" import { function } from "std/array" import { function } from "std/array""#; + let output = r#"import { function } from "std/array" +import { function } from "std/array" +import { function } from "std/array" +"#; + + test_format(input, output); +} + +#[test] +fn imports_whitespace() { + test_format( + r#"import { function } from "std/array" "#, + r#"import { function } from "std/array" +"#, + ); +} + +#[test] +fn imports_newlines() { + test_format( + r#"import +{ +function +} +from +"std/array" +"#, + r#"import { function } from "std/array" +"#, + ); +} + +#[test] +fn imports_multifunction() { + test_format( + r#"import { function,other } from "std/array""#, + r#"import { function, other } from "std/array" +"#, + ); +} From f896c6b82db491854516ae20cd0d9c70211802b5 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Sun, 22 Feb 2026 16:13:39 +0000 Subject: [PATCH 31/37] Preserve line breaks in global statements --- .../src/alpha040/global_statement.rs | 6 ++ crates/formatter/tests/alpha040.rs | 57 +++++++++++++++---- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/crates/formatter/src/alpha040/global_statement.rs b/crates/formatter/src/alpha040/global_statement.rs index e736b39..ee8d61d 100644 --- a/crates/formatter/src/alpha040/global_statement.rs +++ b/crates/formatter/src/alpha040/global_statement.rs @@ -154,6 +154,12 @@ impl TextOutput for GlobalStatement { } GlobalStatement::Statement(statement) => { output.output(ctx, statement).newline(); + + if let Some(next_global) = ctx.next_global() + && matches!(next_global.0, GlobalStatement::Statement(..)) + { + ctx.allow_newline(output, span.end..=next_global.1.start); + } } } } diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index f7fc83c..9b179b5 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -18,21 +18,31 @@ fn parse(data: &str) -> Vec<(GlobalStatement, Span)> { } /// Asserts that the input string matches the output string after formatting. +/// +/// A newline is appended to output. fn test_format(input: &str, output: &str) { let items = parse(input); let formatted = format(&items, input).expect("Able to format amber file"); - assert_eq!(formatted, output); + assert_eq!(formatted, format!("{output}\n")); } #[test] fn import_allow_newlines() { - let data = r#"import { function } from "std/array" + test_format( + r#"import { function } from "std/array" +import { function } from "std/array" + +import { function } from "std/array" + + +import { large } from "std/array""#, + r#"import { function } from "std/array" import { function } from "std/array" import { function } from "std/array" -"#; - test_format(data, data); +import { large } from "std/array""#, + ); } #[test] @@ -40,8 +50,7 @@ fn imports_singleline() { let input = r#"import { function } from "std/array" import { function } from "std/array" import { function } from "std/array""#; let output = r#"import { function } from "std/array" import { function } from "std/array" -import { function } from "std/array" -"#; +import { function } from "std/array""#; test_format(input, output); } @@ -50,8 +59,7 @@ import { function } from "std/array" fn imports_whitespace() { test_format( r#"import { function } from "std/array" "#, - r#"import { function } from "std/array" -"#, + r#"import { function } from "std/array""#, ); } @@ -65,8 +73,7 @@ function from "std/array" "#, - r#"import { function } from "std/array" -"#, + r#"import { function } from "std/array""#, ); } @@ -74,7 +81,35 @@ from fn imports_multifunction() { test_format( r#"import { function,other } from "std/array""#, - r#"import { function, other } from "std/array" + r#"import { function, other } from "std/array""#, + ); +} + +#[test] +fn top_level() { + test_format( + r#"echo "abc" echo "123""#, + r#"echo "abc" +echo "123""#, + ); +} + +#[test] +fn top_level_newlines() { + test_format( + r#"echo "abc" +echo "123" + +echo "123" + + +echo "large" "#, + r#"echo "abc" +echo "123" + +echo "123" + +echo "large""#, ); } From 4ea76c1d571d3d36fe925fd67328365ec175b978 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Tue, 24 Feb 2026 23:15:12 +0000 Subject: [PATCH 32/37] Don't force space after if block --- crates/formatter/src/alpha040/mod.rs | 7 +------ crates/formatter/tests/alpha040.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index c25a2a3..87ec40f 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -83,7 +83,6 @@ impl TextOutput for IterLoopVars { impl TextOutput for Block { fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { - // output.debug_point("Block").span(span); match self { Block::Block(modifiers, statements) => { output.char('{').increase_indentation().end_newline(); @@ -135,11 +134,7 @@ impl TextOutput for IfCondition { fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { match self { IfCondition::IfCondition(condition, block) => { - output - .output(ctx, condition) - .space() - .output(ctx, block) - .newline(); + output.output(ctx, condition).space().end_output(ctx, block) } IfCondition::InlineIfCondition(condition, statement) => { output diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index 9b179b5..675c1bf 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -113,3 +113,15 @@ echo "123" echo "large""#, ); } + +#[test] +fn if_spacing() { + test_format( + r#"if true { + echo "a" +}"#, + r#"if true { + echo "a" +}"#, + ); +} From 3d5508a7db88e61955df64a8563b301d60eba491 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 25 Feb 2026 21:32:25 +0000 Subject: [PATCH 33/37] Don't output () for main with no args --- crates/formatter/src/alpha040/global_statement.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/crates/formatter/src/alpha040/global_statement.rs b/crates/formatter/src/alpha040/global_statement.rs index ee8d61d..a9597a0 100644 --- a/crates/formatter/src/alpha040/global_statement.rs +++ b/crates/formatter/src/alpha040/global_statement.rs @@ -115,16 +115,10 @@ impl TextOutput for GlobalStatement { GlobalStatement::Main(main, args, statements) => { output.output(ctx, main); - output.char('('); if let Some(args) = args { - output.output(ctx, args); + output.char('(').output(ctx, args).char(')'); } - output - .char(')') - .space() - .char('{') - .increase_indentation() - .newline(); + output.space().char('{').increase_indentation().newline(); let mut last_span_end = None; for statement in statements { From 16dc140e986ec75df4114444c9ed0a3d8094dbb0 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 25 Feb 2026 21:33:13 +0000 Subject: [PATCH 34/37] Ensure main and fun blocks are surrounded by newlines --- .../src/alpha040/global_statement.rs | 8 +++++++ crates/formatter/tests/alpha040.rs | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/crates/formatter/src/alpha040/global_statement.rs b/crates/formatter/src/alpha040/global_statement.rs index a9597a0..959dadc 100644 --- a/crates/formatter/src/alpha040/global_statement.rs +++ b/crates/formatter/src/alpha040/global_statement.rs @@ -50,6 +50,10 @@ impl TextOutput for GlobalStatement { return_type, contents, ) => { + if ctx.previous_global().is_some() { + output.newline(); + } + for flag in compiler_flags { output.output(ctx, flag); output.newline(); @@ -113,6 +117,10 @@ impl TextOutput for GlobalStatement { } } GlobalStatement::Main(main, args, statements) => { + if ctx.previous_global().is_some() { + output.newline(); + } + output.output(ctx, main); if let Some(args) = args { diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index 675c1bf..5a41d7c 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -23,6 +23,7 @@ fn parse(data: &str) -> Vec<(GlobalStatement, Span)> { fn test_format(input: &str, output: &str) { let items = parse(input); let formatted = format(&items, input).expect("Able to format amber file"); + eprintln!("Formatted:\n{}\nOutput:\n{}", formatted, output); assert_eq!(formatted, format!("{output}\n")); } @@ -125,3 +126,23 @@ fn if_spacing() { }"#, ); } + +#[test] +fn newline_around_funcs() { + test_format( + concat!( + "let a = 1\n", + "fun some() {\n let a = 2\n}\n", + "let a = 3\n", + "main {\n let a = 4\n}\n", + "let a = 5", + ), + concat!( + "let a = 1\n\n", + "fun some() {\n let a = 2\n}\n\n", + "let a = 3\n\n", + "main {\n let a = 4\n}\n\n", + "let a = 5", + ), + ); +} From 48d3314b6317e01e55d301ded9222e6f2784074c Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 25 Feb 2026 22:06:11 +0000 Subject: [PATCH 35/37] Don't output any whitespace after last amber expression --- crates/formatter/src/format.rs | 4 +++- crates/formatter/tests/alpha040.rs | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/formatter/src/format.rs b/crates/formatter/src/format.rs index 6c77c10..be5f575 100644 --- a/crates/formatter/src/format.rs +++ b/crates/formatter/src/format.rs @@ -298,7 +298,9 @@ impl Output { self } - fn format(self, file_content: &str) -> Result { + fn format(mut self, file_content: &str) -> Result { + self.remove_trailing_whitespace(); + let mut text = String::new(); let mut indentation = String::new(); let mut consecutive_newlines = 0; diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index 5a41d7c..b2d9f8d 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -18,13 +18,11 @@ fn parse(data: &str) -> Vec<(GlobalStatement, Span)> { } /// Asserts that the input string matches the output string after formatting. -/// -/// A newline is appended to output. fn test_format(input: &str, output: &str) { let items = parse(input); let formatted = format(&items, input).expect("Able to format amber file"); eprintln!("Formatted:\n{}\nOutput:\n{}", formatted, output); - assert_eq!(formatted, format!("{output}\n")); + assert_eq!(formatted, output); } #[test] From 9565ed253c0678d3e16de05de20ebed07c6a6306 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 25 Feb 2026 22:06:43 +0000 Subject: [PATCH 36/37] Ensure proper spacing for function return types --- crates/formatter/src/alpha040/global_statement.rs | 8 +++----- crates/formatter/tests/alpha040.rs | 8 ++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/formatter/src/alpha040/global_statement.rs b/crates/formatter/src/alpha040/global_statement.rs index 959dadc..fa01ca7 100644 --- a/crates/formatter/src/alpha040/global_statement.rs +++ b/crates/formatter/src/alpha040/global_statement.rs @@ -80,15 +80,13 @@ impl TextOutput for GlobalStatement { output.output(ctx, arg); } } - output.char(')').space(); + output.char(')'); if let Some(returns) = return_type { - output.char(':'); - output.space(); - output.output(ctx, returns); + output.char(':').space().output(ctx, returns); } - output.char('{').increase_indentation().newline(); + output.space().char('{').increase_indentation().newline(); let mut last_span_end = None; for content in contents { diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index b2d9f8d..c0304f6 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -144,3 +144,11 @@ fn newline_around_funcs() { ), ); } + +#[test] +fn return_type_spacing_func() { + test_format( + concat!("fun some() :Num{\n 2\n}",), + concat!("fun some(): Num {\n 2\n}",), + ); +} From 9201d10cd2faa75a3d0c8090f8ae06c447c51154 Mon Sep 17 00:00:00 2001 From: tye-exe Date: Wed, 25 Feb 2026 22:27:20 +0000 Subject: [PATCH 37/37] Format argument types and default values --- crates/formatter/src/alpha040/mod.rs | 49 +++++++++++++++++++--------- crates/formatter/tests/alpha040.rs | 9 +++-- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/crates/formatter/src/alpha040/mod.rs b/crates/formatter/src/alpha040/mod.rs index 87ec40f..3b6b95b 100644 --- a/crates/formatter/src/alpha040/mod.rs +++ b/crates/formatter/src/alpha040/mod.rs @@ -39,23 +39,42 @@ impl TextOutput for ImportContent { impl TextOutput for FunctionArgument { fn output(&self, span: &Span, output: &mut Output, ctx: &mut FmtContext) { - fn push_arg( - output: &mut Output, - ctx: &mut FmtContext, - is_ref: bool, - text: &impl SpanTextOutput, - ) { - if is_ref { - output.text("ref"); - output.space(); + match self { + FunctionArgument::Generic(is_ref, text) => { + if is_ref.0 { + output.text("ref"); + output.space(); + } + output.output(ctx, text); } - output.output(ctx, text); - } + FunctionArgument::Optional(is_ref, text, data_type, expression) => { + let is_ref = is_ref.0; + if is_ref { + output.text("ref"); + output.space(); + } - match self { - FunctionArgument::Generic(is_ref, text) => push_arg(output, ctx, is_ref.0, text), - FunctionArgument::Optional(is_ref, text, _, _) => push_arg(output, ctx, is_ref.0, text), - FunctionArgument::Typed(is_ref, text, _) => push_arg(output, ctx, is_ref.0, text), + output.output(ctx, text); + + if let Some(data_type) = data_type { + output.char(':').space().output(ctx, data_type); + } + + output.space().char('=').space().output(ctx, expression); + } + FunctionArgument::Typed(is_ref, text, data_type) => { + let is_ref = is_ref.0; + if is_ref { + output.text("ref"); + output.space(); + } + + output + .output(ctx, text) + .char(':') + .space() + .output(ctx, data_type); + } FunctionArgument::Error => { output.error(span); } diff --git a/crates/formatter/tests/alpha040.rs b/crates/formatter/tests/alpha040.rs index c0304f6..b68bd76 100644 --- a/crates/formatter/tests/alpha040.rs +++ b/crates/formatter/tests/alpha040.rs @@ -147,8 +147,13 @@ fn newline_around_funcs() { #[test] fn return_type_spacing_func() { + test_format("fun some() :Num{\n 2\n}", "fun some(): Num {\n 2\n}"); +} + +#[test] +fn function_args() { test_format( - concat!("fun some() :Num{\n 2\n}",), - concat!("fun some(): Num {\n 2\n}",), + "fun some(arg1,arg2:Num,arg3=10,arg4:Num=10) :Num{\n 2\n}", + "fun some(arg1, arg2: Num, arg3 = 10, arg4: Num = 10): Num {\n 2\n}", ); }