From b6b8c5085a6bd37bbf933acb30c560f8ef9638d0 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Thu, 16 Nov 2023 19:22:26 +0100 Subject: [PATCH] implement basic namespacing for types, traits and commands --- src/builtins/mod.rs | 14 +++ src/builtins/namespace.rs | 224 ++++++++++++++++++++++++++++++++++++++ src/main.rs | 18 +++ src/syntax/check.rs | 20 +++- src/syntax/error.rs | 17 ++- 5 files changed, 287 insertions(+), 6 deletions(-) create mode 100644 src/builtins/mod.rs create mode 100644 src/builtins/namespace.rs diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs new file mode 100644 index 0000000..0402004 --- /dev/null +++ b/src/builtins/mod.rs @@ -0,0 +1,14 @@ +use self::namespace::{GlobalNamespace, TypeDef}; + +pub mod namespace; + +pub fn initialise_globals() -> GlobalNamespace { + let mut ns = GlobalNamespace::init(); + + let numeric = ns.register_trait("Numeric").unwrap(); + + ns.register_type("integer").unwrap().add_trait(&numeric); + ns.register_type("float").unwrap().add_trait(&numeric); + + ns +} diff --git a/src/builtins/namespace.rs b/src/builtins/namespace.rs new file mode 100644 index 0000000..1249cf9 --- /dev/null +++ b/src/builtins/namespace.rs @@ -0,0 +1,224 @@ +use std::{ + cell::RefCell, + collections::{HashMap, HashSet}, + rc::Rc, +}; + +pub struct GlobalNamespace { + // types + // constants + // groups (maybe global data namespace that has groups as subset?) + types: RefCell>, + traits: RefCell>, + type_namespace: RefCell>, + commands: RefCell>, + data_namespace: RefCell>, +} + +enum TypeNamespaceId { + Types(usize), + Traits(usize), +} + +enum DataNamespaceId { + Commands(usize), + Globals(usize), +} + +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()), + } + } + pub fn register_type(&self, name: &str) -> Result { + if self.type_namespace.borrow().contains_key(name) { + Err(TypeRegistrationError::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, + }) + } + } + + pub fn register_trait(&self, name: &str) -> Result { + if self.type_namespace.borrow().contains_key(name) { + Err(TypeRegistrationError::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, + }) + } + } + + pub fn register_command( + &self, + name: &str, + input: Option, + output: Option, + ) -> Result { + if self.data_namespace.borrow().contains_key(name) { + Err(TypeRegistrationError::NameAlreadyExists) + } else { + self.commands.borrow_mut().push(InternalCommand { + name: name.to_owned(), + input: input.map(|def| def.into()), + output: output.map(|def| def.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_by_name(&self, name: &str) -> Option { + 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 { + if let Some(TypeNamespaceId::Traits(id)) = self.type_namespace.borrow().get(name) { + Some(Trait { + id: *id, + namespace: self, + }) + } else { + None + } + } +} + +#[derive(Debug)] +pub enum TypeRegistrationError { + NameAlreadyExists, +} + +pub struct Type<'a> { + id: usize, + 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) + } +} + +struct InternalType { + traits: RefCell>, + name: String, +} + +pub struct Trait<'a> { + id: usize, + namespace: &'a GlobalNamespace, +} + +struct InternalTrait { + // make resolution easier + types: RefCell>, + name: String, +} + +struct Command<'a> { + id: usize, + namespace: &'a GlobalNamespace, +} + +struct InternalCommand { + name: String, + // gosh this is hacky + input: Option, + output: Option, +} + +/// the thing used for type annotations etc +/// stores type ids +pub enum TypeDef<'a> { + /// T + Type(Type<'a>), + Trait(Trait<'a>), + /// [ T ] + List(Vec>), + /// { name: T } or in group declarations [ name: T ] + Record(Vec<(String, TypeDef<'a>)>), +} + +enum InternalTypeDef { + Single(TypeNamespaceId), + List(Vec), + Record(Vec<(String, InternalTypeDef)>), +} + +impl From> for InternalTypeDef { + fn from(value: TypeDef) -> Self { + match value { + TypeDef::Type(val) => Self::Single(TypeNamespaceId::Types(val.id)), + TypeDef::Trait(val) => Self::Single(TypeNamespaceId::Traits(val.id)), + TypeDef::List(list) => Self::List(list.into_iter().map(|def| def.into()).collect()), + TypeDef::Record(rec) => Self::Record( + rec.into_iter() + .map(|(name, typ)| (name, typ.into())) + .collect(), + ), + } + } +} + +// what is a type apart from the obvious? +// - it has Traits (integer might have the trait numeric for example)-> also need trait system, should share a namespace tho? + +// spec for add +// +// inputs: [ numeric numeric ] +// outputs: numeric +// diff --git a/src/main.rs b/src/main.rs index 39ff14e..7955b8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use builtins::initialise_globals; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::ColorChoice; @@ -6,6 +7,8 @@ use syntax::parse_syntax; use crate::syntax::check::check; +mod builtins; +mod hir; mod lexer; mod syntax; @@ -13,6 +16,11 @@ fn main() { let mut files = SimpleFiles::new(); let mut out_errs = Vec::new(); + let global_ns = initialise_globals(); + let int = global_ns.get_type_by_name("integer").unwrap(); + let numeric = global_ns.get_trait_by_name("Numeric").unwrap(); + assert!(int.has_trait(&numeric)); + let invalid_toks = "meow | gay $ error!\\"; let invalid_toks_id = files.add("invalid_toks", invalid_toks); if let Err(err) = parse_syntax(invalid_toks, invalid_toks_id) { @@ -39,6 +47,16 @@ fn main() { out_errs.append(&mut errs) } + let invalid_no_sink = "meow | invert | "; + let invalid_no_sink_id = files.add("invalid_no_sink", invalid_no_sink); + if let Err(mut errs) = check( + parse_syntax(invalid_no_sink, invalid_no_sink_id).unwrap(), + invalid_no_sink, + invalid_no_sink_id, + ) { + out_errs.append(&mut errs) + } + // invalid let writer = StandardStream::stderr(ColorChoice::Always); let config = term::Config::default(); diff --git a/src/syntax/check.rs b/src/syntax/check.rs index 48c1a10..953b75e 100644 --- a/src/syntax/check.rs +++ b/src/syntax/check.rs @@ -20,6 +20,10 @@ pub fn check( )) } + if let Err(e_span) = check_missing_sink(&syntax) { + errs.push(SyntaxError::MissingSink(vec![(file_id, e_span)])); + } + if errs.is_empty() { Ok(syntax) } else { @@ -44,11 +48,11 @@ fn check_missing_filters(syntax: &Vec) -> Result<(), Vec) -> Result<(), Vec) -> Result<(), logos::Span> { + if let Some(&PipelineElement { + kind: PipelineElementKind::Pipe, + ref span, + }) = syntax.last() + { + Err(span.clone()) + } else { + Ok(()) + } +} diff --git a/src/syntax/error.rs b/src/syntax/error.rs index d507d0a..19f39fc 100644 --- a/src/syntax/error.rs +++ b/src/syntax/error.rs @@ -13,11 +13,11 @@ pub enum SyntaxError { MissingSink(Vec<(FileId, logos::Span)>), /// This indicates a missing filter somewhere in the pipeline, meaning that there's 2 pipes after one another MissingFilter(Vec<(FileId, logos::Span)>), - /// A literal cannot be a sink + /// A literal cannot be a sink, TODO LiteralAsSink, - /// A literal can't be a filter either + /// A literal can't be a filter either, TODO LiteralAsFilter, - /// A literal acting as streamer cannot take arguments + /// A literal acting as streamer cannot take arguments, TODO LiteralWithArgs, } @@ -51,7 +51,16 @@ impl SyntaxError { }) .collect(), ), - _ => todo!(), + Self::MissingSink(locs) => Diagnostic::error() + .with_message("pipelines cannot end on a pipe") + .with_labels( + locs.into_iter() + .map(|(file_id, span)| { + Label::primary(*file_id, span.clone()).with_message("no sink") + }) + .collect(), + ), + _ => unimplemented!(), } } }