restart basically lmao

This commit is contained in:
Schrottkatze 2023-12-13 18:13:43 +01:00
parent 49995bbc62
commit b4d48a598a
26 changed files with 61 additions and 1907 deletions

326
Cargo.lock generated
View file

@ -3,329 +3,13 @@
version = 3 version = 3
[[package]] [[package]]
name = "anstream" name = "executor"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "beef"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
[[package]]
name = "clap"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[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 = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[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 = "pipeline-lang"
version = "0.1.0" version = "0.1.0"
dependencies = [
"clap",
"codespan-reporting",
"logos",
]
[[package]] [[package]]
name = "proc-macro2" name = "pl-cli"
version = "1.0.69" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]] [[package]]
name = "quote" name = "rpl"
version = "1.0.33" version = "0.1.0"
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 = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[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 = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[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 = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View file

@ -1,14 +1,4 @@
[package] workspace = { members = [ "crates/executor", "crates/pl-cli","crates/rpl"] }
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]
logos = "0.13"
codespan-reporting = "0.11"
clap = { version = "4.4.8", features = [ "derive" ] }
[lints.rust] [lints.rust]
unsafe_code = "deny" unsafe_code = "deny"

View file

@ -0,0 +1,8 @@
[package]
name = "executor"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

8
crates/pl-cli/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "pl-cli"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View file

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

8
crates/rpl/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "rpl"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

14
crates/rpl/src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}

View file

@ -1,11 +0,0 @@
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
pub text: String,
#[arg(long)]
pub debug_tokens: bool,
#[arg(long)]
pub debug_typed_repr: bool,
}

View file

@ -1,39 +0,0 @@
use crate::namespace::{typedef::TypeDef, GlobalNamespace};
pub const TYPE_INTEGER: &str = "int";
pub const TYPE_FLOAT: &str = "float";
pub const TYPE_STRING: &str = "string";
pub const TRAIT_NUMERIC: &str = "Num";
pub const CMD_ADD: &str = "add";
#[allow(
clippy::unwrap_used,
reason = "Errs can only be returned in case of duplicate names in the same namespace, which will not happen here"
)]
#[allow(clippy::missing_panics_doc, reason = "will not panic")]
pub fn initialise_globals() -> GlobalNamespace {
let ns = GlobalNamespace::init();
let numeric = ns.register_trait(TRAIT_NUMERIC).unwrap();
ns.register_type(TYPE_INTEGER).unwrap().add_trait(&numeric);
ns.register_type(TYPE_FLOAT).unwrap().add_trait(&numeric);
ns.register_type(TYPE_STRING).unwrap();
// def math add [ Numeric Numeric ] -> Numeric
ns.register_command(
CMD_ADD,
vec![("T", vec![&numeric])],
Some(&TypeDef::List(vec![
TypeDef::Generic("T".to_owned()),
TypeDef::Generic("T".to_owned()),
])),
Some(&TypeDef::Generic("T".to_owned())),
)
.unwrap();
ns
}

View file

@ -1 +0,0 @@

View file

@ -1,90 +0,0 @@
use codespan_reporting::{
diagnostic::{Diagnostic, Label},
files::SimpleFiles,
};
use crate::Span;
#[derive(Debug)]
pub struct Errors {
pub kind: ErrorKind,
pub locs: Vec<Span>,
}
#[derive(Debug)]
pub enum ErrorKind {
InvalidToken,
SyntaxError(SyntaxErrorKind),
CommandNotFound,
}
#[derive(Debug)]
pub enum SyntaxErrorKind {
/// `MissingStreamer` means, that the pipeline starts with a Pipe (`|`), so it has no streamer as input in front of it.
MissingStreamer,
/// `MissingSink` means, that the pipeline ends with a Pipe (`|`), meaning that the output can't go anywhere
MissingSink,
/// This indicates a missing filter somewhere in the pipeline, meaning that there's 2 pipes after one another
MissingFilter,
/// A literal cannot be a sink
LiteralAsSink,
/// A literal can't be a filter either
LiteralAsFilter,
/// A literal acting as streamer cannot take arguments
LiteralWithArgs,
}
impl Errors {
pub fn new(kind: ErrorKind, locs: Vec<Span>) -> Self {
Self { kind, locs }
}
pub fn new_single(kind: ErrorKind, loc: Span) -> Self {
Self {
kind,
locs: vec![loc],
}
}
pub fn into_diag(
&self,
file_id: usize,
_file_db: &SimpleFiles<&str, String>,
) -> Diagnostic<usize> {
let Errors { kind, locs } = self;
match kind {
ErrorKind::InvalidToken => simple_diag(locs.clone(), file_id, "invalid tokens"),
ErrorKind::SyntaxError(syntax_error) => match syntax_error {
SyntaxErrorKind::MissingStreamer => simple_diag(
locs.clone(),
file_id,
"pipeline is missing an input provider",
),
SyntaxErrorKind::MissingSink => {
simple_diag(locs.clone(), file_id, "pipeline is missing a sink")
}
SyntaxErrorKind::MissingFilter => {
simple_diag(locs.clone(), file_id, "missing filters in pipeline")
}
SyntaxErrorKind::LiteralAsSink => {
simple_diag(locs.clone(), file_id, "pipelines can't end in a literal")
}
SyntaxErrorKind::LiteralAsFilter => {
simple_diag(locs.clone(), file_id, "literals can't filter data")
}
SyntaxErrorKind::LiteralWithArgs => {
simple_diag(locs.clone(), file_id, "literals can't take arguments")
}
},
ErrorKind::CommandNotFound => simple_diag(locs.clone(), file_id, "command not found"),
}
}
}
fn simple_diag(spans: Vec<Span>, file_id: usize, msg: &str) -> Diagnostic<usize> {
Diagnostic::error().with_message(msg).with_labels(
spans
.into_iter()
.map(|span| Label::primary(file_id, span))
.collect(),
)
}

