├── Environment.cpp ├── Environment.hpp ├── FormattedIO.cpp ├── FormattedIO.hpp ├── LICENSE ├── NativeProcedures.cpp ├── NativeProcedures.hpp ├── Parser.cpp ├── Parser.hpp ├── README.md ├── SList.cpp ├── SList.hpp ├── main.cpp ├── makefile └── message.txt /Environment.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Environment.cpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-15. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "Environment.hpp" 10 | 11 | Environment::Environment () : outerEnv(nullptr) {} 12 | 13 | Environment::Environment (SLists params, SLists args, Environment* outerEnv) : outerEnv(outerEnv) { 14 | auto a = args.cbegin(); 15 | for (auto vi = params.cbegin(); vi != params.cend(); vi++) { 16 | env[(*vi).val()] = (*a++); 17 | } 18 | 19 | } 20 | 21 | envType* Environment::find (symbol s) { 22 | if (env.find(s)!=env.end()) { 23 | return &env; 24 | } else { 25 | if (outerEnv==nullptr) { 26 | throw "ERROR: Unbound Symbol"; 27 | } else { 28 | return outerEnv->find(s); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Environment.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Environment.hpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-15. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #ifndef Environment_hpp 10 | #define Environment_hpp 11 | #include 12 | #include "SList.hpp" 13 | 14 | typedef std::string symbol; 15 | typedef std::unordered_map envType; 16 | 17 | class Environment { 18 | public: 19 | Environment (); 20 | Environment (SLists params, SLists args, Environment* outerEnv); 21 | void insert(symbol,SList); 22 | envType* find (symbol); 23 | envType env; 24 | 25 | private: 26 | 27 | Environment* outerEnv; 28 | }; 29 | #endif /* Environment_hpp */ 30 | -------------------------------------------------------------------------------- /FormattedIO.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // STD_IN.cpp 3 | // Scheme_Interpreter 4 | // 5 | // Created by Josh Sun on 2017-05-08. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "FormattedIO.hpp" 10 | 11 | std::string FormattedIO::readLine() { 12 | std::string line = ""; 13 | getline(std::cin,line); 14 | if (line.find_first_not_of(" ")==std::string::npos) 15 | return ""; 16 | else 17 | return line; 18 | } 19 | 20 | std::string FormattedIO::readFile(std::string fileName) { 21 | std::string fn = fileName; 22 | std::ifstream rawFile (fn.c_str()); 23 | std::string rawText = ""; 24 | std::string line = ""; 25 | while (!rawFile.eof()) { 26 | std::getline(rawFile,line); 27 | rawText += line+"\n"; 28 | } 29 | rawFile.close(); 30 | return rawText; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /FormattedIO.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // STD_IN.hpp 3 | // Scheme_Interpreter 4 | // 5 | // Created by Josh Sun on 2017-05-08. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #ifndef FormattedIO_hpp 10 | #define FormattedIO_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | class FormattedIO { 17 | public: 18 | static std::string readLine(); 19 | static std::string readFile(std::string fileName); 20 | virtual ~FormattedIO(); 21 | private: 22 | FormattedIO (); 23 | }; 24 | 25 | #endif /* STD_IN_hpp */ 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Joshua Sun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NativeProcedures.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // NativeProcedures.cpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-12. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "NativeProcedures.hpp" 10 | 11 | SList add (const SLists& argv) { 12 | double dVal = atof(argv[0].val().c_str()); 13 | for (auto vi = argv.begin()+1; vi != argv.end(); vi++) 14 | dVal += atof((*vi).val().c_str()); 15 | return SList(dVal); 16 | } 17 | 18 | SList subtract (const SLists& argv) { 19 | double dVal = atof(argv[0].val().c_str()); 20 | for (auto vi = argv.begin()+1; vi != argv.end(); vi++) 21 | dVal -= atof((*vi).val().c_str()); 22 | return SList(dVal); 23 | } 24 | 25 | SList multiply (const SLists& argv) { 26 | double dVal = atof(argv[0].val().c_str()); 27 | for (auto vi = argv.begin()+1; vi != argv.end(); vi++) 28 | dVal *= atof((*vi).val().c_str()); 29 | return SList(dVal); 30 | } 31 | 32 | SList divide (const SLists& argv) { 33 | double dVal = atof(argv[0].val().c_str()); 34 | for (auto vi = argv.begin()+1; vi != argv.end(); vi++) 35 | dVal /= atof((*vi).val().c_str()); 36 | return SList(dVal); 37 | } 38 | 39 | SList mod (const SLists& argv) { 40 | int iVal = atoi(argv[0].val().c_str()); 41 | for (auto vi = argv.begin()+1; vi != argv.end(); vi++) 42 | iVal %= atoi((*vi).val().c_str()); 43 | return SList(iVal); 44 | } 45 | 46 | SList sqrt (const SLists& arg) { 47 | return SList(sqrt(atof(arg[0].val().c_str()))); 48 | } 49 | 50 | SList sin (const SLists& arg) { 51 | return SList(sin(atof(arg[0].val().c_str()))); 52 | } 53 | 54 | SList cos (const SLists& arg) { 55 | return SList(cos(atof(arg[0].val().c_str()))); 56 | } 57 | 58 | SList tan (const SLists& arg) { 59 | return SList(tan(atof(arg[0].val().c_str()))); 60 | } 61 | 62 | SList arcsin (const SLists& arg) { 63 | return SList(asin(atof(arg[0].val().c_str()))); 64 | } 65 | 66 | SList arccos (const SLists& arg) { 67 | return SList(acos(atof(arg[0].val().c_str()))); 68 | } 69 | 70 | SList arctan (const SLists& arg) { 71 | return SList(atan(atof(arg[0].val().c_str()))); 72 | } 73 | 74 | SList abs (const SLists& arg) { 75 | return SList(fabs(atof(arg[0].val().c_str()))); 76 | } 77 | 78 | SList greater_than (const SLists& argv) { 79 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 80 | if (atof((*(vi+1)).val().c_str()) >= atof((*vi).val().c_str())) 81 | return SList("#f"); 82 | } 83 | return SList("#t"); 84 | } 85 | 86 | SList less_than (const SLists& argv) { 87 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 88 | if (atof((*(vi+1)).val().c_str()) <= atof((*vi).val().c_str())) 89 | return SList("#f"); 90 | } 91 | return SList("#t"); 92 | } 93 | 94 | SList equal_num (const SLists& argv) { 95 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 96 | if (fabs(atof((*(vi+1)).val().c_str())-atof((*vi).val().c_str()))>0.00000001) { 97 | return SList("#f"); 98 | } 99 | } 100 | return SList("#t"); 101 | } 102 | 103 | SList greater_equal (const SLists& argv) { 104 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 105 | if (atof((*(vi+1)).val().c_str()) > atof((*vi).val().c_str())) 106 | return SList("#f"); 107 | } 108 | return SList("#t"); 109 | } 110 | 111 | SList less_equal (const SLists& argv) { 112 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 113 | if (atof((*(vi+1)).val().c_str()) < atof((*vi).val().c_str())) 114 | return SList("#f"); 115 | } 116 | return SList("#t"); 117 | } 118 | 119 | SList append (const SLists& argv) { 120 | SList l; 121 | for (auto vi = argv.begin(); vi != argv.end(); vi++) { 122 | l.pushList(vi->getList()); 123 | 124 | } 125 | return SList(l); 126 | } 127 | 128 | SList apply (const SLists& argv) { 129 | return argv[0].getProc()(argv[1].getList()); 130 | } 131 | 132 | SList map (const SLists& argv) { 133 | SList newList(SList::LIST); 134 | for (int i = 0; i < argv[1].getList().size(); i++) { 135 | SLists n; 136 | SList args(SList::LIST); 137 | n.push_back(argv[0].getProc()); 138 | for (int j = 1; j < argv.size(); j++) { 139 | args.push(argv[j].getList()[i]); 140 | } 141 | n.push_back(args); 142 | newList.push(apply(n)); 143 | } 144 | return newList; 145 | } 146 | 147 | SList max (const SLists& argv) { 148 | double max = -std::numeric_limits::infinity(); 149 | for (auto vi = argv.begin(); vi != argv.end(); vi++) { 150 | if (atof(vi->val().c_str()) > max) 151 | max = atof(vi->val().c_str()); 152 | } 153 | return max; 154 | } 155 | 156 | SList min (const SLists& argv) { 157 | double min = std::numeric_limits::infinity(); 158 | for (auto vi = argv.begin(); vi != argv.end(); vi++) { 159 | if (atof(vi->val().c_str()) < min) 160 | min = atof(vi->val().c_str()); 161 | } 162 | return min; 163 | } 164 | 165 | SList eqv (const SLists& argv) { 166 | for (auto vi = argv.begin(); vi != argv.end()-1; vi++) { 167 | if (vi->getType() != (vi+1)->getType()) { 168 | return SList("#f"); 169 | } else if (vi->getType() == SList::SYMBOL) { 170 | if (vi->val() == "#f" || vi->val() != (vi+1)->val()) 171 | return SList("#f"); 172 | } else if (vi->getType() == SList::NUMBER) { 173 | if (fabs(atof((*(vi+1)).val().c_str())-atof((*vi).val().c_str()))>0.00000001) 174 | return SList("#f"); 175 | } else if (vi->getType() == SList::LAMBDA || vi->getType() == SList::PROC) { 176 | if (vi->getProc() != (vi+1)->getProc()) 177 | return SList("#f"); 178 | } else { 179 | return eqv(vi->getList()); 180 | } 181 | } 182 | return SList("#t"); 183 | } 184 | 185 | SList isNumber (const SLists& arg) { 186 | return arg[0].getType()==SList::NUMBER ? SList("#t") : SList("#f"); 187 | } 188 | 189 | SList isProcedure (const SLists& arg) { 190 | return arg[0].getType()==SList::PROC ? SList("#t") : SList("#f"); 191 | } 192 | 193 | SList isSymbol (const SLists& arg) { 194 | return arg[0].getType()==SList::SYMBOL ? SList("#t") : SList("#f"); 195 | } 196 | 197 | SList isList (const SLists& arg) { 198 | return arg[0].getType()==SList::LIST ? SList("#t") : SList("#f"); 199 | } 200 | 201 | SList isNull (const SLists& arg) { 202 | if (arg[0].getType()!=SList::LIST) return SList("#f"); 203 | if (arg[0].getList().size()==0) return SList("#t"); 204 | return SList("#f"); 205 | } 206 | 207 | SList length (const SLists& arg) { 208 | return arg[0].getList().size(); 209 | } 210 | 211 | SList list (const SLists& argv) { 212 | SList newList(SList::LIST); 213 | for (auto vi = argv.begin(); vi != argv.end(); vi++) { 214 | newList.push(*vi); 215 | } 216 | return newList; 217 | } 218 | 219 | SList cons (const SLists& argv) { 220 | SList newList(SList::LIST); 221 | newList.push(argv[0]); 222 | newList.push(argv[1]); 223 | return newList; 224 | } 225 | 226 | SList car (const SLists& argv) { 227 | return argv[0].getList()[0]; 228 | } 229 | 230 | SList cdr (const SLists& argv) { 231 | SList s(SList::LIST); 232 | for (int i = 1; i < argv[0].getList().size(); i++) { 233 | s.push(argv[0].getList()[i]); 234 | } 235 | return s; 236 | } 237 | 238 | -------------------------------------------------------------------------------- /NativeProcedures.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // NativeProcedures.hpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-12. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #ifndef NativeProcedures_hpp 10 | #define NativeProcedures_hpp 11 | 12 | #include "SList.hpp" 13 | #include "stdlib.h" 14 | #include "math.h" 15 | 16 | //primitives 17 | SList add (const SLists& arg); 18 | SList subtract (const SLists& arg); 19 | SList multiply (const SLists& arg); 20 | SList divide (const SLists& arg); 21 | SList mod (const SLists& arg); 22 | SList sqrt (const SLists& arg); 23 | SList max (const SLists& argv); 24 | SList min (const SLists& argv); 25 | 26 | SList sin (const SLists& arg); 27 | SList cos (const SLists& arg); 28 | SList tan (const SLists& arg); 29 | SList arcsin (const SLists& arg); 30 | SList arccos (const SLists& arg); 31 | SList arctan (const SLists& arg); 32 | 33 | SList abs (const SLists& arg); 34 | 35 | SList greater_than(const SLists& argv); 36 | SList less_than (const SLists& argv); 37 | SList equal_num (const SLists& argv); 38 | SList append (const SLists& argv); 39 | SList apply (const SLists& argv); 40 | SList map (const SLists& argv); 41 | SList max (const SLists& argv); 42 | SList max (const SLists& argv); 43 | SList eqv (const SLists& argv); 44 | 45 | SList isNumber (const SLists& arg); 46 | SList isProcedure (const SLists& arg); 47 | SList isSymbol (const SLists& arg); 48 | SList isList (const SLists& arg); 49 | SList isNull (const SLists& arg); 50 | 51 | SList length (const SLists& arg); 52 | SList list (const SLists& argv); 53 | SList cons (const SLists& argv); 54 | SList car (const SLists& argv); 55 | SList cdr (const SLists& argv); 56 | #endif /* NativeProcedures_hpp */ 57 | -------------------------------------------------------------------------------- /Parser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.cpp 3 | // Scheme_Interpreter 4 | // 5 | // Created by Josh Sun on 2017-05-08. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "Parser.hpp" 10 | 11 | SList Parser::parse(const std::string rawInput) { 12 | std::deque temp = tokenize(rawInput); 13 | return process_syntax(temp); 14 | } 15 | 16 | 17 | std::deque Parser::tokenize(const std::string rawInput) { 18 | std::deque tokens; 19 | for (int i = 0; i < rawInput.length(); ++i) { 20 | while (rawInput[i] == ' ') ++i; 21 | if (rawInput[i] == '(') tokens.push_back("("); 22 | else if (rawInput[i] == ')') tokens.push_back(")"); 23 | else { 24 | int j = i; 25 | while (rawInput[j] && rawInput[j] != ')' && rawInput[j] != '(' && rawInput[j] != ' ') 26 | ++j; 27 | tokens.push_back(rawInput.substr(i,j-i)); 28 | i = j-1; 29 | } 30 | } 31 | return tokens; 32 | } 33 | 34 | 35 | SList Parser::process_syntax (std::deque& tokens) { 36 | if (tokens.size()==0) throw "Syntax Error: Unexpected End of Line"; 37 | std::string token = tokens[0]; 38 | tokens.pop_front(); 39 | if (token == "'") { 40 | for (auto vi = tokens.begin(); vi != tokens.end(); vi++) { 41 | if (*vi==")") { 42 | tokens.insert(vi,")"); 43 | break; 44 | } 45 | } 46 | tokens.push_front("quote"); 47 | tokens.push_front("("); 48 | return process_syntax (tokens); 49 | } else if (token == "(") { //expression 50 | SLists list; 51 | while (tokens[0]!=")") { 52 | list.push_back(process_syntax(tokens)); 53 | } 54 | tokens.pop_front(); 55 | return SList(list); 56 | } else if (token == ")") { 57 | throw "Syntax Error: Unexpected Syntax ')'"; 58 | } else { //atom 59 | return atomic(token); 60 | } 61 | } 62 | 63 | SList Parser::atomic (std::string s) { 64 | if (isNumber(s)) { 65 | return SList(atof(s.c_str())); //double/float 66 | } 67 | return SList(s); //symbol 68 | } 69 | 70 | bool Parser::isNumber (std::string s) { 71 | return s.find_first_not_of( "0123456789." ) == std::string::npos; 72 | } 73 | 74 | bool Parser::isInteger (std::string s) { 75 | if(s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ; 76 | char * p ; 77 | strtol(s.c_str(), &p, 10) ; 78 | return (*p == 0) ; 79 | } 80 | 81 | -------------------------------------------------------------------------------- /Parser.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.hpp 3 | // Scheme_Interpreter 4 | // 5 | // Created by Josh Sun on 2017-05-08. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #ifndef Parser_hpp 10 | #define Parser_hpp 11 | 12 | #include "FormattedIO.hpp" 13 | #include "SList.hpp" 14 | #include 15 | #include 16 | #include 17 | 18 | class Parser { 19 | public: 20 | static SList parse (const std::string rawInput) ; 21 | virtual ~Parser(); 22 | private: 23 | Parser(); 24 | static std::deque tokenize(const std::string rawInput); 25 | static SList process_syntax (std::deque& tokens); 26 | static SList atomic (std::string s); 27 | static bool isNumber (std::string s); 28 | static bool isInteger (std::string s); 29 | }; 30 | 31 | #endif /* Parser_hpp */ 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LispCC 2 | A simple and fun Scheme interpreter written in C++ (C++11). 3 | 4 | Scheme is a simple but powerful functional language first developed in the 1990s at MIT. 5 | 6 | I've enjoyed reading Peter Norvig's blog [(How to Write a (Lisp) Interpreter (in Python))](http://norvig.com/lispy.html), in which he writes a Scheme interpreter in 90+ lines of Python. The C++ implementation is a bit more challenging and I thought I'd give it a try. 7 | 8 | UC Berkley's [CS61A: Structure and Interpretation of Computer Programs](https://cs61a.org) has also been very helpful. 9 | 10 | 11 | ## Core Concepts 12 | 13 | The interpreter is divided into 2 parts. 14 | 15 | ``` 16 | 1. Parsing (Lexical Analysis) 17 | - Read input program 18 | - Break program into tokens 19 | - Analysis of tokens according our Abstract Syntax Tree 20 | 2. Evaluation & Execution 21 | - Tokens are processed according to the semantics of Scheme 22 | - Carry out the computation 23 | ``` 24 | 25 | Storing Scheme Expressions 26 | - Scheme objects - defined in SList.cpp 27 | - Environment - stored as STL unordered_map 28 | 29 | Overview of internal processes 30 | ``` 31 | Program => Parse => Abstract Syntax Tree => eval => result 32 | ``` 33 | 34 | Simple Demo Program 35 | ``` 36 | (define square (lambda (x) (* x x)) 37 | => LAMBDA 38 | (define pi 3.1416) 39 | => 3.141600 40 | (if (> pi (square (pi))) (square (pi)) else (sin (sqrt (square (pi))))) 41 | => -0.000007 42 | ``` 43 | 44 | ## Installation 45 | 46 | ### Prerequisites 47 | 48 | ``` 49 | GNU g++ -std=c++11 50 | ``` 51 | 52 | ### Installing 53 | 54 | To clone this repo to your local machine: 55 | ``` 56 | $ git clone 57 | $ cd SchemePlusPlus 58 | ``` 59 | 60 | Compile and Link: 61 | ``` 62 | $ make build 63 | ``` 64 | 65 | Run: 66 | ``` 67 | $ ./scheme 68 | ``` 69 | 70 | Clean: 71 | ``` 72 | $ make clean 73 | ``` 74 | 75 | ## How it works 76 | 77 | ### Representation of Scheme objects in C++ 78 | | __Scheme Data Type__ | __Our Internal Representation__ | 79 | |-----------------------|--------------------------------------------------| 80 | | Numbers | C++'s 'int' and 'double' | 81 | | Symbols | C++'s built-in `std::string` data type. | 82 | | Booleans (`#t`, `#f`) | C++'s built-in `std::string` data type. | 83 | | Pairs and Lists | The `SList` class, defined in `SList.cpp`. | 84 | 85 | ### List of Supported Scheme Expressions 86 | ``` 87 | quote, ' 88 | if ... else ... 89 | define 90 | set! 91 | lambda 92 | +, -, *, /, >, <, >=, <= 93 | =, eqv? 94 | abs 95 | append 96 | apply 97 | begin 98 | car 99 | cdr 100 | cons 101 | length 102 | list 103 | list? 104 | map 105 | max 106 | min 107 | null? 108 | number? 109 | procedure? 110 | symbol? 111 | mod 112 | sqrt 113 | sin 114 | cos 115 | tan 116 | asin 117 | acos 118 | atan 119 | ``` 120 | 121 | ### Files 122 | - FormattedIO.cpp: reading input from stdin 123 | - Parser.cpp: lexical analysis 124 | - SList.cpp: stores any scheme expression 125 | - NativeProcedures.cpp: defines the standard Scheme procedures 126 | - Environment.cpp: execution environement, can be nested 127 | - Main.cpp: REPL 128 | 129 | ### Example Scheme Programs 130 | 131 | ## Running Tests 132 | 133 | ## Contributing 134 | 135 | Feel free to contribute and/or create new issues 136 | 137 | ## Versioning 138 | 139 | I use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/jsun98/SchemePlusPlus/tags). 140 | 141 | ## Authors 142 | 143 | * **Joshua Sun** - [github](https://github.com/jsun98) 144 | 145 | See also the list of [contributors](https://github.com/your/project/contributors) who participated in this project. 146 | 147 | ## License 148 | 149 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 150 | 151 | ## Acknowledgments 152 | 153 | * Peter Norvig, Director of Research, Google Inc. 154 | 155 | -------------------------------------------------------------------------------- /SList.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // SList.cpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-10. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "SList.hpp" 10 | 11 | SList::SList(){}; 12 | SList::SList(sType type): type(type) {}; 13 | SList::SList(std::string s) : value(s), type (SYMBOL) {} 14 | SList::SList(double s) : value(std::to_string(s)), type(NUMBER) {} 15 | SList::SList(SLists s) : list(s), type(LIST) {} 16 | SList::SList(proc s) : p(s) , type(PROC) {} 17 | 18 | bool double_is_int(std::string dVal) { 19 | double trouble = atof(dVal.c_str()); 20 | return fabs(trouble) == floor(fabs(trouble)); 21 | } 22 | 23 | std::string double_to_int(std::string dVal) { 24 | return std::to_string((int)atof(dVal.c_str())); 25 | } 26 | 27 | void SList::setType (sType t) { 28 | type = t; 29 | } 30 | 31 | void SList::push(SList s) { 32 | list.push_back(s); 33 | } 34 | 35 | void SList::pushList(SLists s) { 36 | for (auto vi = s.begin(); vi != s.end(); vi++) 37 | list.push_back(*vi); 38 | } 39 | 40 | SList::sType SList::getType() const { 41 | return type; 42 | } 43 | 44 | std::string SList::getTypeString() const { 45 | if (type == SYMBOL) return "SYMBOL"; 46 | else if (type == NUMBER) return "NUMBER"; 47 | else if (type == LIST) return "LIST"; 48 | else if (type == PROC) return "PROC"; 49 | else return "LAMBDA"; 50 | } 51 | 52 | SLists SList::getList () const { 53 | return list; 54 | } 55 | 56 | SList::proc SList::getProc() const { 57 | return p; 58 | } 59 | 60 | std::string SList::val() const {return value;} 61 | 62 | std::string SList::listToString() { 63 | if (type == SYMBOL) return value; 64 | if (type == NUMBER) return double_is_int(value) ? double_to_int(value) : value; 65 | std::string strVal = ""; 66 | for (auto vi = list.begin(); vi != list.end(); vi++) { 67 | strVal += vi->listToString(); 68 | if (vi!=list.end()-1) 69 | strVal += " "; 70 | } 71 | return strVal; 72 | } 73 | 74 | 75 | //for debugging 76 | std::string SList::getPrintString() const { 77 | std::string s = ""; 78 | if (type == NUMBER) { 79 | s+=(double_is_int(value) ? double_to_int(value) : value); 80 | } else if (type == SYMBOL) { 81 | if (value == "#t") 82 | return "true"; 83 | else if (value == "#f") 84 | return "false"; 85 | else 86 | return value; 87 | } else if (type == PROC) { 88 | return "PROC"; 89 | } else if (type == LAMBDA) { 90 | return "LAMBDA"; 91 | } else { 92 | s+="("; 93 | for (auto vi = list.cbegin(); vi != list.cend(); vi++) { 94 | s+=vi->getPrintString(); 95 | if (vi!=list.cend()-1) s+= " "; 96 | } 97 | if (s.back() == ' ') s.pop_back(); 98 | s+=") "; 99 | } 100 | return s; 101 | } 102 | -------------------------------------------------------------------------------- /SList.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // SList.hpp 3 | // Scheme++ 4 | // 5 | // Created by Josh Sun on 2017-05-10. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #ifndef SList_hpp 10 | #define SList_hpp 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | #include 19 | 20 | 21 | 22 | class SList { 23 | public: 24 | typedef SList (*proc)(const std::vector&); 25 | enum sType {SYMBOL, NUMBER, LIST, PROC, LAMBDA}; 26 | 27 | void push(SList s); 28 | std::string getPrintString() const; 29 | size_t size() const; 30 | sType getType() const; 31 | void setType(sType t); 32 | std::string val() const; 33 | std::vector getList () const; 34 | std::string listToString(); 35 | proc getProc() const; 36 | void pushList(std::vector s); 37 | std::string getTypeString() const; 38 | 39 | SList(); 40 | SList(sType type); 41 | SList(std::string s); 42 | SList(double s); 43 | SList(std::vector s); 44 | SList(proc s); 45 | private: 46 | std::string value; 47 | std::vector list; 48 | proc p; 49 | 50 | sType type; 51 | 52 | 53 | }; 54 | 55 | typedef std::vector SLists; 56 | 57 | #endif /* SList_hpp */ 58 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // Scheme_Interpreter 4 | // 5 | // Created by Josh Sun on 2017-05-08. 6 | // Copyright © 2017 Josh Sun. All rights reserved. 7 | // 8 | 9 | #include "FormattedIO.hpp" 10 | #include "SList.hpp" 11 | #include "Parser.hpp" 12 | #include "NativeProcedures.hpp" 13 | #include "Environment.hpp" 14 | 15 | #include 16 | 17 | 18 | using namespace std; 19 | 20 | 21 | SLists getArgs (SList l) { 22 | SLists unproc_args = l.getList(); 23 | SLists args; 24 | for (int i = 1; i < unproc_args.size(); i++) { 25 | args.push_back(unproc_args[i]); 26 | } 27 | return args; 28 | } 29 | 30 | SList evaluate (SList s, Environment* env) { 31 | if (s.getType() == SList::SYMBOL) { //variable reference 32 | if (s.val()[0] == '\'') 33 | return SList(s.val().substr(1,s.val().length()-1)); 34 | return (*(env->find(s.val())))[s.val()]; 35 | } else if (s.getType() == SList::NUMBER) { //constant literal 36 | return s; 37 | } else if (s.getList().size() == 0) { 38 | return s; 39 | } else if (s.getList()[0].val() == "define") { 40 | return (env->env[s.getList()[1].val()] = evaluate(s.getList()[2],env)); 41 | } else if (s.getList()[0].val() == "lambda") { 42 | s.setType(SList::LAMBDA); 43 | return s; 44 | } else if (s.getList()[0].val() == "quote") { 45 | return s.getList()[1]; 46 | } else if (s.getList()[0].val() == "set!") { 47 | (*(env->find(s.getList()[1].val())))[s.getList()[1].val()] = evaluate(s.getList()[2],env); 48 | return SList(); 49 | } else if (s.getList()[0].val() == "if") { 50 | return evaluate(s.getList()[1],env).val()=="#t" ? evaluate(s.getList()[2],env) : (s.getList()[3].val() == "else" ? evaluate(s.getList()[4],env) : SList()); 51 | } else if (s.getList()[0].val() == "begin") { 52 | for (int i = 1; i < s.getList().size()-1; i++) evaluate(s.getList()[i], env); 53 | return evaluate(s.getList()[s.getList().size()-1],env); 54 | } else { //procedure call 55 | SList p = evaluate(s.getList()[0],env); 56 | SLists args = getArgs(s); 57 | for (int i = 0; i < args.size(); i++) 58 | args[i] = evaluate (args[i],env); 59 | if (p.getType() == SList::LAMBDA) { 60 | return evaluate(p.getList()[2], new Environment(p.getList()[1].getList(),args,env)); 61 | } else if (p.getType()==SList::PROC) { 62 | return p.getProc()(args); 63 | } else { 64 | return evaluate(p,env); 65 | } 66 | } 67 | } 68 | 69 | 70 | void env_setup (Environment* std_env) { 71 | std_env->env.insert({"#f",SList("#f")}); 72 | std_env->env.insert({"#f",SList("#t")}); 73 | std_env->env.insert({"nil",SList("nil")}); 74 | std_env->env.insert({"+",SList(&add)}); 75 | std_env->env.insert({"-",SList(&subtract)}); 76 | std_env->env.insert({"*",SList(&multiply)}); 77 | std_env->env.insert({"/",SList(÷)}); 78 | std_env->env.insert({"mod",SList(&mod)}); 79 | std_env->env.insert({"sqrt",SList(&sqrt)}); 80 | std_env->env.insert({"sin",SList(&sin)}); 81 | std_env->env.insert({"cos",SList(&cos)}); 82 | std_env->env.insert({"tan",SList(&tan)}); 83 | std_env->env.insert({"asin",SList(&arcsin)}); 84 | std_env->env.insert({"acos",SList(&arccos)}); 85 | std_env->env.insert({"atan",SList(&arctan)}); 86 | std_env->env.insert({"abs",SList(&abs)}); 87 | std_env->env.insert({">",SList(&greater_than)}); 88 | std_env->env.insert({"<",SList(&less_than)}); 89 | std_env->env.insert({"=",SList(&equal_num)}); 90 | std_env->env.insert({"=",SList(&equal_num)}); 91 | std_env->env.insert({"append",SList(&append)}); 92 | std_env->env.insert({"apply",SList(&apply)}); 93 | std_env->env.insert({"map",SList(&map)}); 94 | std_env->env.insert({"max",SList(&max)}); 95 | std_env->env.insert({"min",SList(&min)}); 96 | std_env->env.insert({"eqv",SList(&eqv)}); 97 | std_env->env.insert({"number?",SList(&isNumber)}); 98 | std_env->env.insert({"list?",SList(&isList)}); 99 | std_env->env.insert({"procedure?",SList(&isProcedure)}); 100 | std_env->env.insert({"null?",SList(&isNull)}); 101 | std_env->env.insert({"length",SList(&length)}); 102 | std_env->env.insert({"list",SList(&list)}); 103 | std_env->env.insert({"cons",SList(&cons)}); 104 | std_env->env.insert({"car",SList(&car)}); 105 | std_env->env.insert({"cdr",SList(&cdr)}); 106 | } 107 | 108 | //repl 109 | int main(int argc, const char * argv[]) { 110 | Environment* std_env = new Environment; 111 | env_setup (std_env); 112 | cout << FormattedIO::readFile("message.txt") << endl;; 113 | while (true) { 114 | SList temp; 115 | try { 116 | std::cout << "schm/> "; 117 | std::string line = FormattedIO::readLine(); 118 | if (line.length()==0) continue; 119 | //std::cout << line << std::endl; 120 | temp = evaluate(Parser::parse(line), std_env); 121 | } catch (const char* msg) { 122 | cerr << msg << endl; 123 | continue; 124 | } catch (...) { 125 | cerr << "Interpreter Error: Unhandled Exception" << endl; 126 | continue; 127 | } 128 | //cout << "(dev msg) " << temp.getTypeString() << endl; 129 | cout << "=> " << temp.getPrintString() << endl; 130 | 131 | } 132 | return 0; 133 | } 134 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | build: main.cpp 2 | g++ -std=c++11 -g -Wall *.cpp -o scheme 3 | clean: 4 | $(RM) scheme -------------------------------------------------------------------------------- /message.txt: -------------------------------------------------------------------------------- 1 | SchemePlusPlus 2 | MIT/GNU Scheme Interpreter 3 | Compiled With C++ Apple LLVM 8.1 4 | Version Beta 1.0.0 5 | ******************************* 6 | 7 | By Joshua Sun 8 | Software Engineering Student, University of Waterloo 9 | 10 | MIT License 11 | Copyright (c) 2017 Joshua Sun 12 | 13 | Check out the latest release @ 14 | https://github.com/jsun98/SchemePlusPlus --------------------------------------------------------------------------------