├── .gitattributes ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── grammar.ebnf ├── scope_access ├── src ├── ast │ ├── mod.rs │ └── tests.rs ├── generator.rs ├── main.rs └── scanner.rs └── test ├── LICENSE ├── README.md ├── stage_1 ├── invalid │ ├── missing_paren.c │ ├── missing_retval.c │ ├── no_brace.c │ ├── no_semicolon.c │ ├── no_space.c │ └── wrong_case.c └── valid │ ├── multi_digit.c │ ├── newlines.c │ ├── no_newlines.c │ ├── return_0.c │ ├── return_2.c │ └── spaces.c ├── stage_2 ├── invalid │ ├── missing_const.c │ ├── missing_semicolon.c │ ├── nested_missing_const.c │ └── wrong_order.c └── valid │ ├── bitwise.c │ ├── bitwise_zero.c │ ├── neg.c │ ├── nested_ops.c │ ├── nested_ops_2.c │ ├── not_five.c │ └── not_zero.c ├── stage_3 ├── invalid │ ├── malformed_paren.c │ ├── missing_first_op.c │ ├── missing_second_op.c │ └── no_semicolon.c └── valid │ ├── add.c │ ├── associativity.c │ ├── associativity_2.c │ ├── div.c │ ├── mult.c │ ├── parens.c │ ├── precedence.c │ ├── sub.c │ ├── sub_neg.c │ ├── unop_add.c │ └── unop_parens.c ├── stage_4 ├── invalid │ ├── missing_first_op.c │ ├── missing_mid_op.c │ ├── missing_second_op.c │ ├── missing_semicolon.c │ └── split_le.c └── valid │ ├── and_false.c │ ├── and_true.c │ ├── eq_false.c │ ├── eq_true.c │ ├── ge_false.c │ ├── ge_true.c │ ├── gt_false.c │ ├── gt_true.c │ ├── le_false.c │ ├── le_true.c │ ├── lt_false.c │ ├── lt_true.c │ ├── ne_false.c │ ├── ne_true.c │ ├── or_false.c │ ├── or_true.c │ ├── precedence.c │ ├── precedence_2.c │ ├── precedence_3.c │ └── precedence_4.c ├── stage_5 ├── invalid │ ├── redefine.c │ ├── syntax_err_bad_decl.c │ ├── syntax_err_bad_decl_2.c │ ├── syntax_err_bad_lvalue.c │ ├── syntax_err_bad_lvalue_2.c │ ├── syntax_err_no_semicolon.c │ ├── undeclared_var.c │ └── var_declared_late.c └── valid │ ├── assign.c │ ├── assign_val.c │ ├── compund_assignment.c │ ├── exp_return_val.c │ ├── initialize.c │ ├── missing_return.c │ ├── multiple_vars.c │ ├── no_initialize.c │ ├── refer.c │ └── unused_exp.c ├── stage_6 ├── invalid │ ├── expression │ │ ├── incomplete_ternary.c │ │ ├── malformed_ternary.c │ │ ├── malformed_ternary_2.c │ │ └── ternary_assign.c │ └── statement │ │ ├── declare_statement.c │ │ ├── if_assignment.c │ │ └── mismatched_nesting.c └── valid │ ├── expression │ ├── assign_ternary.c │ ├── multiple_ternary.c │ ├── nested_ternary.c │ ├── nested_ternary_2.c │ ├── rh_assignment.c │ └── ternary.c │ └── statement │ ├── else.c │ ├── if_nested.c │ ├── if_nested_2.c │ ├── if_nested_3.c │ ├── if_nested_4.c │ ├── if_nested_5.c │ ├── if_not_taken.c │ ├── if_taken.c │ └── multiple_if.c ├── stage_7 ├── invalid │ ├── double_define.c │ ├── out_of_scope.c │ ├── syntax_err_extra_brace.c │ └── syntax_err_missing_brace.c └── valid │ ├── comparison.c │ ├── consecutive_blocks.c │ ├── consecutive_declarations.c │ ├── declare_after_block.c │ ├── declare_block.c │ ├── declare_late.c │ ├── multi_nesting.c │ ├── nested_if.c │ └── nested_scope.c ├── stage_8 ├── invalid │ ├── break_not_in_loop.c │ ├── continue_not_in_loop.c │ ├── out_of_scope.c │ ├── out_of_scope_do_while.c │ ├── syntax_err_do_no_semicolon.c │ ├── syntax_err_empty_clause.c │ ├── syntax_err_paren_mismatch.c │ ├── syntax_err_statement_in_condition.c │ ├── syntax_err_too_few_for_clauses.c │ └── syntax_err_too_many_for_clauses.c └── valid │ ├── break.c │ ├── continue.c │ ├── continue_empty_post.c │ ├── do_while.c │ ├── empty_expression.c │ ├── for.c │ ├── for_decl.c │ ├── for_empty.c │ ├── for_nested_scope.c │ ├── for_variable_shadow.c │ ├── nested_break.c │ ├── nested_while.c │ ├── return_in_while.c │ ├── scope_access.c │ ├── while_multi_statement.c │ └── while_single_statement.c ├── stage_9 ├── invalid │ ├── bad_arg.c │ ├── declaration_mismatch.c │ ├── declaration_mismatch_2.c │ ├── redefine_function.c │ ├── redefine_variable.c │ └── too_many_args.c └── valid │ ├── expression_args.c │ ├── fib.c │ ├── forward_decl.c │ ├── forward_decl_args.c │ ├── forward_decl_multi_arg.c │ ├── fun_in_expr.c │ ├── hello_world.c │ ├── later_decl.c │ ├── multi_arg.c │ ├── mutual_recursion.c │ ├── no_arg.c │ ├── precedence.c │ ├── rename_function_param.c │ └── single_arg.c └── test_compiler.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.s 4 | *.exe 5 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "oxc" 5 | version = "0.1.0" 6 | dependencies = [ 7 | "peek-nth 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 8 | ] 9 | 10 | [[package]] 11 | name = "peek-nth" 12 | version = "0.2.0" 13 | source = "registry+https://github.com/rust-lang/crates.io-index" 14 | dependencies = [ 15 | "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 16 | ] 17 | 18 | [[package]] 19 | name = "smallvec" 20 | version = "0.6.9" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | 23 | [metadata] 24 | "checksum peek-nth 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb7a9ed6377ba979ee205df10c62755c97594d0aae99a34ea0b7c02766de1fed" 25 | "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "oxc" 3 | version = "0.1.0" 4 | authors = ["Luke Wilson <13669835+asmoaesl@users.noreply.github.com>"] 5 | 6 | [dependencies] 7 | peek-nth = "0.2.0" 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Luke Wilson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | A C compiler written in Rust for experimentation and understanding compilers. Should be easy to fork from and work with. 3 | Everything is handwritten and the compiler uses no third-party libraries. 4 | 5 | Please keep in mind, **the compiler is currently EXPERIMENTAL, and is NOT PRODUCTION READY.** 6 | 7 | The compiler is roughly following [this article](https://norasandler.com/2017/11/29/Write-a-Compiler.html). 8 | 9 | # Backend 10 | The compiler generates 32-bit AT&T-style assembly. The backend can be easily replaced with any other. The code generator uses a recursive descent style similar 11 | to the parser. 12 | See `src/generator.rs`. 13 | 14 | # Where The Language is Right Now 15 | All valid tests/stage_1 through tests/stage_8. 16 | 17 | # Testing 18 | ```rs 19 | $ cargo build 20 | ... 21 | $ .\target\debug\oxc.exe .\test\stage_7\valid\consecutive_declarations.c 22 | .\test\stage_7\valid\consecutive_declarations.c: 23 | int main() { 24 | int a = 0; 25 | { 26 | int b = 1; 27 | a = b; 28 | } 29 | { 30 | int b = 2; 31 | a = a + b; 32 | } 33 | return a; 34 | } 35 | 36 | Lexically analyzed in 0.000175057s: option 'lex' to print tokens 37 | 38 | Parsed in 0.0018207730000000001s: 39 | Function( 40 | Function( 41 | "main", 42 | [ 43 | Declaration( 44 | Declare( 45 | "a", 46 | Some( 47 | Const( 48 | 0 49 | ) 50 | ) 51 | ) 52 | ), 53 | Statement( 54 | Compound( 55 | [ 56 | Declaration( 57 | Declare( 58 | "b", 59 | Some( 60 | Const( 61 | 1 62 | ) 63 | ) 64 | ) 65 | ), 66 | Statement( 67 | Expr( 68 | Some( 69 | Assign( 70 | Assignment, 71 | "a", 72 | Var( 73 | "b" 74 | ) 75 | ) 76 | ) 77 | ) 78 | ) 79 | ] 80 | ) 81 | ), 82 | Statement( 83 | Compound( 84 | [ 85 | Declaration( 86 | Declare( 87 | "b", 88 | Some( 89 | Const( 90 | 2 91 | ) 92 | ) 93 | ) 94 | ), 95 | Statement( 96 | Expr( 97 | Some( 98 | Assign( 99 | Assignment, 100 | "a", 101 | BinOp( 102 | Plus, 103 | Var( 104 | "a" 105 | ), 106 | Var( 107 | "b" 108 | ) 109 | ) 110 | ) 111 | ) 112 | ) 113 | ) 114 | ] 115 | ) 116 | ), 117 | Statement( 118 | Return( 119 | Var( 120 | "a" 121 | ) 122 | ) 123 | ) 124 | ] 125 | ) 126 | ) 127 | 128 | Generated assembly in 0.097695794s: 129 | .globl _main 130 | _main: 131 | push %ebp 132 | movl %esp, %ebp 133 | movl $0, %eax 134 | movl %eax, -4(%ebp) 135 | movl $1, %eax 136 | movl %eax, -8(%ebp) 137 | movl -8(%ebp), %eax 138 | movl %eax, -4(%ebp) 139 | addl $4, %esp 140 | movl $2, %eax 141 | movl %eax, -12(%ebp) 142 | movl -4(%ebp), %eax 143 | movl %eax, %ecx 144 | movl -12(%ebp), %eax 145 | addl %ecx, %eax 146 | movl %eax, -4(%ebp) 147 | addl $4, %esp 148 | movl -4(%ebp), %eax 149 | jmp _main_epilogue 150 | addl $4, %esp 151 | movl $0, %eax 152 | _main_epilogue: 153 | movl %ebp, %esp 154 | pop %ebp 155 | ret 156 | 157 | Assembling... 158 | Assembled in 0.218045083s 159 | ``` 160 | -------------------------------------------------------------------------------- /grammar.ebnf: -------------------------------------------------------------------------------- 1 | program = function; 2 | function = "int", identifier, "(", ")", "{", { block_item }, "}"; 3 | block_item = statement | declaration ; 4 | declaration = "int", identifier, [ "=", expr ], ";" ; 5 | statement = "return", expr, ";" 6 | | expr_option (* null statement *) 7 | | "if", "(", expr, ")", statement, [ "else", statement ] 8 | | "{", { block_item }, "}" 9 | | "for", "(", expr_option, ";", expr_option, ";", expr_option, ")", statement 10 | | "for", "(", declaration, expr_option, ";", expr_option, ")", statement 11 | | "while", "(", expr, ")", statement 12 | | "do", statement, "while", "(", expr, ")", ";" 13 | | "break", ";" 14 | | "continue", ";" ; 15 | expr_option = expr | ";" ; 16 | expr = identifier, assignment_operator, expr 17 | | conditional_expr ; 18 | conditional_expr = logical_or_expr, [ "?", expr, ":", conditional_expr ] ; 19 | logical_or_expr = logical_and_expr, { "||", logical_and_expr }; 20 | logical_and_expr = equality_expr, { "&&", equality_expr }; 21 | equality_expr = relational_expr, { ("!=" | "=="), relational_expr }; 22 | relational_expr = bitwise_expr, { ("<" | ">" | "<=" | ">="), bitwise_expr }; 23 | bitwise_expr = additive_expr, { ("&" | "|" | "^" | "<<" | ">>"), additive_expr }; 24 | additive_expr = term, { ("+" | "-"), term }; 25 | term = factor, { ("*" | "/" | "%"), factor }; 26 | factor = "(", expr, ")" 27 | | unary_op, factor 28 | | int 29 | | identifier ; 30 | unary_op = "!" | "~" | "-"; 31 | assignment_operator = "=" | "+=" | "-="; 32 | -------------------------------------------------------------------------------- /scope_access: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lukewilson2002/oxc/5e0388fb5c258030b78765a32e984fea62a0f911/scope_access -------------------------------------------------------------------------------- /src/ast/mod.rs: -------------------------------------------------------------------------------- 1 | mod tests; 2 | 3 | use scanner::*; 4 | use std::slice::Iter; 5 | use std::iter::Peekable; 6 | use peek_nth::{PeekableNth, IteratorExt}; 7 | 8 | macro_rules! expect_token_eq { 9 | ($token_option:expr, $($token:expr)+, $message:expr) => { 10 | if let Some(t) = $token_option { 11 | if $(t != $token)||+ { 12 | panic!("Unexpected token: {:?}, {:?}", t.token, $message); 13 | } 14 | } else { 15 | panic!($message); 16 | } 17 | }; 18 | } 19 | 20 | #[derive(Debug, Clone, PartialEq)] 21 | pub enum Expr { 22 | Const(i32), 23 | Assign(Operator, String, Box), 24 | Var(String), 25 | BinOp(Operator, Box, Box), // op, lhs, rhs 26 | UnaryOp(Operator, Box), 27 | Conditional(Box, Box, Box), // ternary if: expr1 ? expr2 : expr3 28 | } 29 | 30 | #[derive(Debug, Clone, PartialEq)] 31 | pub enum Statement { 32 | Return(Expr), // Return statement 33 | Expr(Option), // Any expression 34 | Conditional(Expr, Box, Option>), // if (expr) statement1 else statement2 35 | Compound(Vec), // { int foo = 2; foo += 3; } 36 | // for (initial clause; controlling expression; post-expression) 37 | ForExpr(Option, Expr, Option, Box), // for (;;) say_hello(); 38 | ForDecl(Declaration, Expr, Option, Box), // for (int a = 0; a < 3; a++) say_hello(); 39 | While(Expr, Box), 40 | Do(Box, Expr), 41 | Break, 42 | Continue, 43 | } 44 | 45 | #[derive(Debug, Clone, PartialEq)] 46 | pub enum Declaration { 47 | Declare(String, Option), // Variable declaration 48 | } 49 | 50 | #[derive(Debug, Clone, PartialEq)] 51 | pub enum BlockItem { 52 | Statement(Statement), 53 | Declaration(Declaration), 54 | } 55 | 56 | #[derive(Debug, Clone, PartialEq)] 57 | pub enum FunctionDeclaration { 58 | Function(String, Vec), 59 | } 60 | 61 | #[derive(Debug, Clone, PartialEq)] 62 | pub enum Program { 63 | Function(FunctionDeclaration), 64 | } 65 | 66 | // program = function; 67 | pub fn parse(tokens: &[TokenData]) -> Program { 68 | parse_function(&mut tokens.iter().peekable_nth()) 69 | } 70 | 71 | // function = "int", identifier, "(", ")", "{", { block_item }, "}"; 72 | fn parse_function(tokens: &mut PeekableNth>) -> Program { 73 | expect_token_eq!(tokens.next(), &Token::Keyword(Keyword::Int), "Expected keyword 'int' to begin function"); 74 | 75 | match tokens.next() { 76 | Some(t) => match &t.token { 77 | Token::Id(id) => { 78 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::LParen), "Expected '(' after 'int'"); 79 | 80 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RParen), "Expected ')' to end function arguments"); 81 | 82 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::LBrace), "Expected '{' after function signature to begin statements"); 83 | 84 | // Parse block items 85 | let mut block_items = Vec::::new(); 86 | loop { 87 | match tokens.peek() { 88 | Some(&t) if t != &Token::Symbol(Symbol::RBrace) => block_items.push(parse_block_item(tokens)), 89 | _ => break, 90 | } 91 | } 92 | 93 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RBrace), "Expected closing brace to finish function definition"); 94 | 95 | return Program::Function(FunctionDeclaration::Function(id.to_owned(), block_items)); 96 | } 97 | _ => panic!("Expected identifier on function"), 98 | } 99 | _ => panic!("Expected identifier on function"), 100 | } 101 | } 102 | 103 | // block_item = statement | declaration ; 104 | fn parse_block_item(tokens: &mut PeekableNth>) -> BlockItem { 105 | match tokens.peek() { 106 | Some(&t) if t == &Token::Keyword(Keyword::Int) => BlockItem::Declaration(parse_declaration(tokens)), 107 | _ => BlockItem::Statement(parse_statement(tokens)), 108 | } 109 | } 110 | 111 | // declaration = "int", identifier, [ "=", expr ], ";" ; 112 | fn parse_declaration(tokens: &mut PeekableNth>) -> Declaration { 113 | match tokens.next() { 114 | Some(t) if t == &Token::Keyword(Keyword::Int) => { 115 | match tokens.next() { 116 | Some(t) => match &t.token { 117 | Token::Id(id) => { 118 | let declaration; 119 | 120 | match tokens.peek() { 121 | Some(&t) if t == &Token::Operator(Operator::Assignment) => { 122 | tokens.next(); // Consume '=' 123 | declaration = Declaration::Declare(id.clone(), Some(parse_expr(tokens))); 124 | } 125 | _ => declaration = Declaration::Declare(id.clone(), None), 126 | } 127 | 128 | match tokens.next() { 129 | Some(t) if t == &Token::Symbol(Symbol::Semicolon) => return declaration, 130 | _ => panic!("Expected semicolon at end of declaration: {:?}", tokens), 131 | } 132 | } 133 | _ => panic!("Expected identifier for integer declaration"), 134 | } 135 | _ => panic!("Expected identifier for integer declaration"), 136 | } 137 | } 138 | _ => panic!("Expected type 'int' for declaration"), 139 | } 140 | } 141 | 142 | // statement = "return", expr, ";" 143 | // | expr, ";" 144 | // | "if", "(", expr, ")", statement, [ "else", statement ] ; 145 | fn parse_statement(tokens: &mut PeekableNth>) -> Statement { 146 | let statement: Statement; 147 | match tokens.peek_nth(0) { 148 | Some(&t) => match t.token { 149 | Token::Keyword(Keyword::Return) => { 150 | tokens.next(); 151 | statement = Statement::Return(parse_expr(tokens)); 152 | 153 | match tokens.next() { 154 | Some(t) if t == &Token::Symbol(Symbol::Semicolon) => return statement, 155 | _ => panic!("Expected semicolon at end of statement: {:?}", tokens), 156 | } 157 | } 158 | Token::Keyword(Keyword::If) => { 159 | tokens.next(); 160 | 161 | match tokens.next() { 162 | Some(t) if t == &Token::Symbol(Symbol::LParen) => { 163 | let condition = parse_expr(tokens); 164 | match tokens.next() { 165 | Some(t) if t == &Token::Symbol(Symbol::RParen) => { 166 | let true_statement = parse_statement(tokens); 167 | match tokens.peek() { 168 | Some(&t) if t == &Token::Keyword(Keyword::Else) => { 169 | tokens.next(); 170 | return Statement::Conditional(condition, Box::new(true_statement), Some(Box::new(parse_statement(tokens)))) 171 | } 172 | _ => return Statement::Conditional(condition, Box::new(true_statement), None), 173 | } 174 | } 175 | _ => panic!("Expected closing parenthesis after condition on 'if' statement"), 176 | } 177 | } 178 | _ => panic!("Expected opening parenthesis before condition on 'if' statement"), 179 | } 180 | } 181 | Token::Symbol(Symbol::LBrace) => { 182 | tokens.next(); // consume opening brace 183 | 184 | let mut block_items = Vec::::new(); 185 | loop { 186 | match tokens.peek() { 187 | Some(&t) if t != &Token::Symbol(Symbol::RBrace) => block_items.push(parse_block_item(tokens)), 188 | _ => break, 189 | } 190 | } 191 | 192 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RBrace), "Expected closing brace after compound statement"); 193 | 194 | return Statement::Compound(block_items); 195 | } 196 | Token::Keyword(Keyword::For) => { 197 | tokens.next(); // consume 'for' 198 | 199 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::LParen), "Expected '(' after 'for'"); 200 | 201 | match tokens.peek() { 202 | Some(&t) => match t.token { 203 | Token::Keyword(kwd) if kwd.is_type() => { // ForDecl 204 | let initial_clause = parse_declaration(tokens); 205 | // no need to consume semicolon; declaration requires one 206 | let controlling_expr = parse_expr_option(tokens); 207 | 208 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::Semicolon), "Expected ';' after second expression inside 'for' loop"); 209 | 210 | let post_expr = parse_expr_option(tokens); 211 | 212 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RParen), "Expected ')' after 'for' expression"); 213 | 214 | return Statement::ForDecl(initial_clause, match controlling_expr { 215 | Some(expr) => expr, 216 | None => Expr::Const(1), 217 | }, post_expr, Box::new(parse_statement(tokens))); 218 | } 219 | _ => { // ForExpr 220 | let initial_clause = parse_expr_option(tokens); 221 | 222 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::Semicolon), "Expected ';' after first expression inside 'for' loop"); 223 | 224 | let controlling_expr = parse_expr_option(tokens); 225 | 226 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::Semicolon), "Expected ';' after second expression inside 'for' loop"); 227 | 228 | let post_expr = parse_expr_option(tokens); 229 | 230 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RParen), "Expected ')' after 'for' expression"); 231 | 232 | return Statement::ForExpr(initial_clause, match controlling_expr { 233 | Some(expr) => expr, 234 | None => Expr::Const(1), 235 | }, post_expr, Box::new(parse_statement(tokens))); 236 | } 237 | } 238 | _ => panic!("Expected expression or variable declaration"), 239 | } 240 | } 241 | Token::Keyword(Keyword::While) => { 242 | tokens.next(); // consume 'while' 243 | 244 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::LParen), "Expected '(' after 'while'"); 245 | 246 | let expr = parse_expr(tokens); 247 | 248 | expect_token_eq!(tokens.next(), &Token::Symbol(Symbol::RParen), "Expected ')' after expression on 'while'"); 249 | 250 | return Statement::While(expr, Box::new(parse_statement(tokens))); 251 | } 252 | Token::Keyword(Keyword::Do) => { 253 | tokens.next(); // consume 'do' 254 | 255 | let statement = parse_statement(tokens); 256 | 257 | if let Some(t) = tokens.next() { 258 | if t != &Token::Keyword(Keyword::While) { 259 | panic!("Expected semicolon after statement"); 260 | } 261 | } 262 | 263 | let expr = parse_expr(tokens); 264 | 265 | if let Some(t) = tokens.next() { 266 | if t != &Token::Symbol(Symbol::RParen) { 267 | panic!("Expected semicolon after statement"); 268 | } 269 | } 270 | 271 | return Statement::Do(Box::new(statement), expr); 272 | } 273 | Token::Keyword(Keyword::Break) => { 274 | tokens.next(); 275 | 276 | if let Some(t) = tokens.next() { 277 | if t != &Token::Symbol(Symbol::Semicolon) { 278 | panic!("Expected semicolon after statement"); 279 | } 280 | } 281 | 282 | return Statement::Break; 283 | } 284 | Token::Keyword(Keyword::Continue) => { 285 | tokens.next(); 286 | 287 | if let Some(t) = tokens.next() { 288 | if t != &Token::Symbol(Symbol::Semicolon) { 289 | panic!("Expected semicolon after statement"); 290 | } 291 | } 292 | 293 | return Statement::Continue; 294 | } 295 | _ => { 296 | statement = Statement::Expr(parse_expr_option(tokens)); 297 | 298 | match tokens.next() { 299 | Some(t) if t == &Token::Symbol(Symbol::Semicolon) => return statement, 300 | _ => panic!("Expected semicolon at end of statement: {:?}", tokens), 301 | } 302 | } 303 | } 304 | None => panic!("Expected statement"), 305 | } 306 | } 307 | 308 | // expr_option = expr, ";" | ";" ; 309 | fn parse_expr_option(tokens: &mut PeekableNth>) -> Option { 310 | match tokens.peek() { 311 | Some(t) => match t.token { 312 | Token::Symbol(Symbol::Semicolon) | Token::Symbol(Symbol::RParen) => return None, 313 | _ => {} 314 | } 315 | _ => {} 316 | } 317 | 318 | let expression = parse_expr(tokens); 319 | match tokens.peek() { 320 | Some(t) if t == &&Token::Symbol(Symbol::Semicolon) || t == &&Token::Symbol(Symbol::RParen) => Some(expression), 321 | _ => panic!("Expected semicolon after optional expression"), 322 | } 323 | } 324 | 325 | // expr = identifier, assignment_operator, expr 326 | // | conditional_expr ; 327 | fn parse_expr(tokens: &mut PeekableNth>) -> Expr { 328 | match tokens.peek_nth(0) { 329 | Some(&t) => match &t.token { 330 | Token::Id(id) => { 331 | match tokens.peek_nth(1) { 332 | Some(&t) => match t.token { 333 | Token::Operator(op) if op.is_assignment() => { 334 | tokens.next(); // Consume identifier 335 | tokens.next(); // Consume assignment operator 336 | Expr::Assign(op, id.clone(), Box::new(parse_expr(tokens))) 337 | } 338 | _ => parse_conditional_expr(tokens), 339 | } 340 | _ => parse_conditional_expr(tokens), 341 | } 342 | } 343 | _ => parse_conditional_expr(tokens), 344 | } 345 | _ => parse_conditional_expr(tokens), 346 | } 347 | } 348 | 349 | // conditional_expr = logical_or_expr, [ "?", expr, ":", conditional_expr ] ; 350 | fn parse_conditional_expr(tokens: &mut PeekableNth>) -> Expr { 351 | let expr1 = parse_logical_or_expr(tokens); 352 | match tokens.peek_nth(0) { 353 | Some(&t) if t == &Token::Operator(Operator::QuestionMark) => { 354 | tokens.next(); 355 | let expr2 = parse_expr(tokens); 356 | match tokens.next() { 357 | Some(t) if t == &Token::Symbol(Symbol::Colon) => { 358 | Expr::Conditional(Box::new(expr1), Box::new(expr2), Box::new(parse_conditional_expr(tokens))) 359 | } 360 | _ => panic!("Expected colon in ternary expression"), 361 | } 362 | } 363 | _ => expr1, 364 | } 365 | } 366 | 367 | fn parse_logical_or_expr(tokens: &mut PeekableNth>) -> Expr { // logical_or_expr = logical_and_expr, { "||", logical_and_expr } 368 | let mut expr = parse_logical_and_expr(tokens); 369 | loop { 370 | match tokens.peek_nth(0) { 371 | Some(&t) => match t.token { 372 | Token::Operator(peek) if peek == Operator::Or => { 373 | let mut op = match tokens.next().unwrap().token { 374 | Token::Operator(oper) => oper, 375 | _ => unreachable!() // impossible 376 | }; 377 | 378 | let next_expr = parse_logical_and_expr(tokens); 379 | expr = Expr::BinOp(op, Box::new(expr), Box::new(next_expr)); 380 | } 381 | _ => break, // no more matches 382 | } 383 | _ => break, // no more matches 384 | } 385 | } 386 | expr 387 | } 388 | 389 | fn parse_logical_and_expr(tokens: &mut PeekableNth>) -> Expr { // logical_and_expr = equality_expr, { "&&", equality_expr } 390 | let mut expr = parse_equality_expr(tokens); 391 | loop { 392 | match tokens.peek_nth(0) { 393 | Some(&t) => match t.token { 394 | Token::Operator(peek) if peek == Operator::And => { 395 | let mut op = match tokens.next().unwrap().token { 396 | Token::Operator(oper) => oper, 397 | _ => unreachable!() // impossible 398 | }; 399 | 400 | let next_expr = parse_equality_expr(tokens); 401 | expr = Expr::BinOp(op, Box::new(expr), Box::new(next_expr)); 402 | } 403 | _ => break, // no more matches 404 | } 405 | _ => break, // no more matches 406 | } 407 | } 408 | expr 409 | } 410 | 411 | fn parse_equality_expr(tokens: &mut PeekableNth>) -> Expr { // equality_expr = relational_expr, { ("!=" | "=="), relational_expr } 412 | let mut expr = parse_relational_expr(tokens); 413 | loop { 414 | match tokens.peek_nth(0) { 415 | Some(&t) => match t.token { 416 | Token::Operator(peek) if peek == Operator::NotEqual || peek == Operator::EqualEqual => { 417 | let mut op = match tokens.next().unwrap().token { 418 | Token::Operator(oper) => oper, 419 | _ => unreachable!() // impossible 420 | }; 421 | 422 | let next_expr = parse_relational_expr(tokens); 423 | expr = Expr::BinOp(op, Box::new(expr), Box::new(next_expr)); 424 | } 425 | _ => break, // no more matches 426 | } 427 | _ => break, // no more matches 428 | } 429 | } 430 | expr 431 | } 432 | 433 | fn parse_relational_expr(tokens: &mut PeekableNth>) -> Expr { // relational_expr = bitwise_expr, { ("<" | ">" | "<=" | ">="), bitwise_expr } 434 | let mut expr = parse_bitwise_expr(tokens); 435 | loop { 436 | match tokens.peek_nth(0) { 437 | Some(&t) => match t.token { 438 | Token::Operator(peek) 439 | if peek == Operator::LessThan || peek == Operator::GreaterThan || 440 | peek == Operator::LessEqual || peek == Operator::GreaterEqual => { 441 | let mut op = match tokens.next().unwrap().token { 442 | Token::Operator(oper) => oper, 443 | _ => unreachable!() // impossible 444 | }; 445 | 446 | let next_expr = parse_bitwise_expr(tokens); 447 | expr = Expr::BinOp(op, Box::new(expr), Box::new(next_expr)); 448 | } 449 | _ => break, // no more matches 450 | } 451 | _ => break, // no more matches 452 | } 453 | } 454 | expr 455 | } 456 | 457 | fn parse_bitwise_expr(tokens: &mut PeekableNth>) -> Expr { // bitwise_expr = additive_expr, { ("&" | "|" | "^" | "<<" | ">>"), additive_expr } 458 | // Bitwise expressions like 2 & 1, 2 ^ 1, etc. 459 | let mut term = parse_additive_expr(tokens); 460 | loop { 461 | match tokens.peek_nth(0) { 462 | Some(&t) => match t.token { 463 | Token::Operator(peek) if peek.is_bitwise() => { 464 | let mut op = match tokens.next().unwrap().token { 465 | Token::Operator(oper) => oper, 466 | _ => unreachable!() // impossible 467 | }; 468 | 469 | let next_term = parse_additive_expr(tokens); 470 | term = Expr::BinOp(op, Box::new(term), Box::new(next_term)); 471 | } 472 | _ => break, // no more matches 473 | } 474 | _ => break, // no more matches 475 | } 476 | } 477 | term 478 | } 479 | 480 | fn parse_additive_expr(tokens: &mut PeekableNth>) -> Expr { // additive_expr = term, { ("+" | "-"), term } 481 | // Number expressions like 1+1 or 2+3*2 being 2+(3*2) using associativity 482 | let mut term = parse_term(tokens); 483 | loop { 484 | match tokens.peek_nth(0) { 485 | Some(&t) => match t.token { 486 | Token::Operator(Operator::Plus) | Token::Operator(Operator::Minus) => { 487 | let mut op = match tokens.next().unwrap().token { 488 | Token::Operator(oper) => oper, 489 | _ => unreachable!() // impossible 490 | }; 491 | 492 | let next_term = parse_term(tokens); 493 | term = Expr::BinOp(op, Box::new(term), Box::new(next_term)); 494 | } 495 | _ => break, // no more matches 496 | } 497 | _ => break, // no more matches 498 | } 499 | } 500 | term 501 | } 502 | 503 | fn parse_term(tokens: &mut PeekableNth>) -> Expr { // term = factor, { ("*" | "/" | "%"), factor } 504 | let mut term = parse_factor(tokens); 505 | loop { 506 | match tokens.peek_nth(0) { 507 | Some(&t) => match t.token { 508 | Token::Operator(peek) if peek == Operator::Star || peek == Operator::Slash || peek == Operator::Modulo => { 509 | // More terms 510 | let mut op = match tokens.next().unwrap().token { 511 | Token::Operator(oper) => oper, 512 | _ => unreachable!(), // impossible 513 | }; 514 | 515 | let next_term = parse_factor(tokens); 516 | term = Expr::BinOp(op, Box::new(term), Box::new(next_term)); 517 | } 518 | _ => break, // no more matches 519 | } 520 | _ => break, // no more matches 521 | } 522 | } 523 | term 524 | } 525 | 526 | // factor = "(", expr, ")" 527 | // | unary_op, factor 528 | // | int 529 | // | identifier ; 530 | fn parse_factor(tokens: &mut PeekableNth>) -> Expr { 531 | match tokens.next() { 532 | Some(t) => match &t.token { 533 | Token::Symbol(Symbol::LParen) => { // factor = "(", expression, ")" 534 | let expr = parse_expr(tokens); // parse expression in parenthesis 535 | match tokens.next() { // closing parenthesis 536 | Some(t) if t == &Token::Symbol(Symbol::RParen) => return expr, 537 | _ => panic!("Missing closing parenthesis on expression"), 538 | } 539 | } 540 | Token::Operator(op) if op.is_unary() => { // factor = unary_op, factor 541 | let factor = parse_factor(tokens); 542 | return Expr::UnaryOp(*op, Box::new(factor)); 543 | } 544 | Token::Integer(num) => return Expr::Const(*num), // factor = int 545 | Token::Id(id) => return Expr::Var(id.clone()), 546 | _ => panic!("Expected factor: {:?}", tokens), 547 | } 548 | _ => panic!("Expected factor: {:?}", tokens), 549 | } 550 | } 551 | -------------------------------------------------------------------------------- /src/ast/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use super::{*, Expr::*, Statement::*, Declaration::*, BlockItem::*, FunctionDeclaration::*, Program::*}; 4 | use scanner::{*, Keyword::*, Symbol::*, Operator::*, Token::*}; 5 | 6 | #[test] 7 | fn simple_main_return_zero() { 8 | use scanner::*; 9 | use scanner::Keyword::*; 10 | let tokens = [ 11 | TokenData { 12 | token: Keyword( 13 | Int, 14 | ), 15 | slice: "int", 16 | line: 1, 17 | col: 1, 18 | }, 19 | TokenData { 20 | token: Id( 21 | "main".to_owned(), 22 | ), 23 | slice: "main", 24 | line: 1, 25 | col: 5, 26 | }, 27 | TokenData { 28 | token: Symbol( 29 | LParen, 30 | ), 31 | slice: "(", 32 | line: 1, 33 | col: 10, 34 | }, 35 | TokenData { 36 | token: Symbol( 37 | RParen, 38 | ), 39 | slice: ")", 40 | line: 1, 41 | col: 11, 42 | }, 43 | TokenData { 44 | token: Symbol( 45 | LBrace, 46 | ), 47 | slice: "{", 48 | line: 1, 49 | col: 13, 50 | }, 51 | TokenData { 52 | token: Keyword( 53 | Return, 54 | ), 55 | slice: "return", 56 | line: 2, 57 | col: 4, 58 | }, 59 | TokenData { 60 | token: Integer( 61 | 0, 62 | ), 63 | slice: "0", 64 | line: 2, 65 | col: 11, 66 | }, 67 | TokenData { 68 | token: Symbol( 69 | Semicolon, 70 | ), 71 | slice: ";", 72 | line: 2, 73 | col: 13, 74 | }, 75 | TokenData { 76 | token: Symbol( 77 | RBrace, 78 | ), 79 | slice: "}", 80 | line: 3, 81 | col: 1, 82 | }, 83 | ]; 84 | 85 | let ast = parse(&tokens); 86 | 87 | let expected_ast = Program::Function(FunctionDeclaration::Function("main".to_owned(), vec!(BlockItem::Statement(super::Statement::Return(super::Expr::Const(0)))))); 88 | 89 | assert_eq!(ast, expected_ast); 90 | } 91 | 92 | #[test] 93 | fn simple_variables() { 94 | use scanner::*; 95 | use scanner::Keyword::*; 96 | let tokens = [ 97 | TokenData { 98 | token: Keyword( 99 | Int, 100 | ), 101 | slice: "int", 102 | line: 1, 103 | col: 1, 104 | }, 105 | TokenData { 106 | token: Id( 107 | "main".to_owned(), 108 | ), 109 | slice: "main", 110 | line: 1, 111 | col: 5, 112 | }, 113 | TokenData { 114 | token: Symbol( 115 | LParen, 116 | ), 117 | slice: "(", 118 | line: 1, 119 | col: 10, 120 | }, 121 | TokenData { 122 | token: Symbol( 123 | RParen, 124 | ), 125 | slice: ")", 126 | line: 1, 127 | col: 11, 128 | }, 129 | TokenData { 130 | token: Symbol( 131 | LBrace, 132 | ), 133 | slice: "{", 134 | line: 1, 135 | col: 12, 136 | }, 137 | TokenData { 138 | token: Keyword( 139 | Int, 140 | ), 141 | slice: "int", 142 | line: 2, 143 | col: 4, 144 | }, 145 | TokenData { 146 | token: Id( 147 | "a".to_owned(), 148 | ), 149 | slice: "a", 150 | line: 2, 151 | col: 8, 152 | }, 153 | TokenData { 154 | token: Operator( 155 | Assignment, 156 | ), 157 | slice: "=", 158 | line: 2, 159 | col: 11, 160 | }, 161 | TokenData { 162 | token: Integer( 163 | 2, 164 | ), 165 | slice: "2", 166 | line: 2, 167 | col: 12, 168 | }, 169 | TokenData { 170 | token: Symbol( 171 | Semicolon, 172 | ), 173 | slice: ";", 174 | line: 2, 175 | col: 14, 176 | }, 177 | TokenData { 178 | token: Keyword( 179 | If, 180 | ), 181 | slice: "if", 182 | line: 3, 183 | col: 4, 184 | }, 185 | TokenData { 186 | token: Symbol( 187 | LParen, 188 | ), 189 | slice: "(", 190 | line: 3, 191 | col: 8, 192 | }, 193 | TokenData { 194 | token: Id( 195 | "a".to_owned(), 196 | ), 197 | slice: "a", 198 | line: 3, 199 | col: 8, 200 | }, 201 | TokenData { 202 | token: Operator( 203 | LessThan, 204 | ), 205 | slice: "<", 206 | line: 3, 207 | col: 11, 208 | }, 209 | TokenData { 210 | token: Integer( 211 | 3, 212 | ), 213 | slice: "3", 214 | line: 3, 215 | col: 12, 216 | }, 217 | TokenData { 218 | token: Symbol( 219 | RParen, 220 | ), 221 | slice: ")", 222 | line: 3, 223 | col: 14, 224 | }, 225 | TokenData { 226 | token: Symbol( 227 | LBrace, 228 | ), 229 | slice: "{", 230 | line: 3, 231 | col: 16, 232 | }, 233 | TokenData { 234 | token: Symbol( 235 | LBrace, 236 | ), 237 | slice: "{", 238 | line: 4, 239 | col: 9, 240 | }, 241 | TokenData { 242 | token: Keyword( 243 | Int, 244 | ), 245 | slice: "int", 246 | line: 5, 247 | col: 12, 248 | }, 249 | TokenData { 250 | token: Id( 251 | "a".to_owned(), 252 | ), 253 | slice: "a", 254 | line: 5, 255 | col: 16, 256 | }, 257 | TokenData { 258 | token: Operator( 259 | Assignment, 260 | ), 261 | slice: "=", 262 | line: 5, 263 | col: 19, 264 | }, 265 | TokenData { 266 | token: Integer( 267 | 3, 268 | ), 269 | slice: "3", 270 | line: 5, 271 | col: 20, 272 | }, 273 | TokenData { 274 | token: Symbol( 275 | Semicolon, 276 | ), 277 | slice: ";", 278 | line: 5, 279 | col: 22, 280 | }, 281 | TokenData { 282 | token: Keyword( 283 | Return, 284 | ), 285 | slice: "return", 286 | line: 6, 287 | col: 12, 288 | }, 289 | TokenData { 290 | token: Id( 291 | "a".to_owned(), 292 | ), 293 | slice: "a", 294 | line: 6, 295 | col: 19, 296 | }, 297 | TokenData { 298 | token: Symbol( 299 | Semicolon, 300 | ), 301 | slice: ";", 302 | line: 6, 303 | col: 21, 304 | }, 305 | TokenData { 306 | token: Symbol( 307 | RBrace, 308 | ), 309 | slice: "}", 310 | line: 7, 311 | col: 9, 312 | }, 313 | TokenData { 314 | token: Keyword( 315 | Return, 316 | ), 317 | slice: "return", 318 | line: 8, 319 | col: 8, 320 | }, 321 | TokenData { 322 | token: Id( 323 | "a".to_owned(), 324 | ), 325 | slice: "a", 326 | line: 8, 327 | col: 15, 328 | }, 329 | TokenData { 330 | token: Symbol( 331 | Semicolon, 332 | ), 333 | slice: ";", 334 | line: 8, 335 | col: 17, 336 | }, 337 | TokenData { 338 | token: Symbol( 339 | RBrace, 340 | ), 341 | slice: "}", 342 | line: 9, 343 | col: 5, 344 | }, 345 | TokenData { 346 | token: Symbol( 347 | RBrace, 348 | ), 349 | slice: "}", 350 | line: 10, 351 | col: 1, 352 | }, 353 | ]; 354 | 355 | let ast = parse(&tokens); 356 | 357 | let expected_ast = Program::Function( 358 | FunctionDeclaration::Function( 359 | "main".to_owned(), 360 | vec![ 361 | Declaration( 362 | Declare( 363 | "a".to_owned(), 364 | Some( 365 | Const( 366 | 2, 367 | ), 368 | ), 369 | ), 370 | ), 371 | Statement( 372 | super::Statement::Conditional( 373 | BinOp( 374 | LessThan, 375 | Box::new(Var( 376 | "a".to_owned(), 377 | )), 378 | Box::new(Const( 379 | 3, 380 | )), 381 | ), 382 | Box::new(Compound( 383 | vec![ 384 | Statement( 385 | Compound( 386 | vec![ 387 | Declaration( 388 | Declare( 389 | "a".to_owned(), 390 | Some( 391 | Const( 392 | 3, 393 | ), 394 | ), 395 | ), 396 | ), 397 | Statement( 398 | super::Statement::Return( 399 | Var( 400 | "a".to_owned(), 401 | ), 402 | ), 403 | ), 404 | ], 405 | ), 406 | ), 407 | Statement( 408 | super::Statement::Return( 409 | Var( 410 | "a".to_owned(), 411 | ), 412 | ), 413 | ), 414 | ], 415 | )), 416 | None, 417 | ), 418 | ), 419 | ], 420 | ), 421 | ); 422 | 423 | assert_eq!(ast, expected_ast); 424 | } 425 | -------------------------------------------------------------------------------- /src/generator.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ast::*; 4 | use scanner::*; 5 | 6 | type VariableMap = HashMap; // name, location on stack 7 | 8 | /// Gives priority to map2. map2 should always be the current scope. 9 | fn merge_maps<'a>(map1: &'a VariableMap, map2: &'a VariableMap) -> VariableMap { 10 | let mut new_map = map1.clone(); 11 | for (k, &v) in map2 { 12 | *new_map.entry(k.clone()).or_insert(0) = v; 13 | } 14 | new_map 15 | } 16 | 17 | #[derive(Debug, PartialEq)] 18 | struct Context<'a> { 19 | function_name: String, 20 | outer_scope: &'a VariableMap, 21 | current_scope: &'a mut VariableMap, 22 | stack_index: &'a mut isize, 23 | counter: &'a mut u32, 24 | loop_label_begin: Option, 25 | loop_label_end: Option, 26 | } 27 | 28 | impl<'a> Context<'a> { 29 | pub fn new(function_name: String, outer_scope: &'a VariableMap, current_scope: &'a mut VariableMap, stack_index: &'a mut isize, counter: &'a mut u32, loop_label_begin: Option, loop_label_end: Option) -> Self { 30 | Context { 31 | function_name, 32 | outer_scope, 33 | current_scope, 34 | stack_index, 35 | counter, 36 | loop_label_begin, 37 | loop_label_end, 38 | } 39 | } 40 | } 41 | 42 | fn generate_expression(expression: &Option<&Expr>, context: &mut Context) -> String { 43 | match expression { 44 | Some(Expr::Const(num)) => return format!(" movl ${}, %eax\n", num), 45 | Some(Expr::BinOp(op, lhs, rhs)) => { 46 | let mut generated: String; 47 | match op { 48 | Operator::Plus | Operator::Minus | Operator::Star | Operator::Slash | Operator::Modulo => { 49 | // We reverse who is in ecx register because subtraction is dst - src -> dst. 50 | // Otherwise we'd have to `movl %ecx, %eax`. This is an optimization. 51 | if *op == Operator::Minus || *op == Operator::Slash { 52 | generated = generate_expression(&Some(&**rhs), context); 53 | generated.push_str(" movl %eax, %ecx\n"); // rhs is now in ecx register 54 | generated.push_str(&generate_expression(&Some(&**lhs), context)); 55 | } else { 56 | generated = generate_expression(&Some(&**lhs), context); 57 | generated.push_str(" movl %eax, %ecx\n"); // lhs is now in ecx register 58 | generated.push_str(&generate_expression(&Some(&**rhs), context)); 59 | } 60 | 61 | match op { 62 | Operator::Plus => generated.push_str(" addl %ecx, %eax\n"), 63 | Operator::Minus => generated.push_str(" subl %ecx, %eax\n"), 64 | Operator::Star => generated.push_str(" imull %ecx\n"), 65 | Operator::Slash => generated.push_str(" idivl %ecx\n movl %ecx, %eax\n"), 66 | Operator::Modulo => generated.push_str(" idivl %ecx\n movl %edx, %eax\n"), 67 | _ => unimplemented!() 68 | } 69 | } 70 | Operator::EqualEqual | Operator::NotEqual | // Equality and comparison 71 | Operator::LessThan | Operator::LessEqual | 72 | Operator::GreaterThan | Operator::GreaterEqual => { 73 | generated = generate_expression(&Some(&**rhs), context); 74 | generated.push_str(" push %eax\n"); 75 | generated.push_str(&generate_expression(&Some(&**lhs), context)); // lhs is now in eax register 76 | generated.push_str(" pop %ecx\n"); // rhs is now in ecx register 77 | generated.push_str(" cmpl %eax, %ecx\n"); 78 | generated.push_str(" xor %eax, %eax\n"); // zero eax register 79 | generated.push_str(match op { // Ex. `sete %al` will set 1 on lower byte of eax if true 80 | Operator::EqualEqual => " sete %al\n", 81 | Operator::NotEqual => " setne %al\n", 82 | Operator::LessThan => " setl %al\n", 83 | Operator::LessEqual => " setle %al\n", 84 | Operator::GreaterThan => " setg %al\n", 85 | Operator::GreaterEqual => " setge %al\n", 86 | _ => unimplemented!() 87 | }); 88 | } 89 | Operator::Or | Operator::And => { 90 | generated = generate_expression(&Some(&**lhs), context); 91 | generated.push_str(" push %eax\n"); 92 | generated.push_str(&generate_expression(&Some(&**rhs), context)); 93 | generated.push_str(" pop %ecx\n"); 94 | generated.push_str(match op { 95 | Operator::Or => " orl %ecx, %eax\n movl $0, %eax\n setne %al\n", 96 | /* 1. Set `cl` to 1 if lhs != 0 97 | * 2. Set `al` to 1 if rhs != 0 98 | * 3. Store `al` and `cl` in `al` 99 | */ 100 | Operator::And => " cmpl $0, %eax\n setne %cl\n cmpl $0, %eax\n movl $0, %eax\n setne %al\n andb %cl, %al\n", 101 | _ => unsafe { ::std::hint::unreachable_unchecked() }, 102 | }); 103 | } 104 | _ if op.is_bitwise() => { 105 | generated = generate_expression(&Some(&**lhs), context); 106 | generated.push_str(" push %eax\n"); 107 | generated.push_str(&generate_expression(&Some(&**rhs), context)); 108 | generated.push_str(" pop %ebx\n"); 109 | match op { 110 | Operator::BitwiseAND => generated.push_str(" and %ebx, %eax\n"), 111 | Operator::BitwiseOR => generated.push_str(" or %ebx, %eax\n"), 112 | Operator::BitwiseXOR => generated.push_str(" xor %ebx, %eax\n"), 113 | Operator::BitwiseShiftLeft => generated.push_str(" shl %ebx, %eax\n"), 114 | Operator::BitwiseShiftRight => generated.push_str(" shr %ebx, %eax\n"), 115 | _ => unimplemented!(), // should be impossible 116 | } 117 | } 118 | _ => unimplemented!() 119 | } 120 | return generated; 121 | } 122 | Some(Expr::UnaryOp(op, expr)) => { 123 | let mut generated_expr = generate_expression(&Some(&**expr), context); 124 | match op { 125 | Operator::LogicalNegation => { 126 | generated_expr.push_str(" cmpl $0, %eax\n sete %al\n"); 127 | } 128 | Operator::Minus => { 129 | generated_expr.push_str(" neg %eax\n"); 130 | } 131 | Operator::BitwiseComplement => { 132 | generated_expr.push_str(" not %eax\n"); 133 | } 134 | _ => unimplemented!(), 135 | } 136 | return generated_expr; 137 | } 138 | Some(Expr::Assign(op, name, expr)) => { // `op` is guaranteed valid assignment operator by parser 139 | let mut output = generate_expression(&Some(&**expr), context); 140 | 141 | let offset: isize; 142 | if context.outer_scope.contains_key(name) { 143 | offset = *context.outer_scope.get(name).unwrap(); 144 | } else if context.current_scope.contains_key(name) { 145 | offset = *context.current_scope.get(name).unwrap(); 146 | } else { 147 | panic!("Attempting to assign to an undeclared variable"); 148 | } 149 | 150 | match op { 151 | Operator::Assignment => output.push_str(&format!(" movl %eax, {}(%ebp)\n", offset)), 152 | Operator::PlusAssign => output.push_str(&format!(" addl %eax, {}(%ebp)\n", offset)), 153 | Operator::MinusAssign => output.push_str(&format!(" subl %eax, {}(%ebp)\n", offset)), 154 | Operator::StarAssign => output.push_str(&format!(" imull {}(%ebp)\n", offset)), 155 | Operator::SlashAssign => output.push_str(&format!(" movl %eax, %ecx\n movl {0}(%ebp), %eax\n idivl %ecx\n movl %ecx, %eax\n movl %eax, {0}(%ebp)\n", offset)), 156 | Operator::ModAssign => output.push_str(&format!(" movl %eax, %ecx\n movl {0}(%ebp), %eax\n idivl %ecx\n movl %edx, %eax\n movl %eax, {0}(%ebp)\n", offset)), 157 | // NOTE: Operators LeftShiftAssign, RightShiftAssign, ANDAssign, ORAssign, and XORAssign are all omitted until further development. 158 | _ => unimplemented!(), 159 | } 160 | 161 | return output; 162 | } 163 | Some(Expr::Var(name)) => { 164 | let offset: isize; 165 | if context.outer_scope.contains_key(name) { 166 | offset = *context.outer_scope.get(name).unwrap(); 167 | } else if context.current_scope.contains_key(name) { 168 | offset = *context.current_scope.get(name).unwrap(); 169 | } else { 170 | panic!("Attempting to assign to an undeclared variable {:?}", name); 171 | } 172 | 173 | return format!(" movl {}(%ebp), %eax\n", offset); 174 | } 175 | Some(Expr::Conditional(expr1, expr2, expr3)) => { 176 | // conditional 177 | let mut output = generate_expression(&Some(&**expr1), context); 178 | 179 | let label = format!("_t{}", *context.counter); 180 | *context.counter += 1; 181 | 182 | // if condition is false, jump to _tN_else 183 | output.push_str(" cmpl $0, %eax\n"); 184 | output.push_str(&format!(" je {}_else\n", label)); 185 | 186 | // condition is true 187 | output.push_str(&generate_expression(&Some(&**expr2), context)); 188 | output.push_str(&format!(" jmp {}_end\n", label)); 189 | 190 | // condition is false 191 | output.push_str(&format!("{}_else:\n", label)); 192 | output.push_str(&generate_expression(&Some(&**expr3), context)); 193 | 194 | output.push_str(&format!("{}_end:\n", label)); 195 | 196 | return output; 197 | } 198 | None => return String::new(), 199 | } 200 | } 201 | 202 | fn generate_declaration(declaration: &Declaration, context: &mut Context) -> String { 203 | let mut output = String::new(); 204 | match declaration { 205 | Declaration::Declare(name, value) => { 206 | if context.current_scope.contains_key(name) { 207 | panic!("Can't declare a variable twice in the same scope"); 208 | } 209 | 210 | if let Some(expr) = value { 211 | output.push_str(&generate_expression(&Some(&*expr), context)); 212 | //output.push_str(" pushl %eax\n"); 213 | output.push_str(&format!(" movl %eax, {}(%ebp)\n", context.stack_index)); 214 | } else { 215 | //output.push_str(" pushl $0\n"); 216 | output.push_str(&format!(" movl $0, {}(%ebp)\n", context.stack_index)); 217 | } 218 | 219 | context.current_scope.insert(name.clone(), *context.stack_index); 220 | *context.stack_index -= 4; 221 | } 222 | } 223 | output 224 | } 225 | 226 | fn generate_statement(statement: &Statement, context: &mut Context) -> String { 227 | let mut output = String::new(); 228 | match statement { 229 | Statement::Return(expr) => { 230 | output.push_str(&generate_expression(&Some(&*expr), context)); 231 | output.push_str(&format!(" jmp _{}_epilogue\n", context.function_name)); 232 | } 233 | Statement::Expr(expr) => output.push_str(&generate_expression(&expr.as_ref(), context)), 234 | Statement::Conditional(condition, expr1, expr2) => { 235 | output.push_str(&generate_expression(&Some(&*condition), context)); 236 | 237 | let label = format!("_c{}", *context.counter); 238 | *context.counter += 1; 239 | 240 | // if condition is false, jump to _cN_else 241 | output.push_str(" cmpl $0, %eax\n"); 242 | output.push_str(&format!(" je {}_else\n", label)); 243 | 244 | // condition is true 245 | output.push_str(&generate_statement(expr1, context)); 246 | 247 | match expr2 { 248 | Some(else_expr) => { 249 | // (for previous true statement) 250 | output.push_str(&format!(" jmp {}_end\n", label)); 251 | 252 | // condition is false 253 | output.push_str(&format!("{}_else:\n", label)); 254 | output.push_str(&generate_statement(else_expr, context)); 255 | 256 | output.push_str(&format!("{}_end:\n", label)); 257 | } 258 | None => { 259 | // no else statement 260 | output.push_str(&format!("{}_else:\n", label)); 261 | } 262 | } 263 | } 264 | Statement::Compound(block_items) => { 265 | return generate_block(block_items, context); 266 | } 267 | Statement::ForExpr(clause, control, post_expr, statement) => { 268 | let label = format!("_f{}", *context.counter); 269 | *context.counter += 1; 270 | 271 | output.push_str(&generate_expression(&clause.as_ref(), context)); 272 | 273 | output.push_str(&format!("{}_begin:\n", label)); 274 | 275 | output.push_str(&generate_expression(&Some(&control), context)); 276 | 277 | output.push_str(" cmpl $0, %eax\n"); 278 | output.push_str(&format!(" je {}_end\n", label)); 279 | 280 | output.push_str(&generate_statement(statement, context)); 281 | 282 | output.push_str(&generate_expression(&post_expr.as_ref(), context)); 283 | 284 | output.push_str(&format!(" jmp {0}_begin\n{0}_end:\n", label)); 285 | } 286 | Statement::ForDecl(clause, control, post_expr, statement) => { 287 | let label = format!("_f{}", *context.counter); 288 | *context.counter += 1; 289 | 290 | // output.push_str(&generate_declaration(clause, variables, &mut init_scope, stack_index, counter)); 291 | let mut loop_scope = VariableMap::new(); 292 | let mut loop_context = Context::new(context.function_name.clone(), context.current_scope, &mut loop_scope, context.stack_index, context.counter, context.loop_label_begin.clone(), context.loop_label_end.clone()); 293 | output.push_str(&generate_declaration(clause, &mut loop_context)); 294 | 295 | output.push_str(&format!("{}_begin:\n", label)); 296 | 297 | output.push_str(&generate_expression(&Some(&control), &mut loop_context)); 298 | 299 | output.push_str(" cmpl $0, %eax\n"); 300 | output.push_str(&format!(" je {}_end\n", label)); 301 | 302 | output.push_str(&generate_statement(statement, &mut loop_context)); 303 | 304 | output.push_str(&generate_expression(&post_expr.as_ref(), &mut loop_context)); 305 | 306 | output.push_str(&format!(" jmp {0}_begin\n{0}_end:\n", label)); 307 | 308 | // deallocate initialization variable(s) 309 | if !loop_context.current_scope.is_empty() { 310 | let bytes_to_deallocate = 4 * loop_context.current_scope.len(); 311 | output.push_str(&format!(" addl ${}, %esp\n", bytes_to_deallocate)); 312 | } 313 | } 314 | Statement::While(expr, statement) => { 315 | let label = format!("_w{}", *context.counter); 316 | *context.counter += 1; 317 | 318 | output.push_str(&format!("{}_begin:\n", label)); 319 | 320 | output.push_str(&generate_expression(&Some(&expr), context)); 321 | 322 | output.push_str(" cmpl $0, %eax\n"); 323 | output.push_str(&format!(" je {}_end\n", label)); 324 | 325 | output.push_str(&generate_statement(statement, context)); 326 | 327 | output.push_str(&format!(" jmp {0}_begin\n{0}_end:\n", label)); 328 | } 329 | Statement::Do(statement, expr) => { 330 | let label = format!("_w{}", *context.counter); 331 | *context.counter += 1; 332 | 333 | output.push_str(&format!("{}_begin:\n", label)); 334 | 335 | output.push_str(&generate_statement(statement, context)); 336 | 337 | output.push_str(&generate_expression(&Some(&expr), context)); 338 | 339 | output.push_str(" cmpl $0, %eax\n"); 340 | output.push_str(&format!(" jne {}_begin\n", label)); 341 | } 342 | Statement::Break => { 343 | match &context.loop_label_end { 344 | Some(label) => output.push_str(&format!(" jmp {}\n", label)), 345 | None => panic!("Cannot 'break' from this location"), 346 | } 347 | } 348 | Statement::Continue => { 349 | match &context.loop_label_begin { 350 | Some(label) => output.push_str(&format!(" jmp {}\n", label)), 351 | None => panic!("Cannot 'continue' from this location"), 352 | } 353 | } 354 | } 355 | output 356 | } 357 | 358 | fn generate_block_item(block_item: &BlockItem, context: &mut Context) -> String { 359 | match block_item { 360 | BlockItem::Declaration(declaration) => generate_declaration(declaration, context), 361 | BlockItem::Statement(statement) => generate_statement(statement, context), 362 | } 363 | } 364 | 365 | fn generate_block(block_items: &Vec, context: &mut Context) -> String { 366 | let mut output = String::new(); 367 | let mut inner_scope = merge_maps(&context.outer_scope, &context.current_scope); 368 | for block_item in block_items { 369 | output.push_str(&generate_block_item(block_item, { 370 | &mut Context::new(context.function_name.clone(), context.current_scope, &mut inner_scope, context.stack_index, context.counter, context.loop_label_begin.clone(), context.loop_label_end.clone()) 371 | })); 372 | } 373 | 374 | // deallocate variables 375 | if !inner_scope.is_empty() { 376 | let bytes_to_deallocate = 4 * inner_scope.len(); 377 | output.push_str(&format!(" addl ${}, %esp\n", bytes_to_deallocate)); 378 | } 379 | 380 | output 381 | } 382 | 383 | pub fn generate_function(function: &FunctionDeclaration, outer_scope: &VariableMap, counter: &mut u32) -> String { 384 | let mut output = String::new(); 385 | match function { 386 | FunctionDeclaration::Function(name, block_items) => { 387 | let mut variable_map = VariableMap::new(); 388 | let mut stack_index = -4isize; // ESP - 4 389 | 390 | if name == "main" { 391 | if cfg!(target_os = "linux") { 392 | output.push_str(&format!(" .globl main\nmain:\n")); 393 | } else if cfg!(target_os = "windows") || cfg!(target_os = "macos") { 394 | output.push_str(&format!(" .globl _main\n_main:\n")); 395 | } else { 396 | unimplemented!(); 397 | } 398 | } else { 399 | output.push_str(&format!(" .globl _{0}\n_{0}:\n", name)); 400 | } 401 | 402 | // Function prologue 403 | output.push_str(" push %ebp\n movl %esp, %ebp\n"); 404 | 405 | output.push_str(&generate_block( 406 | block_items, 407 | &mut Context { 408 | function_name: name.clone(), 409 | outer_scope: outer_scope, 410 | current_scope: &mut variable_map, 411 | stack_index: &mut stack_index, 412 | counter: counter, 413 | loop_label_end: None, 414 | loop_label_begin: None, 415 | }, 416 | )); 417 | 418 | if !output.ends_with(&format!("jmp _{}_epilogue\n", name)) { 419 | // No return issued, so we return zero by default 420 | output.push_str(" movl $0, %eax\n"); 421 | } else { 422 | // Output ends with "jmp _{}_epilogue", which is dumb because we define the epilogue immediately after 423 | output = output[0..output.len() - format!(" jmp _{}_epilogue\n", name).len()].to_owned(); 424 | } 425 | 426 | // Function epilogue 427 | output.push_str(&format!("_{}_epilogue:\n movl %ebp, %esp\n pop %ebp\n ret\n", name)); 428 | } 429 | } 430 | output 431 | } 432 | 433 | pub fn generate(ast: &Program) -> String { 434 | let mut output = String::new(); 435 | let mut block_counter = 0u32; 436 | match ast { 437 | Program::Function(function) => { // Code generated when a function is made 438 | output.push_str(&generate_function(function, &VariableMap::new(), &mut block_counter)); 439 | } 440 | } 441 | output 442 | } 443 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | //! This "main.rs" file is extremely experimental and is currently making OX just a one-off compiler, 2 | //! when that is really not the case. OX will become a library in the future. This file will be trashed 3 | //! someday -- it is just for testing the compiler. 4 | 5 | // Made by following along with https://norasandler.com/2017/11/29/Write-a-Compiler.html 6 | #![allow(unused_imports)] 7 | 8 | extern crate peek_nth; 9 | 10 | use std::env; 11 | use std::fs::File; 12 | use std::io::prelude::*; 13 | use std::process::Command; 14 | use std::path::Path; 15 | use std::time::Instant; 16 | 17 | mod scanner; 18 | mod ast; 19 | mod generator; 20 | 21 | use scanner::*; 22 | use ast::*; 23 | use generator::generate; 24 | 25 | fn help() { 26 | println!("Usage: oxc{} [OPTIONS]\n", if cfg!(target_os = "windows") { ".exe" } else { "" }); 27 | println!("OPTIONS:\n\tlex\tOnly lexically analyze the file."); 28 | println!("\tparse\tOnly lex and generate an abstract syntax tree for the file."); 29 | } 30 | 31 | fn main() { 32 | let argv: Vec = env::args().collect(); 33 | 34 | let mut to_lex = false; 35 | let mut to_parse = false; 36 | 37 | for arg in &argv { 38 | match &arg[..] { 39 | "help" => { 40 | help(); 41 | return; 42 | } 43 | "lex" => to_lex = true, 44 | "parse" => to_parse = true, 45 | _ => {}, 46 | } 47 | } 48 | 49 | let mut contents = String::new(); 50 | match argv.get(1) { 51 | Some(file) => { File::open(&file).unwrap().read_to_string(&mut contents).unwrap(); }, 52 | None => { 53 | println!("Error: not given. See help:\n"); 54 | help(); 55 | return; 56 | } 57 | } 58 | 59 | // Comment the following line if you don't want the source file to be printed 60 | println!("{}:\n{}\n", &argv[1], contents); 61 | 62 | let started_processing = Instant::now(); 63 | 64 | let tokens = Lexer::new(&contents).collect::>(); 65 | let lexing_time = { 66 | let duration = started_processing.elapsed(); 67 | duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 68 | }; 69 | if to_lex { 70 | println!("Lexically analyzed in {}s:\n{:#?}\n", lexing_time, tokens); 71 | } else if to_parse { 72 | println!("Lexically analyzed in {}s: option 'lex' to print tokens\n", lexing_time); 73 | let ast = parse(&tokens[..]); 74 | let parsing_time = { 75 | let duration = started_processing.elapsed(); 76 | duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 77 | } - lexing_time; 78 | println!("Parsed in {}s:\n{:#?}\n", parsing_time, ast); 79 | } else { 80 | println!("Lexically analyzed in {}s: option 'lex' to print tokens\n", lexing_time); 81 | let ast = parse(&tokens[..]); 82 | let parsing_time = { 83 | let duration = started_processing.elapsed(); 84 | duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 85 | } - lexing_time; 86 | println!("Parsed in {}s:\n{:#?}\n", parsing_time, ast); 87 | 88 | // Comment out everything below this line to disable code generation 89 | let generated = generate(&ast); 90 | let generation_time = { 91 | let duration = started_processing.elapsed(); 92 | duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 93 | } - (lexing_time + parsing_time); 94 | println!("Generated assembly in {}s:\n{}", generation_time, generated); 95 | 96 | let file_name = Path::new(&argv[1]).file_stem().unwrap().to_str().unwrap(); 97 | 98 | let mut output_file = File::create(&format!("{}.s", file_name)).unwrap(); 99 | output_file.write_all(generated.as_bytes()).unwrap(); 100 | 101 | let output_name = if cfg!(target_os = "windows") { 102 | format!("{}.exe", file_name) 103 | } else { 104 | file_name.to_owned() 105 | }; 106 | 107 | println!("Assembling..."); 108 | Command::new("gcc") 109 | .args(&["-m32", "-Wall", &format!("{}.s", file_name), "-o", &output_name]) 110 | .spawn() 111 | .unwrap() 112 | .wait() 113 | .unwrap(); 114 | let assembly_time = { 115 | let duration = started_processing.elapsed(); 116 | duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 117 | } - (lexing_time + parsing_time + generation_time); 118 | println!("Assembled in {}s", assembly_time); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/scanner.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Peekable; 2 | use std::str::Chars; 3 | 4 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 5 | pub enum Symbol { 6 | LBrace, // { 7 | RBrace, // } 8 | LParen, // ( 9 | RParen, // ) 10 | Semicolon, // ; 11 | Colon, // : 12 | } 13 | use self::Symbol::*; 14 | 15 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 16 | pub enum Operator { 17 | LogicalNegation, // ! 18 | Minus, // - 19 | BitwiseComplement, // ~ 20 | Plus, // + 21 | Star, // * 22 | Slash, // / 23 | Modulo, // % 24 | Assignment, // = 25 | PlusAssign, // += 26 | MinusAssign, // -= 27 | SlashAssign, // /= 28 | StarAssign, // *= 29 | ModAssign, // %= 30 | LeftShiftAssign, // <<= 31 | RightShiftAssign, // >>= 32 | ANDAssign, // &= 33 | ORAssign, // |= 34 | XORAssign, // ^= 35 | And, // && 36 | Or, // || 37 | EqualEqual, // == 38 | NotEqual, // != 39 | LessThan, // < 40 | LessEqual, // <= 41 | GreaterThan, // > 42 | GreaterEqual, // >= 43 | BitwiseAND, // & 44 | BitwiseOR, // | 45 | BitwiseXOR, // ^ 46 | BitwiseShiftLeft, // << 47 | BitwiseShiftRight, // >> 48 | QuestionMark, // ? 49 | 50 | } 51 | use self::Operator::*; 52 | 53 | impl Operator { 54 | pub fn is_unary(&self) -> bool { 55 | match self { 56 | | Operator::Minus 57 | | Operator::LogicalNegation 58 | | Operator::BitwiseComplement => true, 59 | _ => false, 60 | } 61 | } 62 | 63 | pub fn is_bitwise(&self) -> bool { 64 | // Does not include BitwiseCompliment because it is unary 65 | match self { 66 | | Operator::BitwiseAND 67 | | Operator::BitwiseOR 68 | | Operator::BitwiseXOR 69 | | Operator::BitwiseShiftLeft 70 | | Operator::BitwiseShiftRight => true, 71 | _ => false, 72 | } 73 | } 74 | 75 | pub fn is_assignment(&self) -> bool { 76 | match self { 77 | | Operator::Assignment 78 | | Operator::PlusAssign 79 | | Operator::MinusAssign 80 | | Operator::SlashAssign 81 | | Operator::StarAssign 82 | | Operator::ModAssign 83 | | Operator::LeftShiftAssign 84 | | Operator::RightShiftAssign 85 | | Operator::ANDAssign 86 | | Operator::ORAssign 87 | | Operator::XORAssign => true, 88 | _ => false, 89 | } 90 | } 91 | } 92 | 93 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 94 | pub enum Keyword { 95 | Int, 96 | Return, 97 | If, 98 | Else, 99 | For, 100 | Do, 101 | While, 102 | Break, 103 | Continue, 104 | } 105 | use self::Keyword::*; 106 | 107 | impl Keyword { 108 | pub fn is_type(&self) -> bool { 109 | match self { 110 | Int => true, 111 | _ => false, 112 | } 113 | } 114 | } 115 | 116 | #[derive(Debug, Clone, PartialEq, Eq)] 117 | pub enum Token { 118 | Symbol(Symbol), 119 | Operator(Operator), 120 | Keyword(Keyword), 121 | Id(String), 122 | Integer(i32), 123 | } 124 | use self::Token::*; 125 | 126 | #[derive(Debug, Clone, PartialEq, Eq)] 127 | pub struct TokenData<'a> { 128 | pub token: Token, 129 | pub slice: &'a str, 130 | pub line: usize, 131 | pub col: usize, 132 | } 133 | 134 | impl<'a> TokenData<'a> { 135 | pub fn new(token: Token, slice: &'a str, line: usize, col: usize) -> TokenData { 136 | TokenData { token, slice, line, col } 137 | } 138 | } 139 | 140 | impl<'a> PartialEq for TokenData<'a> { 141 | fn eq(&self, token: &Token) -> bool { 142 | &self.token == token 143 | } 144 | } 145 | 146 | pub struct Lexer<'a> { 147 | source: &'a str, 148 | chars: Peekable>, 149 | pos: usize, // index in source 150 | line: usize, 151 | col: usize, 152 | first_run: bool, 153 | } 154 | 155 | impl<'a> Lexer<'a> { 156 | /// The source must contain no return-carriage characters: `\r`. 157 | pub fn new(source: &'a str) -> Lexer { 158 | Lexer { 159 | source, 160 | chars: source.chars().peekable(), 161 | pos: 0, 162 | line: 1, 163 | col: 1, 164 | first_run: true, 165 | } 166 | } 167 | 168 | /// To be used instead of `self.chars.next()` 169 | fn next_char(&mut self) -> Option { 170 | if let Some(c) = self.chars.next() { 171 | if !self.first_run { 172 | // Offset counter by -1, so that 173 | // self.pos returns last position 174 | // instead of next search position. 175 | self.pos += 1; 176 | } else { 177 | self.first_run = false; 178 | } 179 | 180 | if c == '\n' { 181 | self.line += 1; 182 | self.col = 0; 183 | } else { 184 | self.col += 1; 185 | } 186 | 187 | Some(c) 188 | } else { 189 | None 190 | } 191 | } 192 | } 193 | 194 | impl<'a> Iterator for Lexer<'a> { 195 | type Item = TokenData<'a>; 196 | fn next(&mut self) -> Option { 197 | loop { // Only intention is for `continue` on whitespace encountered 198 | return if let Some(c) = self.next_char() { 199 | match c { 200 | '{' => Some(TokenData::new(Symbol(LBrace), &self.source[self.pos..=self.pos], self.line, self.col)), 201 | '}' => Some(TokenData::new(Symbol(RBrace), &self.source[self.pos..=self.pos], self.line, self.col)), 202 | '(' => Some(TokenData::new(Symbol(LParen), &self.source[self.pos..=self.pos], self.line, self.col)), 203 | ')' => Some(TokenData::new(Symbol(RParen), &self.source[self.pos..=self.pos], self.line, self.col)), 204 | ';' => Some(TokenData::new(Symbol(Semicolon), &self.source[self.pos..=self.pos], self.line, self.col)), 205 | ':' => Some(TokenData::new(Symbol(Colon), &self.source[self.pos..=self.pos], self.line, self.col)), 206 | '~' => Some(TokenData::new(Operator(BitwiseComplement), &self.source[self.pos..=self.pos], self.line, self.col)), 207 | '?' => Some(TokenData::new(Operator(QuestionMark), &self.source[self.pos..=self.pos], self.line, self.col)), 208 | '!' => if self.chars.peek() == Some(&'=') { 209 | self.next_char().unwrap(); 210 | Some(TokenData::new(Operator(NotEqual), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 211 | } else { 212 | Some(TokenData::new(Operator(LogicalNegation), &self.source[self.pos..=self.pos], self.line, self.col)) 213 | } 214 | '-' => if self.chars.peek() == Some(&'=') { 215 | self.next_char().unwrap(); 216 | Some(TokenData::new(Operator(MinusAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 217 | } else { 218 | Some(TokenData::new(Operator(Minus), &self.source[self.pos..=self.pos], self.line, self.col)) 219 | } 220 | '+' => if self.chars.peek() == Some(&'=') { 221 | self.next_char().unwrap(); 222 | Some(TokenData::new(Operator(PlusAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 223 | } else { 224 | Some(TokenData::new(Operator(Plus), &self.source[self.pos..=self.pos], self.line, self.col)) 225 | } 226 | '*' => if self.chars.peek() == Some(&'=') { 227 | self.next_char().unwrap(); 228 | Some(TokenData::new(Operator(StarAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 229 | } else { 230 | Some(TokenData::new(Operator(Star), &self.source[self.pos..=self.pos], self.line, self.col)) 231 | } 232 | '/' => if self.chars.peek() == Some(&'=') { 233 | self.next_char().unwrap(); 234 | Some(TokenData::new(Operator(SlashAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 235 | } else { 236 | Some(TokenData::new(Operator(Slash), &self.source[self.pos..=self.pos], self.line, self.col)) 237 | } 238 | '%' => if self.chars.peek() == Some(&'=') { 239 | self.next_char().unwrap(); 240 | Some(TokenData::new(Operator(ModAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 241 | } else { 242 | Some(TokenData::new(Operator(Modulo), &self.source[self.pos..=self.pos], self.line, self.col)) 243 | } 244 | '&' => if self.chars.peek() == Some(&'&') { 245 | self.next_char().unwrap(); 246 | Some(TokenData::new(Operator(And), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 247 | } else if self.chars.peek() == Some(&'=') { 248 | self.next_char().unwrap(); 249 | Some(TokenData::new(Operator(ANDAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 250 | } else { 251 | Some(TokenData::new(Operator(BitwiseAND), &self.source[self.pos..=self.pos], self.line, self.col)) 252 | } 253 | '|' => if self.chars.peek() == Some(&'|') { 254 | self.next_char().unwrap(); 255 | Some(TokenData::new(Operator(Or), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 256 | } else if self.chars.peek() == Some(&'=') { 257 | self.next_char().unwrap(); 258 | Some(TokenData::new(Operator(ORAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 259 | } else { 260 | Some(TokenData::new(Operator(BitwiseOR), &self.source[self.pos..=self.pos], self.line, self.col)) 261 | } 262 | '=' => if self.chars.peek() == Some(&'=') { 263 | self.next_char().unwrap(); 264 | Some(TokenData::new(Operator(EqualEqual), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 265 | } else { 266 | Some(TokenData::new(Operator(Assignment), &self.source[self.pos..=self.pos], self.line, self.col)) 267 | } 268 | '<' => if self.chars.peek() == Some(&'=') { 269 | self.next_char().unwrap(); 270 | Some(TokenData::new(Operator(LessEqual), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 271 | } else if self.chars.peek() == Some(&'<') { 272 | self.next_char().unwrap(); 273 | if self.chars.peek() == Some(&'=') { 274 | self.next_char().unwrap(); 275 | Some(TokenData::new(Operator(LeftShiftAssign), &self.source[self.pos-2..=self.pos], self.line, self.col-2)) 276 | } else { 277 | Some(TokenData::new(Operator(BitwiseShiftLeft), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 278 | } 279 | } else { 280 | Some(TokenData::new(Operator(LessThan), &self.source[self.pos..=self.pos], self.line, self.col)) 281 | } 282 | '>' => if self.chars.peek() == Some(&'=') { 283 | self.next_char().unwrap(); 284 | Some(TokenData::new(Operator(GreaterEqual), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 285 | } else if self.chars.peek() == Some(&'>') { 286 | self.next_char().unwrap(); 287 | if self.chars.peek() == Some(&'=') { 288 | self.next_char().unwrap(); 289 | Some(TokenData::new(Operator(RightShiftAssign), &self.source[self.pos-2..=self.pos], self.line, self.col-2)) 290 | } else { 291 | Some(TokenData::new(Operator(BitwiseShiftRight), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 292 | } 293 | } else { 294 | Some(TokenData::new(Operator(GreaterThan), &self.source[self.pos..=self.pos], self.line, self.col)) 295 | } 296 | '^' => if self.chars.peek() == Some(&'=') { 297 | self.next_char().unwrap(); 298 | Some(TokenData::new(Operator(XORAssign), &self.source[self.pos-1..=self.pos], self.line, self.col-1)) 299 | } else { 300 | Some(TokenData::new(Operator(BitwiseXOR), &self.source[self.pos..=self.pos], self.line, self.col)) 301 | } 302 | _ => { 303 | if c.is_whitespace() { 304 | continue; 305 | } else if c.is_alphabetic() || c == '_' { 306 | let mut full = c.to_string(); 307 | let start_pos = self.pos; 308 | 309 | while let Some(&c) = self.chars.peek() { // Read an identifier 310 | if c.is_alphabetic() || c.is_digit(10) { 311 | full.push(self.next_char().unwrap()); 312 | } else { 313 | break; 314 | } 315 | } 316 | 317 | let slice = &self.source[start_pos..=self.pos]; 318 | let col = self.col - slice.len(); 319 | match &full.to_lowercase()[..] { 320 | "int" => Some(TokenData::new(Keyword(Int), slice, self.line, col)), 321 | "return" => Some(TokenData::new(Keyword(Return), slice, self.line, col)), 322 | "if" => Some(TokenData::new(Keyword(If), slice, self.line, col)), 323 | "else" => Some(TokenData::new(Keyword(Else), slice, self.line, col)), 324 | "for" => Some(TokenData::new(Keyword(For), slice, self.line, col)), 325 | "do" => Some(TokenData::new(Keyword(Do), slice, self.line, col)), 326 | "while" => Some(TokenData::new(Keyword(While), slice, self.line, col)), 327 | "break" => Some(TokenData::new(Keyword(Break), slice, self.line, col)), 328 | "continue" => Some(TokenData::new(Keyword(Continue), slice, self.line, col)), 329 | _ => Some(TokenData::new(Id(full), slice, self.line, col)), 330 | } 331 | } 332 | else if c.is_digit(10) { 333 | let mut full = c.to_string(); 334 | let start_pos = self.pos; 335 | 336 | while let Some(&c) = self.chars.peek() { // Read the entire number 337 | if c.is_digit(10) { 338 | full.push(self.next_char().unwrap()); 339 | } else { 340 | break; 341 | } 342 | } 343 | 344 | let slice = &self.source[start_pos..=self.pos]; 345 | Some(TokenData::new(Integer(full.parse().unwrap()), slice, self.line, self.col - slice.len())) 346 | } else { 347 | panic!("Unrecognized character: {:?}", c); 348 | } 349 | } 350 | } 351 | } else { 352 | None 353 | } 354 | } 355 | } 356 | } 357 | 358 | #[cfg(test)] 359 | mod tests { 360 | use super::*; 361 | 362 | #[test] 363 | fn basics() { 364 | let source = "int main() {\n return 2 >> 3;\n}\n"; // A typical source file has a lot of whitespace 365 | let mut lexer = super::Lexer::new(&source); 366 | 367 | let tok = lexer.next().unwrap(); 368 | eprintln!("int: {:?}", tok); 369 | assert!(tok.token == Token::Keyword(Keyword::Int)); 370 | assert!(tok.slice == "int"); 371 | 372 | lexer.next().unwrap(); 373 | lexer.next().unwrap(); 374 | lexer.next().unwrap(); 375 | lexer.next().unwrap(); 376 | 377 | let tok = lexer.next().unwrap(); // 'return' 378 | assert!(tok.token == Token::Keyword(Keyword::Return)); 379 | assert!(tok.slice == "return"); 380 | assert!(tok.line == 2); 381 | assert!(tok.col == 4); 382 | } 383 | } 384 | -------------------------------------------------------------------------------- /test/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nora Sandler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Write a C Compiler! 2 | 3 | This is a set of C test programs to help you write your own compiler. They were written to accompany [this tutorial](https://norasandler.com/2017/11/29/Write-a-Compiler.html). 4 | 5 | ## Usage 6 | 7 | ``` 8 | ./test_compiler.sh /path/to/your/compiler 9 | ``` 10 | 11 | In order to use this script, your compiler needs to follow this spec: 12 | 13 | 1. It can be invoked from the command line, taking only a C source file as an argument, e.g.: `./YOUR_COMPILER /path/to/program.c` 14 | 15 | 2. When passed `program.c`, it generates executable `program` in the same directory. 16 | 17 | 3. It doesn’t generate assembly or an executable if parsing fails (this is what the test script checks for invalid test programs). 18 | 19 | The script doesn’t check whether your compiler outputs sensible error messages, but you can use the invalid test programs to test that manually. 20 | 21 | ## Contribute 22 | 23 | Additional test cases welcome! You can also file issues here, either about the test suite itself or about the content of the tutorial. 24 | -------------------------------------------------------------------------------- /test/stage_1/invalid/missing_paren.c: -------------------------------------------------------------------------------- 1 | int main( { 2 | return 0; 3 | } -------------------------------------------------------------------------------- /test/stage_1/invalid/missing_retval.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return; 3 | } -------------------------------------------------------------------------------- /test/stage_1/invalid/no_brace.c: -------------------------------------------------------------------------------- 1 | int main { 2 | return 0; 3 | -------------------------------------------------------------------------------- /test/stage_1/invalid/no_semicolon.c: -------------------------------------------------------------------------------- 1 | int main { 2 | return 0 3 | } -------------------------------------------------------------------------------- /test/stage_1/invalid/no_space.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return0; 3 | } -------------------------------------------------------------------------------- /test/stage_1/invalid/wrong_case.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | RETURN 0; 3 | } -------------------------------------------------------------------------------- /test/stage_1/valid/multi_digit.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 100; 3 | } -------------------------------------------------------------------------------- /test/stage_1/valid/newlines.c: -------------------------------------------------------------------------------- 1 | 2 | int 3 | main 4 | ( 5 | ) 6 | { 7 | return 8 | 0 9 | ; 10 | } -------------------------------------------------------------------------------- /test/stage_1/valid/no_newlines.c: -------------------------------------------------------------------------------- 1 | int main(){return 0;} -------------------------------------------------------------------------------- /test/stage_1/valid/return_0.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /test/stage_1/valid/return_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2; 3 | } -------------------------------------------------------------------------------- /test/stage_1/valid/spaces.c: -------------------------------------------------------------------------------- 1 | int main ( ) { return 0 ; } -------------------------------------------------------------------------------- /test/stage_2/invalid/missing_const.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !; 3 | } -------------------------------------------------------------------------------- /test/stage_2/invalid/missing_semicolon.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !5 3 | } -------------------------------------------------------------------------------- /test/stage_2/invalid/nested_missing_const.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !~; 3 | } -------------------------------------------------------------------------------- /test/stage_2/invalid/wrong_order.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 4-; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/bitwise.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !12; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/bitwise_zero.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return ~0; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/neg.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return -5; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/nested_ops.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !-3; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/nested_ops_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return -~0; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/not_five.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !5; 3 | } -------------------------------------------------------------------------------- /test/stage_2/valid/not_zero.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return !0; 3 | } -------------------------------------------------------------------------------- /test/stage_3/invalid/malformed_paren.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 (- 3); 3 | } -------------------------------------------------------------------------------- /test/stage_3/invalid/missing_first_op.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return /3; 3 | } -------------------------------------------------------------------------------- /test/stage_3/invalid/missing_second_op.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 + ; 3 | } -------------------------------------------------------------------------------- /test/stage_3/invalid/no_semicolon.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2*2 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/add.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 + 2; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/associativity.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 - 2 - 3; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/associativity_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 6 / 3 / 2; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/div.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 4 / 2; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/mult.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 * 3; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/parens.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 * (3 + 4); 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/precedence.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 + 3 * 4; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/sub.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 - 2; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/sub_neg.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2- -1; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/unop_add.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return ~2 + 3; 3 | } -------------------------------------------------------------------------------- /test/stage_3/valid/unop_parens.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return ~(1 + 1); 3 | } -------------------------------------------------------------------------------- /test/stage_4/invalid/missing_first_op.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return <= 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/invalid/missing_mid_op.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 < > 3; 3 | } -------------------------------------------------------------------------------- /test/stage_4/invalid/missing_second_op.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 && 3 | } -------------------------------------------------------------------------------- /test/stage_4/invalid/missing_semicolon.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 || 2 3 | } -------------------------------------------------------------------------------- /test/stage_4/invalid/split_le.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 < = 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/and_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 && 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/and_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 && -1; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/eq_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 == 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/eq_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 == 1; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/ge_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 >= 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/ge_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 >= 1; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/gt_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 > 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/gt_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 > 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/le_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 <= -1; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/le_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0 <= 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/lt_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 < 1; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/lt_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 < 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/ne_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0 != 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/ne_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return -1 != -2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/or_false.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0 || 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/or_true.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 || 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/precedence.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 || 0 && 2; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/precedence_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return (1 || 0) && 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/precedence_3.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 == 2 > 0; 3 | } -------------------------------------------------------------------------------- /test/stage_4/valid/precedence_4.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 2 == 2 || 0; 3 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/redefine.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | int a = 2; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/syntax_err_bad_decl.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | ints a = 1; 3 | return a; 4 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/syntax_err_bad_decl_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int foo bar = 3; 3 | return bar; 4 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/syntax_err_bad_lvalue.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | a + 3 = 4; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/syntax_err_bad_lvalue_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | !a = 3; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/syntax_err_no_semicolon.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2 3 | a = a + 4; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/undeclared_var.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return a; 3 | } -------------------------------------------------------------------------------- /test/stage_5/invalid/var_declared_late.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | a = 1 + 2; 3 | int a; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/valid/assign.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a; 3 | a = 2; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_5/valid/assign_val.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a; 3 | int b = a = 0; 4 | return b; 5 | } -------------------------------------------------------------------------------- /test/stage_5/valid/compund_assignment.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | int a = 5; 4 | int b; 5 | int c; 6 | b += a; 7 | c += (b *= 2); 8 | c /= 2; 9 | return c == 5; 10 | } 11 | -------------------------------------------------------------------------------- /test/stage_5/valid/exp_return_val.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a; 3 | int b; 4 | a = b = 4; 5 | return a - b; 6 | } -------------------------------------------------------------------------------- /test/stage_5/valid/initialize.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | return 0; 4 | } -------------------------------------------------------------------------------- /test/stage_5/valid/missing_return.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | 3 | } -------------------------------------------------------------------------------- /test/stage_5/valid/multiple_vars.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | int b = 2; 4 | return a + b; 5 | } -------------------------------------------------------------------------------- /test/stage_5/valid/no_initialize.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a; 3 | return 0; 4 | } -------------------------------------------------------------------------------- /test/stage_5/valid/refer.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | return a; 4 | } -------------------------------------------------------------------------------- /test/stage_5/valid/unused_exp.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | 2 + 2; 3 | return 0; 4 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/expression/incomplete_ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 ? 2; 3 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/expression/malformed_ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 ? 2 : 3 : 4; 3 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/expression/malformed_ternary_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1 ? 2 ? 3 : 4; 3 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/expression/ternary_assign.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | int b = 1; 4 | a > b ? a = 1 : a = 0; 5 | return a; 6 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/statement/declare_statement.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | if (5) 3 | int i = 0; 4 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/statement/if_assignment.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int flag = 0; 3 | int a = if (flag) 4 | 2; 5 | else 6 | 3; 7 | return a; 8 | } -------------------------------------------------------------------------------- /test/stage_6/invalid/statement/mismatched_nesting.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (1) 4 | return 1; 5 | else 6 | return 2; 7 | else 8 | return 3; 9 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/assign_ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | a = 1 ? 2 : 3; 4 | return a; 5 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/multiple_ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1 > 2 ? 3 : 4; 3 | int b = 1 > 2 ? 5 : 6; 4 | return a + b; 5 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/nested_ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | int b = 2; 4 | int flag = 0; 5 | 6 | return a > b ? 5 : flag ? 6 : 7; 7 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/nested_ternary_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1 ? 2 ? 3 : 4 : 5; 3 | int b = 0 ? 2 ? 3 : 4 : 5; 4 | return a * b; 5 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/rh_assignment.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int flag = 1; 3 | int a = 0; 4 | flag ? a = 1 : (a = 0); 5 | return a; 6 | } -------------------------------------------------------------------------------- /test/stage_6/valid/expression/ternary.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | return a > -1 ? 4 : 5; 4 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/else.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (a) 4 | return 1; 5 | else 6 | return 2; 7 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_nested.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | int b = 0; 4 | if (a) 5 | b = 1; 6 | else if (b) 7 | b = 2; 8 | return b; 9 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_nested_2.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | int b = 1; 4 | if (a) 5 | b = 1; 6 | else if (b) 7 | b = 2; 8 | return b; 9 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_nested_3.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (1) 4 | if (2) 5 | a = 3; 6 | else 7 | a = 4; 8 | 9 | return a; 10 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_nested_4.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (1) 4 | if (0) 5 | a = 3; 6 | else 7 | a = 4; 8 | 9 | return a; 10 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_nested_5.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (0) 4 | if (0) 5 | a = 3; 6 | else 7 | a = 4; 8 | else 9 | a = 1; 10 | 11 | return a; 12 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_not_taken.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | int b = 0; 4 | if (a) 5 | b = 1; 6 | return b; 7 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/if_taken.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | int b = 0; 4 | if (a) 5 | b = 1; 6 | return b; 7 | } -------------------------------------------------------------------------------- /test/stage_6/valid/statement/multiple_if.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | int b = 0; 4 | 5 | if (a) 6 | a = 2; 7 | else 8 | a = 3; 9 | 10 | if (b) 11 | b = 4; 12 | else 13 | b = 5; 14 | 15 | return a + b; 16 | } -------------------------------------------------------------------------------- /test/stage_7/invalid/double_define.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | { 3 | int a; 4 | int a; 5 | } 6 | } -------------------------------------------------------------------------------- /test/stage_7/invalid/out_of_scope.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | { 3 | int a = 2; 4 | } 5 | return a; 6 | } -------------------------------------------------------------------------------- /test/stage_7/invalid/syntax_err_extra_brace.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | if(0){ 3 | return 1; 4 | }} 5 | return 2; 6 | } -------------------------------------------------------------------------------- /test/stage_7/invalid/syntax_err_missing_brace.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | if(0){ 3 | return 1; 4 | return 2; 5 | } -------------------------------------------------------------------------------- /test/stage_7/valid/comparison.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | int b = 3; 4 | if (b < a) { 5 | return a; 6 | } else { 7 | return b; 8 | } 9 | } -------------------------------------------------------------------------------- /test/stage_7/valid/consecutive_blocks.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | { 4 | int a = 2; 5 | } 6 | { 7 | return a; 8 | } 9 | } -------------------------------------------------------------------------------- /test/stage_7/valid/consecutive_declarations.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | { 4 | int b = 1; 5 | a = b; 6 | } 7 | { 8 | int b = 2; 9 | a = a + b; 10 | } 11 | return a; 12 | } -------------------------------------------------------------------------------- /test/stage_7/valid/declare_after_block.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 0; 3 | { 4 | int a = 2; 5 | } 6 | int b = 3; 7 | return b; 8 | } -------------------------------------------------------------------------------- /test/stage_7/valid/declare_block.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | if (5) { 3 | int i = 0; 4 | return i; 5 | } 6 | } -------------------------------------------------------------------------------- /test/stage_7/valid/declare_late.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | { 4 | a = 3; 5 | int a = 0; 6 | } 7 | return a; 8 | } -------------------------------------------------------------------------------- /test/stage_7/valid/multi_nesting.c: -------------------------------------------------------------------------------- 1 | int main(){ 2 | int a = 2; 3 | if (a < 3) { 4 | { 5 | int a = 3; 6 | return a; 7 | } 8 | return a; 9 | } 10 | } -------------------------------------------------------------------------------- /test/stage_7/valid/nested_if.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | if (a) { 4 | int b = 2; 5 | return b; 6 | } else { 7 | int c = 3; 8 | if (a < c) { 9 | return 4; 10 | } else { 11 | return 5; 12 | } 13 | } 14 | return a; 15 | } -------------------------------------------------------------------------------- /test/stage_7/valid/nested_scope.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 2; 3 | int b = 3; 4 | { 5 | int a = 1; 6 | b = b + a; 7 | } 8 | return b; 9 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/break_not_in_loop.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | break; 3 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/continue_not_in_loop.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | continue; 3 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/out_of_scope.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | 3 | while (1) { 4 | int a = 2; 5 | } 6 | 7 | return a; 8 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/out_of_scope_do_while.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | do { 3 | int a = 2; 4 | } while (a); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_do_no_semicolon.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | do 3 | 3; 4 | while (4) 5 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_empty_clause.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | for (int i = 2; )) 3 | int a = 0; 4 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_paren_mismatch.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | for (int i = 2; )) 3 | int a = 0; 4 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_statement_in_condition.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | while(int a) { 3 | 2; 4 | } 5 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_too_few_for_clauses.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | for (int i = 2; i < 3) 3 | 3; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /test/stage_8/invalid/syntax_err_too_many_for_clauses.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | for (;;;) 3 | 3; 4 | return 0; 5 | } -------------------------------------------------------------------------------- /test/stage_8/valid/break.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int sum = 0; 3 | for (int i = 0; i < 10; i = i + 1) { 4 | sum = sum + i; 5 | if (sum > 10) 6 | break; 7 | } 8 | return sum; 9 | } -------------------------------------------------------------------------------- /test/stage_8/valid/continue.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int sum = 0; 3 | for (int i = 0; i < 10; i = i + 1) { 4 | if (sum % 2) 5 | continue; 6 | sum = sum + i; 7 | } 8 | return sum; 9 | } -------------------------------------------------------------------------------- /test/stage_8/valid/continue_empty_post.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int sum = 0; 3 | for (int i = 0; i < 10;) { 4 | i = i + 1; 5 | if (i % 2) 6 | continue; 7 | sum = sum + i; 8 | } 9 | return sum; 10 | } -------------------------------------------------------------------------------- /test/stage_8/valid/do_while.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | do { 4 | a = a * 2; 5 | } while(a < 11); 6 | 7 | return a; 8 | } -------------------------------------------------------------------------------- /test/stage_8/valid/empty_expression.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 3; 3 | ; 4 | for (int i = 0; i < 10; i = i + 1) 5 | ; 6 | return i; 7 | } -------------------------------------------------------------------------------- /test/stage_8/valid/for.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | 4 | for (a = 0; a < 3; a = a + 1) 5 | a = a * 2; 6 | return a; 7 | } -------------------------------------------------------------------------------- /test/stage_8/valid/for_decl.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | 4 | for (int i = 0; i < 3; i = i + 1) 5 | a = a + 1; 6 | return a; 7 | } -------------------------------------------------------------------------------- /test/stage_8/valid/for_empty.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | for (; ; ) { 4 | a = a + 1; 5 | if (a > 3) 6 | break; 7 | } 8 | 9 | return a; 10 | } -------------------------------------------------------------------------------- /test/stage_8/valid/for_nested_scope.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 0; 3 | int j = 0; 4 | 5 | for (int i = 100; i > 0; i = i - 1) { 6 | int i = 0; 7 | int j = j * 2 + i; 8 | } 9 | 10 | int k = 3; 11 | 12 | return j + k; 13 | } -------------------------------------------------------------------------------- /test/stage_8/valid/for_variable_shadow.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int i = 0; 3 | int j = 0; 4 | for (i = 0; i < 10; i = i + 1) { 5 | int k = i; 6 | for (int i = k; i < 10; i = i + 1) 7 | j = j + 1; 8 | } 9 | return j + i; 10 | } -------------------------------------------------------------------------------- /test/stage_8/valid/nested_break.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int ans = 0; 3 | for (int i = 0; i < 10; i = i + 1) 4 | for (int j = 0; j < 10; j = j + 1) 5 | if (i % 2 == 0) 6 | break; 7 | else 8 | ans = ans + i; 9 | return ans; 10 | } -------------------------------------------------------------------------------- /test/stage_8/valid/nested_while.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 1; 3 | 4 | while (a % 3 != 0) { 5 | int b = 1; 6 | while (b < 10) 7 | b = b*2; 8 | a = a + b; 9 | } 10 | 11 | return a; 12 | } -------------------------------------------------------------------------------- /test/stage_8/valid/return_in_while.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | while (1) { 3 | return 2; 4 | } 5 | } -------------------------------------------------------------------------------- /test/stage_8/valid/scope_access.c: -------------------------------------------------------------------------------- 1 | int main() 2 | { 3 | int a = 0; 4 | { 5 | a = 1; 6 | { 7 | a = 2; 8 | } 9 | } 10 | return a; 11 | } -------------------------------------------------------------------------------- /test/stage_8/valid/while_multi_statement.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | int b = 1; 4 | 5 | while (a < 5) { 6 | a = a + 2; 7 | b = b * a; 8 | } 9 | 10 | return a; 11 | } -------------------------------------------------------------------------------- /test/stage_8/valid/while_single_statement.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | int a = 0; 3 | 4 | while (a < 5) 5 | a = a + 2; 6 | 7 | return a; 8 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/bad_arg.c: -------------------------------------------------------------------------------- 1 | int foo(int a){ 2 | return 3 + a; 3 | } 4 | 5 | int main(){ 6 | return foo(); 7 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/declaration_mismatch.c: -------------------------------------------------------------------------------- 1 | int foo(int a); 2 | 3 | int main() { 4 | return 5; 5 | } 6 | 7 | int foo(int a, int b) { 8 | return 4; 9 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/declaration_mismatch_2.c: -------------------------------------------------------------------------------- 1 | int foo(int a, int b); 2 | 3 | int main() { 4 | return 5; 5 | } 6 | 7 | int foo(int a) { 8 | return 4; 9 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/redefine_function.c: -------------------------------------------------------------------------------- 1 | int foo(){ 2 | return 3; 3 | } 4 | 5 | int main() { 6 | return foo(); 7 | } 8 | 9 | int foo(){ 10 | return 4; 11 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/redefine_variable.c: -------------------------------------------------------------------------------- 1 | int foo(int x) { 2 | int x = 3; 3 | } 4 | 5 | int main() { 6 | foo(1); 7 | } -------------------------------------------------------------------------------- /test/stage_9/invalid/too_many_args.c: -------------------------------------------------------------------------------- 1 | int foo(int a) { 2 | return a + 1; 3 | } 4 | 5 | int main() { 6 | return foo(1, 2); 7 | } -------------------------------------------------------------------------------- /test/stage_9/valid/expression_args.c: -------------------------------------------------------------------------------- 1 | int add(int a, int b) { 2 | return a + b; 3 | } 4 | 5 | int main() { 6 | int sum = add(1 + 2, 4); 7 | return sum + sum; 8 | } 9 | -------------------------------------------------------------------------------- /test/stage_9/valid/fib.c: -------------------------------------------------------------------------------- 1 | int fib(int n) { 2 | if (n == 0 || n == 1) { 3 | return n; 4 | } else { 5 | return fib(n - 1) + fib(n - 2); 6 | } 7 | } 8 | 9 | int main() { 10 | int n = 5; 11 | return fib(n); 12 | } -------------------------------------------------------------------------------- /test/stage_9/valid/forward_decl.c: -------------------------------------------------------------------------------- 1 | int foo(); 2 | 3 | int main() { 4 | return foo(); 5 | } 6 | 7 | int foo() { 8 | return 3; 9 | } -------------------------------------------------------------------------------- /test/stage_9/valid/forward_decl_args.c: -------------------------------------------------------------------------------- 1 | int foo(int a); 2 | 3 | int main(){ 4 | return foo(3); 5 | } 6 | 7 | int foo(int a){ 8 | return a + 1; 9 | } -------------------------------------------------------------------------------- /test/stage_9/valid/forward_decl_multi_arg.c: -------------------------------------------------------------------------------- 1 | int foo(int a, int b); 2 | 3 | int main() { 4 | return foo(1, 2); 5 | } 6 | 7 | int foo(int x, int y){ 8 | return x - y; 9 | } -------------------------------------------------------------------------------- /test/stage_9/valid/fun_in_expr.c: -------------------------------------------------------------------------------- 1 | int sum(int a, int b) { 2 | return a + b; 3 | } 4 | 5 | int main() { 6 | int a = sum(1, 2) % 2; 7 | int b = 2*sum(3, 4) + sum(1, 2); 8 | return b - a; 9 | } -------------------------------------------------------------------------------- /test/stage_9/valid/hello_world.c: -------------------------------------------------------------------------------- 1 | int putchar(int c); 2 | 3 | int main() { 4 | putchar(72); 5 | putchar(101); 6 | putchar(108); 7 | putchar(108); 8 | putchar(111); 9 | putchar(44); 10 | putchar(32); 11 | putchar(87); 12 | putchar(111); 13 | putchar(114); 14 | putchar(108); 15 | putchar(100); 16 | putchar(33); 17 | putchar(10); 18 | } -------------------------------------------------------------------------------- /test/stage_9/valid/later_decl.c: -------------------------------------------------------------------------------- 1 | int foo(int a) { 2 | return a + 1; 3 | } 4 | 5 | int main() { 6 | return foo(4); 7 | } 8 | 9 | int foo(int a); -------------------------------------------------------------------------------- /test/stage_9/valid/multi_arg.c: -------------------------------------------------------------------------------- 1 | int sub_3(int x, int y, int z) { 2 | return x - y - z; 3 | } 4 | 5 | int main() { 6 | return sub_3(10, 4, 2); 7 | } -------------------------------------------------------------------------------- /test/stage_9/valid/mutual_recursion.c: -------------------------------------------------------------------------------- 1 | int foo(int a); 2 | int bar(int b); 3 | 4 | int main() { 5 | return foo(5); 6 | } 7 | 8 | int foo(int a) { 9 | if (a <= 0) { 10 | return a; 11 | } 12 | 13 | return a + bar(a - 1); 14 | } 15 | 16 | int bar(int b) { 17 | if (b <= 0) { 18 | return b; 19 | } 20 | 21 | return b + bar(b / 2); 22 | } -------------------------------------------------------------------------------- /test/stage_9/valid/no_arg.c: -------------------------------------------------------------------------------- 1 | int three(){ 2 | return 3; 3 | } 4 | 5 | int main() { 6 | return three(); 7 | } -------------------------------------------------------------------------------- /test/stage_9/valid/precedence.c: -------------------------------------------------------------------------------- 1 | int three() { 2 | return 3; 3 | } 4 | 5 | int main() { 6 | return !three(); 7 | } -------------------------------------------------------------------------------- /test/stage_9/valid/rename_function_param.c: -------------------------------------------------------------------------------- 1 | int foo(int b); 2 | 3 | int main(){ 4 | return foo(3); 5 | } 6 | 7 | int foo(int a){ 8 | return a + 1; 9 | } -------------------------------------------------------------------------------- /test/stage_9/valid/single_arg.c: -------------------------------------------------------------------------------- 1 | int twice(int x){ 2 | return 2 * x; 3 | } 4 | 5 | int main() { 6 | return twice(3); 7 | } -------------------------------------------------------------------------------- /test/test_compiler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | padding_dots=$(printf '%0.1s' "."{1..60}) 4 | padlength=50 5 | 6 | test_success () { 7 | echo "OK" 8 | ((success++)) 9 | } 10 | 11 | test_failure () { 12 | echo "FAIL" 13 | ((fail++)) 14 | } 15 | 16 | if [ "$1" == "" ]; then 17 | echo "USAGE: ./test_compiler.sh /path/to/compiler" 18 | exit 1 19 | fi 20 | 21 | cmp=$1 22 | 23 | success_total=0 24 | failure_total=0 25 | 26 | num_stages=9 27 | 28 | for i in `seq 1 $num_stages`; do 29 | success=0 30 | fail=0 31 | echo "====================================================" 32 | echo "STAGE $i" 33 | echo "===================Valid Programs===================" 34 | for prog in `ls stage_$i/valid/{,**/}*.c 2>/dev/null`; do 35 | gcc -w $prog 36 | expected_out=`./a.out` 37 | expected_exit_code=$? 38 | rm a.out 39 | 40 | $cmp $prog >/dev/null 41 | base="${prog%.*}" #name of executable (filename w/out extension) 42 | actual_out=`./$base` 43 | actual_exit_code=$? 44 | test_name="${base##*valid/}" 45 | printf '%s' "$test_name" 46 | printf '%*.*s' 0 $((padlength - ${#test_name})) "$padding_dots" 47 | 48 | if [[ $test_name == "undefined"* ]]; then 49 | # return value is undefined 50 | # make sure it runs w/out segfaulting, but otherwise don't check exit code 51 | if [ "$actual_exit_code" -eq 139 ]; then 52 | #segfault! 53 | test_failure 54 | else 55 | test_success 56 | fi 57 | else 58 | # make sure exit code is correct 59 | if [ "$expected_exit_code" -ne "$actual_exit_code" ] || [ "$expected_out" != "$actual_out" ] 60 | then 61 | test_failure 62 | else 63 | test_success 64 | fi 65 | fi 66 | rm $base 67 | done 68 | echo "===================Invalid Programs=================" 69 | for prog in `ls stage_$i/invalid/{,**/}*.c 2>/dev/null`; do 70 | base="${prog%.*}" #name of executable (filename w/out extension) 71 | test_name="${base##*invalid/}" 72 | 73 | $cmp $prog >/dev/null 2>&1 74 | failed=$? #failed, as we expect, if exit code != 0 75 | 76 | printf '%s' "$test_name" 77 | printf '%.*s' $((padlength - ${#test_name})) "$padding_dots" 78 | 79 | if [[ -f $base || -f $base".s" ]] #make sure neither executable nor assembly was produced 80 | then 81 | test_failure 82 | rm $base 2>/dev/null 83 | rm $base".s" 2>/dev/null 84 | else 85 | test_success 86 | fi 87 | done 88 | echo "===================Stage $i Summary=================" 89 | printf "%d successes, %d failures\n" $success $fail 90 | ((success_total=success_total+success)) 91 | ((failure_total=failure_total + fail)) 92 | done 93 | 94 | echo "===================TOTAL SUMMARY====================" 95 | printf "%d successes, %d failures\n" $success_total $failure_total 96 | --------------------------------------------------------------------------------