├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── array_variable.cpp ├── array_variable.h ├── char_variable.cpp ├── char_variable.h ├── error.h ├── function.cpp ├── function.h ├── function_variable.cpp ├── function_variable.h ├── int_variable.cpp ├── int_variable.h ├── library_adaptor.h ├── main.cpp ├── quixey.cpp ├── quixey.h ├── runtime_error.h ├── script_types.h ├── string_util.h ├── string_variable.cpp ├── string_variable.h ├── syntax_error.h ├── test ├── import.inc ├── input │ ├── test1.txt │ ├── test2.txt │ ├── test3.txt │ ├── test4.txt │ ├── test5.txt │ ├── test6.txt │ ├── test7.txt │ └── test8.txt ├── output │ ├── test1.txt │ ├── test2.txt │ ├── test3.txt │ ├── test4.txt │ ├── test5.txt │ ├── test6.txt │ ├── test7.txt │ └── test8.txt ├── runtests.sh ├── test1.qc ├── test2.qc ├── test3.qc ├── test4.qc ├── test5.qc ├── test6.qc ├── test7.qc └── test8.qc ├── token.cpp ├── token.h ├── variable.cpp ├── variable.h └── variable_base.h /.gitignore: -------------------------------------------------------------------------------- 1 | .obj 2 | quixey 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.0) 2 | 3 | project(quixey) 4 | 5 | 6 | set(quixey_SOURCES 7 | array_variable.cpp 8 | array_variable.h 9 | char_variable.cpp 10 | char_variable.h 11 | error.h 12 | function.cpp 13 | function.h 14 | function_variable.cpp 15 | function_variable.h 16 | int_variable.cpp 17 | int_variable.h 18 | library_adaptor.h 19 | main.cpp 20 | quixey.cpp 21 | quixey.h 22 | runtime_error.h 23 | script_types.h 24 | string_util.h 25 | string_variable.cpp 26 | string_variable.h 27 | syntax_error.h 28 | token.cpp 29 | token.h 30 | variable_base.h 31 | variable.cpp 32 | variable.h 33 | ) 34 | 35 | add_executable(quixey ${quixey_SOURCES}) 36 | 37 | set_property(TARGET quixey PROPERTY CXX_STANDARD 11) 38 | 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -W -Wall -Wmissing-field-initializers -Wunused -Wshadow") 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Quixey 3 | ====== 4 | 5 | Quixey is a C-ish scripting language where I toy around with a few ideas that I 6 | have found interesting over the years. It inherits a many things from C, such 7 | as the scoping rules, most operators, and general curly brace syntax. However, 8 | unlike C, it has a native `string` type, ranged `for`, lambdas, an `auto` type, 9 | and much more. 10 | 11 | I don't intend for this language to be taken particularly seriously, but it 12 | was fun to develop. 13 | 14 | An interesting property of the implementation of the `auto` keyword is that 15 | since it is defined as assuming the type of the first assignment to it, is 16 | that it can have a different type in different scopes... But it can never 17 | change types in a given scope. For example: 18 | 19 | auto foo(int x) { 20 | if(x) { 21 | return "Hello"; 22 | } else { 23 | return 42; 24 | } 25 | } 26 | 27 | // since this function returns the auto type, we must capture the result 28 | // in an auto type as well. 29 | auto n1 = foo(1); // n1 == "Hello" and is of type string 30 | auto n2 = foo(0); // n2 == 42 and is of type int 31 | 32 | // Now we can use is_integer and is_string to decide what to do with the 33 | // values as needed. 34 | 35 | Aditionally, for similar reasons, the following is perfectly legal: 36 | 37 | auto n; 38 | if(func()) { 39 | n = "A String!"; 40 | } else { 41 | n = 123; 42 | } 43 | 44 | // at this point n is one of the two possible types depending on the result 45 | // of func() 46 | 47 | Of course for "poor mans templates", you can use `auto` for function parameters 48 | as well. 49 | 50 | auto func(auto n) { 51 | // do something with n depending on the type! 52 | } 53 | 54 | However, you cannot change the type once it is set. So unlike weakly typed 55 | languages. **The following is not allowed in quixey**. 56 | 57 | auto x = 1; 58 | x = 'A'; // ERROR: cannot change type! 59 | 60 | 61 | ## Supported Escape Sequences: 62 | * `\'` : Single quote 63 | * `\"` : Double quote 64 | * `\\` : Backslash 65 | * `\a` : Audible bell 66 | * `\b` : Backspace 67 | * `\f` : Formfeed 68 | * `\n` : Newline 69 | * `\r` : Carriage return 70 | * `\t` : Horizontal tab 71 | * `\v` : Vertical tab 72 | * `\xnnn` : Hexadecimal number (nnn) 73 | * `\nnn` : number (nnn) 74 | 75 | **NOTE**: in octal and hex decimal escape sequences, there is no limit on the number 76 | of digits but the least significant digits will be used (as happens in gcc). 77 | So for example `'\x12345678'` is functionally the same as `'\x78'` 78 | 79 | ## Supported Operators: 80 | 81 | * `/=` : divide left by right and assign to left 82 | * `/` : divide left by right 83 | * `&=` : binary AND left and right and assign to left 84 | * `&&` : logical AND left and right 85 | * `&` : binary AND left and right 86 | * `|=` : binary OR left and right and assign to left 87 | * `||` : logical OR left and right 88 | * `|` : binary OR left and right 89 | * `^=` : XOR left and right and assign to left 90 | * `^` : XOR left and right 91 | * `~` : compliment unary operand 92 | * `==` : test if left equals right 93 | * `=` : assign right to left 94 | * `!=` : test if left does not equal right 95 | * `!` : logical NOT of unary operand 96 | * `+=` : add left and right and assign to left 97 | * `+` : add left and right 98 | * `-=` : subtract right from left and assign to left 99 | * `-` : subtract right from left 100 | * `*=` : multiply left and right and assign to left 101 | * `*` : multiply left and right 102 | * `%=` : modulo divide left by right and assign to left 103 | * `%` : modulo divide left by right 104 | * `>>=` : right shift left by right and assign to left 105 | * `>>` : right shift left by right 106 | * `>=` : test if left is greater than or equal to right 107 | * `>` : test if left is greater than right 108 | * `<<=` : left shift left by right and assign to left 109 | * `<<` : left shift left by right 110 | * `<=` : test if left is less than or equal to right 111 | * `<` : test if left is less than right 112 | 113 | ## Supported Types: 114 | 115 | * `char` 116 | * `int` 117 | * `string` 118 | * `auto` (assumes the type of the first thing assigned to it) 119 | 120 | **NOTE**: modifiers such as unsigned are not supported 121 | 122 | ## Supported Keywords: 123 | 124 | **NOTE**: unlike C/C++ keywords like `if`, `else`, `for`, `do`, `while` **require** the 125 | curly braces, they are not optional 126 | 127 | `if`, `else`: 128 | 129 | if(x) { } 130 | if(x) { } else { } 131 | if(x) { } else if(y) { } 132 | if(x) { } else if(y) { } else { } 133 | 134 | `for`: 135 | 136 | **NOTE**: you may declare a variable in the initialization part of the 137 | for loop, this variable only exists inside the loop. Just like in C++. 138 | 139 | **NOTE**: the for each style syntax requires that you declare a variable as the 140 | initializer. 141 | 142 | for(i = 0; i < 10; i += 1) { } 143 | for(int i = 0; i < 10; i += 1) { } 144 | for(auto e : a) {} 145 | 146 | `do`: 147 | 148 | do { } while(x); 149 | 150 | `while`: 151 | 152 | while(x) { } 153 | 154 | `return`: 155 | 156 | **NOTE**: Every function has a return type. If the end of a function is reached with no return, then an implicit 157 | `return 0;` is executed at function exit. 158 | 159 | `int`, `char`, `string`. `auto` 160 | 161 | int x = 5; 162 | char y; 163 | string s = "hello"; 164 | char ch = s[3]; 165 | auto a1 = "hello"; 166 | auto a2 = 5; 167 | auto a3 = 'C'; 168 | auto a4 = a2; 169 | 170 | ## Arrays 171 | 172 | Arrays are heterogeneous, and are created via array literals so far: 173 | 174 | auto x = ['a', 'b', 'c', 1234, "HELLO", [1, 2, 3, 4], function() { printf("W00t!\n"); }]; 175 | 176 | See `test4.qc` for some advanced usage of them such as type deduction. 177 | 178 | ## Lambdas 179 | 180 | lamdas are supported, and can only be assigned to a variable of type `auto`: 181 | 182 | auto f = function() { 183 | puts("hello world"); 184 | }; 185 | 186 | f(); // prints "hello world" 187 | 188 | lambdas also, always have the return type of `auto`. 189 | 190 | 191 | ## Built-in Functions 192 | * `int size(auto x); // returns the length of an array or string` 193 | * `int puts(string s); // just like C's puts` 194 | * `int getch(); // gets 1 character from stdin` 195 | * `int getnum(); // gets an integer from stdin` 196 | * `int putchar(char ch); // puts a character to stdout` 197 | * `int is_integer(auto x); // returns non-zero if the parameter is an integer` 198 | * `int is_character(auto x); // returns non-zero if the parameter is a character` 199 | * `int is_string(auto x); // returns non-zero if the parameter is a string` 200 | * `int is_function(auto x); // returns non-zero if the parameter is an function or lambda` 201 | * `int is_array(auto x); // returns non-zero if the parameter is an array` 202 | * `int printf(string s, ...); // just like C's printf, an experiement in supporting variadic functions (may not stick around)` 203 | -------------------------------------------------------------------------------- /array_variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "array_variable.h" 3 | #include "function.h" 4 | #include "error.h" 5 | #include 6 | 7 | variable_base::pointer array_variable::clone() const { 8 | return std::make_shared(*this); 9 | } 10 | 11 | variable_base &array_variable::operator=(const variable_base &rhs) { 12 | value_ = rhs.to_array(); 13 | return *this; 14 | } 15 | 16 | variable_base &array_variable::operator+=(const variable_base &rhs) { 17 | 18 | std::vector other = rhs.to_array(); 19 | value_.insert(value_.end(), other.begin(), other.end()); 20 | return *this; 21 | } 22 | 23 | variable_base &array_variable::operator-=(const variable_base &rhs) { 24 | (void)rhs; 25 | throw invalid_operation_for_type(); 26 | } 27 | 28 | variable_base &array_variable::operator*=(const variable_base &rhs) { 29 | (void)rhs; 30 | throw invalid_operation_for_type(); 31 | } 32 | 33 | variable_base &array_variable::operator/=(const variable_base &rhs) { 34 | (void)rhs; 35 | throw invalid_operation_for_type(); 36 | } 37 | 38 | variable_base &array_variable::operator&=(const variable_base &rhs) { 39 | (void)rhs; 40 | throw invalid_operation_for_type(); 41 | } 42 | 43 | variable_base &array_variable::operator|=(const variable_base &rhs) { 44 | (void)rhs; 45 | throw invalid_operation_for_type(); 46 | } 47 | 48 | variable_base &array_variable::operator^=(const variable_base &rhs) { 49 | (void)rhs; 50 | throw invalid_operation_for_type(); 51 | } 52 | 53 | variable_base &array_variable::operator%=(const variable_base &rhs) { 54 | (void)rhs; 55 | throw invalid_operation_for_type(); 56 | } 57 | 58 | variable_base &array_variable::operator>>=(const variable_base &rhs) { 59 | (void)rhs; 60 | throw invalid_operation_for_type(); 61 | } 62 | 63 | variable_base &array_variable::operator<<=(const variable_base &rhs) { 64 | (void)rhs; 65 | throw invalid_operation_for_type(); 66 | } 67 | 68 | variable_base::pointer array_variable::operator+() const { 69 | throw invalid_operation_for_type(); 70 | } 71 | 72 | variable_base::pointer array_variable::operator-() const { 73 | throw invalid_operation_for_type(); 74 | } 75 | 76 | variable_base::pointer array_variable::operator!() const { 77 | throw invalid_operation_for_type(); 78 | } 79 | 80 | variable_base::pointer array_variable::operator~() const { 81 | throw invalid_operation_for_type(); 82 | } 83 | 84 | int array_variable::size() const { 85 | return value_.size(); 86 | } 87 | 88 | char array_variable::to_character() const { 89 | throw invalid_operation_for_type(); 90 | } 91 | 92 | int array_variable::to_integer() const { 93 | throw invalid_operation_for_type(); 94 | } 95 | 96 | std::string array_variable::to_string() const { 97 | throw invalid_operation_for_type(); 98 | } 99 | 100 | function array_variable::to_function() const { 101 | throw invalid_operation_for_type(); 102 | } 103 | 104 | std::vector array_variable::to_array() const { 105 | return value_; 106 | } 107 | 108 | int array_variable::compare(const variable_base &rhs) const { 109 | (void)rhs; 110 | throw invalid_operation_for_type(); 111 | } 112 | 113 | variable_base::pointer array_variable::operator[](const variable_base &index) const { 114 | size_t n = index.to_integer(); 115 | if(n >= value_.size()) { 116 | throw out_of_bounds(); 117 | } 118 | 119 | return value_[n]; 120 | } 121 | 122 | function array_variable::operator()() const { 123 | throw invalid_operation_for_type(); 124 | } 125 | -------------------------------------------------------------------------------- /array_variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ARRAY_VARIABLE_10012011_H_ 3 | #define ARRAY_VARIABLE_10012011_H_ 4 | 5 | #include "variable_base.h" 6 | #include 7 | 8 | class array_variable : public variable_base { 9 | public: 10 | 11 | array_variable() { 12 | } 13 | 14 | explicit array_variable(const std::vector &value) : value_(value) { 15 | } 16 | 17 | public: 18 | virtual pointer clone() const; 19 | 20 | public: 21 | virtual variable_base &operator=(const variable_base &rhs); 22 | virtual variable_base &operator+=(const variable_base &rhs); 23 | virtual variable_base &operator-=(const variable_base &rhs); 24 | virtual variable_base &operator*=(const variable_base &rhs); 25 | virtual variable_base &operator/=(const variable_base &rhs); 26 | virtual variable_base &operator&=(const variable_base &rhs); 27 | virtual variable_base &operator|=(const variable_base &rhs); 28 | virtual variable_base &operator^=(const variable_base &rhs); 29 | virtual variable_base &operator%=(const variable_base &rhs); 30 | virtual variable_base &operator>>=(const variable_base &rhs); 31 | virtual variable_base &operator<<=(const variable_base &rhs); 32 | 33 | public: 34 | virtual pointer operator[](const variable_base &index) const; 35 | 36 | public: 37 | virtual int size() const; 38 | 39 | public: 40 | virtual pointer operator+() const; 41 | virtual pointer operator-() const; 42 | virtual pointer operator!() const; 43 | virtual pointer operator~() const; 44 | 45 | public: 46 | virtual function operator()() const; 47 | 48 | public: 49 | int compare(const variable_base &rhs) const; 50 | 51 | public: 52 | virtual char to_character() const; 53 | virtual int to_integer() const; 54 | virtual std::string to_string() const; 55 | virtual function to_function() const; 56 | virtual std::vector to_array() const; 57 | 58 | public: 59 | virtual bool is_character() const { return false; } 60 | virtual bool is_integer() const { return false; } 61 | virtual bool is_string() const { return false; } 62 | virtual bool is_function() const { return false; } 63 | virtual bool is_array() const { return true; } 64 | 65 | private: 66 | std::vector value_; 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /char_variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "char_variable.h" 3 | #include "function.h" 4 | #include "error.h" 5 | #include 6 | 7 | variable_base::pointer char_variable::clone() const { 8 | return std::make_shared(*this); 9 | } 10 | 11 | variable_base &char_variable::operator=(const variable_base &rhs) { 12 | value_ = rhs.to_integer(); 13 | return *this; 14 | } 15 | 16 | variable_base &char_variable::operator+=(const variable_base &rhs) { 17 | value_ += rhs.to_integer(); 18 | return *this; 19 | } 20 | 21 | variable_base &char_variable::operator-=(const variable_base &rhs) { 22 | value_ -= rhs.to_integer(); 23 | return *this; 24 | } 25 | 26 | variable_base &char_variable::operator*=(const variable_base &rhs) { 27 | value_ *= rhs.to_integer(); 28 | return *this; 29 | } 30 | 31 | variable_base &char_variable::operator/=(const variable_base &rhs) { 32 | value_ /= rhs.to_integer(); 33 | return *this; 34 | } 35 | 36 | variable_base &char_variable::operator&=(const variable_base &rhs) { 37 | value_ &= rhs.to_integer(); 38 | return *this; 39 | } 40 | 41 | variable_base &char_variable::operator|=(const variable_base &rhs) { 42 | value_ |= rhs.to_integer(); 43 | return *this; 44 | } 45 | 46 | variable_base &char_variable::operator^=(const variable_base &rhs) { 47 | value_ ^= rhs.to_integer(); 48 | return *this; 49 | } 50 | 51 | variable_base &char_variable::operator%=(const variable_base &rhs) { 52 | value_ %= rhs.to_integer(); 53 | return *this; 54 | } 55 | 56 | variable_base &char_variable::operator>>=(const variable_base &rhs) { 57 | value_ >>= rhs.to_integer(); 58 | return *this; 59 | } 60 | 61 | variable_base &char_variable::operator<<=(const variable_base &rhs) { 62 | value_ <<= rhs.to_integer(); 63 | return *this; 64 | } 65 | 66 | variable_base::pointer char_variable::operator+() const { 67 | return std::make_shared(+value_); 68 | } 69 | 70 | variable_base::pointer char_variable::operator-() const { 71 | return std::make_shared(-value_); 72 | } 73 | 74 | variable_base::pointer char_variable::operator!() const { 75 | return std::make_shared(!value_); 76 | } 77 | 78 | variable_base::pointer char_variable::operator~() const { 79 | return std::make_shared(~value_); 80 | } 81 | 82 | int char_variable::size() const { 83 | throw invalid_operation_for_type(); 84 | } 85 | 86 | char char_variable::to_character() const { 87 | return value_; 88 | } 89 | 90 | int char_variable::to_integer() const { 91 | return static_cast(value_); 92 | } 93 | 94 | std::string char_variable::to_string() const { 95 | return std::string(1, value_); 96 | } 97 | 98 | function char_variable::to_function() const { 99 | throw invalid_type_conversion(); 100 | } 101 | 102 | std::vector char_variable::to_array() const { 103 | throw invalid_type_conversion(); 104 | } 105 | 106 | int char_variable::compare(const variable_base &rhs) const { 107 | if(const char_variable *const x = dynamic_cast(&rhs)) { 108 | return value_ - x->value_; 109 | } 110 | throw invalid_type_conversion(); 111 | } 112 | 113 | variable_base::pointer char_variable::operator[](const variable_base &index) const { 114 | (void)index; 115 | throw invalid_operation_for_type(); 116 | } 117 | 118 | function char_variable::operator()() const { 119 | throw invalid_operation_for_type(); 120 | } 121 | -------------------------------------------------------------------------------- /char_variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CHAR_VARIABLE_10012011_H_ 3 | #define CHAR_VARIABLE_10012011_H_ 4 | 5 | #include "variable_base.h" 6 | 7 | class char_variable : public variable_base { 8 | public: 9 | explicit char_variable(char value) : value_(value) { 10 | } 11 | 12 | public: 13 | virtual pointer clone() const; 14 | 15 | public: 16 | virtual variable_base &operator=(const variable_base &rhs); 17 | virtual variable_base &operator+=(const variable_base &rhs); 18 | virtual variable_base &operator-=(const variable_base &rhs); 19 | virtual variable_base &operator*=(const variable_base &rhs); 20 | virtual variable_base &operator/=(const variable_base &rhs); 21 | virtual variable_base &operator&=(const variable_base &rhs); 22 | virtual variable_base &operator|=(const variable_base &rhs); 23 | virtual variable_base &operator^=(const variable_base &rhs); 24 | virtual variable_base &operator%=(const variable_base &rhs); 25 | virtual variable_base &operator>>=(const variable_base &rhs); 26 | virtual variable_base &operator<<=(const variable_base &rhs); 27 | 28 | public: 29 | virtual pointer operator[](const variable_base &index) const; 30 | 31 | public: 32 | virtual int size() const; 33 | 34 | public: 35 | virtual pointer operator+() const; 36 | virtual pointer operator-() const; 37 | virtual pointer operator!() const; 38 | virtual pointer operator~() const; 39 | 40 | public: 41 | virtual function operator()() const; 42 | 43 | public: 44 | int compare(const variable_base &rhs) const; 45 | 46 | public: 47 | virtual char to_character() const; 48 | virtual int to_integer() const; 49 | virtual std::string to_string() const; 50 | virtual function to_function() const; 51 | virtual std::vector to_array() const; 52 | 53 | public: 54 | virtual bool is_character() const { return true; } 55 | virtual bool is_integer() const { return false; } 56 | virtual bool is_string() const { return false; } 57 | virtual bool is_function() const { return false; } 58 | virtual bool is_array() const { return false; } 59 | 60 | private: 61 | char value_; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ERROR_20111021_H_ 3 | #define ERROR_20111021_H_ 4 | 5 | #include 6 | 7 | struct error : std::exception { 8 | virtual const char *what() const throw() { 9 | return "error"; 10 | } 11 | 12 | int line_number = -1; 13 | std::string filename; 14 | }; 15 | 16 | #include "syntax_error.h" 17 | #include "runtime_error.h" 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /function.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "function.h" 3 | #include "token.h" 4 | 5 | function::function() : type_(token::UNKNOWN), offset_(-1), param_count_(0) { 6 | } 7 | 8 | function::function(int type, address_t offset, unsigned int param_count) : type_(type), offset_(offset), param_count_(param_count) { 9 | } 10 | 11 | function::function(const std::string &name) : type_(token::UNKNOWN), offset_(-1), param_count_(0), name_(name) { 12 | } 13 | -------------------------------------------------------------------------------- /function.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FUNCTION_20060121_H_ 3 | #define FUNCTION_20060121_H_ 4 | 5 | #include "script_types.h" 6 | #include 7 | 8 | class function { 9 | public: 10 | function(); 11 | explicit function(const std::string &name); 12 | function(int type, address_t offset, unsigned int param_count); 13 | 14 | public: 15 | address_t offset() const { return offset_; } 16 | unsigned int param_count() const { return param_count_; } 17 | int type() const { return type_; } 18 | std::string name() const { return name_; } 19 | 20 | private: 21 | int type_; 22 | address_t offset_; 23 | unsigned int param_count_; 24 | std::string name_; 25 | }; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /function_variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "function_variable.h" 3 | #include "error.h" 4 | #include 5 | #include 6 | 7 | variable_base::pointer function_variable::clone() const { 8 | return std::make_shared(*this); 9 | } 10 | 11 | variable_base &function_variable::operator=(const variable_base &rhs) { 12 | value_ = rhs.to_function(); 13 | return *this; 14 | } 15 | 16 | variable_base &function_variable::operator+=(const variable_base &rhs) { 17 | (void)rhs; 18 | throw invalid_operation_for_type(); 19 | } 20 | 21 | variable_base &function_variable::operator-=(const variable_base &rhs) { 22 | (void)rhs; 23 | throw invalid_operation_for_type(); 24 | } 25 | 26 | variable_base &function_variable::operator*=(const variable_base &rhs) { 27 | (void)rhs; 28 | throw invalid_operation_for_type(); 29 | } 30 | 31 | variable_base &function_variable::operator/=(const variable_base &rhs) { 32 | (void)rhs; 33 | throw invalid_operation_for_type(); 34 | } 35 | 36 | variable_base &function_variable::operator&=(const variable_base &rhs) { 37 | (void)rhs; 38 | throw invalid_operation_for_type(); 39 | } 40 | 41 | variable_base &function_variable::operator|=(const variable_base &rhs) { 42 | (void)rhs; 43 | throw invalid_operation_for_type(); 44 | } 45 | 46 | variable_base &function_variable::operator^=(const variable_base &rhs) { 47 | (void)rhs; 48 | throw invalid_operation_for_type(); 49 | } 50 | 51 | variable_base &function_variable::operator%=(const variable_base &rhs) { 52 | (void)rhs; 53 | throw invalid_operation_for_type(); 54 | } 55 | 56 | variable_base &function_variable::operator>>=(const variable_base &rhs) { 57 | (void)rhs; 58 | throw invalid_operation_for_type(); 59 | } 60 | 61 | variable_base &function_variable::operator<<=(const variable_base &rhs) { 62 | (void)rhs; 63 | throw invalid_operation_for_type(); 64 | } 65 | 66 | variable_base::pointer function_variable::operator+() const { 67 | throw invalid_operation_for_type(); 68 | } 69 | 70 | variable_base::pointer function_variable::operator-() const { 71 | throw invalid_operation_for_type(); 72 | } 73 | 74 | variable_base::pointer function_variable::operator!() const { 75 | throw invalid_operation_for_type(); 76 | } 77 | 78 | variable_base::pointer function_variable::operator~() const { 79 | throw invalid_operation_for_type(); 80 | } 81 | 82 | int function_variable::size() const { 83 | throw invalid_operation_for_type(); 84 | } 85 | 86 | char function_variable::to_character() const { 87 | throw invalid_operation_for_type(); 88 | } 89 | 90 | int function_variable::to_integer() const { 91 | throw invalid_operation_for_type(); 92 | } 93 | 94 | std::string function_variable::to_string() const { 95 | throw invalid_operation_for_type(); 96 | } 97 | 98 | function function_variable::to_function() const { 99 | return value_; 100 | } 101 | 102 | std::vector function_variable::to_array() const { 103 | throw invalid_type_conversion(); 104 | } 105 | 106 | int function_variable::compare(const variable_base &rhs) const { 107 | (void)rhs; 108 | throw invalid_operation_for_type(); 109 | } 110 | 111 | variable_base::pointer function_variable::operator[](const variable_base &index) const { 112 | (void)index; 113 | throw invalid_operation_for_type(); 114 | } 115 | 116 | function function_variable::operator()() const { 117 | return value_; 118 | } 119 | -------------------------------------------------------------------------------- /function_variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FUNCTION_VARIABLE_10012011_H_ 3 | #define FUNCTION_VARIABLE_10012011_H_ 4 | 5 | #include "function.h" 6 | #include "variable_base.h" 7 | 8 | class function_variable : public variable_base { 9 | public: 10 | explicit function_variable(const function &value) : value_(value) { 11 | } 12 | 13 | public: 14 | virtual pointer clone() const; 15 | 16 | public: 17 | virtual variable_base &operator=(const variable_base &rhs); 18 | virtual variable_base &operator+=(const variable_base &rhs); 19 | virtual variable_base &operator-=(const variable_base &rhs); 20 | virtual variable_base &operator*=(const variable_base &rhs); 21 | virtual variable_base &operator/=(const variable_base &rhs); 22 | virtual variable_base &operator&=(const variable_base &rhs); 23 | virtual variable_base &operator|=(const variable_base &rhs); 24 | virtual variable_base &operator^=(const variable_base &rhs); 25 | virtual variable_base &operator%=(const variable_base &rhs); 26 | virtual variable_base &operator>>=(const variable_base &rhs); 27 | virtual variable_base &operator<<=(const variable_base &rhs); 28 | 29 | public: 30 | virtual pointer operator[](const variable_base &index) const; 31 | 32 | public: 33 | virtual int size() const; 34 | 35 | public: 36 | virtual pointer operator+() const; 37 | virtual pointer operator-() const; 38 | virtual pointer operator!() const; 39 | virtual pointer operator~() const; 40 | 41 | public: 42 | virtual function operator()() const; 43 | 44 | public: 45 | int compare(const variable_base &rhs) const; 46 | 47 | public: 48 | virtual char to_character() const; 49 | virtual int to_integer() const; 50 | virtual std::string to_string() const; 51 | virtual function to_function() const; 52 | virtual std::vector to_array() const; 53 | 54 | public: 55 | virtual bool is_character() const { return false; } 56 | virtual bool is_integer() const { return false; } 57 | virtual bool is_string() const { return false; } 58 | virtual bool is_function() const { return true; } 59 | virtual bool is_array() const { return false; } 60 | 61 | private: 62 | function value_; 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /int_variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "int_variable.h" 3 | #include "function.h" 4 | #include "error.h" 5 | #include 6 | 7 | variable_base::pointer int_variable::clone() const { 8 | return std::make_shared(*this); 9 | } 10 | 11 | variable_base &int_variable::operator=(const variable_base &rhs) { 12 | value_ = rhs.to_integer(); 13 | return *this; 14 | } 15 | 16 | variable_base &int_variable::operator+=(const variable_base &rhs) { 17 | value_ += rhs.to_integer(); 18 | return *this; 19 | } 20 | 21 | variable_base &int_variable::operator-=(const variable_base &rhs) { 22 | value_ -= rhs.to_integer(); 23 | return *this; 24 | } 25 | 26 | variable_base &int_variable::operator*=(const variable_base &rhs) { 27 | value_ *= rhs.to_integer(); 28 | return *this; 29 | } 30 | 31 | variable_base &int_variable::operator/=(const variable_base &rhs) { 32 | value_ /= rhs.to_integer(); 33 | return *this; 34 | } 35 | 36 | variable_base &int_variable::operator&=(const variable_base &rhs) { 37 | value_ &= rhs.to_integer(); 38 | return *this; 39 | } 40 | 41 | variable_base &int_variable::operator|=(const variable_base &rhs) { 42 | value_ |= rhs.to_integer(); 43 | return *this; 44 | } 45 | 46 | variable_base &int_variable::operator^=(const variable_base &rhs) { 47 | value_ ^= rhs.to_integer(); 48 | return *this; 49 | } 50 | 51 | variable_base &int_variable::operator%=(const variable_base &rhs) { 52 | value_ %= rhs.to_integer(); 53 | return *this; 54 | } 55 | 56 | variable_base &int_variable::operator>>=(const variable_base &rhs) { 57 | value_ >>= rhs.to_integer(); 58 | return *this; 59 | } 60 | 61 | variable_base &int_variable::operator<<=(const variable_base &rhs) { 62 | value_ <<= rhs.to_integer(); 63 | return *this; 64 | } 65 | 66 | variable_base::pointer int_variable::operator+() const { 67 | return std::make_shared(+value_); 68 | } 69 | 70 | variable_base::pointer int_variable::operator-() const { 71 | return std::make_shared(-value_); 72 | } 73 | 74 | variable_base::pointer int_variable::operator!() const { 75 | return std::make_shared(!value_); 76 | } 77 | 78 | variable_base::pointer int_variable::operator~() const { 79 | return std::make_shared(~value_); 80 | } 81 | 82 | int int_variable::size() const { 83 | throw invalid_operation_for_type(); 84 | } 85 | 86 | int int_variable::to_integer() const { 87 | return value_; 88 | } 89 | 90 | char int_variable::to_character() const { 91 | throw invalid_type_conversion(); 92 | } 93 | 94 | std::string int_variable::to_string() const { 95 | throw invalid_type_conversion(); 96 | } 97 | 98 | function int_variable::to_function() const { 99 | throw invalid_type_conversion(); 100 | } 101 | 102 | std::vector int_variable::to_array() const { 103 | throw invalid_type_conversion(); 104 | } 105 | 106 | int int_variable::compare(const variable_base &rhs) const { 107 | if(const int_variable *const x = dynamic_cast(&rhs)) { 108 | return value_ - x->value_; 109 | } 110 | throw invalid_type_conversion(); 111 | } 112 | 113 | variable_base::pointer int_variable::operator[](const variable_base &index) const { 114 | (void)index; 115 | throw invalid_operation_for_type(); 116 | } 117 | 118 | function int_variable::operator()() const { 119 | throw invalid_operation_for_type(); 120 | } 121 | -------------------------------------------------------------------------------- /int_variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INT_VARIABLE_10012011_H_ 3 | #define INT_VARIABLE_10012011_H_ 4 | 5 | #include "variable_base.h" 6 | 7 | class int_variable : public variable_base { 8 | public: 9 | explicit int_variable(int value) : value_(value) { 10 | } 11 | 12 | public: 13 | virtual pointer clone() const; 14 | 15 | public: 16 | virtual variable_base &operator=(const variable_base &rhs); 17 | virtual variable_base &operator+=(const variable_base &rhs); 18 | virtual variable_base &operator-=(const variable_base &rhs); 19 | virtual variable_base &operator*=(const variable_base &rhs); 20 | virtual variable_base &operator/=(const variable_base &rhs); 21 | virtual variable_base &operator&=(const variable_base &rhs); 22 | virtual variable_base &operator|=(const variable_base &rhs); 23 | virtual variable_base &operator^=(const variable_base &rhs); 24 | virtual variable_base &operator%=(const variable_base &rhs); 25 | virtual variable_base &operator>>=(const variable_base &rhs); 26 | virtual variable_base &operator<<=(const variable_base &rhs); 27 | 28 | public: 29 | virtual pointer operator[](const variable_base &index) const; 30 | 31 | public: 32 | virtual int size() const; 33 | 34 | public: 35 | virtual pointer operator+() const; 36 | virtual pointer operator-() const; 37 | virtual pointer operator!() const; 38 | virtual pointer operator~() const; 39 | 40 | public: 41 | virtual function operator()() const; 42 | 43 | public: 44 | int compare(const variable_base &rhs) const; 45 | 46 | public: 47 | virtual char to_character() const; 48 | virtual int to_integer() const; 49 | virtual std::string to_string() const; 50 | virtual function to_function() const; 51 | virtual std::vector to_array() const; 52 | 53 | public: 54 | virtual bool is_character() const { return false; } 55 | virtual bool is_integer() const { return true; } 56 | virtual bool is_string() const { return false; } 57 | virtual bool is_function() const { return false; } 58 | virtual bool is_array() const { return false; } 59 | 60 | private: 61 | int value_; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /library_adaptor.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LIBRARY_ADAPTOR_H_ 3 | #define LIBRARY_ADAPTOR_H_ 4 | 5 | #include "error.h" 6 | #include "quixey.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace detail { 13 | 14 | template 15 | struct call_impl { 16 | static int call(F f, Tuple && t) { 17 | return call_impl::call(f, std::forward(t)); 18 | } 19 | }; 20 | 21 | template 22 | struct call_impl { 23 | static int call(F f, Tuple && t) { 24 | return f(std::get(std::forward(t))...); 25 | } 26 | }; 27 | 28 | //----------------------------------------------------------------------------- 29 | // Name: call 30 | // Desc: calls f, with each element of the tuple t as arguments 31 | //----------------------------------------------------------------------------- 32 | template 33 | int call(F f, Tuple &&t) { 34 | using tuple_type = typename std::decay::type; 35 | return call_impl::value, std::tuple_size::value>::call(f, std::forward(t)); 36 | } 37 | 38 | //----------------------------------------------------------------------------- 39 | // Name: for_each 40 | // Desc: for_each, but for tuples (terminating case) 41 | //----------------------------------------------------------------------------- 42 | template 43 | typename std::enable_if::type for_each(std::tuple &, Func) { 44 | } 45 | 46 | //----------------------------------------------------------------------------- 47 | // Name: for_each 48 | // Desc: for_each, but for tuples (general case) 49 | //----------------------------------------------------------------------------- 50 | template 51 | typename std::enable_if::type for_each(std::tuple& t, Func f) { 52 | f(std::get(t)); 53 | for_each(t, f); 54 | } 55 | 56 | 57 | // helpers to fetch arguments more genrically 58 | template 59 | struct fetch_argument { 60 | }; 61 | 62 | //----------------------------------------------------------------------------- 63 | // Name: fetch_argument 64 | // Desc: 65 | //----------------------------------------------------------------------------- 66 | template <> 67 | struct fetch_argument { 68 | fetch_argument(quixey *engine) : engine_(engine) { 69 | assert(engine); 70 | } 71 | 72 | int operator()() const { 73 | variable ret; 74 | engine_->eval_exp(ret); 75 | return to_integer(ret); 76 | } 77 | private: 78 | quixey *const engine_; 79 | }; 80 | 81 | //----------------------------------------------------------------------------- 82 | // Name: fetch_argument 83 | // Desc: 84 | //----------------------------------------------------------------------------- 85 | template <> 86 | struct fetch_argument { 87 | fetch_argument(quixey *engine) : engine_(engine) { 88 | assert(engine); 89 | } 90 | 91 | char operator()() const { 92 | variable ret; 93 | engine_->eval_exp(ret); 94 | return static_cast(to_integer(ret)); 95 | } 96 | private: 97 | quixey *const engine_; 98 | }; 99 | 100 | //----------------------------------------------------------------------------- 101 | // Name: fetch_argument 102 | // Desc: 103 | //----------------------------------------------------------------------------- 104 | template <> 105 | struct fetch_argument { 106 | fetch_argument(quixey *engine) : engine_(engine) { 107 | assert(engine); 108 | } 109 | 110 | std::string operator()() const { 111 | variable ret; 112 | engine_->eval_exp(ret); 113 | return to_string(ret); 114 | } 115 | private: 116 | quixey *const engine_; 117 | }; 118 | 119 | //----------------------------------------------------------------------------- 120 | // Name: fetch_argument 121 | // Desc: 122 | //----------------------------------------------------------------------------- 123 | template <> 124 | struct fetch_argument { 125 | fetch_argument(quixey *engine) : engine_(engine) { 126 | assert(engine); 127 | } 128 | 129 | variable operator()() const { 130 | variable ret; 131 | engine_->eval_exp(ret); 132 | return ret; 133 | } 134 | private: 135 | quixey *const engine_; 136 | }; 137 | 138 | //----------------------------------------------------------------------------- 139 | // Name: fetch_argument 140 | // Desc: 141 | //----------------------------------------------------------------------------- 142 | template <> 143 | struct fetch_argument { 144 | fetch_argument(quixey *engine) : engine_(engine) { 145 | assert(engine); 146 | } 147 | 148 | quixey *operator()() const { 149 | return engine_; 150 | } 151 | private: 152 | quixey *const engine_; 153 | }; 154 | 155 | 156 | //----------------------------------------------------------------------------- 157 | // Name: fetch_and_test_token_x 158 | // Desc: read a token and make sure it's correct 159 | //----------------------------------------------------------------------------- 160 | template 161 | void fetch_and_test_token_x(quixey *engine, token::Type expected) { 162 | assert(engine); 163 | const token &tok = engine->get_token(); 164 | if(tok.type() != expected) { 165 | throw T(); 166 | } 167 | } 168 | 169 | //----------------------------------------------------------------------------- 170 | // Name: assignment_helper 171 | // Desc: reads a comma, then an argument of type T and assigns it to the argument 172 | // wish we had generic lambdas like C++14 :-P 173 | //----------------------------------------------------------------------------- 174 | struct assignment_helper { 175 | 176 | assignment_helper(quixey *engine) : engine_(engine) { 177 | } 178 | 179 | template 180 | void operator()(T &arg) const { 181 | // we want a comma, then an arg. 182 | // this is used to fetch arguments 2-N 183 | fetch_and_test_token_x(engine_, token::COMMA); 184 | arg = fetch_argument(engine_)(); 185 | } 186 | private: 187 | quixey *const engine_; 188 | }; 189 | 190 | //----------------------------------------------------------------------------- 191 | // Name: function_helper 192 | // Desc: handles 0 193 | //----------------------------------------------------------------------------- 194 | template 195 | struct function_helper; 196 | 197 | // no arguments 198 | template 199 | struct function_helper { 200 | explicit function_helper(F f) : f_(f) { 201 | } 202 | 203 | int operator()(quixey *engine) const { 204 | (void)engine; 205 | assert(engine); 206 | return f_(); 207 | } 208 | private: 209 | F f_; 210 | }; 211 | 212 | //----------------------------------------------------------------------------- 213 | // Name: function_helper 214 | // Desc: handles 1 or more arguments 215 | //----------------------------------------------------------------------------- 216 | template 217 | struct function_helper { 218 | public: 219 | explicit function_helper(F f) : f_(f) { 220 | } 221 | 222 | int operator()(quixey *engine) const { 223 | 224 | assert(engine); 225 | 226 | // get the first argument 227 | auto arg1 = std::make_tuple(fetch_argument(engine)()); 228 | 229 | // read the rest of the arguments 230 | std::tuple args; 231 | for_each(args, assignment_helper(engine)); 232 | 233 | // merge the two tuples into 1 and then call f_ 234 | // with the full tuple as arguments 235 | auto all_args = std::tuple_cat(arg1, args); 236 | return call(f_, all_args); 237 | } 238 | private: 239 | F f_; 240 | }; 241 | 242 | } 243 | 244 | //----------------------------------------------------------------------------- 245 | // Name: wrap_function 246 | // Desc: works for plain old function pointers 247 | //----------------------------------------------------------------------------- 248 | template 249 | std::function wrap_function(R(*f)(Args...)) { 250 | return detail::function_helper(f); 251 | } 252 | 253 | //----------------------------------------------------------------------------- 254 | // Name: wrap_function 255 | // Desc: works with std::function<> 256 | //----------------------------------------------------------------------------- 257 | template 258 | std::function wrap_function(std::function f) { 259 | return detail::function_helper(f); 260 | } 261 | 262 | #endif 263 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "quixey.h" 4 | #include "error.h" 5 | 6 | //----------------------------------------------------------------------------- 7 | // Name: main(int argc, char *argv[]) 8 | //----------------------------------------------------------------------------- 9 | int main(int argc, char *argv[]) { 10 | 11 | if(argc != 2) { 12 | std::cerr << "Usage: " << argv[0] << " " << std::endl; 13 | return 1; 14 | } 15 | 16 | quixey scripter; 17 | #if 0 18 | try { 19 | #endif 20 | // load the program to execute 21 | scripter.load_program(argv[1]); 22 | return scripter.exec("main"); 23 | #if 0 24 | } catch(const error &e) { 25 | std::cerr << "-------------------------------" << std::endl; 26 | std::cerr << "An Error Occurred: " << e.what() << std::endl; 27 | 28 | const token &tok = scripter.current_token(); 29 | 30 | if(e.line_number >= 0) { 31 | std::cerr << "On Line Number: " << e.line_number << std::endl; 32 | std::cerr << "In File : " << e.filename << std::endl; 33 | } else { 34 | std::cerr << "Current Token : " << to_string(tok) << std::endl; 35 | std::cerr << "On Line Number: " << tok.line_number << std::endl; 36 | std::cerr << "In File : " << tok.filename << std::endl; 37 | } 38 | std::cerr << "-------------------------------" << std::endl; 39 | } 40 | #endif 41 | } 42 | -------------------------------------------------------------------------------- /quixey.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "quixey.h" 3 | #include "error.h" 4 | #include "library_adaptor.h" 5 | #include "string_util.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace { 15 | 16 | //----------------------------------------------------------------------------- 17 | // Name: is_delim 18 | //----------------------------------------------------------------------------- 19 | bool is_delim(char c) { 20 | using std::strchr; 21 | return strchr(" !:;,+-<>'/*%^=()[].\r\n\t", c) != nullptr; 22 | } 23 | 24 | //----------------------------------------------------------------------------- 25 | // Name: is_odigit 26 | //----------------------------------------------------------------------------- 27 | bool is_odigit(char c) { 28 | return (c >= '0' && c <= '7'); 29 | } 30 | 31 | //----------------------------------------------------------------------------- 32 | // Name: skip_whitespace 33 | //----------------------------------------------------------------------------- 34 | template 35 | void skip_whitespace(In &it, In end) { 36 | 37 | using std::isspace; 38 | 39 | // skip over white space 40 | while(it != end && isspace(*it)) { 41 | ++it; 42 | } 43 | } 44 | 45 | //----------------------------------------------------------------------------- 46 | // Name: skip_comments 47 | //----------------------------------------------------------------------------- 48 | template 49 | bool skip_comments(In &it, In end) { 50 | 51 | if(it == end) { 52 | return false; 53 | } 54 | 55 | const In original_pc = it; 56 | 57 | if(*it == '/') { 58 | ++it; 59 | 60 | // c++ style comments 61 | if(it != end && *it == '/') { 62 | while(it != end && *it != '\n') { 63 | ++it; 64 | } 65 | return true; 66 | 67 | 68 | // c style comments 69 | } else if(it != end && *it == '*') { 70 | 71 | do { 72 | // find end of comment 73 | while(it != end && *it != '*') { 74 | ++it; 75 | } 76 | 77 | if(it != end) { 78 | ++it; 79 | } 80 | } while(it != end && *it != '/'); 81 | 82 | if(it == end) { 83 | throw unexpected_eof(); 84 | } 85 | 86 | ++it; 87 | return true; 88 | 89 | // not a comment 90 | } else { 91 | it = original_pc; 92 | } 93 | } else if(*it == '#') { 94 | ++it; 95 | 96 | // shell style comments 97 | while(it != end && *it != '\n') { 98 | ++it; 99 | } 100 | return true; 101 | } 102 | 103 | return false; 104 | } 105 | 106 | //----------------------------------------------------------------------------- 107 | // Name: process_char 108 | // Desc: processes a character constant (assumes is either in a string or a 109 | // quoted char constant) 110 | //----------------------------------------------------------------------------- 111 | template 112 | char process_char(In &it, In end) { 113 | 114 | using std::isxdigit; 115 | 116 | if(it == end) { 117 | return '\0'; 118 | } 119 | 120 | char temp_char; 121 | 122 | // is it an escape sequence? 123 | if(*it == '\\') { 124 | 125 | std::string temp_string; 126 | 127 | // skip past escape char 128 | ++it; 129 | 130 | if(it == end) { 131 | throw unexpected_eof(); 132 | } 133 | 134 | // which special sequence is it? 135 | switch(*it) { 136 | case '\'': temp_char = '\''; break; 137 | case '\"': temp_char = '\"'; break; 138 | case '\\': temp_char = '\\'; break; 139 | case '0': temp_char = '\0'; break; 140 | case 'a': temp_char = '\a'; break; 141 | case 'b': temp_char = '\b'; break; 142 | case 'f': temp_char = '\f'; break; 143 | case 'n': temp_char = '\n'; break; 144 | case 'r': temp_char = '\r'; break; 145 | case 't': temp_char = '\t'; break; 146 | case 'v': temp_char = '\v'; break; 147 | case 'x': 148 | ++it; 149 | while(it != end && isxdigit(*it)) { 150 | temp_string += *it; 151 | ++it; 152 | } 153 | 154 | if(it == end) { 155 | throw unexpected_eof(); 156 | } 157 | 158 | temp_char = static_cast(stoi(temp_string, nullptr, 16)); 159 | --it; 160 | break; 161 | default: 162 | while(it != end && is_odigit(*it)) { 163 | temp_string += *it; 164 | ++it; 165 | } 166 | 167 | if(it == end) { 168 | throw unexpected_eof(); 169 | } 170 | 171 | temp_char = static_cast(stoi(temp_string, nullptr, 8)); 172 | --it; 173 | break; 174 | } 175 | 176 | } else { 177 | temp_char = *it; 178 | } 179 | 180 | ++it; 181 | 182 | return temp_char; 183 | } 184 | 185 | //----------------------------------------------------------------------------- 186 | // Name: call_size 187 | //----------------------------------------------------------------------------- 188 | int call_size(std::string s) { 189 | return s.size(); 190 | } 191 | 192 | //----------------------------------------------------------------------------- 193 | // Name: call_puts 194 | //----------------------------------------------------------------------------- 195 | int call_puts(std::string s) { 196 | std::cout << s << '\n'; 197 | return 0; 198 | } 199 | 200 | //----------------------------------------------------------------------------- 201 | // Name: call_getch 202 | //----------------------------------------------------------------------------- 203 | int call_getch() { 204 | char ch; 205 | std::cin >> ch; 206 | return ch; 207 | } 208 | 209 | //----------------------------------------------------------------------------- 210 | // Name: call_getnum 211 | //----------------------------------------------------------------------------- 212 | int call_getnum() { 213 | int num; 214 | std::cin >> num; 215 | return num; 216 | } 217 | 218 | //----------------------------------------------------------------------------- 219 | // Name: call_putchar 220 | //----------------------------------------------------------------------------- 221 | int call_putchar(char ch) { 222 | std::cout << ch; 223 | return 0; 224 | } 225 | 226 | //----------------------------------------------------------------------------- 227 | // Name: call_is_integer 228 | //----------------------------------------------------------------------------- 229 | int call_is_integer(variable x) { 230 | return is_integer(x); 231 | } 232 | 233 | //----------------------------------------------------------------------------- 234 | // Name: call_is_character 235 | //----------------------------------------------------------------------------- 236 | int call_is_character(variable x) { 237 | return is_character(x); 238 | } 239 | 240 | //----------------------------------------------------------------------------- 241 | // Name: call_is_string 242 | //----------------------------------------------------------------------------- 243 | int call_is_string(variable x) { 244 | return is_string(x); 245 | } 246 | 247 | //----------------------------------------------------------------------------- 248 | // Name: call_is_function 249 | //----------------------------------------------------------------------------- 250 | int call_is_function(variable x) { 251 | return is_function(x); 252 | } 253 | 254 | //----------------------------------------------------------------------------- 255 | // Name: call_is_array 256 | //----------------------------------------------------------------------------- 257 | int call_is_array(variable x) { 258 | return is_array(x); 259 | } 260 | 261 | // example of a variadic built in function 262 | // much more complicated, but works great :) 263 | // by only taking a single param of type 264 | // quixey * the function system knows 265 | // this is a variadic function, and will leave it 266 | // up to the implementer to pull arguments as needed 267 | // BE CAREFUL! 268 | //----------------------------------------------------------------------------- 269 | // Name: call_printf 270 | //----------------------------------------------------------------------------- 271 | int call_printf(quixey *engine) { 272 | 273 | assert(engine); 274 | 275 | variable x; 276 | engine->eval_exp(x); 277 | 278 | auto format = to_string(x); 279 | 280 | for(auto it = format.begin(); it != format.end(); ++it) { 281 | if(*it == '%') { 282 | if(++it != format.end()) { 283 | switch(*it) { 284 | case 'c': 285 | do { 286 | engine->get_token(); 287 | engine->test_token(token::COMMA); 288 | 289 | variable v; 290 | engine->eval_exp(v); 291 | std::cout << to_character(v); 292 | } while(0); 293 | break; 294 | 295 | case 'u': 296 | do { 297 | engine->get_token(); 298 | engine->test_token(token::COMMA); 299 | 300 | variable v; 301 | engine->eval_exp(v); 302 | std::cout << std::dec << static_cast(to_integer(v)); 303 | } while(0); 304 | break; 305 | 306 | case 'd': 307 | do { 308 | engine->get_token(); 309 | engine->test_token(token::COMMA); 310 | 311 | variable v; 312 | engine->eval_exp(v); 313 | std::cout << std::dec << to_integer(v); 314 | } while(0); 315 | break; 316 | 317 | case 'x': 318 | do { 319 | engine->get_token(); 320 | engine->test_token(token::COMMA); 321 | 322 | variable v; 323 | engine->eval_exp(v); 324 | std::cout << std::hex << to_integer(v); 325 | } while(0); 326 | break; 327 | 328 | case 's': 329 | do { 330 | engine->get_token(); 331 | engine->test_token(token::COMMA); 332 | 333 | variable v; 334 | engine->eval_exp(v); 335 | std::cout << to_string(v); 336 | } while(0); 337 | break; 338 | default: 339 | std::cout << *it; 340 | break; 341 | } 342 | } else { 343 | std::cout << '%'; 344 | } 345 | } else { 346 | std::cout << *it; 347 | } 348 | } 349 | return 0; 350 | } 351 | 352 | } 353 | 354 | //----------------------------------------------------------------------------- 355 | // Name: quixey 356 | //----------------------------------------------------------------------------- 357 | quixey::quixey() : block_depth_(0), prescan_(false) { 358 | 359 | // setup keywords 360 | keywords_.emplace("break", token::BREAK); 361 | keywords_.emplace("case", token::CASE); 362 | keywords_.emplace("char", token::CHAR); 363 | keywords_.emplace("continue", token::CONTINUE); 364 | keywords_.emplace("do", token::DO); 365 | keywords_.emplace("else", token::ELSE); 366 | keywords_.emplace("for", token::FOR); 367 | keywords_.emplace("if", token::IF); 368 | keywords_.emplace("int", token::INT); 369 | keywords_.emplace("auto", token::AUTO); 370 | keywords_.emplace("return", token::RETURN); 371 | keywords_.emplace("string", token::STRING); 372 | keywords_.emplace("switch", token::SWITCH); 373 | keywords_.emplace("while", token::WHILE); 374 | keywords_.emplace("function", token::FUNCTION); 375 | 376 | // setup types 377 | types_.insert("char"); 378 | types_.insert("int"); 379 | types_.insert("string"); 380 | types_.insert("auto"); 381 | 382 | // setup built in functions 383 | register_function("getch", call_getch); 384 | register_function("getnum", call_getnum); 385 | register_function("is_array", call_is_array); 386 | register_function("is_character", call_is_character); 387 | register_function("is_function", call_is_function); 388 | register_function("is_integer", call_is_integer); 389 | register_function("is_string", call_is_string); 390 | register_function("printf", call_printf); 391 | register_function("putchar", call_putchar); 392 | register_function("puts", call_puts); 393 | register_function("size", call_size); 394 | 395 | reset(); 396 | } 397 | 398 | //----------------------------------------------------------------------------- 399 | // Name: create_scope 400 | //----------------------------------------------------------------------------- 401 | void quixey::create_scope() { 402 | assert(!function_variables_.empty()); 403 | function_variables_.top().push_front({}); 404 | } 405 | 406 | //----------------------------------------------------------------------------- 407 | // Name: destroy_scope 408 | //----------------------------------------------------------------------------- 409 | void quixey::destroy_scope() { 410 | assert(!function_variables_.empty()); 411 | function_variables_.top().pop_front(); 412 | } 413 | 414 | //----------------------------------------------------------------------------- 415 | // Name: register_function 416 | //----------------------------------------------------------------------------- 417 | template 418 | void quixey::register_function(const std::string &name, F func) { 419 | builtin_functions_.emplace(name, wrap_function(func)); 420 | } 421 | 422 | //----------------------------------------------------------------------------- 423 | // Name: tokenize 424 | // Desc: tokenizes the whole program array and places it 425 | // into program_ 426 | //----------------------------------------------------------------------------- 427 | void quixey::tokenize(std::vector::const_iterator first, std::vector::const_iterator last) { 428 | // this will quickly run through the program 429 | // tokenizing as it goes and pushing it the tokens onto 430 | // our list... this is our sort of "compiling" 431 | auto it = first; 432 | try { 433 | while(1) { 434 | // eat up leading whitespace 435 | skip_whitespace(it, last); 436 | 437 | // eat up all the comments and whitespace 438 | // loop cause there may be more than one in a row 439 | while(skip_comments(it, last)) { 440 | skip_whitespace(it, last); 441 | } 442 | 443 | token tok = process_token(first, it, last); 444 | if(tok.type() == token::FINISHED) { 445 | break; 446 | } 447 | 448 | tok.filename = imports_.top(); 449 | tok.line_number = std::count(first, it, '\n') + 1; 450 | 451 | program_.push_back(tok); 452 | } 453 | } catch(error &e) { 454 | if(e.line_number == -1) { 455 | e.line_number = std::count(first, it, '\n') + 1; 456 | e.filename = imports_.top(); 457 | } 458 | throw; 459 | } 460 | } 461 | 462 | //----------------------------------------------------------------------------- 463 | // Name: is_type 464 | // Desc: returns true if the string is a typename 465 | //----------------------------------------------------------------------------- 466 | bool quixey::is_type(const std::string &s) const { 467 | return types_.find(s) != types_.end(); 468 | } 469 | 470 | //----------------------------------------------------------------------------- 471 | // Name: is_type 472 | // Desc: returns true if the token is a typename 473 | //----------------------------------------------------------------------------- 474 | bool quixey::is_type(const token &token) const { 475 | return is_type(to_string(token)); 476 | } 477 | 478 | //----------------------------------------------------------------------------- 479 | // Name: is_keyword 480 | // Desc: returns true if the string is a keyword 481 | //----------------------------------------------------------------------------- 482 | bool quixey::is_keyword(const std::string &s) const { 483 | return get_keyword(s) != token::UNKNOWN; 484 | } 485 | 486 | //----------------------------------------------------------------------------- 487 | // Name: declare_function 488 | // Desc: 489 | //----------------------------------------------------------------------------- 490 | void quixey::declare_function(const std::string &name, int return_type, int &brace) { 491 | 492 | if(is_keyword(name)) { 493 | throw function_name_is_keyword(); 494 | } 495 | 496 | // track the program counter's original location 497 | const address_t function_location = program_counter_; 498 | 499 | // we just saw an lparen, get next token... 500 | // it should be either a type or an rparen 501 | get_token(); 502 | 503 | unsigned int param_count = 0; 504 | 505 | while(token_.type() != token::RPAREN) { 506 | 507 | // while we are at it, get some basic data on params 508 | if(is_type(token_)) { 509 | ++param_count; 510 | 511 | get_token(); 512 | 513 | // if we didn't read an identifier (the name of the variable) 514 | test_token(token::IDENTIFIER); 515 | 516 | get_token(); 517 | if(token_.type() == token::COMMA) { 518 | get_token(); 519 | } else if(token_.type() != token::RPAREN) { 520 | throw paren_expected(); 521 | } 522 | } else { 523 | throw type_expected(); 524 | } 525 | } 526 | 527 | // create a new function object and add it to our map 528 | if(functions_.insert(std::make_pair(name, function(return_type, function_location, param_count))).second == false) { 529 | throw duplicate_function(); 530 | } 531 | 532 | // the token following a function's right paren should ALWAYS be 533 | // a left brace 534 | get_token(); 535 | test_token(token::LBRACE); 536 | ++brace; 537 | } 538 | 539 | //----------------------------------------------------------------------------- 540 | // Name: prescan 541 | // Desc: Find the location of all functions in the 542 | // program and store global variables 543 | //----------------------------------------------------------------------------- 544 | void quixey::prescan() { 545 | const address_t program_start = program_counter_; 546 | int brace = 0; // When 0, this var tells us that 547 | // current source position is outside 548 | // of any function. 549 | 550 | prescan_ = true; 551 | 552 | do { 553 | while(brace != 0) { 554 | // bypass code inside functions 555 | get_token(); 556 | if(token_.type() == token::LBRACE) ++brace; 557 | else if(token_.type() == token::RBRACE) --brace; 558 | } 559 | 560 | // save current position 561 | const address_t typename_location = program_counter_; 562 | get_token(); 563 | 564 | // global var type or function return type 565 | if(is_type(token_)) { 566 | 567 | // save data type 568 | const token::Type data_type = token_.type(); 569 | get_token(); 570 | 571 | if(token_.type() == token::IDENTIFIER) { 572 | auto identifier_name = to_string(token_); 573 | get_token(); 574 | 575 | if(token_.type() != token::LPAREN) { 576 | // must be global var 577 | // return to start of declaration 578 | program_counter_ = typename_location; 579 | declare_global(); 580 | test_token(token::SEMICOLON); 581 | } else { 582 | // must be a function 583 | declare_function(identifier_name, data_type, brace); 584 | } 585 | } else { 586 | throw identifier_expected(); 587 | } 588 | } else if(token_.type() != token::FINISHED) { 589 | throw type_expected(); 590 | } 591 | } while(token_.type() != token::FINISHED); 592 | 593 | program_counter_ = program_start; 594 | 595 | prescan_ = false; 596 | } 597 | 598 | //----------------------------------------------------------------------------- 599 | // Name: import_code 600 | //----------------------------------------------------------------------------- 601 | void quixey::import_code(std::string name) { 602 | 603 | std::string parent_import; 604 | std::string import_path; 605 | 606 | if(!imports_.empty()) { 607 | parent_import = imports_.top(); 608 | size_t n = parent_import.find_last_of('/'); 609 | if(n != std::string::npos) { 610 | import_path = parent_import.substr(0, n + 1); 611 | } else { 612 | import_path = "./"; 613 | } 614 | } 615 | 616 | name = import_path + name; 617 | 618 | // the idea here is that imports_.top() will represent the file currently being imported 619 | imports_.push(name); 620 | 621 | std::vector source; 622 | std::ifstream file(name); 623 | 624 | if(!file) { 625 | throw unable_to_read_file(); 626 | } 627 | 628 | for(std::string line; std::getline(file, line); ) { 629 | 630 | // TODO(eteran): this isn't the most efficient implementation 631 | // let's think about ways to do less substring operations 632 | std::string temp = ltrim_copy(line); 633 | if(starts_with(temp, "@import")) { 634 | // we are looking at an import statement! 635 | 636 | std::string import_name = temp.substr(7); 637 | trim(import_name); 638 | 639 | if(!starts_with(import_name, '"') || !ends_with(import_name, '"')) { 640 | throw syntax_error(); 641 | } 642 | 643 | import_name = import_name.substr(1, import_name.size() - 2); 644 | 645 | import_code(import_path + import_name); 646 | 647 | source.push_back('\n'); 648 | continue; 649 | 650 | } 651 | 652 | source.insert(source.end(), line.begin(), line.end()); 653 | source.push_back('\n'); 654 | } 655 | 656 | tokenize(source.begin(), source.end()); 657 | imports_.pop(); 658 | 659 | } 660 | 661 | //----------------------------------------------------------------------------- 662 | // Name: dump_tokens 663 | //----------------------------------------------------------------------------- 664 | void quixey::dump_tokens() { 665 | std::cout << "--------------------\n"; 666 | for(auto token : program_) { 667 | std::cout << "\t" << token << "\n"; 668 | } 669 | std::cout << "--------------------\n"; 670 | } 671 | 672 | //----------------------------------------------------------------------------- 673 | // Name: load_program 674 | //----------------------------------------------------------------------------- 675 | void quixey::load_program(const std::string &name) { 676 | 677 | import_code(name); 678 | // make sure that the program has a terminator 679 | program_.emplace_back(token::FINISHED); 680 | 681 | #if 0 682 | dump_tokens(); 683 | #endif 684 | 685 | prescan(); 686 | } 687 | 688 | //----------------------------------------------------------------------------- 689 | // Name: get_keyword 690 | // Desc: resolves a string keyword to a token 691 | //----------------------------------------------------------------------------- 692 | token::Type quixey::get_keyword(const std::string &s) const { 693 | auto it = keywords_.find(s); 694 | return (it != keywords_.end()) ? it->second : token::UNKNOWN; 695 | } 696 | 697 | //----------------------------------------------------------------------------- 698 | // Name: get_function 699 | //----------------------------------------------------------------------------- 700 | const function &quixey::get_function(const std::string &name) const { 701 | 702 | auto it = functions_.find(name); 703 | if(it == functions_.end()) { 704 | throw undefined_function(); 705 | } 706 | 707 | return it->second; 708 | } 709 | 710 | //----------------------------------------------------------------------------- 711 | // Name: get_variable 712 | //----------------------------------------------------------------------------- 713 | variable &quixey::get_variable(const std::string &name) { 714 | 715 | // first, see if it's a local variable 716 | if(!function_variables_.empty()) { 717 | locals_t &locals = function_variables_.top(); 718 | 719 | for(std::vector &vars : locals) { 720 | for(local_variable &local : vars) { 721 | if(local.name == name) { 722 | return local.value; 723 | } 724 | } 725 | } 726 | } 727 | 728 | // otherwise, try global vars 729 | auto it = global_variables_.find(name); 730 | if(it != global_variables_.end()) { 731 | return it->second; 732 | } 733 | 734 | // variable not found 735 | throw variable_not_found(); 736 | } 737 | 738 | //----------------------------------------------------------------------------- 739 | // Name: reset 740 | //----------------------------------------------------------------------------- 741 | void quixey::reset() { 742 | 743 | // clear out global variables and functions 744 | global_variables_.clear(); 745 | functions_.clear(); 746 | 747 | // initialize local variable stack 748 | function_variables_ = std::stack(); 749 | 750 | // set program pointer to start of program buffer 751 | program_counter_ = 0; 752 | } 753 | 754 | //----------------------------------------------------------------------------- 755 | // Name: exec 756 | // Desc: really all this does is simulate what it would look like to the 757 | // interpreter if this function had been called by an already running 758 | // script 759 | //----------------------------------------------------------------------------- 760 | int quixey::exec(const std::string &function_name) { 761 | 762 | // setup call to entry point 763 | const function &func = get_function(function_name); 764 | 765 | program_counter_ = func.offset(); 766 | 767 | // intialize our token to point to main 768 | token_ = token(token::IDENTIFIER, function_name); 769 | 770 | // back up to opening ( 771 | --program_counter_; 772 | 773 | // call entry point to start interpreting 774 | return to_integer(call()); 775 | } 776 | 777 | //----------------------------------------------------------------------------- 778 | // Name: exec 779 | // Desc: really all this does is simulate what it would look like to the 780 | // interpreter if this function had been called by an already running 781 | // script 782 | //----------------------------------------------------------------------------- 783 | int quixey::exec(const std::string &function_name, const std::vector &args) { 784 | // setup call to entry point 785 | const function &func = get_function(function_name); 786 | 787 | program_counter_ = func.offset(); 788 | 789 | // intialize our token to point to main 790 | token_ = token(token::IDENTIFIER, function_name); 791 | 792 | // back up to opening ( 793 | --program_counter_; 794 | 795 | // call entry point to start interpreting 796 | return to_integer(call(args)); 797 | } 798 | 799 | //----------------------------------------------------------------------------- 800 | // Name: call 801 | //----------------------------------------------------------------------------- 802 | variable quixey::call(const std::string &function_name) { 803 | return call(get_function(function_name)); 804 | } 805 | 806 | //----------------------------------------------------------------------------- 807 | // Name: call 808 | //----------------------------------------------------------------------------- 809 | variable quixey::call(const std::string &function_name, const std::vector &args) { 810 | return call(get_function(function_name), args); 811 | } 812 | 813 | //----------------------------------------------------------------------------- 814 | // Name: call 815 | //----------------------------------------------------------------------------- 816 | variable quixey::call(const std::vector &args) { 817 | return call(to_string(token_), args); 818 | } 819 | 820 | //----------------------------------------------------------------------------- 821 | // Name: call 822 | //----------------------------------------------------------------------------- 823 | variable quixey::call() { 824 | return call(to_string(token_)); 825 | } 826 | 827 | //----------------------------------------------------------------------------- 828 | // Name: call 829 | //----------------------------------------------------------------------------- 830 | variable quixey::call(const function &func) { 831 | 832 | // is it a builtin? 833 | if(!func.name().empty()) { 834 | auto it = builtin_functions_.find(func.name()); 835 | if(it != builtin_functions_.end()) { 836 | 837 | get_token(); 838 | test_token(token::LPAREN); 839 | 840 | return_value_ = variable(it->second(this)); 841 | 842 | get_token(); 843 | test_token(token::RPAREN); 844 | 845 | return return_value_; 846 | } 847 | } 848 | 849 | 850 | const std::vector args = get_arguments(); 851 | 852 | if(args.size() != func.param_count()) { 853 | throw incorrect_param_count(); 854 | } 855 | 856 | 857 | push_function(); // save return location 858 | program_counter_ = func.offset(); // set program_counter_ to start of function 859 | 860 | 861 | // load the function's parameters with the values of the arguments 862 | const std::vector argument_names = get_parameter_metadata(args); 863 | 864 | function_variables_.push(locals_t()); 865 | create_scope(); 866 | 867 | // finally, push the parameters onto the stack 868 | for(size_t i = 0; i < args.size(); ++i) { 869 | push_local(args[i], argument_names[i]); 870 | } 871 | 872 | const int return_seen = interpret_block(); // interpret the function 873 | program_counter_ = pop_function(); // reset the program pointer 874 | 875 | destroy_scope(); 876 | function_variables_.pop(); 877 | 878 | // if no return seen, return int(0) 879 | if(!return_seen) { 880 | return_value_ = variable(0); 881 | } 882 | 883 | // force the variable to be a specific type 884 | switch(func.type()) { 885 | case token::STRING: 886 | if(!is_string(return_value_)) throw invalid_type_conversion(); 887 | break; 888 | case token::INT: 889 | if(!is_integer(return_value_)) throw invalid_type_conversion(); 890 | break; 891 | case token::CHAR: 892 | if(!is_character(return_value_)) throw invalid_type_conversion(); 893 | break; 894 | case token::AUTO: 895 | // generic! 896 | break; 897 | default: 898 | throw type_expected(); 899 | } 900 | 901 | // should be setup by now 902 | return return_value_; 903 | } 904 | 905 | //----------------------------------------------------------------------------- 906 | // Name: call 907 | //----------------------------------------------------------------------------- 908 | variable quixey::call(const function &func, const std::vector &args) { 909 | 910 | // is it a builtin? 911 | if(!func.name().empty()) { 912 | auto it = builtin_functions_.find(func.name()); 913 | if(it != builtin_functions_.end()) { 914 | throw builtins_cannot_be_entry_points(); 915 | } 916 | } 917 | 918 | if(args.size() != func.param_count()) { 919 | throw incorrect_param_count(); 920 | } 921 | 922 | 923 | push_function(); // save return location 924 | program_counter_ = func.offset(); // set program_counter_ to start of function 925 | 926 | 927 | // load the function's parameters with the values of the arguments 928 | const std::vector argument_names = get_parameter_metadata(args); 929 | 930 | function_variables_.push(locals_t()); 931 | create_scope(); 932 | 933 | // finally, push the parameters onto the stack 934 | for(size_t i = 0; i < args.size(); ++i) { 935 | push_local(args[i], argument_names[i]); 936 | } 937 | 938 | const int return_seen = interpret_block(); // interpret the function 939 | program_counter_ = pop_function(); // reset the program pointer 940 | 941 | destroy_scope(); 942 | function_variables_.pop(); 943 | 944 | // if no return seen, return int(0) 945 | if(!return_seen) { 946 | return_value_ = variable(0); 947 | } 948 | 949 | // force the variable to be a specific type 950 | switch(func.type()) { 951 | case token::STRING: 952 | if(!is_string(return_value_)) throw invalid_type_conversion(); 953 | break; 954 | case token::INT: 955 | if(!is_integer(return_value_)) throw invalid_type_conversion(); 956 | break; 957 | case token::CHAR: 958 | if(!is_character(return_value_)) throw invalid_type_conversion(); 959 | break; 960 | case token::AUTO: 961 | // generic! 962 | break; 963 | default: 964 | throw type_expected(); 965 | } 966 | 967 | // should be setup by now 968 | return return_value_; 969 | } 970 | 971 | //----------------------------------------------------------------------------- 972 | // Name: exec_return 973 | //----------------------------------------------------------------------------- 974 | int quixey::exec_return() { 975 | 976 | // get return value, if any 977 | variable value; 978 | eval_exp(value); 979 | 980 | // must be followed by a semicolon... 981 | test_token(token::SEMICOLON); 982 | return_value_ = value; 983 | return 1; 984 | } 985 | 986 | //----------------------------------------------------------------------------- 987 | // Name: declare_variable 988 | // Desc: common variable declaration code 989 | //----------------------------------------------------------------------------- 990 | template 991 | variable &quixey::declare_variable(F func) { 992 | // get type 993 | get_token(); 994 | 995 | // save var type 996 | // TODO: make it get this from a general type system 997 | const token::Type var_type = token_.type(); 998 | 999 | if(!is_type(token_)) { 1000 | throw type_expected(); 1001 | } 1002 | 1003 | std::string last_variable; 1004 | 1005 | // process comma-separated list 1006 | do { 1007 | // get var name 1008 | get_token(); 1009 | 1010 | auto var_name = to_string(token_); 1011 | last_variable = var_name; 1012 | 1013 | if(is_keyword(var_name)) { 1014 | throw variable_name_is_keyword(); 1015 | } 1016 | 1017 | // get next token ( , or ; or =) 1018 | get_token(); 1019 | 1020 | 1021 | variable expression; 1022 | if(token_.type() == token::ASSIGN) { 1023 | 1024 | eval_exp(expression); 1025 | 1026 | // get next token (, or ;) 1027 | get_token(); 1028 | } 1029 | 1030 | // so, we get the expression and create the variable 1031 | // seperately so that we can force the variable to be 1032 | // the requested type, and then when the expression result 1033 | // gets assigned to the variable, the type safety can kick 1034 | // in and make sure it is the right type. 1035 | variable var; 1036 | 1037 | // force the variable to be a specific type 1038 | switch(var_type) { 1039 | case token::STRING: 1040 | var = variable::create_string(); 1041 | break; 1042 | case token::INT: 1043 | var = variable::create_integer(); 1044 | break; 1045 | case token::CHAR: 1046 | var = variable::create_character(); 1047 | break; 1048 | case token::AUTO: 1049 | // generic! 1050 | break; 1051 | default: 1052 | throw type_expected(); 1053 | } 1054 | 1055 | var = expression; 1056 | 1057 | // push it on the stack of variables 1058 | func(var, var_name); 1059 | 1060 | } while(token_.type() == token::COMMA); 1061 | 1062 | return get_variable(last_variable); 1063 | } 1064 | 1065 | //----------------------------------------------------------------------------- 1066 | // Name: declare_local 1067 | //----------------------------------------------------------------------------- 1068 | variable &quixey::declare_local() { 1069 | return declare_variable([this](const variable &v, const std::string &name) { 1070 | push_local(v, name); 1071 | }); 1072 | } 1073 | 1074 | //----------------------------------------------------------------------------- 1075 | // Name: declare_global 1076 | //----------------------------------------------------------------------------- 1077 | variable &quixey::declare_global() { 1078 | return declare_variable([this](const variable &v, const std::string &name) { 1079 | push_global(v, name); 1080 | }); 1081 | } 1082 | 1083 | //----------------------------------------------------------------------------- 1084 | // Name: get_arguments 1085 | //----------------------------------------------------------------------------- 1086 | std::vector quixey::get_arguments() { 1087 | std::vector arguments; 1088 | 1089 | get_token(); 1090 | test_token(token::LPAREN); 1091 | 1092 | // process a comma-separated list of values 1093 | if(peek_token().type() != token::RPAREN) { 1094 | do { 1095 | variable argument_value; 1096 | eval_exp(argument_value); 1097 | arguments.push_back(std::move(argument_value)); 1098 | get_token(); 1099 | } while(token_.type() == token::COMMA); 1100 | } else { 1101 | // ok we just saw a rparen, eat it up so the parser is happy 1102 | get_token(); 1103 | } 1104 | 1105 | test_token(token::RPAREN); 1106 | 1107 | return arguments; 1108 | } 1109 | 1110 | //----------------------------------------------------------------------------- 1111 | // Name: interpret_block 1112 | // Desc: Interpret a single statement or block of code. When 1113 | // interp_block() returns from its initial call, the final 1114 | // brace (or a return) in main() has been encountered. 1115 | //----------------------------------------------------------------------------- 1116 | int quixey::interpret_block() { 1117 | 1118 | const int block_start = block_depth_; 1119 | 1120 | if(peek_token().type() != token::LBRACE) { 1121 | throw brace_expected(); 1122 | } 1123 | 1124 | do { 1125 | get_token(); 1126 | 1127 | // see what kind of token is up 1128 | if(is_type(token_)) { 1129 | // declare local variables 1130 | put_back(); 1131 | declare_local(); 1132 | test_token(token::SEMICOLON); 1133 | } else { 1134 | 1135 | switch(token_.type()) { 1136 | case token::IDENTIFIER: 1137 | case token::LPAREN: 1138 | { 1139 | put_back(); // restore token to input stream for 1140 | // further processing by eval_exp() 1141 | 1142 | // process the expression, since this is a stand-alone 1143 | // expression, we just ditch the return value 1144 | variable value; 1145 | eval_exp(value); 1146 | 1147 | test_token(token::SEMICOLON); 1148 | } 1149 | break; 1150 | 1151 | // keywords 1152 | case token::RETURN: // return from function call 1153 | exec_return(); 1154 | 1155 | do { 1156 | get_token(); 1157 | if(token_.type() == token::LBRACE) { ++block_depth_; create_scope(); } 1158 | else if(token_.type() == token::RBRACE) { --block_depth_; destroy_scope(); } 1159 | } while(block_start != block_depth_); 1160 | 1161 | // this return instead of a break is important 1162 | // it makes sure that we stop interpreting this block 1163 | return 1; 1164 | 1165 | case token::IF: // process an if statement 1166 | if(exec_if()) { 1167 | do { 1168 | get_token(); 1169 | if(token_.type() == token::LBRACE) { ++block_depth_; create_scope(); } 1170 | else if(token_.type() == token::RBRACE) { --block_depth_; destroy_scope(); } 1171 | } while(block_start != block_depth_); 1172 | return 1; 1173 | } 1174 | break; 1175 | 1176 | case token::WHILE: // process a while loop 1177 | if(exec_while()) { 1178 | do { 1179 | get_token(); 1180 | if(token_.type() == token::LBRACE) { ++block_depth_; create_scope(); } 1181 | else if(token_.type() == token::RBRACE) { --block_depth_; destroy_scope(); } 1182 | } while(block_start != block_depth_); 1183 | return 1; 1184 | } 1185 | break; 1186 | 1187 | case token::DO: // process a do-while loop 1188 | if(exec_do()) { 1189 | do { 1190 | get_token(); 1191 | if(token_.type() == token::LBRACE) { ++block_depth_; create_scope(); } 1192 | else if(token_.type() == token::RBRACE) { --block_depth_; destroy_scope(); } 1193 | } while(block_start != block_depth_); 1194 | return 1; 1195 | 1196 | } 1197 | break; 1198 | 1199 | case token::FOR: // process a for loop 1200 | if(exec_for()) { 1201 | do { 1202 | get_token(); 1203 | if(token_.type() == token::LBRACE) { ++block_depth_; create_scope(); } 1204 | else if(token_.type() == token::RBRACE) { --block_depth_; destroy_scope(); } 1205 | } while(block_start != block_depth_); 1206 | return 1; 1207 | 1208 | } 1209 | break; 1210 | 1211 | case token::BREAK: 1212 | throw unimplemented_keyword(); 1213 | break; 1214 | 1215 | case token::CONTINUE: 1216 | throw unimplemented_keyword(); 1217 | break; 1218 | 1219 | case token::SWITCH: 1220 | throw unimplemented_keyword(); 1221 | break; 1222 | 1223 | case token::CASE: 1224 | throw unimplemented_keyword(); 1225 | break; 1226 | 1227 | // block delimiters 1228 | case token::LBRACE: 1229 | ++block_depth_; 1230 | create_scope(); 1231 | break; 1232 | 1233 | case token::RBRACE: 1234 | destroy_scope(); 1235 | --block_depth_; 1236 | break; 1237 | 1238 | case token::SEMICOLON: 1239 | // do nothing :) 1240 | break; 1241 | 1242 | default: 1243 | throw syntax_error(); 1244 | break; 1245 | } 1246 | } 1247 | } while (token_.type() != token::FINISHED && (block_depth_ != block_start)); 1248 | 1249 | return 0; 1250 | } 1251 | 1252 | //----------------------------------------------------------------------------- 1253 | // Name: push_function 1254 | //----------------------------------------------------------------------------- 1255 | void quixey::push_function() { 1256 | call_stack_.push(program_counter_); 1257 | } 1258 | 1259 | //----------------------------------------------------------------------------- 1260 | // Name: pop_function 1261 | //----------------------------------------------------------------------------- 1262 | address_t quixey::pop_function() { 1263 | if(call_stack_.empty()) { 1264 | throw return_outside_call(); 1265 | } 1266 | 1267 | const address_t return_address = call_stack_.top(); 1268 | call_stack_.pop(); 1269 | return return_address; 1270 | } 1271 | 1272 | //----------------------------------------------------------------------------- 1273 | // Name: get_parameter_metadata 1274 | // Desc: binds types/name to arguments of a function when a function gets 1275 | // called, until this, they are typeless and nameless 1276 | //----------------------------------------------------------------------------- 1277 | std::vector quixey::get_parameter_metadata(const std::vector &arguments) { 1278 | 1279 | std::vector names; 1280 | int i = 0; 1281 | do { 1282 | // process comma-separated list of parameters 1283 | get_token(); 1284 | if(token_.type() != token::RPAREN) { 1285 | 1286 | if(!is_type(token_)) { 1287 | throw type_expected(); 1288 | } 1289 | 1290 | // link parameter type with argument already on 1291 | // local var stack 1292 | const token::Type type = token_.type(); 1293 | 1294 | switch(type) { 1295 | case token::STRING: 1296 | if(!is_string(arguments[i])) throw invalid_type_conversion(); 1297 | break; 1298 | case token::INT: 1299 | if(!is_integer(arguments[i])) throw invalid_type_conversion(); 1300 | break; 1301 | case token::CHAR: 1302 | if(!is_character(arguments[i])) throw invalid_type_conversion(); 1303 | break; 1304 | case token::AUTO: 1305 | // generic! 1306 | break; 1307 | default: 1308 | throw type_expected(); 1309 | } 1310 | 1311 | get_token(); 1312 | 1313 | // link parameter name with argument already on 1314 | // local var stack 1315 | names.push_back(to_string(token_)); 1316 | 1317 | get_token(); 1318 | ++i; 1319 | } 1320 | } while(token_.type() == token::COMMA); 1321 | 1322 | // if it wasn't a comma (which is why we are here) 1323 | // then it should have been an rparen 1324 | test_token(token::RPAREN); 1325 | 1326 | assert(arguments.size() == names.size()); 1327 | return names; 1328 | } 1329 | 1330 | //----------------------------------------------------------------------------- 1331 | // Name: push_local 1332 | //----------------------------------------------------------------------------- 1333 | void quixey::push_local(const variable &v, const std::string &name) { 1334 | 1335 | locals_t &locals = function_variables_.top(); 1336 | std::vector ¤t_scope = locals.front(); 1337 | 1338 | for(const local_variable &local : current_scope) { 1339 | if(local.name == name) { 1340 | throw duplicate_local(); 1341 | } 1342 | } 1343 | 1344 | current_scope.push_back({name, v}); 1345 | } 1346 | 1347 | //----------------------------------------------------------------------------- 1348 | // Name: push_global 1349 | //----------------------------------------------------------------------------- 1350 | void quixey::push_global(const variable &v, const std::string &name) { 1351 | if(global_variables_.insert(std::make_pair(name, v)).second == false) { 1352 | throw duplicate_global(); 1353 | } 1354 | } 1355 | 1356 | //----------------------------------------------------------------------------- 1357 | // Name: is_variable 1358 | //----------------------------------------------------------------------------- 1359 | bool quixey::is_variable(const token &tok) const { 1360 | return is_variable(to_string(tok)); 1361 | } 1362 | 1363 | //----------------------------------------------------------------------------- 1364 | // Name: is_variable 1365 | //----------------------------------------------------------------------------- 1366 | bool quixey::is_variable(const std::string &name) const { 1367 | 1368 | if(!function_variables_.empty()) { 1369 | const locals_t &locals = function_variables_.top(); 1370 | 1371 | // first, see if it's a local variable 1372 | for(const std::vector &local : locals) { 1373 | for(const local_variable& elem : local) { 1374 | if(elem.name == name) { 1375 | return true; 1376 | } 1377 | } 1378 | } 1379 | } 1380 | 1381 | // otherwise, try global vars 1382 | auto it = global_variables_.find(name); 1383 | return it != global_variables_.end(); 1384 | } 1385 | 1386 | //----------------------------------------------------------------------------- 1387 | // Name: is_function 1388 | //----------------------------------------------------------------------------- 1389 | bool quixey::is_function(const std::string &name) const { 1390 | auto it = functions_.find(name); 1391 | return it != functions_.end(); 1392 | } 1393 | 1394 | //----------------------------------------------------------------------------- 1395 | // Name: is_function 1396 | //----------------------------------------------------------------------------- 1397 | bool quixey::is_function(const token &tok) const { 1398 | return is_function(to_string(tok)); 1399 | } 1400 | 1401 | //----------------------------------------------------------------------------- 1402 | // Name: exec_if 1403 | //----------------------------------------------------------------------------- 1404 | int quixey::exec_if() { 1405 | 1406 | // make sure that the expression is in parens following the if 1407 | if(peek_token().type() != token::LPAREN) { 1408 | throw paren_expected(); 1409 | } 1410 | 1411 | // get if expression 1412 | variable cond; 1413 | eval_exp(cond); 1414 | 1415 | if(to_integer(cond)) { 1416 | 1417 | // is true so process target of IF 1418 | if(interpret_block()) { 1419 | return 1; 1420 | } 1421 | 1422 | // now we need to eat up as many if's as neccessary 1423 | // until we are no longer on an if chain 1424 | 1425 | while(peek_token().type() == token::ELSE) { 1426 | 1427 | // eat up the "else" 1428 | get_token(); 1429 | 1430 | if(peek_token().type() == token::IF) { 1431 | // eat the if 1432 | get_token(); 1433 | 1434 | // eat the expression 1435 | while(token_.type() != token::RPAREN) { 1436 | get_token(); 1437 | } 1438 | 1439 | // ok, we've just eaten up the rparam 1440 | test_token(token::RPAREN); 1441 | } 1442 | 1443 | // get to the next "}", note, find_eob will eat up the starting "{" too 1444 | find_eob(); 1445 | test_token(token::RBRACE); 1446 | } 1447 | } else { 1448 | 1449 | // otherwise skip around IF block and 1450 | // process the ELSE, if present 1451 | 1452 | find_eob(); // find start of next line 1453 | 1454 | get_token(); 1455 | 1456 | if(token_.type() != token::ELSE) { 1457 | put_back(); // restore token if no ELSE is present 1458 | } else { 1459 | if(peek_token().type() == token::IF) { 1460 | // we are in an else if block.. 1461 | get_token(); 1462 | return exec_if(); 1463 | } 1464 | } 1465 | } 1466 | 1467 | return 0; 1468 | } 1469 | 1470 | //----------------------------------------------------------------------------- 1471 | // Name: exec_while 1472 | //----------------------------------------------------------------------------- 1473 | int quixey::exec_while() { 1474 | 1475 | // save location of top of while loop 1476 | // minus one because we want to reprocess the while each time 1477 | const address_t while_start = program_counter_ - 1; 1478 | 1479 | // make sure that the expression is in parens following the while 1480 | if(peek_token().type() != token::LPAREN) { 1481 | throw paren_expected(); 1482 | } 1483 | 1484 | // get if expression 1485 | variable cond; 1486 | eval_exp(cond); 1487 | 1488 | if(to_integer(cond)) { 1489 | if(interpret_block()) { // if true, interpret 1490 | return 1; 1491 | } 1492 | 1493 | // loop back to top 1494 | program_counter_ = while_start; 1495 | } else { 1496 | // otherwise, skip around loop 1497 | find_eob(); 1498 | } 1499 | 1500 | return 0; 1501 | } 1502 | 1503 | //----------------------------------------------------------------------------- 1504 | // Name: exec_do 1505 | //----------------------------------------------------------------------------- 1506 | int quixey::exec_do() { 1507 | // save location of top of do loop 1508 | const address_t do_start = program_counter_ - 1; 1509 | 1510 | // interpret loop 1511 | if(interpret_block()) { 1512 | return 1; 1513 | } 1514 | 1515 | get_token(); 1516 | test_token(token::WHILE); 1517 | 1518 | // make sure that the expression is in parens following the while 1519 | if(peek_token().type() != token::LPAREN) { 1520 | throw paren_expected(); 1521 | } 1522 | 1523 | variable cond; 1524 | eval_exp(cond); // check the loop condition 1525 | test_token(token::SEMICOLON); 1526 | 1527 | if(to_integer(cond)) { 1528 | program_counter_ = do_start; // if true loop; otherwise, continue on 1529 | } 1530 | 1531 | return 0; 1532 | } 1533 | 1534 | //----------------------------------------------------------------------------- 1535 | // Name: find_eob 1536 | //----------------------------------------------------------------------------- 1537 | void quixey::find_eob() { 1538 | int brace = 1; 1539 | 1540 | // for this to work, we need to eat the first { 1541 | get_token(); 1542 | test_token(token::LBRACE); 1543 | 1544 | do { 1545 | get_token(); 1546 | if(token_.type() == token::LBRACE) ++brace; 1547 | else if(token_.type() == token::RBRACE) --brace; 1548 | } while(brace); 1549 | } 1550 | 1551 | 1552 | //----------------------------------------------------------------------------- 1553 | // Name: 1554 | //----------------------------------------------------------------------------- 1555 | int quixey::exec_foreach_body(variable &it) { 1556 | 1557 | get_token(); 1558 | test_token(token::COLON); 1559 | 1560 | variable var; 1561 | eval_exp(var); 1562 | 1563 | const int sz = size(var); 1564 | 1565 | get_token(); 1566 | test_token(token::RPAREN); 1567 | 1568 | const address_t loop_start = program_counter_; 1569 | for(int i = 0; i < sz; ++i) { 1570 | it = var[variable(i)]; 1571 | if(interpret_block()) { 1572 | destroy_scope(); 1573 | return 1; 1574 | } 1575 | program_counter_ = loop_start; 1576 | 1577 | } 1578 | find_eob(); 1579 | 1580 | destroy_scope(); 1581 | return 0; 1582 | } 1583 | 1584 | //----------------------------------------------------------------------------- 1585 | // Name: 1586 | //----------------------------------------------------------------------------- 1587 | int quixey::exec_for_body() { 1588 | // should be followed by a semicolon 1589 | test_token(token::SEMICOLON); 1590 | 1591 | // get past the ; 1592 | get_token(); 1593 | 1594 | // remember this spot, it's the loop invariant 1595 | const address_t invariant_address = program_counter_; 1596 | 1597 | for(;;) { 1598 | variable cond; 1599 | eval_exp(cond); // check the condition 1600 | 1601 | test_token(token::SEMICOLON); 1602 | 1603 | // get past the ; 1604 | get_token(); 1605 | 1606 | // this points to the loop increment part 1607 | const address_t increment_address = program_counter_; 1608 | 1609 | // find the start of the for block 1610 | int brace = 1; 1611 | do { 1612 | get_token(); 1613 | if(token_.type() == token::LPAREN) ++brace; 1614 | else if(token_.type() == token::RPAREN) --brace; 1615 | } while(brace); 1616 | 1617 | if(to_integer(cond)) { 1618 | if(interpret_block()) { // if true, interpret 1619 | destroy_scope(); 1620 | return 1; 1621 | } 1622 | } else { 1623 | // otherwise, skip around loop 1624 | find_eob(); 1625 | break; 1626 | } 1627 | 1628 | program_counter_ = increment_address; 1629 | eval_exp(cond); // do the increment 1630 | program_counter_ = invariant_address; // loop back to top 1631 | } 1632 | 1633 | destroy_scope(); 1634 | return 0; 1635 | } 1636 | 1637 | //----------------------------------------------------------------------------- 1638 | // Name: exec_for 1639 | //----------------------------------------------------------------------------- 1640 | int quixey::exec_for() { 1641 | 1642 | create_scope(); 1643 | 1644 | // eat up the leading ( 1645 | get_token(); 1646 | test_token(token::LPAREN); 1647 | 1648 | // initialization expression 1649 | if(is_type(peek_token())) { 1650 | variable &var = declare_local(); 1651 | if(current_token().type() == token::COLON) { 1652 | put_back(); 1653 | return exec_foreach_body(var); 1654 | } 1655 | put_back(); 1656 | } else { 1657 | variable init_expression; 1658 | eval_exp(init_expression); 1659 | } 1660 | 1661 | return exec_for_body(); 1662 | } 1663 | 1664 | //----------------------------------------------------------------------------- 1665 | // Name: put_back 1666 | //----------------------------------------------------------------------------- 1667 | void quixey::put_back() { 1668 | --program_counter_; 1669 | } 1670 | 1671 | //----------------------------------------------------------------------------- 1672 | // Name: get_token 1673 | //----------------------------------------------------------------------------- 1674 | token &quixey::get_token() { 1675 | token_ = peek_token(); 1676 | ++program_counter_; 1677 | return token_; 1678 | } 1679 | 1680 | //----------------------------------------------------------------------------- 1681 | // Name: peek_token 1682 | //----------------------------------------------------------------------------- 1683 | token &quixey::peek_token() { 1684 | if(program_counter_ >= program_.size()) { 1685 | throw unexpected_eof(); 1686 | } 1687 | 1688 | return program_[program_counter_]; 1689 | } 1690 | 1691 | //----------------------------------------------------------------------------- 1692 | // Name: process_token 1693 | //----------------------------------------------------------------------------- 1694 | template 1695 | token quixey::process_token(In first, In &it, In end) const { 1696 | 1697 | (void)first; 1698 | 1699 | using std::isdigit; 1700 | using std::isalpha; 1701 | 1702 | std::string temp_string; 1703 | 1704 | if(it == end) { 1705 | return token(token::FINISHED); 1706 | } 1707 | 1708 | switch(*it) { 1709 | case '{': 1710 | ++it; 1711 | return token(token::LBRACE); 1712 | case '}': 1713 | ++it; 1714 | return token(token::RBRACE); 1715 | case '[': 1716 | ++it; 1717 | return token(token::LBRACKET); 1718 | case ']': 1719 | ++it; 1720 | return token(token::RBRACKET); 1721 | case '.': 1722 | ++it; 1723 | return token(token::DOT); 1724 | case ';': 1725 | ++it; 1726 | return token(token::SEMICOLON); 1727 | case '(': 1728 | ++it; 1729 | return token(token::LPAREN); 1730 | case ')': 1731 | ++it; 1732 | return token(token::RPAREN); 1733 | case ',': 1734 | ++it; 1735 | return token(token::COMMA); 1736 | case '~': 1737 | ++it; 1738 | return token(token::CMP); 1739 | 1740 | case ':': 1741 | ++it; 1742 | if(it != end && *it == ':') { 1743 | ++it; 1744 | return token(token::DOUBLECOLON); 1745 | } else { 1746 | return token(token::COLON); 1747 | } 1748 | case '/': 1749 | ++it; 1750 | if(it != end && *it == '=') { 1751 | ++it; 1752 | return token(token::DIV_EQ); 1753 | } else { 1754 | return token(token::DIV); 1755 | } 1756 | case '^': 1757 | ++it; 1758 | if(it != end && *it == '=') { 1759 | ++it; 1760 | return token(token::XOR_EQ); 1761 | } else { 1762 | return token(token::XOR); 1763 | } 1764 | case '=': 1765 | ++it; 1766 | if(it != end && *it == '=') { 1767 | ++it; 1768 | return token(token::EQ); 1769 | } else { 1770 | return token(token::ASSIGN); 1771 | } 1772 | case '+': 1773 | ++it; 1774 | if(it != end && *it == '=') { 1775 | ++it; 1776 | return token(token::PLUS_EQ); 1777 | } else { 1778 | return token(token::PLUS); 1779 | } 1780 | case '-': 1781 | ++it; 1782 | if(it != end && *it == '=') { 1783 | ++it; 1784 | return token(token::MINUS_EQ); 1785 | } else { 1786 | return token(token::MINUS); 1787 | } 1788 | case '!': 1789 | ++it; 1790 | if(it != end && *it == '=') { 1791 | ++it; 1792 | return token(token::NE); 1793 | } else { 1794 | return token(token::NOT); 1795 | } 1796 | case '*': 1797 | ++it; 1798 | if(it != end && *it == '=') { 1799 | ++it; 1800 | return token(token::MUL_EQ); 1801 | } else { 1802 | return token(token::MUL); 1803 | } 1804 | case '%': 1805 | ++it; 1806 | if(it != end && *it == '=') { 1807 | ++it; 1808 | return token(token::MOD_EQ); 1809 | } else { 1810 | return token(token::MOD); 1811 | } 1812 | case '&': 1813 | ++it; 1814 | if(it != end && *it == '=') { 1815 | ++it; 1816 | return token(token::AND_EQ); 1817 | } else if(it != end && *it == '&') { 1818 | ++it; 1819 | return token(token::LOGICAL_AND); 1820 | } else { 1821 | return token(token::AND); 1822 | } 1823 | case '|': 1824 | ++it; 1825 | if(it != end && *it == '=') { 1826 | ++it; 1827 | return token(token::OR_EQ); 1828 | } else if(it != end && *it == '|') { 1829 | ++it; 1830 | return token(token::LOGICAL_OR); 1831 | } else { 1832 | return token(token::OR); 1833 | } 1834 | case '>': 1835 | ++it; 1836 | if(it != end && *it == '>') { 1837 | ++it; 1838 | if(it != end && *it == '=') { 1839 | ++it; 1840 | return token(token::RSHIFT_EQ); 1841 | } else { 1842 | return token(token::RSHIFT); 1843 | } 1844 | } else if(it != end && *it == '=') { 1845 | ++it; 1846 | return token(token::GE); 1847 | } else { 1848 | return token(token::GT); 1849 | } 1850 | case '<': 1851 | ++it; 1852 | if(it != end && *it == '<') { 1853 | ++it; 1854 | if (it != end && *it == '=') { 1855 | ++it; 1856 | return token(token::LSHIFT_EQ); 1857 | } else { 1858 | return token(token::LSHIFT); 1859 | } 1860 | } else if(it != end && *it == '=') { 1861 | ++it; 1862 | return token(token::LE); 1863 | } else { 1864 | return token(token::LT); 1865 | } 1866 | 1867 | // character constant 1868 | case '\'': 1869 | // skip past opening quote 1870 | ++it; 1871 | 1872 | temp_string = process_char(it, end); 1873 | 1874 | // the next character better be a single quote 1875 | if(it == end || *it != '\'') { 1876 | throw quote_expected(); 1877 | } 1878 | 1879 | // skip past the ending quote 1880 | ++it; 1881 | 1882 | return token(token::CHARACTER, temp_string); 1883 | 1884 | // quoted string 1885 | case '"': 1886 | 1887 | // skip past opening quote 1888 | ++it; 1889 | 1890 | // start with empty string 1891 | temp_string = ""; 1892 | 1893 | while(it != end && *it != '"' && *it != '\n') { 1894 | temp_string += process_char(it, end); 1895 | } 1896 | 1897 | if(it == end) { 1898 | throw unexpected_eof(); 1899 | } 1900 | 1901 | // make sure we stoped because of an ending quote, not a newline... 1902 | if(*it != '"') { 1903 | throw multiline_string_literal(); 1904 | } 1905 | 1906 | // skip past the ending quote 1907 | ++it; 1908 | 1909 | return token(token::STRING_LITERAL, temp_string); 1910 | 1911 | default: 1912 | if(isdigit(*it)) { 1913 | // number 1914 | temp_string = ""; 1915 | char ch; 1916 | 1917 | while(it != end && !is_delim((ch = *it))) { 1918 | temp_string += ch; 1919 | ++it; 1920 | } 1921 | 1922 | if(it == end) { 1923 | throw unexpected_eof(); 1924 | } 1925 | 1926 | return token(token::INTEGER, temp_string); 1927 | 1928 | } else if(isalpha(*it)) { 1929 | // var or command 1930 | temp_string = ""; 1931 | char ch; 1932 | 1933 | while(it != end && !is_delim((ch = *it))) { 1934 | temp_string += ch; 1935 | ++it; 1936 | } 1937 | 1938 | if(it == end) { 1939 | throw unexpected_eof(); 1940 | } 1941 | 1942 | // is a keyword or an identifier 1943 | if(is_keyword(temp_string)) { 1944 | return token(get_keyword(temp_string), temp_string); 1945 | } else { 1946 | return token(token::IDENTIFIER, temp_string); 1947 | } 1948 | } 1949 | } 1950 | 1951 | throw syntax_error(); 1952 | } 1953 | 1954 | //----------------------------------------------------------------------------- 1955 | // Name: eval_exp 1956 | // Desc: entry point of expression parser 1957 | //----------------------------------------------------------------------------- 1958 | void quixey::eval_exp(variable &value) { 1959 | get_token(); 1960 | 1961 | if(token_.type() == token::SEMICOLON) { 1962 | // empty expression 1963 | } else { 1964 | eval_exp0(value); 1965 | // return last token read to input stream 1966 | put_back(); 1967 | } 1968 | } 1969 | 1970 | //----------------------------------------------------------------------------- 1971 | // Name: do_assignment 1972 | // Desc: 1973 | //----------------------------------------------------------------------------- 1974 | bool quixey::do_assignment(variable &var_ref, variable &value, address_t restore_point) { 1975 | get_token(); 1976 | 1977 | 1978 | // if a var, see if assignment 1979 | switch(token_.type()) { 1980 | case token::ASSIGN: 1981 | get_token(); 1982 | eval_exp0(value); 1983 | var_ref = value; 1984 | return true; 1985 | case token::PLUS_EQ: 1986 | get_token(); 1987 | eval_exp0(value); 1988 | var_ref += value; 1989 | return true; 1990 | case token::MINUS_EQ: 1991 | get_token(); 1992 | eval_exp0(value); 1993 | var_ref -= value; 1994 | return true; 1995 | case token::AND_EQ: 1996 | get_token(); 1997 | eval_exp0(value); 1998 | var_ref &= value; 1999 | return true; 2000 | case token::OR_EQ: 2001 | get_token(); 2002 | eval_exp0(value); 2003 | var_ref |= value; 2004 | return true; 2005 | case token::XOR_EQ: 2006 | get_token(); 2007 | eval_exp0(value); 2008 | var_ref ^= value; 2009 | return true; 2010 | case token::MUL_EQ: 2011 | get_token(); 2012 | eval_exp0(value); 2013 | var_ref *= value; 2014 | return true; 2015 | case token::MOD_EQ: 2016 | get_token(); 2017 | eval_exp0(value); 2018 | var_ref %= value; 2019 | return true; 2020 | case token::DIV_EQ: 2021 | get_token(); 2022 | eval_exp0(value); 2023 | var_ref /= value; 2024 | return true; 2025 | case token::LSHIFT_EQ: 2026 | get_token(); 2027 | eval_exp0(value); 2028 | var_ref <<= value; 2029 | return true; 2030 | case token::RSHIFT_EQ: 2031 | get_token(); 2032 | eval_exp0(value); 2033 | var_ref >>= value; 2034 | return true; 2035 | default: 2036 | // roll back this read 2037 | program_counter_ = restore_point; 2038 | put_back(); 2039 | get_token(); 2040 | } 2041 | 2042 | return false; 2043 | } 2044 | 2045 | //----------------------------------------------------------------------------- 2046 | // Name: eval_exp0 2047 | // Desc: Process an assignment expression 2048 | //----------------------------------------------------------------------------- 2049 | void quixey::eval_exp0(variable &value) { 2050 | 2051 | if(token_.type() == token::IDENTIFIER) { 2052 | if(is_variable(token_)) { 2053 | 2054 | 2055 | address_t restore_point = program_counter_; 2056 | 2057 | // holds name of var receiving the assignment 2058 | const token temp_token = token_; 2059 | variable &var_ref = get_variable(to_string(temp_token)); 2060 | 2061 | // handle l-value array expressions 2062 | if(peek_token().type() == token::LBRACKET) { 2063 | 2064 | // read the l-bracket 2065 | get_token(); 2066 | 2067 | // evaluate the index expression 2068 | variable index_expression; 2069 | eval_exp(index_expression); 2070 | 2071 | // get the r-bracket 2072 | get_token(); 2073 | test_token(token::RBRACKET); 2074 | variable var_ref2 = var_ref[index_expression]; 2075 | 2076 | 2077 | if(do_assignment(var_ref2, value, restore_point)) { 2078 | return; 2079 | } 2080 | } else if(do_assignment(var_ref, value, restore_point)) { 2081 | return; 2082 | } 2083 | 2084 | 2085 | 2086 | } 2087 | } 2088 | 2089 | eval_exp1(value); 2090 | } 2091 | 2092 | //----------------------------------------------------------------------------- 2093 | // Name: eval_exp1 2094 | // Desc: Process logical operations 2095 | //----------------------------------------------------------------------------- 2096 | void quixey::eval_exp1(variable &value) { 2097 | int op; 2098 | 2099 | eval_exp2(value); 2100 | 2101 | while((op = token_.type()) == token::LOGICAL_AND || op == token::LOGICAL_OR) { 2102 | variable partial_value; 2103 | 2104 | get_token(); 2105 | eval_exp2(partial_value); 2106 | 2107 | // perform the relational operation 2108 | switch(op) { 2109 | case token::LOGICAL_AND: 2110 | value = value && partial_value; 2111 | break; 2112 | 2113 | case token::LOGICAL_OR: 2114 | value = value || partial_value; 2115 | break; 2116 | } 2117 | } 2118 | } 2119 | 2120 | //----------------------------------------------------------------------------- 2121 | // Name: eval_exp2 2122 | // Desc: Process binary operations 2123 | //----------------------------------------------------------------------------- 2124 | void quixey::eval_exp2(variable &value) { 2125 | int op; 2126 | 2127 | eval_exp3(value); 2128 | 2129 | while((op = token_.type()) == token::AND || op == token::XOR || op == token::OR) { 2130 | variable partial_value; 2131 | 2132 | get_token(); 2133 | eval_exp3(partial_value); 2134 | 2135 | // add or subtract 2136 | switch(op) { 2137 | case token::AND: 2138 | value &= partial_value; 2139 | break; 2140 | 2141 | case token::OR: 2142 | value |= partial_value; 2143 | break; 2144 | 2145 | case token::XOR: 2146 | value ^= partial_value; 2147 | break; 2148 | } 2149 | } 2150 | } 2151 | 2152 | 2153 | //----------------------------------------------------------------------------- 2154 | // Name: eval_exp3 2155 | // Desc: Process relational operators 2156 | //----------------------------------------------------------------------------- 2157 | void quixey::eval_exp3(variable &value) { 2158 | int op; 2159 | 2160 | eval_exp4(value); 2161 | 2162 | while((op = token_.type()) == token::LT || op == token::LE || op == token::GT || op == token::GE || op == token::EQ || op == token::NE) { 2163 | variable partial_value; 2164 | 2165 | get_token(); 2166 | eval_exp4(partial_value); 2167 | 2168 | // perform the relational operation 2169 | switch(op) { 2170 | case token::LT: 2171 | value = value < partial_value; 2172 | break; 2173 | 2174 | case token::LE: 2175 | value = value <= partial_value; 2176 | break; 2177 | 2178 | case token::GT: 2179 | value = value > partial_value; 2180 | break; 2181 | 2182 | case token::GE: 2183 | value = value >= partial_value; 2184 | break; 2185 | 2186 | case token::EQ: 2187 | value = value == partial_value; 2188 | break; 2189 | 2190 | case token::NE: 2191 | value = value != partial_value; 2192 | break; 2193 | } 2194 | } 2195 | } 2196 | 2197 | //----------------------------------------------------------------------------- 2198 | // Name: eval_exp4 2199 | // Desc: Process shift operations 2200 | //----------------------------------------------------------------------------- 2201 | void quixey::eval_exp4(variable &value) { 2202 | int op; 2203 | 2204 | eval_exp5(value); 2205 | 2206 | while((op = token_.type()) == token::RSHIFT || op == token::LSHIFT) { 2207 | variable partial_value; 2208 | 2209 | get_token(); 2210 | eval_exp5(partial_value); 2211 | 2212 | // perform the shift operation 2213 | switch(op) { 2214 | case token::LSHIFT: 2215 | value <<= partial_value; 2216 | break; 2217 | 2218 | case token::RSHIFT: 2219 | value >>= partial_value; 2220 | break; 2221 | } 2222 | } 2223 | } 2224 | 2225 | 2226 | //----------------------------------------------------------------------------- 2227 | // Name: eval_exp5 2228 | // Desc: Add or subtract two terms 2229 | //----------------------------------------------------------------------------- 2230 | void quixey::eval_exp5(variable &value) { 2231 | int op; 2232 | 2233 | eval_exp6(value); 2234 | 2235 | while((op = token_.type()) == token::PLUS || op == token::MINUS) { 2236 | variable partial_value; 2237 | 2238 | get_token(); 2239 | eval_exp6(partial_value); 2240 | 2241 | // add or subtract 2242 | switch(op) { 2243 | case token::MINUS: 2244 | value -= partial_value; 2245 | break; 2246 | 2247 | case token::PLUS: 2248 | value += partial_value; 2249 | break; 2250 | } 2251 | } 2252 | } 2253 | 2254 | 2255 | //----------------------------------------------------------------------------- 2256 | // Name: eval_exp6 2257 | // Desc: Multiply or divide two factors 2258 | //----------------------------------------------------------------------------- 2259 | void quixey::eval_exp6(variable &value) { 2260 | 2261 | int op; 2262 | 2263 | eval_exp7(value); 2264 | 2265 | while((op = token_.type()) == token::MUL || op == token::DIV || op == token::MOD) { 2266 | variable partial_value; 2267 | 2268 | get_token(); 2269 | eval_exp7(partial_value); 2270 | 2271 | // mul, div, or modulus 2272 | switch(op) { 2273 | case token::MUL: 2274 | value *= partial_value; 2275 | break; 2276 | 2277 | case token::DIV: 2278 | value /= partial_value; 2279 | break; 2280 | 2281 | case token::MOD: 2282 | value %= partial_value; 2283 | break; 2284 | } 2285 | } 2286 | } 2287 | 2288 | 2289 | //----------------------------------------------------------------------------- 2290 | // Name: eval_exp7 2291 | // Desc: Is a unary +, - or ~ 2292 | //----------------------------------------------------------------------------- 2293 | void quixey::eval_exp7(variable &value) { 2294 | 2295 | int op; 2296 | 2297 | if((op = token_.type()) == token::PLUS || op == token::MINUS || op == token::CMP || op == token::NOT) { 2298 | get_token(); 2299 | } 2300 | 2301 | eval_exp8(value); 2302 | 2303 | switch(op) { 2304 | case token::MINUS: 2305 | value = -value; 2306 | break; 2307 | case token::CMP: 2308 | value = ~value; 2309 | break; 2310 | case token::PLUS: 2311 | value = +value; 2312 | break; 2313 | case token::NOT: 2314 | value = !value; 2315 | break; 2316 | } 2317 | } 2318 | 2319 | 2320 | //----------------------------------------------------------------------------- 2321 | // Name: eval_exp8 2322 | // Desc: Process parenthesized expression 2323 | //----------------------------------------------------------------------------- 2324 | void quixey::eval_exp8(variable &value) { 2325 | 2326 | switch(token_.type()) { 2327 | case token::LPAREN: 2328 | get_token(); 2329 | // get subexpression 2330 | eval_exp0(value); 2331 | test_token(token::RPAREN); 2332 | get_token(); 2333 | break; 2334 | default: 2335 | atom(value); 2336 | break; 2337 | } 2338 | } 2339 | 2340 | 2341 | //----------------------------------------------------------------------------- 2342 | // Name: atom 2343 | // Desc: Find value of number, variable, or function. 2344 | //----------------------------------------------------------------------------- 2345 | void quixey::atom(variable &value) { 2346 | 2347 | // so, we could just reuse value this whole time... 2348 | // but, if we did that, then we would lose the original 2349 | // type of the variable being set, and would lose the automatic 2350 | // detection of incorrect types we have in place, so we work with the 2351 | // temporary until the whole thing is ready, then assign it. At that point 2352 | // it better have the right type! 2353 | 2354 | variable partial_value; 2355 | 2356 | auto token_string = to_string(token_); 2357 | 2358 | switch(token_.type()) { 2359 | case token::FUNCTION: 2360 | partial_value = function_literal(); 2361 | break; 2362 | 2363 | case token::LBRACKET: 2364 | partial_value = array_literal(); 2365 | break; 2366 | 2367 | case token::IDENTIFIER: 2368 | if(is_variable(token_string)) { 2369 | // get var's value 2370 | partial_value = get_variable(token_string); 2371 | } else if(is_function(token_string)) { 2372 | // call user-defined function 2373 | partial_value = variable(get_function(token_string)); 2374 | } else { 2375 | // call "built-in" function 2376 | if(builtin_functions_.find(token_string) == builtin_functions_.end()) { 2377 | throw unknown_identifier(); 2378 | } 2379 | partial_value = variable(function(token_string)); 2380 | } 2381 | break; 2382 | 2383 | case token::INTEGER: 2384 | // is a numeric constant 2385 | partial_value = variable(static_cast(stol(token_string, nullptr, 0))); 2386 | break; 2387 | 2388 | case token::CHARACTER: 2389 | // is a character constant 2390 | partial_value = variable(token_string[0]); 2391 | break; 2392 | 2393 | case token::STRING_LITERAL: 2394 | // is a string constant 2395 | partial_value = variable(token_string); 2396 | break; 2397 | 2398 | default: 2399 | 2400 | // constructor syntax for types 2401 | // like: // string("hello") or just string() 2402 | if(is_type(token_) && peek_token().type() == token::LPAREN) { 2403 | 2404 | token::Type var_type = token_.type(); 2405 | 2406 | get_token(); 2407 | 2408 | if(peek_token().type() != token::RPAREN) { 2409 | variable param_value; 2410 | eval_exp(param_value); 2411 | 2412 | switch(var_type) { 2413 | case token::STRING: 2414 | partial_value = variable(to_string(param_value)); 2415 | break; 2416 | case token::INT: 2417 | partial_value = variable(to_integer(param_value)); 2418 | break; 2419 | case token::CHAR: 2420 | // special case integer -> character explicit conversion 2421 | partial_value = variable(static_cast(to_integer(param_value))); 2422 | break; 2423 | default: 2424 | throw type_expected(); 2425 | } 2426 | } else { 2427 | switch(var_type) { 2428 | case token::STRING: 2429 | partial_value = variable(""); 2430 | break; 2431 | case token::INT: 2432 | partial_value = variable(0); 2433 | break; 2434 | case token::CHAR: 2435 | partial_value = variable('\0'); 2436 | break; 2437 | default: 2438 | throw type_expected(); 2439 | } 2440 | } 2441 | 2442 | get_token(); 2443 | test_token(token::RPAREN); 2444 | 2445 | } else { 2446 | throw syntax_error(); // syntax error 2447 | } 2448 | } 2449 | 2450 | // handle R-VALUE indexes 2451 | 2452 | while(peek_token().type() == token::LBRACKET || peek_token().type() == token::LPAREN) { 2453 | switch(peek_token().type()) { 2454 | case token::LBRACKET: 2455 | { 2456 | get_token(); 2457 | variable index_expression; 2458 | 2459 | eval_exp(index_expression); 2460 | 2461 | get_token(); 2462 | test_token(token::RBRACKET); 2463 | 2464 | partial_value = partial_value[index_expression]; 2465 | } 2466 | break; 2467 | case token::LPAREN: 2468 | { 2469 | if(prescan_) { 2470 | throw function_during_prescan(); 2471 | } 2472 | partial_value = call(partial_value()); 2473 | } 2474 | break; 2475 | default: 2476 | break; 2477 | } 2478 | } 2479 | 2480 | value = partial_value; 2481 | get_token(); 2482 | } 2483 | 2484 | 2485 | //----------------------------------------------------------------------------- 2486 | // Name: array_literal 2487 | //----------------------------------------------------------------------------- 2488 | variable quixey::array_literal() { 2489 | test_token(token::LBRACKET); 2490 | 2491 | std::vector contents; 2492 | 2493 | if(peek_token().type() == token::RBRACKET) { 2494 | get_token(); 2495 | } else { 2496 | 2497 | while(token_.type() != token::RBRACKET) { 2498 | 2499 | variable value; 2500 | eval_exp(value); 2501 | 2502 | contents.push_back(value.get()); 2503 | 2504 | get_token(); 2505 | if(token_.type() != token::COMMA) { 2506 | break; 2507 | } 2508 | } 2509 | } 2510 | 2511 | test_token(token::RBRACKET); 2512 | 2513 | return variable(std::move(contents)); 2514 | } 2515 | 2516 | //----------------------------------------------------------------------------- 2517 | // Name: function_literal 2518 | //----------------------------------------------------------------------------- 2519 | variable quixey::function_literal() { 2520 | 2521 | get_token(); 2522 | test_token(token::LPAREN); 2523 | 2524 | // track the program counter's original location 2525 | const address_t function_location = program_counter_; 2526 | 2527 | // we just saw an lparen, get next token... 2528 | // it should be either a type or an rparen 2529 | get_token(); 2530 | 2531 | unsigned int param_count = 0; 2532 | 2533 | while(token_.type() != token::RPAREN) { 2534 | 2535 | // while we are at it, get some basic data on params 2536 | if(is_type(token_)) { 2537 | ++param_count; 2538 | 2539 | get_token(); 2540 | 2541 | // if we didn't read an identifier (the name of the variable) 2542 | test_token(token::IDENTIFIER); 2543 | 2544 | get_token(); 2545 | if(token_.type() == token::COMMA) { 2546 | get_token(); 2547 | } else if(token_.type() != token::RPAREN) { 2548 | throw paren_expected(); 2549 | } 2550 | } else { 2551 | throw type_expected(); 2552 | } 2553 | } 2554 | 2555 | auto partial_value = variable(function(token::AUTO, function_location, param_count)); 2556 | 2557 | // the token following a function's right paren should ALWAYS be 2558 | // a left brace 2559 | get_token(); 2560 | test_token(token::LBRACE); 2561 | int brace = 1; 2562 | 2563 | while(brace != 0) { 2564 | // bypass code inside functions 2565 | get_token(); 2566 | if(token_.type() == token::LBRACE) ++brace; 2567 | else if(token_.type() == token::RBRACE) --brace; 2568 | } 2569 | 2570 | return partial_value; 2571 | } 2572 | 2573 | //----------------------------------------------------------------------------- 2574 | // Name: current_token 2575 | //----------------------------------------------------------------------------- 2576 | const token &quixey::current_token() const { 2577 | return token_; 2578 | } 2579 | -------------------------------------------------------------------------------- /quixey.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef QUIXEY_20060121_H_ 3 | #define QUIXEY_20060121_H_ 4 | 5 | #include "function.h" 6 | #include "script_types.h" 7 | #include "token.h" 8 | #include "variable.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | struct local_variable { 19 | std::string name; 20 | variable value; 21 | }; 22 | 23 | class quixey { 24 | public: 25 | quixey(); 26 | 27 | public: 28 | void eval_exp(variable &value); 29 | 30 | private: 31 | void eval_exp0(variable &value); // Process an assignment expression 32 | void eval_exp1(variable &value); // Process comparitive operators 33 | void eval_exp2(variable &value); // Process binary operators 34 | void eval_exp3(variable &value); // Process relational operators 35 | void eval_exp4(variable &value); // Process shift operators 36 | void eval_exp5(variable &value); // Add or subtract two terms 37 | void eval_exp6(variable &value); // Multiply or divide two factors 38 | void eval_exp7(variable &value); // Is a unary +, - or ! 39 | void eval_exp8(variable &value); // Process parenthesized expression 40 | void atom(variable &value); 41 | 42 | private: 43 | token token_; 44 | variable return_value_; 45 | 46 | private: 47 | typedef std::function builtin_t; 48 | typedef std::unordered_map globals_t; 49 | typedef std::deque> locals_t; 50 | typedef std::unordered_map builtin_functions_t; 51 | typedef std::unordered_map functions_t; 52 | typedef std::unordered_map keywords_t; 53 | 54 | private: 55 | std::unordered_set types_; 56 | keywords_t keywords_; 57 | builtin_functions_t builtin_functions_; 58 | std::vector program_; 59 | std::stack call_stack_; 60 | 61 | private: 62 | // cleared on reset 63 | globals_t global_variables_; 64 | functions_t functions_; 65 | address_t program_counter_; 66 | std::stack function_variables_; 67 | int block_depth_; 68 | bool prescan_; 69 | std::stack imports_; 70 | 71 | public: 72 | const token ¤t_token() const; 73 | token &get_token(); 74 | void reset(); 75 | void load_program(const std::string &name); 76 | int exec(const std::string &function); 77 | int exec(const std::string &function, const std::vector &args); 78 | 79 | private: 80 | void dump_tokens(); 81 | void prescan(); 82 | void tokenize(std::vector::const_iterator first, std::vector::const_iterator last); 83 | void import_code(std::string name); 84 | address_t pop_function(); 85 | bool is_keyword(const std::string &s) const; 86 | bool is_type(const std::string &s) const; 87 | bool is_type(const token &tok) const; 88 | bool is_variable(const std::string &name) const; 89 | bool is_variable(const token &tok) const; 90 | bool is_function(const std::string &name) const; 91 | bool is_function(const token &tok) const; 92 | const function &get_function(const std::string &name) const; 93 | int exec_do(); 94 | int exec_for(); 95 | int exec_for_body(); 96 | int exec_foreach_body(variable &it); 97 | int exec_if(); 98 | int exec_return(); 99 | int exec_while(); 100 | int interpret_block(); 101 | std::vector get_arguments(); 102 | token &peek_token(); 103 | token::Type get_keyword(const std::string &s) const; 104 | variable &get_variable(const std::string &name); 105 | variable call(); 106 | variable call(const std::vector &args); 107 | variable call(const function &func); 108 | variable call(const function &func, const std::vector &args); 109 | variable call(const std::string &name); 110 | variable call(const std::string &name, const std::vector &args); 111 | variable function_literal(); 112 | variable array_literal(); 113 | void create_scope(); 114 | void declare_function(const std::string &name, int return_type, int &brace); 115 | variable &declare_global(); 116 | variable &declare_local(); 117 | void destroy_scope(); 118 | void find_eob(); 119 | std::vector get_parameter_metadata(const std::vector &arguments); 120 | void push_function(); 121 | void push_global(const variable &v, const std::string &name); 122 | void push_local(const variable &v, const std::string &name); 123 | void put_back(); 124 | std::vector load_preprocessed_file(const std::string &name); 125 | bool do_assignment(variable &var_ref, variable &value, address_t restore_point); 126 | 127 | template 128 | variable &declare_variable(F func); 129 | 130 | template 131 | token process_token(In first, In &it, In end) const; 132 | 133 | public: 134 | template 135 | void test_token(token::Type expected) const { 136 | if (current_token().type() != expected) { 137 | throw T(); 138 | } 139 | } 140 | 141 | template 142 | void register_function(const std::string &name, F func); 143 | }; 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /runtime_error.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef RUNTIME_ERROR_20111021_H_ 3 | #define RUNTIME_ERROR_20111021_H_ 4 | 5 | #include "error.h" 6 | 7 | struct builtins_cannot_be_entry_points : error { 8 | virtual const char *what() const throw() { 9 | return "builtins_cannot_be_entry_points"; 10 | } 11 | }; 12 | 13 | struct runtime_error : error { 14 | virtual const char *what() const throw() { 15 | return "runtime_error"; 16 | } 17 | }; 18 | 19 | struct divide_by_zero : runtime_error { 20 | virtual const char *what() const throw() { 21 | return "divide_by_zero"; 22 | } 23 | }; 24 | 25 | struct uninitialized_variable_used : runtime_error { 26 | virtual const char *what() const throw() { 27 | return "uninitialized_variable_used"; 28 | } 29 | }; 30 | 31 | struct variable_not_found : runtime_error { 32 | virtual const char *what() const throw() { 33 | return "variable_not_found"; 34 | } 35 | }; 36 | 37 | struct incorrect_param_count : runtime_error { 38 | virtual const char *what() const throw() { 39 | return "incorrect_param_count"; 40 | } 41 | }; 42 | 43 | struct duplicate_function : runtime_error { 44 | virtual const char *what() const throw() { 45 | return "duplicate_function"; 46 | } 47 | }; 48 | 49 | struct duplicate_global : runtime_error { 50 | virtual const char *what() const throw() { 51 | return "duplicate_global"; 52 | } 53 | }; 54 | 55 | struct duplicate_local : runtime_error { 56 | virtual const char *what() const throw() { 57 | return "duplicate_local"; 58 | } 59 | }; 60 | 61 | struct undefined_function : syntax_error { 62 | virtual const char *what() const throw() { 63 | return "undefined_function"; 64 | } 65 | }; 66 | 67 | struct unknown_identifier : syntax_error { 68 | virtual const char *what() const throw() { 69 | return "unknown_identifier"; 70 | } 71 | }; 72 | 73 | struct out_of_bounds : runtime_error { 74 | virtual const char *what() const throw() { 75 | return "out_of_bounds"; 76 | } 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /script_types.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SCRIPT_TYPES_20060121_H_ 3 | #define SCRIPT_TYPES_20060121_H_ 4 | 5 | typedef unsigned long address_t; 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /string_util.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef STRING_UTIL_20060121_H_ 3 | #define STRING_UTIL_20060121_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | //------------------------------------------------------------------------------ 10 | // Name: rtrim 11 | //------------------------------------------------------------------------------ 12 | inline void rtrim(std::string &s) { 13 | s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); 14 | } 15 | 16 | //------------------------------------------------------------------------------ 17 | // Name: ltrim 18 | //------------------------------------------------------------------------------ 19 | inline void ltrim(std::string &s) { 20 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); 21 | } 22 | 23 | //------------------------------------------------------------------------------ 24 | // Name: trim 25 | //------------------------------------------------------------------------------ 26 | inline void trim(std::string &s) { 27 | ltrim(s); 28 | rtrim(s); 29 | } 30 | 31 | //------------------------------------------------------------------------------ 32 | // Name: rtrim_copy 33 | //------------------------------------------------------------------------------ 34 | inline std::string rtrim_copy(std::string s) { 35 | rtrim(s); 36 | return s; 37 | } 38 | 39 | //------------------------------------------------------------------------------ 40 | // Name: ltrim_copy 41 | //------------------------------------------------------------------------------ 42 | inline std::string ltrim_copy(std::string s) { 43 | ltrim(s); 44 | return s; 45 | } 46 | 47 | //------------------------------------------------------------------------------ 48 | // Name: trim_copy 49 | //------------------------------------------------------------------------------ 50 | inline std::string trim_copy(std::string &s) { 51 | trim(s); 52 | return s; 53 | } 54 | 55 | //------------------------------------------------------------------------------ 56 | // Name: starts_with 57 | //------------------------------------------------------------------------------ 58 | inline bool starts_with(const std::string &s, char ch) { 59 | return !s.empty() && s.front() == ch; 60 | } 61 | 62 | //------------------------------------------------------------------------------ 63 | // Name: starts_with 64 | //------------------------------------------------------------------------------ 65 | inline bool starts_with(const std::string &s, const std::string &prefix) { 66 | return std::mismatch(prefix.begin(), prefix.end(), s.begin()).first == prefix.end(); 67 | } 68 | 69 | //------------------------------------------------------------------------------ 70 | // Name: ends_with 71 | //------------------------------------------------------------------------------ 72 | inline bool ends_with(const std::string &s, char ch) { 73 | return !s.empty() && s.back() == ch; 74 | } 75 | 76 | //------------------------------------------------------------------------------ 77 | // Name: ends_with 78 | //------------------------------------------------------------------------------ 79 | inline bool ends_with(const std::string &s, const std::string &suffix) { 80 | return std::mismatch(suffix.rbegin(), suffix.rend(), s.rbegin()).first == suffix.rend(); 81 | } 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /string_variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "string_variable.h" 3 | #include "char_variable.h" 4 | #include "function.h" 5 | #include "error.h" 6 | #include 7 | #include 8 | 9 | variable_base::pointer string_variable::clone() const { 10 | return std::make_shared(*this); 11 | } 12 | 13 | variable_base &string_variable::operator=(const variable_base &rhs) { 14 | value_ = rhs.to_string(); 15 | return *this; 16 | } 17 | 18 | variable_base &string_variable::operator+=(const variable_base &rhs) { 19 | value_ += rhs.to_string(); 20 | return *this; 21 | } 22 | 23 | variable_base &string_variable::operator-=(const variable_base &rhs) { 24 | (void)rhs; 25 | throw invalid_operation_for_type(); 26 | } 27 | 28 | variable_base &string_variable::operator*=(const variable_base &rhs) { 29 | (void)rhs; 30 | throw invalid_operation_for_type(); 31 | } 32 | 33 | variable_base &string_variable::operator/=(const variable_base &rhs) { 34 | (void)rhs; 35 | throw invalid_operation_for_type(); 36 | } 37 | 38 | variable_base &string_variable::operator&=(const variable_base &rhs) { 39 | (void)rhs; 40 | throw invalid_operation_for_type(); 41 | } 42 | 43 | variable_base &string_variable::operator|=(const variable_base &rhs) { 44 | (void)rhs; 45 | throw invalid_operation_for_type(); 46 | } 47 | 48 | variable_base &string_variable::operator^=(const variable_base &rhs) { 49 | (void)rhs; 50 | throw invalid_operation_for_type(); 51 | } 52 | 53 | variable_base &string_variable::operator%=(const variable_base &rhs) { 54 | (void)rhs; 55 | throw invalid_operation_for_type(); 56 | } 57 | 58 | variable_base &string_variable::operator>>=(const variable_base &rhs) { 59 | (void)rhs; 60 | throw invalid_operation_for_type(); 61 | } 62 | 63 | variable_base &string_variable::operator<<=(const variable_base &rhs) { 64 | (void)rhs; 65 | throw invalid_operation_for_type(); 66 | } 67 | 68 | variable_base::pointer string_variable::operator+() const { 69 | throw invalid_operation_for_type(); 70 | } 71 | 72 | variable_base::pointer string_variable::operator-() const { 73 | throw invalid_operation_for_type(); 74 | } 75 | 76 | variable_base::pointer string_variable::operator!() const { 77 | throw invalid_operation_for_type(); 78 | } 79 | 80 | variable_base::pointer string_variable::operator~() const { 81 | throw invalid_operation_for_type(); 82 | } 83 | 84 | int string_variable::size() const { 85 | return value_.size(); 86 | } 87 | 88 | char string_variable::to_character() const { 89 | throw invalid_type_conversion(); 90 | } 91 | 92 | int string_variable::to_integer() const { 93 | throw invalid_type_conversion(); 94 | } 95 | 96 | std::string string_variable::to_string() const { 97 | return value_; 98 | } 99 | 100 | function string_variable::to_function() const { 101 | throw invalid_type_conversion(); 102 | } 103 | 104 | std::vector string_variable::to_array() const { 105 | throw invalid_type_conversion(); 106 | } 107 | 108 | int string_variable::compare(const variable_base &rhs) const { 109 | if(const string_variable *const x = dynamic_cast(&rhs)) { 110 | if(value_ < x->value_) { 111 | return -1; 112 | } else if(value_ > x->value_) { 113 | return 1; 114 | } else { 115 | return 0; 116 | } 117 | } 118 | 119 | throw invalid_type_conversion(); 120 | } 121 | 122 | variable_base::pointer string_variable::operator[](const variable_base &index) const { 123 | size_t n = index.to_integer(); 124 | if(n >= value_.size()) { 125 | throw out_of_bounds(); 126 | } 127 | return std::make_shared(value_[n]); 128 | } 129 | 130 | 131 | function string_variable::operator()() const { 132 | throw invalid_operation_for_type(); 133 | } 134 | -------------------------------------------------------------------------------- /string_variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef STRING_VARIABLE_10012011_H_ 3 | #define STRING_VARIABLE_10012011_H_ 4 | 5 | #include "variable_base.h" 6 | 7 | class string_variable : public variable_base { 8 | public: 9 | explicit string_variable(const std::string &value) : value_(value) { 10 | } 11 | 12 | public: 13 | virtual pointer clone() const; 14 | 15 | public: 16 | virtual variable_base &operator=(const variable_base &rhs); 17 | virtual variable_base &operator+=(const variable_base &rhs); 18 | virtual variable_base &operator-=(const variable_base &rhs); 19 | virtual variable_base &operator*=(const variable_base &rhs); 20 | virtual variable_base &operator/=(const variable_base &rhs); 21 | virtual variable_base &operator&=(const variable_base &rhs); 22 | virtual variable_base &operator|=(const variable_base &rhs); 23 | virtual variable_base &operator^=(const variable_base &rhs); 24 | virtual variable_base &operator%=(const variable_base &rhs); 25 | virtual variable_base &operator>>=(const variable_base &rhs); 26 | virtual variable_base &operator<<=(const variable_base &rhs); 27 | 28 | public: 29 | virtual pointer operator[](const variable_base &index) const; 30 | 31 | public: 32 | virtual int size() const; 33 | 34 | public: 35 | virtual pointer operator+() const; 36 | virtual pointer operator-() const; 37 | virtual pointer operator!() const; 38 | virtual pointer operator~() const; 39 | 40 | public: 41 | virtual function operator()() const; 42 | 43 | public: 44 | int compare(const variable_base &rhs) const; 45 | 46 | public: 47 | virtual char to_character() const; 48 | virtual int to_integer() const; 49 | virtual std::string to_string() const; 50 | virtual function to_function() const; 51 | virtual std::vector to_array() const; 52 | 53 | public: 54 | virtual bool is_character() const { return false; } 55 | virtual bool is_integer() const { return false; } 56 | virtual bool is_string() const { return true; } 57 | virtual bool is_function() const { return false; } 58 | virtual bool is_array() const { return false; } 59 | 60 | private: 61 | std::string value_; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /syntax_error.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SYTAX_ERROR_20111021_H_ 3 | #define SYTAX_ERROR_20111021_H_ 4 | 5 | #include "error.h" 6 | 7 | struct unable_to_read_file : error { 8 | virtual const char *what() const throw() { 9 | return "unable_to_read_file"; 10 | } 11 | }; 12 | 13 | struct syntax_error : error { 14 | virtual const char *what() const throw() { 15 | return "syntax_error"; 16 | } 17 | }; 18 | 19 | struct unexpected_eof : syntax_error { 20 | virtual const char *what() const throw() { 21 | return "unexpected_eof"; 22 | } 23 | }; 24 | 25 | struct semicolon_expected : syntax_error { 26 | virtual const char *what() const throw() { 27 | return "semicolon_expected"; 28 | } 29 | }; 30 | 31 | struct type_expected : syntax_error { 32 | virtual const char *what() const throw() { 33 | return "type_expected"; 34 | } 35 | }; 36 | 37 | struct paren_expected : syntax_error { 38 | virtual const char *what() const throw() { 39 | return "paren_expected"; 40 | } 41 | }; 42 | 43 | struct colon_expected : syntax_error { 44 | virtual const char *what() const throw() { 45 | return "colon_expected"; 46 | } 47 | }; 48 | 49 | struct brace_expected : syntax_error { 50 | virtual const char *what() const throw() { 51 | return "brace_expected"; 52 | } 53 | }; 54 | 55 | struct bracket_expected : syntax_error { 56 | virtual const char *what() const throw() { 57 | return "bracket_expected"; 58 | } 59 | }; 60 | 61 | struct comma_expected : syntax_error { 62 | virtual const char *what() const throw() { 63 | return "comma_expected"; 64 | } 65 | }; 66 | 67 | struct while_expected : syntax_error { 68 | virtual const char *what() const throw() { 69 | return "while_expected"; 70 | } 71 | }; 72 | 73 | struct quote_expected : syntax_error { 74 | virtual const char *what() const throw() { 75 | return "quote_expected"; 76 | } 77 | }; 78 | 79 | struct string_expected : syntax_error { 80 | virtual const char *what() const throw() { 81 | return "string_expected"; 82 | } 83 | }; 84 | 85 | struct identifier_expected : syntax_error { 86 | virtual const char *what() const throw() { 87 | return "identifier_expected"; 88 | } 89 | }; 90 | 91 | struct multiline_string_literal : syntax_error { 92 | virtual const char *what() const throw() { 93 | return "multiline_string_literal"; 94 | } 95 | }; 96 | 97 | struct unimplemented_keyword : syntax_error { 98 | virtual const char *what() const throw() { 99 | return "unimplemented_keyword"; 100 | } 101 | }; 102 | 103 | struct unimplemented_operator : syntax_error { 104 | virtual const char *what() const throw() { 105 | return "unimplemented_operator"; 106 | } 107 | }; 108 | 109 | struct invalid_operation_for_type : syntax_error { 110 | virtual const char *what() const throw() { 111 | return "invalid_operation_for_type"; 112 | } 113 | }; 114 | 115 | struct invalid_type_conversion : syntax_error { 116 | virtual const char *what() const throw() { 117 | return "invalid_type_conversion"; 118 | } 119 | }; 120 | 121 | struct return_outside_call : syntax_error { 122 | virtual const char *what() const throw() { 123 | return "return_outside_call"; 124 | } 125 | }; 126 | 127 | struct function_during_prescan : syntax_error { 128 | virtual const char *what() const throw() { 129 | return "function_during_prescan"; 130 | } 131 | }; 132 | 133 | struct function_name_is_keyword : syntax_error { 134 | virtual const char *what() const throw() { 135 | return "function_name_is_keyword"; 136 | } 137 | }; 138 | 139 | struct variable_name_is_keyword : syntax_error { 140 | virtual const char *what() const throw() { 141 | return "variable_name_is_keyword"; 142 | } 143 | }; 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /test/import.inc: -------------------------------------------------------------------------------- 1 | /* this is just a test */ 2 | int x = 30; 3 | 4 | int func() { 5 | printf("%d\n", x); 6 | } 7 | -------------------------------------------------------------------------------- /test/input/test1.txt: -------------------------------------------------------------------------------- 1 | 10 2 | 0 3 | 4 | -------------------------------------------------------------------------------- /test/input/test2.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test2.txt -------------------------------------------------------------------------------- /test/input/test3.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test3.txt -------------------------------------------------------------------------------- /test/input/test4.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test4.txt -------------------------------------------------------------------------------- /test/input/test5.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test5.txt -------------------------------------------------------------------------------- /test/input/test6.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test6.txt -------------------------------------------------------------------------------- /test/input/test7.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test7.txt -------------------------------------------------------------------------------- /test/input/test8.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/quixey/2847f29742679c0839e9df0c99d9e9f0288b03f0/test/input/test8.txt -------------------------------------------------------------------------------- /test/output/test1.txt: -------------------------------------------------------------------------------- 1 | 15 2 | 5,10,A,TESTING 3 | -1 4 | z = 78 5 | 0 % 6 | TESING LOCALS 1 = 1 7 | TESING LOCALS 2 = 2 8 | CHAR TEST 'A' 9 | 5 10 | z = 78 11 | tesing escape sequences: '"\ 12 | 13 | d = 4660 14 | 15 | d is something else! 16 | quixey Demo 17 | 18 | ABCDEFGHIJKLMNOPQRSTUVWXYZ 19 | testing testing 123 20 | testing assignment 21 | a == 2 22 | b == 6 23 | 24 | testing left shift operator 25 | [a <<= 3] == 16 26 | 27 | testing several bitwise operations 28 | [a = ( ~a ^ b << 1) >> 1] == -15 29 | 30 | testing bitwise AND 31 | [a &= 7] == 1 32 | 33 | testing assignment 34 | a == 456 35 | b == 123 36 | 37 | testing XOR swap trick 38 | a == 123 39 | b == 456 40 | 41 | testing multiple assignment.. 42 | a == 5 43 | b == 5 44 | 45 | this number of bits set in a is: 2 46 | 47 | enter a number (0 to quit): 48 | 1 summed is 1 49 | 2 summed is 3 50 | 3 summed is 6 51 | 4 summed is 10 52 | 5 summed is 15 53 | 6 summed is 21 54 | 7 summed is 28 55 | 8 summed is 36 56 | 9 summed is 45 57 | 10 summed is 55 58 | enter a number (0 to quit): 59 | -------------------------------------------------------------------------------- /test/output/test2.txt: -------------------------------------------------------------------------------- 1 | hello 2 | 10 10 3 | hello, Evan 4 | hello, Evan 5 | STRING! 6 | AAAAAAAAAA 7 | AAAAAAAAAA 8 | 0 1 0 0 9 | 0 0 0 0 10 | hello ABSS 11 | -------------------------------------------------------------------------------- /test/output/test3.txt: -------------------------------------------------------------------------------- 1 | A world 2 | -------------------------------------------------------------------------------- /test/output/test4.txt: -------------------------------------------------------------------------------- 1 | Array( 2 | C: a 3 | C: b 4 | C: c 5 | I: 1234 6 | S: HELLO 7 | Array( 8 | Array( 9 | I: 1 10 | I: 2 11 | I: 3 12 | I: 4 13 | ) 14 | ) 15 | F: W00t! 16 | ) 17 | e 18 | -------------------------------------------------------------------------------- /test/output/test5.txt: -------------------------------------------------------------------------------- 1 | 10 2 | 5 3 | -------------------------------------------------------------------------------- /test/output/test6.txt: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /test/output/test7.txt: -------------------------------------------------------------------------------- 1 | Hello World 2 | 30 3 | -------------------------------------------------------------------------------- /test/output/test8.txt: -------------------------------------------------------------------------------- 1 | 3 2 | 10 3 | -------------------------------------------------------------------------------- /test/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DIR=$(cd "$(dirname "${BASH_SOURCE[0]}" )" && pwd) 4 | 5 | for FILE in $(ls $DIR/*.qc); do 6 | TEST=$(basename $FILE .qc) 7 | 8 | cat $DIR/input/$TEST.txt | $DIR/../build/quixey $DIR/$TEST.qc | diff - $DIR/output/$TEST.txt 9 | R=$? 10 | if [ $R -ne 0 ]; then 11 | echo "$TEST FAILED!" 12 | exit 1 13 | fi 14 | done 15 | 16 | echo "PASS" 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /test/test1.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | /* 4 | * this is a multi-line C-style comment 5 | */ 6 | 7 | # this is a shell style comment 8 | 9 | int i, j, k; 10 | int ret = 5, l = 1 | 2; 11 | int G = ret + 10; // globals can reference previously declared globals! 12 | 13 | //----------------------------------------------------------------------------- 14 | // Name: func 15 | //----------------------------------------------------------------------------- 16 | int func() { 17 | int i = 2; 18 | do { 19 | int i = 1; 20 | { 21 | printf("TESING LOCALS 1 = %d\n", i); 22 | } 23 | } while(0); 24 | printf("TESING LOCALS 2 = %d\n", i); 25 | } 26 | 27 | //----------------------------------------------------------------------------- 28 | // Name: test 29 | //----------------------------------------------------------------------------- 30 | int test(int a, int b, char c, string d) { 31 | printf("%d,%d,%c,%s\n", a, b, c, d); 32 | } 33 | 34 | //----------------------------------------------------------------------------- 35 | // Name: main 36 | //----------------------------------------------------------------------------- 37 | int main() { 38 | 39 | printf("%d\n", G); 40 | 41 | test(5, 10, 'A', "TESTING"); 42 | 43 | int i, j; 44 | int a, b; 45 | int d = 0x1234; 46 | int q = 0xffffffff; 47 | 48 | printf("%d\n", q); 49 | 50 | char z = '\x12345678'; 51 | 52 | printf("z = %x\n", z); 53 | 54 | char ch = '\xff'; 55 | ch += 1; 56 | printf("%d %%\n", ch); 57 | 58 | func(); 59 | 60 | printf("CHAR TEST '%c'\n", 'A'); 61 | 62 | if(0) { 63 | // we should be able to report this non-existant function call 64 | // but we can't because we only check for validity of syntactically 65 | // correct code when we execute the code... which never gets reached 66 | // here. 67 | // we could add a second pass which would check for calls to functions 68 | // which don't exist 69 | fun1234(); 70 | } else { 71 | } 72 | 73 | 74 | i = -(-5); 75 | 76 | printf("%d\n", i); 77 | 78 | 79 | printf("z = %x\n", z); 80 | 81 | puts("tesing escape sequences: \'\"\\\a\b\f\n\t\v"); 82 | 83 | // this would cause a duplicate declaration error if it were uncommented 84 | // can't have two locals of the same name in the same scope! 85 | // int i; 86 | 87 | printf("d = %d\n\n", d); 88 | 89 | if(d == 1) { 90 | puts("d is 1!"); 91 | } else if(d == 0) { 92 | puts("d is 0!"); 93 | } else { 94 | puts("d is something else!"); 95 | } 96 | 97 | // should support arbitrary sub blocks for better scope definition 98 | { 99 | { 100 | { 101 | { 102 | 103 | // output a title 104 | puts("quixey Demo\n"); 105 | } 106 | } 107 | } 108 | } 109 | 110 | // test declaration inside a loop 111 | // this should effectivly declare and destroy 112 | // the local var e 1000 times 113 | for(d = 0; d < 1000; d += 1) { 114 | int e; 115 | } 116 | 117 | // by the way, you can intersperse declarations and code 118 | // it is actually harder to enforce them being first then just allowing it.. 119 | int c; 120 | 121 | // print out alphabet 122 | print_alpha(); 123 | 124 | puts("testing testing 123"); 125 | puts("testing assignment"); 126 | 127 | a = 2; 128 | b = 6; 129 | 130 | printf("a == %d\n", a); 131 | printf("b == %d\n\n", b); 132 | 133 | puts("testing left shift operator"); 134 | a <<= 3; 135 | printf("[a <<= 3] == %d\n\n", a); 136 | 137 | puts("testing several bitwise operations"); 138 | a = ( ~a ^ b << 1) >> 1; 139 | 140 | printf("[a = ( ~a ^ b << 1) >> 1] == %d\n\n", a); 141 | 142 | puts("testing bitwise AND"); 143 | a &= 7; 144 | printf("[a &= 7] == %d\n\n", a); 145 | 146 | puts("testing assignment"); 147 | a = 456; 148 | b = 123; 149 | printf("a == %d\n", a); 150 | printf("b == %d\n\n", b); 151 | 152 | puts("testing XOR swap trick"); 153 | a ^= b; 154 | b ^= a; 155 | a ^= b; 156 | printf("a == %d\n", a); 157 | printf("b == %d\n\n", b); 158 | 159 | puts("testing multiple assignment.."); 160 | a = b = 5; 161 | printf("a == %d\n", a); 162 | printf("b == %d\n\n", b); 163 | 164 | printf("this number of bits set in a is: %d\n\n", bits_set(a)); 165 | 166 | do { 167 | puts("enter a number (0 to quit):"); 168 | 169 | i = getnum(); 170 | 171 | if(i < 0) { 172 | puts("numbers must be positive, try again"); 173 | } else { 174 | 175 | for(j = 1; j <= i; j += 1) { 176 | printf("%d summed is %d\n", j, sum(j)); 177 | } 178 | } 179 | } while(i != 0); 180 | } 181 | 182 | //----------------------------------------------------------------------------- 183 | // Name: sum 184 | //----------------------------------------------------------------------------- 185 | int sum(int num) { 186 | int running_sum = 0; 187 | 188 | while(num) { 189 | running_sum += num; 190 | num -= 1; 191 | } 192 | 193 | return running_sum; 194 | } 195 | 196 | //----------------------------------------------------------------------------- 197 | // Name: print_alpha 198 | //----------------------------------------------------------------------------- 199 | int print_alpha() { 200 | for(char ch : "ABCDEFGHIJKLMNOPQRSTUVWXYZ") { 201 | putchar(ch); 202 | } 203 | 204 | putchar('\n'); 205 | } 206 | 207 | //----------------------------------------------------------------------------- 208 | // Name: bits_set 209 | //----------------------------------------------------------------------------- 210 | int bits_set(int value) { 211 | 212 | // this is a global.. 213 | ret = 0; 214 | while(value) { 215 | ret += 1; 216 | value &= (value - 1); 217 | } 218 | return ret; 219 | } 220 | -------------------------------------------------------------------------------- /test/test2.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | string hello(auto x) { 4 | return "hello, " + x; 5 | } 6 | 7 | int f(auto x) { 8 | 9 | if(is_string(x)) { 10 | for(auto ch : x) { 11 | printf("%c", ch); 12 | } 13 | printf("\n"); 14 | } 15 | 16 | return 10; 17 | } 18 | 19 | int main() { 20 | string s = hello("Evan"); 21 | 22 | auto z = f(20); 23 | auto h = f("hello"); 24 | 25 | printf("%d %d\n", z, h); 26 | 27 | for(int i = 0; i < size(s); i += 1) { 28 | printf("%c", s[i]); 29 | } 30 | printf("\n"); 31 | 32 | f(s); 33 | 34 | if(is_string(s)) { 35 | printf("STRING!\n"); 36 | } else { 37 | printf("NO?!\n"); 38 | } 39 | 40 | auto undefined; 41 | 42 | 43 | int nn = 10; 44 | while(nn > 0) { 45 | putchar('A'); 46 | nn = nn - 1; 47 | } 48 | 49 | printf("\n"); 50 | 51 | nn = 10; 52 | do { 53 | putchar('A'); 54 | nn = nn - 1; 55 | } while(nn > 0); 56 | 57 | printf("\n"); 58 | 59 | printf("%d %d %d %d\n", is_integer(s), is_string(s), is_character(s), is_array(s)); 60 | printf("%d %d %d %d\n", is_integer(undefined), is_string(undefined), is_character(undefined), is_array(undefined)); 61 | 62 | 63 | char ch = int(0x42); 64 | 65 | string ss = "hello "; 66 | ss += char(0x41); 67 | ss += ch; 68 | ss += "SS"; 69 | puts(ss); 70 | 71 | } 72 | -------------------------------------------------------------------------------- /test/test3.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | int main() { 4 | auto x = 'A' + " world"; 5 | puts(x); 6 | } 7 | -------------------------------------------------------------------------------- /test/test4.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | auto dump_array(auto a) { 4 | 5 | if(is_array(a)) { 6 | printf("Array(\n"); 7 | for(auto e : a) { 8 | if(is_integer(e)) { 9 | printf("I: %d\n", e); 10 | } 11 | 12 | if(is_character(e)) { 13 | printf("C: %c\n", e); 14 | } 15 | 16 | if(is_string(e)) { 17 | printf("S: %s\n", e); 18 | } 19 | 20 | if(is_function(e)) { 21 | printf("F: "); 22 | e(); 23 | } 24 | 25 | if(is_array(e)) { 26 | printf("Array(\n"); 27 | dump_array(e); 28 | printf(")\n"); 29 | } 30 | } 31 | printf(")\n"); 32 | } 33 | } 34 | 35 | int main() { 36 | auto x = ['a', 'b', 'c', 1234, "HELLO", [1, 2, 3, 4], function() { printf("W00t!\n"); }]; 37 | dump_array(x); 38 | printf("%c\n", "test"[1]); 39 | } 40 | -------------------------------------------------------------------------------- /test/test5.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | int f(int s) { 4 | s = 10; 5 | printf("%d\n", s); 6 | } 7 | int main() { 8 | int s = 5; 9 | f(s); 10 | printf("%d\n", s); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/test6.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | int main() { 4 | string s = "World!"; 5 | 6 | auto f = function(auto x) { 7 | x = "Hello"; 8 | printf("%s, ", x); 9 | }; 10 | 11 | f(s); 12 | puts(s); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /test/test7.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | @import "import.inc" 4 | 5 | int main() { 6 | puts("Hello World"); 7 | func(); 8 | } 9 | -------------------------------------------------------------------------------- /test/test8.qc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/quixey 2 | 3 | int main() { 4 | 5 | auto arr = [1, 2, 3, 4, 5]; 6 | printf("%d\n", arr[2]); 7 | arr[2] = 10; 8 | printf("%d\n", arr[2]); 9 | } 10 | -------------------------------------------------------------------------------- /token.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "token.h" 3 | #include "error.h" 4 | 5 | token::token(Type type, const std::string &string) : string_(string), token_type_(type), line_number(-1) { 6 | } 7 | 8 | token::token(Type type) : token(type, "") { 9 | } 10 | 11 | token::token() : token_type_(UNKNOWN), line_number(-1) { 12 | } 13 | 14 | token &token::operator=(const token &rhs) { 15 | string_ = rhs.string_; 16 | token_type_ = rhs.token_type_; 17 | filename = rhs.filename; 18 | line_number = rhs.line_number; 19 | return *this; 20 | } 21 | 22 | token::token(const token &other) : string_(other.string_), token_type_(other.token_type_), filename(other.filename), line_number(other.line_number) { 23 | } 24 | 25 | std::string to_string(const token &t) { 26 | switch(t.type()) { 27 | case token::AND: return "&"; 28 | case token::AND_EQ: return "&="; 29 | case token::ASSIGN: return "="; 30 | case token::AUTO: return "auto"; 31 | case token::BREAK: return "break"; 32 | case token::CASE: return "case"; 33 | case token::CHAR: return "char"; 34 | case token::CMP: return "~"; 35 | case token::COLON: return ":"; 36 | case token::COMMA: return ","; 37 | case token::CONTINUE: return "continue"; 38 | case token::DIV: return "/"; 39 | case token::DIV_EQ: return "/="; 40 | case token::DO: return "do"; 41 | case token::DOT: return "."; 42 | case token::DOUBLECOLON: return "::"; 43 | case token::ELSE: return "else"; 44 | case token::EQ: return "=="; 45 | case token::FOR: return "for"; 46 | case token::FUNCTION: return "function"; 47 | case token::GE: return ">="; 48 | case token::GT: return ">"; 49 | case token::IF: return "if"; 50 | case token::INT: return "int"; 51 | case token::LBRACE: return "{"; 52 | case token::LBRACKET: return "["; 53 | case token::LE: return "<="; 54 | case token::LOGICAL_AND: return "&&"; 55 | case token::LOGICAL_OR: return "||"; 56 | case token::LPAREN: return "("; 57 | case token::LSHIFT: return "<<"; 58 | case token::LSHIFT_EQ: return "<<="; 59 | case token::LT: return "<"; 60 | case token::MINUS: return "-"; 61 | case token::MINUS_EQ: return "-="; 62 | case token::MOD: return "%"; 63 | case token::MOD_EQ: return "%="; 64 | case token::MUL: return "*"; 65 | case token::MUL_EQ: return "*="; 66 | case token::NE: return "!="; 67 | case token::NOT: return "!"; 68 | case token::OR: return "|"; 69 | case token::OR_EQ: return "|="; 70 | case token::PLUS: return "+"; 71 | case token::PLUS_EQ: return "+="; 72 | case token::RBRACE: return "}"; 73 | case token::RBRACKET: return "]"; 74 | case token::RETURN: return "return "; 75 | case token::RPAREN: return ")"; 76 | case token::RSHIFT: return ">>"; 77 | case token::RSHIFT_EQ: return ">>="; 78 | case token::SEMICOLON: return ";"; 79 | case token::SINGLEQUOTE: return "'"; 80 | case token::STRING: return "string"; 81 | case token::SWITCH: return "switch"; 82 | case token::WHILE: return "while"; 83 | case token::XOR: return "^"; 84 | case token::XOR_EQ: return "^="; 85 | case token::FINISHED: return ""; 86 | 87 | case token::IDENTIFIER: 88 | case token::STRING_LITERAL: 89 | case token::CHARACTER: 90 | case token::INTEGER: 91 | return t.string_; 92 | 93 | default: 94 | return ""; 95 | } 96 | } 97 | 98 | std::ostream &operator<<(std::ostream &o, const token &t) { 99 | return o << to_string(t); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /token.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef TOKEN_20060121_H_ 3 | #define TOKEN_20060121_H_ 4 | 5 | #include "script_types.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class token { 11 | public: 12 | enum Type : uint16_t { 13 | 14 | UNKNOWN = 0xffff, 15 | 16 | // delimiters 17 | DELIMETER = 0x1000, 18 | SEMICOLON = DELIMETER | 0x00, 19 | LPAREN = DELIMETER | 0x01, 20 | RPAREN = DELIMETER | 0x02, 21 | COMMA = DELIMETER | 0x03, 22 | SINGLEQUOTE = DELIMETER | 0x04, 23 | LBRACE = DELIMETER | 0x05, 24 | RBRACE = DELIMETER | 0x06, 25 | LBRACKET = DELIMETER | 0x07, 26 | RBRACKET = DELIMETER | 0x08, 27 | DOT = DELIMETER | 0x09, 28 | COLON = DELIMETER | 0x0a, 29 | DOUBLECOLON = DELIMETER | 0x0b, 30 | 31 | // operators 32 | OPERATOR = 0x2000, 33 | LT = OPERATOR | 0x00, 34 | GT = OPERATOR | 0x01, 35 | PLUS = OPERATOR | 0x02, 36 | MUL = OPERATOR | 0x03, 37 | MOD = OPERATOR | 0x04, 38 | MINUS = OPERATOR | 0x05, 39 | ASSIGN = OPERATOR | 0x06, 40 | DIV = OPERATOR | 0x07, 41 | AND = OPERATOR | 0x08, 42 | OR = OPERATOR | 0x09, 43 | XOR = OPERATOR | 0x0a, 44 | CMP = OPERATOR | 0x0b, 45 | NOT = OPERATOR | 0x0c, 46 | LE = OPERATOR | 0x0d, 47 | GE = OPERATOR | 0x0e, 48 | EQ = OPERATOR | 0x0f, 49 | NE = OPERATOR | 0x10, 50 | LOGICAL_AND = OPERATOR | 0x11, 51 | LOGICAL_OR = OPERATOR | 0x12, 52 | LSHIFT = OPERATOR | 0x13, 53 | RSHIFT = OPERATOR | 0x14, 54 | 55 | PLUS_EQ = OPERATOR | 0x15, 56 | MINUS_EQ = OPERATOR | 0x16, 57 | MUL_EQ = OPERATOR | 0x17, 58 | MOD_EQ = OPERATOR | 0x18, 59 | DIV_EQ = OPERATOR | 0x19, 60 | AND_EQ = OPERATOR | 0x1a, 61 | XOR_EQ = OPERATOR | 0x1b, 62 | OR_EQ = OPERATOR | 0x1c, 63 | RSHIFT_EQ = OPERATOR | 0x1d, 64 | LSHIFT_EQ = OPERATOR | 0x1e, 65 | 66 | // types that a variable can have.. 67 | TYPENAME = 0x3000, 68 | STRING_LITERAL = TYPENAME | 0x00, 69 | INTEGER = TYPENAME | 0x01, 70 | CHARACTER = TYPENAME | 0x02, 71 | 72 | // keywords 73 | KEYWORD = 0x4000, 74 | IF = KEYWORD | 0x00, 75 | ELSE = KEYWORD | 0x01, 76 | FOR = KEYWORD | 0x02, 77 | DO = KEYWORD | 0x03, 78 | WHILE = KEYWORD | 0x04, 79 | CHAR = KEYWORD | 0x05, 80 | INT = KEYWORD | 0x06, 81 | STRING = KEYWORD | 0x07, 82 | RETURN = KEYWORD | 0x08, 83 | BREAK = KEYWORD | 0x09, 84 | CONTINUE = KEYWORD | 0x0a, 85 | SWITCH = KEYWORD | 0x0b, 86 | CASE = KEYWORD | 0x0c, 87 | AUTO = KEYWORD | 0x0d, 88 | FOREACH = KEYWORD | 0x0e, 89 | FUNCTION = KEYWORD | 0x0f, 90 | 91 | IDENTIFIER = 0x5000, 92 | FINISHED = 0x6000 93 | }; 94 | public: 95 | 96 | token(Type token, const std::string &string); 97 | explicit token(Type token); 98 | token(); 99 | token(const token &other); 100 | token &operator=(const token &rhs); 101 | 102 | public: 103 | Type type() const { return token_type_; } 104 | Type type_class() const { return static_cast(token_type_ & 0xf000); } 105 | 106 | private: 107 | 108 | // TODO(eteran): use a string table for string literals, and enable 109 | // string pooling 110 | std::string string_; 111 | Type token_type_; 112 | 113 | public: 114 | // TODO(eteran): use some sort of string pooling to not have LOTS of 115 | // copies of the filename here, perhaps some sort of 116 | // debug info table? 117 | std::string filename; 118 | int line_number; 119 | 120 | private: 121 | friend std::string to_string(const token &t); 122 | }; 123 | 124 | 125 | std::string to_string(const token &t); 126 | 127 | std::ostream &operator<<(std::ostream &o, const token &t); 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /variable.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "variable.h" 3 | #include "array_variable.h" 4 | #include "char_variable.h" 5 | #include "error.h" 6 | #include "function_variable.h" 7 | #include "int_variable.h" 8 | #include "string_variable.h" 9 | #include 10 | #include 11 | 12 | variable variable::create_array() { 13 | return variable(std::make_shared()); 14 | } 15 | 16 | variable variable::create_function() { 17 | return variable(std::make_shared(function())); 18 | } 19 | 20 | variable variable::create_character() { 21 | return variable(std::make_shared('\0')); 22 | } 23 | 24 | variable variable::create_integer() { 25 | return variable(std::make_shared(0)); 26 | } 27 | 28 | variable variable::create_string() { 29 | return variable(std::make_shared("")); 30 | } 31 | 32 | void variable::swap(variable &other) { 33 | using std::swap; 34 | swap(var_, other.var_); 35 | } 36 | 37 | void variable::reset(const variable &other) { 38 | variable(other).swap(*this); 39 | } 40 | 41 | variable::variable(int value) : var_(std::make_shared(value)) { 42 | } 43 | 44 | variable::variable(const std::string &value) : var_(std::make_shared(value)) { 45 | } 46 | 47 | variable::variable(char value) : var_(std::make_shared(value)) { 48 | } 49 | 50 | variable::variable(const function &value) : var_(std::make_shared(value)) { 51 | } 52 | 53 | variable::variable(const std::vector &value) : var_(std::make_shared(value)) { 54 | } 55 | 56 | variable::variable(const variable_base::pointer &p) : var_(p) { 57 | 58 | } 59 | 60 | variable::variable() { 61 | } 62 | 63 | variable::variable(variable &&other) : var_(std::move(other.var_)) { 64 | } 65 | 66 | variable::variable(const variable &other) { 67 | if(other.var_) { 68 | var_ = other.var_->clone(); 69 | } 70 | } 71 | 72 | variable& variable::operator=(variable &&rhs) { 73 | if(&rhs != this) { 74 | var_ = std::move(rhs.var_); 75 | } 76 | return *this; 77 | 78 | } 79 | 80 | variable &variable::operator=(const variable &rhs) { 81 | if(!var_ && rhs.var_) { 82 | var_ = rhs.var_->clone(); 83 | } else if(var_ && rhs.var_) { 84 | *var_ = *rhs.var_; 85 | } else { 86 | variable(rhs.var_).swap(*this); 87 | } 88 | return *this; 89 | } 90 | 91 | variable &variable::operator+=(const variable &rhs) { 92 | if(!var_) { 93 | throw uninitialized_variable_used(); 94 | } 95 | 96 | // TODO: is there a better approach to this? 97 | // this code makes things like 'A' + "B" work 98 | 99 | if(var_->is_character() && rhs.var_->is_string()) { 100 | var_ = std::make_shared(var_->to_string()); 101 | } 102 | 103 | *var_ += *rhs.var_; 104 | return *this; 105 | } 106 | 107 | variable &variable::operator-=(const variable &rhs) { 108 | if(!var_) { 109 | throw uninitialized_variable_used(); 110 | } 111 | *var_ -= *rhs.var_; 112 | return *this; 113 | } 114 | 115 | variable &variable::operator*=(const variable &rhs) { 116 | if(!var_) { 117 | throw uninitialized_variable_used(); 118 | } 119 | *var_ *= *rhs.var_; 120 | return *this; 121 | } 122 | 123 | variable &variable::operator/=(const variable &rhs) { 124 | if(!var_) { 125 | throw uninitialized_variable_used(); 126 | } 127 | *var_ /= *rhs.var_; 128 | return *this; 129 | } 130 | 131 | variable &variable::operator&=(const variable &rhs) { 132 | if(!var_) { 133 | throw uninitialized_variable_used(); 134 | } 135 | *var_ &= *rhs.var_; 136 | return *this; 137 | } 138 | 139 | variable &variable::operator|=(const variable &rhs) { 140 | if(!var_) { 141 | throw uninitialized_variable_used(); 142 | } 143 | *var_ |= *rhs.var_; 144 | return *this; 145 | } 146 | 147 | variable &variable::operator^=(const variable &rhs) { 148 | if(!var_) { 149 | throw uninitialized_variable_used(); 150 | } 151 | *var_ ^= *rhs.var_; 152 | return *this; 153 | } 154 | 155 | variable &variable::operator%=(const variable &rhs) { 156 | if(!var_) { 157 | throw uninitialized_variable_used(); 158 | } 159 | *var_ %= *rhs.var_; 160 | return *this; 161 | } 162 | 163 | variable &variable::operator>>=(const variable &rhs) { 164 | if(!var_) { 165 | throw uninitialized_variable_used(); 166 | } 167 | *var_ >>= *rhs.var_; 168 | return *this; 169 | } 170 | 171 | variable &variable::operator<<=(const variable &rhs) { 172 | if(!var_) { 173 | throw uninitialized_variable_used(); 174 | } 175 | *var_ <<= *rhs.var_; 176 | return *this; 177 | } 178 | 179 | variable variable::operator+() const { 180 | if(!var_) { 181 | throw uninitialized_variable_used(); 182 | } 183 | return variable(+(*var_)); 184 | } 185 | 186 | variable variable::operator-() const { 187 | if(!var_) { 188 | throw uninitialized_variable_used(); 189 | } 190 | return variable(-(*var_)); 191 | } 192 | 193 | variable variable::operator!() const { 194 | if(!var_) { 195 | throw uninitialized_variable_used(); 196 | } 197 | return variable(!(*var_)); 198 | } 199 | 200 | variable variable::operator~() const { 201 | if(!var_) { 202 | throw uninitialized_variable_used(); 203 | } 204 | return variable(~(*var_)); 205 | } 206 | 207 | function variable::operator()() const { 208 | if(!var_) { 209 | throw uninitialized_variable_used(); 210 | } 211 | return (*var_)(); 212 | } 213 | 214 | variable variable::operator[](const variable &index) const { 215 | if(!var_) { 216 | throw uninitialized_variable_used(); 217 | } 218 | return variable((*var_)[*(index.var_)]); 219 | } 220 | 221 | char to_character(const variable &var) { 222 | if(!var.var_) { 223 | throw uninitialized_variable_used(); 224 | } 225 | 226 | return var.var_->to_character(); 227 | } 228 | 229 | int to_integer(const variable &var) { 230 | if(!var.var_) { 231 | throw uninitialized_variable_used(); 232 | } 233 | 234 | return var.var_->to_integer(); 235 | } 236 | 237 | std::string to_string(const variable &var) { 238 | if(!var.var_) { 239 | throw uninitialized_variable_used(); 240 | } 241 | 242 | return var.var_->to_string(); 243 | } 244 | 245 | function to_function(const variable &var) { 246 | if(!var.var_) { 247 | throw uninitialized_variable_used(); 248 | } 249 | 250 | return var.var_->to_function(); 251 | } 252 | 253 | std::vector to_array(const variable &var) { 254 | if(!var.var_) { 255 | throw uninitialized_variable_used(); 256 | } 257 | 258 | return var.var_->to_array(); 259 | } 260 | 261 | 262 | bool is_character(const variable &var) { 263 | if(!var.var_) { 264 | return false; 265 | } 266 | 267 | return var.var_->is_character(); 268 | } 269 | 270 | bool is_integer(const variable &var) { 271 | if(!var.var_) { 272 | return false; 273 | } 274 | 275 | return var.var_->is_integer(); 276 | } 277 | 278 | bool is_string(const variable &var) { 279 | if(!var.var_) { 280 | return false; 281 | } 282 | 283 | return var.var_->is_string(); 284 | } 285 | 286 | 287 | bool is_function(const variable &var) { 288 | if(!var.var_) { 289 | return false; 290 | } 291 | 292 | return var.var_->is_function(); 293 | } 294 | 295 | bool is_array(const variable &var) { 296 | if(!var.var_) { 297 | return false; 298 | } 299 | 300 | return var.var_->is_array(); 301 | } 302 | 303 | int size(const variable &var) { 304 | if(!var.var_) { 305 | throw uninitialized_variable_used(); 306 | } 307 | 308 | return var.var_->size(); 309 | } 310 | -------------------------------------------------------------------------------- /variable.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef VARIABLE_20060121_H_ 3 | #define VARIABLE_20060121_H_ 4 | 5 | #include "variable_base.h" 6 | 7 | class function; 8 | 9 | class variable { 10 | friend char to_character(const variable &); 11 | friend int to_integer(const variable &); 12 | friend std::string to_string(const variable &); 13 | friend function to_function(const variable &); 14 | friend std::vector to_array(const variable &); 15 | 16 | friend bool is_character(const variable &); 17 | friend bool is_integer(const variable &); 18 | friend bool is_string(const variable &); 19 | friend bool is_function(const variable &); 20 | friend bool is_array(const variable &); 21 | 22 | friend int size(const variable &); 23 | 24 | friend variable operator&&(const variable &lhs, const variable &rhs); 25 | friend variable operator||(const variable &lhs, const variable &rhs); 26 | friend variable operator<(const variable &lhs, const variable &rhs); 27 | friend variable operator>(const variable &lhs, const variable &rhs); 28 | friend variable operator<=(const variable &lhs, const variable &rhs); 29 | friend variable operator>=(const variable &lhs, const variable &rhs); 30 | friend variable operator!=(const variable &lhs, const variable &rhs); 31 | friend variable operator==(const variable &lhs, const variable &rhs); 32 | 33 | private: 34 | explicit variable(const variable_base::pointer &p); 35 | 36 | public: 37 | explicit variable(char value); 38 | explicit variable(int value); 39 | explicit variable(const std::string &value); 40 | explicit variable(const function &value); 41 | explicit variable(const std::vector &value); 42 | 43 | public: 44 | static variable create_character(); 45 | static variable create_integer(); 46 | static variable create_string(); 47 | static variable create_function(); 48 | static variable create_array(); 49 | 50 | public: 51 | variable(); 52 | variable(const variable &other); 53 | variable(variable &&other); 54 | variable& operator=(variable &&rhs); 55 | 56 | public: 57 | variable &operator=(const variable &rhs); 58 | variable &operator+=(const variable &rhs); 59 | variable &operator-=(const variable &rhs); 60 | variable &operator*=(const variable &rhs); 61 | variable &operator/=(const variable &rhs); 62 | variable &operator&=(const variable &rhs); 63 | variable &operator|=(const variable &rhs); 64 | variable &operator^=(const variable &rhs); 65 | variable &operator%=(const variable &rhs); 66 | variable &operator>>=(const variable &rhs); 67 | variable &operator<<=(const variable &rhs); 68 | 69 | public: 70 | variable operator[](const variable &index) const; 71 | 72 | public: 73 | variable operator+() const; 74 | variable operator-() const; 75 | variable operator!() const; 76 | variable operator~() const; 77 | 78 | public: 79 | function operator()() const; 80 | 81 | public: 82 | void swap(variable &other); 83 | void reset(const variable &other); 84 | 85 | public: 86 | variable_base::pointer get() const { return var_; } 87 | 88 | private: 89 | variable_base::pointer var_; 90 | }; 91 | 92 | char to_character(const variable &var); 93 | int to_integer(const variable &var); 94 | std::string to_string(const variable &var); 95 | function to_function(const variable &var); 96 | std::vector to_array(const variable &var); 97 | 98 | bool is_character(const variable &); 99 | bool is_integer(const variable &); 100 | bool is_string(const variable &); 101 | bool is_function(const variable &); 102 | bool is_array(const variable &); 103 | 104 | int size(const variable &); 105 | 106 | inline variable operator&&(const variable &lhs, const variable &rhs) { return variable(to_integer(lhs) && to_integer(rhs)); } 107 | inline variable operator||(const variable &lhs, const variable &rhs) { return variable(to_integer(lhs) && to_integer(rhs)); } 108 | 109 | inline variable operator<(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) < 0); } 110 | inline variable operator>(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) > 0); } 111 | inline variable operator<=(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) <= 0); } 112 | inline variable operator>=(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) >= 0); } 113 | inline variable operator!=(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) != 0); } 114 | inline variable operator==(const variable &lhs, const variable &rhs) { return variable(lhs.var_->compare(*rhs.var_) == 0); } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /variable_base.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef VARIABLE_BASE_10012011_H_ 3 | #define VARIABLE_BASE_10012011_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | class function; 10 | 11 | class variable_base { 12 | public: 13 | typedef std::shared_ptr pointer; 14 | 15 | public: 16 | virtual ~variable_base() { 17 | } 18 | 19 | public: 20 | virtual pointer clone() const = 0; 21 | 22 | public: 23 | virtual variable_base &operator=(const variable_base &rhs) = 0; 24 | virtual variable_base &operator+=(const variable_base &rhs) = 0; 25 | virtual variable_base &operator-=(const variable_base &rhs) = 0; 26 | virtual variable_base &operator*=(const variable_base &rhs) = 0; 27 | virtual variable_base &operator/=(const variable_base &rhs) = 0; 28 | virtual variable_base &operator&=(const variable_base &rhs) = 0; 29 | virtual variable_base &operator|=(const variable_base &rhs) = 0; 30 | virtual variable_base &operator^=(const variable_base &rhs) = 0; 31 | virtual variable_base &operator%=(const variable_base &rhs) = 0; 32 | virtual variable_base &operator>>=(const variable_base &rhs) = 0; 33 | virtual variable_base &operator<<=(const variable_base &rhs) = 0; 34 | 35 | public: 36 | virtual pointer operator[](const variable_base &index) const = 0; 37 | 38 | public: 39 | virtual int size() const = 0; 40 | 41 | public: 42 | virtual pointer operator+() const = 0; 43 | virtual pointer operator-() const = 0; 44 | virtual pointer operator!() const = 0; 45 | virtual pointer operator~() const = 0; 46 | 47 | public: 48 | virtual function operator()() const = 0; 49 | 50 | public: 51 | virtual int compare(const variable_base &rhs) const = 0; 52 | 53 | public: 54 | virtual char to_character() const = 0; 55 | virtual int to_integer() const = 0; 56 | virtual std::string to_string() const = 0; 57 | virtual function to_function() const = 0; 58 | virtual std::vector to_array() const = 0; 59 | 60 | public: 61 | virtual bool is_character() const = 0; 62 | virtual bool is_integer() const = 0; 63 | virtual bool is_string() const = 0; 64 | virtual bool is_function() const = 0; 65 | virtual bool is_array() const = 0; 66 | }; 67 | 68 | #endif 69 | --------------------------------------------------------------------------------