View file

@ -1,137 +0,0 @@
use std::{collections::HashMap, process::exit};
use clap::error::Result;
use codespan_reporting::{
files::SimpleFiles,
term::{
self,
termcolor::{ColorChoice, StandardStream},
},
};
use crate::{
builtins::initialise_globals,
error::{ErrorKind, Errors},
syntax::{check::check, parse_syntax, PipelineElement},
typed::into_typed_repr,
};
pub struct EvalConfig {
debug_raw_toks: bool,
debug_print_typed_repr: bool,
}
impl EvalConfig {
pub fn new(debug_raw_toks: bool, debug_print_typed_repr: bool) -> Self {
Self {
debug_raw_toks,
debug_print_typed_repr,
}
}
}
pub type FileId = usize;
// this is also bad
// need a better architecture for this
pub struct Evaluator<'a> {
curr_phase: EvalPhase,
files: SimpleFiles<&'a str, String>,
errors: HashMap<FileId, Vec<Errors>>,
cfg: EvalConfig,
}
impl<'a> Evaluator<'a> {
pub fn init(cfg: EvalConfig) -> Self {
Self {
curr_phase: EvalPhase::Lex,
files: SimpleFiles::new(),
errors: HashMap::new(),
cfg,
}
}
pub fn run(&mut self, input: &str, name: Option<&'a str>) {
let fid = self.files.add(name.unwrap_or("input"), input.to_owned());
let syntax = parse_syntax(input);
if self.cfg.debug_raw_toks {
println!("Raw tokens: {syntax:#?}");
}
match syntax {
Ok(syntax) => self.curr_phase = EvalPhase::Check(fid, syntax),
Err(errs) => {
self.errors.insert(
fid,
vec![Errors {
kind: ErrorKind::InvalidToken,
locs: errs,
}],
);
self.curr_phase = EvalPhase::Failed;
}
};
}
pub fn next(mut self) {
match self.curr_phase {
EvalPhase::Lex => {
unreachable!()
}
EvalPhase::Check(file_id, syntax) => {
let r = check(&syntax);
if let Err(errs) = r {
self.errors.insert(file_id, errs);
self.curr_phase = EvalPhase::Failed;
} else {
self.curr_phase = EvalPhase::BareTyped(file_id, syntax.clone());
}
}
EvalPhase::BareTyped(file_id, syntax) => {
let ns = initialise_globals();
let r = into_typed_repr(&ns, syntax);
match r {
Ok(typed) => {
if self.cfg.debug_print_typed_repr {
println!("Typed repr: {typed:#?}");
}
todo!()
}
Err(errs) => {
self.errors.insert(file_id, vec![errs]);
self.curr_phase = EvalPhase::Failed;
}
}
}
EvalPhase::Failed => self.error_out().expect("unable to print errors"),
}
self.next();
}
pub fn error_out(self) -> Result<!, codespan_reporting::files::Error> {
let Evaluator { files, errors, .. } = self;
let writer = StandardStream::stderr(ColorChoice::Always);
let config = term::Config::default();
for (file_id, errors) in &errors {
let writer = &mut writer.lock();
for error in errors {
term::emit(writer, &config, &files, &error.into_diag(*file_id, &files))?;
}
}
exit(1)
}
}
enum EvalPhase {
Lex,
Check(FileId, Vec<PipelineElement>),
BareTyped(FileId, Vec<PipelineElement>),
Failed,
}

View file

@ -1,26 +0,0 @@
// concepts
// root type namespace
// -> builtin types/traits/functions
// -> builtin constants
//
// generic item namespaces
// -> in generic functions ( `add<T: Num> [ T T ] -> T` )
// -> in generic types ( vec<T> )
// -> generic traits ( T: From<E> )
//
// TODO: how builtins? just compile everything to builtin types/defs?
/// This is the root type namespace.
struct RootTypeNamespace {
}
mod r#type {
}
struct Type<'a> {
pub(super) id: usize,
pub(super) namespace: &'a GlobalNamespace,
}
struct InternalType

View file

