diff --git a/.gitignore b/.gitignore index 7811c03..2112842 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ target # MSVC Windows builds of rustc generate these, which store debugging information *.pdb +*.exe # Generated by cargo mutants # Contains mutation testing data diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 5051a1d..03a5bd1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -17,10 +17,22 @@ pub enum TypePostfix { RefMut, } +#[derive(Debug, Clone, Serialize)] +pub enum Attribute { + /// #[test] + Path(Path), + + /// #[name = "value"] + NameValue { path: Path, value: Literal }, + + /// #[derive(Clone, Copy)] + List { path: Path, items: Vec }, +} + #[derive(Debug, Clone, Serialize)] pub enum TypeExprKind { - Path(StaticPath), - PathParams(StaticPath, Vec), + Path(Path), + PathParams(Path, Vec), Tuple(Vec), } @@ -28,7 +40,7 @@ pub enum TypeExprKind { pub struct TypeExpr(pub TypeExprKind, pub Vec); #[derive(Debug, Clone, Serialize)] -pub struct StaticPath(pub Vec); +pub struct Path(pub Vec); #[derive(Debug, Clone, Serialize)] pub enum BinaryOp { @@ -46,8 +58,12 @@ pub enum BinaryOp { } #[derive(Debug, Clone, Serialize)] -pub enum TopLevel { - Include(StaticPath), +pub struct TopLevel(pub TopLevelKind, pub Vec); + +#[derive(Debug, Clone, Serialize)] +pub enum TopLevelKind { + ModAttribute, + Include(Path), StructDecl { export: bool, name: String, @@ -137,15 +153,20 @@ pub struct ForStmt { #[derive(Debug, Clone, Serialize)] pub enum Expression { - Path(StaticPath), - IntLiteral(i64), - FloatLiteral(f64), - BoolLiteral(bool), - StringLiteral(String), - TupleLiteral(Vec), + Literal(Literal), + Path(Path), Fix { initial: Box, prefixes: Vec, postfixes: Vec, }, } + +#[derive(Debug, Clone, Serialize)] +pub enum Literal { + String(String), + Int(i64), + Float(f64), + Bool(bool), + Tuple(Vec), +} diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 406001c..59d1490 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -19,7 +19,25 @@ var_decl = { type_expr ~ mutable? ~ identifier } // ================= TOP LEVEL ================= -top_level = _{ import | function_decl | struct_decl } +attribute = { + "#" ~ "[" ~ meta ~ "]" +} + +mod_attribute = { + "#" ~ "!" ~ "[" ~ meta ~ "]" +} + +meta = { + static_path ~ ("=" ~ primary | "(" ~ meta_list? ~ ")")? +} + +meta_list = { + meta ~ ("," ~ meta)* ~ ","? +} + +attributes = { attribute* } + +top_level = { (attributes ~ (import | function_decl | struct_decl)) | mod_attribute } import = { sharp ~ "include" ~ "<" ~ static_path ~ ">" } @@ -90,8 +108,8 @@ macro_inner_v = { tuple = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } primary = { - integer - | float + float + | integer | boolean | string_lit | static_path @@ -110,7 +128,7 @@ postfix = { field_px = { "." ~ identifier } call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } macro_call_px = { "!" ~ "(" ~ macro_inner_v* ~ ")" } -struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ "}" } +struct_px = { "{" ~ (struct_field_decl ~ ("," ~ struct_field_decl)*)? ~ ","? ~ "}" } index_px = { "[" ~ expr ~ "]" } binary_px = { bin_op ~ expr } bin_op = { "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" } diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 036e8cc..e2014f4 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -18,8 +18,8 @@ pub fn parse(source: &str) -> Result, ParseError> { let mut statements = vec![]; for pair in pairs.next().unwrap().into_inner() { - if let Ok(stmt) = TopLevel::try_from(pair) { - statements.push(stmt); + if pair.as_rule() != Rule::EOI { + statements.push(TopLevel::from(pair)); } } @@ -67,7 +67,7 @@ impl From> for TypeExprKind { match rule { Rule::tuple_type => TypeExprKind::Tuple(inner.map(TypeExpr::from).collect()), Rule::path_type => { - let path = StaticPath::from(inner.next().unwrap()); + let path = Path::from(inner.next().unwrap()); let params = inner.map(TypeExpr::from).collect::>(); if params.len() == 0 { @@ -81,12 +81,10 @@ impl From> for TypeExprKind { } } -impl From> for StaticPath { +impl From> for Path { fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { match pair.as_rule() { - Rule::static_path => { - StaticPath(pair.into_inner().map(|i| i.as_str().to_string()).collect()) - } + Rule::static_path => Path(pair.into_inner().map(|i| i.as_str().to_string()).collect()), _ => unimplemented!("{pair:#?}"), } } @@ -120,14 +118,91 @@ impl From> for ParamList { } } -impl TryFrom> for TopLevel { - type Error = (); - fn try_from(pair: pest::iterators::Pair) -> Result { +impl From> for Attribute { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + match pair.as_rule() { + Rule::attribute => { + // unwrap #[ ... ] + Attribute::from(pair.into_inner().next().unwrap()) + } + + Rule::meta => { + let mut inner = pair.into_inner(); + + // first item is always the path + let path = Path::from(inner.next().unwrap()); + + // check what comes next + match inner.next() { + None => { + // #[path] + Attribute::Path(path) + } + + Some(next) => match next.as_rule() { + Rule::primary => { + // #[path = literal] + Attribute::NameValue { + path, + value: Literal::from(next), + } + } + + Rule::meta_list => { + // #[path(...)] + let items = next.into_inner().map(Attribute::from).collect(); + + Attribute::List { path, items } + } + + _ => unreachable!("unexpected rule in meta: {:?}", next.as_rule()), + }, + } + } + + Rule::meta_list => { + // This case usually won't be hit directly, + // but it's nice to keep it safe if reused + let items = pair.into_inner().map(Attribute::from).collect::>(); + + // NOTE: this shouldn't normally construct an Attribute alone + // but you can panic or wrap depending on your design + panic!("meta_list should be handled inside meta: {:?}", items); + } + + _ => unreachable!("unexpected rule: {:?}", pair.as_rule()), + } + } +} + +impl From> for TopLevel { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let mut inner = pair.into_inner(); + + let attributes = inner + .next() + .unwrap() + .into_inner() + .map(Attribute::from) + .collect::>(); + + TopLevel( + inner + .next() + .map(TopLevelKind::from) + .unwrap_or(TopLevelKind::ModAttribute), + attributes, + ) + } +} + +impl From> for TopLevelKind { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { let rule = pair.as_rule(); let mut inner = pair.into_inner(); match rule { - Rule::import => Ok(TopLevel::Include(StaticPath::from(inner.next().unwrap()))), + Rule::import => TopLevelKind::Include(Path::from(inner.next().unwrap())), Rule::function_decl => { let export = if let Some(first) = inner.peek() { @@ -152,13 +227,13 @@ impl TryFrom> for TopLevel { let body = Block::from(inner.next().unwrap()); - Ok(TopLevel::FunctionDecl { + TopLevelKind::FunctionDecl { export, name, params, return_type, body, - }) + } } Rule::struct_decl => { @@ -176,14 +251,12 @@ impl TryFrom> for TopLevel { let fields_pair = inner.next().unwrap(); let fields = FieldList::from(fields_pair); - Ok(TopLevel::StructDecl { + TopLevelKind::StructDecl { export, name, fields, - }) + } } - - Rule::EOI => Err(()), _ => unimplemented!("{rule:#?}"), } } @@ -262,6 +335,23 @@ impl From> for Statement { } } +impl From> for Literal { + fn from(pair: pest::iterators::Pair<'_, Rule>) -> Self { + let rule = pair.as_rule(); + let mut inner = pair.clone().into_inner(); + + match rule { + Rule::primary => Self::from(inner.next().unwrap()), + Rule::integer => Literal::Int(pair.as_str().parse::().unwrap()), + Rule::float => Literal::Float(pair.as_str().parse::().unwrap()), + Rule::boolean => Literal::Bool(pair.as_str().parse::().unwrap()), + Rule::string_lit => Literal::String(inner.as_str().to_string()), + Rule::tuple => Literal::Tuple(inner.map(Expression::from).collect()), + _ => unimplemented!("{rule:#?}"), + } + } +} + impl From> for Expression { fn from(pair: pest::iterators::Pair) -> Self { let rule = pair.as_rule(); @@ -292,12 +382,20 @@ impl From> for Expression { } } Rule::primary => Expression::from(inner.next().unwrap()), - Rule::static_path => Expression::Path(StaticPath::from(pair)), - Rule::integer => Expression::IntLiteral(pair.as_str().parse::().unwrap()), - Rule::float => Expression::FloatLiteral(pair.as_str().parse::().unwrap()), - Rule::boolean => Expression::BoolLiteral(pair.as_str().parse::().unwrap()), - Rule::string_lit => Expression::StringLiteral(inner.as_str().to_string()), - Rule::tuple => Expression::TupleLiteral(inner.map(Expression::from).collect()), + Rule::static_path => Expression::Path(Path::from(pair)), + Rule::integer => { + Expression::Literal(Literal::Int(pair.as_str().parse::().unwrap())) + } + Rule::float => { + Expression::Literal(Literal::Float(pair.as_str().parse::().unwrap())) + } + Rule::boolean => { + Expression::Literal(Literal::Bool(pair.as_str().parse::().unwrap())) + } + Rule::string_lit => Expression::Literal(Literal::String(inner.as_str().to_string())), + Rule::tuple => { + Expression::Literal(Literal::Tuple(inner.map(Expression::from).collect())) + } _ => unimplemented!("{rule:#?}"), } } diff --git a/src/codegen.rs b/src/codegen.rs index db79b79..b4c4a35 100644 --- a/src/codegen.rs +++ b/src/codegen.rs @@ -1,6 +1,7 @@ use parser::ast::{ - BinaryOp, Block, Expression, IfStmt, Postfix, Prefix, Statement, StaticPath, TopLevel, - TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, VarDeclStmt, WhileStmt, + Attribute, BinaryOp, Block, Expression, IfStmt, Literal, Path, Postfix, Prefix, Statement, + TopLevel, TopLevelKind, TypeExpr, TypeExprKind, TypePostfix, VarAssignStmt, VarDecl, + VarDeclStmt, WhileStmt, }; // --------------------------------------------------------------------------- @@ -105,15 +106,14 @@ impl GetRust for TypeExprKind { } } -impl GetRust for Expression { +impl GetRust for Literal { fn get_rust(&self) -> String { match self { - Expression::Path(path) => path.get_rust(), - Expression::IntLiteral(n) => n.to_string(), - Expression::FloatLiteral(n) => n.to_string(), - Expression::BoolLiteral(b) => b.to_string(), - Expression::StringLiteral(s) => format!("\"{s}\""), - Expression::TupleLiteral(t) => { + Self::Int(n) => n.to_string(), + Self::Float(n) => format!("{n:?}"), + Self::Bool(b) => b.to_string(), + Self::String(s) => format!("\"{s}\""), + Self::Tuple(t) => { format!( "({})", t.iter() @@ -122,6 +122,15 @@ impl GetRust for Expression { .join(", ") ) } + } + } +} + +impl GetRust for Expression { + fn get_rust(&self) -> String { + match self { + Expression::Path(path) => path.get_rust(), + Expression::Literal(literal) => literal.get_rust(), Expression::Fix { initial, prefixes, @@ -219,12 +228,54 @@ impl ToRust for Block { impl ToRust for TopLevel { fn to_rust(&self, cg: &mut RustCodegen) { + match &self.0 { + TopLevelKind::ModAttribute => { + for attr in &self.1 { + cg.addln(&format!("#![{}]", attr.get_rust())); + } + } + _ => { + for attr in &self.1 { + cg.addln(&format!("#[{}]", attr.get_rust())); + } + } + } + + self.0.to_rust(cg); + } +} + +impl GetRust for Attribute { + fn get_rust(&self) -> String { match self { - TopLevel::Include(path) => { + Self::Path(path) => path.get_rust(), + Self::NameValue { path, value } => { + format!("{} = {}", path.get_rust(), value.get_rust()) + } + Self::List { path, items } => { + format!( + "{}({})", + path.get_rust(), + items + .iter() + .map(Attribute::get_rust) + .collect::>() + .join(", ") + ) + } + } + } +} + +impl ToRust for TopLevelKind { + fn to_rust(&self, cg: &mut RustCodegen) { + match self { + Self::ModAttribute => {} + Self::Include(path) => { cg.addln(&format!("use {};", path.get_rust())); } - TopLevel::StructDecl { + Self::StructDecl { export, name, fields, @@ -242,7 +293,7 @@ impl ToRust for TopLevel { cg.addln("}\n"); } - TopLevel::FunctionDecl { + Self::FunctionDecl { export, name, params, @@ -359,7 +410,7 @@ impl GetRust for VarDecl { } } -impl GetRust for StaticPath { +impl GetRust for Path { fn get_rust(&self) -> String { self.0.join("::") } @@ -374,7 +425,7 @@ impl GetRust for TypePostfix { } } -pub fn get_static_type_path(path: &StaticPath) -> String { +pub fn get_static_type_path(path: &Path) -> String { let rust_path = path.get_rust(); if rust_path == "void" { diff --git a/test/src/main.mist b/test/src/main.mist index f5c3828..40ba951 100644 --- a/test/src/main.mist +++ b/test/src/main.mist @@ -1,3 +1,13 @@ +#![allow(unused_mut)] + +#[derive(Debug)] +public struct ApiCall { + i32 x, + i32 y, +} + void main() { - (i32, i32, str*) my_tuple = (10, 20, "hello, world"); -} \ No newline at end of file + ApiCall call = ApiCall { x: 10, y: 12 }; + + println!("{call:?}"); +}