├── .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 |
--------------------------------------------------------------------------------