From b71b7f309b7978c863da7d6e3e4d62045cab91bf Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Wed, 15 Nov 2023 10:55:14 +0100 Subject: [PATCH] start with bare basics --- .envrc | 1 + .gitignore | 3 + Cargo.lock | 168 +++++++++++++++++++++++++++ Cargo.toml | 11 ++ flake.lock | 305 ++++++++++++++++++++++++++++++++++++++++++++++++++ flake.nix | 39 +++++++ src/lexer.rs | 58 ++++++++++ src/main.rs | 16 +++ src/syntax.rs | 98 ++++++++++++++++ src/utils.rs | 13 +++ 10 files changed, 712 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 src/lexer.rs create mode 100644 src/main.rs create mode 100644 src/syntax.rs create mode 100644 src/utils.rs diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..ff5954f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake . --impure \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dd461a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.devenv/ +.direnv/ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..eb9eb9b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,168 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "logos" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c000ca4d908ff18ac99b93a062cb8958d331c3220719c52e77cb19cc6ac5d2c1" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" +dependencies = [ + "beef", + "fnv", + "proc-macro2", + "quote", + "regex-syntax", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfc0d229f1f42d790440136d941afd806bc9e949e2bcb8faa813b0f00d1267e" +dependencies = [ + "logos-codegen", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "pipeline-lang" +version = "0.1.0" +dependencies = [ + "codespan-reporting", + "logos", + "winnow", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winnow" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..82f150d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pipeline-lang" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +winnow = "0.5" +logos = "0.13" +codespan-reporting = "0.11" diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..0ee4e36 --- /dev/null +++ b/flake.lock @@ -0,0 +1,305 @@ +{ + "nodes": { + "devenv": { + "inputs": { + "flake-compat": "flake-compat", + "nix": "nix", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1699541523, + "narHash": "sha256-Rv0ryuBC5KtA/3YqwIEe58Tabu71qSGnGcGRd67mMUY=", + "owner": "cachix", + "repo": "devenv", + "rev": "14fdefc0bb80c3d6f3a18a491e33429b4064c371", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": "nixpkgs_2", + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1699510895, + "narHash": "sha256-eaOkJUvHeYNW/xEoRotz0rHkKihKoQdWB1ctX4q1MTQ=", + "owner": "nix-community", + "repo": "fenix", + "rev": "8eeef23f2c8d092227af40eff98afe5b41891e3b", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1660459072, + "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "lowdown-src": { + "flake": false, + "locked": { + "lastModified": 1633514407, + "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", + "owner": "kristapsdz", + "repo": "lowdown", + "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "type": "github" + }, + "original": { + "owner": "kristapsdz", + "repo": "lowdown", + "type": "github" + } + }, + "nix": { + "inputs": { + "lowdown-src": "lowdown-src", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1676545802, + "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "owner": "domenkozar", + "repo": "nix", + "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "relaxed-flakes", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1678875422, + "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1685801374, + "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1699099776, + "narHash": "sha256-X09iKJ27mGsGambGfkKzqvw5esP1L/Rf8H3u3fCqIiU=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "85f1ba3e51676fa8cc604a3d863d729026a6b8eb", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1699291058, + "narHash": "sha256-5ggduoaAMPHUy4riL+OrlAZE14Kh7JWX4oLEs22ZqfU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "41de143fda10e33be0f47eab2bfe08a50f234267", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1688056373, + "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "fenix": "fenix", + "nixpkgs": "nixpkgs_3", + "systems": "systems_2" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1699451299, + "narHash": "sha256-7HJMyp62fCS6/aCCCASz8MdJM2/M8d1pBNukyLmPdwA=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "7059ae2fc2d55fa20d7e2671597b516431129445", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "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" + } + }, + "systems_2": { + "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..6aaf749 --- /dev/null +++ b/flake.nix @@ -0,0 +1,39 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05"; + systems.url = "github:nix-systems/default"; + devenv.url = "github:cachix/devenv"; + fenix.url = "github:nix-community/fenix"; + }; + + nixConfig = { + extra-trusted-public-keys = "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="; + extra-substituters = "https://devenv.cachix.org"; + }; + + outputs = { self, nixpkgs, devenv, systems, ... } @ inputs: + let + forEachSystem = nixpkgs.lib.genAttrs (import systems); + in + { + devShells = forEachSystem + (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + in + { + default = devenv.lib.mkShell { + inherit inputs pkgs; + modules = [ + ({pkgs, config, ...}: { + languages.rust = { + enable = true; + channel = "nightly"; + }; + packages = []; + }) + ]; + }; + }); + }; +} diff --git a/src/lexer.rs b/src/lexer.rs new file mode 100644 index 0000000..831a1c4 --- /dev/null +++ b/src/lexer.rs @@ -0,0 +1,58 @@ +use logos::Logos; + +#[derive(Logos, Debug, PartialEq)] +#[logos(skip r"[\s]+")] +pub enum Token<'a> { + #[regex("[\\w]+", |lex| lex.slice())] + Word(&'a str), + #[regex("[\\d]+", priority = 2, callback = |lex| lex.slice().parse::().unwrap())] + IntLiteral(i64), + #[regex("[\\d]+.[\\d]+", |lex| lex.slice().parse::().unwrap())] + FloatLiteral(f64), + #[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice().to_owned())] + StringLiteral(String), + #[token("def")] + Define, + #[token("type")] + Type, + #[token("->")] + RightArrow, + #[token("|")] + Pipe, + #[token("[")] + BracketOpening, + #[token("]")] + BracketClosing, + #[token("(")] + ParensOpening, + #[token(")")] + ParensClosing, + #[token("{")] + BraceOpening, + #[token("}")] + BraceClosing, + #[token("+")] + Plus, + #[token("-")] + Minus, + #[token("*")] + Multiply, + #[token("/")] + Divide, + #[token("%")] + Percent, + #[token("&")] + Ampersand, + #[token(":")] + Colon, + #[token(";")] + Semicolon, + #[token(".")] + Dot, + #[token(",")] + Comma, + #[token("!")] + ExclaimationMark, + #[token("?")] + QuestionMark, +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..ad6846e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,16 @@ +use lexer::Token; +use logos::Lexer; +use logos::Logos; +use syntax::parse; +use utils::ws; +use winnow::prelude::*; +use winnow::Parser; + +mod lexer; +mod syntax; +mod utils; + +fn main() { + let input = "load \"./image.png\" | invert | save \"./image_processed.jpg\""; + dbg!(parse(input)); +} diff --git a/src/syntax.rs b/src/syntax.rs new file mode 100644 index 0000000..b4ed5c6 --- /dev/null +++ b/src/syntax.rs @@ -0,0 +1,98 @@ +use std::mem; + +use logos::Logos; +use logos::Span; + +use crate::lexer::Token; + +#[derive(Debug, Clone, PartialEq)] +pub struct PipelineElement { + kind: PipelineElementKind, + span: Span, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum PipelineElementKind { + Pipe, + Command(Vec), +} + +#[derive(Debug, Clone, PartialEq)] +pub struct CommandPart { + kind: CommandPartKind, + span: Span, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum CommandPartKind { + Word(String), + Integer(i64), + Float(f64), + String(String), +} + +pub fn parse(input: &str) -> Vec { + let lexer = Token::lexer(input); + + let mut r = Vec::new(); + + let mut partial_command: Vec = Vec::new(); + for (tok, span) in lexer.spanned().into_iter() { + if let Ok(tok) = tok { + match tok { + Token::Pipe => { + if !partial_command.is_empty() { + let span = partial_command.first().unwrap().span.start + ..partial_command.last().unwrap().span.end; + r.push(PipelineElement { + kind: PipelineElementKind::Command(mem::replace( + &mut partial_command, + Vec::new(), + )), + span, + }); + } + r.push(PipelineElement { + kind: PipelineElementKind::Pipe, + span, + }); + } + Token::Word(word) => partial_command.push(CommandPart { + kind: CommandPartKind::Word(word.to_owned()), + span, + }), + Token::IntLiteral(int) => partial_command.push(CommandPart { + kind: CommandPartKind::Integer(int), + span, + }), + Token::FloatLiteral(float) => partial_command.push(CommandPart { + kind: CommandPartKind::Float(float), + span, + }), + Token::StringLiteral(string) => partial_command.push(CommandPart { + kind: CommandPartKind::String(string), + span, + }), + _ => {} + } + } else { + } + } + + if !partial_command.is_empty() { + let span = + partial_command.first().unwrap().span.start..partial_command.last().unwrap().span.end; + r.push(PipelineElement { + kind: PipelineElementKind::Command(mem::replace(&mut partial_command, Vec::new())), + span, + }); + } + + r +} + +#[test] +fn test_simple_parse_pipeline() { + let test_pipeline = "load ./image.png | invert | save ./image_processed.jpg"; + parse(test_pipeline); +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..5386119 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,13 @@ +use winnow::ascii::space0; +// from https://docs.rs/winnow/latest/winnow/_topic/language/index.html#whitespace +use winnow::prelude::*; +use winnow::{ascii::multispace0, combinator::delimited, error::ParserError}; + +/// A combinator that takes a parser `inner` and produces a parser that also consumes both leading and +/// trailing whitespace, returning the output of `inner`. +pub fn ws<'a, F, O, E: ParserError<&'a str>>(inner: F) -> impl Parser<&'a str, O, E> +where + F: Parser<&'a str, O, E>, +{ + delimited(space0, inner, space0) +}