├── .gitignore ├── LICENSE.md ├── README.md ├── basic.hpp ├── examples ├── 01.example.cpp ├── 02.while.cpp ├── 03.string.cpp ├── 04.strfuncs.cpp ├── 05.bubblesort.cpp └── 06.array-2d.cpp └── structured_basic.hpp /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rollbear/basicpp/b47839f51cac016c4db70e3631b827461e50b3c3/.gitignore -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## **`C++`** today is as simple as **`BASIC`** was in the 80s 2 | 3 | Single header **`BASIC`** emulation in **`C++`**. This is based on my 4 | (flawed) memory of the **`BASIC`** dialect in the 5 | [EACA](https://en.wikipedia.org/wiki/EACA) 6 | [Colour Genie](https://en.wikipedia.org/wiki/Colour_Genie) computer I 7 | got on my 15th birthday. 8 | 9 | ## Versions 10 | 11 | * [Original BASIC](#basic) - AKA Dartmouth BASIC 12 | * [Structured BASIC] (#sbasic) 13 | 14 | 15 | 16 | ## Basic 17 | *Original BASIC - AKA Dartmouth BASIC* 18 | 19 | Header file: [basic.hpp](./basic.hpp) 20 | 21 | ### Example program: 22 | ```Cpp 23 | #include "basic.hpp" 24 | 25 | int main() 26 | { 27 | _10: LET COUNT = 1; 28 | _20: LET SUM = 0; 29 | _30: LET STACK$ = "."; 30 | _40: LET TRY = 0; 31 | _50: FOR TRY = 3 TO 1 STEP -1; 32 | _60: PRINT "You have ", TRY, " chances to get this right: "; 33 | _70: INPUT "Enter a positive number ", COUNT; 34 | _80: IF COUNT > 0 THEN GOTO _130; 35 | _90: PRINT "I said positive. ", COUNT, " isn't"; 36 | _100: NEXT; 37 | _110: PRINT "I give up on you!"; 38 | _120: END; 39 | _130: GOSUB _160; 40 | _140: PRINT "SUM=", SUM, " STACK=", STACK$; 41 | _150: END; 42 | _160: SUM = SUM + COUNT; 43 | _170: COUNT = COUNT - 1; 44 | _180: IF COUNT > 0 THEN GOSUB _160; 45 | _190: STACK$ = STACK$ + STACK$; 46 | _200: RETURN; 47 | } 48 | ``` 49 | 50 | ## SBASIC 51 | *Structured BASIC - AKA SBASIC* 52 | 53 | Header file: [structured_basic.hpp](./structured_basic.hpp) 54 | 55 | ### Example program: 56 | ```Cpp 57 | #include "structured_basic.hpp" 58 | 59 | int main() 60 | { 61 | _10: LET X = 1; 62 | _20: INPUT "enter a positive number: " , X; 63 | _30: IF X > 0 THEN GOTO _60; 64 | _40: PRINT "Positive means greater than 0"; 65 | _50: GOTO _20; 66 | _60: LET i = 0; 67 | _70: DO ; 68 | _80: PRINT i+1, " Basic is awesome"; 69 | _90: i = i + 1 ; 70 | _100: LOOP WHILE ( i < X ); 71 | } 72 | ``` 73 | 74 | ## Limitations 75 | * C++11 or later (easy to back-port, but why?) 76 | * Those pesky semicolons... 77 | * Variables must be defined prior to use 78 | * Can't be used in kernel modules ;-) 79 | * Many language features missing, e.g. PRINT USING and Most I/O 80 | 81 | PR's for fixes and added functionality are most welcome. 82 | -------------------------------------------------------------------------------- /basic.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Originally written by Björn Fahller (bjorn@fahller.se) 3 | // Modified by Jeff Bush 4 | // 5 | // No rights claimed. The sources are released to the public domain 6 | // 7 | // For license information, please refer to 8 | // 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | namespace basic { 20 | struct stack_frame { jmp_buf buf; }; 21 | std::forward_list stack; 22 | 23 | #define GOSUB \ 24 | if (!(setjmp((basic::stack.emplace_front(),basic::stack.front().buf)) \ 25 | && (basic::stack.pop_front(),true))) \ 26 | goto 27 | 28 | #define RETURN if (basic::stack.empty()) return 0; longjmp(basic::stack.front().buf, 1) 29 | 30 | template 31 | struct separator 32 | { 33 | static char const *str() { return ""; } 34 | }; 35 | 36 | template 37 | struct separator 38 | { 39 | static char const *str() { return ","; } 40 | }; 41 | 42 | inline void type_mismatch() { 43 | std::cout << "Type mismatch error\n"; 44 | exit(1); 45 | } 46 | 47 | inline void array_out_of_bounds() { 48 | std::cout << "Array out of bounds error\n"; 49 | exit(1); 50 | } 51 | 52 | inline void dimension_error() { 53 | std::cout << "Array Dimension error\n"; 54 | exit(1); 55 | } 56 | 57 | inline void illegal_quantity_error() { 58 | std::cout << "Illegal quantity error\n"; 59 | exit(1); 60 | } 61 | 62 | class variant 63 | { 64 | public: 65 | variant() 66 | : isnum(true), 67 | numval(0) 68 | {} 69 | 70 | variant(double _numval) 71 | : isnum(true), 72 | numval(_numval) 73 | {} 74 | 75 | variant(int _numval) 76 | : isnum(true), 77 | numval(_numval) 78 | {} 79 | 80 | variant(const std::string _strval) 81 | : isnum(false), 82 | strval(_strval) 83 | {} 84 | 85 | variant(const char *_strval) 86 | : isnum(false), 87 | strval(_strval) 88 | {} 89 | 90 | double numeric() const { 91 | if (!isnum) 92 | type_mismatch(); 93 | return numval; 94 | } 95 | 96 | std::string string() const { 97 | if (isnum) 98 | type_mismatch(); 99 | return strval; 100 | } 101 | variant toString() const { 102 | if (isnum) 103 | return variant(std::to_string(numval)); 104 | else 105 | return *this; 106 | } 107 | 108 | variant toNum() const { 109 | if (!isnum) 110 | return variant(stod(strval)); 111 | else 112 | return *this; 113 | } 114 | variant &operator=(const variant ©from) { 115 | isnum = copyfrom.isnum; 116 | numval = copyfrom.numval; 117 | strval = copyfrom.strval; 118 | return *this; 119 | } 120 | 121 | // Note that strings in BASIC are 1 based, so subtract 1 from the left 122 | // index 123 | variant midStr(const variant &left, const variant &right) const { 124 | if (isnum || !left.isnum || !right.isnum) 125 | type_mismatch(); 126 | 127 | return variant(strval.substr(int(left.numval) - 1, (int(right.numval) - int(left.numval) + 1))); 128 | } 129 | 130 | variant leftStr(const variant &count) const { 131 | if (isnum) 132 | type_mismatch(); 133 | 134 | return variant(strval.substr(0, int(count.numval))); 135 | } 136 | 137 | variant rightStr(const variant &count) const { 138 | if (isnum) 139 | type_mismatch(); 140 | 141 | return variant(strval.substr(strval.length() - int(count.numval) - 1, int(count.numval))); 142 | } 143 | 144 | variant strlen() const { 145 | if (isnum) 146 | type_mismatch(); 147 | 148 | return variant((int)strval.length()); 149 | } 150 | 151 | variant operator+(const variant op) const { 152 | if (isnum != op.isnum) 153 | type_mismatch(); 154 | 155 | if (isnum) 156 | return variant(numval + op.numval); 157 | else 158 | return variant(strval + op.strval); 159 | } 160 | 161 | variant operator-(const variant op) const { 162 | if (!isnum || !op.isnum) 163 | type_mismatch(); 164 | 165 | return variant(numval - op.numval); 166 | } 167 | 168 | variant operator*(const variant op) const { 169 | if (!isnum || !op.isnum) 170 | type_mismatch(); 171 | 172 | return variant(numval * op.numval); 173 | } 174 | 175 | variant operator/(const variant op) const { 176 | if (!isnum || !op.isnum) 177 | type_mismatch(); 178 | 179 | return variant(numval / op.numval); 180 | } 181 | 182 | bool operator>(const variant op) const { 183 | if (isnum != op.isnum) 184 | type_mismatch(); 185 | 186 | if (isnum) 187 | return numval > op.numval; 188 | else 189 | return strval > op.strval; 190 | } 191 | 192 | bool operator>=(const variant op) const { 193 | if (isnum != op.isnum) 194 | type_mismatch(); 195 | 196 | if (isnum) 197 | return numval >= op.numval; 198 | else 199 | return strval >= op.strval; 200 | } 201 | 202 | bool operator<(const variant op) const { 203 | if (isnum != op.isnum) 204 | type_mismatch(); 205 | 206 | if (isnum) 207 | return numval < op.numval; 208 | else 209 | return strval < op.strval; 210 | } 211 | 212 | bool operator<=(const variant op) const { 213 | if (isnum != op.isnum) 214 | type_mismatch(); 215 | 216 | if (isnum) 217 | return numval <= op.numval; 218 | else 219 | return strval <= op.strval; 220 | } 221 | 222 | bool operator!=(const variant op) const { 223 | if (isnum != op.isnum) 224 | type_mismatch(); 225 | 226 | if (isnum) 227 | return numval != op.numval; 228 | else 229 | return strval != op.strval; 230 | } 231 | 232 | bool operator==(const variant op) const { 233 | if (isnum != op.isnum) 234 | type_mismatch(); 235 | 236 | if (isnum) 237 | return numval == op.numval; 238 | else 239 | return strval == op.strval; 240 | } 241 | 242 | bool isnum; 243 | double numval; 244 | std::string strval; 245 | }; 246 | 247 | template 248 | typename std::enable_if::value && !std::is_same::type, variant>::value, variant>::type 249 | operator+(T&& t, variant const& p) 250 | { 251 | return variant(std::forward(t)) + p; 252 | } 253 | 254 | template 255 | typename std::enable_if::value && !std::is_same::type, variant>::value, variant>::type 256 | operator-(T&& t, variant const& p) 257 | { 258 | return variant(std::forward(t)) - p; 259 | } 260 | 261 | template 262 | typename std::enable_if::value && !std::is_same::type, variant>::value, variant>::type 263 | operator*(T&& t, variant const& p) 264 | { 265 | return variant(std::forward(t)) * p; 266 | } 267 | 268 | template 269 | typename std::enable_if::value && !std::is_same::type, variant>::value, variant>::type 270 | operator/(T&& t, variant const& p) 271 | { 272 | return variant(std::forward(t)) / p; 273 | } 274 | 275 | template 276 | typename std::enable_if::value && !std::is_same::type, variant>::value, bool>::type 277 | operator==(T&& t, variant const& p) 278 | { 279 | return variant(std::forward(t)) == p; 280 | } 281 | 282 | template 283 | struct is_dimmable 284 | { 285 | using DT = typename std::decay::type; 286 | static constexpr bool value = std::is_integral
::value || std::is_same::value; 287 | }; 288 | 289 | template