@ -1,58 +0,0 @@
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::<i64>().expect("regex should only match valid integers. This is a bug."))]
IntLiteral(i64),
#[regex("[\\d]+\\.[\\d]+", |lex| lex.slice().parse::<f64>().expect("regex should only match valid floats. This is a bug."))]
FloatLiteral(f64),
#[regex(r#""([^"\\]|\\["\\bnfrt]|u[a-fA-F0-9]{4})*""#, |lex| lex.slice().to_owned())]
StringLiteral(String),
#[token("|")]
Pipe,
// #[token("def")]
// Define,
// #[token("type")]
// Type,
// #[token("->")]
// RightArrow,
// #[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,
}

View file

@ -1,20 +0,0 @@
#![feature(never_type, lint_reasons)]
use std::ops::Range;
pub mod args;
pub mod builtins;
pub mod error;
pub mod evaluator;
#[allow(clippy::indexing_slicing, reason = "in logos, outside our control")]
pub mod lexer;
pub mod namespace;
pub mod syntax;
#[allow(dead_code, reason = "the future!!!")]
pub mod typed;
// basically logos::Span but in this repo
pub type Span = Range<usize>;

View file

@ -1,12 +0,0 @@
use clap::Parser;
use pipeline_lang::args::Args;
use pipeline_lang::evaluator::{EvalConfig, Evaluator};
fn main() {
let args = Args::parse();
let mut evaluator = Evaluator::init(EvalConfig::new(args.debug_tokens, args.debug_typed_repr));
evaluator.run(&args.text, None);
evaluator.next();
}

View file

@ -1,70 +0,0 @@
use std::fmt::Debug;
use std::fmt::Display;
use super::typedef::{InternalTypeDef, TypeDef};
use super::CommandId;
use super::GlobalNamespace;
#[derive(Clone, Copy)]
pub struct Command<'a> {
pub(super) id: CommandId,
pub(super) namespace: &'a GlobalNamespace,
}
impl<'a> Command<'a> {
pub fn get_input_types(&self) -> Option<TypeDef<'a>> {
self.namespace.commands.borrow()[self.id]
.input
.as_ref()
.map(|def| TypeDef::from_internal(self.namespace, def))
}
// get a count of inputs required
pub fn input_count(&self) -> usize {
self.namespace.commands.borrow()[self.id]
.input
.as_ref()
.map_or(0, |inputs| match inputs {
InternalTypeDef::Single(_) | InternalTypeDef::Generic(_) => 1,
InternalTypeDef::List(list) => list.len(),
InternalTypeDef::Record(rec) => rec.len(),
})
}
pub fn get_output_types(&self) -> Option<TypeDef> {
self.namespace.commands.borrow()[self.id]
.output
.as_ref()
.map(|def| TypeDef::from_internal(self.namespace, def))
}
}
impl Display for Command<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = &self.namespace.commands.borrow()[self.id].name;
f.write_fmt(format_args!(
"{name} {} -> {}",
self.get_input_types()
.map_or("!".to_owned(), |v| v.to_string()),
self.get_output_types()
.map_or("!".to_owned(), |v| v.to_string())
))
}
}
impl Debug for Command<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = &self.namespace.commands.borrow()[self.id].name;
f.write_fmt(format_args!(
"{name:?} {:?} -> {:?}",
self.get_input_types(),
self.get_output_types()
))
}
}
pub(super) struct InternalCommand {
pub(super) name: String,
// gosh this is hacky
pub(super) input: Option<InternalTypeDef>,
pub(super) output: Option<InternalTypeDef>,
}

View file

