diff --git a/src/namespace/command.rs b/src/namespace/command.rs index 40186d9..3402dc3 100644 --- a/src/namespace/command.rs +++ b/src/namespace/command.rs @@ -18,6 +18,18 @@ impl<'a> Command<'a> { .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(|inputs| match inputs { + InternalTypeDef::Single(_) => 1, + InternalTypeDef::List(list) => list.len(), + InternalTypeDef::Record(rec) => rec.len(), + }) + .unwrap_or(0) + } pub fn get_output_types(&self) -> Option { self.namespace.commands.borrow()[self.id] .output diff --git a/src/namespace/typedef.rs b/src/namespace/typedef.rs index 191e447..ca4488a 100644 --- a/src/namespace/typedef.rs +++ b/src/namespace/typedef.rs @@ -101,6 +101,18 @@ impl Debug for TypeDef<'_> { } } +impl<'a> From> for TypeDef<'a> { + fn from(value: Type<'a>) -> Self { + TypeDef::Type(value) + } +} + +impl<'a> From> for TypeDef<'a> { + fn from(value: Trait<'a>) -> Self { + TypeDef::Trait(value) + } +} + pub(super) enum InternalTypeDef { Single(TypeNamespaceId), List(Vec), diff --git a/src/typed.rs b/src/typed.rs index 764916e..6bf77bb 100644 --- a/src/typed.rs +++ b/src/typed.rs @@ -1,9 +1,15 @@ use core::panic; +use std::process::CommandEnvs; use crate::{ builtins::{TYPE_FLOAT, TYPE_INTEGER, TYPE_STRING}, error::Errors, - namespace::{command::Command, r#type::Type, GlobalNamespace}, + namespace::{ + command::{self, Command}, + r#type::Type, + typedef::TypeDef, + GlobalNamespace, + }, syntax::{CommandPart, CommandPartKind, PipelineElement, PipelineElementKind}, Span, }; @@ -15,15 +21,59 @@ pub struct Expr<'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> { + match &self.kind { + ExprKind::Command(c) => c.command.get_input_types(), + ExprKind::Literal(_) => None, + } + } +} + #[derive(Debug)] pub enum ExprKind<'a> { - Command { - command: Command<'a>, - args: Vec>, - }, + 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)] +struct CommandExpr<'a> { + command: Command<'a>, + args: Vec>, +} + +impl<'a> CommandExpr<'a> { + fn try_find_concrete(&'a self, ns: &'a GlobalNamespace) -> IoTypes<'a> { + let Self { command, args } = 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), @@ -31,17 +81,26 @@ pub enum LiteralKind { 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" )] - pub fn get_type<'a>(&self, ns: &'a GlobalNamespace) -> Type<'a> { - match self { + pub 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(), - } + }) } } @@ -61,7 +120,7 @@ pub fn into_typed_repr( res.push(Expr { kind: match kind { - CommandPartKind::Word(val) => ExprKind::Command { + CommandPartKind::Word(val) => ExprKind::Command(CommandExpr { command: { let Some(c) = ns.get_command_by_name(val) else { errs.push(span); @@ -70,7 +129,7 @@ pub fn into_typed_repr( c }, args: Vec::new(), - }, + }), CommandPartKind::Integer(val) => { ExprKind::Literal(LiteralKind::Int(*val)) } @@ -93,7 +152,7 @@ pub fn into_typed_repr( }; res.push(Expr { - kind: ExprKind::Command { + kind: ExprKind::Command(CommandExpr { command: { let Some(c) = ns.get_command_by_name(name) else { errs.push(span.clone()); @@ -116,7 +175,7 @@ pub fn into_typed_repr( span: span.clone(), }) .collect(), - }, + }), span: span.clone(), }); } @@ -131,3 +190,60 @@ pub fn into_typed_repr( Err(Errors::new(crate::error::ErrorKind::CommandNotFound, errs)) } } + +enum ConcreteTypeDef<'a> { + Single(Type<'a>), + List(Vec>), + Record(Vec<(String, ConcreteTypeDef<'a>)>), +} + +impl<'a> ConcreteTypeDef<'a> { + fn try_from_typedef(val: &TypeDef<'a>) -> Option { + match val { + TypeDef::Type(typ) => Some(Self::Single(*typ)), + TypeDef::Trait(_) => None, + TypeDef::List(list) => { + let out: Vec> = + 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>, +} + +impl<'a> TypeChecker<'a> { + fn new(ns: &'a GlobalNamespace, syntax: Vec>) -> 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(&self) { + let i = 0; + + // loop { + // let maybe_concrete = match self.typed_syntax[i].kind { + // ExprKind::Literal(l) => todo!(), + // ExprKind::Command(c) => todo!(), + // } + // } + } +}