diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4b01aef..42c462e 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -142,7 +142,12 @@ impl GoCodegen { self.indent -= 1; self.add_indentedln("}\n"); } - Statement::VarDecl { kind, name, init } => { + Statement::VarDecl { + kind, + name, + init, + type_, + } => { let go_kind = match kind { VarKind::Let | VarKind::Const => "var", VarKind::Var => "var", @@ -151,7 +156,14 @@ impl GoCodegen { .as_ref() .map(|e| format!(" = {}", self.generate_expression(e))) .unwrap_or_else(|| "".to_string()); - self.add_indentedln(&format!("{} {}{};\n", go_kind, name, init_expr)); + let type_expr = type_ + .clone() + .map(|t| self.translate_type(&t)) + .unwrap_or_default(); + self.add_indentedln(&format!( + "{} {} {}{};\n", + go_kind, name, type_expr, init_expr + )); } Statement::VarAssign { target, value } => { self.add_indentedln(&format!( diff --git a/parser/src/ast.rs b/parser/src/ast.rs index 0313478..3c0f3d1 100644 --- a/parser/src/ast.rs +++ b/parser/src/ast.rs @@ -67,6 +67,7 @@ pub enum Statement { kind: VarKind, name: String, init: Option, + type_: Option, }, VarAssign { diff --git a/parser/src/grammar.pest b/parser/src/grammar.pest index 4cc1a94..058b782 100644 --- a/parser/src/grammar.pest +++ b/parser/src/grammar.pest @@ -17,19 +17,19 @@ top_level = _{ import | function_decl | struct_decl | package } package = { "package" ~ identifier ~ ";" } -export = { "export" } +export = { "public" } import = { "import" ~ string_lit ~ ";" } param_list = { param ~ ("," ~ param)* ~ ","? } -param = { export? ~ identifier ~ ":" ~ type_expr } +param = { export? ~ type_expr ~ identifier } struct_decl = { export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" } function_decl = { - export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block + export? ~ type_expr ~ identifier ~ "(" ~ param_list? ~ ")" ~ block } // ================= STATEMENTS ================= @@ -63,10 +63,10 @@ continue_stmt = { "continue" ~ ";" } // --- variable declarations --- -var_decl_kind = { "let" | "const" | "var" } +// var_decl_kind = { "let" | "const" | "var" } var_decl = { - var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" + type_expr ~ identifier ~ ("=" ~ expr)? ~ ";" } var_assign = { diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 2dbe131..bd9c374 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -4,6 +4,8 @@ use pest::Parser; use pest_derive::Parser; pub mod ast; +pub mod script_parser; + use ast::*; #[derive(Parser)] @@ -19,7 +21,7 @@ pub fn parse(source: &str) -> Result, ParseError> { let mut statements = vec![]; for pair in pairs.next().unwrap().into_inner() { - if let Some(stmt) = TopLevel::from_pair(pair) { + if let Ok(stmt) = TopLevel::try_from(pair) { statements.push(stmt); } } @@ -27,21 +29,27 @@ pub fn parse(source: &str) -> Result, ParseError> { Ok(statements) } -impl TypeExpr { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { - match pair.as_rule() { +impl TryFrom> for TypeExpr { + type Error = (); + + fn try_from(pair: pest::iterators::Pair<'_, Rule>) -> Result { + if pair.as_str() == "void" { + return Err(()); + } + + Ok(match pair.as_rule() { Rule::type_expr => { let inner = pair.into_inner().next().unwrap(); - TypeExpr::from_pair(inner) + TypeExpr::try_from(inner)? } Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), - _ => unimplemented!("TypeExpr parsing not implemented yet"), - } + _ => unimplemented!("{pair:#?}"), + }) } } -impl ParamList { - pub fn from_pair(export_allowed: bool, pair: pest::iterators::Pair) -> Self { +impl From<(bool, pest::iterators::Pair<'_, Rule>)> for ParamList { + fn from((export_allowed, pair): (bool, pest::iterators::Pair)) -> Self { let params = pair .into_inner() .map(|p| { @@ -53,8 +61,8 @@ impl ParamList { } else { false }; + let param_type = TypeExpr::try_from(param_inner.next().unwrap()).unwrap(); let param_name = param_inner.next().unwrap().as_str().to_string(); - let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); (param_name, (export, param_type)) }) .collect(); @@ -63,16 +71,17 @@ impl ParamList { } } -impl TopLevel { - pub fn from_pair(pair: pest::iterators::Pair) -> Option { +impl TryFrom> for TopLevel { + type Error = (); + fn try_from(pair: pest::iterators::Pair) -> Result { match pair.as_rule() { Rule::import => { let path = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Import(path)) + Ok(TopLevel::Import(path)) } Rule::package => { let name = pair.into_inner().next().unwrap().as_str().to_string(); - Some(TopLevel::Package(name)) + Ok(TopLevel::Package(name)) } Rule::function_decl => { let mut inner = pair.into_inner(); @@ -87,15 +96,10 @@ impl TopLevel { } else { false }; - let name = inner.next().unwrap().as_str().to_string(); - let params = if inner.peek().unwrap().as_rule() == Rule::param_list { - ParamList::from_pair(false, inner.next().unwrap()) - } else { - ParamList(HashMap::new()) - }; + let return_type = if let Some(next) = inner.peek() { if next.as_rule() == Rule::type_expr { - Some(TypeExpr::from_pair(inner.next().unwrap())) + TypeExpr::try_from(inner.next().unwrap()).ok() } else { None } @@ -103,9 +107,16 @@ impl TopLevel { None }; - let body = Block::from_pair(inner.next().unwrap()); + let name = inner.next().unwrap().as_str().to_string(); + let params = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from((false, inner.next().unwrap())) + } else { + ParamList(HashMap::new()) + }; - Some(TopLevel::FunctionDecl { + let body = Block::from(inner.next().unwrap()); + + Ok(TopLevel::FunctionDecl { export, name, params, @@ -128,30 +139,30 @@ impl TopLevel { }; let name = inner.next().unwrap().as_str().to_string(); let fields_pair = inner.next().unwrap(); - let fields = ParamList::from_pair(true, fields_pair); + let fields = ParamList::from((true, fields_pair)); - Some(TopLevel::StructDecl { + Ok(TopLevel::StructDecl { export, name, fields, }) } - Rule::EOI => None, + Rule::EOI => Err(()), _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), } } } -impl Block { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Block { + fn from(pair: pest::iterators::Pair) -> Self { let statements = pair .into_inner() .flat_map(|pair| { if pair.as_rule() == Rule::statement_list { - pair.into_inner().map(Statement::from_pair).collect() + pair.into_inner().map(Statement::from).collect() } else { - vec![Statement::from_pair(pair)] + vec![Statement::from(pair)] } }) .collect(); @@ -159,47 +170,40 @@ impl Block { } } -impl Statement { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Statement { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { Rule::statement => { let inner = pair.into_inner().next().unwrap(); - Statement::from_pair(inner) + Statement::from(inner) } Rule::expr_stmt => { let expr_pair = pair.into_inner().next().unwrap(); - Statement::Expression(Expression::from_pair(expr_pair)) + Statement::Expression(Expression::from(expr_pair)) } - Rule::block => Statement::Block(Block::from_pair(pair.into_inner().next().unwrap())), + Rule::block => Statement::Block(Block::from(pair.into_inner().next().unwrap())), Rule::var_decl => { let mut inner = pair.into_inner(); - let kind_pair = inner.next().unwrap(); // let/const/var - let name_pair = inner.next().unwrap(); // identifier - - let init = inner.next().map(Expression::from_pair); - - let kind = match kind_pair.as_str() { - "let" => VarKind::Let, - "const" => VarKind::Const, - "var" => VarKind::Var, - _ => unreachable!(), - }; + let type_ = inner.next().map(TypeExpr::try_from).unwrap().ok(); + let name = inner.next().unwrap().as_str().to_string(); + let init = inner.next().map(Expression::from); Statement::VarDecl { - kind, - name: name_pair.as_str().to_string(), + kind: VarKind::Var, + name: name.as_str().to_string(), init, + type_, } } Rule::return_stmt => { let mut inner = pair.into_inner(); - let expr = inner.next().map(Expression::from_pair); + let expr = inner.next().map(Expression::from); Statement::Return(expr) } @@ -211,10 +215,10 @@ impl Statement { Rule::if_stmt => { let mut inner = pair.into_inner(); - let condition = Expression::from_pair(inner.next().unwrap()); - let then_branch = Statement::from_pair(inner.next().unwrap()); + let condition = Expression::from(inner.next().unwrap()); + let then_branch = Statement::from(inner.next().unwrap()); - let else_branch = inner.next().map(Statement::from_pair); + let else_branch = inner.next().map(Statement::from); Statement::If { condition, @@ -226,8 +230,8 @@ impl Statement { Rule::while_stmt => { let mut inner = pair.into_inner(); - let condition = Expression::from_pair(inner.next().unwrap()); - let body = Statement::from_pair(inner.next().unwrap()); + let condition = Expression::from(inner.next().unwrap()); + let body = Statement::from(inner.next().unwrap()); Statement::While { condition, @@ -254,7 +258,7 @@ impl Statement { let name = it.next().unwrap().as_str().to_string(); let init_expr = it .next() - .map(|e| Expression::from_pair(e.into_inner().next().unwrap())); + .map(|e| Expression::from(e.into_inner().next().unwrap())); (kind, name, init_expr) } @@ -265,9 +269,9 @@ impl Statement { }) .unwrap(); - let condition = inner.next().map(Expression::from_pair); + let condition = inner.next().map(Expression::from); let update = inner.next().map(parse_var_assign_no_semicolon); - let body = Statement::from_pair(inner.next().unwrap()); + let body = Statement::from(inner.next().unwrap()); Statement::For { init, @@ -279,8 +283,8 @@ impl Statement { Rule::var_assign => { let mut inner = pair.into_inner(); - let target = Expression::from_pair(inner.next().unwrap()); - let value = Expression::from_pair(inner.next().unwrap()); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); Statement::VarAssign { target, value } } @@ -293,23 +297,23 @@ impl Statement { } } -impl Expression { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Expression { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { Rule::expr => { let mut inner = pair.into_inner(); - let exp = Expression::from_pair(inner.next().unwrap()); + let exp = Expression::from(inner.next().unwrap()); if inner.len() > 0 { Expression::Postfix { initial: Box::new(exp), - postfixes: inner.map(|p| Postfix::from_pair(p)).collect(), + postfixes: inner.map(|p| Postfix::from(p)).collect(), } } else { exp } } - Rule::primary => Expression::from_pair(pair.into_inner().next().unwrap()), + Rule::primary => Expression::from(pair.into_inner().next().unwrap()), Rule::identifier => Expression::Identifier(pair.as_str().to_string()), Rule::integer => { let value = pair.as_str().parse::().unwrap(); @@ -336,21 +340,19 @@ impl Expression { } } -impl Postfix { - pub fn from_pair(pair: pest::iterators::Pair) -> Self { +impl From> for Postfix { + fn from(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::postfix => Postfix::from_pair(pair.into_inner().next().unwrap()), + Rule::postfix => Postfix::from(pair.into_inner().next().unwrap()), Rule::field_px => { let field_name = pair.into_inner().next().unwrap().as_str().to_string(); Postfix::FieldAccess(field_name) } - Rule::call_px => Postfix::Call(pair.into_inner().map(Expression::from_pair).collect()), + Rule::call_px => Postfix::Call(pair.into_inner().map(Expression::from).collect()), - Rule::index_px => { - Postfix::Index(Expression::from_pair(pair.into_inner().next().unwrap())) - } + Rule::index_px => Postfix::Index(Expression::from(pair.into_inner().next().unwrap())), Rule::binary_px => { let mut inner = pair.into_inner(); @@ -372,7 +374,7 @@ impl Postfix { unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) } }; - Postfix::Binary(op, Expression::from_pair(inner.next().unwrap())) + Postfix::Binary(op, Expression::from(inner.next().unwrap())) } _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), @@ -382,8 +384,8 @@ impl Postfix { fn parse_var_assign_no_semicolon(pair: pest::iterators::Pair) -> Statement { let mut inner = pair.into_inner(); - let target = Expression::from_pair(inner.next().unwrap()); - let value = Expression::from_pair(inner.next().unwrap()); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); Statement::VarAssign { target, value } } diff --git a/parser/src/script_grammar.pest b/parser/src/script_grammar.pest new file mode 100644 index 0000000..4cc1a94 --- /dev/null +++ b/parser/src/script_grammar.pest @@ -0,0 +1,120 @@ +WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } + +program = { SOI ~ top_level* ~ EOI } + +// primitives +identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } +integer = @{ ASCII_DIGIT+ } +float = @{ ASCII_DIGIT+ ~ "." ~ ASCII_DIGIT+ } +boolean = { "true" | "false" } +string_lit = { "\"" ~ inner_str ~ "\"" } +inner_str = @{ (!"\"" ~ ANY)* } + +// ================= TOP LEVEL ================= + +top_level = _{ import | function_decl | struct_decl | package } + +package = { "package" ~ identifier ~ ";" } + +export = { "export" } + +import = { "import" ~ string_lit ~ ";" } + +param_list = { param ~ ("," ~ param)* ~ ","? } +param = { export? ~ identifier ~ ":" ~ type_expr } + +struct_decl = { + export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" +} + +function_decl = { + export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block +} + +// ================= STATEMENTS ================= + +block = { "{" ~ statement_list ~ "}" } + +statement_list = { statement* } + +statement = _{ + expr_stmt + | var_decl + | var_assign + | if_stmt + | while_stmt + | for_stmt + | return_stmt + | break_stmt + | continue_stmt + | block +} + +// --- basic statements --- + +expr_stmt = { expr ~ ";" } + +return_stmt = { "return" ~ expr? ~ ";" } + +break_stmt = { "break" ~ ";" } + +continue_stmt = { "continue" ~ ";" } + +// --- variable declarations --- + +var_decl_kind = { "let" | "const" | "var" } + +var_decl = { + var_decl_kind ~ identifier ~ ("=" ~ expr)? ~ ";" +} + +var_assign = { + expr ~ "=" ~ expr ~ ";" +} + +// --- control flow --- + +if_stmt = { + "if" ~ "(" ~ expr ~ ")" ~ statement ~ ("else" ~ statement)? +} + +while_stmt = { + "while" ~ "(" ~ expr ~ ")" ~ statement +} + +for_stmt = { + "for" ~ "(" ~ var_decl ~ expr ~ ";" ~ var_assign_no_semicolon ~ ")" ~ statement +} + +var_assign_no_semicolon = { + expr ~ "=" ~ expr +} + +// ================= EXPRESSIONS ================= +type_expr = { identifier } + +primary = { + integer + | float + | boolean + | string_lit + | identifier +} + +postfix = { + field_px + | call_px + | index_px + | binary_px +} + +field_px = { "." ~ identifier } +call_px = { "(" ~ (expr ~ ("," ~ expr)*)? ~ ")" } +index_px = { "[" ~ expr ~ "]" } +binary_px = { bin_op ~ expr } +bin_op = { "+" | "-" | "*" | "/" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" } + +expr = { + primary ~ postfix* +} diff --git a/parser/src/script_parser.rs b/parser/src/script_parser.rs new file mode 100644 index 0000000..cff3b6f --- /dev/null +++ b/parser/src/script_parser.rs @@ -0,0 +1,388 @@ +use std::collections::HashMap; + +use pest::Parser; +use pest_derive::Parser; + +use crate::ast::*; + +#[derive(Parser)] +#[grammar = "./src/script_grammar.pest"] +pub struct MistScriptParser; + +// convenience alias for pest errors +pub type ParseError = pest::error::Error; + +pub fn parse(source: &str) -> Result, ParseError> { + let mut pairs = MistScriptParser::parse(Rule::program, source)?; + + let mut statements = vec![]; + + for pair in pairs.next().unwrap().into_inner() { + if let Ok(stmt) = TopLevel::try_from(pair) { + statements.push(stmt); + } + } + + Ok(statements) +} + +impl From> for TypeExpr { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::type_expr => { + let inner = pair.into_inner().next().unwrap(); + TypeExpr::from(inner) + } + Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), + _ => unimplemented!("TypeExpr parsing not implemented yet"), + } + } +} + +impl From<(bool, pest::iterators::Pair<'_, Rule>)> for ParamList { + fn from((export_allowed, pair): (bool, pest::iterators::Pair)) -> Self { + let params = pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let export = + if export_allowed && param_inner.peek().unwrap().as_rule() == Rule::export { + param_inner.next().unwrap(); + true + } else { + false + }; + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from(param_inner.next().unwrap()); + (param_name, (export, param_type)) + }) + .collect(); + + ParamList(params) + } +} + +impl TryFrom> for TopLevel { + type Error = (); + fn try_from(pair: pest::iterators::Pair) -> Result { + match pair.as_rule() { + Rule::import => { + let path = pair.into_inner().next().unwrap().as_str().to_string(); + Ok(TopLevel::Import(path)) + } + Rule::package => { + let name = pair.into_inner().next().unwrap().as_str().to_string(); + Ok(TopLevel::Package(name)) + } + Rule::function_decl => { + let mut inner = pair.into_inner(); + + let export = if let Some(first) = inner.peek() { + if first.as_rule() == Rule::export { + inner.next(); + true + } else { + false + } + } else { + false + }; + let name = inner.next().unwrap().as_str().to_string(); + let params = if inner.peek().unwrap().as_rule() == Rule::param_list { + ParamList::from((false, inner.next().unwrap())) + } else { + ParamList(HashMap::new()) + }; + let return_type = if let Some(next) = inner.peek() { + if next.as_rule() == Rule::type_expr { + Some(TypeExpr::from(inner.next().unwrap())) + } else { + None + } + } else { + None + }; + + let body = Block::from(inner.next().unwrap()); + + Ok(TopLevel::FunctionDecl { + export, + name, + params, + return_type, + body, + }) + } + + Rule::struct_decl => { + let mut inner = pair.into_inner(); + let export = if let Some(first) = inner.peek() { + if first.as_rule() == Rule::export { + inner.next(); + true + } else { + false + } + } else { + false + }; + let name = inner.next().unwrap().as_str().to_string(); + let fields_pair = inner.next().unwrap(); + let fields = ParamList::from((true, fields_pair)); + + Ok(TopLevel::StructDecl { + export, + name, + fields, + }) + } + + Rule::EOI => Err(()), + _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +impl From> for Block { + fn from(pair: pest::iterators::Pair) -> Self { + let statements = pair + .into_inner() + .flat_map(|pair| { + if pair.as_rule() == Rule::statement_list { + pair.into_inner().map(Statement::from).collect() + } else { + vec![Statement::from(pair)] + } + }) + .collect(); + Block(statements) + } +} + +impl From> for Statement { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::statement => { + let inner = pair.into_inner().next().unwrap(); + Statement::from(inner) + } + + Rule::expr_stmt => { + let expr_pair = pair.into_inner().next().unwrap(); + Statement::Expression(Expression::from(expr_pair)) + } + + Rule::block => Statement::Block(Block::from(pair.into_inner().next().unwrap())), + + Rule::var_decl => { + let mut inner = pair.into_inner(); + + let kind_pair = inner.next().unwrap(); // let/const/var + let name_pair = inner.next().unwrap(); // identifier + + let init = inner.next().map(Expression::from); + + let kind = match kind_pair.as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, + _ => unreachable!(), + }; + + Statement::VarDecl { + kind, + name: name_pair.as_str().to_string(), + init, + type_: None, + } + } + + Rule::return_stmt => { + let mut inner = pair.into_inner(); + + let expr = inner.next().map(Expression::from); + + Statement::Return(expr) + } + + Rule::break_stmt => Statement::Break, + + Rule::continue_stmt => Statement::Continue, + + Rule::if_stmt => { + let mut inner = pair.into_inner(); + + let condition = Expression::from(inner.next().unwrap()); + let then_branch = Statement::from(inner.next().unwrap()); + + let else_branch = inner.next().map(Statement::from); + + Statement::If { + condition, + then_branch: Box::new(then_branch), + else_branch: else_branch.map(Box::new), + } + } + + Rule::while_stmt => { + let mut inner = pair.into_inner(); + + let condition = Expression::from(inner.next().unwrap()); + let body = Statement::from(inner.next().unwrap()); + + Statement::While { + condition, + body: Box::new(body), + } + } + + Rule::for_stmt => { + let mut inner = pair.into_inner(); + + let init = inner + .next() + .map(|p| match p.as_rule() { + Rule::var_decl => { + let mut it = p.into_inner(); + + let kind = match it.next().unwrap().as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, + _ => unreachable!(), + }; + + let name = it.next().unwrap().as_str().to_string(); + let init_expr = it + .next() + .map(|e| Expression::from(e.into_inner().next().unwrap())); + + (kind, name, init_expr) + } + _ => unimplemented!( + "For loop init parsing not implemented yet: {:?}", + p.as_rule() + ), + }) + .unwrap(); + + let condition = inner.next().map(Expression::from); + let update = inner.next().map(parse_var_assign_no_semicolon); + let body = Statement::from(inner.next().unwrap()); + + Statement::For { + init, + condition, + update: update.map(Box::new), + body: Box::new(body), + } + } + + Rule::var_assign => { + let mut inner = pair.into_inner(); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); + + Statement::VarAssign { target, value } + } + + _ => unimplemented!( + "Statement parsing not implemented yet: {:?}", + pair.as_rule() + ), + } + } +} + +impl From> for Expression { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::expr => { + let mut inner = pair.into_inner(); + let exp = Expression::from(inner.next().unwrap()); + + if inner.len() > 0 { + Expression::Postfix { + initial: Box::new(exp), + postfixes: inner.map(|p| Postfix::from(p)).collect(), + } + } else { + exp + } + } + Rule::primary => Expression::from(pair.into_inner().next().unwrap()), + Rule::identifier => Expression::Identifier(pair.as_str().to_string()), + Rule::integer => { + let value = pair.as_str().parse::().unwrap(); + Expression::IntLiteral(value) + } + Rule::float => { + let value = pair.as_str().parse::().unwrap(); + Expression::FloatLiteral(value) + } + Rule::boolean => { + let value = pair.as_str().parse::().unwrap(); + Expression::BoolLiteral(value) + } + Rule::string_lit => { + let inner_str = pair.into_inner().next().unwrap().as_str(); + Expression::StringLiteral(inner_str.to_string()) + } + + _ => unimplemented!( + "Expression parsing not implemented yet {:?}", + pair.as_rule() + ), + } + } +} + +impl From> for Postfix { + fn from(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::postfix => Postfix::from(pair.into_inner().next().unwrap()), + + Rule::field_px => { + let field_name = pair.into_inner().next().unwrap().as_str().to_string(); + Postfix::FieldAccess(field_name) + } + + Rule::call_px => Postfix::Call(pair.into_inner().map(Expression::from).collect()), + + Rule::index_px => Postfix::Index(Expression::from(pair.into_inner().next().unwrap())), + + Rule::binary_px => { + let mut inner = pair.into_inner(); + let op_pair = inner.next().unwrap(); + let op = match op_pair.as_str() { + "+" => BinaryOp::Plus, + "-" => BinaryOp::Minus, + "*" => BinaryOp::Multiply, + "/" => BinaryOp::Divide, + "%" => BinaryOp::Modulo, + "==" => BinaryOp::Equal, + "!=" => BinaryOp::NotEqual, + "<" => BinaryOp::LessThan, + ">" => BinaryOp::GreaterThan, + "<=" => BinaryOp::LessThanOrEqual, + ">=" => BinaryOp::GreaterThanOrEqual, + + _ => { + unimplemented!("Binary operator not implemented yet: {}", op_pair.as_str()) + } + }; + Postfix::Binary(op, Expression::from(inner.next().unwrap())) + } + + _ => unimplemented!("Postfix parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +fn parse_var_assign_no_semicolon(pair: pest::iterators::Pair) -> Statement { + let mut inner = pair.into_inner(); + let target = Expression::from(inner.next().unwrap()); + let value = Expression::from(inner.next().unwrap()); + + Statement::VarAssign { target, value } +} diff --git a/semantic/src/lib.rs b/semantic/src/lib.rs index 9af6dfa..340e2ea 100644 --- a/semantic/src/lib.rs +++ b/semantic/src/lib.rs @@ -26,11 +26,6 @@ pub fn walk_ast(top_scope: Arc, tl: &mut Vec) { *name = rf.name.clone(); } - match &*rf.var_type { - hir::TypeRef::Function(f) => walk_param_list(&f.params, params), - _ => unimplemented!(), - } - let scope = LocalScope::new(top_scope.clone()); scope.with_params(params); diff --git a/semantic/src/scope.rs b/semantic/src/scope.rs index 4ad0655..78e88dc 100644 --- a/semantic/src/scope.rs +++ b/semantic/src/scope.rs @@ -38,6 +38,10 @@ impl Scope { Scope::Local(l) => l.parent.next_var_idx(), } } + + pub fn get_name(&self, export: bool) -> String { + format!("{}{}", if export { 'V' } else { 'v' }, self.next_var_idx()) + } } #[derive(Debug)] @@ -55,15 +59,16 @@ impl LocalScope { } pub fn get_reference(&self, name: &String) -> Option> { - let rf = self - .variables + self.variables .lock() .unwrap() .get(name) .cloned() - .or_else(|| self.parent.get_reference(name)); + .or_else(|| self.parent.get_reference(name)) + } - match rf { + pub fn get_type_reference(&self, name: &String) -> Option> { + match self.get_reference(name) { Some(v) => Some(v), None => { let var_ref = Arc::new(VarRef { @@ -82,29 +87,53 @@ impl LocalScope { } } + pub fn with_statement(self: &Arc, statement: &mut Statement) { + match statement { + Statement::Block(b) => self.clone().with_block(b), + Statement::VarDecl { name, init, .. } => { + if let Some(init) = init { + let var_type = self.get_type_from_expr(init).unwrap(); + + let var_name = name.clone(); + + *name = self.parent.get_name(false); + + self.variables.lock().unwrap().insert( + var_name, + Arc::new(VarRef { + export: false, + name: name.clone(), + var_type, + }), + ); + } + } + Statement::Expression(e) => { + self.get_type_from_expr(e); + } + Statement::If { + condition, + then_branch, + else_branch, + } => { + self.get_type_from_expr(condition); + self.clone().with_statement(then_branch); + if let Some(else_branch) = else_branch { + self.clone().with_statement(else_branch); + } + } + Statement::Return(expr) => { + if let Some(expr) = expr { + self.get_type_from_expr(expr); + } + } + _ => {} + } + } + pub fn with_block(self: &Arc, block: &mut parser::ast::Block) { for statement in &mut block.0 { - match statement { - Statement::Block(b) => self.clone().with_block(b), - Statement::VarDecl { name, init, .. } => { - if let Some(init) = init { - let var_type = self.get_type_from_expr(init).unwrap(); - - self.variables.lock().unwrap().insert( - name.clone(), - Arc::new(VarRef { - export: false, - name: name.to_string(), - var_type, - }), - ); - } - } - Statement::Expression(e) => { - self.get_type_from_expr(e); - } - _ => {} - } + self.with_statement(statement); } } @@ -130,13 +159,33 @@ impl LocalScope { } _ => unimplemented!(), }, - Postfix::Call(_args) => match &*current_type { + Postfix::Call(args) => match &*current_type { TypeRef::Function(s) => { - // TODO: arg checking + for arg in args { + self.get_type_from_expr(arg); + } current_type = s.return_type.clone()?; } _ => unimplemented!(), }, + Postfix::Binary(op, right) => match op { + parser::ast::BinaryOp::Equal + | parser::ast::BinaryOp::NotEqual + | parser::ast::BinaryOp::GreaterThan + | parser::ast::BinaryOp::LessThan + | parser::ast::BinaryOp::GreaterThanOrEqual + | parser::ast::BinaryOp::LessThanOrEqual => { + self.get_type_from_expr(right)?; + current_type = Arc::new(TypeRef::Name("bool".to_string())); + } + parser::ast::BinaryOp::Plus + | parser::ast::BinaryOp::Minus + | parser::ast::BinaryOp::Multiply + | parser::ast::BinaryOp::Divide + | parser::ast::BinaryOp::Modulo => { + self.get_type_from_expr(right)?; + } + }, _ => unimplemented!(), } } @@ -150,19 +199,19 @@ impl LocalScope { ) -> Option> { match expr { ast::Expression::IntLiteral(_) => self - .get_reference(&"int".to_string()) + .get_type_reference(&"int".to_string()) .map(|r| r.var_type.clone()), ast::Expression::FloatLiteral(_) => self - .get_reference(&"float".to_string()) + .get_type_reference(&"float".to_string()) .map(|r| r.var_type.clone()), ast::Expression::BoolLiteral(_) => self - .get_reference(&"bool".to_string()) + .get_type_reference(&"bool".to_string()) .map(|r| r.var_type.clone()), ast::Expression::StringLiteral(_) => self - .get_reference(&"string".to_string()) + .get_type_reference(&"string".to_string()) .map(|r| r.var_type.clone()), ast::Expression::Identifier(id) => { @@ -177,24 +226,32 @@ impl LocalScope { } } - pub fn with_params(self: &Arc, param_list: &ParamList) { - for (param_name, (export, type_expr)) in ¶m_list.0 { - match type_expr { + pub fn with_params(self: &Arc, param_list: &mut ParamList) { + let mut new_param_list = HashMap::new(); + + for (param_name, (export, type_expr)) in param_list.0.drain() { + match &type_expr { parser::ast::TypeExpr::Identifier(id) => { - if let Some(var_type) = - self.parent.get_reference(id).map(|r| r.var_type.clone()) - { - self.variables.lock().unwrap().insert( - param_name.clone(), - Arc::new(VarRef { - export: *export, - var_type, - name: param_name.clone(), - }), - ); - } + let new_name = self.parent.get_name(export); + + self.variables.lock().unwrap().insert( + param_name.clone(), + Arc::new(VarRef { + export: export, + var_type: self + .parent + .get_reference(id) + .map(|r| r.var_type.clone()) + .unwrap(), + name: new_name.clone(), + }), + ); + + new_param_list.insert(new_name, (export, type_expr)); } } } + + param_list.0 = new_param_list; } } diff --git a/src/compiler.rs b/src/compiler.rs index c7cbbe9..c4071a8 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -4,8 +4,8 @@ use serde::Deserialize; #[derive(Deserialize)] struct Config { - entry: String, - out_dir: String, + src: String, + output: String, } pub fn build() { @@ -21,55 +21,68 @@ pub fn build() { // 2. load config let config = load_config(&root); - let entry_path = root.join(&config.entry); - let out_dir = root.join(&config.out_dir); + let src = root.join(&config.src); - println!(" → entry: {}", entry_path.display()); + for entry in fs::read_dir(src).unwrap() { + if let Ok(entry) = entry { + let entry_path = root.join(&entry.path()); + let out_dir = root.join(&config.output); + let file_name = entry.file_name(); + let file_name = file_name.to_string_lossy(); - // 3. read entry file - let source = match fs::read_to_string(&entry_path) { - Ok(s) => s, - Err(e) => { - eprintln!("error: failed to read entry file\n {}", e); - process::exit(1); + let script = if file_name.ends_with(".ms") { + true + } else if file_name.ends_with(".mist") { + false + } else { + continue; + }; + + // 3. read entry file + let source = match fs::read_to_string(&entry_path) { + Ok(s) => s, + Err(e) => { + eprintln!("error: failed to read entry file\n {}", e); + process::exit(1); + } + }; + + let parser_result = if script { + parser::script_parser::parse(&source).map_err(|e| e.to_string()) + } else { + parser::parse(&source).map_err(|e| e.to_string()) + }; + + let mut ast = match parser_result { + Ok(ast) => ast, + Err(e) => { + eprintln!("error: parse failed\n{}", e); + process::exit(1); + } + }; + + semantic::walk_ast(semantic::scope::Scope::from_top(&root, &ast), &mut ast); + + let mut gc = codegen::GoCodegen::new(); + let output = gc.generate(&ast); + + if let Err(e) = fs::create_dir_all(&out_dir) { + eprintln!("error: failed to create build dir\n {}", e); + process::exit(1); + } + + let out_file = + out_dir.join(file_name.replace(if script { ".ms" } else { ".mist" }, ".go")); + + if let Err(e) = fs::write(&out_file, output) { + eprintln!("error: failed to write output\n {}", e); + process::exit(1); + } } - }; - - println!(" → parsing..."); - - let mut ast = match parser::parse(&source) { - Ok(ast) => ast, - Err(e) => { - eprintln!("error: parse failed\n{}", e); - process::exit(1); - } - }; - - println!(" → type checking..."); - - semantic::walk_ast(semantic::scope::Scope::from_top(&root, &ast), &mut ast); - - println!(" → generating Go code..."); - - let mut gc = codegen::GoCodegen::new(); - let output = gc.generate(&ast); - - // 4. ensure build dir - if let Err(e) = fs::create_dir_all(&out_dir) { - eprintln!("error: failed to create build dir\n {}", e); - process::exit(1); - } - - let out_file = out_dir.join("main.go"); - - if let Err(e) = fs::write(&out_file, output) { - eprintln!("error: failed to write output\n {}", e); - process::exit(1); } let elapsed = start.elapsed(); - println!(" ✓ built {}", out_file.display()); println!("build finished in {:.2?}", elapsed); } diff --git a/src/main.rs b/src/main.rs index 6331f9c..2632a49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,6 @@ -pub mod compiler; - -use std::fs; -use std::path::PathBuf; use std::process; -use semantic::walk_ast; +pub mod compiler; fn main() { let args: Vec = std::env::args().collect(); @@ -22,20 +18,6 @@ fn main() { } compiler::build(); } - "check" => { - if args.len() < 3 { - eprintln!("error: expected a file path\n usage: mist check "); - process::exit(1); - } - cmd_check(&args[2]); - } - "parse" => { - if args.len() < 3 { - eprintln!("error: expected a file path\n usage: mist check "); - process::exit(1); - } - cmd_parse(&args[2]); - } "version" | "--version" | "-v" => { println!("mist {}", env!("CARGO_PKG_VERSION")); } @@ -50,62 +32,6 @@ fn main() { } } -fn cmd_check(path: &str) { - let source = read_ms_file(path); - match parser::parse(&source) { - Ok(mut ast) => { - println!("parse: ok"); - - // walk_ast(semantic::scope::Scope::from_top(&ast), &mut ast); - - println!("{:#?}", ast) - } - Err(e) => { - eprintln!("parse error:\n{}", e); - process::exit(1); - } - } -} - -fn cmd_parse(path: &str) { - let source = read_ms_file(path); - match parser::parse(&source) { - Ok(mut ast) => { - // walk_ast(semantic::scope::Scope::from_top(&ast), &mut ast); - - fs::write("output.json", serde_json::to_string_pretty(&ast).unwrap()).unwrap_or_else( - |e| { - eprintln!("error: could not write output.json: {}", e); - process::exit(1); - }, - ); - } - Err(e) => { - eprintln!("parse error:\n{}", e); - process::exit(1); - } - } -} - -fn read_ms_file(path: &str) -> String { - let pb = PathBuf::from(path); - - if !pb.exists() { - eprintln!("error: file '{}' not found", path); - process::exit(1); - } - - if pb.extension().and_then(|e| e.to_str()) != Some("ms") { - eprintln!("error: expected a .ms file, got '{}'", path); - process::exit(1); - } - - fs::read_to_string(&pb).unwrap_or_else(|e| { - eprintln!("error: could not read '{}': {}", path, e); - process::exit(1); - }) -} - fn print_usage() { println!("mist - the mist compiler"); println!(); diff --git a/test/mist.json b/test/mist.json index c639516..6632b01 100644 --- a/test/mist.json +++ b/test/mist.json @@ -1,4 +1,4 @@ { - "entry": "src/main.ms", - "out_dir": "build" + "src": "src", + "output": "build" } \ No newline at end of file diff --git a/test/src/main.mist b/test/src/main.mist new file mode 100644 index 0000000..7bc9895 --- /dev/null +++ b/test/src/main.mist @@ -0,0 +1,5 @@ +package main; + +int test(int a, int b) { + return a + b; +} diff --git a/test/src/main.ms b/test/src/main.ms deleted file mode 100644 index d994a44..0000000 --- a/test/src/main.ms +++ /dev/null @@ -1,7 +0,0 @@ -package main; - -import "fmt"; - -function main() { - fmt.println("hello"); -}