diff --git a/.gitignore b/.gitignore index 7e6aa69..7811c03 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ target /target -/test/build \ No newline at end of file +/test/build +/test/output.json \ No newline at end of file diff --git a/src/compiler.rs b/src/compiler.rs index 7091a9a..136aed5 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -41,7 +41,7 @@ pub fn build() { let ast = match parser::parse(&source) { Ok(ast) => { - println!(" ✓ parsed {} items", ast.statements.len()); + println!(" ✓ parsed {} items", ast.len()); ast } Err(e) => { @@ -52,7 +52,8 @@ pub fn build() { println!(" → generating Go code..."); - let output = gogen::generate(&ast); + // let output = gogen::generate(&ast); + let output = String::from("// TODO: implement code generation\n"); // 4. ensure build dir if let Err(e) = fs::create_dir_all(&out_dir) { diff --git a/src/gogen/mod.rs b/src/gogen/mod.rs index 4402e36..d41f710 100644 --- a/src/gogen/mod.rs +++ b/src/gogen/mod.rs @@ -1,283 +1,283 @@ -use crate::parser::ast::{ - BinOperator, Class, Expression, Function, Program, Statement, Struct, TopLevel, TypeExpr, -}; +// use crate::parser::ast::{ +// BinOperator, Class, Expression, Function, Statement, Struct, TopLevel, TopLevel, TypeExpr, +// }; -pub fn generate(program: &Program) -> String { - let mut out = String::new(); +// pub fn generate(program: &TopLevel) -> String { +// let mut out = String::new(); - out.push_str("package main\n\n"); +// out.push_str("package main\n\n"); - // imports - let mut imports = vec![]; - for item in &program.statements { - if let TopLevel::Import(i) = item { - imports.push(i.path.clone()); - } - } +// // imports +// let mut imports = vec![]; +// for item in &program.statements { +// if let TopLevel::Import(i) = item { +// imports.push(i.path.clone()); +// } +// } - if !imports.is_empty() { - out.push_str("import (\n"); - for imp in imports { - out.push_str(&format!(" {}\n", imp)); - } - out.push_str(")\n\n"); - } +// if !imports.is_empty() { +// out.push_str("import (\n"); +// for imp in imports { +// out.push_str(&format!(" {}\n", imp)); +// } +// out.push_str(")\n\n"); +// } - // rest - for item in &program.statements { - match item { - TopLevel::Function(f) => out.push_str(&gen_function(f)), - TopLevel::Struct(s) => out.push_str(&gen_struct(s)), - TopLevel::Class(c) => out.push_str(&gen_class(c)), - TopLevel::Import(_) => {} - } - out.push('\n'); - } +// // rest +// for item in &program.statements { +// match item { +// TopLevel::Function(f) => out.push_str(&gen_function(f)), +// TopLevel::Struct(s) => out.push_str(&gen_struct(s)), +// TopLevel::Class(c) => out.push_str(&gen_class(c)), +// TopLevel::Import(_) => {} +// } +// out.push('\n'); +// } - out -} +// out +// } -fn gen_struct(s: &Struct) -> String { - let mut out = format!("type {} struct {{\n", s.name); +// fn gen_struct(s: &Struct) -> String { +// let mut out = format!("type {} struct {{\n", s.name); - for field in &s.fields { - out.push_str(&format!( - " {} {}\n", - capitalize(&field.name), - gen_type(&field.type_expr) - )); - } +// for field in &s.fields { +// out.push_str(&format!( +// " {} {}\n", +// capitalize(&field.name), +// gen_type(&field.type_expr) +// )); +// } - out.push_str("}\n"); - out -} +// out.push_str("}\n"); +// out +// } -fn gen_class(c: &Class) -> String { - let mut out = String::new(); +// fn gen_class(c: &Class) -> String { +// let mut out = String::new(); - // struct - out.push_str(&format!("type {} struct {{\n", c.name)); - for field in &c.fields { - out.push_str(&format!( - " {} {}\n", - capitalize(&field.name), - gen_type(&field.type_expr) - )); - } - out.push_str("}\n\n"); +// // struct +// out.push_str(&format!("type {} struct {{\n", c.name)); +// for field in &c.fields { +// out.push_str(&format!( +// " {} {}\n", +// capitalize(&field.name), +// gen_type(&field.type_expr) +// )); +// } +// out.push_str("}\n\n"); - // methods - for method in &c.methods { - out.push_str(&gen_method(c, method)); - out.push('\n'); - } +// // methods +// for method in &c.methods { +// out.push_str(&gen_method(c, method)); +// out.push('\n'); +// } - out -} +// out +// } -fn gen_function(f: &Function) -> String { - let mut out = format!("func {}(", f.name); +// fn gen_function(f: &Function) -> String { +// let mut out = format!("func {}(", f.name); - // params - for (i, p) in f.params.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); - } +// // params +// for (i, p) in f.params.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); +// } - out.push(')'); +// out.push(')'); - // return - if let Some(ret) = &f.return_type { - out.push_str(&format!(" {}", gen_type(ret))); - } +// // return +// if let Some(ret) = &f.return_type { +// out.push_str(&format!(" {}", gen_type(ret))); +// } - out.push_str(" {\n"); +// out.push_str(" {\n"); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str("}\n"); - out -} +// out.push_str("}\n"); +// out +// } -fn gen_method(class: &Class, f: &Function) -> String { - let mut out = format!("func (self *{}) {}(", class.name, f.name); +// fn gen_method(class: &Class, f: &Function) -> String { +// let mut out = format!("func (self *{}) {}(", class.name, f.name); - for (i, p) in f.params.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); - } +// for (i, p) in f.params.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{} {}", p.name, gen_type(&p.type_expr))); +// } - out.push(')'); +// out.push(')'); - if let Some(ret) = &f.return_type { - out.push_str(&format!(" {}", gen_type(ret))); - } +// if let Some(ret) = &f.return_type { +// out.push_str(&format!(" {}", gen_type(ret))); +// } - out.push_str(" {\n"); +// out.push_str(" {\n"); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str("}\n"); +// out.push_str("}\n"); - out -} +// out +// } -fn gen_statement(stmt: &Statement) -> String { - match stmt { - Statement::Let(s) => { - let mut out = format!(" {} := {}", s.name, gen_expr(&s.value)); +// fn gen_statement(stmt: &Statement) -> String { +// match stmt { +// Statement::Let(s) => { +// let mut out = format!(" {} := {}", s.name, gen_expr(&s.value)); - out.push_str(";\n"); - out - } +// out.push_str(";\n"); +// out +// } - Statement::Return(r) => match &r.value { - Some(v) => format!(" return {};\n", gen_expr(v)), - None => " return;\n".to_string(), - }, +// Statement::Return(r) => match &r.value { +// Some(v) => format!(" return {};\n", gen_expr(v)), +// None => " return;\n".to_string(), +// }, - Statement::Expression(e) => { - format!(" {};\n", gen_expr(e)) - } +// Statement::Expression(e) => { +// format!(" {};\n", gen_expr(e)) +// } - Statement::If(i) => { - let mut out = format!(" if {} {{\n", gen_expr(&i.condition)); +// Statement::If(i) => { +// let mut out = format!(" if {} {{\n", gen_expr(&i.condition)); - for stmt in &i.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &i.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str(" }"); +// out.push_str(" }"); - if let Some(else_body) = &i.else_body { - out.push_str(" else {\n"); - for stmt in else_body { - out.push_str(&gen_statement(stmt)); - } - out.push_str(" }"); - } +// if let Some(else_body) = &i.else_body { +// out.push_str(" else {\n"); +// for stmt in else_body { +// out.push_str(&gen_statement(stmt)); +// } +// out.push_str(" }"); +// } - out.push('\n'); - out - } +// out.push('\n'); +// out +// } - Statement::For(f) => { - let mut out = format!( - " for _, {} := range {} {{\n", - f.var, - gen_expr(&f.iterator) - ); +// Statement::For(f) => { +// let mut out = format!( +// " for _, {} := range {} {{\n", +// f.var, +// gen_expr(&f.iterator) +// ); - for stmt in &f.body { - out.push_str(&gen_statement(stmt)); - } +// for stmt in &f.body { +// out.push_str(&gen_statement(stmt)); +// } - out.push_str(" }\n"); - out - } - } -} +// out.push_str(" }\n"); +// out +// } +// } +// } -fn gen_expr(expr: &Expression) -> String { - match expr { - Expression::Identifier(name, _) => name.clone(), - Expression::Integer(v, _) => v.to_string(), - Expression::Float(v, _) => v.to_string(), - Expression::StringLit(s, _) => format!("\"{}\"", s), - Expression::Bool(b, _) => b.to_string(), +// fn gen_expr(expr: &Expression) -> String { +// match expr { +// Expression::Identifier(name, _) => name.clone(), +// Expression::Integer(v, _) => v.to_string(), +// Expression::Float(v, _) => v.to_string(), +// Expression::StringLit(s, _) => format!("\"{}\"", s), +// Expression::Bool(b, _) => b.to_string(), - Expression::BinaryOp(b) => format!( - "{} {} {}", - gen_expr(&b.left), - op_to_str(&b.op), - gen_expr(&b.right) - ), +// Expression::BinaryOp(b) => format!( +// "{} {} {}", +// gen_expr(&b.left), +// op_to_str(&b.op), +// gen_expr(&b.right) +// ), - Expression::Call(c) => { - let args = c.args.iter().map(gen_expr).collect::>().join(", "); - format!("{}({})", gen_expr(&c.callee), args) - } +// Expression::Call(c) => { +// let args = c.args.iter().map(gen_expr).collect::>().join(", "); +// format!("{}({})", gen_expr(&c.callee), args) +// } - Expression::FieldAccess(f) => { - format!("{}.{}", gen_expr(&f.object), capitalize(&f.field)) - } +// Expression::FieldAccess(f) => { +// format!("{}.{}", gen_expr(&f.object), capitalize(&f.field)) +// } - Expression::StructInit(s) => { - let mut out = format!("{}{{", s.name); +// Expression::StructInit(s) => { +// let mut out = format!("{}{{", s.name); - for (i, (name, val)) in s.fields.iter().enumerate() { - if i > 0 { - out.push_str(", "); - } - out.push_str(&format!("{}: {}", capitalize(name), gen_expr(val))); - } +// for (i, (name, val)) in s.fields.iter().enumerate() { +// if i > 0 { +// out.push_str(", "); +// } +// out.push_str(&format!("{}: {}", capitalize(name), gen_expr(val))); +// } - out.push('}'); - out - } +// out.push('}'); +// out +// } - Expression::ArrayLiteral(arr) => { - let elems = arr - .elements - .iter() - .map(gen_expr) - .collect::>() - .join(", "); - format!("[]any{{{}}}", elems) // simple version - } +// Expression::ArrayLiteral(arr) => { +// let elems = arr +// .elements +// .iter() +// .map(gen_expr) +// .collect::>() +// .join(", "); +// format!("[]any{{{}}}", elems) // simple version +// } - _ => todo!(), - } -} +// _ => todo!(), +// } +// } -fn gen_type(t: &TypeExpr) -> String { - match t { - TypeExpr::Named(n) => match n.as_str() { - "int" => "int".into(), - "float" => "float64".into(), - "string" => "string".into(), - _ => n.clone(), - }, +// fn gen_type(t: &TypeExpr) -> String { +// match t { +// TypeExpr::Named(n) => match n.as_str() { +// "int" => "int".into(), +// "float" => "float64".into(), +// "string" => "string".into(), +// _ => n.clone(), +// }, - TypeExpr::Array(inner) => { - format!("[]{}", gen_type(inner)) - } +// TypeExpr::Array(inner) => { +// format!("[]{}", gen_type(inner)) +// } - TypeExpr::Optional(inner) => { - format!("*{}", gen_type(inner)) // pointer for optional - } - } -} +// TypeExpr::Optional(inner) => { +// format!("*{}", gen_type(inner)) // pointer for optional +// } +// } +// } -fn capitalize(s: &str) -> String { - let mut chars = s.chars(); - match chars.next() { - Some(c) => c.to_uppercase().collect::() + chars.as_str(), - None => String::new(), - } -} +// fn capitalize(s: &str) -> String { +// let mut chars = s.chars(); +// match chars.next() { +// Some(c) => c.to_uppercase().collect::() + chars.as_str(), +// None => String::new(), +// } +// } -fn op_to_str(op: &BinOperator) -> &'static str { - match op { - BinOperator::Add => "+", - BinOperator::Sub => "-", - BinOperator::Mul => "*", - BinOperator::Div => "/", - BinOperator::Eq => "==", - BinOperator::NotEq => "!=", - BinOperator::Lt => "<", - BinOperator::Gt => ">", - BinOperator::LtEq => "<=", - BinOperator::GtEq => ">=", - BinOperator::And => "&&", - BinOperator::Or => "||", - } -} +// fn op_to_str(op: &BinOperator) -> &'static str { +// match op { +// BinOperator::Add => "+", +// BinOperator::Sub => "-", +// BinOperator::Mul => "*", +// BinOperator::Div => "/", +// BinOperator::Eq => "==", +// BinOperator::NotEq => "!=", +// BinOperator::Lt => "<", +// BinOperator::Gt => ">", +// BinOperator::LtEq => "<=", +// BinOperator::GtEq => ">=", +// BinOperator::And => "&&", +// BinOperator::Or => "||", +// } +// } diff --git a/src/main.rs b/src/main.rs index 56fd64a..d2d17ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,13 @@ fn main() { } 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")); } @@ -61,6 +68,26 @@ fn cmd_check(path: &str) { } } +fn cmd_parse(path: &str) { + let source = read_ms_file(path); + match parser::parse(&source) { + Ok(output) => { + fs::write( + "output.json", + serde_json::to_string_pretty(&output).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); diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 057fac0..63d7e24 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -1,187 +1,125 @@ -#[derive(Debug, Clone)] -pub struct Span { - pub start: usize, - pub end: usize, -} +use serde::Serialize; -#[derive(Debug, Clone)] -pub struct Program { - pub statements: Vec, -} +#[derive(Debug, Clone, Serialize)] +pub struct ParamList(pub Vec<(String, TypeExpr)>); -#[derive(Debug, Clone)] -pub enum TopLevel { - Function(Function), - Struct(Struct), - Class(Class), - Import(Import), -} +#[derive(Debug, Clone, Serialize)] +pub struct Block(pub Vec); -#[derive(Debug, Clone)] -pub struct Function { - pub name: String, - pub params: Vec, - pub return_type: Option, - pub body: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Param { - pub name: String, - pub type_expr: TypeExpr, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Struct { - pub name: String, - pub fields: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct StructField { - pub name: String, - pub type_expr: TypeExpr, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Class { - pub name: String, - pub fields: Vec, - pub methods: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct Import { - pub path: String, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum Statement { - Let(LetStatement), - Return(ReturnStatement), - Expression(Expression), - If(IfStatement), - For(ForStatement), -} - -#[derive(Debug, Clone)] -pub struct LetStatement { - pub name: String, - pub type_expr: Option, - pub value: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct ReturnStatement { - pub value: Option, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct IfStatement { - pub condition: Expression, - pub body: Vec, - pub else_body: Option>, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct ForStatement { - pub var: String, - pub iterator: Expression, - pub body: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum Expression { - Identifier(String, Span), - Integer(i64, Span), - Float(f64, Span), - StringLit(String, Span), - Bool(bool, Span), - BinaryOp(Box), - UnaryOp(Box), - Call(Box), - FieldAccess(Box), - StructInit(Box), - ArrayLiteral(Box), -} - -#[derive(Debug, Clone)] -pub struct BinaryOp { - pub left: Expression, - pub op: BinOperator, - pub right: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum BinOperator { - Add, - Sub, - Mul, - Div, - Eq, - NotEq, - Lt, - Gt, - LtEq, - GtEq, - And, - Or, -} - -#[derive(Debug, Clone)] -pub struct UnaryOp { - pub op: UnaryOperator, - pub expr: Expression, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub enum UnaryOperator { - Neg, - Not, -} - -#[derive(Debug, Clone)] -pub struct CallExpr { - pub callee: Expression, - pub args: Vec, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct FieldAccess { - pub object: Expression, - pub field: String, - pub span: Span, -} - -#[derive(Debug, Clone)] -pub struct StructInit { - pub name: String, - pub fields: Vec<(String, Expression)>, - pub span: Span, -} - -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] pub enum TypeExpr { - Named(String), - Array(Box), - Optional(Box), + Identifier(String), } -#[derive(Debug, Clone)] -pub struct ArrayLiteral { - pub elements: Vec, - pub span: Span, +#[derive(Debug, Clone, Serialize)] +pub enum BinaryOp { + Plus, + Minus, + Multiply, + Divide, + Modulo, + Equal, + NotEqual, + LessThan, + GreaterThan, + LessThanOrEqual, + GreaterThanOrEqual, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum TopLevel { + Import(String), + StructDecl { + export: bool, + name: String, + fields: ParamList, + }, + FunctionDecl { + export: bool, + name: String, + params: ParamList, + return_type: Option, + body: Block, + }, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum Postfix { + FieldAccess(String), + Call(Vec), + Index(Expression), + Binary(BinaryOp, Expression), +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum Statement { + // expr; + Expression(Expression), + + // { ... } + Block(Block), + + // let/const/var x = ... + VarDecl { + kind: VarKind, + name: String, + init: Option, + }, + + VarAssign { + target: Expression, + value: Expression, + }, + + // if (...) stmt else stmt + If { + condition: Expression, + then_branch: Box, + else_branch: Option>, + }, + + // while (...) stmt + While { + condition: Expression, + body: Box, + }, + + // for (...) stmt + For { + init: (VarKind, String, Option), + condition: Option, + update: Option>, + body: Box, + }, + + // return expr?; + Return(Option), + + Break, + Continue, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(tag = "type", content = "value")] +pub enum Expression { + Identifier(String), + IntLiteral(i64), + FloatLiteral(f64), + BoolLiteral(bool), + StringLiteral(String), + Postfix { + initial: Box, + postfixes: Vec, + }, +} + +#[derive(Debug, Clone, Serialize)] +pub enum VarKind { + Let, + Const, + Var, } diff --git a/src/parser/grammar.pest b/src/parser/grammar.pest index 8a59ca2..ef15bad 100644 --- a/src/parser/grammar.pest +++ b/src/parser/grammar.pest @@ -1,8 +1,7 @@ WHITESPACE = _{ " " | "\t" | "\r" | "\n" } COMMENT = _{ "//" ~ (!"\n" ~ ANY)* } -program = { SOI ~ item* ~ EOI } -item = _{ import_decl | function_decl | struct_decl | class_decl } +program = { SOI ~ top_level* ~ EOI } // primitives identifier = @{ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* } @@ -12,152 +11,108 @@ boolean = { "true" | "false" } string_lit = { "\"" ~ inner_str ~ "\"" } inner_str = @{ (!"\"" ~ ANY)* } -// ================= TYPE SCRIPT STYLE CHANGES ================= +// ================= TOP LEVEL ================= -// import becomes TS-like (still identical structure) -import_decl = { "import" ~ string_lit ~ ";" } +top_level = _{ import | function_decl | struct_decl } -// struct stays, but feels like TS "type object shape" -struct_decl = { - "struct" ~ identifier ~ "{" ~ struct_field* ~ "}" -} +export = { "export" } -// class already TS-like — just improved consistency -class_decl = { - "class" ~ identifier ~ "{" ~ class_member* ~ "}" -} +import = { "import" ~ string_lit ~ ";" } -class_member = _{ struct_field | function_decl } - -// function keyword changed feel: fn → function -function_decl = { - "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ ("->" ~ type_expr)? ~ block -} - -// struct fields: TS-style semicolon consistency (already good) -struct_field = { identifier ~ ":" ~ type_expr ~ ";" } - -// params: TS optional typing style feel (unchanged structure) param_list = { param ~ ("," ~ param)* ~ ","? } param = { identifier ~ ":" ~ type_expr } +struct_decl = { + export? ~ "struct" ~ identifier ~ "{" ~ param_list? ~ "}" +} + +function_decl = { + export? ~ "function" ~ identifier ~ "(" ~ param_list? ~ ")" ~ (":" ~ type_expr)? ~ block +} + // ================= STATEMENTS ================= -block = { "{" ~ statement* ~ "}" } +block = { "{" ~ statement_list ~ "}" } + +statement_list = { statement* } statement = _{ - let_stmt - | return_stmt + expr_stmt + | var_decl + | var_assign | if_stmt + | while_stmt | for_stmt - | assign_stmt - | expression_stmt + | return_stmt + | break_stmt + | continue_stmt + | block } -// expression statements: TS allows no semicolon in real life, -// but we keep your structure -expression_stmt = { expression ~ ";" } +// --- basic statements --- -// assignment unchanged (JS/TS feel already) -assign_stmt = { identifier ~ "=" ~ expression ~ ";" } +expr_stmt = { expr ~ ";" } -// let → const/let feel (still your rule, just TS naming) -let_stmt = { - "const" ~ identifier ~ (":" ~ type_expr)? ~ "=" ~ expression ~ ";" +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)? ~ ";" } -// return: TS-like (no change except feel consistency) -return_stmt = { "return" ~ expression? ~ ";" } +var_assign = { + expr ~ "=" ~ expr ~ ";" +} + +// --- control flow --- -// if: add TS-style parentheses (MAJOR feel upgrade, same structure) if_stmt = { - "if" ~ "(" ~ expression ~ ")" ~ block ~ ("else" ~ block)? + "if" ~ "(" ~ expr ~ ")" ~ statement ~ ("else" ~ statement)? +} + +while_stmt = { + "while" ~ "(" ~ expr ~ ")" ~ statement } -// for: TS-like "of" for_stmt = { - "for" ~ identifier ~ "of" ~ expression ~ block + "for" ~ "(" ~ var_decl ~ expr ~ ";" ~ var_assign_no_semicolon ~ ")" ~ statement } -// ================= TYPES (KEEP STRUCTURE, TS FEEL) ================= - -type_expr = { base_type ~ "?"? } -base_type = _{ array_type | identifier } -array_type = { "[" ~ type_expr ~ "]" } +var_assign_no_semicolon = { + expr ~ "=" ~ expr +} // ================= EXPRESSIONS ================= +type_expr = { identifier } -expression = { term ~ (bin_op ~ term)* } - -bin_op = _{ - eq - | neq - | lte - | gte - | lt - | gt - | and - | or - | add - | sub - | mul - | div -} - -add = { "+" } -sub = { "-" } -mul = { "*" } -div = { "/" } -eq = { "==" } -neq = { "!=" } -lte = { "<=" } -gte = { ">=" } -lt = { "<" } -gt = { ">" } -and = { "&&" } -or = { "||" } - -// ================= LITERALS ================= - -struct_literal = { - identifier ~ "{" ~ struct_init_list? ~ "}" -} - -struct_init_list = { - struct_init_field ~ ("," ~ struct_init_field)* ~ ","? -} - -struct_init_field = { - identifier ~ ":" ~ expression -} - -array_literal = { - "[" ~ (expression ~ ("," ~ expression)*)? ~ ","? ~ "]" -} - -// ================= CHAINING ================= - -term = { primary ~ (field_access | call_suffix)* } - -field_access = { "." ~ identifier } - -call_suffix = { - "(" ~ (expression ~ ("," ~ expression)*)? ~ ","? ~ ")" -} - -// ================= PRIMARY ================= - -primary = _{ - struct_literal - | array_literal +primary = { + integer | float - | integer - | string_lit | boolean - | self_kw - | null_kw + | string_lit | identifier } -self_kw = { "this" } -null_kw = { "null" } +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/src/parser/mod.rs b/src/parser/mod.rs index a8ad54b..c000532 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1,5 +1,4 @@ use pest::Parser; -use pest::iterators::Pair; use pest_derive::Parser; pub mod ast; @@ -12,352 +11,370 @@ pub struct MistParser; // convenience alias for pest errors pub type ParseError = pest::error::Error; -pub fn parse(source: &str) -> Result { - let pairs = MistParser::parse(Rule::program, source)?; +pub fn parse(source: &str) -> Result, ParseError> { + let mut pairs = MistParser::parse(Rule::program, source)?; let mut statements = vec![]; - // pairs is an iterator over the top-level program pair - // we need to get its inner children - for pair in pairs { + for pair in pairs.next().unwrap().into_inner() { + if let Some(stmt) = TopLevel::from_pair(pair) { + statements.push(stmt); + } + } + + Ok(statements) +} + +impl TypeExpr { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { match pair.as_rule() { - Rule::program => { - for inner in pair.into_inner() { - match inner.as_rule() { - Rule::function_decl => { - statements.push(TopLevel::Function(parse_function(inner))) - } - Rule::struct_decl => statements.push(TopLevel::Struct(parse_struct(inner))), - Rule::class_decl => statements.push(TopLevel::Class(parse_class(inner))), - Rule::import_decl => statements.push(TopLevel::Import(parse_import(inner))), - Rule::EOI => {} - _ => {} + Rule::type_expr => { + let inner = pair.into_inner().next().unwrap(); + TypeExpr::from_pair(inner) + } + Rule::identifier => TypeExpr::Identifier(pair.as_str().to_string()), + _ => unimplemented!("TypeExpr parsing not implemented yet"), + } + } +} + +impl ParamList { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + let params = pair + .into_inner() + .map(|p| { + let mut param_inner = p.into_inner(); + let param_name = param_inner.next().unwrap().as_str().to_string(); + let param_type = TypeExpr::from_pair(param_inner.next().unwrap()); + (param_name, param_type) + }) + .collect(); + + ParamList(params) + } +} + +impl TopLevel { + pub fn from_pair(pair: pest::iterators::Pair) -> Option { + match pair.as_rule() { + Rule::import => { + let path = pair.into_inner().next().unwrap().as_str().to_string(); + Some(TopLevel::Import(path)) + } + 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_pair = inner.next().unwrap(); + let params = if params_pair.as_rule() == Rule::param_list { + ParamList::from_pair(params_pair) + } else { + ParamList(vec![]) + }; + let return_type = if let Some(next) = inner.peek() { + if next.as_rule() == Rule::type_expr { + Some(TypeExpr::from_pair(inner.next().unwrap())) + } else { + None + } + } else { + None + }; + + let body = Block::from_pair(inner.next().unwrap()); + + Some(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_pair(fields_pair); + + Some(TopLevel::StructDecl { + export, + name, + fields, + }) + } + + Rule::EOI => None, + _ => unimplemented!("TopLevel parsing not implemented yet {:?}", pair.as_rule()), + } + } +} + +impl Block { + pub fn from_pair(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() + } else { + vec![Statement::from_pair(pair)] } + }) + .collect(); + Block(statements) + } +} + +impl Statement { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::statement => { + let inner = pair.into_inner().next().unwrap(); + Statement::from_pair(inner) } - Rule::EOI => {} - _ => {} - } - } - Ok(Program { statements }) -} - -fn span_of(pair: &Pair) -> Span { - let s = pair.as_span(); - Span { - start: s.start(), - end: s.end(), - } -} - -fn parse_function(pair: Pair) -> Function { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - - let name = inner.next().unwrap().as_str().to_string(); - - let mut params = vec![]; - let mut return_type = None; - let mut body = vec![]; - - for part in inner { - match part.as_rule() { - Rule::param_list => params = parse_param_list(part), - Rule::type_expr => return_type = Some(parse_type_expr(part)), - Rule::block => body = parse_block(part), - _ => {} - } - } - - Function { - name, - params, - return_type, - body, - span, - } -} - -fn parse_param_list(pair: Pair) -> Vec { - pair.into_inner() - .map(|p| { - let span = span_of(&p); - let mut inner = p.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let type_expr = parse_type_expr(inner.next().unwrap()); - Param { - name, - type_expr, - span, + Rule::expr_stmt => { + let expr_pair = pair.into_inner().next().unwrap(); + Statement::Expression(Expression::from_pair(expr_pair)) } - }) - .collect() -} -fn parse_struct(pair: Pair) -> Struct { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let fields = inner.map(|f| parse_struct_field(f)).collect(); - Struct { name, fields, span } -} + Rule::block => Statement::Block(Block::from_pair(pair.into_inner().next().unwrap())), -fn parse_struct_field(pair: Pair) -> StructField { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let type_expr = parse_type_expr(inner.next().unwrap()); - StructField { - name, - type_expr, - span, - } -} + Rule::var_decl => { + let mut inner = pair.into_inner(); -fn parse_class(pair: Pair) -> Class { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - let mut fields = vec![]; - let mut methods = vec![]; + let kind_pair = inner.next().unwrap(); // let/const/var + let name_pair = inner.next().unwrap(); // identifier - for part in inner { - match part.as_rule() { - Rule::struct_field => fields.push(parse_struct_field(part)), - Rule::function_decl => methods.push(parse_function(part)), - _ => {} - } - } + let init = inner.next().map(|expr_pair| { + // expects "=" expr + Expression::from_pair(expr_pair.into_inner().next().unwrap()) + }); - Class { - name, - fields, - methods, - span, - } -} - -fn parse_import(pair: Pair) -> Import { - let span = span_of(&pair); - let path = pair.into_inner().next().unwrap().as_str().to_string(); - Import { path, span } -} - -fn parse_block(pair: Pair) -> Vec { - pair.into_inner() - .filter_map(|p| parse_statement(p)) - .collect() -} - -fn parse_statement(pair: Pair) -> Option { - match pair.as_rule() { - Rule::let_stmt => Some(Statement::Let(parse_let(pair))), - Rule::return_stmt => Some(Statement::Return(parse_return(pair))), - Rule::if_stmt => Some(Statement::If(parse_if(pair))), - Rule::for_stmt => Some(Statement::For(parse_for(pair))), - Rule::expression_stmt => { - let expr = pair.into_inner().next().unwrap(); - Some(Statement::Expression(parse_expression(expr))) - } - Rule::expression => Some(Statement::Expression(parse_expression(pair))), - _ => None, - } -} - -fn parse_let(pair: Pair) -> LetStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - - // peek ahead — next is either a type or an expression - let next = inner.next().unwrap(); - let (type_expr, value) = if next.as_rule() == Rule::type_expr { - ( - Some(parse_type_expr(next)), - parse_expression(inner.next().unwrap()), - ) - } else { - (None, parse_expression(next)) - }; - - LetStatement { - name, - type_expr, - value, - span, - } -} - -fn parse_return(pair: Pair) -> ReturnStatement { - let span = span_of(&pair); - let value = pair.into_inner().next().map(|p| parse_expression(p)); - ReturnStatement { value, span } -} - -fn parse_if(pair: Pair) -> IfStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let condition = parse_expression(inner.next().unwrap()); - let body = parse_block(inner.next().unwrap()); - let else_body = inner.next().map(|p| parse_block(p)); - IfStatement { - condition, - body, - else_body, - span, - } -} - -fn parse_for(pair: Pair) -> ForStatement { - let span = span_of(&pair); - let mut inner = pair.into_inner(); - let var = inner.next().unwrap().as_str().to_string(); - let iterator = parse_expression(inner.next().unwrap()); - let body = parse_block(inner.next().unwrap()); - ForStatement { - var, - iterator, - body, - span, - } -} - -fn parse_expression(pair: Pair) -> Expression { - match pair.as_rule() { - Rule::expression => { - let mut inner = pair.into_inner(); - let mut expr = parse_term(inner.next().unwrap()); - - // consume pairs of (bin_op, term) - while let Some(op_pair) = inner.next() { - let right = parse_term(inner.next().unwrap()); - let span = span_of(&op_pair); - let op = match op_pair.as_rule() { - Rule::add => BinOperator::Add, - Rule::sub => BinOperator::Sub, - Rule::mul => BinOperator::Mul, - Rule::div => BinOperator::Div, - Rule::eq => BinOperator::Eq, - Rule::neq => BinOperator::NotEq, - Rule::lt => BinOperator::Lt, - Rule::gt => BinOperator::Gt, - Rule::lte => BinOperator::LtEq, - Rule::gte => BinOperator::GtEq, - Rule::and => BinOperator::And, - Rule::or => BinOperator::Or, + let kind = match kind_pair.as_str() { + "let" => VarKind::Let, + "const" => VarKind::Const, + "var" => VarKind::Var, _ => unreachable!(), }; - expr = Expression::BinaryOp(Box::new(BinaryOp { - left: expr, - op, - right, - span, - })); + + Statement::VarDecl { + kind, + name: name_pair.as_str().to_string(), + init, + } } - expr + Rule::return_stmt => { + let mut inner = pair.into_inner(); + + let expr = inner.next().map(Expression::from_pair); + + 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_pair(inner.next().unwrap()); + let then_branch = Statement::from_pair(inner.next().unwrap()); + + let else_branch = inner.next().map(Statement::from_pair); + + 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_pair(inner.next().unwrap()); + let body = Statement::from_pair(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_pair(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_pair); + let update = inner.next().map(parse_var_assign_no_semicolon); + let body = Statement::from_pair(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_pair(inner.next().unwrap()); + let value = Expression::from_pair(inner.next().unwrap()); + + Statement::VarAssign { target, value } + } + + _ => unimplemented!( + "Statement parsing not implemented yet: {:?}", + pair.as_rule() + ), } - _ => parse_term(pair), } } -fn parse_term(pair: Pair) -> Expression { +impl Expression { + pub fn from_pair(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()); + + if inner.len() > 0 { + Expression::Postfix { + initial: Box::new(exp), + postfixes: inner.map(|p| Postfix::from_pair(p)).collect(), + } + } else { + exp + } + } + Rule::primary => Expression::from_pair(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 Postfix { + pub fn from_pair(pair: pest::iterators::Pair) -> Self { + match pair.as_rule() { + Rule::postfix => Postfix::from_pair(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::index_px => { + Postfix::Index(Expression::from_pair(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_pair(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 mut expr = parse_primary(inner.next().unwrap()); + let target = Expression::from_pair(inner.next().unwrap()); + let value = Expression::from_pair(inner.next().unwrap()); - for part in inner { - let span = span_of(&part); - match part.as_rule() { - Rule::field_access => { - let field = part.into_inner().next().unwrap().as_str().to_string(); - expr = Expression::FieldAccess(Box::new(FieldAccess { - object: expr, - field, - span, - })); - } - Rule::call_suffix => { - let args = part.into_inner().map(|p| parse_expression(p)).collect(); - expr = Expression::Call(Box::new(CallExpr { - callee: expr, - args, - span, - })); - } - _ => {} - } - } - - expr -} - -fn parse_primary(pair: Pair) -> Expression { - let span = span_of(&pair); - - match pair.as_rule() { - Rule::struct_literal => { - let mut inner = pair.into_inner(); - let name = inner.next().unwrap().as_str().to_string(); - - inner = inner.next().unwrap().into_inner(); - - // println!("{inner:#?}"); - - let mut fields = vec![]; - for field in inner { - let mut f_inner = field.into_inner(); - let field_name = f_inner.next().unwrap().as_str().to_string(); - let value = parse_expression(f_inner.next().unwrap()); - - fields.push((field_name, value)); - } - - Expression::StructInit(Box::new(StructInit { name, fields, span })) - } - - Rule::array_literal => { - let elements = pair.into_inner().map(|p| parse_expression(p)).collect(); - - Expression::ArrayLiteral(Box::new(ArrayLiteral { elements, span })) - } - - Rule::integer => Expression::Integer(pair.as_str().parse().unwrap(), span), - Rule::float => Expression::Float(pair.as_str().parse().unwrap(), span), - - Rule::string_lit => { - Expression::StringLit(pair.into_inner().next().unwrap().as_str().to_string(), span) - } - - Rule::boolean => Expression::Bool(pair.as_str() == "true", span), - - Rule::self_kw => Expression::Identifier("self".to_string(), span), - Rule::null_kw => Expression::Identifier("null".to_string(), span), - Rule::identifier => Expression::Identifier(pair.as_str().to_string(), span), - - Rule::term => parse_term(pair), - - _ => unreachable!("unexpected primary rule: {:?}", pair.as_rule()), - } -} - -fn parse_type_expr(pair: Pair) -> TypeExpr { - let mut inner = pair.into_inner(); - let base = inner.next().unwrap(); - - let base_type = match base.as_rule() { - Rule::array_type => { - let inner_type = parse_type_expr(base.into_inner().next().unwrap()); - TypeExpr::Array(Box::new(inner_type)) - } - Rule::identifier => TypeExpr::Named(base.as_str().to_string()), - _ => unreachable!(), - }; - - // if a "?" suffix was present, wrap in Optional - if inner.next().is_some() { - TypeExpr::Optional(Box::new(base_type)) - } else { - base_type - } + Statement::VarAssign { target, value } } diff --git a/test/src/main.ms b/test/src/main.ms index ce922de..755f17d 100644 --- a/test/src/main.ms +++ b/test/src/main.ms @@ -1,9 +1,17 @@ -import "fmt"; +function add(a: int, b: int): int { + let sum = 0; -function add(a: int, b: int) -> int { - return a + b; + for (let i = 0; i < 10; i = i + 1) { + if (i == 5) { + continue; + } + + sum = sum + i; + } + + if (sum > 20) { + return sum; + } else { + return 0; + } } - -function main() { - fmt.Println(add(10, 10) / 2); -} \ No newline at end of file