@ -1,213 +0,0 @@
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
};
use self::{
command::{Command, InternalCommand},
r#trait::{InternalTrait, Trait},
r#type::{InternalType, Type},
typedef::TypeDef,
};
pub mod command;
pub mod r#trait;
pub mod r#type;
pub mod typedef;
pub struct GlobalNamespace {
types: RefCell<Vec<InternalType>>,
traits: RefCell<Vec<InternalTrait>>,
type_namespace: RefCell<HashMap<String, TypeNamespaceId>>,
commands: RefCell<Vec<InternalCommand>>,
data_namespace: RefCell<HashMap<String, DataNamespaceId>>,
}
pub type TypeId = usize;
pub type TraitId = usize;
pub type CommandId = usize;
enum TypeNamespaceId {
Types(TypeId),
Traits(TraitId),
}
enum DataNamespaceId {
Commands(CommandId),
}
impl GlobalNamespace {
pub fn init() -> Self {
Self {
types: RefCell::new(Vec::new()),
traits: RefCell::new(Vec::new()),
type_namespace: RefCell::new(HashMap::new()),
commands: RefCell::new(Vec::new()),
data_namespace: RefCell::new(HashMap::new()),
}
}
/// register a type to the namespace
///
/// # Errors
///
/// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use
pub fn register_type(&self, name: &str) -> Result<Type, NsRegistrationError> {
if self.type_namespace.borrow().contains_key(name) {
Err(NsRegistrationError::NameAlreadyExists)
} else {
self.types.borrow_mut().push(InternalType {
traits: RefCell::new(HashSet::new()),
name: name.to_owned(),
});
let id = self.types.borrow().len() - 1;
let _ = self
.type_namespace
.borrow_mut()
.insert(name.to_owned(), TypeNamespaceId::Types(id));
Ok(Type {
id,
namespace: self,
})
}
}
/// register a trait to the namespace
///
/// # Errors
///
/// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use
pub fn register_trait(&self, name: &str) -> Result<Trait, NsRegistrationError> {
if self.type_namespace.borrow().contains_key(name) {
Err(NsRegistrationError::NameAlreadyExists)
} else {
self.traits.borrow_mut().push(InternalTrait {
types: RefCell::new(HashSet::new()),
name: name.to_owned(),
});
let id = self.traits.borrow().len() - 1;
let _ = self
.type_namespace
.borrow_mut()
.insert(name.to_owned(), TypeNamespaceId::Traits(id));
Ok(Trait {
id,
namespace: self,
})
}
}
/// register a command to the namespace
///
/// # Errors
///
/// Will return `NsRegistrationError::NameAlreadyExists` if the desired name is already in use
pub fn register_command<'a>(
&self,
name: &'a str,
// TODO: refacto with builder pattern maybe?
generics: Vec<(&'a str, Vec<&Trait>)>,
input: Option<&'a TypeDef>,
output: Option<&'a TypeDef>,
) -> Result<Command, NsRegistrationError<'a>> {
if self.data_namespace.borrow().contains_key(name) {
Err(NsRegistrationError::NameAlreadyExists)
} else {
let mut internal_generics_namespace: HashMap<String, Vec<TraitId>> = HashMap::new();
for (name, conditions) in generics {
if internal_generics_namespace.contains_key(name) {
return Err(NsRegistrationError::GenericNameAlreadyExists);
}
internal_generics_namespace
.insert(name.to_owned(), conditions.iter().map(|t| t.id).collect());
}
if let Some(def) = input {
def.check_generics_exist(&internal_generics_namespace)
.map_err(NsRegistrationError::UnregisteredGenericsUsed)?;
}
if let Some(def) = output {
def.check_generics_exist(&internal_generics_namespace)
.map_err(NsRegistrationError::UnregisteredGenericsUsed)?;
}
self.commands.borrow_mut().push(InternalCommand {
name: name.to_owned(),
input: input.map(std::convert::Into::into),
output: output.map(std::convert::Into::into),
});
let id = self.traits.borrow().len() - 1;
let _ = self
.data_namespace
.borrow_mut()
.insert(name.to_owned(), DataNamespaceId::Commands(id));
Ok(Command {
id,
namespace: self,
})
}
}
pub fn get_type(&self, id: TypeId) -> Option<Type> {
(self.types.borrow().len() > id).then_some(Type {
id,
namespace: self,
})
}
pub fn get_trait(&self, id: TraitId) -> Option<Trait> {
(self.traits.borrow().len() > id).then_some(Trait {
id,
namespace: self,
})
}
pub fn get_command(&self, id: CommandId) -> Option<Command> {
(self.commands.borrow().len() > id).then_some(Command {
id,
namespace: self,
})
}
pub fn get_type_by_name(&self, name: &str) -> Option<Type> {
if let Some(TypeNamespaceId::Types(id)) = self.type_namespace.borrow().get(name) {
Some(Type {
id: *id,
namespace: self,
})
} else {
None
}
}
pub fn get_trait_by_name(&self, name: &str) -> Option<Trait> {
if let Some(TypeNamespaceId::Traits(id)) = self.type_namespace.borrow().get(name) {
Some(Trait {
id: *id,
namespace: self,
})
} else {
None
}
}
pub fn get_command_by_name(&self, name: &str) -> Option<Command> {
if let Some(DataNamespaceId::Commands(id)) = self.data_namespace.borrow().get(name) {
Some(Command {
id: *id,
namespace: self,
})
} else {
None
}
}
}
#[derive(Debug)]
pub enum NsRegistrationError<'a> {
NameAlreadyExists,
GenericNameAlreadyExists,
UnregisteredGenericsUsed(Vec<&'a str>),
}

View file

@ -1,35 +0,0 @@
use std::collections::HashSet;
use std::cell::RefCell;
use std::fmt::Debug;
use std::fmt::Display;
use super::GlobalNamespace;
use super::TraitId;
use super::TypeId;
#[derive(Clone, Copy)]
pub struct Trait<'a> {
pub(super) id: TraitId,
pub(super) namespace: &'a GlobalNamespace,
}
impl Display for Trait<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = &self.namespace.traits.borrow()[self.id].name;
f.write_fmt(format_args!("{name}"))
}
}
impl Debug for Trait<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Trait({self})")
}
}
pub(super) struct InternalTrait {
// make resolution easier
pub(super) types: RefCell<HashSet<TypeId>>,
pub(super) name: String,
}

View file

