├── .gitignore ├── LICENSE ├── README.md ├── assets ├── logo.png ├── wisp.png └── xkcd.png ├── examples ├── hello_world.lisp ├── io.lisp ├── list.lisp ├── loops.lisp └── math.lisp └── wisp.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | a.out 3 | main 4 | lisp 5 | wisp 6 | boot.lisp 7 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Adam McDaniel 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

👻wisp

3 |

4 | A lisp👽 written in C++ 5 |

6 |

7 | 8 | 9 |

10 |
11 | 12 | ## Why write a lisp? 13 | 14 | Lisp is one of those niche, beautiful languages that people only use for two specific purposes: 15 | 16 | 1. Write a lisp interpreter 17 | 2. To illustrate that code is data!! 18 | 19 | _So why add to the list of infinite lisp interpreters?_ 20 | 21 | The answer is simple: _**I'm bored out of my mind in quarantine.**_ If you were looking to find out why _this particular_ lisp is special, you're fresh out of luck. 22 | 23 | But isn't the fact that it's a lisp _*enough*_? 24 | 25 | ![Lisp](./assets/xkcd.png) 26 | 27 | yes. 28 | 29 | ## Syntax and Special Forms 30 | 31 | Like every other lisp, this language uses s-expressions for code syntax and data syntax. So, for example, the s-expression `(print 5)` is both a valid code snippet, and a valid list containing the items `print` and `5`. 32 | 33 | When the data `(print 5)` is evaluated by the interpreter, it evaluates `print` and `5`, and then applies `print` to `5`. 34 | 35 | Here's the result. 36 | 37 | ```lisp 38 | >>> (print 5) 39 | 5 40 | => 5 41 | ``` 42 | 43 | That's super cool! But what if we want to define our own functions? _We can use the builtin function `defun`!_ 44 | 45 | ```lisp 46 | ; define a function `fact` that takes an argument `n` 47 | (defun fact (n) 48 | (if (<= n 1) 49 | 1 50 | (* n (fact (- n 1))) 51 | )) 52 | ``` 53 | 54 | Thats awesome! But did you notice anything different about the `defun` function? _It doesn't evaluate its arguments._ If the atom `fact` were evaluated, it would throw an error like so: 55 | 56 | ```lisp 57 | >>> fact 58 | error: the expression `fact` failed in scope { } with message "atom not defined" 59 | ``` 60 | 61 | This is known as a special form, where certain functions "quote" their arguments. We can quote things ourselves too, but the language _automatically_ quotes arguments to special forms itself. 62 | 63 | If you want to "quote" a value yourself, you can do it like this. 64 | 65 | ```lisp 66 | ; quote the s-expression (1 2 3) so it's not evaluated 67 | >>> (print '(1 2 3)) 68 | (1 2 3) 69 | => (1 2 3) 70 | ``` 71 | 72 | As you can see, quote negates an evaluation. For example, whenever the expression `''a` is evaluated, it becomes `'a`. This can be useful for when you want to write long lists of data or variable names without wanting to evaluate them as code. 73 | 74 | |Special Form|Argument Evaluations|Purpose| 75 | |:-|-|-| 76 | |`(if cond a b)`|`if` only evaluates its `cond` argument. If `cond` is truthy (non-zero), then `a` is evaluated. Otherwise, `b` is evaluated.|This special form is the main method of control flow.| 77 | |`(do a b c ...)`|`do` takes a list of s-expressions and evaluates them in the order they were given (in the current scope), and then returns the result of the last s-expression.|This special form allows lambda functions to have multi-step bodies.| 78 | |`(scope a b c ...)`|`scope` takes a list of s-expressions and evaluates them in the order they were given _in a new scope_, and then returns the result of the last s-expression.|This special form allows the user to evaluate blocks of code in new scopes.| 79 | |`(defun name params body)`|`defun` evaluates none of its arguments.|This special form allows the user to conveniently define functions.| 80 | |`(define name value)`|`define` evaluates the `value` argument, which is then assigned to `name` in the current scope.|This special form allows the user to bind atoms to values in a scope.| 81 | |`(lambda params body)`|`lambda` evaluates none of its arguments.|This special form allows the user to define anonymous functions.| 82 | |`(quote x)`|`quote` evaluates none of its arguments.|This is equivalent to the `'expr` syntactic sugar.| 83 | |`(for x list ...)`|`for` evaluates only its list argument.|`for` iterates through the list storing each element in `x`, and then evaluating all of the rest of the values in the `for` body. It then returns the last value evaluated.| 84 | |`(while cond ...)`|`while` evaluates only its cond argument.|`while` evaluates its condition expression every iteration before running. If it is true, it continues to evaluate every expression in the `while` body. It then returns the last value evaluated.| 85 | 86 | 87 | ## Examples 88 | 89 | Here are some example math-y functions to wrap your head around. 90 | 91 | ```lisp 92 | ; quicksort 93 | (defun qs (l) 94 | (if (<= (len l) 1) 95 | l 96 | (do 97 | (define pivot (first l)) 98 | (+ 99 | (qs (filter (lambda (n) (> pivot n)) l)) 100 | (list pivot) 101 | (qs (tail (filter (lambda (n) (<= pivot n)) l))) 102 | )) 103 | )) 104 | 105 | ; decrement a number 106 | (defun dec (n) (- n 1)) 107 | ; increment a number 108 | (defun inc (n) (+ n 1)) 109 | ; not a bool 110 | (defun not (x) (if x 0 1)) 111 | 112 | ; negate a number 113 | (defun neg (n) (- 0 n)) 114 | 115 | ; is a number positive? 116 | (defun is-pos? (n) (> n 0)) 117 | ; is a number negative? 118 | (defun is-neg? (n) (< n 0)) 119 | ``` 120 | 121 | ## Usage 122 | 123 | Using and compiling wisp 124 | 125 | #### Dependencies 126 | 127 | Compile with your C++ compiler of choice. This is compatible with all standard versions of C++ since ANSI C++. 128 | 129 | ```bash 130 | $ git clone https://github.com/adam-mcdaniel/wisp 131 | $ cd wisp 132 | $ g++ wisp.cpp -o wisp 133 | ``` 134 | 135 | #### Using the binary 136 | 137 | Run wisp in interactive mode: 138 | 139 | ```bash 140 | $ ./wisp 141 | >>> (print "Hello world!") 142 | Hello world! 143 | => "Hello world!" 144 | ``` 145 | 146 | Interpret a file: 147 | 148 | ```bash 149 | $ ./wisp "examples/hello_world.lisp" 150 | Hello world! 151 | $ ./wisp -f "examples/hello_world.lisp" 152 | Hello world! 153 | ``` 154 | 155 | Interpret from command line argument: 156 | 157 | ```bash 158 | $ ./wisp -c '(print "Hello world!")' 159 | Hello world! 160 | ``` 161 | 162 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-mcdaniel/wisp/4b16ebf3ae739d10f96803ad4dd7f784347dc2ac/assets/logo.png -------------------------------------------------------------------------------- /assets/wisp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-mcdaniel/wisp/4b16ebf3ae739d10f96803ad4dd7f784347dc2ac/assets/wisp.png -------------------------------------------------------------------------------- /assets/xkcd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-mcdaniel/wisp/4b16ebf3ae739d10f96803ad4dd7f784347dc2ac/assets/xkcd.png -------------------------------------------------------------------------------- /examples/hello_world.lisp: -------------------------------------------------------------------------------- 1 | (print "Hello world!") -------------------------------------------------------------------------------- /examples/io.lisp: -------------------------------------------------------------------------------- 1 | (defun clear () (do (map (lambda (_) (print "")) (range 0 100)) @)) 2 | (define cls clear) 3 | 4 | (cls) 5 | 6 | (print "success") 7 | -------------------------------------------------------------------------------- /examples/list.lisp: -------------------------------------------------------------------------------- 1 | ; (defun repeat (x count) (map (lambda (_) x) (range 0 count))) 2 | (defun len (l) (reduce (lambda (acc _) (+ acc 1)) 0 l)) 3 | 4 | ; get the nth item in a list 5 | (defun index (l n) 6 | ; get the head of the result 7 | (head 8 | ; get the nth tail of the list `l` 9 | (reduce 10 | (lambda (acc arg) (tail acc)) 11 | l 12 | (range 0 n))) 13 | ) 14 | 15 | (defun min (a b) (if (< a b) a b)) 16 | (defun max (a b) (if (< a b) b a)) 17 | 18 | (defun second (l) (index l 1)) 19 | 20 | (defun random-list (l h size) 21 | (map 22 | (lambda (_) (random l h)) 23 | (range 0 size) 24 | )) 25 | 26 | ; (defun median-of-three (l) 27 | ; (if (>= (len l) 3) 28 | ; (do (define n1 (first l)) 29 | ; (define n2 (index l (/ (len n) 2))) 30 | ; (define n3 (last l)) 31 | ; (if (* (<= n2 n1) (<= n1 n3)) n1 32 | ; (* (<= n1 n2) (<= n2 n3)) n2 33 | ; n3)) 34 | ; (first l))) 35 | 36 | (defun median-of-three (l) 37 | (if (>= (len l) 3) 38 | (min (max (first l) (index l (/ (len l) 2))) (last l)) 39 | (first l))) 40 | 41 | ; quicksort a list 42 | (defun qs (l) 43 | (if (<= (len l) 1) 44 | l 45 | (do 46 | ; (define pivot (first l)) 47 | (define pivot (median-of-three l)) 48 | (+ 49 | (qs (filter (lambda (n) (> pivot n)) l)) 50 | (list pivot) 51 | (qs (tail (filter (lambda (n) (<= pivot n)) l))) 52 | )) 53 | )) 54 | 55 | 56 | (defun split-list (l) (do 57 | (define a '()) 58 | (define b '()) 59 | (define count 0) 60 | (for x l 61 | (if (< count (/ (len l) 2)) 62 | (define a (push a x)) 63 | (define b (push b x))) 64 | (define count (+ count 1)) 65 | ) 66 | (list a b) 67 | )) 68 | 69 | (defun is-empty (l) (= (len l) 0)) 70 | 71 | (defun merge-sort (l) 72 | (if (<= (len l) 1) 73 | l 74 | (if (= (len l) 2) 75 | (list (min (first l) (second l)) (max (first l) (second l))) 76 | (do 77 | (define sub-list (split-list l)) 78 | (define a (merge-sort (first sub-list))) 79 | (define b (merge-sort (second sub-list))) 80 | (define result '()) 81 | (while (< (len result) (len l)) 82 | (if (is-empty a) 83 | (do 84 | (define result (push result (first b))) 85 | (define b (tail b)) 86 | ) 87 | (if (is-empty b) 88 | (do 89 | (define result (push result (first a))) 90 | (define a (tail a)) 91 | ) 92 | (if (<= (first a) (first b)) 93 | (do 94 | (define result (push result (first a))) 95 | (define a (tail a)) 96 | ) 97 | (do 98 | (define result (push result (first b))) 99 | (define b (tail b)) 100 | ) 101 | ) 102 | ) 103 | ) 104 | ) 105 | result 106 | ) 107 | ) 108 | )) 109 | 110 | 111 | (define list-to-sort (random-list 0 100 100)) 112 | 113 | (print "Unsorted list: " list-to-sort) 114 | (print "Merge-sorted list: " (merge-sort list-to-sort)) 115 | (print "Quick-sorted list: " (qs list-to-sort)) 116 | 117 | ; this is too! 118 | "success" 119 | -------------------------------------------------------------------------------- /examples/loops.lisp: -------------------------------------------------------------------------------- 1 | (print "result:" 2 | (for x (range 0 10) 3 | (print (* 2 x)))) 4 | 5 | (defun yes_or_no (prompt) 6 | (if (= (input prompt) "y") 1 0)) 7 | 8 | 9 | (defun collatz (n) (do 10 | (print "performing collatz of" n) 11 | (define save n) 12 | (while (!= n 1) 13 | (print n) 14 | (if (% n 2) 15 | (define n (+ (* 3 n) 1)) 16 | (define n (/ n 2)) 17 | ) 18 | ) 19 | (print "collatz of" save "reaches 1") 20 | )) 21 | 22 | (collatz (first (parse (input "Enter a number to perform collatz on: ")))) -------------------------------------------------------------------------------- /examples/math.lisp: -------------------------------------------------------------------------------- 1 | (defun dec (n) (- n 1)) 2 | (defun inc (n) (+ n 1)) 3 | (defun not (x) (if x 0 1)) 4 | 5 | (defun neg (n) (- 0 n)) 6 | 7 | (defun is-pos (n) (> n 0)) 8 | (defun is-neg (n) (< n 0)) 9 | 10 | (defun const (n) (lambda (_) n)) 11 | 12 | (defun pow (n exp) 13 | (if (= exp 0) 14 | 1 15 | (if (< exp 0) 16 | (pow (/ 1 n) (neg exp)) 17 | (reduce 18 | (lambda (acc x) (* acc x)) 19 | n 20 | (map (const n) (range 1 exp)) 21 | )) 22 | ) 23 | ) 24 | 25 | (defun is-odd (n) (% n 2)) 26 | (defun is-even (n) (not (is-odd n))) 27 | 28 | (defun fact (n) (reduce * 1.0 (range 2 (inc n)))) 29 | 30 | (defun collatz (n) 31 | (if (= (print n) 1) 1 32 | (if (is-odd n) 33 | (collatz (+ (* n 3) 1)) 34 | (collatz (/ n 2)) 35 | ))) 36 | 37 | "success" 38 | -------------------------------------------------------------------------------- /wisp.cpp: -------------------------------------------------------------------------------- 1 | /// A microlisp named Wisp, by Adam McDaniel 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | /// LANGUAGE OPTIONS /////////////////////////////////////////////////////////// 5 | //////////////////////////////////////////////////////////////////////////////// 6 | 7 | // Comment this define out to drop support for libm functions 8 | #define HAS_LIBM 9 | #ifdef HAS_LIBM 10 | #include 11 | #else 12 | #define NO_LIBM_SUPPORT "no libm support" 13 | #endif 14 | 15 | 16 | // Comment this define out to drop support for standard library functions. 17 | // This allows the program to run without a runtime. 18 | #define USE_STD 19 | #ifdef USE_STD 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | std::string read_file_contents(std::string filename) { 26 | std::ifstream f; 27 | f.open(filename.c_str()); 28 | if (!f) 29 | throw std::runtime_error("could not open file"); 30 | 31 | f.seekg(0, std::ios::end); 32 | std::string contents; 33 | contents.reserve(f.tellg()); 34 | f.seekg(0, std::ios::beg); 35 | contents.assign(std::istreambuf_iterator(f), 36 | std::istreambuf_iterator()); 37 | f.close(); 38 | 39 | return contents; 40 | } 41 | 42 | #else 43 | #define NO_STD "no standard library support" 44 | #endif 45 | 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | /// REQUIRED INCLUDES ////////////////////////////////////////////////////////// 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | /// ERROR MESSAGES ///////////////////////////////////////////////////////////// 59 | //////////////////////////////////////////////////////////////////////////////// 60 | 61 | #define TOO_FEW_ARGS "too few arguments to function" 62 | #define TOO_MANY_ARGS "too many arguments to function" 63 | #define INVALID_ARGUMENT "invalid argument" 64 | #define MISMATCHED_TYPES "mismatched types" 65 | #define CALL_NON_FUNCTION "called non-function" 66 | #define UNKNOWN_ERROR "unknown exception" 67 | #define INVALID_LAMBDA "invalid lambda" 68 | #define INVALID_BIN_OP "invalid binary operation" 69 | #define INVALID_ORDER "cannot order expression" 70 | #define BAD_CAST "cannot cast" 71 | #define ATOM_NOT_DEFINED "atom not defined" 72 | #define EVAL_EMPTY_LIST "evaluated empty list" 73 | #define INTERNAL_ERROR "interal virtual machine error" 74 | #define INDEX_OUT_OF_RANGE "index out of range" 75 | #define MALFORMED_PROGRAM "malformed program" 76 | 77 | //////////////////////////////////////////////////////////////////////////////// 78 | /// TYPE NAMES ///////////////////////////////////////////////////////////////// 79 | //////////////////////////////////////////////////////////////////////////////// 80 | 81 | #define STRING_TYPE "string" 82 | #define INT_TYPE "int" 83 | #define FLOAT_TYPE "float" 84 | #define UNIT_TYPE "unit" 85 | #define FUNCTION_TYPE "function" 86 | #define ATOM_TYPE "atom" 87 | #define QUOTE_TYPE "quote" 88 | #define LIST_TYPE "list" 89 | 90 | //////////////////////////////////////////////////////////////////////////////// 91 | /// HELPER FUNCTIONS /////////////////////////////////////////////////////////// 92 | //////////////////////////////////////////////////////////////////////////////// 93 | 94 | // Convert an object to a string using a stringstream conveniently 95 | template 96 | std::string to_string(T t) { 97 | // Create a stringstream 98 | std::ostringstream ss; 99 | // Convert the object to a string 100 | ss << std::dec << t; 101 | // Return the string 102 | return ss.str(); 103 | } 104 | 105 | // Replace a substring with a replacement string in a source string 106 | void replace_substring(std::string &src, std::string substr, std::string replacement) { 107 | size_t i=0; 108 | for (i=src.find(substr, i); i!=std::string::npos; i=src.find(substr, i)) { 109 | src.replace(i, substr.size(), replacement); 110 | i += replacement.size(); 111 | } 112 | } 113 | 114 | // Is this character a valid lisp symbol character 115 | bool is_symbol(char ch) { 116 | return (isalnum(ch) || ispunct(ch)) && ch != '(' && ch != ')' && ch != '"' && ch != '\''; 117 | } 118 | 119 | //////////////////////////////////////////////////////////////////////////////// 120 | /// LISP CONSTRUCTS //////////////////////////////////////////////////////////// 121 | //////////////////////////////////////////////////////////////////////////////// 122 | 123 | // Forward declaration for Environment class definition 124 | class Value; 125 | 126 | 127 | // An instance of a function's scope. 128 | class Environment { 129 | public: 130 | // Default constructor 131 | Environment() : parent_scope(NULL) {} 132 | 133 | // Does this environment, or its parent environment, 134 | // have this atom in scope? 135 | // This is only used to determine which atoms to capture when 136 | // creating a lambda function. 137 | bool has(std::string name) const; 138 | // Get the value associated with this name in this scope 139 | Value get(std::string name) const; 140 | // Set the value associated with this name in this scope 141 | void set(std::string name, Value value); 142 | 143 | void combine(Environment const &other); 144 | 145 | void set_parent_scope(Environment *parent) { 146 | parent_scope = parent; 147 | } 148 | 149 | // Output this scope in readable form to a stream. 150 | friend std::ostream &operator<<(std::ostream &os, Environment const &v); 151 | private: 152 | 153 | // The definitions in the scope. 154 | std::map defs; 155 | Environment *parent_scope; 156 | }; 157 | 158 | 159 | // An exception thrown by the lisp 160 | class Error { 161 | public: 162 | // Create an error with the value that caused the error, 163 | // the scope where the error was found, and the message. 164 | Error(Value v, Environment const &env, const char *msg); 165 | // Copy constructor is needed to prevent double frees 166 | Error(Error const &other); 167 | ~Error(); 168 | 169 | // Get the printable error description. 170 | std::string description(); 171 | private: 172 | Value *cause; 173 | Environment env; 174 | const char *msg; 175 | }; 176 | 177 | // The type for a builtin function, which takes a list of values, 178 | // and the environment to run the function in. 179 | typedef Value (*Builtin)(std::vector, Environment &); 180 | 181 | class Value { 182 | public: 183 | //////////////////////////////////////////////////////////////////////////////// 184 | /// CONSTRUCTORS /////////////////////////////////////////////////////////////// 185 | //////////////////////////////////////////////////////////////////////////////// 186 | 187 | // Constructs a unit value 188 | Value() : type(UNIT) {} 189 | 190 | // Constructs an integer 191 | Value(int i) : type(INT) { stack_data.i = i; } 192 | // Constructs a floating point value 193 | Value(double f) : type(FLOAT) { stack_data.f = f; } 194 | // Constructs a list 195 | Value(std::vector list) : type(LIST), list(list) {} 196 | 197 | // Construct a quoted value 198 | static Value quote(Value quoted) { 199 | Value result; 200 | result.type = QUOTE; 201 | 202 | // The first position in the list is 203 | // used to store the quoted expression. 204 | result.list.push_back(quoted); 205 | return result; 206 | } 207 | 208 | // Construct an atom 209 | static Value atom(std::string s) { 210 | Value result; 211 | result.type = ATOM; 212 | 213 | // We use the `str` member to store the atom. 214 | result.str = s; 215 | return result; 216 | } 217 | 218 | // Construct a string 219 | static Value string(std::string s) { 220 | Value result; 221 | result.type = STRING; 222 | 223 | // We use the `str` member to store the string. 224 | result.str = s; 225 | return result; 226 | } 227 | 228 | // Construct a lambda function 229 | Value(std::vector params, Value ret, Environment const &env) : type(LAMBDA) { 230 | // We store the params and the result in the list member 231 | // instead of having dedicated members. This is to save memory. 232 | list.push_back(Value(params)); 233 | list.push_back(ret); 234 | 235 | // Lambdas capture only variables that they know they will use. 236 | std::vector used_atoms = ret.get_used_atoms(); 237 | for (size_t i=0; i get_used_atoms() { 259 | std::vector result, tmp; 260 | switch (type) { 261 | case QUOTE: 262 | // The data for a quote is stored in the 263 | // first slot of the list member. 264 | return list[0].get_used_atoms(); 265 | case ATOM: 266 | // If this is an atom, add it to the list 267 | // of used atoms in this expression. 268 | result.push_back(as_atom()); 269 | return result; 270 | case LAMBDA: 271 | // If this is a lambda, get the list of used atoms in the body 272 | // of the expression. 273 | return list[1].get_used_atoms(); 274 | case LIST: 275 | // If this is a list, add each of the atoms used in all 276 | // of the elements in the list. 277 | for (size_t i=0; i args, Environment &env); 296 | // Evaluate this value as lisp code. 297 | Value eval(Environment &env); 298 | 299 | bool is_number() const { 300 | return type == INT || type == FLOAT; 301 | } 302 | 303 | // Get the "truthy" boolean value of this value. 304 | bool as_bool() const { 305 | return *this != Value(0); 306 | } 307 | 308 | // Get this item's integer value 309 | int as_int() const { 310 | return cast_to_int().stack_data.i; 311 | } 312 | 313 | // Get this item's floating point value 314 | double as_float() const { 315 | return cast_to_int().stack_data.f; 316 | } 317 | 318 | // Get this item's string value 319 | std::string as_string() const { 320 | // If this item is not a string, throw a cast error. 321 | if (type != STRING) 322 | throw Error(*this, Environment(), BAD_CAST); 323 | return str; 324 | } 325 | 326 | // Get this item's atom value 327 | std::string as_atom() const { 328 | // If this item is not an atom, throw a cast error. 329 | if (type != ATOM) 330 | throw Error(*this, Environment(), BAD_CAST); 331 | return str; 332 | } 333 | 334 | // Get this item's list value 335 | std::vector as_list() const { 336 | // If this item is not a list, throw a cast error. 337 | if (type != LIST) 338 | throw Error(*this, Environment(), BAD_CAST); 339 | return list; 340 | } 341 | 342 | // Push an item to the end of this list 343 | void push(Value val) { 344 | // If this item is not a list, you cannot push to it. 345 | // Throw an error. 346 | if (type != LIST) 347 | throw Error(*this, Environment(), MISMATCHED_TYPES); 348 | 349 | list.push_back(val); 350 | } 351 | 352 | // Push an item from the end of this list 353 | Value pop() { 354 | // If this item is not a list, you cannot pop from it. 355 | // Throw an error. 356 | if (type != LIST) 357 | throw Error(*this, Environment(), MISMATCHED_TYPES); 358 | 359 | // Remember the last item in the list 360 | Value result = list[list.size()-1]; 361 | // Remove it from this instance 362 | list.pop_back(); 363 | // Return the remembered value 364 | return result; 365 | } 366 | 367 | //////////////////////////////////////////////////////////////////////////////// 368 | /// TYPECASTING METHODS //////////////////////////////////////////////////////// 369 | //////////////////////////////////////////////////////////////////////////////// 370 | 371 | // Cast this to an integer value 372 | Value cast_to_int() const { 373 | switch (type) { 374 | case INT: return *this; 375 | case FLOAT: return Value(int(stack_data.f)); 376 | // Only ints and floats can be cast to an int 377 | default: 378 | throw Error(*this, Environment(), BAD_CAST); 379 | } 380 | } 381 | 382 | // Cast this to a floating point value 383 | Value cast_to_float() const { 384 | switch (type) { 385 | case FLOAT: return *this; 386 | case INT: return Value(float(stack_data.i)); 387 | // Only ints and floats can be cast to a float 388 | default: 389 | throw Error(*this, Environment(), BAD_CAST); 390 | } 391 | } 392 | 393 | //////////////////////////////////////////////////////////////////////////////// 394 | /// COMPARISON OPERATIONS ////////////////////////////////////////////////////// 395 | //////////////////////////////////////////////////////////////////////////////// 396 | 397 | bool operator==(Value other) const { 398 | // If either of these values are floats, promote the 399 | // other to a float, and then compare for equality. 400 | if (type == FLOAT && other.type == INT) return *this == other.cast_to_float(); 401 | else if (type == INT && other.type == FLOAT) return this->cast_to_float() == other; 402 | // If the values types aren't equal, then they cannot be equal. 403 | else if (type != other.type) return false; 404 | 405 | switch (type) { 406 | case FLOAT: 407 | return stack_data.f == other.stack_data.f; 408 | case INT: 409 | return stack_data.i == other.stack_data.i; 410 | case BUILTIN: 411 | return stack_data.b == other.stack_data.b; 412 | case STRING: 413 | case ATOM: 414 | // Both atoms and strings store their 415 | // data in the str member. 416 | return str == other.str; 417 | case LAMBDA: 418 | case LIST: 419 | // Both lambdas and lists store their 420 | // data in the list member. 421 | return list == other.list; 422 | case QUOTE: 423 | // The values for quotes are stored in the 424 | // first slot of the list member. 425 | return list[0] == other.list[0]; 426 | default: 427 | return true; 428 | } 429 | } 430 | 431 | bool operator!=(Value other) const { 432 | return !(*this == other); 433 | } 434 | 435 | // bool operator<(Value other) const { 436 | // if (other.type != FLOAT && other.type != INT) 437 | // throw Error(*this, Environment(), INVALID_BIN_OP); 438 | 439 | // switch (type) { 440 | // case FLOAT: 441 | // return stack_data.f < other.cast_to_float().stack_data.f; 442 | // case INT: 443 | // if (other.type == FLOAT) 444 | // return cast_to_float().stack_data.f < other.stack_data.f; 445 | // else return stack_data.i < other.stack_data.i; 446 | // default: 447 | // throw Error(*this, Environment(), INVALID_ORDER); 448 | // } 449 | // } 450 | 451 | //////////////////////////////////////////////////////////////////////////////// 452 | /// ORDERING OPERATIONS //////////////////////////////////////////////////////// 453 | //////////////////////////////////////////////////////////////////////////////// 454 | 455 | bool operator>=(Value other) const { 456 | return !(*this < other); 457 | } 458 | 459 | bool operator<=(Value other) const { 460 | return (*this == other) || (*this < other); 461 | } 462 | 463 | bool operator>(Value other) const { 464 | return !(*this <= other); 465 | } 466 | 467 | bool operator<(Value other) const { 468 | // Other type must be a float or an int 469 | if (other.type != FLOAT && other.type != INT) 470 | throw Error(*this, Environment(), INVALID_BIN_OP); 471 | 472 | switch (type) { 473 | case FLOAT: 474 | // If this is a float, promote the other value to a float and compare. 475 | return stack_data.f < other.cast_to_float().stack_data.f; 476 | case INT: 477 | // If the other value is a float, promote this value to a float and compare. 478 | if (other.type == FLOAT) 479 | return cast_to_float().stack_data.f < other.stack_data.f; 480 | // Otherwise compare the integer values 481 | else return stack_data.i < other.stack_data.i; 482 | default: 483 | // Only allow comparisons between integers and floats 484 | throw Error(*this, Environment(), INVALID_ORDER); 485 | } 486 | } 487 | 488 | //////////////////////////////////////////////////////////////////////////////// 489 | /// ARITHMETIC OPERATIONS ////////////////////////////////////////////////////// 490 | //////////////////////////////////////////////////////////////////////////////// 491 | 492 | // This function adds two lisp values, and returns the lisp value result. 493 | Value operator+(Value other) const { 494 | // If the other value's type is the unit type, 495 | // don't even bother continuing. 496 | // Unit types consume all arithmetic operations. 497 | if (other.type == UNIT) return other; 498 | 499 | // Other type must be a float or an int 500 | if ((is_number() || other.is_number()) && 501 | !(is_number() && other.is_number())) 502 | throw Error(*this, Environment(), INVALID_BIN_OP); 503 | 504 | switch (type) { 505 | case FLOAT: 506 | // If one is a float, promote the other by default and do 507 | // float addition. 508 | return Value(stack_data.f + other.cast_to_float().stack_data.f); 509 | case INT: 510 | // If the other type is a float, go ahead and promote this expression 511 | // before continuing with the addition. 512 | if (other.type == FLOAT) 513 | return Value(cast_to_float() + other.stack_data.f); 514 | // Otherwise, do integer addition. 515 | else return Value(stack_data.i + other.stack_data.i); 516 | case STRING: 517 | // If the other value is also a string, do the concat 518 | if (other.type == STRING) 519 | return Value::string(str + other.str); 520 | // We throw an error if we try to concat anything of non-string type 521 | else throw Error(*this, Environment(), INVALID_BIN_OP); 522 | case LIST: 523 | // If the other value is also a list, do the concat 524 | if (other.type == LIST) { 525 | // Maintain the value that will be returned 526 | Value result = *this; 527 | // Add each item in the other list to the end of this list 528 | for (size_t i=0; i"; 722 | case UNIT: 723 | return "@"; 724 | default: 725 | // We don't know how to display whatever type this is. 726 | // This isn't the users fault, this is just unhandled. 727 | // This should never be reached. 728 | throw Error(*this, Environment(), INTERNAL_ERROR); 729 | } 730 | } 731 | 732 | std::string debug() const { 733 | std::string result; 734 | switch (type) { 735 | case QUOTE: 736 | return "'" + list[0].debug(); 737 | case ATOM: 738 | return str; 739 | case INT: 740 | return to_string(stack_data.i); 741 | case FLOAT: 742 | return to_string(stack_data.f); 743 | case STRING: 744 | for (size_t i=0; i"; 763 | case UNIT: 764 | return "@"; 765 | default: 766 | // We don't know how to debug whatever type this is. 767 | // This isn't the users fault, this is just unhandled. 768 | // This should never be reached. 769 | throw Error(*this, Environment(), INTERNAL_ERROR); 770 | } 771 | } 772 | 773 | friend std::ostream &operator<<(std::ostream &os, Value const &v) { 774 | return os << v.display(); 775 | } 776 | 777 | private: 778 | enum { 779 | QUOTE, 780 | ATOM, 781 | INT, 782 | FLOAT, 783 | LIST, 784 | STRING, 785 | LAMBDA, 786 | BUILTIN, 787 | UNIT 788 | } type; 789 | 790 | union { 791 | int i; 792 | double f; 793 | Builtin b; 794 | } stack_data; 795 | 796 | std::string str; 797 | std::vector list; 798 | Environment lambda_scope; 799 | }; 800 | 801 | Error::Error(Value v, Environment const &env, const char *msg) : env(env), msg(msg) { 802 | cause = new Value; 803 | *cause = v; 804 | } 805 | 806 | Error::Error(Error const &other) : env(other.env), msg(other.msg) { 807 | cause = new Value(*other.cause); 808 | } 809 | 810 | Error::~Error() { 811 | delete cause; 812 | } 813 | 814 | std::string Error::description() { 815 | return "error: the expression `" + cause->debug() + "` failed in scope " + to_string(env) + " with message \"" + msg + "\""; 816 | } 817 | 818 | void Environment::combine(Environment const &other) { 819 | // Normally, I would use the `insert` method of the `map` class, 820 | // but it doesn't overwrite previously declared values for keys. 821 | std::map::const_iterator itr = other.defs.begin(); 822 | for (; itr!=other.defs.end(); itr++) { 823 | // Iterate through the keys and assign each value. 824 | defs[itr->first] = itr->second; 825 | } 826 | } 827 | 828 | std::ostream &operator<<(std::ostream &os, Environment const &e) { 829 | std::map::const_iterator itr = e.defs.begin(); 830 | os << "{ "; 831 | for (; itr != e.defs.end(); itr++) { 832 | os << '\'' << itr->first << "' : " << itr->second.debug() << ", "; 833 | } 834 | return os << "}"; 835 | } 836 | 837 | void Environment::set(std::string name, Value value) { 838 | defs[name] = value; 839 | } 840 | 841 | 842 | Value Value::apply(std::vector args, Environment &env) { 843 | Environment e; 844 | std::vector params; 845 | switch (type) { 846 | case LAMBDA: 847 | // Get the list of parameter atoms 848 | params = list[0].list; 849 | if (params.size() != args.size()) 850 | throw Error(Value(args), env, args.size() > params.size()? 851 | TOO_MANY_ARGS : TOO_FEW_ARGS 852 | ); 853 | 854 | // Get the captured scope from the lambda 855 | e = lambda_scope; 856 | // And make this scope the parent scope 857 | e.set_parent_scope(&env); 858 | 859 | // Iterate through the list of parameters and 860 | // insert the arguments into the scope. 861 | for (size_t i=0; i args; 885 | Value function; 886 | Environment e; 887 | switch (type) { 888 | case QUOTE: 889 | return list[0]; 890 | case ATOM: 891 | return env.get(str); 892 | case LIST: 893 | if (list.size() < 1) 894 | throw Error(*this, env, EVAL_EMPTY_LIST); 895 | 896 | args = std::vector(list.begin() + 1, list.end()); 897 | 898 | // Only evaluate our arguments if it's not builtin! 899 | // Builtin functions can be special forms, so we 900 | // leave them to evaluate their arguments. 901 | function = list[0].eval(env); 902 | 903 | if (!function.is_builtin()) 904 | for (size_t i=0; i()); 954 | 955 | while (s[ptr] != ')') 956 | result.push(parse(s, ptr)); 957 | 958 | skip_whitespace(s, ++ptr); 959 | return result; 960 | 961 | } else if (isdigit(s[ptr]) || (s[ptr] == '-' && isdigit(s[ptr + 1]))) { 962 | // If this is a number 963 | bool negate = s[ptr] == '-'; 964 | if (negate) ptr++; 965 | 966 | int save_ptr = ptr; 967 | while (isdigit(s[ptr]) || s[ptr] == '.') ptr++; 968 | std::string n = s.substr(save_ptr, ptr); 969 | skip_whitespace(s, ptr); 970 | 971 | if (n.find('.') != std::string::npos) 972 | return Value((negate? -1 : 1) * atof(n.c_str())); 973 | else return Value((negate? -1 : 1) * atoi(n.c_str())); 974 | 975 | } else if (s[ptr] == '\"') { 976 | // If this is a string 977 | int n = 1; 978 | while (s[ptr + n] != '\"') { 979 | if (ptr + n >= int(s.length())) 980 | throw std::runtime_error(MALFORMED_PROGRAM); 981 | 982 | if (s[ptr + n] == '\\') n++; 983 | n++; 984 | } 985 | 986 | std::string x = s.substr(ptr+1, n-1); 987 | ptr += n+1; 988 | skip_whitespace(s, ptr); 989 | 990 | // Iterate over the characters in the string, and 991 | // replace escaped characters with their intended values. 992 | for (size_t i=0; i parse(std::string s) { 1027 | int i=0, last_i=-1; 1028 | std::vector result; 1029 | // While the parser is making progress (while the pointer is moving right) 1030 | // and the pointer hasn't reached the end of the string, 1031 | while (last_i != i && i <= int(s.length()-1)) { 1032 | // Parse another expression and add it to the list. 1033 | last_i = i; 1034 | result.push_back(parse(s, i)); 1035 | } 1036 | 1037 | // If the whole string wasn't parsed, the program must be bad. 1038 | if (i < int(s.length())) 1039 | throw std::runtime_error(MALFORMED_PROGRAM); 1040 | 1041 | // Return the list of values parsed. 1042 | return result; 1043 | } 1044 | 1045 | // Execute code in an environment 1046 | Value run(std::string code, Environment &env) { 1047 | // Parse the code 1048 | std::vector parsed = parse(code); 1049 | // Iterate over the expressions and evaluate them 1050 | // in this environment. 1051 | for (size_t i=0; i &args, Environment &env) { 1067 | for (size_t i=0; i args, Environment &env) { 1073 | if (args.size() < 2) 1074 | throw Error(Value("lambda", lambda), env, TOO_FEW_ARGS); 1075 | 1076 | if (args[0].get_type_name() != LIST_TYPE) 1077 | throw Error(Value("lambda", lambda), env, INVALID_LAMBDA); 1078 | 1079 | return Value(args[0].as_list(), args[1], env); 1080 | } 1081 | 1082 | // if-else (SPECIAL FORM) 1083 | Value if_then_else(std::vector args, Environment &env) { 1084 | if (args.size() != 3) 1085 | throw Error(Value("if", if_then_else), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); 1086 | if (args[0].eval(env).as_bool()) 1087 | return args[1].eval(env); 1088 | else return args[2].eval(env); 1089 | } 1090 | 1091 | // Define a variable with a value (SPECIAL FORM) 1092 | Value define(std::vector args, Environment &env) { 1093 | if (args.size() != 2) 1094 | throw Error(Value("define", define), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1095 | 1096 | Value result = args[1].eval(env); 1097 | env.set(args[0].display(), result); 1098 | return result; 1099 | } 1100 | 1101 | // Define a function with parameters and a result expression (SPECIAL FORM) 1102 | Value defun(std::vector args, Environment &env) { 1103 | if (args.size() != 3) 1104 | throw Error(Value("defun", defun), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); 1105 | 1106 | if (args[1].get_type_name() != LIST_TYPE) 1107 | throw Error(Value("defun", defun), env, INVALID_LAMBDA); 1108 | 1109 | Value f = Value(args[1].as_list(), args[2], env); 1110 | env.set(args[0].display(), f); 1111 | return f; 1112 | } 1113 | 1114 | // Loop over a list of expressions with a condition (SPECIAL FORM) 1115 | Value while_loop(std::vector args, Environment &env) { 1116 | Value acc; 1117 | while (args[0].eval(env).as_bool()) { 1118 | for (size_t i=1; i args, Environment &env) { 1127 | Value acc; 1128 | std::vector list = args[1].eval(env).as_list(); 1129 | 1130 | for (size_t i=0; i args, Environment &env) { 1143 | Value acc; 1144 | for (size_t i=0; i args, Environment &env) { 1151 | Environment e = env; 1152 | Value acc; 1153 | for (size_t i=0; i args, Environment &) { 1160 | std::vector v; 1161 | for (size_t i=0; i args, Environment &env) { 1169 | // Is not a special form, so we can evaluate our args. 1170 | eval_args(args, env); 1171 | 1172 | std::exit(args.size() < 1? 0 : args[0].cast_to_int().as_int()); 1173 | return Value(); 1174 | } 1175 | 1176 | // Print several values and return the last one 1177 | Value print(std::vector args, Environment &env) { 1178 | // Is not a special form, so we can evaluate our args. 1179 | eval_args(args, env); 1180 | 1181 | if (args.size() < 1) 1182 | throw Error(Value("print", print), env, TOO_FEW_ARGS); 1183 | 1184 | Value acc; 1185 | for (size_t i=0; i args, Environment &env) { 1197 | // Is not a special form, so we can evaluate our args. 1198 | eval_args(args, env); 1199 | 1200 | if (args.size() > 1) 1201 | throw Error(Value("input", input), env, TOO_MANY_ARGS); 1202 | 1203 | if (!args.empty()) 1204 | std::cout << args[0]; 1205 | 1206 | std::string s; 1207 | std::getline(std::cin, s); 1208 | return Value::string(s); 1209 | } 1210 | 1211 | // Get a random number between two numbers inclusively 1212 | Value random(std::vector args, Environment &env) { 1213 | // Is not a special form, so we can evaluate our args. 1214 | eval_args(args, env); 1215 | 1216 | if (args.size() != 2) 1217 | throw Error(Value("random", random), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1218 | 1219 | int low = args[0].as_int(), high = args[1].as_int(); 1220 | return Value(rand()%(high-low+1) + low); 1221 | } 1222 | 1223 | // Get the contents of a file 1224 | Value read_file(std::vector args, Environment &env) { 1225 | // Is not a special form, so we can evaluate our args. 1226 | eval_args(args, env); 1227 | 1228 | if (args.size() != 1) 1229 | throw Error(Value("read-file", read_file), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1230 | 1231 | // return Value::string(content); 1232 | return Value::string(read_file_contents(args[0].as_string())); 1233 | } 1234 | 1235 | // Write a string to a file 1236 | Value write_file(std::vector args, Environment &env) { 1237 | // Is not a special form, so we can evaluate our args. 1238 | eval_args(args, env); 1239 | 1240 | if (args.size() != 2) 1241 | throw Error(Value("write-file", write_file), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1242 | 1243 | std::ofstream f; 1244 | // The first argument is the file name 1245 | f.open(args[0].as_string().c_str()); 1246 | // The second argument is the contents of the file to write 1247 | Value result = Value((f << args[1].as_string())? 1 : 0); 1248 | f.close(); 1249 | return result; 1250 | } 1251 | 1252 | // Read a file and execute its code 1253 | Value include(std::vector args, Environment &env) { 1254 | // Import is technically not a special form, it's more of a macro. 1255 | // We can evaluate our arguments. 1256 | eval_args(args, env); 1257 | 1258 | if (args.size() != 1) 1259 | throw Error(Value("include", include), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1260 | 1261 | Environment e; 1262 | Value result = run(read_file_contents(args[0].as_string()), e); 1263 | env.combine(e); 1264 | return result; 1265 | } 1266 | #endif 1267 | 1268 | // Evaluate a value as code 1269 | Value eval(std::vector args, Environment &env) { 1270 | // Is not a special form, so we can evaluate our args. 1271 | eval_args(args, env); 1272 | 1273 | if (args.size() != 1) 1274 | throw Error(Value("eval", eval), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1275 | else return args[0].eval(env); 1276 | } 1277 | 1278 | // Create a list of values 1279 | Value list(std::vector args, Environment &env) { 1280 | // Is not a special form, so we can evaluate our args. 1281 | eval_args(args, env); 1282 | 1283 | return Value(args); 1284 | } 1285 | 1286 | // Sum multiple values 1287 | Value sum(std::vector args, Environment &env) { 1288 | // Is not a special form, so we can evaluate our args. 1289 | eval_args(args, env); 1290 | 1291 | if (args.size() < 2) 1292 | throw Error(Value("+", sum), env, TOO_FEW_ARGS); 1293 | 1294 | Value acc = args[0]; 1295 | for (size_t i=1; i args, Environment &env) { 1302 | // Is not a special form, so we can evaluate our args. 1303 | eval_args(args, env); 1304 | 1305 | if (args.size() != 2) 1306 | throw Error(Value("-", subtract), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1307 | return args[0] - args[1]; 1308 | } 1309 | 1310 | // Multiply several values 1311 | Value product(std::vector args, Environment &env) { 1312 | // Is not a special form, so we can evaluate our args. 1313 | eval_args(args, env); 1314 | 1315 | if (args.size() < 2) 1316 | throw Error(Value("*", product), env, TOO_FEW_ARGS); 1317 | 1318 | Value acc = args[0]; 1319 | for (size_t i=1; i args, Environment &env) { 1326 | // Is not a special form, so we can evaluate our args. 1327 | eval_args(args, env); 1328 | 1329 | if (args.size() != 2) 1330 | throw Error(Value("/", divide), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1331 | return args[0] / args[1]; 1332 | } 1333 | 1334 | // Get the remainder of values 1335 | Value remainder(std::vector args, Environment &env) { 1336 | // Is not a special form, so we can evaluate our args. 1337 | eval_args(args, env); 1338 | 1339 | if (args.size() != 2) 1340 | throw Error(Value("%", remainder), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1341 | return args[0] % args[1]; 1342 | } 1343 | 1344 | // Are two values equal? 1345 | Value eq(std::vector args, Environment &env) { 1346 | // Is not a special form, so we can evaluate our args. 1347 | eval_args(args, env); 1348 | 1349 | if (args.size() != 2) 1350 | throw Error(Value("=", eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1351 | return Value(int(args[0] == args[1])); 1352 | } 1353 | 1354 | // Are two values not equal? 1355 | Value neq(std::vector args, Environment &env) { 1356 | // Is not a special form, so we can evaluate our args. 1357 | eval_args(args, env); 1358 | 1359 | if (args.size() != 2) 1360 | throw Error(Value("!=", neq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1361 | return Value(int(args[0] != args[1])); 1362 | } 1363 | 1364 | // Is one number greater than another? 1365 | Value greater(std::vector args, Environment &env) { 1366 | // Is not a special form, so we can evaluate our args. 1367 | eval_args(args, env); 1368 | 1369 | if (args.size() != 2) 1370 | throw Error(Value(">", greater), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1371 | return Value(int(args[0] > args[1])); 1372 | } 1373 | 1374 | // Is one number less than another? 1375 | Value less(std::vector args, Environment &env) { 1376 | // Is not a special form, so we can evaluate our args. 1377 | eval_args(args, env); 1378 | 1379 | if (args.size() != 2) 1380 | throw Error(Value("<", less), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1381 | return Value(int(args[0] < args[1])); 1382 | } 1383 | 1384 | // Is one number greater than or equal to another? 1385 | Value greater_eq(std::vector args, Environment &env) { 1386 | // Is not a special form, so we can evaluate our args. 1387 | eval_args(args, env); 1388 | 1389 | if (args.size() != 2) 1390 | throw Error(Value(">=", greater_eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1391 | return Value(int(args[0] >= args[1])); 1392 | } 1393 | 1394 | // Is one number less than or equal to another? 1395 | Value less_eq(std::vector args, Environment &env) { 1396 | // Is not a special form, so we can evaluate our args. 1397 | eval_args(args, env); 1398 | 1399 | if (args.size() != 2) 1400 | throw Error(Value("<=", less_eq), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1401 | return Value(int(args[0] <= args[1])); 1402 | } 1403 | 1404 | // Get the type name of a value 1405 | Value get_type_name(std::vector args, Environment &env) { 1406 | // Is not a special form, so we can evaluate our args. 1407 | eval_args(args, env); 1408 | 1409 | if (args.size() != 1) 1410 | throw Error(Value("type", get_type_name), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1411 | 1412 | return Value::string(args[0].get_type_name()); 1413 | } 1414 | 1415 | // Cast an item to a float 1416 | Value cast_to_float(std::vector args, Environment &env) { 1417 | // Is not a special form, so we can evaluate our args. 1418 | eval_args(args, env); 1419 | 1420 | if (args.size() != 1) 1421 | throw Error(Value(FLOAT_TYPE, cast_to_float), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1422 | return args[0].cast_to_float(); 1423 | } 1424 | 1425 | // Cast an item to an int 1426 | Value cast_to_int(std::vector args, Environment &env) { 1427 | // Is not a special form, so we can evaluate our args. 1428 | eval_args(args, env); 1429 | 1430 | if (args.size() != 1) 1431 | throw Error(Value(INT_TYPE, cast_to_int), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1432 | return args[0].cast_to_int(); 1433 | } 1434 | 1435 | // Index a list 1436 | Value index(std::vector args, Environment &env) { 1437 | // Is not a special form, so we can evaluate our args. 1438 | eval_args(args, env); 1439 | 1440 | if (args.size() != 2) 1441 | throw Error(Value("index", index), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1442 | 1443 | std::vector list = args[0].as_list(); 1444 | int i = args[1].as_int(); 1445 | if (list.empty() || i >= (int)list.size()) 1446 | throw Error(list, env, INDEX_OUT_OF_RANGE); 1447 | 1448 | return list[i]; 1449 | } 1450 | 1451 | // Insert a value into a list 1452 | Value insert(std::vector args, Environment &env) { 1453 | // Is not a special form, so we can evaluate our args. 1454 | eval_args(args, env); 1455 | 1456 | if (args.size() != 3) 1457 | throw Error(Value("insert", insert), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); 1458 | 1459 | std::vector list = args[0].as_list(); 1460 | int i = args[1].as_int(); 1461 | if (i > (int)list.size()) 1462 | throw Error(list, env, INDEX_OUT_OF_RANGE); 1463 | 1464 | list.insert(list.begin() + args[1].as_int(), args[2]); 1465 | return Value(list); 1466 | } 1467 | 1468 | // Remove a value at an index from a list 1469 | Value remove(std::vector args, Environment &env) { 1470 | // Is not a special form, so we can evaluate our args. 1471 | eval_args(args, env); 1472 | 1473 | if (args.size() != 2) 1474 | throw Error(Value("remove", remove), env, args.size() > 2? TOO_MANY_ARGS : TOO_FEW_ARGS); 1475 | 1476 | std::vector list = args[0].as_list(); 1477 | int i = args[1].as_int(); 1478 | if (list.empty() || i >= (int)list.size()) 1479 | throw Error(list, env, INDEX_OUT_OF_RANGE); 1480 | 1481 | list.erase(list.begin() + i); 1482 | return Value(list); 1483 | } 1484 | 1485 | // Get the length of a list 1486 | Value len(std::vector args, Environment &env) { 1487 | // Is not a special form, so we can evaluate our args. 1488 | eval_args(args, env); 1489 | 1490 | if (args.size() != 1) 1491 | throw Error(Value("len", len), env, args.size() > 1? 1492 | TOO_MANY_ARGS : TOO_FEW_ARGS 1493 | ); 1494 | 1495 | return Value(int(args[0].as_list().size())); 1496 | } 1497 | 1498 | // Add an item to the end of a list 1499 | Value push(std::vector args, Environment &env) { 1500 | // Is not a special form, so we can evaluate our args. 1501 | eval_args(args, env); 1502 | 1503 | if (args.size() == 0) 1504 | throw Error(Value("push", push), env, TOO_FEW_ARGS); 1505 | for (size_t i=1; i args, Environment &env) { 1511 | // Is not a special form, so we can evaluate our args. 1512 | eval_args(args, env); 1513 | 1514 | if (args.size() != 1) 1515 | throw Error(Value("pop", pop), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1516 | return args[0].pop(); 1517 | } 1518 | 1519 | Value head(std::vector args, Environment &env) { 1520 | // Is not a special form, so we can evaluate our args. 1521 | eval_args(args, env); 1522 | 1523 | if (args.size() != 1) 1524 | throw Error(Value("head", head), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1525 | std::vector list = args[0].as_list(); 1526 | if (list.empty()) 1527 | throw Error(Value("head", head), env, INDEX_OUT_OF_RANGE); 1528 | 1529 | return list[0]; 1530 | } 1531 | 1532 | Value tail(std::vector args, Environment &env) { 1533 | // Is not a special form, so we can evaluate our args. 1534 | eval_args(args, env); 1535 | 1536 | if (args.size() != 1) 1537 | throw Error(Value("tail", tail), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1538 | 1539 | std::vector result, list = args[0].as_list(); 1540 | 1541 | for (size_t i = 1; i args, Environment &env) { 1548 | // Is not a special form, so we can evaluate our args. 1549 | eval_args(args, env); 1550 | 1551 | if (args.size() != 1) 1552 | throw Error(Value("parse", parse), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1553 | if (args[0].get_type_name() != STRING_TYPE) 1554 | throw Error(args[0], env, INVALID_ARGUMENT); 1555 | std::vector parsed = ::parse(args[0].as_string()); 1556 | 1557 | // if (parsed.size() == 1) 1558 | // return parsed[0]; 1559 | // else return Value(parsed); 1560 | return Value(parsed); 1561 | } 1562 | 1563 | Value replace(std::vector args, Environment &env) { 1564 | // Is not a special form, so we can evaluate our args. 1565 | eval_args(args, env); 1566 | 1567 | if (args.size() != 3) 1568 | throw Error(Value("replace", replace), env, args.size() > 3? TOO_MANY_ARGS : TOO_FEW_ARGS); 1569 | 1570 | std::string src = args[0].as_string(); 1571 | replace_substring(src, args[1].as_string(), args[2].as_string()); 1572 | return Value::string(src); 1573 | } 1574 | 1575 | Value display(std::vector args, Environment &env) { 1576 | // Is not a special form, so we can evaluate our args. 1577 | eval_args(args, env); 1578 | 1579 | if (args.size() != 1) 1580 | throw Error(Value("display", display), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1581 | 1582 | return Value::string(args[0].display()); 1583 | } 1584 | 1585 | Value debug(std::vector args, Environment &env) { 1586 | // Is not a special form, so we can evaluate our args. 1587 | eval_args(args, env); 1588 | 1589 | if (args.size() != 1) 1590 | throw Error(Value("debug", debug), env, args.size() > 1? TOO_MANY_ARGS : TOO_FEW_ARGS); 1591 | 1592 | return Value::string(args[0].debug()); 1593 | } 1594 | 1595 | Value map_list(std::vector args, Environment &env) { 1596 | // Is not a special form, so we can evaluate our args. 1597 | eval_args(args, env); 1598 | 1599 | std::vector result, l=args[1].as_list(), tmp; 1600 | for (size_t i=0; i args, Environment &env) { 1609 | // Is not a special form, so we can evaluate our args. 1610 | eval_args(args, env); 1611 | 1612 | std::vector result, l=args[1].as_list(), tmp; 1613 | for (size_t i=0; i args, Environment &env) { 1623 | // Is not a special form, so we can evaluate our args. 1624 | eval_args(args, env); 1625 | 1626 | std::vector l=args[2].as_list(), tmp; 1627 | Value acc = args[1]; 1628 | for (size_t i=0; i args, Environment &env) { 1638 | // Is not a special form, so we can evaluate our args. 1639 | eval_args(args, env); 1640 | 1641 | std::vector result; 1642 | Value low = args[0], high = args[1]; 1643 | if (low.get_type_name() != INT_TYPE && low.get_type_name() != FLOAT_TYPE) 1644 | throw Error(low, env, MISMATCHED_TYPES); 1645 | if (high.get_type_name() != INT_TYPE && high.get_type_name() != FLOAT_TYPE) 1646 | throw Error(high, env, MISMATCHED_TYPES); 1647 | 1648 | if (low >= high) return Value(result); 1649 | 1650 | while (low < high) { 1651 | result.push_back(low); 1652 | low = low + Value(1); 1653 | } 1654 | return Value(result); 1655 | } 1656 | } 1657 | 1658 | void repl(Environment &env) { 1659 | #ifdef USE_STD 1660 | std::string code; 1661 | std::string input; 1662 | Value tmp; 1663 | std::vector parsed; 1664 | while (std::cin) { 1665 | std::cout << ">>> "; 1666 | std::getline(std::cin, input); 1667 | if (input == "!quit" || input == "!q") 1668 | break; 1669 | else if (input == "!env" || input == "!e") 1670 | std::cout << env << std::endl; 1671 | else if (input == "!export" || input == "!x") { 1672 | std::cout << "File to export to: "; 1673 | std::getline(std::cin, input); 1674 | 1675 | std::ofstream f; 1676 | f.open(input.c_str(), std::ofstream::out); 1677 | f << code; 1678 | f.close(); 1679 | } else if (input != "") { 1680 | try { 1681 | tmp = run(input, env); 1682 | std::cout << " => " << tmp.debug() << std::endl; 1683 | code += input + "\n"; 1684 | } catch (Error &e) { 1685 | std::cerr << e.description() << std::endl; 1686 | } catch (std::runtime_error &e) { 1687 | std::cerr << e.what() << std::endl; 1688 | } 1689 | } 1690 | } 1691 | #endif 1692 | } 1693 | 1694 | // Does this environment, or its parent environment, have a variable? 1695 | bool Environment::has(std::string name) const { 1696 | // Find the value in the map 1697 | std::map::const_iterator itr = defs.find(name); 1698 | if (itr != defs.end()) 1699 | // If it was found 1700 | return true; 1701 | else if (parent_scope != NULL) 1702 | // If it was not found in the current environment, 1703 | // try to find it in the parent environment 1704 | return parent_scope->has(name); 1705 | else return false; 1706 | } 1707 | 1708 | // Get the value associated with this name in this scope 1709 | Value Environment::get(std::string name) const { 1710 | // Meta operations 1711 | if (name == "eval") return Value("eval", builtin::eval); 1712 | if (name == "type") return Value("type", builtin::get_type_name); 1713 | if (name == "parse") return Value("parse", builtin::parse); 1714 | 1715 | // Special forms 1716 | if (name == "do") return Value("do", builtin::do_block); 1717 | if (name == "if") return Value("if", builtin::if_then_else); 1718 | if (name == "for") return Value("for", builtin::for_loop); 1719 | if (name == "while") return Value("while", builtin::while_loop); 1720 | if (name == "scope") return Value("scope", builtin::scope); 1721 | if (name == "quote") return Value("quote", builtin::quote); 1722 | if (name == "defun") return Value("defun", builtin::defun); 1723 | if (name == "define") return Value("define", builtin::define); 1724 | if (name == "lambda") return Value("lambda", builtin::lambda); 1725 | 1726 | // Comparison operations 1727 | if (name == "=") return Value("=", builtin::eq); 1728 | if (name == "!=") return Value("!=", builtin::neq); 1729 | if (name == ">") return Value(">", builtin::greater); 1730 | if (name == "<") return Value("<", builtin::less); 1731 | if (name == ">=") return Value(">=", builtin::greater_eq); 1732 | if (name == "<=") return Value("<=", builtin::less_eq); 1733 | 1734 | // Arithmetic operations 1735 | if (name == "+") return Value("+", builtin::sum); 1736 | if (name == "-") return Value("-", builtin::subtract); 1737 | if (name == "*") return Value("*", builtin::product); 1738 | if (name == "/") return Value("/", builtin::divide); 1739 | if (name == "%") return Value("%", builtin::remainder); 1740 | 1741 | // List operations 1742 | if (name == "list") return Value("list", builtin::list); 1743 | if (name == "insert") return Value("insert", builtin::insert); 1744 | if (name == "index") return Value("index", builtin::index); 1745 | if (name == "remove") return Value("remove", builtin::remove); 1746 | 1747 | if (name == "len") return Value("len", builtin::len); 1748 | 1749 | if (name == "push") return Value("push", builtin::push); 1750 | if (name == "pop") return Value("pop", builtin::pop); 1751 | if (name == "head") return Value("head", builtin::head); 1752 | if (name == "tail") return Value("tail", builtin::tail); 1753 | if (name == "first") return Value("first", builtin::head); 1754 | if (name == "last") return Value("last", builtin::pop); 1755 | if (name == "range") return Value("range", builtin::range); 1756 | 1757 | // Functional operations 1758 | if (name == "map") return Value("map", builtin::map_list); 1759 | if (name == "filter") return Value("filter", builtin::filter_list); 1760 | if (name == "reduce") return Value("reduce", builtin::reduce_list); 1761 | 1762 | // IO operations 1763 | #ifdef USE_STD 1764 | if (name == "exit") return Value("exit", builtin::exit); 1765 | if (name == "quit") return Value("quit", builtin::exit); 1766 | if (name == "print") return Value("print", builtin::print); 1767 | if (name == "input") return Value("input", builtin::input); 1768 | if (name == "random") return Value("random", builtin::random); 1769 | if (name == "include") return Value("include", builtin::include); 1770 | if (name == "read-file") return Value("read-file", builtin::read_file); 1771 | if (name == "write-file") return Value("write-file", builtin::write_file); 1772 | #endif 1773 | 1774 | // String operations 1775 | if (name == "debug") return Value("debug", builtin::debug); 1776 | if (name == "replace") return Value("replace", builtin::replace); 1777 | if (name == "display") return Value("display", builtin::display); 1778 | 1779 | // Casting operations 1780 | if (name == "int") return Value("int", builtin::cast_to_int); 1781 | if (name == "float") return Value("float", builtin::cast_to_float); 1782 | 1783 | // Constants 1784 | if (name == "endl") return Value::string("\n"); 1785 | 1786 | std::map::const_iterator itr = defs.find(name); 1787 | if (itr != defs.end()) return itr->second; 1788 | else if (parent_scope != NULL) { 1789 | itr = parent_scope->defs.find(name); 1790 | if (itr != parent_scope->defs.end()) return itr->second; 1791 | else return parent_scope->get(name); 1792 | } 1793 | 1794 | throw Error(Value::atom(name), *this, ATOM_NOT_DEFINED); 1795 | } 1796 | 1797 | int main(int argc, const char **argv) { 1798 | Environment env; 1799 | std::vector args; 1800 | for (int i=0; i