diff --git a/examples/if.rs b/examples/if.rs deleted file mode 100644 index 8bda63162c5aaa6613801468a47b3a458e88956a..0000000000000000000000000000000000000000 --- a/examples/if.rs +++ /dev/null @@ -1,6 +0,0 @@ -fn main() { - let a = { - let b = 1; - b - }; -} diff --git a/examples/syntax.rs b/examples/syntax.rs new file mode 100644 index 0000000000000000000000000000000000000000..5b2d45f2eab9e88388db36594fec92792b68d1a6 --- /dev/null +++ b/examples/syntax.rs @@ -0,0 +1,15 @@ +fn main() { + let a = false; + + let a = { + let b = 1 + 1; + // extra `;` are allowed + if a {} // no trialing `;` + b // return value + }; + + while false { + // do something here + } + let _b = if a < 5 { 1 } else { 2 }; +} diff --git a/src/ast/README.md b/src/ast/README.md new file mode 100644 index 0000000000000000000000000000000000000000..5416404cf277e047192ebf7d8bff8ccdbd33612c --- /dev/null +++ b/src/ast/README.md @@ -0,0 +1,48 @@ +# Rust Syntax + +As it turns out Rust syntax is a bit tricky to parse. + +Consider the following program (`examples/syntax.rs`): + +``` Rust +fn main() { + let a = false; + + let a = { + let b = 1 + 1; + // extra `;` are allowed + if a {} // no trialing `;` + b // return value + }; + + while false { + // do something here + } + let _b = if a < 5 { 1 } else { 2 }; +} +``` + +At the top level, we have a *block* (sequence of statements). We also see, that the `let` statement accepts a *block* of statements as part of an assignment. Inside a *block*, statements are typically separated by `;`, with the following exceptions. + +- `if` statements, and +- `while` statements may omit the trailing `;`. + +Additionally Rust allows for additional `;` in between statements (but extra `;` are considered non-idiomatic and thus removed by `rustfmt`). + +Omitting trailing `;` for the last statement in a block renders an implicit return. This is allowed by the Rust compiler in case the statement can be interpreted as an expression. + +## An example grammar + +The example grammar in `ast/parser.lalrpop` covers a minimal subset of Rust, sufficient to parse the given `syntax.rs` example. Each action merely produces a unit result (no AST is built). + +Some interesting design decisions: + +- We treat statements that may be considered as expressions by a special rule `ExprStmt`, where we accept either `if then else` or a `block`. (This is where we likely add `match` and similar statements later.) + +Recall that a statement can be a return value, thus must somehow accept an expression. A `if then else` statement is one such possible expression. Now, here is the crux, without the + + + +- `;` is treated as a `stmt`, hence we allow *blocks* like `{; let a=5; let b=6;;;;;}` +- "let" Id "=" Expr ";" + diff --git a/src/ast/main.rs b/src/ast/main.rs index 3e05258d76b8c6703911db9d9771e2067fa79561..7e980d3706aff5b1ca34ce5ef18edcb9ed2b881e 100644 --- a/src/ast/main.rs +++ b/src/ast/main.rs @@ -19,23 +19,12 @@ pub fn read(file_name: &str) -> std::io::Result<String> { Ok(contents) } -// pub fn parse(file_name: &str) -> Program { -// let p = read(file_name).expect("File not found"); -// ProgramParser::new().parse(&p).unwrap() -// } +pub fn parse(file_name: &str) { + let p = read(file_name).expect("File not found"); + FunctionParser::new().parse(&p).unwrap() +} #[test] -fn t1() { - let s = " - { - let a = { - let b = 1 + 1; - ; - if a { } - - b - } - } - "; - StmtsParser::new().parse(s).unwrap(); +fn syntax() { + parse("examples/syntax3.rs"); } diff --git a/src/ast/parser.lalrpop b/src/ast/parser.lalrpop index 2688e4aae7b764e33bff25995c07f90a8873cc92..4d5db9b75c3a227fc29bdfa0c3a882467ff5a761 100644 --- a/src/ast/parser.lalrpop +++ b/src/ast/parser.lalrpop @@ -14,15 +14,60 @@ match { _ } -//pub Expr: () +pub Function: () = { + "fn" Id Params ("->" Type)? Stmts => (), +} + +pub Params: () = { + "()" => (), // seems like a haxx + "(" ((Param ",")* Param)? ")" => (), +} + +pub Param:() = { + Id ":" Type, +} + +pub Type:() = { + "i32" => (), + "bool" => (), + "()" => (), +} + +pub Stmts: () = { + "{" Stmt* "}" => (), +} + +pub ExprStmt: () = { + "if" Expr Stmts "else" Stmts => (), + Stmts => (), +} + +pub Stmt: () = { + ";" => (), + "let" Id "=" Expr ";" => (), + ExprNoBlock "=" Expr => (), + "if" Expr Stmts ("else" Stmts)? => (), + ExprNoBlock => (), + Stmts => (), +} + pub Expr: () = { ExprStmt => (), ExprNoBlock => (), } pub ExprNoBlock: () = { - Expr AddSub Factor => (), - Factor => (), + ExprNoBlock Cmp Expr1 => (), + Expr1 => (), +} + +pub Cmp: () = { + "<" => () +} + +pub Expr1: () = { + Expr1 AddSub Expr2 => (), + Expr2 => (), } pub AddSub: () = { @@ -30,8 +75,8 @@ pub AddSub: () = { "-" => (), } -pub Factor: () = { - Factor MulDiv Term => (), +pub Expr2: () = { + Expr2 MulDiv Term => (), Term => (), } @@ -43,33 +88,16 @@ pub MulDiv: () = { pub Term: () = { Id => (), Num => (), + "(" Expr ")" => (), } -pub ExprStmt: () = { - "if" Expr Stmts "else" Stmts => (), - Stmts => (), -} - -pub Stmts: () = { - "{" Stmt* "}" => (), -} - -pub Stmt: () = { - ";" => (), - "let" Id "=" Expr => (), - "if" Expr Stmts ("else" Stmts)? => (), - ExprNoBlock => (), - Stmts => (), -} - - pub Num: i32 = { r"[0-9]+" => i32::from_str(<>).unwrap(), }; pub Id: String = { - r"([a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*" => String::from_str(<>).unwrap(), + r"(_|[a-z]|[A-Z])([a-z]|[A-Z]|[0-9]|_)*" => String::from_str(<>).unwrap(), }; \ No newline at end of file