@ -1,55 +0,0 @@
use std::collections::HashSet;
use std::cell::RefCell;
use std::fmt::Debug;
use std::fmt::Display;
use super::Trait;
use super::GlobalNamespace;
use super::TraitId;
use super::TypeId;
#[derive(Clone, Copy)]
pub struct Type<'a> {
pub(super) id: TypeId,
pub(super) namespace: &'a GlobalNamespace,
}
impl<'a> Type<'a> {
pub fn add_trait(&self, to_add: &'a Trait) {
let internal_self = &self.namespace.types.borrow()[self.id];
internal_self.traits.borrow_mut().insert(to_add.id);
self.namespace.traits.borrow_mut()[to_add.id]
.types
.borrow_mut()
.insert(self.id);
}
pub fn has_trait(&self, to_check: &'a Trait) -> bool {
self.namespace.types.borrow()[self.id]
.traits
.borrow()
.contains(&to_check.id)
}
}
impl Display for Type<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = &self.namespace.types.borrow()[self.id].name;
f.write_fmt(format_args!("{name}"))
}
}
impl Debug for Type<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Type({self})")
}
}
pub(super) struct InternalType {
pub(super) traits: RefCell<HashSet<TraitId>>,
pub(super) name: String,
}

View file

@ -1,184 +0,0 @@
use std::collections::HashMap;
use std::fmt::Debug;
use std::fmt::Display;
use super::TraitId;
use super::TypeId;
use super::GlobalNamespace;
use super::r#type::Type;
pub enum TypeDef<'a> {
Type(Type<'a>),
Generic(String),
List(Vec<TypeDef<'a>>),
Record(Vec<(String, TypeDef<'a>)>),
}
impl<'a> TypeDef<'a> {
pub(super) fn check_generics_exist(
&'a self,
map: &HashMap<String, Vec<TraitId>>,
) -> Result<(), Vec<&'a str>> {
match self {
TypeDef::Type(_) => Ok(()),
TypeDef::Generic(name) => {
if map.contains_key(name) {
Ok(())
} else {
Err(vec![name])
}
}
TypeDef::List(defs) => {
let r = defs
.iter()
.map(|def| def.check_generics_exist(map))
.filter_map(|check_res| {
if let Err(invalid_names) = check_res {
Some(invalid_names)
} else {
None
}
})
.reduce(|mut acc, mut errs| {
acc.append(&mut errs);
acc
});
match r {
Some(e) => Err(e),
None => Ok(()),
}
}
TypeDef::Record(rec) => {
let r = rec
.iter()
.map(|(_n, def)| def.check_generics_exist(map))
.filter_map(|check_res| {
if let Err(invalid_names) = check_res {
Some(invalid_names)
} else {
None
}
})
.reduce(|mut acc, mut errs| {
acc.append(&mut errs);
acc
});
match r {
Some(e) => Err(e),
None => Ok(()),
}
}
}
}
pub(super) fn from_internal(ns: &'a GlobalNamespace, def: &InternalTypeDef) -> TypeDef<'a> {
match def {
InternalTypeDef::Single(id) => TypeDef::Type(
ns.get_type(*id)
.expect("Unregistered internal type ID. This is a bug."),
),
InternalTypeDef::Generic(name) => TypeDef::Generic(name.clone()),
InternalTypeDef::List(list) => TypeDef::List(
list.iter()
.map(|def| Self::from_internal(ns, def))
.collect(),
),
InternalTypeDef::Record(rec) => TypeDef::Record(
rec.iter()
.map(|(name, def)| (name.clone(), Self::from_internal(ns, def)))
.collect(),
),
}
}
}
impl Display for TypeDef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeDef::Type(t) => Display::fmt(&t, f),
TypeDef::Generic(name) => Display::fmt(name, f),
TypeDef::List(l) => {
f.write_str("[ ")?;
if let Some(first) = l.first() {
Display::fmt(&first, f)?;
}
for item in l.iter().skip(1) {
f.write_str(", ")?;
Display::fmt(&item, f)?;
}
f.write_str(" ]")
}
TypeDef::Record(rec) => {
f.write_str("{ ")?;
for (i, item) in rec.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
f.write_fmt(format_args!("{}: {}", item.0, item.1))?;
}
f.write_str(" }")
}
}
}
}
impl Debug for TypeDef<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TypeDef::Type(t) => Debug::fmt(&t, f),
TypeDef::Generic(name) => Debug::fmt(name, f),
TypeDef::List(l) => {
f.write_str("[ ")?;
for (i, item) in l.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
Debug::fmt(&item, f)?;
}
f.write_str(" ]")
}
TypeDef::Record(rec) => {
f.write_str("{ ")?;
for (i, item) in rec.iter().enumerate() {
if i != 0 {
f.write_str(", ")?;
}
f.write_fmt(format_args!("{:?}: {:?}", item.0, item.1))?;
}
f.write_str(" }")
}
}
}
}
impl<'a> From<Type<'a>> for TypeDef<'a> {
fn from(value: Type<'a>) -> Self {
TypeDef::Type(value)
}
}
pub(super) enum InternalTypeDef {
Single(TypeId),
Generic(String),
List(Vec<InternalTypeDef>),
Record(Vec<(String, InternalTypeDef)>),
}
impl From<&TypeDef<'_>> for InternalTypeDef {
fn from(value: &TypeDef) -> Self {
match value {
TypeDef::Type(val) => Self::Single(val.id),
// TODO: rewrite this to be better
TypeDef::Generic(name) => Self::Generic(name.to_owned()),
TypeDef::List(list) => Self::List(list.iter().map(std::convert::Into::into).collect()),
TypeDef::Record(rec) => Self::Record(
rec.iter()
.map(|(name, typ)| (name.to_owned(), typ.into()))
.collect(),
),
}
}
}

