iowo/src/typed.rs

243 lines
7.9 KiB
Rust

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, 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<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::Trait(_) => None,
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(&self) {
let i = 0;
// loop {
// let maybe_concrete = match self.typed_syntax[i].kind {
// ExprKind::Literal(l) => todo!(),
// ExprKind::Command(c) => todo!(),
// }
// }
}
}