diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 4e95b1f..7a7b835 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -16,14 +16,33 @@ use tower_lsp::lsp_types::{self, *}; use tower_lsp::{Client, LanguageServer, LspService, Server}; use crate::rust_analyzer::RustAnalyzer; -use crate::transpiler::{TranspileError, transpile_mist, transpile_mist_no_sem}; +use crate::transpiler::{TranspileError, format_mist, transpile_mist, transpile_mist_no_sem}; static MARKER_COUNTER: AtomicU64 = AtomicU64::new(0); const KEYWORDS: [&'static str; 22] = [ - "if", "else", "for", "while", "match", "return", "break", "continue ", "struct ", "enum ", - "class ", "trait ", "impl ", "pub ", "mut ", "let ", "dyn ", "loop", "unsafe", - "override", "module ", "void ", + "if", + "else", + "for", + "while", + "match", + "return", + "break", + "continue ", + "struct ", + "enum ", + "class ", + "trait ", + "impl ", + "pub ", + "mut ", + "let ", + "dyn ", + "loop", + "unsafe", + "override", + "module ", + "void ", ]; fn keyword_completion_items() -> impl Iterator { @@ -666,6 +685,8 @@ impl LanguageServer for Backend { }, )); + res.capabilities.document_formatting_provider = Some(OneOf::Left(true)); + res.capabilities.completion_provider = Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![ @@ -1256,6 +1277,34 @@ impl LanguageServer for Backend { } } } + + async fn formatting( + &self, + params: DocumentFormattingParams, + ) -> tower_lsp::jsonrpc::Result>> { + let mist_path = params + .text_document + .uri + .to_file_path() + .map_err(|_| tower_lsp::jsonrpc::Error::internal_error())?; + + let docs = self.documents.lock().await; + + let old_text = docs + .get(&mist_path) + .map_or_else(|| Err(tower_lsp::jsonrpc::Error::internal_error()), Ok)?; + + let new_text = format_mist(&old_text.to_string()) + .map_err(|_| tower_lsp::jsonrpc::Error::internal_error())?; + + Ok(Some(vec![TextEdit { + new_text, + range: Range { + start: Position::new(0, 0), + end: Position::new(u32::MAX, u32::MAX), + }, + }])) + } } fn mist_ify_completions(source: &str, pos: &Position, items: &mut Vec) { diff --git a/crates/analyzer/src/transpiler.rs b/crates/analyzer/src/transpiler.rs index 2ffbfed..8413a8e 100644 --- a/crates/analyzer/src/transpiler.rs +++ b/crates/analyzer/src/transpiler.rs @@ -1,5 +1,6 @@ use std::path::{Path, PathBuf}; +use mist_codegen::fmt::MistCodegen; use mist_codegen::RustCodegen; use mist_parser::parse; use mist_parser::rev_mapper::Mapping; @@ -83,3 +84,13 @@ pub fn transpile_mist_no_sem( mapping: codegen.mapping, }) } + +pub fn format_mist(source: &str) -> Result { + let parsed = parse(source).map_err(|e| format!("parse error: {e:?}"))?; + + let mut codegen = MistCodegen::new(); + codegen.generate(parsed.mod_attributes); + let output = codegen.generate(parsed.items); + + Ok(output) +} diff --git a/crates/codegen/src/fmt/expr.rs b/crates/codegen/src/fmt/expr.rs new file mode 100644 index 0000000..ce55bdd --- /dev/null +++ b/crates/codegen/src/fmt/expr.rs @@ -0,0 +1,177 @@ +use mist_parser::ast::*; + +use crate::fmt::{Context, GenMist, GetMist, MistCodegen}; + +impl GenMist for Expression { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + let ensure_semicolon = if ctx.expr_ensure_semicolon { + ctx.expr_ensure_semicolon = false; + true + } else { + false + }; + + match self { + Expression::Path(path) => cg.add(&path.get_mist()), + Expression::Literal(literal) => literal.gen_mist(ctx, cg), + Expression::Statement(stmt) => stmt.gen_mist(ctx, cg), + Expression::Array(values) => { + cg.add("["); + for (i, val) in values.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + val.gen_mist(ctx, cg); + } + cg.add("]"); + } + Expression::ArrayRepeat(value, repeat) => { + cg.add("["); + value.gen_mist(ctx, cg); + cg.add("; "); + repeat.gen_mist(ctx, cg); + cg.add("]"); + } + Expression::Fix { + initial, + prefixes, + postfixes, + } => { + for prefix in prefixes { + prefix.gen_mist(ctx, cg); + } + initial.gen_mist(ctx, cg); + for postfix in postfixes { + postfix.gen_mist(ctx, cg); + } + } + Expression::Binary { lhs, op, rhs } => { + lhs.gen_mist(ctx, cg); + cg.add(" "); + cg.add(op); + cg.add(" "); + rhs.gen_mist(ctx, cg); + } + Expression::Closure { + return_type, + params, + body, + } => { + cg.add("("); + for (i, arg) in params.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + arg.gen_mist(ctx, cg); + } + cg.add(") => "); + + if let Some(ty) = return_type { + cg.add(&ty.get_mist()); + cg.add(" "); + cg.ensure_brackets_expr(ctx, body); + } else { + body.gen_mist(ctx, cg); + } + } + } + + if ensure_semicolon { + ctx.expr_ensure_semicolon = true; + if !self.is_block() { + cg.add(";"); + } + } + } +} + +impl GenMist for Prefix { + fn gen_mist(&self, _ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Self::Deref => cg.add("*"), + Self::Ref => cg.add("&"), + Self::RefMut => cg.add("&mut "), + Self::Not => cg.add("!"), + Self::Neg => cg.add("-"), + } + } +} + +impl GenMist for Postfix { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Postfix::TupleFieldAccess(field, generics) => { + cg.add(&format!( + ".{}{}", + field, + generics + .iter() + .map(|v| format!("::{}", v.get_mist())) + .collect::() + )); + } + Postfix::FieldAccess(field, generics) => { + cg.add(&format!( + ".{}{}", + field.get_mist(), + generics + .iter() + .map(|v| format!("::{}", v.get_mist())) + .collect::() + )); + } + Postfix::Call(args) => { + cg.add("("); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + arg.gen_mist(ctx, cg); + } + cg.add(")"); + } + Postfix::MacroCall { inner, delimiter } => { + let (open, close) = match delimiter { + MacroDelimiter::Paren => ("!(", ")"), + MacroDelimiter::Bracket => ("![", "]"), + MacroDelimiter::Brace => ("!{", "}"), + }; + cg.add(open); + cg.add(inner); + cg.add(close); + } + Postfix::StructCall(fields) => { + cg.addln(" {"); + cg.indent += 1; + for (name, expr) in fields { + cg.add_indented(&name.get_mist()); + if let Some(expr) = expr { + cg.add(": "); + expr.gen_mist(ctx, cg); + } + cg.addln(","); + } + cg.indent -= 1; + cg.add_indented("}"); + } + Postfix::Index(idx) => { + cg.add("["); + idx.gen_mist(ctx, cg); + cg.add("]"); + } + Postfix::As(ty) => { + cg.add(" as "); + cg.add(&ty.get_mist()); + } + Postfix::Try => cg.add("?"), + Postfix::Assign(cmp, expr) => { + cg.add(" "); + cg.add(cmp); + cg.add(" "); + expr.gen_mist(ctx, cg); + } + Postfix::Increment => cg.add("++"), + Postfix::Decrement => cg.add("--"), + } + } +} diff --git a/crates/codegen/src/fmt/mod.rs b/crates/codegen/src/fmt/mod.rs new file mode 100644 index 0000000..6da4bdd --- /dev/null +++ b/crates/codegen/src/fmt/mod.rs @@ -0,0 +1,412 @@ +pub mod expr; +pub mod statement; +pub mod top_level; + +use mist_parser::ast::*; + +pub struct Context { + pub expr_ensure_semicolon: bool, +} + +pub struct MistCodegen { + indent_amount: u8, + output: String, + indent: usize, +} + +impl MistCodegen { + pub fn new() -> Self { + Self { + output: String::new(), + indent: 0, + indent_amount: 4, + } + } + + fn indent_str(&self) -> String { + " ".repeat(self.indent_amount as usize).repeat(self.indent) + } + + pub fn add(&mut self, s: &str) { + self.output.push_str(s); + } + + pub fn addln(&mut self, s: &str) { + self.add(s); + self.add("\n"); + } + + pub fn add_indented(&mut self, s: &str) { + let line = format!("{}{}", self.indent_str(), s); + self.add(&line); + } + + pub fn add_indentedln(&mut self, s: &str) { + let line = format!("{}{}\n", self.indent_str(), s); + self.add(&line); + } + + pub fn start_bracket(&mut self) { + self.addln(""); + self.add_indentedln("{"); + + self.indent += 1; + } + + pub fn into_output(self) -> String { + self.output + } + + pub fn generate(&mut self, toplevels: Vec) -> String { + let mut ctx = Context { + expr_ensure_semicolon: true, + }; + + for (i, tl) in toplevels.iter().enumerate() { + if i > 0 { + self.add("\n"); + } + tl.gen_mist(&mut ctx, self); + } + + self.output.clone() + } + + pub fn ensure_brackets(&mut self, ctx: &mut Context, stmt: &Box) { + match &**stmt { + Statement::Block(_) => stmt.gen_mist(ctx, self), + _ => { + self.add("{"); + self.indent += 1; + stmt.gen_mist(ctx, self); + self.indent -= 1; + self.add_indented("}"); + } + } + } + + pub fn ensure_brackets_expr(&mut self, ctx: &mut Context, expr: &Expression) { + match expr { + Expression::Statement(stmt) => self.ensure_brackets(ctx, stmt), + _ => { + self.add("{"); + self.indent += 1; + expr.gen_mist(ctx, self); + self.indent -= 1; + self.add_indented("}"); + } + } + } +} + +pub trait GenMist { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen); +} + +pub trait GetMist { + fn get_mist(&self) -> String; +} + +impl GetMist for Path { + fn get_mist(&self) -> String { + self.0 + .iter() + .map(Identifier::get_mist) + .collect::>() + .join("::") + } +} + +impl GetMist for Identifier { + fn get_mist(&self) -> String { + self.0.clone() + } +} + +impl GetMist for Visibility { + fn get_mist(&self) -> String { + match self { + Visibility::Public => "pub ".to_string(), + Visibility::PublicTarget(path) => format!("pub({}) ", path.get_mist()), + Visibility::Private => "".to_string(), + } + } +} + +impl GetMist for TypeExpr { + fn get_mist(&self) -> String { + match self { + Self::Path(path, generics) => { + if let Some(generics) = generics { + format!("{}{}", path.get_mist(), generics.get_mist()) + } else { + path.get_mist() + } + } + Self::Lifetime(name) => format!("'{}", name.get_mist()), + Self::Tuple(types) => format!( + "({})", + types + .iter() + .map(|t| t.get_mist()) + .collect::>() + .join(", ") + ), + Self::StaticFn(types, return_type) => { + if let Some(return_type) = return_type { + format!( + "fn({}) -> {}", + types + .iter() + .map(|t| t.get_mist()) + .collect::>() + .join(", "), + return_type.get_mist() + ) + } else { + format!( + "fn({})", + types + .iter() + .map(|t| t.get_mist()) + .collect::>() + .join(", "), + ) + } + } + Self::UnsafePtr { mutable, ty } => { + let mutable = if *mutable { "mut " } else { "const " }; + format!("*{mutable}{}", ty.get_mist()) + } + Self::Ref { + lifetime, + mutable, + ty, + } => { + let base = ty.get_mist(); + if let Some(lifetime) = lifetime { + match lifetime { + Lifetime::Lifetime(v) => { + format!( + "{} {} '{}&", + base, + if *mutable { "mut" } else { "" }, + v.get_mist() + ) + } + Lifetime::Unsafe => { + format!("{} {} unsafe&", base, if *mutable { "mut" } else { "const" }) + } + } + } else if *mutable { + format!("{} mut&", base) + } else { + format!("{}&", base) + } + } + Self::Dyn(ty) => { + format!("dyn {}", ty.get_mist()) + } + } + } +} + +impl GetMist for ExprPath { + fn get_mist(&self) -> String { + self.0 + .iter() + .map(ExprPathSegment::get_mist) + .collect::>() + .join("::") + } +} + +impl GetMist for ExprPathSegment { + fn get_mist(&self) -> String { + format!( + "{}{}", + self.ident.get_mist(), + self.generics + .as_ref() + .map(|v| format!("::{}", v.get_mist())) + .unwrap_or_default() + ) + } +} + +impl GetMist for Generics { + fn get_mist(&self) -> String { + if self.0.is_empty() { + String::new() + } else { + format!( + "<{}>", + self.0 + .iter() + .map(Generic::get_mist) + .collect::>() + .join(", ") + ) + } + } +} + +impl GetMist for Generic { + fn get_mist(&self) -> String { + match self { + Self::Lifetime(name) => format!("'{}", name.get_mist()), + Self::Type(ty) => ty.get_mist(), + } + } +} + +impl GetMist for GenericsDecl { + fn get_mist(&self) -> String { + if self.0.is_empty() { + String::new() + } else { + format!( + "<{}>", + self.0 + .iter() + .map(|v| v.get_mist()) + .collect::>() + .join(", ") + ) + } + } +} + +impl GetMist for GenericDecl { + fn get_mist(&self) -> String { + match self { + GenericDecl::Lifetime(name) => format!("'{}", name.get_mist()), + GenericDecl::Type(name, requirements) => { + name.get_mist() + + &(if !requirements.is_empty() { + format!( + ": {}", + requirements + .iter() + .map(TypeExpr::get_mist) + .collect::>() + .join(" + ") + ) + } else { + String::new() + }) + } + } + } +} + +impl GenMist for T { + fn gen_mist(&self, _: &mut Context, cg: &mut MistCodegen) { + cg.add(&self.get_mist()); + } +} + +impl GenMist for Spanned { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + self.item.gen_mist(ctx, cg); + } +} + +impl GenMist for Attribute { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Self::Path(path) => cg.add(&path.get_mist()), + Self::NameValue { path, value } => { + cg.add(&format!("{} = ", path.get_mist())); + value.gen_mist(ctx, cg); + } + Self::List { path, items } => { + cg.add(&path.get_mist()); + cg.add("("); + for (i, item) in items.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + item.gen_mist(ctx, cg); + } + cg.add(")"); + } + } + } +} + +impl GenMist for Pattern { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Self::Etc => cg.add(".."), + Self::Literal(lit) => lit.gen_mist(ctx, cg), + Self::Path(mutable, path) => { + if *mutable { + cg.add("mut "); + } + cg.add(&path.get_mist()) + } + Self::Struct(path, inner) => { + cg.add(&path.get_mist()); + cg.add(" {"); + for (idx, i) in inner.iter().enumerate() { + if idx > 0 { + cg.add(", "); + } + if let Some((name, pat)) = i { + cg.add(&name.get_mist()); + if let Some(pat) = pat { + cg.add(": "); + pat.gen_mist(ctx, cg); + } + } else { + cg.add(".."); + } + } + cg.add("}"); + } + Self::NamedTuple(path, inner) => { + cg.add(&path.get_mist()); + cg.add("("); + for (i, pat) in inner.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + pat.gen_mist(ctx, cg); + } + cg.add(")"); + } + Self::Tuple(inner) => { + cg.add("("); + for (i, pat) in inner.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + pat.gen_mist(ctx, cg); + } + cg.add(")"); + } + } + } +} + +impl GenMist for Literal { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Self::Int(n) => cg.add(&n.to_string()), + Self::Float(n) => cg.add(&format!("{n:?}")), + Self::Bool(b) => cg.add(&b.to_string()), + Self::String(s) => cg.add(&format!("\"{s}\"")), + Self::Tuple(values) => { + cg.add("("); + for (i, val) in values.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + val.gen_mist(ctx, cg); + } + cg.add(")"); + } + } + } +} diff --git a/crates/codegen/src/fmt/statement.rs b/crates/codegen/src/fmt/statement.rs new file mode 100644 index 0000000..e38a87e --- /dev/null +++ b/crates/codegen/src/fmt/statement.rs @@ -0,0 +1,157 @@ +use mist_parser::ast::*; + +use crate::fmt::{Context, GenMist, GetMist, MistCodegen}; + +impl GenMist for Block { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + cg.addln("{"); + cg.indent += 1; + + for stmt in &self.statements { + ctx.expr_ensure_semicolon = true; + cg.add_indented(""); + stmt.gen_mist(ctx, cg); + cg.addln(""); + } + + if let Some(soft_return) = &self.soft_return { + ctx.expr_ensure_semicolon = false; + cg.add_indented(""); + soft_return.gen_mist(ctx, cg); + cg.addln(""); + } + + cg.indent -= 1; + cg.add_indented("}"); + } +} + +impl GenMist for Statement { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Statement::UnsafeBlock(block) => { + cg.add("unsafe "); + block.gen_mist(ctx, cg); + } + Statement::Block(block) => block.gen_mist(ctx, cg), + Statement::VarDecl(VarDeclStmt { decl, init }) => { + if decl.type_.is_some() { + cg.add(&decl.type_.as_ref().unwrap().get_mist()); + cg.add(" "); + decl.name.gen_mist(ctx, cg); + } else { + cg.add("let "); + decl.name.gen_mist(ctx, cg); + } + if let Some(init) = init { + cg.add(" = "); + init.gen_mist(ctx, cg); + } + } + Statement::Match(expr, match_items) => { + cg.add("match "); + expr.gen_mist(ctx, cg); + cg.add(" {"); + if !match_items.is_empty() { + cg.addln(""); + cg.indent += 1; + for match_item in match_items { + let MatchItem(pat, body) = &match_item.item; + cg.add_indented(""); + for (i, p) in pat.iter().enumerate() { + if i > 0 { + cg.add(" | "); + } + p.gen_mist(ctx, cg); + } + cg.add(" => "); + if body.is_block() { + body.gen_mist(ctx, cg); + cg.addln(""); + } else { + ctx.expr_ensure_semicolon = false; + body.gen_mist(ctx, cg); + cg.addln(","); + } + } + cg.indent -= 1; + cg.add_indented("}"); + } else { + cg.add("}"); + } + } + Statement::If { + initial, + else_if, + else_branch, + } => { + cg.add("if "); + ctx.expr_ensure_semicolon = false; + initial.condition.gen_mist(ctx, cg); + cg.add(" "); + initial.body.gen_mist(ctx, cg); + + for else_if_branch in else_if { + cg.add(" else if "); + ctx.expr_ensure_semicolon = false; + else_if_branch.condition.gen_mist(ctx, cg); + cg.add(" "); + else_if_branch.body.gen_mist(ctx, cg); + } + + if let Some(else_br) = else_branch { + cg.add(" else "); + else_br.gen_mist(ctx, cg); + } + } + Statement::While(StatementBranch { condition, body }) => { + cg.add("while "); + condition.gen_mist(ctx, cg); + cg.add(" "); + body.gen_mist(ctx, cg); + } + Statement::Loop(body) => { + cg.add("loop "); + body.gen_mist(ctx, cg); + } + Statement::CStyleFor { + init, + condition, + update, + body, + } => { + cg.add("for ("); + ctx.expr_ensure_semicolon = true; + init.gen_mist(ctx, cg); + cg.add(" "); + ctx.expr_ensure_semicolon = false; + condition.gen_mist(ctx, cg); + cg.add(" "); + update.gen_mist(ctx, cg); + cg.add(") "); + body.gen_mist(ctx, cg); + } + Statement::For { + pattern, + iterator, + body, + } => { + cg.add("for "); + pattern.gen_mist(ctx, cg); + cg.add(" in "); + iterator.gen_mist(ctx, cg); + cg.add(" "); + body.gen_mist(ctx, cg); + } + Statement::Return(expr) => { + cg.add("return"); + if let Some(expr) = expr { + cg.add(" "); + expr.gen_mist(ctx, cg); + } + } + Statement::Break => cg.add("break"), + Statement::Continue => cg.add("continue"), + } + } +} diff --git a/crates/codegen/src/fmt/top_level.rs b/crates/codegen/src/fmt/top_level.rs new file mode 100644 index 0000000..60923e0 --- /dev/null +++ b/crates/codegen/src/fmt/top_level.rs @@ -0,0 +1,337 @@ +use mist_parser::ast::*; + +use crate::fmt::{Context, GenMist, GetMist, MistCodegen}; + +impl GenMist for ImplDecl { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + cg.add_indented("impl "); + let generics = self.generics.get_mist(); + if !generics.is_empty() { + cg.add(&generics); + cg.add(" "); + } + if let Some(trait_) = &self.trait_ { + cg.add(&trait_.get_mist()); + cg.add(" for "); + } + + cg.add(&self.target.get_mist()); + + cg.start_bracket(); + + for method in &self.methods { + method.gen_mist(ctx, cg); + } + + cg.indent -= 1; + cg.add_indentedln("}"); + } +} + +impl GenMist for FunctionDecl { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + cg.add_indented(&self.visibility.get_mist()); + + if self.return_type.is_none() { + cg.add("void "); + } else { + cg.add(&self.return_type.as_ref().unwrap().get_mist()); + cg.add(" "); + } + + cg.add(&self.name.get_mist()); + cg.add(&self.generics.get_mist()); + cg.add("("); + + if let Some((is_ref, lifetime, is_mut)) = &self.self_param { + if *is_ref { + cg.add("&"); + } + if let Some(lifetime) = lifetime { + cg.add(&format!("'{} ", lifetime.0)); + } + if *is_mut { + cg.add("mut "); + } + cg.add("self"); + if !self.params.0.is_empty() { + cg.add(", "); + } + } + + for (i, param) in self.params.0.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + param.gen_mist(ctx, cg); + } + + cg.add(")"); + + if let Some(override_spec) = &self.is_override { + cg.add(" override"); + if let Some(path) = &override_spec.0 { + cg.add("("); + cg.add(&path.get_mist()); + cg.add(")"); + } + } + + if let Some(body) = &self.body { + cg.addln(""); + cg.add_indented(""); + body.gen_mist(ctx, cg); + cg.addln(""); + } else { + cg.addln(";"); + } + } +} + +impl GenMist for ParamList { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + for (i, param) in self.0.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + param.gen_mist(ctx, cg); + } + } +} + +impl GenMist for VarDecl { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + if let Some(type_) = &self.type_ { + cg.add(&type_.get_mist()); + cg.add(" "); + } + self.name.gen_mist(ctx, cg); + } +} + +impl GetMist for FieldDecl { + fn get_mist(&self) -> String { + format!( + "{}{} {}", + self.visibility.get_mist(), + self.type_.get_mist(), + self.name.get_mist(), + ) + } +} + +impl GetMist for EnumItem { + fn get_mist(&self) -> String { + match self { + Self::Named(id) => id.get_mist(), + Self::Struct(id, s) => format!( + "{} {{{}}}", + id.get_mist(), + s.iter() + .map(FieldDecl::get_mist) + .collect::>() + .join(", ") + ), + Self::Tuple(id, t) => format!( + "{}({})", + id.get_mist(), + t.iter() + .map(TypeExpr::get_mist) + .collect::>() + .join(", ") + ), + } + } +} + +impl GenMist for TopLevel { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + if let TopLevelKind::ModAttribute = self.0.item { + for attr in &self.1 { + cg.add("#!["); + attr.gen_mist(ctx, cg); + cg.addln("]"); + } + } else { + for attr in &self.1 { + cg.add("#["); + attr.gen_mist(ctx, cg); + cg.addln("]"); + } + } + + self.0.gen_mist(ctx, cg); + } +} + +impl GenMist for TopLevelKind { + fn gen_mist(&self, ctx: &mut Context, cg: &mut MistCodegen) { + match self { + Self::ModAttribute => {} + Self::Import(vis, path) => { + cg.addln(&format!("{}use {};", vis.get_mist(), path.get_mist())) + } + Self::DeclareModule(vis, name) => { + cg.addln(&format!("{}module {};", vis.get_mist(), name.get_mist())) + } + Self::FunctionDecl(decl) => decl.gen_mist(ctx, cg), + Self::ImplDecl(impl_) => impl_.gen_mist(ctx, cg), + Self::StructDecl { + visibility, + name, + generics, + fields, + } => { + cg.add(&format!( + "{}struct {}{}", + visibility.get_mist(), + name.get_mist(), + generics.get_mist() + )); + + if fields.is_empty() { + cg.add(" {}"); + } else { + cg.start_bracket(); + + for field in fields { + cg.add_indentedln(&format!("{},", field.item.get_mist())); + } + + cg.indent -= 1; + cg.add_indented("}"); + } + + cg.addln(""); + } + Self::EnumDecl { + visibility, + name, + generics, + fields, + } => { + cg.add(&format!( + "{}enum {}{}", + visibility.get_mist(), + name.get_mist(), + generics.get_mist() + )); + + if fields.is_empty() { + cg.add(" {}"); + } else { + cg.start_bracket(); + + for field in fields { + cg.add_indentedln(&format!("{},", field.item.get_mist())); + } + + cg.indent -= 1; + cg.add_indented("}"); + } + + cg.addln(""); + } + Self::TraitDecl { + visibility, + name, + generics, + requirements, + items, + } => { + cg.add(&format!( + "{}trait {}{}", + visibility.get_mist(), + name.get_mist(), + generics.get_mist() + )); + if !requirements.is_empty() { + cg.add(" : "); + cg.add( + &requirements + .iter() + .map(TypeExpr::get_mist) + .collect::>() + .join(" + "), + ); + } + cg.start_bracket(); + for item in items { + item.gen_mist(ctx, cg); + } + cg.indent -= 1; + cg.add_indentedln("}"); + } + Self::ClassDecl { + visibility, + name, + generics, + inherits, + fields, + constructor, + items, + } => { + cg.add(&format!( + "{}class {}{}", + visibility.get_mist(), + name.get_mist(), + generics.get_mist() + )); + if let Some(inherits) = inherits { + cg.add(" : "); + cg.add(&inherits.get_mist()); + } + + cg.start_bracket(); + + for field in fields { + cg.add_indented(&field.item.decl.get_mist()); + if let Some(init) = &field.item.init { + cg.add(" = "); + init.gen_mist(ctx, cg); + } + cg.addln(";"); + } + + if let Some(constructor) = constructor { + if fields.len() > 0 { + cg.addln(""); + } + + cg.add_indented(&constructor.item.visibility.get_mist()); + cg.add("constructor"); + cg.add(&constructor.item.generics.get_mist()); + cg.add("("); + for (i, param) in constructor.item.params.0.iter().enumerate() { + if i > 0 { + cg.add(", "); + } + param.gen_mist(ctx, cg); + } + cg.addln(") "); + cg.add_indented(""); + constructor.item.body.gen_mist(ctx, cg); + cg.addln(""); + + if items.len() > 0 { + cg.addln(""); + } + } + + for (i, item) in items.iter().enumerate() { + if i > 0 { + cg.addln(""); + } + + match item { + ClassItem::Method(method) => method.gen_mist(ctx, cg), + ClassItem::ImplDecl(impl_) => impl_.gen_mist(ctx, cg), + } + } + + cg.indent -= 1; + cg.add_indentedln("}"); + } + } + } +} diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index 83217fb..6323b91 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1,5 +1,6 @@ pub mod class_decl; pub mod expr; +pub mod fmt; pub mod statement; pub mod top_level; diff --git a/crates/parser/src/grammar.pest b/crates/parser/src/grammar.pest index 719e55d..ae5c4bc 100644 --- a/crates/parser/src/grammar.pest +++ b/crates/parser/src/grammar.pest @@ -450,7 +450,7 @@ loop_stmt = { pattern_list = { pattern ~ ("|" ~ pattern)* } match_item = { - pattern_list ~ "=>" ~ (statement | (expr ~ ",")) + pattern_list ~ "=>" ~ (block | (expr ~ ",")) } match_stmt = { diff --git a/mist-test/src/common/decl_test.mist b/mist-test/src/common/decl_test.mist index 57a8717..4f00a07 100644 --- a/mist-test/src/common/decl_test.mist +++ b/mist-test/src/common/decl_test.mist @@ -1,44 +1,47 @@ pub module decl_test; -pub void test_let_basic() { +pub void test_let_basic() +{ let x = 42; assert_eq!(x, 42); } -pub void test_let_typed() { +pub void test_let_typed() +{ i32 x = 42; assert_eq!(x, 42); - str& s = "hello"; assert_eq!(s, "hello"); - bool b = true; assert_eq!(b, true); - f64 f = 3.14; assert_eq!(f, 3.14); } -pub void test_let_mut() { +pub void test_let_mut() +{ let mut _x = 42; _x = 100; assert_eq!(_x, 100); } -pub void test_let_tuple_destructure() { +pub void test_let_tuple_destructure() +{ let (a, b) = (10, "hello"); assert_eq!(a, 10); assert_eq!(b, "hello"); } -pub void test_let_nested_tuple() { +pub void test_let_nested_tuple() +{ let (a, (b, c)) = (1, (2, 3)); assert_eq!(a, 1); assert_eq!(b, 2); assert_eq!(c, 3); } -pub void test_let_pattern_irrefutable() { +pub void test_let_pattern_irrefutable() +{ let x = 5; let (y, z) = (1, 2); assert_eq!(x, 5); @@ -46,30 +49,35 @@ pub void test_let_pattern_irrefutable() { assert_eq!(z, 2); } -pub void test_multiple_decls() { +pub void test_multiple_decls() +{ let a = 1; let b = 2; let c = 3; assert_eq!(a + b + c, 6); } -pub void test_let_with_type_and_init() { +pub void test_let_with_type_and_init() +{ f64 x = 10.0; assert_eq!(x, 10.0); } -pub void test_string_literal() { +pub void test_string_literal() +{ let s = "hello, world!"; assert_eq!(s.len(), 13); } -pub void test_let_bool() { +pub void test_let_bool() +{ let t = true; let f = false; assert_eq!(t, !f); } -pub void run_tests() { +pub void run_tests() +{ test_let_basic(); test_let_typed(); test_let_mut(); diff --git a/mist-test/src/common/expr_test.mist b/mist-test/src/common/expr_test.mist index c344a00..c2f9fd7 100644 --- a/mist-test/src/common/expr_test.mist +++ b/mist-test/src/common/expr_test.mist @@ -1,6 +1,7 @@ pub module expr_test; -pub void test_literals() { +pub void test_literals() +{ assert_eq!(42, 42); assert_eq!(3.14, 3.14); assert_eq!(true, true); @@ -8,28 +9,32 @@ pub void test_literals() { assert_eq!("hello", "hello"); } -pub void test_tuples() { +pub void test_tuples() +{ let t = (1, true, "three"); assert_eq!(t.0, 1); assert_eq!(t.1, true); assert_eq!(t.2, "three"); } -pub void test_arrays() { +pub void test_arrays() +{ let arr = [1, 2, 3]; assert_eq!(arr[0], 1); assert_eq!(arr[1], 2); assert_eq!(arr[2], 3); } -pub void test_array_repeat() { +pub void test_array_repeat() +{ let arr = [0; 5]; assert_eq!(arr[0], 0); assert_eq!(arr[4], 0); assert_eq!(arr.len(), 5); } -pub void test_binary_arith() { +pub void test_binary_arith() +{ assert_eq!(10 + 5, 15); assert_eq!(10 - 5, 5); assert_eq!(10 * 5, 50); @@ -37,7 +42,8 @@ pub void test_binary_arith() { assert_eq!(10 % 3, 1); } -pub void test_binary_cmp() { +pub void test_binary_cmp() +{ assert_eq!(5 < 10, true); assert_eq!(10 <= 10, true); assert_eq!(10 > 5, true); @@ -46,14 +52,16 @@ pub void test_binary_cmp() { assert_eq!(5 != 10, true); } -pub void test_binary_logic() { +pub void test_binary_logic() +{ assert_eq!(true && true, true); assert_eq!(true && false, false); assert_eq!(true || false, true); assert_eq!(false || false, false); } -pub void test_binary_bit() { +pub void test_binary_bit() +{ assert_eq!(5 & 3, 1); assert_eq!(5 | 3, 7); assert_eq!(5 ^ 3, 6); @@ -61,20 +69,23 @@ pub void test_binary_bit() { assert_eq!(4 >> 1, 2); } -pub void test_prefix() { +pub void test_prefix() +{ let x = 42; assert_eq!(-x, -42); assert_eq!(!true, false); assert_eq!(!false, true); } -pub void test_ref_deref() { +pub void test_ref_deref() +{ let x = 42; let r = &x; assert_eq!(*r, 42); } -pub void test_postfix_inc_dec() { +pub void test_postfix_inc_dec() +{ let mut x = 5; x++; assert_eq!(x, 6); @@ -82,17 +93,20 @@ pub void test_postfix_inc_dec() { assert_eq!(x, 5); } -pub void test_as_cast() { +pub void test_as_cast() +{ let x = 42; assert_eq!(x as f64, 42.0); } -pub void test_closure() { +pub void test_closure() +{ let add = (a, b) => a + b; assert_eq!(add(2, 3), 5); } -pub void test_block_expr() { +pub void test_block_expr() +{ let x = { let y = 10; y + 5 @@ -100,16 +114,25 @@ pub void test_block_expr() { assert_eq!(x, 15); } -pub void test_if_expr() { - let x = if true { 1 } else { 2 }; +pub void test_if_expr() +{ + let x = if true { + 1 + } else { + 2 + }; assert_eq!(x, 1); - - let y = if false { 1 } else { 2 }; + let y = if false { + 1 + } else { + 2 + }; assert_eq!(y, 2); } -pub void test_range_expr() { - let r = 0..5; +pub void test_range_expr() +{ + let r = 0 .. 5; let mut count = 0; for i in r { count++; @@ -117,7 +140,8 @@ pub void test_range_expr() { assert_eq!(count, 5); } -pub void test_binary_assign() { +pub void test_binary_assign() +{ let mut x = 10; x += 5; assert_eq!(x, 15); @@ -131,7 +155,8 @@ pub void test_binary_assign() { assert_eq!(x, 2); } -pub void run_tests() { +pub void run_tests() +{ test_literals(); test_tuples(); test_arrays(); diff --git a/mist-test/src/common/statement_test.mist b/mist-test/src/common/statement_test.mist index 00ca939..aeab488 100644 --- a/mist-test/src/common/statement_test.mist +++ b/mist-test/src/common/statement_test.mist @@ -1,30 +1,29 @@ pub module statement_test; -pub void test_var_decl() { +pub void test_var_decl() +{ let x = 42; assert_eq!(x, 42); - i32 y = 10; assert_eq!(y, 10); - str& name = "mist"; assert_eq!(name, "mist"); } -pub void test_var_decl_with_init() { +pub void test_var_decl_with_init() +{ let mut x = 0; x = 5; assert_eq!(x, 5); } -pub void test_if_statement() { +pub void test_if_statement() +{ let mut x = 0; - if true { x = 1; } assert_eq!(x, 1); - if false { x = 2; } else { @@ -33,9 +32,9 @@ pub void test_if_statement() { assert_eq!(x, 3); } -pub void test_if_else_if() { +pub void test_if_else_if() +{ let mut x = 0; - if x == 1 { x = 10; } else if x == 0 { @@ -46,27 +45,27 @@ pub void test_if_else_if() { assert_eq!(x, 20); } -pub void test_while_loop() { +pub void test_while_loop() +{ let mut count = 0; - while count < 5 { count++; } assert_eq!(count, 5); } -pub void test_for_loop() { +pub void test_for_loop() +{ let mut sum = 0; - - for i in 0..4 { + for i in 0 .. 4 { sum += i; } assert_eq!(sum, 6); } -pub void test_loop_stmt() { +pub void test_loop_stmt() +{ let mut count = 0; - loop { count++; if count >= 3 { @@ -76,10 +75,10 @@ pub void test_loop_stmt() { assert_eq!(count, 3); } -pub void test_break_continue() { +pub void test_break_continue() +{ let mut sum = 0; - - for i in 0..10 { + for i in 0 .. 10 { if i % 2 == 0 { continue; } @@ -91,11 +90,13 @@ pub void test_break_continue() { assert_eq!(sum, 1 + 3 + 5); } -pub i32 test_return() { +pub i32 test_return() +{ return 42; } -pub void test_block_scope() { +pub void test_block_scope() +{ let x = { let inner = 10; inner + 5 @@ -103,53 +104,64 @@ pub void test_block_scope() { assert_eq!(x, 15); } -pub void test_match_literal() { +pub void test_match_literal() +{ let x = 2; let result; - match x { - 1 => { result = 10; } - 2 => result = 20; + 1 => { + result = 10; + } + 2 => result = 20, 3 => result = 30, - _ => panic!(); + _ => panic!(), } assert_eq!(result, 20); } -pub void test_match_or_pattern() { +pub void test_match_or_pattern() +{ let x = 1; i32 result; - match x { - 1 | 2 => { result = 100; } - 3 => { result = 200; } - _ => panic!(); + 1 | 2 => { + result = 100; + } + 3 => { + result = 200; + } + _ => panic!(), } assert_eq!(result, 100); } -pub void test_match_default() { +pub void test_match_default() +{ let x = 99; let result; - match x { - 1 => { result = 10; } - y => { result = y; } + 1 => { + result = 10; + } + y => { + result = y; + } } assert_eq!(result, 99); } -pub void test_for_with_pattern_destructure() { +pub void test_for_with_pattern_destructure() +{ let pairs = [(1, "a"), (2, "b")]; let mut keys = 0; - for (k, _) in pairs { keys += k; } assert_eq!(keys, 3); } -pub void run_tests() { +pub void run_tests() +{ test_var_decl(); test_var_decl_with_init(); test_if_statement(); diff --git a/mist-test/src/common/types_test.mist b/mist-test/src/common/types_test.mist index 59f4f30..76894b9 100644 --- a/mist-test/src/common/types_test.mist +++ b/mist-test/src/common/types_test.mist @@ -13,7 +13,6 @@ pub void test_float_type() { pub void test_bool_type() { bool x = true; assert_eq!(x, true); - bool y = false; assert_eq!(y, false); } @@ -42,7 +41,8 @@ pub void test_mut_ref_type() { assert_eq!(x, 100); } -pub void test_void_type() {} +pub void test_void_type() { +} i32 add(i32 a, i32 b) { a + b diff --git a/mist-test/src/items/attribute_test.mist b/mist-test/src/items/attribute_test.mist index 03dd461..1d8cc07 100644 --- a/mist-test/src/items/attribute_test.mist +++ b/mist-test/src/items/attribute_test.mist @@ -1,23 +1,29 @@ pub module attribute_test; #[derive(Debug)] -pub struct Debuggable { +pub struct Debuggable +{ i32 value, } -pub void test_derive_attribute() { - let d = Debuggable { value: 42 }; +pub void test_derive_attribute() +{ + let d = Debuggable { + value: 42, + }; assert_eq!(d.value, 42); } #[allow(unused_variables)] -pub void test_allow_attribute() { +pub void test_allow_attribute() +{ let x = 42; let y = "unused"; assert_eq!(x, 42); } -pub void run_tests() { +pub void run_tests() +{ test_derive_attribute(); test_allow_attribute(); } diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index 9cd2fd0..a0a0f5b 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -1,56 +1,68 @@ pub module class_test; -pub class Animal { +pub class Animal +{ pub String name; - constructor() { + constructor() + { self.name = "Rex".to_string(); } - pub String speak(&self) { + pub String speak(&self) + { "Unknown".to_string() } - pub void test_speak<'a>(&'a self) { + pub void test_speak<'a>(&'a self) + { assert_eq!(self.speak(), "Woof!"); } - pub void static_method() {} + pub void static_method() + { + } } -pub class Mammal : Animal { - constructor() { +pub class Mammal : Animal +{ + constructor() + { super = Super::new(); } - pub void args_test(&self, i32 x, str& y) { + pub void args_test(&self, i32 x, str& y) + { assert_eq!(x, 10); assert_eq!(y, "hello"); } } -pub class Dog : Mammal { - constructor() { +pub class Dog : Mammal +{ + constructor() + { super = Super::new(); } - pub String speak(&self) override(Animal) { + pub String speak(&self) override(Animal) + { "Woof!".to_string() } - impl std::fmt::Display { - Result<(), std::fmt::Error> fmt(&self, std::fmt::Formatter<'_> mut& f) { + impl std::fmt::Display + { + Result<(), std::fmt::Error> fmt(&self, std::fmt::Formatter<'_> mut& f) + { write!(f, "🐾 {}", self.name) } } } -pub void run_tests() { +pub void run_tests() +{ let dawg = Dog::new(); - dawg.args_test(10, "hello"); - dawg.test_speak(); - assert_eq!(format!("{dawg}"), "🐾 Rex"); } diff --git a/mist-test/src/items/enum_test.mist b/mist-test/src/items/enum_test.mist index 2eafd99..c46564a 100644 --- a/mist-test/src/items/enum_test.mist +++ b/mist-test/src/items/enum_test.mist @@ -1,60 +1,92 @@ pub module enum_test; -enum OptionInt { +enum OptionInt +{ None, Some(i32), } -pub void test_enum_named() { +pub void test_enum_named() +{ let x = OptionInt::None; match x { - OptionInt::None => { assert_eq!(1, 1); } - OptionInt::Some(_) => { assert_eq!(0, 1); } + OptionInt::None => { + assert_eq!(1, 1); + } + OptionInt::Some(_) => { + assert_eq!(0, 1); + } } } -pub void test_enum_tuple() { +pub void test_enum_tuple() +{ let x = OptionInt::Some(42); match x { - OptionInt::None => { assert_eq!(0, 1); } - OptionInt::Some(v) => { assert_eq!(v, 42); } + OptionInt::None => { + assert_eq!(0, 1); + } + OptionInt::Some(v) => { + assert_eq!(v, 42); + } } } -enum Shape { - Circle { i32 radius }, - Rect { i32 w, i32 h }, +enum Shape +{ + Circle {i32 radius}, + Rect {i32 w, i32 h}, } -pub void test_enum_struct() { - let c = Shape::Circle { radius: 5 }; +pub void test_enum_struct() +{ + let c = Shape::Circle { + radius: 5, + }; match c { - Shape::Circle { radius } => { assert_eq!(radius, 5); } - Shape::Rect { .. } => { assert_eq!(0, 1); } + Shape::Circle {radius} => { + assert_eq!(radius, 5); + } + Shape::Rect {..} => { + assert_eq!(0, 1); + } } - - let r = Shape::Rect { w: 3, h: 4 }; + let r = Shape::Rect { + w: 3, + h: 4, + }; match r { - Shape::Circle { .. } => { assert_eq!(0, 1); } - Shape::Rect { w, h } => { assert_eq!(w * h, 12); } + Shape::Circle {..} => { + assert_eq!(0, 1); + } + Shape::Rect {w, h} => { + assert_eq!(w * h, 12); + } } } -enum Color { +enum Color +{ Red, Green, Blue, } -pub void test_enum_eq() { +pub void test_enum_eq() +{ let c = Color::Red; match c { - Color::Red => { assert_eq!(1, 1); } - _ => { assert_eq!(0, 1); } + Color::Red => { + assert_eq!(1, 1); + } + _ => { + assert_eq!(0, 1); + } } } -pub void run_tests() { +pub void run_tests() +{ test_enum_named(); test_enum_tuple(); test_enum_struct(); diff --git a/mist-test/src/items/function_test.mist b/mist-test/src/items/function_test.mist index a795225..65b0515 100644 --- a/mist-test/src/items/function_test.mist +++ b/mist-test/src/items/function_test.mist @@ -1,60 +1,76 @@ pub module function_test; -void greet() { +void greet() +{ let mut x = 1; x++; } -pub void test_basic_function() { +pub void test_basic_function() +{ greet(); } -i32 add(i32 a, i32 b) { +i32 add(i32 a, i32 b) +{ a + b } -pub void test_fn_params() { +pub void test_fn_params() +{ assert_eq!(add(2, 3), 5); } -pub i32 test_fn_return() { +pub i32 test_fn_return() +{ 42 } -pub i32 test_fn_early_return() { +pub i32 test_fn_early_return() +{ return 100; 0 } -pub i32 test_fn_mut_param(i32 mut x) { +pub i32 test_fn_mut_param(i32 mut x) +{ x = x + 10; x } -pub void test_method_call() { +pub void test_method_call() +{ let s = "hello"; assert_eq!(s.len(), 5); assert_eq!(s.to_uppercase(), "HELLO"); } -i32 triple(i32 x) { +i32 triple(i32 x) +{ add(x, add(x, x)) } -pub void test_nested_fn_calls() { +pub void test_nested_fn_calls() +{ assert_eq!(triple(5), 15); } -i32 factorial(i32 n) { - if n <= 1 { 1 } - else { n * factorial(n - 1) } +i32 factorial(i32 n) +{ + if n <= 1 { + 1 + } else { + n * factorial(n - 1) + } } -pub void test_recursive_fn() { +pub void test_recursive_fn() +{ assert_eq!(factorial(5), 120); } -pub void run_tests() { +pub void run_tests() +{ test_basic_function(); test_fn_params(); assert_eq!(test_fn_return(), 42); diff --git a/mist-test/src/items/impl_test.mist b/mist-test/src/items/impl_test.mist index 36c83ab..c28b869 100644 --- a/mist-test/src/items/impl_test.mist +++ b/mist-test/src/items/impl_test.mist @@ -1,39 +1,55 @@ pub module impl_test; -struct Vector2 { +struct Vector2 +{ i32 x, i32 y, } -impl Vector2 { - pub i32 magnitude(&self) { +impl Vector2 +{ + pub i32 magnitude(&self) + { self.x * self.x + self.y * self.y } } -pub void test_inherent_impl() { - let v = Vector2 { x: 3, y: 4 }; +pub void test_inherent_impl() +{ + let v = Vector2 { + x: 3, + y: 4, + }; assert_eq!(v.magnitude(), 25); } -trait Display { +trait Display +{ String display(&self); } -impl Display for Vector2 { - String display(&self) { +impl Display for Vector2 +{ + String display(&self) + { format!("({}, {})", self.x, self.y) } } -pub void test_trait_impl_block() { - let v = Vector2 { x: 1, y: 2 }; +pub void test_trait_impl_block() +{ + let v = Vector2 { + x: 1, + y: 2, + }; let s = v.display(); assert_eq!(s.len() > 0, true); } -impl Vector2 { - pub Vector2 add(&self, Vector2 other) { +impl Vector2 +{ + pub Vector2 add(&self, Vector2 other) + { Vector2 { x: self.x + other.x, y: self.y + other.y, @@ -41,15 +57,23 @@ impl Vector2 { } } -pub void test_multiple_impls() { - let a = Vector2 { x: 1, y: 2 }; - let b = Vector2 { x: 3, y: 4 }; +pub void test_multiple_impls() +{ + let a = Vector2 { + x: 1, + y: 2, + }; + let b = Vector2 { + x: 3, + y: 4, + }; let c = a.add(b); assert_eq!(c.x, 4); assert_eq!(c.y, 6); } -pub void run_tests() { +pub void run_tests() +{ test_inherent_impl(); test_trait_impl_block(); test_multiple_impls(); diff --git a/mist-test/src/items/struct_test.mist b/mist-test/src/items/struct_test.mist index 1bb4e3e..36e44fb 100644 --- a/mist-test/src/items/struct_test.mist +++ b/mist-test/src/items/struct_test.mist @@ -1,44 +1,66 @@ pub module struct_test; -struct Point { +struct Point +{ i32 x, i32 y, } -pub void test_struct_create_access() { - let p = Point { x: 10, y: 20 }; +pub void test_struct_create_access() +{ + let p = Point { + x: 10, + y: 20, + }; assert_eq!(p.x, 10); assert_eq!(p.y, 20); } -pub void test_struct_mut_field() { - let mut p = Point { x: 1, y: 2 }; +pub void test_struct_mut_field() +{ + let mut p = Point { + x: 1, + y: 2, + }; p.x = 100; assert_eq!(p.x, 100); } -pub void test_struct_destructure() { - let p = Point { x: 3, y: 4 }; - let Point { x, y } = p; +pub void test_struct_destructure() +{ + let p = Point { + x: 3, + y: 4, + }; + let Point {x, y} = p; assert_eq!(x, 3); assert_eq!(y, 4); } -struct Line { +struct Line +{ Point start, Point end, } -pub void test_struct_nested() { +pub void test_struct_nested() +{ let l = Line { - start: Point { x: 0, y: 0 }, - end: Point { x: 5, y: 10 }, + start: Point { + x: 0, + y: 0, + }, + end: Point { + x: 5, + y: 10, + }, }; assert_eq!(l.start.x, 0); assert_eq!(l.end.y, 10); } -pub void run_tests() { +pub void run_tests() +{ test_struct_create_access(); test_struct_mut_field(); test_struct_destructure(); diff --git a/mist-test/src/items/trait_test.mist b/mist-test/src/items/trait_test.mist index 6a26325..c81c125 100644 --- a/mist-test/src/items/trait_test.mist +++ b/mist-test/src/items/trait_test.mist @@ -1,41 +1,55 @@ pub module trait_test; -trait Speak { +trait Speak +{ String speak(&self); } -trait Greet : Speak { +trait Greet : Speak +{ String greet(&self); } -struct Dog { +struct Dog +{ String name, } -impl Speak for Dog { - String speak(&self) { +impl Speak for Dog +{ + String speak(&self) + { "woof".to_string() } } -impl Greet for Dog { - String greet(&self) { +impl Greet for Dog +{ + String greet(&self) + { "hello".to_string() } } -pub void test_trait_impl() { - let d = Dog { name: "Rex".to_string() }; +pub void test_trait_impl() +{ + let d = Dog { + name: "Rex".to_string(), + }; assert_eq!(d.speak(), "woof"); } -pub void test_trait_with_requirement() { - let d = Dog { name: "Buddy".to_string() }; +pub void test_trait_with_requirement() +{ + let d = Dog { + name: "Buddy".to_string(), + }; assert_eq!(d.speak(), "woof"); assert_eq!(d.greet(), "hello"); } -pub void run_tests() { +pub void run_tests() +{ test_trait_impl(); test_trait_with_requirement(); } diff --git a/mist-test/src/main.mist b/mist-test/src/main.mist index 548fa97..e5fd2e4 100644 --- a/mist-test/src/main.mist +++ b/mist-test/src/main.mist @@ -1,11 +1,11 @@ -void main() { +void main() +{ println!("running common tests..."); common::test_package(); common::expr_test::run_tests(); common::statement_test::run_tests(); common::decl_test::run_tests(); common::types_test::run_tests(); - println!("running items tests..."); items::function_test::run_tests(); items::struct_test::run_tests(); @@ -14,6 +14,5 @@ void main() { items::trait_test::run_tests(); items::impl_test::run_tests(); items::attribute_test::run_tests(); - println!("\x1b[32m\nAll tests passed!\x1b[0m"); }