View file

@ -1,214 +0,0 @@
#[cfg(test)]
#[allow(clippy::unwrap_used, reason = "these are tests. they may unwrap.")]
mod test;
use crate::{
error::{ErrorKind, Errors, SyntaxErrorKind},
syntax::CommandPart,
};
use super::{CommandPartKind, PipelineElement, PipelineElementKind};
pub fn check(syntax: &[PipelineElement]) -> Result<(), Vec<Errors>> {
let mut errs = Vec::new();
if let Err(e_span) = check_missing_streamer(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::MissingStreamer),
e_span,
));
}
if let Err(err_locs) = check_missing_filters(syntax) {
errs.push(Errors::new(
ErrorKind::SyntaxError(SyntaxErrorKind::MissingFilter),
err_locs,
));
}
if let Err(e_span) = check_missing_sink(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::MissingSink),
e_span,
));
}
if let Err(e_span) = check_literal_as_sink(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsSink),
e_span,
));
}
if let Err(err_locs) = check_literal_as_filter(syntax) {
errs.push(Errors::new(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralAsFilter),
err_locs,
));
}
if let Err(e_span) = check_literal_with_args(syntax) {
errs.push(Errors::new_single(
ErrorKind::SyntaxError(SyntaxErrorKind::LiteralWithArgs),
e_span,
));
}
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
fn check_missing_streamer(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
if let Some(&PipelineElement {
kind: PipelineElementKind::Pipe,
ref span,
}) = syntax.first()
{
Err(span.clone())
} else {
Ok(())
}
}
fn check_missing_filters(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
let mut missing_filter_locs = Vec::new();
for i in 0..syntax.len() {
if let (
Some(&PipelineElement {
kind: PipelineElementKind::Pipe,
ref span,
}),
Some(&PipelineElement {
kind: PipelineElementKind::Pipe,
span: ref span1,
}),
) = (syntax.get(i), syntax.get(i + 1))
{
missing_filter_locs.push(span.start..span1.end);
}
}
if missing_filter_locs.is_empty() {
Ok(())
} else {
Err(missing_filter_locs)
}
}
fn check_missing_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
if let Some(&PipelineElement {
kind: PipelineElementKind::Pipe,
ref span,
}) = syntax.last()
{
Err(span.clone())
} else {
Ok(())
}
}
fn check_literal_as_sink(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
let last_block: Option<&[PipelineElement]> = syntax
.split(|PipelineElement { kind, .. }| kind == &PipelineElementKind::Pipe)
.last();
// there HAS to be a better way to do this... this is HORRIBLE
if let Some(last_block) = last_block {
if let Some(PipelineElement {
kind: PipelineElementKind::Command(parts),
span,
}) = last_block.first()
{
if let Some(first_part) = parts.first() {
if matches!(
first_part,
CommandPart {
kind: CommandPartKind::Word(_),
..
}
) {
Ok(())
} else {
Err(span.clone())
}
} else {
Ok(())
}
} else {
Ok(())
}
} else {
Ok(())
}
}
fn check_literal_as_filter(syntax: &[PipelineElement]) -> Result<(), Vec<logos::Span>> {
let errs = syntax
.iter()
.take(syntax.len() - 1)
.skip(1)
.filter(|element| {
!matches!(
element,
PipelineElement {
kind: PipelineElementKind::Pipe,
..
}
)
})
.map(|item| {
let PipelineElement {
kind: PipelineElementKind::Command(c),
span,
} = item
else {
return None;
};
let Some(CommandPart { kind, .. }) = c.first() else {
return None;
};
if matches!(kind, CommandPartKind::Word(_)) {
None
} else {
Some(span)
}
})
.filter_map(|err| err.map(Clone::clone))
.collect::<Vec<logos::Span>>();
if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
}
fn check_literal_with_args(syntax: &[PipelineElement]) -> Result<(), logos::Span> {
if let Some(PipelineElement {
kind: PipelineElementKind::Command(c),
span,
}) = syntax.first()
{
if c.len() > 1
&& !matches!(
c.first(),
Some(CommandPart {
kind: CommandPartKind::Word(_),
..
})
)
{
Err(span.clone())
} else {
Ok(())
}
} else {
Ok(())
}
}

View file

