├── LICENSE ├── Makefile ├── README.md ├── assoc_list.h ├── ast.cc ├── ast.h ├── cons_list.h ├── examples ├── addrof1.fc ├── fun-recur.fc ├── fun1.fc ├── fun2.fc ├── funptr1.fc ├── goto1.fc ├── goto2.fc ├── malloc1.fc ├── undef1.fc ├── undef2.fc └── zero.fc ├── interp.cc ├── interp.h ├── syntax.l ├── syntax.y ├── typecheck.cc └── typecheck.h /LICENSE: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | This code is under the Apache License v2.0 with LLVM Exceptions: 3 | ============================================================================== 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright [yyyy] [name of copyright owner] 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | 207 | 208 | ---- LLVM Exceptions to the Apache 2.0 License ---- 209 | 210 | As an exception, if, as a result of your compiling your source code, portions 211 | of this Software are embedded into an Object form of such source code, you 212 | may redistribute such embedded portions in such Object form without complying 213 | with the conditions of Sections 4(a), 4(b) and 4(d) of the License. 214 | 215 | In addition, if you combine or link compiled forms of this Software with 216 | software that is licensed under the GPLv2 ("Combined Software") and if a 217 | court of competent jurisdiction determines that the patent provision (Section 218 | 3), the indemnity provision (Section 9) or other Section of the License 219 | conflicts with the conditions of the GPLv2, you may retroactively and 220 | prospectively choose to deem waived or otherwise exclude such Section(s) of 221 | the License, but only in their entirety and only with respect to the Combined 222 | Software. 223 | 224 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | a.out: syntax.l syntax.y ast.h ast.cc typecheck.cc interp.cc cons_list.h assoc_list.h 2 | bison -d syntax.y 3 | flex syntax.l 4 | bison -v --debug syntax.y -o syntax.tab.cc 5 | g++ -std=c++11 -Wno-deprecated-register -c -g lex.yy.c 6 | g++ -std=c++11 -c -g ast.cc 7 | g++ -std=c++11 -c -g typecheck.cc 8 | g++ -std=c++11 -c -g interp.cc 9 | g++ -std=c++11 -g lex.yy.o ast.o typecheck.o interp.o syntax.tab.cc 10 | 11 | clean: 12 | rm -f *.o 13 | rm -f syntax.tab.h 14 | rm -f syntax.tab.cc syntax.tab.c syntax.output 15 | rm -f lex.yy.c lex.yy.cc 16 | rm -f a.out 17 | rm -f log 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # featherweight-C 2 | 3 | Featherweight C, Executable Semantics: Parser, Type Checker, and 4 | Abstract Machine 5 | 6 | This tiny C-like language includes several kinds of values: integer, 7 | Booleans, pointers, and function pointers. Regarding control-flow, it 8 | includes if statements, goto, and function calls. 9 | 10 | The parser is implemented using the flex and bison parser generator 11 | tools. The syntax of Featherweight C diverges in a few places from C 12 | to make for a simpler grammar. This grammar does not have any parser 13 | conflicts (shift/reduce or reduce/reduce). 14 | 15 | * [`syntax.l`](./syntax.l) the lexer specification 16 | * [`syntax.y`](./syntax.y) the grammar 17 | 18 | The parser translates program text into an abstract syntax tree (AST). 19 | 20 | * [`ast.h`](./ast.h) includes structure definitions for the AST and function 21 | declarations for creating and printing ASTs. 22 | * [`ast.cc`](./ast.cc) contains the function definitions. 23 | 24 | The type checker defines what it means for an AST to be a valid 25 | program. The type checker prints an error and exits if the AST is 26 | invalid. 27 | 28 | * [`typecheck.h`](./typecheck.h) 29 | * [`typecheck.cc`](./typecheck.cc) 30 | 31 | The parser and type checker together specify the static semantics 32 | of Featherweight C. 33 | 34 | The dynamic semantics of Featherweight C is specified by an abstract 35 | machine. Abstract machines have several positive characteristics that 36 | make them good for specification: 37 | 38 | * abstract machines operate on the AST of the program 39 | (and not some lower-level representation such as bytecode) 40 | so they directly connect the program to its behavior 41 | 42 | * abstract machines can easily handle language features with complex 43 | control-flow, such as goto, exceptions, coroutines, and even 44 | first-class continuations. 45 | 46 | The one down-side of abstract machines is that they are not as simple 47 | as a definitional interpreter (a recursive function that interprets 48 | the program), but its more difficult to handle complex control flow in 49 | a definitional interpreter. 50 | 51 | * [`interp.h`](./interp.h) declares the `interp_program` function. 52 | * [`interp.cc`](./interp.cc) implements `interp_program` function using an 53 | abstract machine, as described below. 54 | 55 | The abstract machine implements a state-transition system. The state 56 | is defined by the `State` structure, which includes three components: 57 | the procedure call stack, the heap, and the function definitions. The 58 | `step` function updates the state by executing a little bit of the 59 | program. The `step` function is called repeatedly to execute the 60 | entire program. 61 | 62 | An implementation of Featherweight C (such as a compiler) must be 63 | observationally equivalent to this abstract machine. The notion of 64 | observation is different for each language, and can include things 65 | like input and output. Featherweight C is such a simple language that 66 | the only thing that is observable is the final result, an integer. So 67 | an implementation of Featherweight C must produce the same final 68 | result as the one produces by the abstract machine. In particular, an 69 | implementation does **not** have to mimic each step of the abstract 70 | machine and does not have to use the same kinds of data structures to 71 | store the state of the program. 72 | 73 | A procedure call frame, defined by the `Frame` structure, includes a 74 | pointer to the function being called, the environment that maps 75 | variables to their addresses, and a to-do list of actions. Each 76 | action corresponds to an expression or statement in the program. The 77 | `Act` structure represents an action. An action often spawns other 78 | actions that needs to be completed first and afterwards uses their 79 | results to complete its action. To keep track of this process, each 80 | action includes a position field `pos` that stores an integer that 81 | starts at `-1` and increments as the action makes progress. For 82 | example, suppose the action associated with an addition expression 83 | `e1 + e2` as at the top of the to-do list: 84 | 85 | (e1 + e2) [-1] :: ... 86 | 87 | When this action kicks off (in the `step_exp` function), it increments 88 | `pos` to `0` and pushes `e1` onto the to-do list, so the top of the 89 | todo list now looks like: 90 | 91 | e1 [-1] :: (e1 + e2) [0] :: ... 92 | 93 | Skipping over the processing of `e1`, it eventually turns into 94 | an integer value `n1`: 95 | 96 | n1 :: (e1 + e2) [0] 97 | 98 | Because there is a value at the top of the to-do list, the `step` 99 | function invokes `handle_value` which then dispatches on the next 100 | action on the to-do list, in this case the addition. The addition 101 | action spawns an action for subexpression `e2`, increments 102 | `pos` to `1`, and remembers `n1`. 103 | 104 | e2 [-1] :: (e1 + e2) [1](n1) :: ... 105 | 106 | Skipping over the processing of `e2`, it eventually turns into 107 | an integer value `n2`: 108 | 109 | n2 :: (e1 + e2) [1](n1) :: ... 110 | 111 | Again the `step` function invokes `handle_value` and dispatches to the 112 | addition action which performs the arithmetic and pushes the result on 113 | the to-do list. Let `n3` be the sum of `n1` and `n2`. 114 | 115 | n3 :: ... 116 | 117 | The heap is an array of values. It is used not only for `malloc` but 118 | also to store function arguments and local variables. A pointer is 119 | simply an index into the array. The `malloc` expression causes the 120 | heap to grow (at the end) and returns the index of the last slot. The 121 | dereference expression returns the nth value of the heap, as specified 122 | by the dereferenced pointer. The assignment operation stores the value 123 | of the right-hand side into the heap at the index specified by the 124 | left-hand side lvalue. 125 | 126 | As you might expect, function calls push a new frame on the stack and 127 | the `return` statement pops a frame off the stack. The parameter 128 | passing semantics is call-by-value, so the machine applies `copy_val` 129 | to the incoming arguments and the outgoing return value. Also, the 130 | machine is careful to kill the parameters and local variables when the 131 | function call is complete. 132 | 133 | The handling of the `goto` statement deserves some explanation. It 134 | overwrites the todo list of the frame with a new one computed by the 135 | function `goto_label`. This function searches through the body of the 136 | current frame, looking for the target label. So the first parameter of 137 | `goto_label` is the target label and the second parameter is the 138 | current statement that it is searching. Also, while searching, 139 | `goto_label` accumulates a todo list of statements that come after the 140 | one it is currently searching. The third parameter is for the 141 | accumulated todo list. Once `goto_label` finds a statement that is 142 | labeled with the target label, it adds that statement to the front of 143 | the accumulated todo list and returns it. 144 | 145 | The [`examples/`](./examples/) subdirectory includes some Featherweight C programs. 146 | -------------------------------------------------------------------------------- /assoc_list.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSOC_LIST_H 2 | #define ASSOC_LIST_H 3 | 4 | #include 5 | #include 6 | using std::cerr; 7 | using std::endl; 8 | 9 | template 10 | struct AList { 11 | K key; 12 | V value; 13 | AList* next; 14 | AList(K k, V v, AList* n) : key(k), value(v), next(n) { } 15 | }; 16 | 17 | template 18 | V lookup(AList* alist, K key, void (*print_key)(K)) { 19 | if (alist == NULL) { 20 | cerr << "runtime error: could not find `"; 21 | print_key(key); 22 | cerr << "`" << endl; 23 | exit(-1); 24 | } else if (alist->key == key) { 25 | return alist->value; 26 | } else { 27 | return lookup(alist->next, key, print_key); 28 | } 29 | } 30 | 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /ast.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "ast.h" 6 | 7 | using std::cout; 8 | using std::endl; 9 | 10 | /***** Utilities *****/ 11 | 12 | char* input; 13 | 14 | /***** Types *****/ 15 | 16 | Type* make_int_type(int lineno) { 17 | Type* t = new Type(); 18 | t->tag = IntT; 19 | t->lineno = lineno; 20 | return t; 21 | } 22 | 23 | Type* make_bool_type(int lineno) { 24 | Type* t = new Type(); 25 | t->tag = BoolT; 26 | t->lineno = lineno; 27 | return t; 28 | } 29 | 30 | Type* make_fun_type(int lineno, list* params, Type* ret) { 31 | Type* t = new Type(); 32 | t->tag = FunT; 33 | t->lineno = lineno; 34 | t->u.fun.params = params; 35 | t->u.fun.ret = ret; 36 | return t; 37 | } 38 | 39 | Type* make_ptr_type(int lineno, Type* type) { 40 | Type* t = new Type(); 41 | t->tag = PtrT; 42 | t->lineno = lineno; 43 | t->u.ptr.type = type; 44 | return t; 45 | } 46 | 47 | void print_type(Type* t) { 48 | switch (t->tag) { 49 | case BoolT: 50 | cout << "bool"; 51 | break; 52 | case IntT: 53 | cout << "int"; 54 | break; 55 | case PtrT: 56 | print_type(t->u.ptr.type); 57 | cout << "*"; 58 | break; 59 | case FunT: 60 | cout << "fun "; 61 | cout << "("; 62 | print_list(t->u.fun.params, print_type, ", "); 63 | cout << " "; 64 | print_type(t->u.fun.ret); 65 | break; 66 | } 67 | } 68 | 69 | /***** Expressions *****/ 70 | 71 | Exp* make_var(int lineno, string var) { 72 | Exp* lv = new Exp(); 73 | lv->lineno = lineno; 74 | lv->tag = Var; 75 | lv->u.var = new string(var); 76 | return lv; 77 | } 78 | 79 | Exp* make_deref(int lineno, Exp* exp) { 80 | Exp* lv = new Exp(); 81 | lv->lineno = lineno; 82 | lv->tag = Deref; 83 | lv->u.deref = exp; 84 | return lv; 85 | } 86 | 87 | Exp* make_int(int lineno, int i) { 88 | Exp* e = new Exp(); 89 | e->lineno = lineno; 90 | e->tag = Int; 91 | e->u.integer = i; 92 | return e; 93 | } 94 | 95 | Exp* make_bool(int lineno, bool b) { 96 | Exp* e = new Exp(); 97 | e->lineno = lineno; 98 | e->tag = Bool; 99 | e->u.boolean = b; 100 | return e; 101 | } 102 | 103 | Exp* make_addr_of(int lineno, Exp* lval) { 104 | Exp* e = new Exp(); 105 | e->lineno = lineno; 106 | e->tag = AddrOf; 107 | e->u.addr_of = lval; 108 | return e; 109 | } 110 | 111 | Exp* make_op(int lineno, enum Operator op, list* args) { 112 | Exp* e = new Exp(); 113 | e->lineno = lineno; 114 | e->tag = PrimOp; 115 | e->u.prim_op.op = op; 116 | e->u.prim_op.args = new vector(args->begin(), args->end()); 117 | return e; 118 | } 119 | 120 | Exp* make_unop(int lineno, enum Operator op, Exp* arg) { 121 | Exp* e = new Exp(); 122 | e->lineno = lineno; 123 | e->tag = PrimOp; 124 | e->u.prim_op.op = op; 125 | vector* args = new vector(); 126 | args->push_back(arg); 127 | e->u.prim_op.args = args; 128 | return e; 129 | } 130 | 131 | Exp* make_binop(int lineno, enum Operator op, Exp* arg1, Exp* arg2) { 132 | Exp* e = new Exp(); 133 | e->lineno = lineno; 134 | e->tag = PrimOp; 135 | e->u.prim_op.op = op; 136 | vector* args = new vector(); 137 | args->push_back(arg1); 138 | args->push_back(arg2); 139 | e->u.prim_op.args = args; 140 | return e; 141 | } 142 | 143 | Exp* make_call(int lineno, Exp* fun, list* args) { 144 | Exp* e = new Exp(); 145 | e->lineno = lineno; 146 | e->tag = Call; 147 | e->u.call.fun = fun; 148 | e->u.call.args = new vector(args->begin(), args->end()); 149 | return e; 150 | } 151 | 152 | Exp* make_malloc(int lineno, Type* type) { 153 | Exp* e = new Exp(); 154 | e->lineno = lineno; 155 | e->tag = Malloc; 156 | e->u.malloc = type; 157 | return e; 158 | } 159 | 160 | void print_op(Operator op) { 161 | switch (op) { 162 | case Neg: 163 | cout << "-"; 164 | break; 165 | case Add: 166 | cout << "+"; 167 | break; 168 | case Sub: 169 | cout << "-"; 170 | break; 171 | case Not: 172 | cout << "!"; 173 | break; 174 | case And: 175 | cout << "&&"; 176 | break; 177 | case Or: 178 | cout << "||"; 179 | break; 180 | case Eq: 181 | cout << "=="; 182 | break; 183 | } 184 | } 185 | 186 | void print_exp(Exp* e) { 187 | switch (e->tag) { 188 | case Int: 189 | cout << e->u.integer; 190 | break; 191 | case Bool: 192 | cout << std::boolalpha; 193 | cout << e->u.boolean; 194 | break; 195 | case AddrOf: 196 | cout << "&"; 197 | print_exp(e->u.addr_of); 198 | break; 199 | case PrimOp: 200 | if (e->u.prim_op.args->size() == 0) { 201 | print_op(e->u.prim_op.op); 202 | } else if (e->u.prim_op.args->size() == 1) { 203 | print_op(e->u.prim_op.op); 204 | auto iter = e->u.prim_op.args->begin(); 205 | print_exp(*iter); 206 | } else if (e->u.prim_op.args->size() == 2) { 207 | auto iter = e->u.prim_op.args->begin(); 208 | print_exp(*iter); 209 | print_op(e->u.prim_op.op); 210 | ++iter; 211 | print_exp(*iter); 212 | } 213 | break; 214 | case Var: 215 | cout << *(e->u.var); 216 | break; 217 | case Deref: 218 | cout << "*"; 219 | print_exp(e->u.deref); 220 | break; 221 | case Call: 222 | print_exp(e->u.call.fun); 223 | cout << "("; 224 | print_vector(e->u.call.args, print_exp, ", "); 225 | cout << ")"; 226 | break; 227 | case Malloc: 228 | cout << "malloc("; 229 | print_type(e->u.malloc); 230 | cout << ")"; 231 | break; 232 | } 233 | } 234 | 235 | /***** Statements *****/ 236 | 237 | Stmt* make_exp_stmt(int lineno, Exp* exp) { 238 | Stmt* s = new Stmt(); 239 | s->lineno = lineno; 240 | s->tag = ExpStmt; 241 | s->u.exp = exp; 242 | return s; 243 | } 244 | 245 | Stmt* make_assign(int lineno, Exp* lhs, Exp* rhs) { 246 | Stmt* s = new Stmt(); 247 | s->lineno = lineno; 248 | s->tag = Assign; 249 | s->u.assign.lhs = lhs; 250 | s->u.assign.rhs = rhs; 251 | return s; 252 | } 253 | 254 | Stmt* make_free(int lineno, Exp* e) { 255 | Stmt* s = new Stmt(); 256 | s->lineno = lineno; 257 | s->tag = Free; 258 | s->u.free = e; 259 | return s; 260 | } 261 | 262 | Stmt* make_if(int lineno, Exp* cond, Stmt* thn, Stmt* els) { 263 | Stmt* s = new Stmt(); 264 | s->lineno = lineno; 265 | s->tag = If; 266 | s->u.if_stmt.cond = cond; 267 | s->u.if_stmt.thn = thn; 268 | s->u.if_stmt.els = els; 269 | return s; 270 | } 271 | 272 | Stmt* make_goto(int lineno, string target) { 273 | Stmt* s = new Stmt(); 274 | s->lineno = lineno; 275 | s->tag = Goto; 276 | s->u.goto_stmt.target = new string(target); 277 | return s; 278 | } 279 | 280 | Stmt* make_labeled(int lineno, string label, Stmt* stmt) { 281 | Stmt* s = new Stmt(); 282 | s->lineno = lineno; 283 | s->tag = Label; 284 | s->u.labeled.label = new string(label); 285 | s->u.labeled.stmt = stmt; 286 | return s; 287 | } 288 | 289 | Stmt* make_return(int lineno, Exp* e) { 290 | Stmt* s = new Stmt(); 291 | s->lineno = lineno; 292 | s->tag = Return; 293 | s->u.ret = e; 294 | return s; 295 | } 296 | 297 | Stmt* make_seq(int lineno, Stmt* s1, Stmt* s2) { 298 | Stmt* s = new Stmt(); 299 | s->lineno = lineno; 300 | s->tag = Seq; 301 | s->u.seq.stmt = s1; 302 | s->u.seq.next = s2; 303 | return s; 304 | } 305 | 306 | void print_stmt(Stmt* s, int depth) { 307 | if (depth == 0) { 308 | cout << " ... "; 309 | return; 310 | } 311 | switch (s->tag) { 312 | case ExpStmt: 313 | print_exp(s->u.exp); 314 | cout << ";"; 315 | break; 316 | case Assign: 317 | print_exp(s->u.assign.lhs); 318 | cout << " = "; 319 | print_exp(s->u.assign.rhs); 320 | cout << ";"; 321 | break; 322 | case Free: 323 | cout << "free("; 324 | print_exp(s->u.free); 325 | cout << ")"; 326 | break; 327 | case If: 328 | cout << "if ("; 329 | print_exp(s->u.if_stmt.cond); 330 | cout << ")" << endl; 331 | print_stmt(s->u.if_stmt.thn, depth - 1); 332 | cout << endl << "else" << endl; 333 | print_stmt(s->u.if_stmt.els, depth - 1); 334 | break; 335 | case Goto: 336 | cout << "goto "; 337 | cout << (*s->u.goto_stmt.target) << ";"; 338 | break; 339 | case Label: 340 | cout << *s->u.labeled.label << ":"; 341 | if (depth != 0) 342 | cout << endl; 343 | print_stmt(s->u.labeled.stmt, depth - 1); 344 | break; 345 | case Return: 346 | cout << "return "; 347 | print_exp(s->u.ret); 348 | cout << ";"; 349 | break; 350 | case Seq: 351 | print_stmt(s->u.seq.stmt, depth); 352 | if (depth != 0) 353 | cout << endl; 354 | print_stmt(s->u.seq.next, depth - 1); 355 | break; 356 | } 357 | } 358 | 359 | /***** Declarations *****/ 360 | 361 | FunDef* make_fun_def(int lineno, string name, Type* ret_type, VarTypes* params, 362 | VarTypes* locals, Stmt* body) { 363 | FunDef* f = new FunDef(); 364 | f->lineno = lineno; 365 | f->name = name; 366 | f->return_type = ret_type; 367 | f->params = params; 368 | f->locals = locals; 369 | f->body = body; 370 | return f; 371 | } 372 | 373 | void print_params(VarTypes* ps) { 374 | int i = 0; 375 | for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { 376 | if (i != 0) 377 | cout << ", "; 378 | print_type(iter->second); 379 | cout << " " << iter->first; 380 | } 381 | } 382 | 383 | void print_var_decls(VarTypes* ps) { 384 | int i = 0; 385 | for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { 386 | print_type(iter->second); 387 | cout << " " << iter->first << "; "; 388 | } 389 | } 390 | 391 | void print_fun_def_depth(FunDef* f, int depth) { 392 | cout << "fun " << f->name; 393 | cout << "("; 394 | print_params(f->params); 395 | cout << " "; 396 | print_type(f->return_type); 397 | cout << " {" << endl; 398 | if (f->locals->size() > 0) { 399 | cout << " "; 400 | print_var_decls(f->locals); 401 | cout << endl; 402 | } 403 | print_stmt(f->body, depth); 404 | cout << endl << "}" << endl; 405 | } 406 | 407 | void print_fun_def(FunDef* f) { 408 | print_fun_def_depth(f, -1); 409 | } 410 | 411 | char *read_file(FILE* fp) 412 | { 413 | char *fcontent = NULL; 414 | int fsize = 0; 415 | 416 | if(fp) { 417 | fseek(fp, 0, SEEK_END); 418 | fsize = ftell(fp); 419 | rewind(fp); 420 | 421 | fcontent = (char*) malloc(sizeof(char) * fsize); 422 | fread(fcontent, 1, fsize, fp); 423 | 424 | fclose(fp); 425 | } 426 | return fcontent; 427 | } 428 | -------------------------------------------------------------------------------- /ast.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_H 2 | #define AST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using std::string; 10 | using std::list; 11 | using std::vector; 12 | using std::pair; 13 | 14 | /***** Utilities *****/ 15 | 16 | template 17 | void print_list(list* ts, void(*printer)(T*), const char* sep) { 18 | int i = 0; 19 | for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { 20 | if (i != 0) 21 | printf("%s", sep); 22 | printer(*iter); 23 | } 24 | } 25 | 26 | template 27 | void print_vector(vector* ts, void(*printer)(T*), const char* sep) { 28 | int i = 0; 29 | for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { 30 | if (i != 0) 31 | printf("%s", sep); 32 | printer(*iter); 33 | } 34 | } 35 | 36 | char *read_file(FILE* fp); 37 | 38 | extern char* input; 39 | 40 | /***** Forward Declarations *****/ 41 | 42 | struct Type; 43 | struct LValue; 44 | struct Exp; 45 | struct Stmt; 46 | struct FunDef; 47 | 48 | typedef list< pair > VarTypes; 49 | 50 | /***** Types *****/ 51 | 52 | enum TypeKind { IntT, FunT, PtrT, BoolT }; 53 | 54 | struct Type { 55 | int lineno; 56 | TypeKind tag; 57 | union { 58 | struct { list* params; Type* ret; } fun; 59 | struct { Type* type; } ptr; 60 | } u; 61 | }; 62 | 63 | Type* make_int_type(int lineno); 64 | Type* make_bool_type(int lineno); 65 | Type* make_fun_type(int lineno, list* params, Type* ret); 66 | Type* make_ptr_type(int lineno, Type* type); 67 | 68 | void print_type(Type*); 69 | 70 | /***** Expressions *****/ 71 | 72 | enum ExpKind { Var, Deref, Int, Bool, AddrOf, PrimOp, Call, Malloc }; 73 | enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; 74 | 75 | struct Exp { 76 | int lineno; 77 | ExpKind tag; 78 | union { 79 | string* var; 80 | Exp* deref; 81 | int integer; 82 | bool boolean; 83 | Exp* addr_of; 84 | Type* malloc; 85 | struct { Operator op; vector* args; } prim_op; 86 | struct { Exp* fun; vector* args; } call; 87 | } u; 88 | }; 89 | 90 | Exp* make_var(int lineno, string var); 91 | Exp* make_deref(int lineno, Exp* exp); 92 | Exp* make_int(int lineno, int i); 93 | Exp* make_bool(int lineno, bool b); 94 | Exp* make_addr_of(int lineno, Exp* lval); 95 | Exp* make_op(int lineno, Operator op, list* args); 96 | Exp* make_malloc(int lineno, Type* type); 97 | Exp* make_unop(int lineno, enum Operator op, Exp* arg); 98 | Exp* make_binop(int lineno, enum Operator op, Exp* arg1, Exp* arg2); 99 | Exp* make_call(int lineno, Exp* fun, list* args); 100 | 101 | void print_exp(Exp*); 102 | 103 | /***** Statements *****/ 104 | 105 | enum StmtKind { ExpStmt, Assign, Free, If, Goto, Label, Return, Seq }; 106 | 107 | struct Stmt { 108 | int lineno; 109 | StmtKind tag; 110 | union { 111 | Exp* exp; 112 | struct { Exp* lhs; Exp* rhs; } assign; 113 | Exp* free; 114 | struct { Exp* cond; Stmt* thn; Stmt* els; } if_stmt; 115 | struct { string* target; } goto_stmt; 116 | struct { string* label; Stmt* stmt; } labeled; 117 | Exp* ret; 118 | struct { Stmt* stmt; Stmt* next; } seq; 119 | } u; 120 | }; 121 | 122 | Stmt* make_exp_stmt(int lineno, Exp* exp); 123 | Stmt* make_assign(int lineno, Exp* lhs, Exp* rhs); 124 | Stmt* make_free(int lineno, Exp* e); 125 | Stmt* make_if(int lineno, Exp* cond, Stmt* thn, Stmt* els); 126 | Stmt* make_goto(int lineno, string target); 127 | Stmt* make_labeled(int lineno, string label, Stmt* stmt); 128 | Stmt* make_return(int lineno, Exp* e); 129 | Stmt* make_seq(int lineno, Stmt* s1, Stmt* s2); 130 | 131 | void print_stmt(Stmt*, int); 132 | 133 | /***** Declarations *****/ 134 | 135 | struct FunDef { 136 | int lineno; 137 | string name; 138 | Type* return_type; 139 | VarTypes* params; 140 | VarTypes* locals; 141 | Stmt* body; 142 | }; 143 | 144 | FunDef* make_fun_def(int lineno, string name, Type* ret_type, VarTypes* params, 145 | VarTypes* locals, Stmt* body); 146 | void print_fun_def(FunDef*); 147 | void print_fun_def_depth(FunDef*, int); 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /cons_list.h: -------------------------------------------------------------------------------- 1 | #ifndef CONS_LIST_H 2 | #define CONS_LIST_H 3 | 4 | template 5 | struct Cons { 6 | T curr; 7 | Cons* next; 8 | Cons(T e, Cons* n) : curr(e), next(n) { } 9 | }; 10 | 11 | template 12 | Cons* cons(const T& x, Cons* ls) { 13 | return new Cons(x, ls); 14 | } 15 | 16 | template 17 | unsigned int length(Cons* ls) { 18 | if (ls) { 19 | return 1 + length(ls->next); 20 | } else { 21 | return 0; 22 | } 23 | } 24 | 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /examples/addrof1.fc: -------------------------------------------------------------------------------- 1 | fun main() int { 2 | int x; 3 | int* p; 4 | p = &x; 5 | *p = 0; 6 | return *p; 7 | } -------------------------------------------------------------------------------- /examples/fun-recur.fc: -------------------------------------------------------------------------------- 1 | fun f(int x) int { 2 | if (x == 0) 3 | return x; 4 | else 5 | return f(x - 1); 6 | } 7 | 8 | fun main() int { 9 | return f(2); 10 | } 11 | -------------------------------------------------------------------------------- /examples/fun1.fc: -------------------------------------------------------------------------------- 1 | fun f(int x) int { 2 | return x - 1; 3 | } 4 | 5 | fun main() int { 6 | return f(1); 7 | } -------------------------------------------------------------------------------- /examples/fun2.fc: -------------------------------------------------------------------------------- 1 | // This tests the call-by-value aspect of parameter passing. 2 | // This makes sure that when the value in `x` dies, 3 | // it does not cause the value in `a` to also die. 4 | 5 | fun f(int x) int { 6 | return 0; 7 | } 8 | 9 | fun main() int { 10 | int a; int b; 11 | a = 0; 12 | f(a); 13 | b = a; 14 | return b; 15 | } -------------------------------------------------------------------------------- /examples/funptr1.fc: -------------------------------------------------------------------------------- 1 | fun add1(int x) int { 2 | return x + 1; 3 | } 4 | 5 | fun main() int { 6 | fun(int)int f; 7 | f = add1; 8 | return f(-1); 9 | } 10 | -------------------------------------------------------------------------------- /examples/goto1.fc: -------------------------------------------------------------------------------- 1 | fun main() int 2 | { 3 | int x; 4 | start: 5 | x = 1; 6 | loop: 7 | if (x == 0) { 8 | goto end; 9 | } else { 10 | x = x - 1; 11 | goto loop; 12 | } 13 | end: 14 | return x; 15 | } 16 | -------------------------------------------------------------------------------- /examples/goto2.fc: -------------------------------------------------------------------------------- 1 | fun f(int* p) bool 2 | { 3 | *p = *p - 1; 4 | return true; 5 | } 6 | 7 | fun main() int 8 | { 9 | int* p; 10 | p = malloc(int); 11 | *p = 1; 12 | if (f(p)) { 13 | goto weird; 14 | } else { 15 | *p = 2; 16 | weird: 17 | return *p; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/malloc1.fc: -------------------------------------------------------------------------------- 1 | fun main() int { 2 | int* p; 3 | p = malloc(int); 4 | *p = 0; 5 | return *p; 6 | } -------------------------------------------------------------------------------- /examples/undef1.fc: -------------------------------------------------------------------------------- 1 | // The behavior of this program is undefined because it tries to 2 | // access a local variable after its lifetime is over. 3 | 4 | fun f() int* { 5 | int x; 6 | x = 0; 7 | return &x; 8 | } 9 | 10 | fun main() int { 11 | int* p; 12 | p = f(); 13 | return *p; 14 | } 15 | -------------------------------------------------------------------------------- /examples/undef2.fc: -------------------------------------------------------------------------------- 1 | // Use-after-free. 2 | 3 | fun main() int { 4 | int* p; 5 | p = malloc(int); 6 | *p = 0; 7 | free(p); 8 | return *p; 9 | } 10 | -------------------------------------------------------------------------------- /examples/zero.fc: -------------------------------------------------------------------------------- 1 | fun main () int { 2 | return 0; 3 | } -------------------------------------------------------------------------------- /interp.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "interp.h" 5 | #include "assoc_list.h" 6 | #include "cons_list.h" 7 | #include "typecheck.h" // for print_error_string 8 | 9 | using std::vector; 10 | using std::map; 11 | using std::cout; 12 | using std::cerr; 13 | using std::endl; 14 | 15 | typedef unsigned int address; 16 | 17 | /***** Values *****/ 18 | 19 | struct Value { 20 | TypeKind tag; 21 | bool alive; 22 | union { 23 | int integer; 24 | bool boolean; 25 | FunDef* fun; 26 | address ptr; 27 | } u; 28 | }; 29 | 30 | void check_alive(Value* v) { 31 | if (! v->alive) { 32 | cerr << "undefined: access to dead value" << endl; 33 | exit(-1); 34 | } 35 | } 36 | 37 | Value* make_int_val(int i) { 38 | Value* v = new Value(); 39 | v->alive = true; 40 | v->tag = IntT; v->alive = true; v->u.integer = i; 41 | return v; 42 | } 43 | 44 | Value* make_bool_val(bool b) { 45 | Value* v = new Value(); 46 | v->alive = true; 47 | v->tag = BoolT; v->alive = true; v->u.boolean = b; 48 | return v; 49 | } 50 | 51 | Value* make_fun_val(FunDef* f) { 52 | Value* v = new Value(); 53 | v->alive = true; 54 | v->tag = FunT; v->alive = true; v->u.fun = f; 55 | return v; 56 | } 57 | 58 | Value* make_ptr_val(address addr) { 59 | Value* v = new Value(); 60 | v->alive = true; 61 | v->tag = PtrT; v->alive = true; v->u.ptr = addr; 62 | return v; 63 | } 64 | 65 | Value* copy_val(Value* val) { 66 | check_alive(val); 67 | switch (val->tag) { 68 | case IntT: 69 | return make_int_val(val->u.integer); 70 | case BoolT: 71 | return make_bool_val(val->u.boolean); 72 | case FunT: 73 | return make_fun_val(val->u.fun); 74 | case PtrT: 75 | return make_ptr_val(val->u.ptr); 76 | } 77 | } 78 | 79 | void print_val(Value* val) { 80 | if (! val->alive) { 81 | cout << "X"; 82 | return; 83 | } 84 | switch (val->tag) { 85 | case IntT: 86 | cout << val->u.integer; 87 | break; 88 | case BoolT: 89 | cout << val->u.boolean; 90 | break; 91 | case FunT: 92 | cout << "fun<" << val->u.fun->name << ">"; 93 | break; 94 | case PtrT: 95 | cout << "ptr<" << val->u.ptr << ">"; 96 | break; 97 | } 98 | } 99 | 100 | /***** Actions *****/ 101 | 102 | enum ActKind { LValAct, ExpAct, StmtAct, ValAct }; 103 | 104 | struct Act { 105 | ActKind tag; 106 | union { 107 | Exp* exp; // exp is for LValAct and ExpAct 108 | Stmt* stmt; 109 | Value* val; // val is for finished actions with a value (ValAct) 110 | } u; 111 | int pos; // position or state of the action 112 | vector results; // results from subexpression 113 | }; 114 | typedef Cons* ActList; 115 | 116 | void print_act(Act* act) { 117 | switch (act->tag) { 118 | case LValAct: 119 | case ExpAct: 120 | print_exp(act->u.exp); 121 | break; 122 | case StmtAct: 123 | print_stmt(act->u.stmt, 1); 124 | break; 125 | case ValAct: 126 | print_val(act->u.val); 127 | break; 128 | } 129 | cout << "[" << act->pos << "]"; 130 | if (act->results.size() > 0) { 131 | cout << "("; 132 | for (auto iter = act->results.begin(); iter != act->results.end(); ++iter) { 133 | if (*iter) 134 | print_val(*iter); 135 | cout << ","; 136 | } 137 | cout << ")"; 138 | } 139 | } 140 | 141 | void print_act_list(Cons* ls) { 142 | if (ls) { 143 | print_act(ls->curr); 144 | if (ls->next) { 145 | cout << " :: "; 146 | print_act_list(ls->next); 147 | } 148 | } 149 | } 150 | 151 | Act* make_exp_act(Exp* e) { 152 | Act* act = new Act(); 153 | act->tag = ExpAct; 154 | act->u.exp = e; 155 | act->pos = -1; 156 | return act; 157 | } 158 | 159 | Act* make_lval_act(Exp* e) { 160 | Act* act = new Act(); 161 | act->tag = LValAct; 162 | act->u.exp = e; 163 | act->pos = -1; 164 | return act; 165 | } 166 | 167 | Act* make_stmt_act(Stmt* s) { 168 | Act* act = new Act(); 169 | act->tag = StmtAct; 170 | act->u.stmt = s; 171 | act->pos = -1; 172 | return act; 173 | } 174 | 175 | Act* make_val_act(Value* v) { 176 | Act* act = new Act(); 177 | act->tag = ValAct; 178 | act->u.val = v; 179 | act->pos = -1; 180 | return act; 181 | } 182 | 183 | /***** Frames and State *****/ 184 | 185 | typedef AList Env; 186 | 187 | // { C, E, F } 188 | struct Frame { 189 | Cons* todo; 190 | Env* env; 191 | FunDef* fun; 192 | Frame(FunDef* f, Env* e, Cons* c) : fun(f), env(e), todo(c) { } 193 | }; 194 | 195 | void print_frame(Frame* frame) { 196 | if (frame->fun) 197 | cout << frame->fun->name; 198 | cout << "{"; 199 | print_act_list(frame->todo); 200 | cout << "}"; 201 | } 202 | 203 | // { S, H } 204 | struct State { 205 | Cons* stack; 206 | vector heap; 207 | list* funs; 208 | }; 209 | 210 | void print_stack(Cons* ls) { 211 | if (ls) { 212 | print_frame(ls->curr); 213 | if (ls->next) { 214 | cout << " :: "; 215 | print_stack(ls->next); 216 | } 217 | } 218 | } 219 | 220 | void print_heap(vector& heap) { 221 | for (auto iter = heap.begin(); iter != heap.end(); ++iter) { 222 | if (*iter) { 223 | print_val(*iter); 224 | } else { 225 | cout << "_"; 226 | } 227 | cout << ", "; 228 | } 229 | } 230 | 231 | void print_state(State* state) { 232 | cout << "{" << endl; 233 | cout << "stack: "; 234 | print_stack(state->stack); 235 | cout << endl << "heap: "; 236 | print_heap(state->heap); 237 | cout << endl << "}" << endl; 238 | } 239 | 240 | /***** Auxilliary Functions *****/ 241 | 242 | 243 | int val_to_int(Value* v) { 244 | check_alive(v); 245 | switch (v->tag) { 246 | case IntT: 247 | return v->u.integer; 248 | default: 249 | cerr << "runtime type error: expected an integer" << endl; 250 | exit(-1); 251 | } 252 | } 253 | 254 | int val_to_bool(Value* v) { 255 | check_alive(v); 256 | switch (v->tag) { 257 | case BoolT: 258 | return v->u.boolean; 259 | default: 260 | cerr << "runtime type error: expected a Boolean" << endl; 261 | exit(-1); 262 | } 263 | } 264 | 265 | address val_to_ptr(Value* v) { 266 | check_alive(v); 267 | switch (v->tag) { 268 | case PtrT: 269 | return v->u.ptr; 270 | default: 271 | cerr << "runtime type error: expected a pointer" << endl; 272 | exit(-1); 273 | } 274 | } 275 | 276 | FunDef* val_to_fun(Value* v) { 277 | check_alive(v); 278 | switch (v->tag) { 279 | case FunT: 280 | return v->u.fun; 281 | default: 282 | cerr << "runtime type error: expected a function" << endl; 283 | exit(-1); 284 | } 285 | } 286 | 287 | bool val_equal(Value* v1, Value* v2) { 288 | check_alive(v1); 289 | check_alive(v2); 290 | return (v1->tag == IntT && v2->tag == IntT && v1->u.integer == v2->u.integer) 291 | || (v1->tag == BoolT && v2->tag == BoolT && v1->u.boolean == v2->u.boolean) 292 | || (v1->tag == PtrT && v2->tag == PtrT && v1->u.ptr == v2->u.ptr) 293 | || (v1->tag == FunT && v2->tag == FunT && v1->u.fun == v2->u.fun); 294 | } 295 | 296 | Value* eval_prim(Operator op, const vector& args) { 297 | switch (op) { 298 | case Neg: 299 | return make_int_val(- val_to_int(args[0])); 300 | case Add: 301 | return make_int_val(val_to_int(args[0]) + val_to_int(args[1])); 302 | case Sub: 303 | return make_int_val(val_to_int(args[0]) - val_to_int(args[1])); 304 | case Not: 305 | return make_bool_val(- val_to_bool(args[0])); 306 | case And: 307 | return make_bool_val(val_to_bool(args[0]) && val_to_bool(args[1])); 308 | case Or: 309 | return make_bool_val(val_to_bool(args[0]) || val_to_bool(args[1])); 310 | case Eq: 311 | return make_bool_val(val_equal(args[0], args[1])); 312 | } 313 | } 314 | 315 | Env* global_functions; 316 | 317 | void init_global_functions(State* state) { 318 | global_functions = 0; 319 | for (auto iter = state->funs->begin(); iter != state->funs->end(); ++iter) { 320 | address a = state->heap.size(); 321 | state->heap.push_back(make_fun_val(*iter)); 322 | global_functions = new Env((*iter)->name, a, global_functions); 323 | } 324 | } 325 | 326 | // { S, H} -> { { C, E, F} :: S, H} 327 | // where C is the body of the function, 328 | // E is the environment (functions + parameters + locals) 329 | // F is the function 330 | void call_function(vector operas, State* state) { 331 | FunDef* f = val_to_fun(operas[0]); 332 | Env* env = global_functions; 333 | 334 | // Bind arguments to parameters 335 | int pos = 1; 336 | for (auto vt = f->params->begin(); vt != f->params->end(); ++vt, ++pos) { 337 | address a = state->heap.size(); 338 | state->heap.push_back(copy_val(operas[pos])); 339 | env = new Env((*vt).first, a, env); 340 | } 341 | 342 | // Allocate the local variables for the function 343 | for (auto l = f->locals->begin(); l != f->locals->end(); ++l) { 344 | address a = state->heap.size(); 345 | Value* v = make_int_val(0); 346 | v->alive = false; 347 | state->heap.push_back(v); 348 | env = new Env((*l).first, a, env); 349 | } 350 | 351 | // Create the new frame and push it on the stack 352 | 353 | Frame* frame = new Frame(f, env, cons(make_stmt_act(f->body), 354 | (Cons*)0)); 355 | state->stack = cons(frame, state->stack); 356 | } 357 | 358 | Cons* goto_label(string label, Stmt* stmt, Cons* todo) { 359 | switch (stmt->tag) { 360 | case Label: { 361 | if (*(stmt->u.labeled.label) == label) { 362 | return cons(make_stmt_act(stmt->u.labeled.stmt), todo); 363 | } else { 364 | return goto_label(label, stmt->u.labeled.stmt, todo); 365 | } 366 | } 367 | case Seq: { 368 | Cons* first_todo = goto_label(label, stmt->u.seq.stmt, 369 | cons(make_stmt_act(stmt->u.seq.next), todo)); 370 | if (first_todo) { 371 | return first_todo; 372 | } else { 373 | return goto_label(label, stmt->u.seq.next, todo); 374 | } 375 | } 376 | case If: { 377 | Cons* thn_todo = goto_label(label, stmt->u.if_stmt.thn, todo); 378 | if (thn_todo) { 379 | return thn_todo; 380 | } else { 381 | return goto_label(label, stmt->u.if_stmt.els, todo); 382 | } 383 | } 384 | default: 385 | cerr << "runtime error: jump to undefined label" << endl; 386 | return 0; 387 | } // switch (stmt->tag) 388 | } 389 | 390 | 391 | void kill_params_and_locals(State* state, Frame* frame) { 392 | for (auto iter = frame->fun->params->begin(); 393 | iter != frame->fun->params->end(); ++iter) { 394 | address a = lookup(frame->env, (*iter).first, print_error_string); 395 | state->heap[a]->alive = false; 396 | } 397 | for (auto iter = frame->fun->locals->begin(); 398 | iter != frame->fun->locals->end(); ++iter) { 399 | address a = lookup(frame->env, (*iter).first, print_error_string); 400 | state->heap[a]->alive = false; 401 | } 402 | } 403 | 404 | /***** State transition for handling a value *****/ 405 | 406 | void handle_value(State* state) { 407 | Frame* frame = state->stack->curr; 408 | Act* val_act = frame->todo->curr; 409 | Act* act = frame->todo->next->curr; 410 | 411 | cout << "--- handle value "; print_val(val_act->u.val); 412 | cout << " with "; print_act(act); cout << " --->" << endl; 413 | 414 | act->results.push_back(val_act->u.val); 415 | act->pos++; 416 | 417 | switch (act->tag) { 418 | case ExpAct: { 419 | Exp* exp = act->u.exp; 420 | switch (exp->tag) { 421 | case Deref: { 422 | // { {a :: *[] :: C, E, F} :: S, H} 423 | // -> { {H(a) :: C, E, F} :: S, H} 424 | address a = val_to_ptr(val_act->u.val); 425 | Value* v = state->heap[a]; 426 | if (! v->alive) { 427 | cerr << "undefined: access to dead memory" << endl; 428 | exit(-1); 429 | } 430 | frame->todo = cons(make_val_act(v), 431 | frame->todo->next->next); 432 | break; 433 | } 434 | case PrimOp: { 435 | if (act->pos != exp->u.prim_op.args->size()) { 436 | // { {v :: op(vs,[],e,es) :: C, E, F} :: S, H} 437 | // -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H} 438 | Exp* arg = (*exp->u.prim_op.args)[act->pos]; 439 | frame->todo = cons(make_exp_act(arg), frame->todo->next); 440 | } else { 441 | // { {v :: op(vs,[]) :: C, E, F} :: S, H} 442 | // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H} 443 | Value* v = eval_prim(exp->u.prim_op.op, act->results); 444 | frame->todo = cons(make_val_act(v), frame->todo->next->next); 445 | } 446 | break; 447 | } 448 | case Call: { 449 | if (act->pos == 1 + exp->u.call.args->size()) { 450 | // { { v :: f(vs,[]) :: C, E, F} :: S, H} 451 | // -> { {C',E',F'} :: {C, E, F} :: S, H} 452 | frame->todo = frame->todo->next->next; 453 | call_function(act->results, state); 454 | } else { 455 | // { { v :: f(vs,[],e,es)) :: C, E, F} :: S, H} 456 | // -> { { e :: f(vs,v,[],es)) :: C, E, F} :: S, H} 457 | frame->todo = cons(make_exp_act((*exp->u.call.args)[act->pos - 1]), 458 | frame->todo->next); 459 | } 460 | break; 461 | } 462 | default: 463 | cerr << "bad expression context in handle_value" << endl; 464 | } 465 | break; 466 | } 467 | case StmtAct: { 468 | Stmt* stmt = act->u.stmt; 469 | switch (stmt->tag) { 470 | case ExpStmt: 471 | frame->todo = frame->todo->next->next; 472 | break; 473 | case Assign: 474 | if (act->pos == 1) { 475 | // { { a :: ([] = e) :: C, E, F} :: S, H} 476 | // -> { { e :: (a = []) :: C, E, F} :: S, H} 477 | frame->todo = cons(make_exp_act(stmt->u.assign.rhs), 478 | frame->todo->next); 479 | } else if (act->pos == 2) { 480 | // { { v :: (a = []) :: C, E, F} :: S, H} 481 | // -> { { C, E, F} :: S, H(a := v)} 482 | Value* a = act->results[0]; 483 | Value* v = act->results[1]; 484 | state->heap[val_to_ptr(a)] = v; // copy? -Jeremy 485 | frame->todo = frame->todo->next->next; 486 | } 487 | break; 488 | case If: 489 | if (val_to_bool(act->results[0])) { 490 | // { {true :: if ([]) thn else els :: C, E, F} :: S, H} 491 | // -> { { thn :: C, E, F } :: S, H} 492 | frame->todo = cons(make_stmt_act(stmt->u.if_stmt.thn), 493 | frame->todo->next->next); 494 | } else { 495 | // { {false :: if ([]) thn else els :: C, E, F} :: S, H} 496 | // -> { { els :: C, E, F } :: S, H} 497 | frame->todo = cons(make_stmt_act(stmt->u.if_stmt.els), 498 | frame->todo->next->next); 499 | } 500 | break; 501 | case Return: { 502 | // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} 503 | // -> { {v :: C', E', F'} :: S, H} 504 | Value* ret_val = copy_val(val_act->u.val); 505 | kill_params_and_locals(state, frame); 506 | state->stack = state->stack->next; 507 | frame = state->stack->curr; 508 | frame->todo = cons(make_val_act(ret_val), frame->todo); 509 | break; 510 | } 511 | case Free: { 512 | // { {a :: free [] :: C, E, F} :: S, H} 513 | // -> { { C, E, F} :: S, H - {a}} 514 | address a = val_to_ptr(val_act->u.val); 515 | state->heap[a]->alive = false; 516 | frame->todo = frame->todo->next->next; 517 | break; 518 | } 519 | default: 520 | cerr << "unhandled statement "; 521 | print_stmt(stmt, 1); 522 | cerr << endl; 523 | exit(-1); 524 | } // switch stmt 525 | break; 526 | } 527 | default: 528 | cerr << "bad context in handle_value" << endl; 529 | } // switch act 530 | } 531 | 532 | /***** state transitions for lvalues *****/ 533 | 534 | void step_lvalue(State* state) { 535 | Frame* frame = state->stack->curr; 536 | Act* act = frame->todo->curr; 537 | Exp* exp = act->u.exp; 538 | cout << "--- step lvalue "; print_exp(exp); cout << " --->" << endl; 539 | switch (exp->tag) { 540 | case Var: { 541 | // { {x :: C, E, F} :: S, H} -> { {E(x) :: C, E, F} :: S, H} 542 | address a = lookup(frame->env, *(exp->u.var), print_error_string); 543 | Value* v = make_ptr_val(a); 544 | check_alive(v); 545 | frame->todo = cons(make_val_act(v), frame->todo->next); 546 | break; 547 | } 548 | case Deref: { 549 | // { {*e :: C, E, F} :: S, H} -> { e :: C, E, F} :: S, H} 550 | // Note: we do not push *[] because it's not needed in lvalue context. 551 | frame->todo = cons(make_exp_act(exp->u.deref), frame->todo->next); 552 | act->pos++; 553 | break; 554 | } 555 | default: 556 | cerr << "error, not an lvalue" << endl; 557 | } 558 | } 559 | 560 | /***** state transitions for expressions *****/ 561 | 562 | void step_exp(State* state) { 563 | Frame* frame = state->stack->curr; 564 | Act* act = frame->todo->curr; 565 | Exp* exp = act->u.exp; 566 | cout << "--- step exp "; print_exp(exp); cout << " --->" << endl; 567 | switch (exp->tag) { 568 | case Var: { 569 | // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} 570 | address a = lookup(frame->env, *(exp->u.var), print_error_string); 571 | Value* v = state->heap[a]; 572 | frame->todo = cons(make_val_act(v), frame->todo->next); 573 | break; 574 | } 575 | case Deref: { 576 | // { {*e :: C, E, F} :: S, H} -> { { e :: *[] :: C, E, F} :: S, H} 577 | frame->todo = cons(make_exp_act(exp->u.deref), frame->todo); 578 | act->pos++; 579 | break; 580 | } 581 | case Int: 582 | // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} 583 | frame->todo = cons(make_val_act(make_int_val(exp->u.integer)), 584 | frame->todo->next); 585 | break; 586 | case Bool: 587 | // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} 588 | frame->todo = cons(make_val_act(make_bool_val(exp->u.boolean)), 589 | frame->todo->next); 590 | break; 591 | case AddrOf: 592 | // { {&e :: C, E, F} :: S, H} -> { {e :: C, E, F} :: S, H} 593 | // Note: we do not push &[] because it's the identity. 594 | frame->todo = cons(make_lval_act(exp->u.addr_of), 595 | frame->todo->next); 596 | act->pos++; 597 | break; 598 | case Malloc: { 599 | // { { malloc(T) :: C, E, F} :: S, H} 600 | // -> { { a :: C, E, F} :: S, H(a = _) } 601 | address a = state->heap.size(); 602 | Value* v = make_int_val(-1); 603 | v->alive = false; 604 | state->heap.push_back(v); 605 | frame->todo = cons(make_val_act(make_ptr_val(a)), frame->todo->next); 606 | break; 607 | } 608 | case PrimOp: 609 | // { {op(e :: es) :: C, E, F} :: S, H} 610 | // -> { e :: op([] :: es) :: C, E, F} :: S, H} 611 | frame->todo = cons(make_exp_act(exp->u.prim_op.args->front()), 612 | frame->todo); 613 | act->pos++; 614 | break; 615 | case Call: 616 | // { {e(es) :: C, E, F} :: S, H} 617 | // -> { {e :: [](es) :: C, E, F} :: S, H} 618 | frame->todo = cons(make_exp_act(exp->u.call.fun), 619 | frame->todo); 620 | act->pos++; 621 | break; 622 | } 623 | } 624 | 625 | /***** state transitions for statements *****/ 626 | 627 | void step_stmt(State* state) { 628 | Frame* frame = state->stack->curr; 629 | Act* act = frame->todo->curr; 630 | Stmt* stmt = act->u.stmt; 631 | cout << "--- step stmt "; print_stmt(stmt, 1); cout << " --->" << endl; 632 | switch (stmt->tag) { 633 | case ExpStmt: 634 | // { {e :: C, E, F} :: S, H} 635 | // -> { {e :: C, E, F} :: S, H} 636 | frame->todo = cons(make_exp_act(stmt->u.exp), frame->todo); 637 | break; 638 | case Assign: 639 | // { {(lv = e) :: C, E, F} :: S, H} 640 | // -> { {lv :: ([] = e) :: C, E, F} :: S, H} 641 | frame->todo = cons(make_lval_act(stmt->u.assign.lhs), 642 | frame->todo); 643 | act->pos++; 644 | break; 645 | case Free: 646 | // { {free e :: C, E, F} :: S, H} 647 | // -> { e :: free [] :: C, E, F} :: S, H} 648 | frame->todo = cons(make_exp_act(stmt->u.free), frame->todo); 649 | act->pos++; 650 | break; 651 | case If: 652 | // { {(if (e) thn else els) :: C, E, F} :: S, H} 653 | // -> { { e :: (if ([]) thn else els) :: C, E, F} :: S, H} 654 | frame->todo = cons(make_exp_act(stmt->u.if_stmt.cond), frame->todo); 655 | act->pos++; 656 | break; 657 | case Goto: 658 | // { { goto l :: C, E, F} :: S, H} -> { { goto_label(l), E, F } :: S, H} 659 | frame->todo = goto_label(* (stmt->u.goto_stmt.target), frame->fun->body, 0); 660 | break; 661 | case Label: 662 | // { {(l: s) :: C, E, F} :: S, H} 663 | // -> { {s :: C, E, F} :: S, H} 664 | frame->todo = cons(make_stmt_act(stmt->u.labeled.stmt), frame->todo->next); 665 | break; 666 | case Return: 667 | // { {return e :: C, E, F} :: S, H} 668 | // -> { {e :: return [] :: C, E, F} :: S, H} 669 | frame->todo = cons(make_exp_act(stmt->u.ret), frame->todo); 670 | act->pos++; 671 | break; 672 | case Seq: 673 | // { { (s1,s2) :: C, E, F} :: S, H} 674 | // -> { { s1 :: s2 :: C, E, F} :: S, H} 675 | frame->todo = cons(make_stmt_act(stmt->u.seq.stmt), 676 | cons(make_stmt_act(stmt->u.seq.next), 677 | frame->todo->next)); 678 | break; 679 | } 680 | } 681 | 682 | /***** state transition *****/ 683 | 684 | void step(State* state) { 685 | Frame* frame = state->stack->curr; 686 | if (! frame->todo) { 687 | cerr << "runtime error: fell off end of function " << frame->fun->name 688 | << " without `return`" << endl; 689 | exit(-1); 690 | } 691 | 692 | Act* act = frame->todo->curr; 693 | switch (act->tag) { 694 | case ValAct: 695 | handle_value(state); 696 | break; 697 | case LValAct: 698 | step_lvalue(state); 699 | break; 700 | case ExpAct: 701 | step_exp(state); 702 | break; 703 | case StmtAct: 704 | step_stmt(state); 705 | break; 706 | } // switch 707 | } 708 | 709 | /***** interpret the whole program *****/ 710 | 711 | int interp_program(list* fs) { 712 | State* state = new State(); 713 | state->funs = fs; 714 | 715 | init_global_functions(state); 716 | 717 | Exp* call_main = make_call(0, make_var(0, "main"), new list()); 718 | Cons* todo = cons(make_exp_act(call_main), (Cons*)0); 719 | Frame* frame = new Frame(0, global_functions, todo); 720 | state->stack = cons(frame, (Cons*)0); 721 | 722 | print_state(state); 723 | // run the program 724 | while (length(state->stack) > 1 725 | || length(state->stack->curr->todo) > 1 726 | || state->stack->curr->todo->curr->tag != ValAct) { 727 | step(state); 728 | print_state(state); 729 | } 730 | Value* v = state->stack->curr->todo->curr->u.val; 731 | return val_to_int(v); 732 | } 733 | -------------------------------------------------------------------------------- /interp.h: -------------------------------------------------------------------------------- 1 | #include "ast.h" 2 | 3 | int interp_program(list* fs); 4 | -------------------------------------------------------------------------------- /syntax.l: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include "ast.h" 4 | #include "syntax.tab.h" 5 | %} 6 | %option yylineno 7 | 8 | PLUS "+" 9 | ASTR "*" 10 | MINUS "-" 11 | COLON ":" 12 | SEMICOLON ";" 13 | COMMA "," 14 | LP "(" 15 | RP ")" 16 | LC "{" 17 | RC "}" 18 | INT [0-9]+ 19 | ID [A-Za-z_][A-Za-z0-9_]* 20 | AND "&&" 21 | AMP "&" 22 | OR "||" 23 | BANG "!" 24 | EQUAL "==" 25 | ASSGN "=" 26 | COMMENT \/\/[^\n]*\n 27 | RETURN "return" 28 | GOTO "goto" 29 | IF "if" 30 | ELSE "else" 31 | MALLOC "malloc" 32 | FREE "free" 33 | INTTY "int" 34 | BOOLTY "bool" 35 | FUN "fun" 36 | TRUE "true" 37 | FALSE "false" 38 | %% 39 | {AND} { return AND; } 40 | {AMP} { return AMP; } 41 | {OR} { return OR; } 42 | {BANG} { return BANG; } 43 | {IF} { return IF; } 44 | {ELSE} { return ELSE; } 45 | {INTTY} { return INTTY; } 46 | {BOOLTY} { return BOOLTY; } 47 | {FUN} { return FUN; } 48 | {COLON} { return COLON; } 49 | {SEMICOLON} { return SEMICOLON; } 50 | {COMMA} { return COMMA; } 51 | {PLUS} { return PLUS; } 52 | {ASTR} { return ASTR; } 53 | {MINUS} { return MINUS; } 54 | {EQUAL} { return EQUAL; } 55 | {ASSGN} { return ASSGN; } 56 | {LP} { return LP; } 57 | {RP} { return RP; } 58 | {LC} { return LC; } 59 | {RC} { return RC; } 60 | {RETURN} { return RETURN; } 61 | {GOTO} { return GOTO; } 62 | {FREE} { return FREE; } 63 | {MALLOC} { return MALLOC; } 64 | {TRUE} { return TRUE; } 65 | {FALSE} { return FALSE; } 66 | {ID} { 67 | int n = strlen(yytext); 68 | yylval.str = (char*)malloc((n + 1) * sizeof(char)); 69 | strncpy(yylval.str, yytext, n + 1); 70 | return ID; 71 | } 72 | {INT} {yylval.num = atof(yytext); return INT;} 73 | [ \t\n]+ ; 74 | {COMMENT} ; 75 | . {return yytext[0];} 76 | %% 77 | int yywrap() {return 1;} 78 | 79 | -------------------------------------------------------------------------------- /syntax.y: -------------------------------------------------------------------------------- 1 | %{ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "ast.h" 7 | #include "typecheck.h" 8 | #include "interp.h" 9 | 10 | extern FILE* yyin; 11 | extern int yylineno; 12 | char* input_filename; 13 | 14 | void yyerror(char*s) { 15 | fprintf(stderr, "%s:%d: %s\n", input_filename, yylineno, s); 16 | exit(-1); 17 | } 18 | // void yyerror(char *s, ...); 19 | 20 | extern int yylex(); 21 | extern int yywrap(); 22 | 23 | //#include "typecheck.h" 24 | //#include "eval.h" 25 | #include 26 | #include "ast.h" 27 | using std::list; 28 | using std::pair; 29 | using std::make_pair; 30 | using std::cout; 31 | using std::endl; 32 | 33 | static list program; 34 | %} 35 | %union 36 | { 37 | char* str; 38 | int num; 39 | Type* type; 40 | list* type_list; 41 | Exp* lvalue; 42 | Exp* expr; 43 | list* expr_list; 44 | VarTypes* var_decls; 45 | VarTypes* params; 46 | Stmt* stmt; 47 | Stmt* stmt_list; 48 | FunDef* fun_def; 49 | list* fun_def_list; 50 | }; 51 | 52 | %token INT 53 | %token ID 54 | %type fun_def 55 | %type fun_def_list 56 | %type stmt 57 | %type stmt_list 58 | %type lvalue 59 | %type expr 60 | %type expr_list 61 | %type type 62 | %type type_list 63 | %type var_decls 64 | %type params 65 | %token NOT 66 | %token AND 67 | %token OR 68 | %token INTTY 69 | %token BOOLTY 70 | %token FUN 71 | %token COLON 72 | %token SEMICOLON 73 | %token COMMA 74 | %token LP 75 | %token RP 76 | %token LC 77 | %token RC 78 | %token PLUS 79 | %token ASTR 80 | %token AMP 81 | %token MINUS 82 | %token DIV 83 | %token EQUAL 84 | %token BANG 85 | %token IF 86 | %token ELSE 87 | %token GOTO 88 | %token FREE 89 | %token RETURN 90 | %token TRUE 91 | %token FALSE 92 | %token MALLOC 93 | %left BAR 94 | %nonassoc IF ELSE 95 | %nonassoc LP RP 96 | %nonassoc LS RS LT GT 97 | %nonassoc ASSGN 98 | %nonassoc EQUAL 99 | %left AND OR 100 | %left PLUS MINUS 101 | %nonassoc NOT ASTR AMP 102 | %start input 103 | %locations 104 | %% 105 | input: 106 | fun_def_list { 107 | printf("program:\n"); 108 | print_list($1, print_fun_def, ""); 109 | TypeEnv* top = top_level($1); 110 | for (auto i = $1->begin(); i != $1->end(); ++i) { 111 | typecheck_fun_def(*i, top); 112 | } 113 | printf("\n"); 114 | printf("type checking complete\n"); 115 | int result = interp_program($1); 116 | cout << "result: " << result << endl; 117 | } 118 | ; 119 | params: 120 | /* empty */ { $$ = new VarTypes(); } 121 | | type ID { $$ = new VarTypes; $$->push_front(make_pair($2, $1)); } 122 | | type ID COMMA params { $$ = $4; $$->push_front(make_pair($2, $1)); } 123 | ; 124 | var_decls: 125 | /* empty */ { $$ = new VarTypes(); } 126 | | type ID SEMICOLON var_decls { $$ = $4; $$->push_front(make_pair($2, $1)); } 127 | ; 128 | type_list: 129 | /* empty */ { $$ = new list(); } 130 | | type { $$ = new list(); $$->push_front($1); } 131 | | type COMMA type_list { $$ = $3; $$->push_front($1); } 132 | ; 133 | type: 134 | INTTY { $$ = make_int_type(yylineno); } 135 | | BOOLTY { $$ = make_bool_type(yylineno); } 136 | | FUN LP type_list RP type { $$ = make_fun_type(yylineno, $3, $5); } 137 | | LP type RP { $$ = $2; } 138 | | type ASTR { $$ = make_ptr_type(yylineno, $1); } 139 | ; 140 | lvalue: 141 | ID { $$ = make_var(yylineno, $1); } 142 | | ASTR expr { $$ = make_deref(yylineno, $2); } 143 | ; 144 | expr: 145 | lvalue { $$ = $1; } 146 | | INT { $$ = make_int(yylineno, $1); } 147 | | TRUE { $$ = make_bool(yylineno, true); } 148 | | FALSE { $$ = make_bool(yylineno, false); } 149 | | expr EQUAL expr { $$ = make_binop(yylineno, Eq, $1, $3); } 150 | | expr PLUS expr { $$ = make_binop(yylineno, Add, $1, $3); } 151 | | expr MINUS expr { $$ = make_binop(yylineno, Sub, $1, $3); } 152 | | expr AND expr { $$ = make_binop(yylineno, And, $1, $3); } 153 | | expr OR expr { $$ = make_binop(yylineno, Or, $1, $3); } 154 | | NOT expr { $$ = make_unop(yylineno, Not, $2); } 155 | | MINUS expr { $$ = make_unop(yylineno, Neg, $2); } 156 | | MALLOC LP type RP { $$ = make_malloc(yylineno, $3); } 157 | | AMP expr { $$ = make_addr_of(yylineno, $2); } 158 | | LP expr RP { $$ = $2; } 159 | | expr LP expr_list RP { $$ = make_call(yylineno, $1, $3); } 160 | ; 161 | expr_list: 162 | /* empty */ { $$ = new list(); } 163 | | expr { $$ = new list(); $$->push_front($1); } 164 | | expr COMMA expr_list { $$ = $3; $$->push_front($1); } 165 | ; 166 | stmt: 167 | lvalue ASSGN expr SEMICOLON { $$ = make_assign(yylineno, $1, $3); } 168 | | expr SEMICOLON { $$ = make_exp_stmt(yylineno, $1); } 169 | | FREE LP expr RP SEMICOLON { $$ = make_free(yylineno, $3); } 170 | | GOTO ID SEMICOLON { $$ = make_goto(yylineno, $2); } 171 | | IF LP expr RP stmt ELSE stmt { $$ = make_if(yylineno, $3, $5, $7); } 172 | | ID COLON stmt { $$ = make_labeled(yylineno, $1, $3); } 173 | | RETURN expr SEMICOLON { $$ = make_return(yylineno, $2); } 174 | | LC stmt_list RC { $$ = $2; } 175 | ; 176 | stmt_list : 177 | stmt { $$ = $1; } 178 | | stmt stmt_list { $$ = make_seq(yylineno, $1, $2); } 179 | ; 180 | fun_def: 181 | FUN ID LP params RP type LC var_decls stmt_list RC 182 | { $$ = make_fun_def(yylineno, $2, $6, $4, $8, $9); } 183 | ; 184 | fun_def_list: 185 | /* empty */ { $$ = new list(); } 186 | | fun_def fun_def_list { $$ = $2; $$->push_front($1); } 187 | ; 188 | %% 189 | int main(int argc, char* argv[]) { 190 | /*yydebug = 1;*/ 191 | 192 | if (argc > 1) { 193 | input_filename = argv[1]; 194 | yyin = fopen(argv[1], "r"); 195 | } 196 | if (argc > 2) { 197 | FILE* program = fopen(argv[2], "r"); 198 | input = read_file(program); 199 | } 200 | yyparse(); 201 | return 0; 202 | } 203 | 204 | /* 205 | void yyerror(char *s, ...) 206 | { 207 | va_list ap; 208 | va_start(ap, s); 209 | 210 | if(yylloc.first_line) { 211 | fprintf(stderr, "%d.%d-%d.%d: error: ", 212 | yylloc.first_line, yylloc.first_column, 213 | yylloc.last_line, yylloc.last_column); 214 | } 215 | vfprintf(stderr, s, ap); 216 | fprintf(stderr, "\n"); 217 | } 218 | 219 | void lyyerror(YYLTYPE t, char *s, ...) 220 | { 221 | va_list ap; 222 | va_start(ap, s); 223 | 224 | if(t.first_line) { 225 | fprintf(stderr, "%d.%d-%d.%d: error: ", 226 | t.first_line, t.first_column, 227 | t.last_line, t.last_column); 228 | } 229 | vfprintf(stderr, s, ap); 230 | fprintf(stderr, "\n"); 231 | } 232 | */ 233 | -------------------------------------------------------------------------------- /typecheck.cc: -------------------------------------------------------------------------------- 1 | #include "typecheck.h" 2 | #include 3 | #include 4 | #include 5 | using std::vector; 6 | using std::set; 7 | using std::cerr; 8 | using std::endl; 9 | 10 | template 11 | bool list_equal(list* ts1, list* ts2, bool(*eq)(T*,T*)) { 12 | if (ts1->size() == ts2->size()) { 13 | auto iter2 = ts2->begin(); 14 | for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1, ++iter2) { 15 | if (! eq(*iter1, *iter2)) 16 | return false; 17 | } 18 | return true; 19 | } else { 20 | return false; 21 | } 22 | } 23 | 24 | bool type_equal(Type* t1, Type* t2) { 25 | return (t1->tag == IntT && t2->tag == IntT) 26 | || (t1->tag == BoolT && t2->tag == BoolT) 27 | || (t1->tag == PtrT && t2->tag == PtrT 28 | && type_equal(t1->u.ptr.type, t2->u.ptr.type)) 29 | || (t1->tag == FunT && t2->tag == FunT 30 | && type_equal(t1->u.fun.ret, t2->u.fun.ret) 31 | && list_equal(t1->u.fun.params, t2->u.fun.params, type_equal)); 32 | } 33 | 34 | void expect_type(string context, Type* expected, Type* actual) { 35 | if (! type_equal(expected, actual)) { 36 | cerr << "in " << context << ", expected type "; 37 | print_type(expected); 38 | cerr << " but got type "; 39 | print_type(actual); 40 | cerr << endl; 41 | exit(-1); 42 | } 43 | } 44 | 45 | void print_error_string(string s) { 46 | cerr << s; 47 | } 48 | 49 | Type* typecheck_exp(Exp* e, TypeEnv* env) { 50 | switch (e->tag) { 51 | case Var: 52 | return lookup(env, *(e->u.var), print_error_string); 53 | case Deref: { 54 | Type* t = typecheck_exp(e->u.deref, env); 55 | switch (t->tag) { 56 | case PtrT: 57 | return t->u.ptr.type; 58 | default: 59 | cerr << "type error, expected a pointer in dereference" << endl; 60 | exit(-1); 61 | break; 62 | } 63 | break; 64 | } 65 | case Int: 66 | return make_int_type(e->lineno); 67 | break; 68 | case Bool: 69 | return make_bool_type(e->lineno); 70 | break; 71 | case AddrOf: 72 | return make_ptr_type(e->lineno, typecheck_exp(e->u.addr_of, env)); 73 | case PrimOp: { 74 | vector ts; 75 | for (auto iter = e->u.prim_op.args->begin(); 76 | iter != e->u.prim_op.args->end(); ++iter) { 77 | ts.push_back(typecheck_exp(*iter, env)); 78 | } 79 | switch (e->u.prim_op.op) { 80 | case Neg: 81 | expect_type("negation", make_int_type(e->lineno), ts[0]); 82 | return make_int_type(e->lineno); 83 | case Add: 84 | case Sub: 85 | expect_type("subtraction(1)", make_int_type(e->lineno), ts[0]); 86 | expect_type("substration(2)", make_int_type(e->lineno), ts[1]); 87 | return make_int_type(e->lineno); 88 | case And: 89 | expect_type("&&(1)", make_bool_type(e->lineno), ts[0]); 90 | expect_type("&&(2)", make_bool_type(e->lineno), ts[1]); 91 | return make_bool_type(e->lineno); 92 | case Or: 93 | expect_type("||(1)", make_bool_type(e->lineno), ts[0]); 94 | expect_type("||(2)", make_bool_type(e->lineno), ts[1]); 95 | return make_bool_type(e->lineno); 96 | case Not: 97 | expect_type("!", make_bool_type(e->lineno), ts[0]); 98 | return make_bool_type(e->lineno); 99 | case Eq: 100 | expect_type("==(1)", make_int_type(e->lineno), ts[0]); 101 | expect_type("==(2)", make_int_type(e->lineno), ts[1]); 102 | return make_bool_type(e->lineno); 103 | } 104 | break; 105 | } 106 | case Call: { 107 | Type* funT = typecheck_exp(e->u.call.fun, env); 108 | if (funT->tag != FunT) { 109 | cerr << "error, expected a function in function call" << endl; 110 | exit(-1); 111 | } 112 | if (e->u.call.args->size() != funT->u.fun.params->size()) { 113 | cerr << "error, wrong number of arguments in function call" << endl; 114 | exit(-1); 115 | } 116 | auto param_iter = funT->u.fun.params->begin(); 117 | for (auto arg_iter = e->u.call.args->begin(); 118 | arg_iter != e->u.call.args->end(); ++arg_iter, ++param_iter) { 119 | expect_type("call", *param_iter, typecheck_exp(*arg_iter, env)); 120 | } 121 | return funT->u.fun.ret; 122 | break; 123 | } 124 | case Malloc: 125 | return make_ptr_type(e->lineno, e->u.malloc); 126 | } 127 | } 128 | 129 | void typecheck_stmt(Stmt* s, TypeEnv* env, Type* ret_type, 130 | set& labels) { 131 | switch (s->tag) { 132 | case Seq: { 133 | typecheck_stmt(s->u.seq.stmt, env, ret_type, labels); 134 | typecheck_stmt(s->u.seq.next, env, ret_type, labels); 135 | break; 136 | } 137 | case Assign: { 138 | Type* lhsT = typecheck_exp(s->u.assign.lhs, env); 139 | Type* rhsT = typecheck_exp(s->u.assign.rhs, env); 140 | expect_type("assign", lhsT, rhsT); 141 | break; 142 | } 143 | case ExpStmt: { 144 | typecheck_exp(s->u.exp, env); 145 | break; 146 | } 147 | case Free: { 148 | switch (typecheck_exp(s->u.free, env)->tag) { 149 | case PtrT: 150 | break; 151 | default: 152 | cerr << "error, free expects a pointer" << endl; 153 | exit(-1); 154 | } 155 | break; 156 | } 157 | case If: 158 | expect_type("condition of `if`", make_bool_type(s->lineno), 159 | typecheck_exp(s->u.if_stmt.cond, env)); 160 | typecheck_stmt(s->u.if_stmt.thn, env, ret_type, labels); 161 | typecheck_stmt(s->u.if_stmt.els, env, ret_type, labels); 162 | break; 163 | case Goto: 164 | // to do: check the label 165 | break; 166 | case Label: 167 | if (labels.count(*s->u.labeled.label) > 0) { 168 | cerr << "error, duplicate label " << *(s->u.labeled.label) << endl; 169 | exit(-1); 170 | } 171 | labels.insert(*s->u.labeled.label); 172 | typecheck_stmt(s->u.labeled.stmt, env, ret_type, labels); 173 | break; 174 | case Return: 175 | expect_type("return", ret_type, typecheck_exp(s->u.ret, env)); 176 | break; 177 | } 178 | } 179 | 180 | void typecheck_fun_def(FunDef* f, TypeEnv* env) { 181 | for (auto i = f->params->begin(); i != f->params->end(); ++i) { 182 | env = new TypeEnv(i->first, i->second, env); 183 | } 184 | for (auto i = f->locals->begin(); i != f->locals->end(); ++i) { 185 | env = new TypeEnv(i->first, i->second, env); 186 | } 187 | if (f->name == "main") { 188 | expect_type("return type of `main`", 189 | make_int_type(f->lineno), f->return_type); 190 | if (f->params->size() != 0) { 191 | cerr << "error, main function may not have any parameters" << endl; 192 | exit(-1); 193 | } 194 | } 195 | set labels; 196 | typecheck_stmt(f->body, env, f->return_type, labels); 197 | } 198 | 199 | TypeEnv* top_level(list* fs) { 200 | TypeEnv* top = 0; 201 | bool found_main = false; 202 | for (auto i = fs->begin(); i != fs->end(); ++i) { 203 | list* ps = new list(); 204 | for (auto pt = (*i)->params->begin(); pt != (*i)->params->end(); ++pt) { 205 | ps->push_back(pt->second); 206 | } 207 | top = new TypeEnv((*i)->name, 208 | make_fun_type((*i)->lineno, ps, (*i)->return_type), 209 | top); 210 | if ((*i)->name == "main") { 211 | found_main = true; 212 | } 213 | } 214 | if (found_main == false) 215 | cerr << "error, program must contain a function named `main`" << endl; 216 | return top; 217 | } 218 | -------------------------------------------------------------------------------- /typecheck.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPECHECK_H 2 | #define TYPECHECK_H 3 | 4 | #include "ast.h" 5 | #include "assoc_list.h" 6 | 7 | typedef AList TypeEnv; 8 | 9 | Type* typecheck_exp(Exp*, TypeEnv*); 10 | void typecheck_stmt(Stmt*, TypeEnv*, Type*); 11 | void typecheck_fun_def(FunDef*, TypeEnv*); 12 | TypeEnv* top_level(list* fs); 13 | void print_error_string(string s); 14 | 15 | #endif 16 | --------------------------------------------------------------------------------