Merge pull request #91 from mist-go/mist-fmt

Mist fmt
This commit is contained in:
2026-06-26 02:36:03 +02:00
committed by GitHub
20 changed files with 1499 additions and 185 deletions
+53 -4
View File
@@ -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<Item = CompletionItem> {
@@ -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<Option<Vec<TextEdit>>> {
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<CompletionItem>) {
+11
View File
@@ -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<String, String> {
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)
}
+177
View File
@@ -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::<String>()
));
}
Postfix::FieldAccess(field, generics) => {
cg.add(&format!(
".{}{}",
field.get_mist(),
generics
.iter()
.map(|v| format!("::{}", v.get_mist()))
.collect::<String>()
));
}
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("--"),
}
}
}
+412
View File
@@ -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<TopLevel>) -> 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<Statement>) {
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::<Vec<String>>()
.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::<Vec<_>>()
.join(", ")
),
Self::StaticFn(types, return_type) => {
if let Some(return_type) = return_type {
format!(
"fn({}) -> {}",
types
.iter()
.map(|t| t.get_mist())
.collect::<Vec<_>>()
.join(", "),
return_type.get_mist()
)
} else {
format!(
"fn({})",
types
.iter()
.map(|t| t.get_mist())
.collect::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.join(" + ")
)
} else {
String::new()
})
}
}
}
}
impl<T: GetMist> GenMist for T {
fn gen_mist(&self, _: &mut Context, cg: &mut MistCodegen) {
cg.add(&self.get_mist());
}
}
impl<T: GenMist> GenMist for Spanned<T> {
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(")");
}
}
}
}
+157
View File
@@ -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"),
}
}
}
+337
View File
@@ -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::<Vec<_>>()
.join(", ")
),
Self::Tuple(id, t) => format!(
"{}({})",
id.get_mist(),
t.iter()
.map(TypeExpr::get_mist)
.collect::<Vec<_>>()
.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::<Vec<_>>()
.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("}");
}
}
}
}
+1
View File
@@ -1,5 +1,6 @@
pub mod class_decl;
pub mod expr;
pub mod fmt;
pub mod statement;
pub mod top_level;
+1 -1
View File
@@ -450,7 +450,7 @@ loop_stmt = {
pattern_list = { pattern ~ ("|" ~ pattern)* }
match_item = {
pattern_list ~ "=>" ~ (statement | (expr ~ ","))
pattern_list ~ "=>" ~ (block | (expr ~ ","))
}
match_stmt = {
+22 -14
View File
@@ -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();
+46 -21
View File
@@ -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,15 +114,24 @@ 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() {
pub void test_range_expr()
{
let r = 0 .. 5;
let mut count = 0;
for i in r {
@@ -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();
+48 -36
View File
@@ -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 {
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,9 +75,9 @@ 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 {
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();
+2 -2
View File
@@ -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
+11 -5
View File
@@ -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();
}
+29 -17
View File
@@ -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");
}
+53 -21
View File
@@ -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 {
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);
}
let r = Shape::Rect { w: 3, h: 4 };
Shape::Rect {..} => {
assert_eq!(0, 1);
}
}
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();
+31 -15
View File
@@ -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);
+40 -16
View File
@@ -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();
+34 -12
View File
@@ -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 };
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();
+26 -12
View File
@@ -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();
}
+2 -3
View File
@@ -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");
}