@ -1,66 +0,0 @@
use crate::syntax::{
check::{
check_literal_as_filter, check_literal_as_sink, check_literal_with_args,
check_missing_filters, check_missing_sink, check_missing_streamer,
},
parse_syntax,
};
#[test]
fn test_check_missing_streamer() {
let test_data = "| invert | save \"./image_processed.jpg\"";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_missing_streamer(&syntax), Err(0..1));
}
#[test]
fn test_check_missing_filters() {
let test_data = "meow | | test | awa | | nya";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_missing_filters(&syntax), Err(vec![5..8, 20..25]));
}
#[test]
fn test_check_missing_sink() {
let test_data = "meow | invert | ";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_missing_sink(&syntax), Err(14..15));
}
#[test]
fn test_check_literal_as_sink() {
let test_data = "meow | test | 3";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_literal_as_sink(&syntax), Err(14..15));
}
#[test]
fn test_check_literal_as_filter() {
let test_data = "meow | \"gay\" | 42 | 3.14 | uwu";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(
check_literal_as_filter(&syntax),
Err(vec![7..12, 15..17, 20..24])
);
}
#[test]
fn test_check_literal_as_filter_positive_on_sink() {
let test_data = "meow | 42";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_literal_as_filter(&syntax), Ok(()));
}
#[test]
fn test_check_literal_with_args() {
let test_data = "14 12 | sink";
let syntax = parse_syntax(test_data).unwrap();
assert_eq!(check_literal_with_args(&syntax), Err(0..5));
}

View file

