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> { 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>, } 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), 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" )] 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(), }) } } pub fn into_typed_repr( ns: &GlobalNamespace, syntax: Vec, ) -> Result, 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>), 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!(), // } // } } }