diff --git a/crates/kind-fmt/src/expr.rs b/crates/kind-fmt/src/expr.rs new file mode 100644 index 00000000..f591e5d4 --- /dev/null +++ b/crates/kind-fmt/src/expr.rs @@ -0,0 +1,15 @@ +use kind_syntax::concrete::Expr; +use crate::{Result, FmtContext}; + +impl<'a> FmtContext<'a> { + pub fn expr(&mut self) -> Result { + match self.kind() { + "call" => { + let callee = self.property("callee").unwrap(); + + self.cursor(callee).primary() + }, + kind => todo!("{}", kind), + } + } +} \ No newline at end of file diff --git a/crates/kind-fmt/src/lib.rs b/crates/kind-fmt/src/lib.rs index 11b92116..95732f3f 100644 --- a/crates/kind-fmt/src/lib.rs +++ b/crates/kind-fmt/src/lib.rs @@ -1,19 +1,33 @@ -use std::path::PathBuf; +use thin_vec::ThinVec; +use tree_sitter::{Node, Parser, Tree, TreeCursor}; -use thin_vec::{thin_vec, ThinVec}; -use tree_sitter::{Parser, Tree, TreeCursor}; +use kind_syntax::concrete::Module; +use kind_syntax::lexemes::Token; -use kind_syntax::concrete::{Block, ConstructorExpr, Expr, ExprKind, LocalExpr, Module, Pat, PatKind, Rule, Signature, Stmt, TopLevel, TopLevelKind}; -use kind_syntax::lexemes::{Brace, Colon, Equal, Ident, Item, Name, Span, Token, Tokenized}; +mod expr; +mod name; +mod parameter; +mod pattern; +mod primary; +mod statements; +mod top_level; #[derive(Debug)] pub enum FmtError { IoError(std::io::Error), + Utf8Error(std::str::Utf8Error), TreeSitterLanguageError(tree_sitter::LanguageError), TreeSitterQueryError(tree_sitter::QueryError), UnknownParseError, } +#[derive(Clone, Copy)] +pub struct FmtContext<'a> { + pub file: &'a str, + pub tree: &'a Tree, + pub cursor: *mut TreeCursor<'a>, +} + pub type Result = std::result::Result; pub fn run_fmt(string: String) -> Result { @@ -21,7 +35,7 @@ pub fn run_fmt(string: String) -> Result { parser .set_language(tree_sitter_kind::language()) .map_err(FmtError::TreeSitterLanguageError)?; - let tree = parser + let mut tree = parser .parse(string.as_bytes(), None) .ok_or(FmtError::UnknownParseError)?; @@ -29,10 +43,16 @@ pub fn run_fmt(string: String) -> Result { let mut cursor = tree.root_node().walk(); - let declarations = tree.root_node().children(&mut cursor) - .map(|node| { - specialize_top_level(&string, &tree, &mut node.walk()) - }) + let context = FmtContext { + file: &string, + tree: &tree, + cursor: &mut cursor, + }; + + let declarations = tree + .root_node() + .children(&mut cursor) + .map(|node| context.cursor(node).top_level()) .collect::>>()?; Ok(Module { @@ -42,132 +62,91 @@ pub fn run_fmt(string: String) -> Result { }) } -fn specialize_top_level(file: &String, tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "val_declaration" => { - let name = node.child_by_field_name("name").unwrap(); - let return_type = node.child_by_field_name("return_type"); - let value = node.child_by_field_name("value"); +impl<'a> FmtContext<'a> { + pub fn node(&self) -> Node<'a> { + unsafe { (*self.cursor).node() } + } - Ok(TopLevel { - data: Item::new( - Span::default(), - TopLevelKind::Signature(Signature { - name: specialize_name(file, tree, &mut name.walk())?, - arguments: thin_vec![], - return_type: return_type.map_or(Ok(None), |node| { - let expr = specialize_expr(file, tree, &mut node.walk())?; - Ok(Some(Colon(Token::default(), expr))) - })?, - value: value.map_or(Ok(None), |node| { - let block = specialize_statements(file, tree, &mut node.walk())?; - Ok(Some(block)) - })?, - }), - ), - attributes: thin_vec![], + pub fn get_current_cursor(&self) -> TreeCursor<'a> { + unsafe { (*self.cursor).clone() } + } + + pub fn kind(&self) -> &'static str { + self.node().kind() + } + + pub fn first(&self) -> Option> { + self.node().child(0) + } + + pub fn at(&self, at: usize) -> Option> { + self.node().child(at) + } + + pub fn named_at(&self, at: usize) -> Option> { + self.node().named_child(at) + } + + pub fn property(&self, name: &str) -> Option> { + self.node().child_by_field_name(name) + } + + pub fn properties(&self, name: &'static str) -> impl Iterator> { + unsafe { + self.node().children_by_field_name(name, self.cursor.as_mut().unwrap()) + } + } + + pub fn find(&self, name: &str, mut f: F) -> Result> + where + F: FnMut(Node) -> Result, + { + self + .node() + .child_by_field_name(name) + .map_or(Ok(None), |node| { + let value = f(node)?; + Ok(Some(value)) }) - } - _ => todo!(), } -} -fn specialize_pattern(file: &String, tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "identifier" => { - specialize_name(file, tree, cursor) - .map(|name| Pat::new( - Span::default(), - PatKind::Name(name), - )) + pub fn named_children(&self) -> impl Iterator> + '_ { + unsafe { + self.node().named_children(self.cursor.as_mut().unwrap()) } - "constructor_identifier" => { - specialize_name(file, tree, cursor) - .map(|name| Pat::new( - Span::default(), - PatKind::Name(name), - )) - } - kind => todo!("{}", kind), } -} -fn specialize_expr(file: &String, tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "call" => { - let callee = node - .child_by_field_name("callee") - .unwrap(); + pub fn children(&self) -> impl Iterator> + '_ { + let mut cursor = self.get_current_cursor(); - specialize_primary(file, tree, &mut callee.walk()) - }, - kind => todo!("{}", kind), + cursor.reset(self.node()); + cursor.goto_first_child(); + (0..self.node().child_count()).map(move |_| { + let result = cursor.node(); + cursor.goto_next_sibling(); + result + }) } -} -fn specialize_name(file: &String, _tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "identifier" => { - let name = node.utf8_text(file.as_bytes()).unwrap().to_string(); - - Ok(Name::Ident(Ident(Item::new( - Span::default(), - Tokenized(Token::default(), name), - )))) - } - kind => todo!("{}", kind), + pub fn text(&self) -> Result<&'a str> { + self.node() + .utf8_text(self.file.as_bytes()) + .map_err(FmtError::Utf8Error) } -} -fn specialize_statements(file: &String, tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "statements" => { - let statements = node.named_children(cursor) - .map(|node| { - specialize_expr(file, tree, &mut node.walk()) - }) - .collect::>>()?; - - Ok(Brace( - Token::default(), - statements.iter().map(|expr| Stmt { - value: expr.clone(), - semi: None, - }).collect(), - Token::default(), - )) - } - kind => todo!("{}", kind), + pub fn text_of(&self, node: Node<'_>) -> Result<&'a str> { + node.utf8_text(self.file.as_bytes()) + .map_err(FmtError::Utf8Error) } -} -fn specialize_primary(file: &String, tree: &Tree, cursor: &mut TreeCursor) -> Result { - let node = cursor.node(); - match node.kind() { - "constructor_identifier" => { - specialize_name(file, tree, cursor) - .map(|name| Expr::new( - Span::default(), - ExprKind::Constructor(Box::new(ConstructorExpr { - name, - })), - )) + pub fn cursor<'b>(&'b self, node: Node<'b>) -> FmtContext<'b> { + // fix this leak + let cursor = Box::leak(Box::new(node.walk())); + FmtContext { + file: self.file, + tree: self.tree, + cursor, } - "identifier" => { - specialize_name(file, tree, cursor) - .map(|name| Expr::new( - Span::default(), - ExprKind::Local(Box::new(LocalExpr { - name, - })), - )) - } - _ => todo!(), } } @@ -177,7 +156,7 @@ mod tests { #[test] fn it_works() { - let expr = run_fmt("bao:pao{a}".into()).unwrap(); + let expr = run_fmt("Ata (name: U60) : Type { Something }".into()).unwrap(); println!("{:#?}", expr); } } diff --git a/crates/kind-fmt/src/name.rs b/crates/kind-fmt/src/name.rs new file mode 100644 index 00000000..ec86d10e --- /dev/null +++ b/crates/kind-fmt/src/name.rs @@ -0,0 +1,26 @@ +use kind_syntax::lexemes::{Ident, Item, Name, QualifiedIdent, Span, Token, Tokenized}; +use crate::{Result, FmtContext}; + +impl<'a> FmtContext<'a> { + pub fn name(&mut self) -> Result { + match self.kind() { + "identifier" => { + let name = self.text()?.to_string(); + + Ok(Name::Ident(Ident(Item::new( + Span::default(), + Tokenized(Token::default(), name), + )))) + } + "constructor_identifier" => { + let name = self.text()?.to_string(); + + Ok(Name::QualifiedIdent(QualifiedIdent(Item::new( + Span::default(), + Tokenized(Token::default(), name), + )))) + } + kind => todo!("{}", kind), + } + } +} \ No newline at end of file diff --git a/crates/kind-fmt/src/parameter.rs b/crates/kind-fmt/src/parameter.rs new file mode 100644 index 00000000..f8632adf --- /dev/null +++ b/crates/kind-fmt/src/parameter.rs @@ -0,0 +1,48 @@ +use kind_syntax::concrete::{ParameterBinding, SignatureParameter, TypeBinding}; +use kind_syntax::lexemes::{AngleBracket, Colon, Paren, Token}; + +use crate::{FmtContext, Result}; + +impl<'a> FmtContext<'a> { + pub fn parameter(&mut self) -> Result { + match self.kind() { + "parameter" => { + let modifier = self.find("modifier", |node| self.text_of(node))?; + let signature = match modifier { + Some(x) if x == "+" => SignatureParameter::Include, + Some(x) if x == "-" => SignatureParameter::Exclude, + Some(x) if x == "-+" => SignatureParameter::Both, + Some(x) if x == "+-" => SignatureParameter::Both, + _ => SignatureParameter::Include, + }; + + let parameter = self.cursor(self.first().unwrap()); + + let name = parameter + .find("name", |node| parameter.cursor(node).name())? + .unwrap(); + let binding_type = parameter.clone().find("type", |node| { + let expr = parameter.cursor(node).expr()?; + Ok(Colon(Token::default(), Box::new(expr))) + })?; + + match parameter.kind() { + "explicit_parameter" => Ok(signature(ParameterBinding::Explicit(Paren( + Token::default(), + TypeBinding { name, binding_type }, + Token::default(), + )))), + "implicit_parameter" => { + Ok(signature(ParameterBinding::Implicit(AngleBracket( + Token::default(), + TypeBinding { name, binding_type }, + Token::default(), + )))) + } + kind => todo!("{}", kind), + } + } + kind => todo!("{}", kind), + } + } +} diff --git a/crates/kind-fmt/src/pattern.rs b/crates/kind-fmt/src/pattern.rs new file mode 100644 index 00000000..7b5c3392 --- /dev/null +++ b/crates/kind-fmt/src/pattern.rs @@ -0,0 +1,15 @@ +use kind_syntax::concrete::{Pat, PatKind}; +use kind_syntax::lexemes::Span; +use crate::{Result, FmtContext}; + +impl<'a> FmtContext<'a> { + pub fn pattern(&mut self) -> Result { + match self.kind() { + "identifier" | "constructor_identifier" => { + let name = self.name()?; + Ok(Pat::new(Span::default(), PatKind::Name(name))) + } + kind => todo!("{}", kind), + } + } +} diff --git a/crates/kind-fmt/src/primary.rs b/crates/kind-fmt/src/primary.rs new file mode 100644 index 00000000..51633601 --- /dev/null +++ b/crates/kind-fmt/src/primary.rs @@ -0,0 +1,19 @@ +use kind_syntax::concrete::Expr; +use kind_syntax::lexemes::Span; +use crate::{FmtContext, Result}; + +impl<'a> FmtContext<'a> { + pub fn primary(&mut self) -> Result { + match self.kind() { + "constructor_identifier" => { + let name = self.name()?; + Ok(Expr::constructor(Span::default(), name)) + } + "identifier" => { + let name = self.name()?; + Ok(Expr::local(Span::default(), name)) + } + kind => todo!("{}", kind), + } + } +} \ No newline at end of file diff --git a/crates/kind-fmt/src/statements.rs b/crates/kind-fmt/src/statements.rs new file mode 100644 index 00000000..c292e01e --- /dev/null +++ b/crates/kind-fmt/src/statements.rs @@ -0,0 +1,32 @@ +use thin_vec::ThinVec; + +use kind_syntax::concrete::{Block, Stmt}; +use kind_syntax::lexemes::{Brace, Token}; + +use crate::{FmtContext, Result}; + +impl<'a> FmtContext<'a> { + pub fn statements(&mut self) -> Result { + match self.kind() { + "statements" => { + let statements = self + .named_children() + .map(|node| self.cursor(node).expr()) + .collect::>>()?; + + Ok(Brace( + Token::default(), + statements + .iter() + .map(|expr| Stmt { + value: expr.clone(), + semi: None, + }) + .collect(), + Token::default(), + )) + } + kind => todo!("{}", kind), + } + } +} diff --git a/crates/kind-fmt/src/top_level.rs b/crates/kind-fmt/src/top_level.rs new file mode 100644 index 00000000..810fa510 --- /dev/null +++ b/crates/kind-fmt/src/top_level.rs @@ -0,0 +1,37 @@ +use thin_vec::thin_vec; +use kind_syntax::concrete::{Signature, TopLevel, TopLevelKind}; +use kind_syntax::lexemes::{Colon, Item, Span, Token}; +use crate::{FmtContext, Result}; + +impl<'a> FmtContext<'a> { + pub fn top_level(&mut self) -> Result { + match self.kind() { + "val_declaration" => { + let name = self.find("name", |node| self.cursor(node).name())?.unwrap(); + let value = self.find("value", |node| self.cursor(node).statements())?; + let parameters = self + .properties("parameters") + .map(|node| self.cursor(node).parameter()) + .collect::>()?; + let return_type = self.find("return_type", |node| { + let value = self.cursor(node).expr()?; + Ok(Colon(Token::default(), value)) + })?; + + Ok(TopLevel { + data: Item::new( + Span::default(), + TopLevelKind::Signature(Signature { + name, + parameters, + return_type, + value, + }), + ), + attributes: thin_vec![], + }) + } + kind => todo!("{}", kind), + } + } +} diff --git a/crates/kind-syntax/src/builders.rs b/crates/kind-syntax/src/builders.rs new file mode 100644 index 00000000..aaece842 --- /dev/null +++ b/crates/kind-syntax/src/builders.rs @@ -0,0 +1,32 @@ +use crate::concrete::{ConstructorExpr, Expr, ExprKind, LocalExpr}; +use crate::lexemes::{Name, Span}; + +impl Expr { + pub fn constructor(span: Span, name: Name) -> Self { + Expr::new( + span, + ExprKind::Constructor(Box::new(ConstructorExpr { name })), + ) + } + + pub fn local(span: Span, name: Name) -> Self { + Expr::new(span, ExprKind::Local(Box::new(LocalExpr { name }))) + } +} +// +// macro_rules! impl_expr_kind { +// ( $( $name:ident( $( $arg:ident: $ty:ty ),* ) ),* ) => { +// impl Expr { +// $(pub fn $name($($arg: $ty),* span: Span) -> Self { +// Expr::new( +// span, +// ExprKind::$name(Box::new($name Expr { $($arg),* }))) +// })* +// } +// }; +// } +// +// impl_expr_kind!( +// Local( name: Name, ), +// Constructor( name: Name, ), +// ); diff --git a/crates/kind-syntax/src/concrete.rs b/crates/kind-syntax/src/concrete.rs index ebf9820a..046c2d2a 100644 --- a/crates/kind-syntax/src/concrete.rs +++ b/crates/kind-syntax/src/concrete.rs @@ -41,22 +41,22 @@ pub type Attribute = Item; /// A type binding is a type annotation for a variable. #[derive(Debug, Clone, PartialEq)] pub struct TypeBinding { - pub name: Ident, - pub typ: Colon>, + pub name: Name, + pub binding_type: Option>>, } #[derive(Debug, Clone, PartialEq)] -pub enum ArgumentBinding { - Implicit(AngleBracket), - Explicit(Paren), +pub enum ParameterBinding { + Implicit(AngleBracket), + Explicit(Paren), } /// An argument of a type signature. #[derive(Debug, Clone, PartialEq)] -pub struct Argument { - pub minus: Option, - pub plus: Option, - pub binding: ArgumentBinding, +pub enum SignatureParameter { + Exclude(ParameterBinding), + Include(ParameterBinding), + Both(ParameterBinding), } /// A local expression is a reference atom to a local declaration. @@ -79,9 +79,8 @@ pub struct ConstructorExpr { /// (x : Int) /// // or /// x -/// ``` #[derive(Debug, Clone, PartialEq)] -pub enum Param { +pub enum PiParameter { Named(Paren), Expr(Box), } @@ -90,7 +89,7 @@ pub enum Param { #[derive(Debug, Clone, PartialEq)] pub struct PiExpr { pub r#tilde: lexemes::Tilde, - pub param: Param, + pub param: PiParameter, pub r#arrow: lexemes::RightArrow, pub body: Box, } @@ -109,7 +108,7 @@ pub struct SigmaExpr { #[derive(Debug, Clone, PartialEq)] pub struct LambdaExpr { pub r#tilde: Option, - pub param: Param, + pub param: PiParameter, pub r#arrow: lexemes::FatArrow, pub body: Box, } @@ -309,7 +308,7 @@ pub struct CaseNode { pub struct MatchExpr { pub r#match: lexemes::Match, pub typ: Option, - pub with: Option<(lexemes::With, ThinVec)>, + pub with: Option<(lexemes::With, ThinVec)>, pub scrutinee: Box, pub cases: Brace>, pub motive: Option>>, @@ -387,7 +386,7 @@ pub type Expr = Item; #[derive(Debug, Clone, PartialEq)] pub struct ConstructorPat { pub name: QualifiedIdent, - pub args: ThinVec, + pub args: ThinVec, } /// A pattern is part of a rule. It is a structure that matches an expression. @@ -411,7 +410,7 @@ pub type Pat = Item; #[derive(Debug, Clone, PartialEq)] pub struct Signature { pub name: Name, - pub arguments: ThinVec, + pub parameters: ThinVec, pub return_type: Option>, pub value: Option, } @@ -440,7 +439,7 @@ pub struct Rule { #[derive(Debug, Clone, PartialEq)] pub struct Function { pub name: Ident, - pub arguments: ThinVec, + pub arguments: ThinVec, pub return_typ: Option>, pub value: Brace, } @@ -468,7 +467,7 @@ pub struct Command { #[derive(Debug, Clone, PartialEq)] pub struct Constructor { pub name: Ident, - pub arguments: ThinVec, + pub arguments: ThinVec, pub typ: Option>>, } @@ -478,8 +477,8 @@ pub struct Constructor { pub struct TypeDef { pub name: QualifiedIdent, pub constructors: ThinVec, - pub params: ThinVec, - pub indices: ThinVec, + pub params: ThinVec, + pub indices: ThinVec, } /// A record definition is a top-level structure that defines a type with @@ -488,8 +487,8 @@ pub struct TypeDef { pub struct RecordDef { pub name: QualifiedIdent, pub fields: ThinVec, - pub params: ThinVec, - pub indices: ThinVec, + pub params: ThinVec, + pub indices: ThinVec, } /// A top-level item is a item that is on the outermost level of a diff --git a/crates/kind-syntax/src/lib.rs b/crates/kind-syntax/src/lib.rs index 9a1646ab..86e7bc23 100644 --- a/crates/kind-syntax/src/lib.rs +++ b/crates/kind-syntax/src/lib.rs @@ -4,3 +4,4 @@ pub mod concrete; pub mod core; pub mod lexemes; +pub mod builders;