@ -1,110 +0,0 @@
use logos::Logos;
use logos::Span;
use crate::lexer::Token;
pub mod check;
#[derive(Debug, Clone, PartialEq)]
pub struct PipelineElement {
pub kind: PipelineElementKind,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum PipelineElementKind {
Pipe,
Command(Vec<CommandPart>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct CommandPart {
pub kind: CommandPartKind,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CommandPartKind {
Word(String),
Integer(i64),
Float(f64),
String(String),
}
pub fn parse_syntax(input: &str) -> Result<Vec<PipelineElement>, Vec<logos::Span>> {
let lexer = Token::lexer(input);
let mut errs = Vec::new();
let mut r = Vec::new();
let mut partial_command: Vec<CommandPart> = Vec::new();
for (tok, span) in lexer.spanned() {
if let Ok(tok) = tok {
match tok {
Token::Pipe => {
if !partial_command.is_empty() {
#[allow(
clippy::unwrap_used,
reason = "this branch can only run if partial_command isn't empty"
)]
let span = partial_command.first().unwrap().span.start
..partial_command.last().unwrap().span.end;
r.push(PipelineElement {
kind: PipelineElementKind::Command(std::mem::take(
&mut partial_command,
)),
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 {
errs.push(span);
}
}
if !partial_command.is_empty() {
#[allow(
clippy::unwrap_used,
reason = "this branch can only run if partial_command isn't empty"
)]
let span =
partial_command.first().unwrap().span.start..partial_command.last().unwrap().span.end;
r.push(PipelineElement {
kind: PipelineElementKind::Command(std::mem::take(&mut partial_command)),
span,
});
}
if errs.is_empty() {
Ok(r)
} else {
Err(errs)
}
}
#[test]
fn test_invalid_toks() {
let test_data = "meow | gay $ error!";
assert_eq!(parse_syntax(test_data), Err(vec![11..12, 18..19]));
}

View file

@ -1,234 +0,0 @@
use core::panic;
use crate::{
builtins::{TYPE_FLOAT, TYPE_INTEGER, TYPE_STRING},
error::Errors,
namespace::{command::Command, r#type::Type, typedef::TypeDef, GlobalNamespace},
syntax::{CommandPart, CommandPartKind, PipelineElement, PipelineElementKind},
Span,
};
#[allow(dead_code, reason = "will be used later")]
#[derive(Debug)]
pub struct Expr<'a> {
kind: ExprKind<'a>,
span: Span,
}
impl<'a> Expr<'a> {
fn try_find_concrete(&'a self, ns: &'a GlobalNamespace) -> IoTypes<'a> {
let Self { ref kind, .. } = self;
kind.try_find_concrete(ns)
}
fn get_input_typedef(&'a self) -> Option<TypeDef<'a>> {
match &self.kind {
ExprKind::Command(c) => c.command.get_input_types(),
ExprKind::Literal(_) => None,
}
}
}
#[derive(Debug)]
pub enum ExprKind<'a> {
Command(CommandExpr<'a>),
Literal(LiteralKind),
}
impl<'a> ExprKind<'a> {
fn try_find_concrete(&'a self, ns: &'a GlobalNamespace) -> IoTypes<'a> {
match self {
ExprKind::Literal(lit) => IoTypes::Concrete(lit.get_type(ns)),
ExprKind::Command(c) => c.try_find_concrete(ns),
}
}
}
#[derive(Debug)]
pub struct CommandExpr<'a> {
command: Command<'a>,
args: Vec<Expr<'a>>,
}
impl<'a> CommandExpr<'a> {
fn try_find_concrete(&'a self, _ns: &'a GlobalNamespace) -> IoTypes<'a> {
let Self { command, .. } = self;
match command.get_output_types() {
None => IoTypes::Empty,
Some(def) => {
if let Some(concrete) = ConcreteTypeDef::try_from_typedef(&def) {
IoTypes::Concrete(concrete)
} else {
// TODO: make it possible to get clear output type
IoTypes::Unclear
}
}
}
}
}
#[derive(Debug)]
pub enum LiteralKind {
Int(i64),
Float(f64),
String(String),
}
enum IoTypes<'a> {
/// expression does not expect/return anything
Empty,
/// expression does expect/return something, but some types are unclear
Unclear,
/// we know for sure what types the expression expects/returns
Concrete(ConcreteTypeDef<'a>),
}
impl LiteralKind {
#[allow(
clippy::unwrap_used,
reason = "these are fetched by type name constants used for keeping names consistent in codebase, which cannot be None"
)]
fn get_type<'a>(&self, ns: &'a GlobalNamespace) -> ConcreteTypeDef<'a> {
ConcreteTypeDef::Single(match self {
LiteralKind::Int(_) => ns.get_type_by_name(TYPE_INTEGER).unwrap(),
LiteralKind::Float(_) => ns.get_type_by_name(TYPE_FLOAT).unwrap(),
LiteralKind::String(_) => ns.get_type_by_name(TYPE_STRING).unwrap(),
})
}
}
pub fn into_typed_repr(
ns: &GlobalNamespace,
syntax: Vec<PipelineElement>,
) -> Result<Vec<Expr>, Errors> {
let mut res = Vec::new();
let mut errs = Vec::new();
for item in syntax {
let PipelineElement { kind, span } = item;
match kind {
PipelineElementKind::Command(c) => {
if c.len() == 1 {
let CommandPart { kind, .. } = &c[0];
res.push(Expr {
kind: match kind {
CommandPartKind::Word(val) => ExprKind::Command(CommandExpr {
command: {
let Some(c) = ns.get_command_by_name(val) else {
errs.push(span);
continue;
};
c
},
args: Vec::new(),
}),
CommandPartKind::Integer(val) => {
ExprKind::Literal(LiteralKind::Int(*val))
}
CommandPartKind::Float(val) => {
ExprKind::Literal(LiteralKind::Float(*val))
}
CommandPartKind::String(val) => {
ExprKind::Literal(LiteralKind::String(val.clone()))
}
},
span,
});
} else {
let Some(CommandPart {
kind: CommandPartKind::Word(name),
span,
}) = c.first()
else {
panic!("tried conversion to typed representation with invalid syntax")
};
res.push(Expr {
kind: ExprKind::Command(CommandExpr {
command: {
let Some(c) = ns.get_command_by_name(name) else {
errs.push(span.clone());
continue;
};
c
},
args: c
.iter()
.skip(1)
.map(|CommandPart { kind, span }| Expr {
kind: ExprKind::Literal(match kind {
CommandPartKind::String(val)
| CommandPartKind::Word(val) => {
LiteralKind::String(val.to_string())
}
CommandPartKind::Integer(val) => LiteralKind::Int(*val),
CommandPartKind::Float(val) => LiteralKind::Float(*val),
}),
span: span.clone(),
})
.collect(),
}),
span: span.clone(),
});
}
}
PipelineElementKind::Pipe => {}
}
}
if errs.is_empty() {
Ok(res)
} else {
Err(Errors::new(crate::error::ErrorKind::CommandNotFound, errs))
}
}
enum ConcreteTypeDef<'a> {
Single(Type<'a>),
List(Vec<ConcreteTypeDef<'a>>),
Record(Vec<(String, ConcreteTypeDef<'a>)>),
}
impl<'a> ConcreteTypeDef<'a> {
fn try_from_typedef(val: &TypeDef<'a>) -> Option<Self> {
match val {
TypeDef::Type(typ) => Some(Self::Single(*typ)),
TypeDef::Generic(_) => todo!(),
TypeDef::List(list) => {
let out: Vec<ConcreteTypeDef<'a>> =
list.iter().filter_map(Self::try_from_typedef).collect();
(out.len() == list.len()).then_some(ConcreteTypeDef::List(out))
}
TypeDef::Record(rec) => {
let out: Vec<(String, ConcreteTypeDef<'a>)> = rec
.iter()
.filter_map(|(name, def)| {
Self::try_from_typedef(def).map(|def| (name.clone(), def))
})
.collect();
(out.len() == rec.len()).then_some(ConcreteTypeDef::Record(out))
}
}
}
}
struct TypeChecker<'a> {
global_ns: &'a GlobalNamespace,
typed_syntax: Vec<Expr<'a>>,
}
impl<'a> TypeChecker<'a> {
fn new(ns: &'a GlobalNamespace, syntax: Vec<Expr<'a>>) -> TypeChecker<'a> {
Self {
global_ns: ns,
typed_syntax: syntax,
}
}
// not sure if this is the optimal alg, or even a working one, but lets see
fn check_forward() {}
}