├── .gitignore ├── Makefile ├── README.md ├── build ├── examples ├── doublemath.bl ├── factorial.bl ├── fibonacci.bl ├── hello.bl ├── if.bl └── printn.bl ├── include ├── defs.h ├── errors.h ├── hash.h ├── import.h ├── io.h ├── ir.h ├── lex.h ├── meta.h ├── parse.h ├── source.h ├── str.h ├── term.h ├── type.h ├── utf8.h ├── value.h ├── vec.h └── x64.h ├── interpret ├── lib └── core.c ├── math.bl ├── run ├── run-tests ├── spec.md ├── src ├── basm.cpp ├── errors.cpp ├── hash.cpp ├── import.cpp ├── io.cpp ├── ir.cpp ├── lex.cpp ├── main.cpp ├── meta.cpp ├── parse.cpp ├── source.cpp ├── str.cpp ├── term.cpp ├── type.cpp ├── utf8.cpp ├── value.cpp └── x64.cpp └── test ├── array.bl ├── binary.bl ├── logic.bl ├── math.bl ├── meta-math.bl ├── pure.bl └── strings.bl /.gitignore: -------------------------------------------------------------------------------- 1 | basil 2 | *.o 3 | *.s 4 | .vscode 5 | *~ 6 | notes* 7 | hidden 8 | test/*.out 9 | perf* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: debug release 2 | 3 | SRC := ./src 4 | INCLUDE := ./include 5 | 6 | SRCFILES := $(wildcard $(SRC)/*.cpp) 7 | OBJFILES := $(patsubst $(SRC)/%.cpp,$(SRC)/%.o,$(SRCFILES)) 8 | 9 | CXX := clang++ 10 | CXXDEBUG := -std=c++14 -g3 -Wall -Wno-strict-aliasing -pedantic -I$(INCLUDE) 11 | CXXRELEASE := -std=c++14 -Wall -Wno-strict-aliasing -pedantic -s \ 12 | -Os -fno-ident -fno-rtti -fno-exceptions -fmerge-all-constants -I$(INCLUDE) 13 | 14 | CC := clang 15 | CFLAGS := -std=gnu99 -Os -fno-stack-protector 16 | 17 | debug: CXXFLAGS := $(CXXDEBUG) 18 | debug: basil 19 | debug: lib/core.o 20 | 21 | release: CXXFLAGS := $(CXXRELEASE) 22 | release: basil 23 | release: lib/core.o 24 | 25 | clean: 26 | rm $(wildcard $(SRC)/*.o) basil lib/core.o 27 | 28 | basil: $(OBJFILES) 29 | $(CXX) $(CXXFLAGS) -o basil $^ 30 | 31 | $(SRC)/%.o: $(SRC)/%.cpp 32 | $(CXX) $(CXXFLAGS) -c $< -o $@ 33 | 34 | lib/core.o: lib/core.c 35 | $(CC) $(CFLAGS) -c $< -o $@ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Basil 2 | 3 | Hey! Welcome to the Basil repository. :) 4 | 5 | Basil is currently a _work in progress_. The implementation present here isn't anywhere near usable, and the language isn't really set in stone. But if you'd like to play around with it, or see a more in-depth description of the language as it currently stands, please feel free to clone the project or take a look at the [specification draft](https://github.com/elucent/basil/blob/master/spec.md). 6 | 7 | **As of August 2020, the Basil Programming Language has been reworked, and is now housed at https://github.com/basilTeam/basil.** 8 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | 3 | FNAME=$1 4 | # trap 'tmp="BASH_COMMAND"; eval echo "${!tmp}"' DEBUG 5 | ./basil $FNAME > ${FNAME%.bl}.s 6 | as ${FNAME%.bl}.s -o ${FNAME%.bl}.o 7 | ld -static ${FNAME%.bl}.o lib/core.o -o ${FNAME%.bl} 8 | rm ${FNAME%.bl}.s ${FNAME%.bl}.o 9 | -------------------------------------------------------------------------------- /examples/doublemath.bl: -------------------------------------------------------------------------------- 1 | define let define 2 | let x = 1.0 + 2.0 + 3.0 3 | let y = x * x 4 | let z = y - x * 10 -------------------------------------------------------------------------------- /examples/factorial.bl: -------------------------------------------------------------------------------- 1 | define factorial = & { 2 | 0 -> 1 3 | i64 n -> n - 1 factorial * n 4 | } 5 | 6 | 10 factorial 7 | -------------------------------------------------------------------------------- /examples/fibonacci.bl: -------------------------------------------------------------------------------- 1 | define fib = { 2 | 0 -> 0 3 | & 1 -> 1 4 | & (i64 n) -> (n - 1 fib) + (n - 2 fib) 5 | } 6 | 7 | fib 40 8 | -------------------------------------------------------------------------------- /examples/hello.bl: -------------------------------------------------------------------------------- 1 | print "hello world!\n" -------------------------------------------------------------------------------- /examples/if.bl: -------------------------------------------------------------------------------- 1 | define println = (meta text) -> { 2 | print eval text 3 | print "\n" 4 | } 5 | 6 | define if = (meta cond) -> (meta body) -> { 7 | (1 -> eval body; 1) & (0 -> 0) (eval cond) 8 | } 9 | 10 | define else = (meta body) -> { 11 | (0 -> eval body; 1) & (1 -> 1) 12 | } 13 | 14 | if (1): 1 -------------------------------------------------------------------------------- /examples/printn.bl: -------------------------------------------------------------------------------- 1 | define print-times = & { 2 | 1 -> print "hello world!\n" 3 | i64 n -> print-times(n - 1); print "hello world!\n" 4 | } 5 | 6 | print-times 10 -------------------------------------------------------------------------------- /include/defs.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_DEFS_H 2 | #define BASIL_DEFS_H 3 | 4 | #include 5 | 6 | typedef uint8_t u8; 7 | typedef uint16_t u16; 8 | typedef uint32_t u32; 9 | typedef uint64_t u64; 10 | 11 | typedef int8_t i8; 12 | typedef int16_t i16; 13 | typedef int32_t i32; 14 | typedef int64_t i64; 15 | 16 | // ir.h 17 | 18 | namespace basil { 19 | struct Location; 20 | class CodeFrame; 21 | class Function; 22 | class CodeGenerator; 23 | class InsnClass; 24 | class Insn; 25 | class Data; 26 | class IntData; 27 | class FloatData; 28 | class CharData; 29 | class StringData; 30 | class AddInsn; 31 | class SubInsn; 32 | class MulInsn; 33 | class DivInsn; 34 | class ModInsn; 35 | class FAddInsn; 36 | class FSubInsn; 37 | class FMulInsn; 38 | class FDivInsn; 39 | class FModInsn; 40 | class AndInsn; 41 | class OrInsn; 42 | class NotInsn; 43 | class XorInsn; 44 | class EqInsn; 45 | class NotEqInsn; 46 | class LessInsn; 47 | class GreaterInsn; 48 | class LessEqInsn; 49 | class GreaterEqInsn; 50 | class GotoInsn; 51 | class CallInsn; 52 | class RetInsn; 53 | class MovInsn; 54 | class LeaInsn; 55 | class Label; 56 | } 57 | 58 | // hash.h 59 | 60 | template 61 | class set; 62 | 63 | template 64 | class map; 65 | 66 | // io.h 67 | 68 | class stream; 69 | class file; 70 | class buffer; 71 | 72 | // str.h 73 | 74 | class string; 75 | 76 | // utf8.h 77 | 78 | struct uchar; 79 | class ustring; 80 | 81 | // vec.h 82 | 83 | template 84 | class vector; 85 | 86 | namespace basil { 87 | 88 | // src.h 89 | 90 | class Source; 91 | 92 | // error.h 93 | 94 | struct Error; 95 | 96 | // lex.h 97 | 98 | struct Token; 99 | class TokenCache; 100 | 101 | // type.h 102 | 103 | class TypeClass; 104 | class Type; 105 | class NumericType; 106 | class TupleType; 107 | class UnionType; 108 | class IntersectionType; 109 | class FunctionType; 110 | 111 | // import.h 112 | 113 | class Module; 114 | 115 | // meta.h 116 | class Meta; 117 | class MetaRC; 118 | class MetaString; 119 | class MetaList; 120 | class MetaTuple; 121 | class MetaArray; 122 | class MetaUnion; 123 | class MetaIntersect; 124 | class MetaFunction; 125 | 126 | // term.h 127 | 128 | class TermClass; 129 | class Term; 130 | class IntegerTerm; 131 | class RationalTerm; 132 | class StringTerm; 133 | class CharTerm; 134 | class VariableTerm; 135 | class BlockTerm; 136 | class LambdaTerm; 137 | class AssignTerm; 138 | class ProgramTerm; 139 | 140 | // value.h 141 | 142 | class ValueClass; 143 | class Stack; 144 | class Value; 145 | class IntegerConstant; 146 | class RationalConstant; 147 | class StringConstant; 148 | class CharConstant; 149 | class TypeConstant; 150 | class Variable; 151 | class Block; 152 | class Lambda; 153 | class Program; 154 | class BinaryOp; 155 | class BinaryMath; 156 | class Add; 157 | class Subtract; 158 | class Multiply; 159 | class Divide; 160 | class Modulus; 161 | class BinaryLogic; 162 | class And; 163 | class Or; 164 | class Xor; 165 | class Not; 166 | class BinaryEquality; 167 | class Equal; 168 | class Inequal; 169 | class BinaryRelation; 170 | class Less; 171 | class LessEqual; 172 | class Greater; 173 | class GreaterEqual; 174 | class Join; 175 | class Intersect; 176 | class Define; 177 | class Autodefine; 178 | class Assign; 179 | class Cast; 180 | class Eval; 181 | } 182 | 183 | #endif -------------------------------------------------------------------------------- /include/errors.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_ERROR_H 2 | #define BASIL_ERROR_H 3 | 4 | #include "defs.h" 5 | #include "io.h" 6 | #include "vec.h" 7 | 8 | namespace basil { 9 | struct Error { 10 | const Source* src; 11 | u32 line, column; 12 | buffer message; 13 | 14 | Error(); 15 | void format(stream& io) const; 16 | }; 17 | 18 | enum Phase { 19 | PHASE_LEX, PHASE_PARSE, PHASE_TYPE 20 | }; 21 | 22 | void prefixPhase(buffer& b, Phase phase); 23 | void reportError(const Error& error); 24 | void useSource(Source* src); 25 | Source* currentSource(); 26 | u32 countErrors(); 27 | void printErrors(stream& io); 28 | 29 | void catchErrors(); 30 | void releaseErrors(); 31 | void discardErrors(); 32 | Error& lastError(); 33 | 34 | template 35 | void err(Phase phase, u32 line, u32 column, const Args&... args) { 36 | buffer b; 37 | fprint(b, "(", line, ":", column, ") ", args...); 38 | Error e; 39 | e.line = line, e.column = column; 40 | e.message = b; 41 | e.src = nullptr; 42 | reportError(e); 43 | } 44 | 45 | template 46 | void err(Phase phase, const Source* src, u32 line, u32 column, const Args&... args) { 47 | buffer b; 48 | fprint(b, "(", line, ":", column, ") ", args...); 49 | Error e; 50 | e.line = line, e.column = column; 51 | e.message = b; 52 | e.src = src; 53 | reportError(e); 54 | } 55 | 56 | template 57 | void note(Phase phase, u32 line, u32 column, const Args&... args) { 58 | buffer b; 59 | fprint(b, "(", line, ":", column, ") - ", args...); 60 | fprint(lastError().message, "\n", b); 61 | } 62 | } 63 | 64 | void print(stream& io, const basil::Error& e); 65 | void print(const basil::Error& e); 66 | 67 | #endif -------------------------------------------------------------------------------- /include/hash.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_HASH_H 2 | #define BASIL_HASH_H 3 | 4 | #include "defs.h" 5 | #include "str.h" 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | struct pair { 12 | T first; 13 | U second; 14 | bool operator==(const pair& other) const { 15 | return first == other.first && second == other.second; 16 | } 17 | }; 18 | 19 | template 20 | bool equals(const T& a, const T& b) { 21 | return a == b; 22 | } 23 | 24 | template 25 | bool key_equals(const T& a, const T& b) { 26 | return a.first == b.first; 27 | } 28 | 29 | u64 rotl(u64 u, u64 n); 30 | u64 rotr(u64 u, u64 n); 31 | u64 raw_hash(const void* t, uint64_t size); 32 | 33 | template 34 | u64 hash(const T& t) { 35 | return raw_hash((const u8*)&t, sizeof(T)); 36 | } 37 | 38 | template<> 39 | u64 hash(const char* const& s); 40 | 41 | template<> 42 | u64 hash(const string& s); 43 | 44 | template 45 | u64 key_hash(const T& a) { 46 | return hash(a.first); 47 | } 48 | 49 | template 50 | class set { 51 | enum bucket_status { 52 | EMPTY, GHOST, FILLED 53 | }; 54 | 55 | struct bucket { 56 | u8 data[sizeof(T)]; 57 | bucket_status status; 58 | 59 | bucket(): status(EMPTY) { 60 | // 61 | } 62 | 63 | ~bucket() { 64 | if (status == FILLED) (*(T*)data).~T(); 65 | } 66 | 67 | bucket(const bucket& other): status(other.status) { 68 | if (status == FILLED) new(data) T(*(T*)other.data); 69 | } 70 | 71 | bucket& operator=(const bucket& other) { 72 | if (this != &other) { 73 | evict(); 74 | status = other.status; 75 | if (status == FILLED) new(data) T(*(T*)other.data); 76 | } 77 | return *this; 78 | } 79 | 80 | inline const T& value() const { 81 | return *(const T*)data; 82 | } 83 | 84 | inline T& value() { 85 | return *(T*)data; 86 | } 87 | 88 | inline void fill(const T& value) { 89 | if (status != FILLED) { 90 | status = FILLED, new(data) T(value); 91 | } 92 | else this->value() = value; 93 | } 94 | 95 | inline void evict() { 96 | if (status == FILLED) { 97 | (*(T*)data).~T(); 98 | status = GHOST; 99 | } 100 | } 101 | 102 | inline void clear() { 103 | if (status == FILLED) (*(T*)data).~T(); 104 | status = EMPTY; 105 | } 106 | }; 107 | 108 | bucket* data; 109 | u32 _size, _capacity, _mask; 110 | bool (*equals)(const T&, const T&); 111 | u64 (*hash)(const T&); 112 | 113 | u32 log2(u32 capacity) { 114 | u32 ans = 0; 115 | while (capacity > 1) capacity = capacity >> 2, ++ ans; 116 | return ans; 117 | } 118 | 119 | void init(u32 size) { 120 | _size = 0, _capacity = size; 121 | data = new bucket[size]; 122 | } 123 | 124 | void swap(T& a, T& b) { 125 | T t = a; 126 | a = b; 127 | b = t; 128 | } 129 | 130 | void free() { 131 | delete[] data; 132 | } 133 | 134 | void copy(const bucket* bs) { 135 | for (u32 i = 0; i < _capacity; ++ i) { 136 | data[i] = bs[i]; 137 | } 138 | } 139 | 140 | void grow() { 141 | bucket* old = data; 142 | u32 oldsize = _capacity; 143 | init(_capacity * 2); 144 | _mask = (_mask << 1) | 1; 145 | for (u32 i = 0; i < oldsize; ++ i) { 146 | if (old[i].status == FILLED) insert(old[i].value()); 147 | } 148 | delete[] old; 149 | } 150 | 151 | public: 152 | set(bool (*equals_in)(const T&, const T&) = ::equals, 153 | u64 (*hash_in)(const T&) = ::hash): 154 | equals(equals_in), hash(hash_in) { 155 | init(8); 156 | _mask = 7; 157 | } 158 | 159 | set(const std::initializer_list& init, 160 | bool (*equals_in)(const T&, const T&) = ::equals, 161 | u64 (*hash_in)(const T&) = ::hash): set(equals_in, hash_in) { 162 | for (const T& t : init) insert(t); 163 | } 164 | 165 | ~set() { 166 | free(); 167 | } 168 | 169 | set(const set& other): set(other.equals, other.hash) { 170 | init(other._capacity); 171 | _size = other._size, _mask = other._mask; 172 | copy(other.data); 173 | } 174 | 175 | set& operator=(const set& other) { 176 | if (this != &other) { 177 | free(); 178 | init(other._capacity); 179 | hash = other.hash, equals = other.equals; 180 | _size = other._size, _mask = other._mask; 181 | copy(other.data); 182 | } 183 | return *this; 184 | } 185 | 186 | class const_iterator { 187 | const bucket *ptr, *end; 188 | friend class set; 189 | public: 190 | const_iterator(const bucket* ptr_in, const bucket* end_in): 191 | ptr(ptr_in), end(end_in) { 192 | // 193 | } 194 | 195 | const T& operator*() const { 196 | return ptr->value(); 197 | } 198 | 199 | const T* operator->() const { 200 | return &(ptr->value()); 201 | } 202 | 203 | const_iterator& operator++() { 204 | if (ptr != end) ++ ptr; 205 | while (ptr != end && ptr->status != FILLED) ++ ptr; 206 | return *this; 207 | } 208 | 209 | const_iterator operator++(int) { 210 | iterator it = *this; 211 | operator++(); 212 | return it; 213 | } 214 | 215 | bool operator==(const const_iterator& other) const { 216 | return ptr == other.ptr; 217 | } 218 | 219 | bool operator!=(const const_iterator& other) const { 220 | return ptr != other.ptr; 221 | } 222 | }; 223 | 224 | class iterator { 225 | bucket *ptr, *end; 226 | friend class set; 227 | public: 228 | iterator(bucket* ptr_in, bucket* end_in): ptr(ptr_in), end(end_in) { 229 | // 230 | } 231 | 232 | T& operator*() { 233 | return ptr->value(); 234 | } 235 | 236 | T* operator->() { 237 | return &(ptr->value()); 238 | } 239 | 240 | iterator& operator++() { 241 | if (ptr != end) ++ ptr; 242 | while (ptr != end && ptr->status != FILLED) ++ ptr; 243 | return *this; 244 | } 245 | 246 | iterator operator++(int) { 247 | iterator it = *this; 248 | operator++(); 249 | return it; 250 | } 251 | 252 | bool operator==(const iterator& other) const { 253 | return ptr == other.ptr; 254 | } 255 | 256 | bool operator!=(const iterator& other) const { 257 | return ptr != other.ptr; 258 | } 259 | 260 | operator const_iterator() const { 261 | return const_iterator(ptr, end); 262 | } 263 | }; 264 | 265 | iterator begin() { 266 | bucket *start = data, *end = data + _capacity; 267 | while (start != end && start->status != FILLED) ++ start; 268 | return iterator(start, end); 269 | } 270 | 271 | const_iterator begin() const { 272 | const bucket *start = data, *end = data + _capacity; 273 | while (start != end && start->status != FILLED) ++ start; 274 | return const_iterator(start, end); 275 | } 276 | 277 | iterator end() { 278 | return iterator(data + _capacity, data + _capacity); 279 | } 280 | 281 | const_iterator end() const { 282 | return const_iterator(data + _capacity, data + _capacity); 283 | } 284 | 285 | void insert(const T& t) { 286 | if (double(_size + 1) / double(_capacity) > 0.625) grow(); 287 | u64 h = hash(t); 288 | u64 dist = 0; 289 | u64 i = h & _mask; 290 | T item = t; 291 | while (true) { 292 | if (data[i].status == EMPTY || data[i].status == GHOST) { 293 | data[i].fill(item); 294 | ++ _size; 295 | return; 296 | } 297 | 298 | if (data[i].status == FILLED && equals(data[i].value(), item)) { 299 | return; 300 | } 301 | 302 | u64 other_dist = (i - (hash(data[i].value()) & _mask)) & _mask; 303 | if (other_dist < dist) { 304 | if (data[i].status == GHOST) { 305 | data[i].fill(item); 306 | ++ _size; 307 | return; 308 | } 309 | swap(item, data[i].value()); 310 | dist = other_dist; 311 | } 312 | i = (i + 1) & _mask; 313 | ++ dist; 314 | } 315 | } 316 | 317 | void erase(const T& t) { 318 | u64 h = hash(t); 319 | u64 i = h & _mask; 320 | while (true) { 321 | if (data[i].status == EMPTY) return; 322 | if (data[i].status == FILLED && equals(data[i].value(), t)) { 323 | data[i].evict(); 324 | -- _size; 325 | return; 326 | } 327 | i = (i + 1) & _mask; 328 | } 329 | } 330 | 331 | const_iterator find(const T& t) const { 332 | u64 h = hash(t); 333 | u64 i = h & _mask; 334 | while (true) { 335 | if (data[i].status == EMPTY) return end(); 336 | u64 dist = (i - h) & _mask; 337 | u64 oh = hash(data[i].value()); 338 | u64 other_dist = (i - (oh & _mask)) & _mask; 339 | if (other_dist < dist) return end(); 340 | if (data[i].status == FILLED && equals(data[i].value(), t)) { 341 | return const_iterator(data + i, data + _capacity); 342 | } 343 | i = (i + 1) & _mask; 344 | } 345 | } 346 | 347 | iterator find(const T& t) { 348 | u64 h = hash(t); 349 | u64 i = h & _mask; 350 | while (true) { 351 | if (data[i].status == EMPTY) return end(); 352 | u64 dist = (i - (h & _mask)) & _mask; 353 | u64 oh = hash(data[i].value()); 354 | u64 other_dist = (i - (oh & _mask)) & _mask; 355 | if (other_dist < dist) return end(); 356 | if (data[i].status == FILLED && equals(data[i].value(), t)) { 357 | return iterator(data + i, data + _capacity); 358 | } 359 | i = (i + 1) & _mask; 360 | } 361 | } 362 | 363 | u32 size() const { 364 | return _size; 365 | } 366 | 367 | u32 capacity() const { 368 | return _capacity; 369 | } 370 | }; 371 | 372 | template 373 | class map : public set> { 374 | public: 375 | map(): set>(::key_equals>, ::key_hash>) { 376 | // 377 | } 378 | 379 | map(const std::initializer_list>& init): 380 | map() { 381 | for (auto& p : init) put(p.first, p.second); 382 | } 383 | 384 | void put(const K& key, const V& value) { 385 | set>::insert({ key, value }); 386 | } 387 | 388 | void erase(const K& key) { 389 | set>::erase({ key, V() }); 390 | } 391 | 392 | V& operator[](const K& key) { 393 | pair dummy = { key, V() }; 394 | auto it = set>::find(dummy); 395 | if (it == set>::end()) { 396 | set>::insert(dummy); 397 | } 398 | return set>::find(dummy)->second; 399 | } 400 | 401 | const V& operator[](const K& key) const { 402 | pair dummy = { key, V() }; 403 | auto it = set>::find(dummy); 404 | if (it == set>::end()) { 405 | return *(const V*)nullptr; 406 | } 407 | return set>::find(dummy)->second; 408 | } 409 | 410 | typename set>::const_iterator find(const K& key) const { 411 | return set>::find({ key, V() }); 412 | } 413 | 414 | typename set>::iterator find(const K& key) { 415 | return set>::find({ key, V() }); 416 | } 417 | }; 418 | 419 | #endif 420 | -------------------------------------------------------------------------------- /include/import.h: -------------------------------------------------------------------------------- 1 | #include "defs.h" 2 | #include "hash.h" 3 | #include "str.h" 4 | 5 | namespace basil { 6 | extern map modules; 7 | 8 | class Module { 9 | string _path; 10 | Source* _src; 11 | ProgramTerm* _body; 12 | Stack* _env; 13 | public: 14 | Module(const string& path, Source* src, ProgramTerm* body, Stack* env); 15 | ~Module(); 16 | void useIn(Stack& stack, u32 line, u32 column); 17 | }; 18 | 19 | void freeModules(); 20 | Module* loadModule(const char* path, u32 line, u32 column); 21 | } -------------------------------------------------------------------------------- /include/io.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_IO_H 2 | #define BASIL_IO_H 3 | 4 | #include "defs.h" 5 | #include "stdio.h" 6 | 7 | class stream { 8 | public: 9 | virtual void write(u8 c) = 0; 10 | virtual u8 read() = 0; 11 | virtual u8 peek() const = 0; 12 | virtual void unget(u8 c) = 0; 13 | virtual operator bool() const = 0; 14 | }; 15 | 16 | bool exists(const char* path); 17 | 18 | class file : public stream { 19 | FILE* f; 20 | bool done; 21 | public: 22 | file(const char* fname, const char* flags); 23 | file(FILE* f_in); 24 | ~file(); 25 | file(const file& other) = delete; 26 | file& operator=(const file& other) = delete; 27 | 28 | void write(u8 c) override; 29 | u8 read() override; 30 | u8 peek() const override; 31 | void unget(u8 c) override; 32 | operator bool() const override; 33 | }; 34 | 35 | class buffer : public stream { 36 | u8* data; 37 | u32 _start, _end, _capacity; 38 | u8 buf[8]; 39 | 40 | void init(u32 size); 41 | void free(); 42 | void copy(u8* other, u32 size, u32 start, u32 end); 43 | void grow(); 44 | public: 45 | buffer(); 46 | ~buffer(); 47 | buffer(const buffer& other); 48 | buffer& operator=(const buffer& other); 49 | 50 | void write(u8 c) override; 51 | u8 read() override; 52 | u8 peek() const override; 53 | void unget(u8 c) override; 54 | u32 size() const; 55 | u32 capacity() const; 56 | operator bool() const override; 57 | u8* begin(); 58 | const u8* begin() const; 59 | u8* end(); 60 | const u8* end() const; 61 | }; 62 | 63 | extern stream &_stdin, &_stdout; 64 | void setprecision(u32 p); 65 | 66 | void print(stream& io, u8 c); 67 | void print(stream& io, u16 n); 68 | void print(stream& io, u32 n); 69 | void print(stream& io, u64 n); 70 | void print(stream& io, i8 c); 71 | void print(stream& io, i16 n); 72 | void print(stream& io, i32 n); 73 | void print(stream& io, i64 n); 74 | void print(stream& io, float f); 75 | void print(stream& io, double d); 76 | void print(stream& io, char c); 77 | void print(stream& io, bool b); 78 | void print(stream& io, const u8* s); 79 | void print(stream& io, const char* s); 80 | void print(stream& io, const buffer& b); 81 | 82 | void print(u8 c); 83 | void print(u16 n); 84 | void print(u32 n); 85 | void print(u64 n); 86 | void print(i8 c); 87 | void print(i16 n); 88 | void print(i32 n); 89 | void print(i64 n); 90 | void print(float f); 91 | void print(double d); 92 | void print(char c); 93 | void print(bool b); 94 | void print(const u8* s); 95 | void print(const char* s); 96 | void print(const buffer& b); 97 | 98 | template 99 | void print(stream& io, const T& a, const Args&... args) { 100 | print(io, a); 101 | print(io, args...); 102 | } 103 | 104 | template 105 | void print(const T& a, const Args&... args) { 106 | print(a); 107 | print(args...); 108 | } 109 | 110 | template 111 | void fprint(S& s, const Args&... args) { 112 | print((stream&)s, args...); 113 | } 114 | 115 | template 116 | void println(stream& io, const Args&... args) { 117 | print(io, args...); 118 | print(io, '\n'); 119 | } 120 | 121 | template 122 | void println(const Args&... args) { 123 | print(args...); 124 | print('\n'); 125 | } 126 | 127 | template 128 | void fprintln(S& s, const Args&... args) { 129 | println((stream&)s, args...); 130 | } 131 | 132 | bool isspace(u8 c); 133 | 134 | void read(stream& io, u8& c); 135 | void read(stream& io, u16& n); 136 | void read(stream& io, u32& n); 137 | void read(stream& io, u64& n); 138 | void read(stream& io, i8& c); 139 | void read(stream& io, i16& n); 140 | void read(stream& io, i32& n); 141 | void read(stream& io, i64& n); 142 | void read(stream& io, float& f); 143 | void read(stream& io, double& d); 144 | void read(stream& io, char& c); 145 | void read(stream& io, bool& b); 146 | void read(stream& io, u8* s); 147 | void read(stream& io, char* s); 148 | 149 | void read(u8& c); 150 | void read(u16& n); 151 | void read(u32& n); 152 | void read(u64& n); 153 | void read(i8& c); 154 | void read(i16& n); 155 | void read(i32& n); 156 | void read(i64& n); 157 | void read(float& f); 158 | void read(double& d); 159 | void read(char& c); 160 | void read(bool& b); 161 | void read(u8* s); 162 | void read(char* s); 163 | 164 | template 165 | void read(stream& io, T& t, Args&... args) { 166 | read(io, t); 167 | read(io, args...); 168 | } 169 | 170 | template 171 | void read(T& t, Args&... args) { 172 | read(t); 173 | read(args...); 174 | } 175 | 176 | template 177 | void fread(S& s, Args&... args) { 178 | read((stream&)s, args...); 179 | } 180 | 181 | #endif -------------------------------------------------------------------------------- /include/lex.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_LEX_H 2 | #define BASIL_LEX_H 3 | 4 | #include "defs.h" 5 | #include "str.h" 6 | #include "utf8.h" 7 | #include "vec.h" 8 | #include "io.h" 9 | #include "source.h" 10 | #include "errors.h" 11 | 12 | namespace basil { 13 | extern const u32 14 | TOKEN_NONE, 15 | TOKEN_IDENT, 16 | TOKEN_STRING, 17 | TOKEN_CHAR, 18 | TOKEN_NUMBER, 19 | TOKEN_SYMBOL, 20 | TOKEN_LPAREN, 21 | TOKEN_RPAREN, 22 | TOKEN_LBRACE, 23 | TOKEN_RBRACE, 24 | TOKEN_LBRACK, 25 | TOKEN_RBRACK, 26 | TOKEN_COLON, 27 | TOKEN_SEMI, 28 | TOKEN_NEWLINE, 29 | TOKEN_ASSIGN, 30 | TOKEN_LAMBDA, 31 | TOKEN_DOT, 32 | TOKEN_PLUS, 33 | TOKEN_MINUS, 34 | TOKEN_EVAL, 35 | TOKEN_REF, 36 | TOKEN_BOOL, 37 | TOKEN_QUOTE; 38 | 39 | extern const char* TOKEN_NAMES[24]; 40 | 41 | struct Token { 42 | ustring value; 43 | u32 type; 44 | u32 line, column; 45 | 46 | Token(); 47 | 48 | Token(const ustring& value_in, u32 type_in, 49 | u32 line_in, u32 column_in); 50 | 51 | operator bool() const; 52 | }; 53 | 54 | class TokenCache { 55 | const static Token NONE; 56 | vector tokens; 57 | Source* _src; 58 | public: 59 | TokenCache(Source* src = nullptr); 60 | void push(const Token& t); 61 | 62 | class View { 63 | TokenCache* _cache; 64 | u32 i; 65 | public: 66 | View(TokenCache* cache); 67 | View(TokenCache* cache, u32 index); 68 | 69 | const Token& read(); 70 | const Token& peek() const; 71 | TokenCache& cache(); 72 | operator bool() const; 73 | }; 74 | 75 | Source* source(); 76 | View view(); 77 | View expand(stream& io); 78 | u32 size() const; 79 | const Token* begin() const; 80 | const Token* end() const; 81 | }; 82 | 83 | Token scan(Source::View& view); 84 | TokenCache lex(Source& src); 85 | } 86 | 87 | void print(stream& io, const basil::Token& t); 88 | void print(const basil::Token& t); 89 | void print(stream& io, const basil::TokenCache& c); 90 | void print(const basil::TokenCache& c); 91 | void read(stream& io, basil::Token& t); 92 | void read(basil::Token& t); 93 | void read(stream& io, basil::TokenCache& c); 94 | void read(basil::TokenCache& c); 95 | 96 | #endif -------------------------------------------------------------------------------- /include/meta.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_META_H 2 | #define BASIL_META_H 3 | 4 | #include "defs.h" 5 | #include "vec.h" 6 | #include "hash.h" 7 | #include "utf8.h" 8 | #include "io.h" 9 | 10 | namespace basil { 11 | extern u64 findSymbol(const ustring& name); 12 | extern const ustring& findSymbol(u64 name); 13 | 14 | class Meta { 15 | const Type* _type; 16 | union { 17 | i64 i; 18 | double d; 19 | const Type* t; 20 | bool b; 21 | Meta* r; 22 | MetaString* s; 23 | MetaList* l; 24 | MetaTuple* tu; 25 | MetaArray* a; 26 | MetaUnion* un; 27 | MetaIntersect* in; 28 | MetaFunction* f; 29 | } value; 30 | 31 | void free(); 32 | void copy(const Meta& other); 33 | void assign(const Meta& other); 34 | public: 35 | Meta(); 36 | Meta(const Type* type); 37 | Meta(const Type* type, i64 i); 38 | Meta(const Type* type, double d); 39 | Meta(const Type* type, const Type* t); 40 | Meta(const Type* type, bool b); 41 | Meta(const Type* type, Meta& r); 42 | Meta(const Type* type, const ustring& s); 43 | Meta(const Type* type, MetaList* l); 44 | Meta(const Type* type, MetaTuple* tu); 45 | Meta(const Type* type, MetaArray* a); 46 | Meta(const Type* type, MetaUnion* un); 47 | Meta(const Type* type, MetaIntersect* in); 48 | Meta(const Type* type, MetaFunction* f); 49 | ~Meta(); 50 | Meta(const Meta& other); 51 | Meta& operator=(const Meta& other); 52 | const Type* type() const; 53 | bool isVoid() const; 54 | bool isInt() const; 55 | i64 asInt() const; 56 | i64& asInt(); 57 | bool isFloat() const; 58 | double asFloat() const; 59 | double& asFloat(); 60 | bool isType() const; 61 | const Type* asType() const; 62 | const Type*& asType(); 63 | bool isBool() const; 64 | bool asBool() const; 65 | bool& asBool(); 66 | bool isSymbol() const; 67 | i64 asSymbol() const; 68 | i64& asSymbol(); 69 | bool isRef() const; 70 | const Meta& asRef() const; 71 | Meta& asRef(); 72 | bool isString() const; 73 | const ustring& asString() const; 74 | ustring& asString(); 75 | bool isList() const; 76 | const MetaList& asList() const; 77 | MetaList& asList(); 78 | bool isTuple() const; 79 | const MetaTuple& asTuple() const; 80 | MetaTuple& asTuple(); 81 | bool isArray() const; 82 | const MetaArray& asArray() const; 83 | MetaArray& asArray(); 84 | bool isUnion() const; 85 | const MetaUnion& asUnion() const; 86 | MetaUnion& asUnion(); 87 | bool isIntersect() const; 88 | const MetaIntersect& asIntersect() const; 89 | MetaIntersect& asIntersect(); 90 | bool isFunction() const; 91 | const MetaFunction& asFunction() const; 92 | MetaFunction& asFunction(); 93 | operator bool() const; 94 | Meta clone() const; 95 | void format(stream& io) const; 96 | bool operator==(const Meta& fr) const; 97 | bool operator!=(const Meta& fr) const; 98 | ustring toString() const; 99 | u64 hash() const; 100 | }; 101 | 102 | class MetaRC { 103 | u32 rc; 104 | public: 105 | MetaRC(); 106 | virtual ~MetaRC(); 107 | void inc(); 108 | void dec(); 109 | virtual Meta clone(const Meta& src) const = 0; 110 | }; 111 | 112 | class MetaString : public MetaRC { 113 | ustring s; 114 | public: 115 | MetaString(const ustring& str); 116 | const ustring& str() const; 117 | ustring& str(); 118 | Meta clone(const Meta& src) const override; 119 | }; 120 | 121 | class MetaList : public MetaRC { 122 | Meta v; 123 | Meta n; 124 | public: 125 | MetaList(); 126 | MetaList(Meta val, Meta next); 127 | const Meta& head() const; 128 | Meta& head(); 129 | const Meta& tail() const; 130 | Meta& tail(); 131 | Meta clone(const Meta& src) const override; 132 | }; 133 | 134 | class MetaTuple : public MetaRC { 135 | vector vals; 136 | public: 137 | MetaTuple(const vector& values); 138 | const Meta& operator[](u32 i) const; 139 | Meta& operator[](u32 i); 140 | const Meta* begin() const; 141 | const Meta* end() const; 142 | Meta* begin(); 143 | Meta* end(); 144 | u32 size() const; 145 | Meta clone(const Meta& src) const override; 146 | }; 147 | 148 | class MetaArray : public MetaRC { 149 | vector vals; 150 | public: 151 | MetaArray(const vector& values); 152 | const Meta& operator[](u32 i) const; 153 | Meta& operator[](u32 i); 154 | const Meta* begin() const; 155 | const Meta* end() const; 156 | Meta* begin(); 157 | Meta* end(); 158 | u32 size() const; 159 | Meta clone(const Meta& src) const override; 160 | }; 161 | 162 | class MetaUnion : public MetaRC { 163 | Meta real; 164 | public: 165 | MetaUnion(const Meta& val); 166 | bool is(const Type* t) const; 167 | const Meta& value() const; 168 | Meta& value(); 169 | Meta clone(const Meta& src) const override; 170 | }; 171 | 172 | class MetaIntersect : public MetaRC { 173 | vector vals; 174 | public: 175 | MetaIntersect(const vector& values); 176 | const Meta& as(const Type* t) const; 177 | Meta& as(const Type* t); 178 | const Meta* begin() const; 179 | const Meta* end() const; 180 | Meta* begin(); 181 | Meta* end(); 182 | u32 size() const; 183 | Meta clone(const Meta& src) const override; 184 | }; 185 | 186 | class MetaFunction : public MetaRC { 187 | Value* fn; 188 | map* _captures; 189 | public: 190 | MetaFunction(Value* function); 191 | MetaFunction(Value* function, const map& captures); 192 | ~MetaFunction(); 193 | Value* value() const; 194 | map* captures(); 195 | const map* captures() const; 196 | Meta clone(const Meta& src) const override; 197 | }; 198 | 199 | i64 trunc(i64 n, const Type* dest); 200 | u64 trunc(u64 n, const Type* dest); 201 | double toFloat(const Meta& m); 202 | i64 toInt(const Meta& m); 203 | u64 toUint(const Meta& m); 204 | 205 | Meta add(const Meta& lhs, const Meta& rhs); 206 | Meta sub(const Meta& lhs, const Meta& rhs); 207 | Meta mul(const Meta& lhs, const Meta& rhs); 208 | Meta div(const Meta& lhs, const Meta& rhs); 209 | Meta mod(const Meta& lhs, const Meta& rhs); 210 | Meta andf(const Meta& lhs, const Meta& rhs); 211 | Meta orf(const Meta& lhs, const Meta& rhs); 212 | Meta xorf(const Meta& lhs, const Meta& rhs); 213 | Meta notf(const Meta& operand); 214 | Meta equal(const Meta& lhs, const Meta& rhs); 215 | Meta inequal(const Meta& lhs, const Meta& rhs); 216 | Meta less(const Meta& lhs, const Meta& rhs); 217 | Meta lessequal(const Meta& lhs, const Meta& rhs); 218 | Meta greater(const Meta& lhs, const Meta& rhs); 219 | Meta greaterequal(const Meta& lhs, const Meta& rhs); 220 | Meta cons(const Meta& lhs, const Meta& rhs); 221 | Meta join(const Meta& lhs, const Meta& rhs); 222 | Meta unionf(const Meta& lhs, const Meta& rhs); 223 | Meta intersect(const Meta& lhs, const Meta& rhs); 224 | void assign(Meta& lhs, const Meta& rhs); 225 | } 226 | 227 | template<> 228 | u64 hash(const basil::Meta& m); 229 | 230 | void print(stream& io, const basil::Meta& m); 231 | void print(const basil::Meta& m); 232 | 233 | #endif -------------------------------------------------------------------------------- /include/parse.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_PARSE_H 2 | #define BASIL_PARSE_H 3 | 4 | #include "defs.h" 5 | #include "lex.h" 6 | 7 | namespace basil { 8 | Term* parse(TokenCache::View& view, bool repl = false); 9 | ProgramTerm* parseFull(TokenCache::View& view, bool repl = false); 10 | } 11 | 12 | #endif -------------------------------------------------------------------------------- /include/source.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_SOURCE_H 2 | #define BASIL_SOURCE_H 3 | 4 | #include "vec.h" 5 | #include "utf8.h" 6 | #include "io.h" 7 | 8 | namespace basil { 9 | class Source { 10 | vector lines; 11 | void add(uchar c); 12 | void checkNewline(); 13 | public: 14 | Source(); 15 | Source(stream& f); 16 | explicit Source(const char* path); 17 | 18 | class View { 19 | const Source* src; 20 | u32 _line, _column; 21 | public: 22 | View(const Source* src_in); 23 | View(const Source* src_in, u32 line, u32 column); 24 | 25 | void rewind(); 26 | uchar read(); 27 | uchar peek() const; 28 | u32 line() const; 29 | u32 column() const; 30 | const Source* source() const; 31 | }; 32 | 33 | void load(stream& f); 34 | void add(const ustring& line); 35 | void add(stream& io); 36 | ustring& line(u32 line); 37 | const ustring& line(u32 line) const; 38 | u32 size() const; 39 | View view() const; 40 | View expand(stream& io); 41 | }; 42 | } 43 | 44 | void print(stream& io, const basil::Source& src); 45 | void print(const basil::Source& src); 46 | void read(stream& io, basil::Source& src); 47 | void read(basil::Source& src); 48 | 49 | #endif -------------------------------------------------------------------------------- /include/str.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_STRING_H 2 | #define BASIL_STRING_H 3 | 4 | #include "defs.h" 5 | #include "stdio.h" 6 | 7 | class string { 8 | u8* data; 9 | u32 _size, _capacity; 10 | u8 buf[16]; 11 | 12 | void free(); 13 | void init(u32 size); 14 | void copy(const u8* s); 15 | void grow(); 16 | i32 cmp(const u8* s) const; 17 | i32 cmp(const char* s) const; 18 | 19 | public: 20 | string(); 21 | ~string(); 22 | string(const string& other); 23 | string(const char* s); 24 | string& operator=(const string& other); 25 | 26 | string& operator+=(u8 c); 27 | string& operator+=(char c); 28 | string& operator+=(const u8* s); 29 | string& operator+=(const char* s); 30 | string& operator+=(const string& s); 31 | u32 size() const; 32 | u32 capacity() const; 33 | u8 operator[](u32 i) const; 34 | u8& operator[](u32 i); 35 | const u8* raw() const; 36 | bool operator==(const u8* s) const; 37 | bool operator==(const char* s) const; 38 | bool operator==(const string& s) const; 39 | bool operator<(const u8* s) const; 40 | bool operator<(const char* s) const; 41 | bool operator<(const string& s) const; 42 | bool operator>(const u8* s) const; 43 | bool operator>(const char* s) const; 44 | bool operator>(const string& s) const; 45 | bool operator!=(const u8* s) const; 46 | bool operator!=(const char* s) const; 47 | bool operator!=(const string& s) const; 48 | bool operator<=(const u8* s) const; 49 | bool operator<=(const char* s) const; 50 | bool operator<=(const string& s) const; 51 | bool operator>=(const u8* s) const; 52 | bool operator>=(const char* s) const; 53 | bool operator>=(const string& s) const; 54 | }; 55 | 56 | string operator+(string s, char c); 57 | string operator+(string s, const char* cs); 58 | string operator+(string s, const string& cs); 59 | 60 | void print(stream& io, const string& s); 61 | void print(const string& s); 62 | void read(stream& io, string& s); 63 | void read(string& s); 64 | 65 | #endif -------------------------------------------------------------------------------- /include/term.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_TERM_H 2 | #define BASIL_TERM_H 3 | 4 | #include "defs.h" 5 | #include "vec.h" 6 | #include "utf8.h" 7 | #include "meta.h" 8 | 9 | namespace basil { 10 | class TermClass { 11 | const TermClass* _parent; 12 | public: 13 | TermClass(); 14 | TermClass(const TermClass& parent); 15 | const TermClass* parent() const; 16 | }; 17 | 18 | class Term { 19 | u32 _line, _column; 20 | Term* _parent; 21 | const TermClass* _termclass; 22 | protected: 23 | vector _children; 24 | void indent(stream& io, u32 level) const; 25 | public: 26 | static const TermClass CLASS; 27 | 28 | Term(u32 line, u32 column, const TermClass* tc = &CLASS); 29 | virtual ~Term(); 30 | u32 line() const; 31 | u32 column() const; 32 | void setParent(Term* parent); 33 | virtual void format(stream& io, u32 level = 0) const = 0; 34 | virtual void eval(Stack& stack) = 0; 35 | virtual bool equals(const Term* other) const = 0; 36 | virtual u64 hash() const = 0; 37 | virtual Term* clone() const = 0; 38 | virtual const Type* type() const = 0; 39 | virtual Meta fold() const = 0; 40 | virtual void repr(stream& io) const = 0; 41 | 42 | template 43 | bool is() const { 44 | const TermClass* ptr = _termclass; 45 | while (ptr) { 46 | if (ptr == &T::CLASS) return true; 47 | ptr = ptr->parent(); 48 | } 49 | return false; 50 | } 51 | 52 | template 53 | T* as() { 54 | return (T*)this; 55 | } 56 | 57 | template 58 | const T* as() const { 59 | return (const T*)this; 60 | } 61 | 62 | template 63 | void foreach(const Func& func) { 64 | func(this); 65 | for (Term* v : _children) v->foreach(func); 66 | } 67 | }; 68 | 69 | class IntegerTerm : public Term { 70 | i64 _value; 71 | public: 72 | static const TermClass CLASS; 73 | 74 | IntegerTerm(i64 value, u32 line, u32 column, 75 | const TermClass* tc = &CLASS); 76 | i64 value() const; 77 | virtual void format(stream& io, u32 level = 0) const override; 78 | virtual void eval(Stack& stack) override; 79 | virtual bool equals(const Term* other) const override; 80 | virtual u64 hash() const override; 81 | virtual Term* clone() const override; 82 | virtual const Type* type() const override; 83 | virtual Meta fold() const override; 84 | virtual void repr(stream& io) const override; 85 | }; 86 | 87 | class RationalTerm : public Term { 88 | double _value; 89 | public: 90 | static const TermClass CLASS; 91 | 92 | RationalTerm(double value, u32 line, u32 column, 93 | const TermClass* tc = &CLASS); 94 | double value() const; 95 | virtual void format(stream& io, u32 level = 0) const override; 96 | virtual void eval(Stack& stack) override; 97 | virtual bool equals(const Term* other) const override; 98 | virtual u64 hash() const override; 99 | virtual Term* clone() const override; 100 | virtual const Type* type() const override; 101 | virtual Meta fold() const override; 102 | virtual void repr(stream& io) const override; 103 | }; 104 | 105 | class StringTerm : public Term { 106 | ustring _value; 107 | public: 108 | static const TermClass CLASS; 109 | 110 | StringTerm(const ustring& value, u32 line, u32 column, 111 | const TermClass* tc = &CLASS); 112 | const ustring& value() const; 113 | virtual void format(stream& io, u32 level = 0) const override; 114 | virtual void eval(Stack& stack) override; 115 | virtual bool equals(const Term* other) const override; 116 | virtual u64 hash() const override; 117 | virtual Term* clone() const override; 118 | virtual const Type* type() const override; 119 | virtual Meta fold() const override; 120 | virtual void repr(stream& io) const override; 121 | }; 122 | 123 | class CharTerm : public Term { 124 | uchar _value; 125 | public: 126 | static const TermClass CLASS; 127 | 128 | CharTerm(uchar value, u32 line, u32 column, 129 | const TermClass* tc = &CLASS); 130 | uchar value() const; 131 | virtual void format(stream& io, u32 level = 0) const override; 132 | virtual void eval(Stack& stack) override; 133 | virtual bool equals(const Term* other) const override; 134 | virtual u64 hash() const override; 135 | virtual Term* clone() const override; 136 | virtual const Type* type() const override; 137 | virtual Meta fold() const override; 138 | virtual void repr(stream& io) const override; 139 | }; 140 | 141 | class BoolTerm : public Term { 142 | bool _value; 143 | public: 144 | static const TermClass CLASS; 145 | 146 | BoolTerm(bool value, u32 line, u32 column, 147 | const TermClass* tc = &CLASS); 148 | bool value() const; 149 | virtual void format(stream& io, u32 level = 0) const override; 150 | virtual void eval(Stack& stack) override; 151 | virtual bool equals(const Term* other) const override; 152 | virtual u64 hash() const override; 153 | virtual Term* clone() const override; 154 | virtual const Type* type() const override; 155 | virtual Meta fold() const override; 156 | virtual void repr(stream& io) const override; 157 | }; 158 | 159 | class VoidTerm : public Term { 160 | public: 161 | static const TermClass CLASS; 162 | 163 | VoidTerm(u32 line, u32 column, const TermClass* tc = &CLASS); 164 | 165 | virtual void format(stream& io, u32 level = 0) const override; 166 | virtual void eval(Stack& stack) override; 167 | virtual bool equals(const Term* other) const override; 168 | virtual u64 hash() const override; 169 | virtual Term* clone() const override; 170 | virtual const Type* type() const override; 171 | virtual Meta fold() const override; 172 | virtual void repr(stream& io) const override; 173 | }; 174 | 175 | class EmptyTerm : public Term { 176 | public: 177 | static const TermClass CLASS; 178 | 179 | EmptyTerm(u32 line, u32 column, const TermClass* tc = &CLASS); 180 | 181 | virtual void format(stream& io, u32 level = 0) const override; 182 | virtual void eval(Stack& stack) override; 183 | virtual bool equals(const Term* other) const override; 184 | virtual u64 hash() const override; 185 | virtual Term* clone() const override; 186 | virtual const Type* type() const override; 187 | virtual Meta fold() const override; 188 | virtual void repr(stream& io) const override; 189 | }; 190 | 191 | class VariableTerm : public Term { 192 | ustring _name; 193 | public: 194 | static const TermClass CLASS; 195 | 196 | VariableTerm(const ustring& name, u32 line, u32 column, 197 | const TermClass* tc = &CLASS); 198 | const ustring& name() const; 199 | void rename(const ustring& name); 200 | virtual void format(stream& io, u32 level = 0) const override; 201 | virtual void eval(Stack& stack) override; 202 | virtual bool equals(const Term* other) const override; 203 | virtual u64 hash() const override; 204 | virtual Term* clone() const override; 205 | virtual const Type* type() const override; 206 | virtual Meta fold() const override; 207 | virtual void repr(stream& io) const override; 208 | }; 209 | 210 | class BlockTerm : public Term { 211 | public: 212 | static const TermClass CLASS; 213 | 214 | BlockTerm(const vector& children, u32 line, u32 column, 215 | const TermClass* tc = &CLASS); 216 | const vector& children() const; 217 | void add(Term* child); 218 | virtual void format(stream& io, u32 level = 0) const override; 219 | virtual void eval(Stack& stack) override; 220 | virtual bool equals(const Term* other) const override; 221 | virtual u64 hash() const override; 222 | virtual Term* clone() const override; 223 | virtual const Type* type() const override; 224 | virtual Meta fold() const override; 225 | virtual void repr(stream& io) const override; 226 | }; 227 | 228 | class ProgramTerm : public Term { 229 | Stack *root, *global; 230 | 231 | void initRoot(); 232 | public: 233 | static const TermClass CLASS; 234 | 235 | ProgramTerm(const vector& children, u32 line, u32 column, 236 | const TermClass* tc = &CLASS); 237 | ~ProgramTerm(); 238 | const vector& children() const; 239 | void add(Term* child); 240 | virtual void format(stream& io, u32 level = 0) const override; 241 | virtual void eval(Stack& stack) override; 242 | void evalChild(Stack& stack, Term* t); 243 | Stack& scope(); 244 | virtual bool equals(const Term* other) const override; 245 | virtual u64 hash() const override; 246 | virtual Term* clone() const override; 247 | virtual const Type* type() const override; 248 | virtual Meta fold() const override; 249 | virtual void repr(stream& io) const override; 250 | }; 251 | } 252 | 253 | void print(stream& io, basil::Term* t); 254 | void print(basil::Term* t); 255 | 256 | #endif -------------------------------------------------------------------------------- /include/type.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_TYPE_H 2 | #define BASIL_TYPE_H 3 | 4 | #include "defs.h" 5 | #include "utf8.h" 6 | #include "vec.h" 7 | #include "meta.h" 8 | #include 9 | 10 | namespace basil { 11 | enum ConstraintType { 12 | NULL_CONSTRAINT = 0, UNKNOWN = 1, EQUALS_VALUE = 2, OF_TYPE = 3 13 | }; 14 | 15 | class Constraint { 16 | ConstraintType _type; 17 | Meta _value; 18 | ustring _key; 19 | Constraint(ConstraintType type); 20 | public: 21 | Constraint(Meta value); 22 | Constraint(const Type* type); 23 | Constraint(); 24 | 25 | const static Constraint NONE; 26 | 27 | ConstraintType type() const; 28 | Meta value() const; 29 | bool conflictsWith(const Constraint& other) const; 30 | bool precedes(const Constraint& other) const; 31 | bool matches(Meta value) const; 32 | const ustring& key() const; 33 | operator bool() const; 34 | }; 35 | 36 | class TypeClass { 37 | const TypeClass* _parent; 38 | public: 39 | TypeClass(); 40 | TypeClass(const TypeClass& parent); 41 | const TypeClass* parent() const; 42 | }; 43 | 44 | class Type { 45 | const TypeClass* _typeclass; 46 | static u32 nextid; 47 | protected: 48 | ustring _key; 49 | u32 _size; 50 | u32 _id; 51 | public: 52 | static const TypeClass CLASS; 53 | 54 | Type(const ustring& key, u32 size, const TypeClass* tc = &CLASS); 55 | 56 | u32 size() const; 57 | virtual bool implicitly(const Type* other) const; 58 | virtual bool explicitly(const Type* other) const; 59 | virtual bool conflictsWith(const Type* other) const; 60 | virtual bool wildcard() const; 61 | const ustring& key() const; 62 | virtual void format(stream& io) const; 63 | u32 id() const; 64 | 65 | template 66 | bool is() const { 67 | const TypeClass* tc = _typeclass; 68 | while (tc) { 69 | if (tc == &T::CLASS) return true; 70 | tc = tc->parent(); 71 | } 72 | return false; 73 | } 74 | 75 | template 76 | T* as() { 77 | return (T*)this; 78 | } 79 | 80 | template 81 | const T* as() const { 82 | return (const T*)this; 83 | } 84 | }; 85 | 86 | class NumericType : public Type { 87 | bool _floating; 88 | public: 89 | static const TypeClass CLASS; 90 | 91 | NumericType(u32 size, bool floating, 92 | const TypeClass* tc = &CLASS); 93 | bool floating() const; 94 | virtual bool implicitly(const Type* other) const override; 95 | virtual bool explicitly(const Type* other) const override; 96 | }; 97 | 98 | const Type* unionOf(const vector& elements); 99 | 100 | class ArrayType : public Type { 101 | const Type* _element; 102 | i64 _count; 103 | public: 104 | static const TypeClass CLASS; 105 | 106 | ArrayType(const Type* element, const TypeClass* tc = &CLASS); 107 | ArrayType(const Type* element, u32 size, 108 | const TypeClass* tc = &CLASS); 109 | ArrayType(const vector& elements, 110 | const TypeClass* tc = &CLASS); 111 | const Type* element() const; 112 | i64 count() const; 113 | bool sized() const; 114 | virtual bool implicitly(const Type* other) const override; 115 | virtual bool explicitly(const Type* other) const override; 116 | virtual bool wildcard() const override; 117 | virtual void format(stream& io) const override; 118 | }; 119 | 120 | class TupleType : public Type { 121 | vector _members; 122 | vector _offsets; 123 | public: 124 | static const TypeClass CLASS; 125 | 126 | TupleType(const vector& members, 127 | const TypeClass* tc = &CLASS); 128 | const vector& members() const; 129 | const Type* member(u32 i) const; 130 | u32 offset(u32 i) const; 131 | u32 count() const; 132 | virtual bool implicitly(const Type* other) const override; 133 | virtual bool explicitly(const Type* other) const override; 134 | virtual void format(stream& io) const override; 135 | }; 136 | 137 | class UnionType : public Type { 138 | set _members; 139 | public: 140 | static const TypeClass CLASS; 141 | 142 | UnionType(const set& members, 143 | const TypeClass* tc = &CLASS); 144 | const set& members() const; 145 | bool has(const Type* t) const; 146 | virtual bool implicitly(const Type* other) const override; 147 | virtual bool explicitly(const Type* other) const override; 148 | virtual void format(stream& io) const override; 149 | }; 150 | 151 | class IntersectionType : public Type { 152 | set _members; 153 | public: 154 | static const TypeClass CLASS; 155 | 156 | IntersectionType(const set& members, 157 | const TypeClass* tc = &CLASS); 158 | const set& members() const; 159 | bool has(const Type* t) const; 160 | virtual bool conflictsWith(const Type* other) const override; 161 | virtual bool implicitly(const Type* other) const override; 162 | virtual bool explicitly(const Type* other) const override; 163 | virtual void format(stream& io) const override; 164 | }; 165 | 166 | class ReferenceType : public Type { 167 | const Type* _element; 168 | public: 169 | static const TypeClass CLASS; 170 | 171 | ReferenceType(const Type* element, const TypeClass* tc = &CLASS); 172 | const Type* element() const; 173 | virtual bool implicitly(const Type* other) const override; 174 | virtual bool explicitly(const Type* other) const override; 175 | virtual void format(stream& io) const override; 176 | }; 177 | 178 | class ListType : public Type { 179 | const Type* _element; 180 | public: 181 | static const TypeClass CLASS; 182 | 183 | ListType(const Type* element, const TypeClass* tc = &CLASS); 184 | const Type* element() const; 185 | virtual bool implicitly(const Type* other) const override; 186 | virtual bool explicitly(const Type* other) const override; 187 | virtual void format(stream& io) const override; 188 | }; 189 | 190 | class EmptyType : public Type { 191 | public: 192 | static const TypeClass CLASS; 193 | 194 | EmptyType(const TypeClass* tc = &CLASS); 195 | virtual bool implicitly(const Type* other) const override; 196 | virtual bool explicitly(const Type* other) const override; 197 | virtual void format(stream& io) const override; 198 | }; 199 | 200 | class FunctionType : public Type { 201 | const Type *_arg, *_ret; 202 | vector _cons; 203 | bool _quoting; 204 | public: 205 | static const TypeClass CLASS; 206 | 207 | FunctionType(const Type* arg, const Type* ret, bool quoting, 208 | const vector& cons = {}, 209 | const TypeClass* tc = &CLASS); 210 | FunctionType(const Type* arg, const Type* ret, 211 | const vector& cons = {}, 212 | const TypeClass* tc = &CLASS); 213 | const Type* arg() const; 214 | const Type* ret() const; 215 | bool quoting() const; 216 | const vector& constraints() const; 217 | bool total() const; 218 | virtual bool conflictsWith(const Type* other) const override; 219 | virtual bool implicitly(const Type* other) const override; 220 | virtual bool explicitly(const Type* other) const override; 221 | virtual void format(stream& io) const override; 222 | virtual Constraint matches(Meta fr) const; 223 | }; 224 | 225 | Constraint maxMatch(const vector& cons, Meta fr); 226 | 227 | extern map typemap; 228 | 229 | template 230 | const T* find(Args... args) { 231 | T t(args...); 232 | auto it = typemap.find(t.key()); 233 | if (it == typemap.end()) { 234 | const T* ptr = new T(t); 235 | typemap.insert({ t.key(), ptr }); 236 | return ptr; 237 | } 238 | return it->second->template as(); 239 | } 240 | 241 | template 242 | const T* find(const std::initializer_list& ini, Args... args) { 243 | return find(vector(ini), args...); 244 | } 245 | 246 | const Type* join(const Type* a, const Type* b); 247 | 248 | extern const Type *I8, *I16, *I32, *I64, 249 | *FLOAT, *DOUBLE, *BOOL, *TYPE, *ERROR, 250 | *VOID, *ANY, *STRING, *CHAR, *EMPTY, 251 | *SYMBOL; 252 | 253 | bool shouldAlloca(const Type* a); 254 | } 255 | 256 | void print(stream& io, const basil::Type* t); 257 | void print(const basil::Type* t); 258 | 259 | #endif -------------------------------------------------------------------------------- /include/utf8.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_UNICODE_H 2 | #define BASIL_UNICODE_H 3 | 4 | #include "defs.h" 5 | #include "hash.h" 6 | 7 | struct uchar { 8 | u8 data[4]; 9 | public: 10 | uchar(u8 a = '\0', u8 b = '\0', u8 c = '\0', u8 d = '\0'); 11 | explicit uchar(const char* str); 12 | 13 | u32 size() const; 14 | u32 point() const; 15 | u8& operator[](u32 i); 16 | u8 operator[](u32 i) const; 17 | bool operator==(char c) const; 18 | bool operator!=(char c) const; 19 | bool operator==(uchar c) const; 20 | bool operator!=(uchar c) const; 21 | bool operator<(uchar c) const; 22 | bool operator>(uchar c) const; 23 | bool operator<=(uchar c) const; 24 | bool operator>=(uchar c) const; 25 | operator bool() const; 26 | }; 27 | 28 | bool isspace(uchar c); 29 | bool iscontrol(uchar c); 30 | bool isdigit(uchar c); 31 | bool isalpha(uchar c); 32 | bool isalnum(uchar c); 33 | bool issym(uchar c); 34 | bool isprint(uchar c); 35 | 36 | class ustring { 37 | uchar* data; 38 | u32 _size, _capacity; 39 | 40 | void free(); 41 | void init(u32 size); 42 | void copy(const uchar* s); 43 | void grow(); 44 | i32 cmp(const uchar* s) const; 45 | i32 cmp(const char* s) const; 46 | 47 | public: 48 | ustring(); 49 | ~ustring(); 50 | ustring(const ustring& other); 51 | ustring(const char* s); 52 | ustring& operator=(const ustring& other); 53 | 54 | ustring& operator+=(uchar c); 55 | ustring& operator+=(char c); 56 | ustring& operator+=(const uchar* s); 57 | ustring& operator+=(const char* s); 58 | ustring& operator+=(const ustring& s); 59 | void pop(); 60 | u32 size() const; 61 | u32 capacity() const; 62 | uchar operator[](u32 i) const; 63 | uchar& operator[](u32 i); 64 | const uchar* raw() const; 65 | bool operator==(const uchar* s) const; 66 | bool operator==(const char* s) const; 67 | bool operator==(const ustring& s) const; 68 | bool operator<(const uchar* s) const; 69 | bool operator<(const char* s) const; 70 | bool operator<(const ustring& s) const; 71 | bool operator>(const uchar* s) const; 72 | bool operator>(const char* s) const; 73 | bool operator>(const ustring& s) const; 74 | bool operator!=(const uchar* s) const; 75 | bool operator!=(const char* s) const; 76 | bool operator!=(const ustring& s) const; 77 | bool operator<=(const uchar* s) const; 78 | bool operator<=(const char* s) const; 79 | bool operator<=(const ustring& s) const; 80 | bool operator>=(const uchar* s) const; 81 | bool operator>=(const char* s) const; 82 | bool operator>=(const ustring& s) const; 83 | }; 84 | 85 | ustring operator+(ustring s, uchar c); 86 | ustring operator+(ustring s, const char* cs); 87 | ustring operator+(ustring s, const ustring& cs); 88 | ustring escape(const ustring& s); 89 | 90 | template<> 91 | u64 hash(const ustring& s); 92 | 93 | void print(stream& io, uchar c); 94 | void print(uchar c); 95 | void print(stream& io, const ustring& s); 96 | void print(const ustring& s); 97 | void read(stream& io, uchar& c); 98 | void read(uchar& c); 99 | void read(stream& io, ustring& s); 100 | void read(stream& io, ustring& s); 101 | 102 | #endif -------------------------------------------------------------------------------- /include/vec.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_VECTOR_H 2 | #define BASIL_VECTOR_H 3 | 4 | #include "defs.h" 5 | #include 6 | #include 7 | 8 | template 9 | class vector { 10 | u8* data; 11 | u32 _size, _capacity; 12 | 13 | void free(u8* array) { 14 | T* tptr = (T*)array; 15 | for (u32 i = 0; i < _size; i ++) tptr[i].~T(); 16 | delete[] array; 17 | } 18 | 19 | void init(u32 size) { 20 | _size = 0, _capacity = size; 21 | data = new u8[_capacity * sizeof(T)]; 22 | } 23 | 24 | void copy(const T* ts, u32 n) { 25 | _size = 0; 26 | T* tptr = (T*)data; 27 | for (u32 i = 0; i < n; i ++) { 28 | new(tptr + i) T(ts[i]); 29 | ++ _size; 30 | } 31 | } 32 | 33 | void destruct(u32 i) { 34 | T* tptr = (T*)data; 35 | tptr[i].~T(); 36 | } 37 | 38 | void grow() { 39 | u8* old = data; 40 | u32 oldsize = _size; 41 | init(_capacity * 2); 42 | copy((const T*)old, oldsize); 43 | free(old); 44 | } 45 | 46 | public: 47 | vector() { 48 | init(16); 49 | } 50 | 51 | vector(const std::initializer_list& init): vector() { 52 | for (const T& t : init) push(t); 53 | } 54 | 55 | ~vector() { 56 | free(data); 57 | } 58 | 59 | vector(const vector& other) { 60 | init(other._capacity); 61 | copy((const T*)other.data, other._size); 62 | } 63 | 64 | vector& operator=(const vector& other) { 65 | if (this != &other) { 66 | free(data); 67 | init(other._capacity); 68 | copy((const T*)other.data, other._size); 69 | } 70 | return *this; 71 | } 72 | 73 | void push(const T& t) { 74 | while (_size + 1 >= _capacity) grow(); 75 | T* tptr = (T*)data; 76 | new(tptr + _size) T(t); 77 | ++ _size; 78 | } 79 | 80 | void pop() { 81 | -- _size; 82 | destruct(_size); 83 | } 84 | 85 | void clear() { 86 | for (u32 i = 0; i < _size; ++ i) destruct(i); 87 | _size = 0; 88 | } 89 | 90 | const T& operator[](u32 i) const { 91 | return ((T*)data)[i]; 92 | } 93 | 94 | T& operator[](u32 i) { 95 | return ((T*)data)[i]; 96 | } 97 | 98 | const T* begin() const { 99 | return (const T*)data; 100 | } 101 | 102 | T* begin() { 103 | return (T*)data; 104 | } 105 | 106 | const T* end() const { 107 | return (const T*)data + _size; 108 | } 109 | 110 | T* end() { 111 | return (T*)data + _size; 112 | } 113 | 114 | u32 size() const { 115 | return _size; 116 | } 117 | 118 | u32 capacity() const { 119 | return _capacity; 120 | } 121 | 122 | const T& front() const { 123 | return *begin(); 124 | } 125 | 126 | T& front() { 127 | return *begin(); 128 | } 129 | 130 | const T& back() const { 131 | return *(end() - 1); 132 | } 133 | 134 | T& back() { 135 | return *(end() - 1); 136 | } 137 | }; 138 | 139 | #endif -------------------------------------------------------------------------------- /include/x64.h: -------------------------------------------------------------------------------- 1 | #ifndef BASIL_X64_H 2 | 3 | #include "defs.h" 4 | #include "io.h" 5 | #include "ir.h" 6 | #include "type.h" 7 | 8 | namespace basil { 9 | namespace x64 { 10 | enum Size { 11 | VOID, // 0 bytes 12 | BYTE, // 1 byte 13 | WORD, // 2 bytes 14 | DWORD, // 4 bytes 15 | QWORD, // 8 bytes 16 | SINGLE, // 4 byte float 17 | DOUBLE, // 8 byte float 18 | ERROR // unknown type 19 | }; 20 | 21 | enum Condition { 22 | EQUAL = 0, 23 | NOT_EQUAL = 1, 24 | LESS = 2, 25 | LESS_EQUAL = 3, 26 | GREATER = 4, 27 | GREATER_EQUAL = 5, 28 | ZERO = 6, 29 | NOT_ZERO = 7 30 | }; 31 | 32 | extern const char* CONDITION_NAMES[8]; 33 | 34 | enum Section { 35 | TEXT, 36 | DATA 37 | }; 38 | 39 | Size typeSize(const Type* t); 40 | 41 | namespace printer { 42 | void intconst(buffer& text, buffer& data, i64 value); 43 | void fconst(buffer& text, buffer& data, double value); 44 | void strconst(buffer& text, buffer& data, const ustring& value); 45 | 46 | void text(buffer& text, buffer& data); 47 | void data(buffer& text, buffer& data); 48 | void label(buffer& text, buffer& data, Section section, const ustring& name, bool global); 49 | void mov(buffer& text, buffer& data, Location* src, Location* dst); 50 | void add(buffer& text, buffer& data, Location* src, Location* dst); 51 | void sub(buffer& text, buffer& data, Location* src, Location* dst); 52 | void imul(buffer& text, buffer& data, Location* src, Location* dst); 53 | void mul(buffer& text, buffer& data, Location* src, Location* dst); 54 | void idiv(buffer& text, buffer& data, Location* src); 55 | void div(buffer& text, buffer& data, Location* src); 56 | void fdiv(buffer& text, buffer& data, Location* src, Location* dst); 57 | void cmp(buffer& text, buffer& data, Location* src, Location* dst); 58 | void and_(buffer& text, buffer& data, Location* src, Location* dst); 59 | void or_(buffer& text, buffer& data, Location* src, Location* dst); 60 | void xor_(buffer& text, buffer& data, Location* src, Location* dst); 61 | void not_(buffer& text, buffer& data, Location* operand); 62 | void movsx(buffer& text, buffer& data, Location* src, Location* dst); 63 | void movzx(buffer& text, buffer& data, Location* src, Location* dst); 64 | void cvttsd2si(buffer& text, buffer& data, Location* src, Location* dst); 65 | void cvttss2si(buffer& text, buffer& data, Location* src, Location* dst); 66 | void cvtsd2ss(buffer& text, buffer& data, Location* src, Location* dst); 67 | void cvtss2sd(buffer& text, buffer& data, Location* src, Location* dst); 68 | void cvtsi2sd(buffer& text, buffer& data, Location* src, Location* dst); 69 | void cvtsi2ss(buffer& text, buffer& data, Location* src, Location* dst); 70 | void lea(buffer& text, buffer& data, const ustring& label, Location* dst); 71 | void lea(buffer& text, buffer& data, Location* addr, Location* dst); 72 | void jmp(buffer& text, buffer& data, Location* addr); 73 | void jmp(buffer& text, buffer& data, const ustring& label); 74 | void jcc(buffer& text, buffer& data, const ustring& label, Condition condition); 75 | void setcc(buffer& text, buffer& data, Location* dst, Condition condition); 76 | void syscall(buffer& text, buffer& data); 77 | void syscall(buffer& text, buffer& data); 78 | void ret(buffer& text, buffer& data); 79 | void call(buffer& text, buffer& data, Location* function); 80 | void call(buffer& text, buffer& data, const ustring& function); 81 | void push(buffer& text, buffer& data, Location* src); 82 | void pop(buffer& text, buffer& data, Location* dst); 83 | void cdq(buffer& text, buffer& data); 84 | } 85 | 86 | // namespace assembler { 87 | // void intconst(buffer& text, buffer& data, i64 value); 88 | // void strconst(buffer& text, buffer& data, const ustring& value); 89 | 90 | // void text(buffer& text, buffer& data); 91 | // void data(buffer& text, buffer& data); 92 | // void label(buffer& text, buffer& data, Section section, const ustring& name, bool global); 93 | // void mov(buffer& text, buffer& data, Location* src, Location* dst); 94 | // void add(buffer& text, buffer& data, Location* src, Location* dst); 95 | // void sub(buffer& text, buffer& data, Location* src, Location* dst); 96 | // void imul(buffer& text, buffer& data, Location* src, Location* dst); 97 | // void mul(buffer& text, buffer& data, Location* src, Location* dst); 98 | // void idiv(buffer& text, buffer& data, Location* src); 99 | // void div(buffer& text, buffer& data, Location* src); 100 | // void cmp(buffer& text, buffer& data, Location* src, Location* dst); 101 | // void and_(buffer& text, buffer& data, Location* src, Location* dst); 102 | // void or_(buffer& text, buffer& data, Location* src, Location* dst); 103 | // void xor_(buffer& text, buffer& data, Location* src, Location* dst); 104 | // void cdq(buffer& text, buffer& data); 105 | // void lea(buffer& text, buffer& data, const ustring& label, Location* dst); 106 | // void lea(buffer& text, buffer& data, Location* addr, Location* dst); 107 | // void jmp(buffer& text, buffer& data, const ustring& label); 108 | // void jcc(buffer& text, buffer& data, const ustring& label, Condition condition); 109 | // void setcc(buffer& text, buffer& data, Location* dst, Condition condition); 110 | // void syscall(buffer& text, buffer& data); 111 | // void syscall(buffer& text, buffer& data); 112 | // void ret(buffer& text, buffer& data); 113 | // void call(buffer& text, buffer& data, Location* function); 114 | // void call(buffer& text, buffer& data, const ustring& function); 115 | // void push(buffer& text, buffer& data, Location* src); 116 | // void pop(buffer& text, buffer& data, Location* dst); 117 | // } 118 | } 119 | } 120 | 121 | #endif -------------------------------------------------------------------------------- /interpret: -------------------------------------------------------------------------------- 1 | echo "print: " $* > _tmp.bl 2 | ./basil _tmp.bl > _tmp.s 3 | as _tmp.s -o _tmp.o 4 | ld lib/core.o _tmp.o -o _tmp 5 | ./_tmp 6 | rm _tmp.bl _tmp.s _tmp.o _tmp 7 | -------------------------------------------------------------------------------- /lib/core.c: -------------------------------------------------------------------------------- 1 | /* * * * * * * * * * * * * * * * 2 | * * 3 | * Helpful Typedefs * 4 | * * 5 | * * * * * * * * * * * * * * * */ 6 | 7 | #include "stdint.h" 8 | 9 | #define nullptr 0 10 | 11 | typedef uint8_t u8; 12 | typedef uint16_t u16; 13 | typedef uint32_t u32; 14 | typedef uint64_t u64; 15 | 16 | typedef int8_t i8; 17 | typedef int16_t i16; 18 | typedef int32_t i32; 19 | typedef int64_t i64; 20 | 21 | /* * * * * * * * * * * * * 22 | * * 23 | * Linkage * 24 | * * 25 | * * * * * * * * * * * * */ 26 | 27 | extern void* _mmap(void*, u64, u64, u64) asm("_mmap"); 28 | extern void _munmap(void*, u64) asm("_munmap"); 29 | extern void _exit(u64) asm("_exit"); 30 | extern void _read(u64, char*, u64) asm("_read"); 31 | extern void _write(u64, const char*, u64) asm("_write"); 32 | extern void _printi64(i64) asm("_printi64"); 33 | extern void _printf64(double) asm("_printf64"); 34 | extern void _printstr(void*) asm("_printstr"); 35 | extern void _printbool(i8) asm("_printbool"); 36 | 37 | /* * * * * * * * * * * * * * * * 38 | * * 39 | * System Calls * 40 | * * 41 | * * * * * * * * * * * * * * * */ 42 | 43 | inline void* _mmap(void* addr, u64 len, u64 prot, u64 flags) { 44 | void* ret; 45 | 46 | #ifdef __APPLE__ 47 | #define MMAP_CODE "0x20000C5" 48 | #else if defined(__linux__) 49 | #define MMAP_CODE "9" 50 | #endif 51 | 52 | #define PROT_READ 0x1 53 | #define PROT_WRITE 0x2 54 | #define PROT_EXEC 0x4 55 | #define PROT_NONE 0x0 56 | #define PROT_GROWSDOWN 0x01000000 57 | #define PROT_GROWSUP 0x02000000 58 | 59 | #define MAP_SHARED 0x01 60 | #define MAP_PRIVATE 0x02 61 | #define MAP_ANONYMOUS 0x20 62 | 63 | asm volatile ("mov $" MMAP_CODE ", %%rax\n\t" 64 | "mov %1, %%rdi\n\t" 65 | "mov %2, %%rsi\n\t" 66 | "mov %3, %%rdx\n\t" 67 | "mov %4, %%r10\n\t" 68 | "mov $-1, %%r8\n\t" 69 | "mov $0, %%r9\n\t" 70 | "syscall\n\t" 71 | "mov %%rax, %0" 72 | : "=r" (ret) 73 | : "r" (addr), "r" (len), "r" (prot), "r" (flags) 74 | : "rdi", "rsi", "rdx", "r10", "r9", "r8", "rax" 75 | ); 76 | 77 | return ret; 78 | } 79 | 80 | inline void _munmap(void* addr, u64 len) { 81 | #ifdef __APPLE__ 82 | #define MUNMAP_CODE "0x2000049" 83 | #else if defined(__linux__) 84 | #define MUNMAP_CODE "11" 85 | #endif 86 | 87 | asm volatile ("mov $" MMAP_CODE ", %%rax\n\t" 88 | "mov %0, %%rdi\n\t" 89 | "mov %1, %%rsi\n\t" 90 | "syscall\n\t" 91 | : 92 | : "r" (addr), "r" (len) 93 | : "rdi", "rsi", "rax" 94 | ); 95 | } 96 | 97 | inline void _exit(u64 ret) { 98 | #ifdef __APPLE__ 99 | #define EXIT_CODE "0x2000001" 100 | #else if defined(__linux__) 101 | #define EXIT_CODE "60" 102 | #endif 103 | asm volatile ("mov $" EXIT_CODE ", %%rax\n\t" 104 | "mov %0, %%rdi\n\t" 105 | "syscall\n\t" 106 | : 107 | : "r" (ret) 108 | : "rax", "rdi" 109 | ); 110 | } 111 | 112 | inline void _read(u64 fd, char* buf, u64 len) { 113 | #ifdef __APPLE__ 114 | #define READ_CODE "0x2000003" 115 | #else if defined(__linux__) 116 | #define READ_CODE "0" 117 | #endif 118 | asm volatile ("mov $" READ_CODE ", %%rax\n\t" 119 | "mov %0, %%rdi\n\t" 120 | "mov %1, %%rsi\n\t" 121 | "mov %2, %%rdx\n\t" 122 | "syscall\n\t" 123 | : 124 | : "r" (fd), "r" (buf), "r" (len) 125 | : "rax", "rdi", "rsi", "rdx" 126 | ); 127 | } 128 | 129 | inline void _write(u64 fd, const char* text, u64 len) { 130 | #ifdef __APPLE__ 131 | #define WRITE_CODE "0x2000004" 132 | #else if defined(__linux__) 133 | #define WRITE_CODE "1" 134 | #endif 135 | asm volatile ("mov $" WRITE_CODE ", %%rax\n\t" 136 | "mov %0, %%rdi\n\t" 137 | "mov %1, %%rsi\n\t" 138 | "mov %2, %%rdx\n\t" 139 | "syscall\n\t" 140 | : 141 | : "r" (fd), "r" (text), "r" (len) 142 | : "rax", "rdi", "rsi", "rdx" 143 | ); 144 | } 145 | 146 | struct { i64 tv_sec; i64 tv_nsec } spec; 147 | 148 | inline u64 _now() { 149 | asm volatile ("mov $228, %%rax\n\t" 150 | "mov $1, %%rdi\n\t" 151 | "mov %0, %%rsi\n\t" 152 | "syscall\n\t" 153 | : 154 | : "r" (&spec) 155 | : "rax", "rdi", "rsi", "rdx" 156 | ); 157 | return spec.tv_nsec + spec.tv_sec * 1000000000ul; 158 | } 159 | 160 | /* * * * * * * * * * * * * * * * 161 | * * 162 | * Error Handling * 163 | * * 164 | * * * * * * * * * * * * * * * */ 165 | 166 | void _panic(const char* msg) { 167 | const char* ptr = msg; 168 | while (*ptr) ptr ++; 169 | _write(0, msg, ptr - msg); 170 | _exit(1); 171 | } 172 | 173 | /* * * * * * * * * * * * * * * * 174 | * * 175 | * General-Purpose Allocator * 176 | * * 177 | * * * * * * * * * * * * * * * */ 178 | 179 | const u64 ARENA_SIZE = 0x10000; 180 | 181 | typedef struct node_t { 182 | struct node_t* prev; 183 | struct node_t* next; 184 | } node; 185 | 186 | #define NONE 0 // unused 187 | #define TYPE_128 1 // 16-byte arena 188 | #define TYPE_256 2 // 32-byte arena 189 | #define TYPE_512 3 // 64-byte arena 190 | #define TYPE_1024 4 // 128-byte arena 191 | #define TYPE_2048 5 // 256-byte arena 192 | #define TYPE_4096 6 // 512-byte arena 193 | #define TYPE_8192 7 // 1024-byte arena 194 | #define TYPE_16384 8 // 2048-byte arena 195 | #define TYPE_32768 9 // 4096-byte arena 196 | #define TYPE_65536 10 // 8192-byte arena 197 | #define TYPE_LARGE 11 // > page size arena 198 | #define NUM_TYPES 12 199 | 200 | typedef struct arena_t { 201 | struct arena_t* prev; 202 | struct arena_t* next; 203 | node* nextpos; 204 | node* lastpos; 205 | void* end; 206 | u64 count; 207 | u64 type; 208 | u8 padding[8]; 209 | } arena; 210 | 211 | static arena* new_arena(u64 type, arena* prev, arena* next) { 212 | // allocate region 213 | u8* ptr = _mmap(nullptr, 214 | ARENA_SIZE * 2, 215 | PROT_READ | PROT_WRITE, 216 | MAP_PRIVATE | MAP_ANONYMOUS); 217 | 218 | // align to arena size within region 219 | arena* a = (arena*)((u64)(ptr + ARENA_SIZE) & ~(ARENA_SIZE - 1)); 220 | 221 | // trim region to arena size 222 | if ((u8*)a - ptr) 223 | _munmap(ptr, (u8*)a - ptr); 224 | if ((ptr + ARENA_SIZE) - (u8*)a) 225 | _munmap((u8*)a + ARENA_SIZE, (ptr + ARENA_SIZE) - (u8*)a); 226 | 227 | u64 align = 8 << a->type; // compute element size 228 | if (align < sizeof(arena)) align = sizeof(arena); 229 | 230 | // initialize arena 231 | a->prev = prev; 232 | a->next = next; 233 | if (a->prev) a->prev->next = a; 234 | if (a->next) a->next->prev = a; 235 | a->lastpos = a->nextpos = (node*)((u8*)a + align); 236 | a->nextpos->prev = a->nextpos->next = nullptr; 237 | a->end = (u8*)a + ARENA_SIZE; 238 | a->count = 0; 239 | a->type = type; 240 | return a; 241 | } 242 | 243 | #define defarena(N) \ 244 | static arena* arena##N = nullptr; \ 245 | static void* alloc##N() { \ 246 | if (!arena##N) arena##N = new_arena(TYPE_##N, nullptr, nullptr); \ 247 | arena##N->count ++; \ 248 | /* if space is open before end of region... */\ 249 | if (arena##N->nextpos->next) { \ 250 | /* detach node from linked list */\ 251 | node* n = arena##N->nextpos; \ 252 | n->next->prev = n->prev; \ 253 | /* target next node in linked list */\ 254 | arena##N->nextpos = n->next; \ 255 | return n; \ 256 | } \ 257 | else { \ 258 | node* n = arena##N->nextpos; \ 259 | /* bump nextpos by N / 8 bytes */\ 260 | arena##N->lastpos = arena##N->nextpos = (node*)((u8*)arena##N->nextpos + (N / 8)); \ 261 | /* create new arena if no more room */\ 262 | if (arena##N->nextpos >= (node*)arena##N->end) { \ 263 | arena##N->nextpos = nullptr; \ 264 | arena##N = new_arena(TYPE_##N, arena##N, nullptr); \ 265 | } \ 266 | else { \ 267 | arena##N->nextpos->prev = n->prev; \ 268 | arena##N->nextpos->next = nullptr; \ 269 | } \ 270 | return n; \ 271 | } \ 272 | } \ 273 | static void free##N(arena* a, void* p) { \ 274 | node* n = p; \ 275 | if (a->count < arena##N->count) arena##N = a; \ 276 | /* if we're deleting before the end */\ 277 | if (a->lastpos == (node*)((u8*)p + N)) { \ 278 | /* change lastpos */\ 279 | if (a->lastpos >= (node*)a->end) { \ 280 | n->next = a->nextpos; \ 281 | n->prev = nullptr; \ 282 | a->nextpos = n; \ 283 | } \ 284 | else { \ 285 | n->prev = a->lastpos->prev; \ 286 | if (n->prev) n->prev->next = n; \ 287 | n->next = nullptr; \ 288 | } \ 289 | a->lastpos = n; \ 290 | } \ 291 | else { \ 292 | /* change nextpos */\ 293 | n->prev = nullptr; \ 294 | n->next = a->nextpos; \ 295 | if (n->next) n->next->prev = n->prev; \ 296 | a->nextpos = n; \ 297 | } \ 298 | } 299 | 300 | defarena(128); 301 | defarena(256); 302 | defarena(512); 303 | defarena(1024); 304 | defarena(2048); 305 | defarena(4096); 306 | defarena(8192); 307 | defarena(16384); 308 | defarena(32768); 309 | defarena(65536); 310 | 311 | static void* alloclarge(u64 size) { 312 | size = size + sizeof(arena); 313 | size = (size + ARENA_SIZE - 1) & ~(ARENA_SIZE - 1); 314 | u8* p = _mmap(nullptr, size + ARENA_SIZE, 315 | PROT_READ | PROT_WRITE, 316 | MAP_PRIVATE | MAP_ANONYMOUS); 317 | // align to arena size within region 318 | arena* a = (arena*)((u64)(p + ARENA_SIZE) & ~(ARENA_SIZE - 1)); 319 | 320 | // trim region to arena size 321 | if ((u8*)a - p) 322 | _munmap(p, (u8*)a - p); 323 | if ((p + ARENA_SIZE) - (u8*)a) 324 | _munmap((u8*)a + ARENA_SIZE, (p + ARENA_SIZE) - (u8*)a); 325 | 326 | a->prev = nullptr; 327 | a->next = nullptr; 328 | a->count = 1; 329 | a->end = (u8*)a + size; 330 | a->type = TYPE_LARGE; 331 | a->lastpos = a->end; 332 | a->nextpos = nullptr; 333 | return (u8*)a + sizeof(arena); 334 | } 335 | 336 | static void freelarge(void* p) { 337 | arena* a = (arena*)((u64)p & ~(ARENA_SIZE - 1)); 338 | _munmap(a, (u8*)a->end - (u8*)a); 339 | } 340 | 341 | static arena** arenas[NUM_TYPES] = { 342 | nullptr, // NONE 343 | &arena128, // 128 344 | &arena256, // 256 345 | &arena512, // 512 346 | &arena1024, // 1024 347 | &arena2048, // 2048 348 | &arena4096, // 4096 349 | &arena8192, // 8192 350 | &arena16384, // 16384 351 | &arena32768, // 32768 352 | &arena65536, // 65536 353 | nullptr, // LARGE 354 | }; 355 | 356 | void* _alloc(u64 size) { 357 | if (size <= 16) return alloc128(); 358 | else if (size <= 32) return alloc256(); 359 | else if (size <= 64) return alloc512(); 360 | else if (size <= 128) return alloc1024(); 361 | else if (size <= 256) return alloc2048(); 362 | else if (size <= 512) return alloc4096(); 363 | else if (size <= 1024) return alloc8192(); 364 | else if (size <= 2048) return alloc16384(); 365 | else if (size <= 4096) return alloc32768(); 366 | else if (size <= 8192) return alloc65536(); 367 | else return alloclarge(size); 368 | } 369 | 370 | void _free(void* p) { 371 | // find current arena 372 | arena* a = (arena*)((u64)p & ~(ARENA_SIZE - 1)); 373 | 374 | if (a->type == TYPE_LARGE) return freelarge(p); 375 | 376 | a->count --; 377 | 378 | // free if empty 379 | if (a->count == 0) { 380 | if (a->prev) a->prev->next = a->next; 381 | if (a->next) a->next->prev = a->prev; 382 | 383 | // change base arena for allocation if we're deleting it 384 | if (arenas[a->type] && a == *arenas[a->type]) { 385 | if (a->next) *arenas[a->type] = a->next; 386 | else if (a->prev) *arenas[a->type] = a->prev; 387 | else *arenas[a->type] = nullptr; 388 | } 389 | _munmap(a, ARENA_SIZE); 390 | return; 391 | } 392 | 393 | switch (a->type) { 394 | case TYPE_128: 395 | return free128(a, p); 396 | case TYPE_256: 397 | return free256(a, p); 398 | case TYPE_512: 399 | return free512(a, p); 400 | case TYPE_1024: 401 | return free1024(a, p); 402 | case TYPE_2048: 403 | return free2048(a, p); 404 | case TYPE_4096: 405 | return free4096(a, p); 406 | case TYPE_8192: 407 | return free8192(a, p); 408 | case TYPE_16384: 409 | return free16384(a, p); 410 | case TYPE_32768: 411 | return free32768(a, p); 412 | case TYPE_65536: 413 | return free65536(a, p); 414 | default: 415 | return; 416 | } 417 | } 418 | 419 | /* * * * * * * * * * * * * * * * 420 | * * 421 | * Ref-Counting Utilities * 422 | * * 423 | * * * * * * * * * * * * * * * */ 424 | 425 | #define IMUT_FLAG 1 426 | #define NONRC_FLAG 2 427 | 428 | i64 _rccount(void* ref) { 429 | return *(i64*)((u8*)ref - 8); 430 | } 431 | 432 | void _rcinc(void* ref) { 433 | i64 count = *(u64*)((u8*)ref - 8) + 16; // incr count 434 | if (!(count & IMUT_FLAG)) *(u64*)((u8*)ref - 8) = count; 435 | } 436 | 437 | void _printi64(i64 count); 438 | 439 | void _rcdec(void* ref) { 440 | i64 count = *(i64*)((u8*)ref - 8) - 16; 441 | if (count & IMUT_FLAG) return; 442 | else if (count <= 0 && !(count & NONRC_FLAG)) _free((u8*)ref - 8); 443 | else *(i64*)((u8*)ref - 8) = count; 444 | } 445 | 446 | void* _rccopy(void* dst, void* src) { 447 | _rcinc(src); 448 | dst = src; 449 | return dst; 450 | } 451 | 452 | void* _rcassign(void* dst, void* src) { 453 | _rcinc(src); 454 | void* prev = dst; 455 | dst = src; 456 | _rcdec(prev); 457 | return dst; 458 | } 459 | 460 | void* _rcnew(u64 size) { 461 | size += 8; 462 | u8* ptr = _alloc(size); 463 | *(i64*)ptr = 16; // 1 ref, no flags 464 | return ptr + 8; 465 | } 466 | 467 | /* * * * * * * * * * * * * * * * 468 | * * 469 | * String Functions * 470 | * * 471 | * * * * * * * * * * * * * * * */ 472 | 473 | void* _strnew(u64 length, u8* data) { 474 | void* str = _rcnew(length + 8); // includes 8 bytes for string length 475 | *(u64*)str = length; // store length 476 | if (data) for (u64 i = 0; i < length; i ++) { 477 | ((u8*)str)[i + 8] = data[i]; // copy string data 478 | } 479 | return str; 480 | } 481 | 482 | i64 _strcmp(void* a, void* b) { 483 | u64 asize = *(u64*)a, bsize = *(u64*)b; 484 | a = (u8*)a + 8, b = (u8*)b + 8; 485 | u8* ai = a, *bi = b; 486 | while (ai < (u8*)a + asize && bi < (u8*)b + bsize) { 487 | if (*ai != *bi) return (i64)*ai - (i64)*bi; 488 | ++ ai, ++ bi; 489 | } 490 | return (i64)asize - (i64)bsize; 491 | } 492 | 493 | u64 _strlen(void* str) { 494 | return *(u64*)str; 495 | } 496 | 497 | void* _strcopyref(void* dst, void* src) { 498 | _rccopy(dst, src); 499 | } 500 | 501 | void* _strassign(void* dst, void* src) { 502 | _rcassign(dst, src); 503 | } 504 | 505 | void* _strcat(void* a, void* b) { 506 | void* dst = _strnew(_strlen(a) + _strlen(b), nullptr); 507 | u8* writ = (u8*)dst + 8; 508 | for (u64 i = 0; i < _strlen(a); i ++) *writ++ = ((u8*)a)[i + 8]; 509 | for (u64 i = 0; i < _strlen(b); i ++) *writ++ = ((u8*)b)[i + 8]; 510 | return dst; 511 | } 512 | 513 | void* _strcpy(void* str) { 514 | return _strnew(_strlen(str), (u8*)str + 8); 515 | } 516 | 517 | u8 _strget(void* str, u64 i) { 518 | if (i > _strlen(str)) _panic("Runtime error: out-of-bounds access of string."); 519 | return ((u8*)str)[i + 8]; 520 | } 521 | 522 | void _strset(void** str, u64 i, u8 ch) { 523 | if (i > _strlen(str)) _panic("Runtime error: out-of-bounds access of string."); 524 | if (_rccount(str) & IMUT_FLAG) *str = _strcpy(*str); 525 | ((u8*)*str)[i] = ch; 526 | } 527 | 528 | /* * * * * * * * * * * * * * * * 529 | * * 530 | * List Functions * 531 | * * 532 | * * * * * * * * * * * * * * * */ 533 | 534 | void* _lshead(void* list) { 535 | if (!list) _panic("Runtime error: tried to get head of empty list."); 536 | return (u8*)list + 8; 537 | } 538 | 539 | void* _lstail(void* list) { 540 | if (!list) _panic("Runtime error: tried to get tail of empty list."); 541 | return *(void**)list; 542 | } 543 | 544 | u64 _lsempty(void* list) { 545 | return list ? 1 : 0; 546 | } 547 | 548 | void* _cons(u64 size, void* next) { 549 | u8* dst = _rcnew(size + 8); // include next in size 550 | *(void**)dst = next; 551 | return dst; 552 | } 553 | 554 | /* * * * * * * * * * * * * * * * 555 | * * 556 | * Input and Output * 557 | * * 558 | * * * * * * * * * * * * * * * */ 559 | 560 | void memset(void* b, i64 c, i64 len) { 561 | int i; 562 | u8* p = b; 563 | i = 0; 564 | while(len > 0) { 565 | *p = c; 566 | p ++; 567 | len --; 568 | } 569 | } 570 | 571 | void _printi64(i64 i) { 572 | u8 buf[64]; 573 | u8* writ = buf; 574 | writ = buf; 575 | 576 | if (i < 0) *writ++ = '-', i = -i; 577 | i64 m = i, p = 1; 578 | while (m / 10) m /= 10, p *= 10; 579 | while (p) *writ++ = '0' + (i / p % 10), p /= 10; 580 | 581 | +write(1, buf, writ - buf); 582 | } 583 | 584 | void _printu64(u64 u) { 585 | u8 buf[64]; 586 | u8* writ = buf; 587 | 588 | u64 m = u, p = 1; 589 | while (m / 10) m /= 10, p *= 10; 590 | while (p) *writ++ = '0' + (u / p % 10), p /= 10; 591 | 592 | _write(1, buf, writ - buf); 593 | } 594 | 595 | void _printf64(double d) { 596 | u8 buf[64]; 597 | u8* writ = buf; 598 | 599 | u64 u = (u64)d; // before decimal point 600 | u64 m = u, p = 1; 601 | while (m / 10) m /= 10, p *= 10; 602 | while (p) *writ++ = '0' + (u / p % 10), p /= 10; 603 | 604 | *writ++ = '.'; 605 | 606 | double r = d - u; // after decimal point 607 | p = 10; 608 | u32 zeroes = 0; 609 | int isZero = r == 0 ? 1 : 0; 610 | while (r && p) { 611 | r *= 10; 612 | if ((u8)r) { 613 | isZero = 0; 614 | while (zeroes) *writ++ = '0', -- zeroes; 615 | *writ++ = '0' + (u8)r; 616 | } 617 | else ++ zeroes; 618 | r -= (u8)r; 619 | -- p; 620 | } 621 | if (isZero) *writ++ = '0'; 622 | 623 | _write(1, buf, writ - buf); 624 | } 625 | 626 | i64 prev = 0; 627 | 628 | void _printstr(void* str) { 629 | // if (!prev) prev = now(); 630 | // else { 631 | // // write(0, "time since last print:\n\0\0", 23); 632 | // u64 n = now(); 633 | // _printf64((n - prev) / 1000000000.0); 634 | // prev = n; 635 | // } 636 | _write(1, (u8*)str + 8, _strlen(str)); 637 | } 638 | 639 | void _printbool(i8 b) { 640 | if (b) _write(1, "true", 4); 641 | else _write(1, "false", 5); 642 | } 643 | 644 | /* * * * * * * * * * * * * 645 | * * 646 | * Utilities * 647 | * * 648 | * * * * * * * * * * * * */ 649 | 650 | -------------------------------------------------------------------------------- /math.bl: -------------------------------------------------------------------------------- 1 | 1 + 2 2 | -------------------------------------------------------------------------------- /run: -------------------------------------------------------------------------------- 1 | set -euo pipefail 2 | 3 | FNAME=$1 4 | # trap 'tmp="BASH_COMMAND"; eval echo "${!tmp}"' DEBUG 5 | ./basil $FNAME > ${FNAME%.bl}.s 6 | as ${FNAME%.bl}.s -o ${FNAME%.bl}.o 7 | ld -static lib/core.o ${FNAME%.bl}.o -o ${FNAME%.bl} 8 | ./${FNAME%.bl} 9 | rm ${FNAME%.bl}.s ${FNAME%.bl}.o ${FNAME%.bl} -------------------------------------------------------------------------------- /run-tests: -------------------------------------------------------------------------------- 1 | if test -f test/*.out; then 2 | rm test/*.out 3 | fi 4 | 5 | for f in test/*.bl; do 6 | c="${f%.bl}.correct" 7 | o="${f%.bl}.out" 8 | ./basil $f > $o 9 | diff -I '#.*' -Bb $o $c > /dev/null 10 | if [ $? == 1 ]; then 11 | echo "Files $o and $c differ." 12 | else 13 | echo "Test '${f%.bl}' passed" 14 | rm $o 15 | fi 16 | done -------------------------------------------------------------------------------- /src/basm.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elucent/basil/eb05264b82420cc51eb74aa9e6f042291e759884/src/basm.cpp -------------------------------------------------------------------------------- /src/errors.cpp: -------------------------------------------------------------------------------- 1 | #include "errors.h" 2 | #include "source.h" 3 | 4 | namespace basil { 5 | Error::Error(): src(nullptr), line(0), column(0) { 6 | // 7 | } 8 | 9 | void Error::format(stream& io) const { 10 | println(io, message); 11 | if (src) { 12 | print(io, " ", src->line(line - 1)); 13 | print(io, " "); 14 | for (u32 i = 0; i < column - 1; i ++) print(io, " "); 15 | println(io, "^"); 16 | } 17 | } 18 | 19 | static vector errors; 20 | static set messages; 21 | 22 | static vector> errorFrames; 23 | static vector> frameMessages; 24 | 25 | void catchErrors() { 26 | errorFrames.push({}); 27 | frameMessages.push({}); 28 | } 29 | 30 | void releaseErrors() { 31 | vector removed = errorFrames.back(); 32 | errorFrames.pop(); 33 | frameMessages.pop(); 34 | for (const Error& e : removed) reportError(e); 35 | } 36 | 37 | void discardErrors() { 38 | // if (countErrors()) printErrors(_stdout); 39 | errorFrames.pop(); 40 | frameMessages.pop(); 41 | } 42 | 43 | void prefixPhase(buffer& b, Phase phase) { 44 | switch (phase) { 45 | case PHASE_LEX: 46 | fprint(b, "[TOKEN ERROR]"); 47 | break; 48 | case PHASE_PARSE: 49 | fprint(b, "[PARSE ERROR]"); 50 | break; 51 | case PHASE_TYPE: 52 | fprint(b, "[TYPE ERROR]"); 53 | break; 54 | default: 55 | break; 56 | } 57 | } 58 | 59 | static Source* _src; 60 | 61 | void useSource(Source* src) { 62 | _src = src; 63 | } 64 | 65 | Source* currentSource() { 66 | return _src; 67 | } 68 | 69 | void reportError(const Error& error) { 70 | ustring s; 71 | buffer b = error.message; 72 | fread(b, s); 73 | vector& es = errorFrames.size() 74 | ? errorFrames.back() : errors; 75 | set& ms = frameMessages.size() 76 | ? frameMessages.back() : messages; 77 | if (ms.find(s) == ms.end()) { 78 | ms.insert(s); 79 | es.push(error); 80 | if (!es.back().src && _src) es.back().src = _src; 81 | } 82 | } 83 | 84 | u32 countErrors() { 85 | vector& es = errorFrames.size() 86 | ? errorFrames.back() : errors; 87 | return es.size(); 88 | } 89 | 90 | void printErrors(stream& io) { 91 | println(io, countErrors(), " error", countErrors() != 1 ? "s" : ""); 92 | for (const Error& e : (errorFrames.size() 93 | ? errorFrames.back() : errors)) print(io, e); 94 | } 95 | 96 | Error& lastError() { 97 | return (errorFrames.size() ? errorFrames.back().back() : errors.back()); 98 | } 99 | } 100 | 101 | void print(stream& io, const basil::Error& e) { 102 | e.format(io); 103 | } 104 | 105 | void print(const basil::Error& e) { 106 | e.format(_stdout); 107 | } -------------------------------------------------------------------------------- /src/hash.cpp: -------------------------------------------------------------------------------- 1 | #include "hash.h" 2 | 3 | u64 rotl(u64 u, u64 n) { 4 | return (u << n) | ((u >> (64 - n)) & ~(-1 << n)); 5 | } 6 | 7 | u64 rotr(u64 u, u64 n) { 8 | return (u >> n) | ((u << (64 - n)) & (-1 & ~(-1 << n))); 9 | } 10 | 11 | u64 raw_hash(const void* t, uint64_t size) { 12 | u64 h = 13830991727719723691ul; 13 | 14 | const u64* words = (const u64*)t; 15 | u32 i = 0; 16 | for (; i < size / 8; ++ i) { 17 | uint64_t u = *words * 4695878395758459391ul; 18 | h -= u; 19 | h ^= (h >> 23); 20 | ++ words; 21 | } 22 | 23 | const u8* bytes = (const u8*)words; 24 | i *= 8; 25 | for (; i < size; ++ i) { 26 | uint64_t u = *bytes * 4695878395758459391ul; 27 | h -= u; 28 | h ^= (h >> 23); 29 | ++ bytes; 30 | } 31 | return h ^ (h << 37); 32 | } 33 | 34 | template<> 35 | u64 hash(const char* const& s) { 36 | u32 size = 0; 37 | const char* sptr = s; 38 | while (*sptr) ++ sptr, ++ size; 39 | return raw_hash((const u8*)s, size); 40 | } 41 | 42 | template<> 43 | u64 hash(const string& s) { 44 | return raw_hash((const u8*)s.raw(), s.size()); 45 | } -------------------------------------------------------------------------------- /src/import.cpp: -------------------------------------------------------------------------------- 1 | #include "import.h" 2 | #include "lex.h" 3 | #include "term.h" 4 | #include "parse.h" 5 | #include "errors.h" 6 | #include "value.h" 7 | 8 | namespace basil { 9 | map modules; 10 | 11 | Module::Module(const string& path, Source* src, ProgramTerm* body, Stack* env): 12 | _path(path), _src(src), _body(body), _env(env) { 13 | // 14 | } 15 | 16 | Module::~Module() { 17 | delete _src; 18 | delete _body; 19 | delete _env; 20 | } 21 | 22 | void Module::useIn(Stack& ctx, u32 line, u32 column) { 23 | for (auto& p : _body->scope().scope()) { 24 | auto it = ctx.nearestScope().find(p.first); 25 | if (it != ctx.nearestScope().end()) { 26 | err(PHASE_TYPE, line, column, 27 | "Module '", _path, "' redefines variable '", 28 | p.first, "' from the local environment."); 29 | return; 30 | } 31 | ctx.nearestScope().put(p.first, p.second); 32 | } 33 | } 34 | 35 | void freeModules() { 36 | for (auto& p : modules) delete p.second; 37 | } 38 | 39 | Module* loadModule(const char* path, u32 line, u32 column) { 40 | auto it = modules.find(path); 41 | if (it != modules.end()) { 42 | return it->second; 43 | } 44 | 45 | if (!exists(path)) { 46 | err(PHASE_TYPE, line, column, 47 | "Could not find module at relative path '", 48 | path, "'."); 49 | return nullptr; 50 | } 51 | Source* src = new Source(path); 52 | Source* prev = currentSource(); 53 | useSource(src); 54 | TokenCache tok = lex(*src); 55 | if (countErrors()) { 56 | printErrors(_stdout); 57 | delete src; 58 | return nullptr; 59 | } 60 | TokenCache::View v = tok.view(); 61 | ProgramTerm* module = parseFull(v); 62 | if (countErrors()) { 63 | printErrors(_stdout); 64 | delete src; 65 | delete module; 66 | return nullptr; 67 | } 68 | Stack* env = new Stack(nullptr); 69 | module->eval(*env); 70 | if (countErrors()) { 71 | printErrors(_stdout); 72 | delete src; 73 | delete env; 74 | delete module; 75 | return nullptr; 76 | } 77 | useSource(prev); 78 | return modules[path] = new Module(path, src, module, env); 79 | } 80 | } -------------------------------------------------------------------------------- /src/io.cpp: -------------------------------------------------------------------------------- 1 | #include "io.h" 2 | #include "str.h" 3 | 4 | bool exists(const char* path) { 5 | FILE* f = fopen(path, "r"); 6 | if (!f) return false; 7 | else return fclose(f), true; 8 | } 9 | 10 | file::file(const char* fname, const char* flags): 11 | file(fopen(fname, flags)) { 12 | // 13 | } 14 | 15 | file::file(FILE* f_in): f(f_in), done(false) { 16 | // 17 | } 18 | 19 | file::~file() { 20 | fclose(f); 21 | } 22 | 23 | void file::write(u8 c) { 24 | fputc(c, f); 25 | } 26 | 27 | u8 file::read() { 28 | if (done) return '\0'; 29 | int i = fgetc(f); 30 | if (i == EOF) done = true; 31 | return i; 32 | } 33 | 34 | u8 file::peek() const { 35 | if (done) return '\0'; 36 | int i = fgetc(f); 37 | ungetc(i, f); 38 | if (i == EOF) return '\0'; 39 | return i; 40 | } 41 | 42 | void file::unget(u8 c) { 43 | ungetc(c, f); 44 | } 45 | 46 | file::operator bool() const { 47 | int i = fgetc(f); 48 | ungetc(i, f); 49 | return i != EOF; 50 | } 51 | 52 | void buffer::init(u32 size) { 53 | _start = 0, _end = 0, _capacity = size; 54 | if (_capacity <= 8) data = buf; 55 | else data = new u8[_capacity]; 56 | } 57 | 58 | void buffer::free() { 59 | if (_capacity > 8) delete[] data; 60 | } 61 | 62 | void buffer::copy(u8* other, u32 size, u32 start, u32 end) { 63 | while (start != end) { 64 | data[_end ++] = other[start]; 65 | start = (start + 1) & (size - 1); 66 | } 67 | } 68 | 69 | void buffer::grow() { 70 | u8* old = data; 71 | u32 oldcap = _capacity; 72 | u32 oldstart = _start, oldend = _end; 73 | init(_capacity *= 2); 74 | copy(old, oldcap, oldstart, oldend); 75 | if (oldcap > 8) delete[] old; 76 | } 77 | 78 | buffer::buffer() { 79 | init(8); 80 | } 81 | 82 | buffer::~buffer() { 83 | free(); 84 | } 85 | 86 | buffer::buffer(const buffer& other) { 87 | init(other._capacity); 88 | copy(other.data, other._capacity, other._start, other._end); 89 | } 90 | 91 | buffer& buffer::operator=(const buffer& other) { 92 | if (this != &other) { 93 | free(); 94 | init(other._capacity); 95 | copy(other.data, other._capacity, other._start, other._end); 96 | } 97 | return *this; 98 | } 99 | 100 | void buffer::write(u8 c) { 101 | if (((_end + 1) & (_capacity - 1)) == _start) grow(); 102 | data[_end] = c; 103 | _end = (_end + 1) & (_capacity - 1); 104 | } 105 | 106 | u8 buffer::read() { 107 | if (_start == _end) return '\0'; 108 | u8 c = data[_start]; 109 | _start = (_start + 1) & (_capacity - 1); 110 | return c; 111 | } 112 | 113 | u8 buffer::peek() const { 114 | if (_start == _end) return '\0'; 115 | return data[_start]; 116 | } 117 | 118 | void buffer::unget(u8 c) { 119 | _start = (_start - 1) & (_capacity - 1); 120 | while (_start == _end) grow(), _start = (_start - 1) & (_capacity - 1); 121 | data[_start] = c; 122 | } 123 | 124 | u32 buffer::size() const { 125 | return (i64(_end) - i64(_start)) & (_capacity - 1); 126 | } 127 | 128 | u32 buffer::capacity() const { 129 | return _capacity; 130 | } 131 | 132 | buffer::operator bool() const { 133 | return _start != _end; 134 | } 135 | 136 | u8* buffer::begin() { 137 | return data + _start; 138 | } 139 | 140 | const u8* buffer::begin() const { 141 | return data + _start; 142 | } 143 | 144 | u8* buffer::end() { 145 | return data + _end; 146 | } 147 | 148 | const u8* buffer::end() const { 149 | return data + _end; 150 | } 151 | 152 | 153 | file _stdin_file(stdin), _stdout_file(stdout); 154 | 155 | stream &_stdin = _stdin_file, &_stdout = _stdout_file; 156 | 157 | static u32 precision = 5; 158 | 159 | void setprecision(u32 p) { 160 | precision = p; 161 | } 162 | 163 | static void print_unsigned(stream& io, u64 n) { 164 | u64 m = n, p = 1; 165 | while (m / 10) m /= 10, p *= 10; 166 | while (p) io.write('0' + (n / p % 10)), p /= 10; 167 | } 168 | 169 | static void print_signed(stream& io, i64 n) { 170 | if (n < 0) io.write('-'), n = -n; 171 | print_unsigned(io, n); 172 | } 173 | 174 | static void print_rational(stream& io, double d) { 175 | if (d < 0) io.write('-'), d = -d; 176 | print_unsigned(io, u64(d)); 177 | io.write('.'); 178 | double r = d - u64(d); 179 | u32 p = precision, zeroes = 0; 180 | bool isZero = r == 0; 181 | while (r && p) { 182 | r *= 10; 183 | if (u8(r)) { 184 | isZero = false; 185 | while (zeroes) io.write('0'), -- zeroes; 186 | io.write('0' + u8(r)); 187 | } 188 | else ++ zeroes; 189 | r -= u8(r); 190 | -- p; 191 | } 192 | if (isZero) io.write('0'); 193 | } 194 | 195 | void print(stream& io, u8 c) { 196 | io.write(c); 197 | } 198 | 199 | void print(stream& io, u16 n) { 200 | print_unsigned(io, n); 201 | } 202 | 203 | void print(stream& io, u32 n) { 204 | print_unsigned(io, n); 205 | } 206 | 207 | void print(stream& io, u64 n) { 208 | print_unsigned(io, n); 209 | } 210 | 211 | void print(stream& io, i8 c) { 212 | io.write(c); 213 | } 214 | 215 | void print(stream& io, i16 n) { 216 | print_signed(io, n); 217 | } 218 | 219 | void print(stream& io, i32 n) { 220 | print_signed(io, n); 221 | } 222 | 223 | void print(stream& io, i64 n) { 224 | print_signed(io, n); 225 | } 226 | 227 | void print(stream& io, float f) { 228 | print_rational(io, f); 229 | } 230 | 231 | void print(stream& io, double d) { 232 | print_rational(io, d); 233 | } 234 | 235 | void print(stream& io, char c) { 236 | io.write(c); 237 | } 238 | 239 | void print(stream& io, bool b) { 240 | print(io, b ? "true" : "false"); 241 | } 242 | 243 | void print(stream& io, const u8* s) { 244 | while (*s) io.write(*(s ++)); 245 | } 246 | 247 | void print(stream& io, const char* s) { 248 | while (*s) io.write(*(s ++)); 249 | } 250 | 251 | void print(u8 c) { 252 | print(_stdout, c); 253 | } 254 | 255 | void print(u16 n) { 256 | print(_stdout, n); 257 | } 258 | 259 | void print(u32 n) { 260 | print(_stdout, n); 261 | } 262 | 263 | void print(u64 n) { 264 | print(_stdout, n); 265 | } 266 | 267 | void print(i8 c) { 268 | print(_stdout, c); 269 | } 270 | 271 | void print(i16 n) { 272 | print(_stdout, n); 273 | } 274 | 275 | void print(i32 n) { 276 | print(_stdout, n); 277 | } 278 | 279 | void print(i64 n) { 280 | print(_stdout, n); 281 | } 282 | 283 | void print(char c) { 284 | print(_stdout, c); 285 | } 286 | 287 | void print(bool b) { 288 | print(_stdout, b); 289 | } 290 | 291 | void print(float f) { 292 | print(_stdout, f); 293 | } 294 | 295 | void print(double d) { 296 | print(_stdout, d); 297 | } 298 | 299 | void print(const u8* s) { 300 | print(_stdout, s); 301 | } 302 | 303 | void print(const char* s) { 304 | print(_stdout, s); 305 | } 306 | 307 | void print(stream& io, const buffer& b) { 308 | for (u8 c : b) io.write(c); 309 | } 310 | 311 | void print(const buffer& b) { 312 | print(_stdout, b); 313 | } 314 | 315 | bool isspace(u8 c) { 316 | return c == ' ' || c == '\t' || c == '\n' || c == '\r'; 317 | } 318 | 319 | static u64 read_unsigned(stream& io) { 320 | while (isspace(io.peek())) io.read(); // consume leading spaces 321 | u64 result = 0; 322 | while (isspace(io.peek())) io.read(); 323 | while (io.peek()) { 324 | u8 c = io.peek(); 325 | if (isspace(c)) break; 326 | else if (c < '0' || c > '9') { 327 | while (io.peek() && !isspace(io.peek())) io.read(); 328 | return 0; 329 | } 330 | result *= 10; 331 | result += (c - '0'); 332 | io.read(); 333 | } 334 | return result; 335 | } 336 | 337 | static i64 read_signed(stream& io) { 338 | while (isspace(io.peek())) io.read(); // consume leading spaces 339 | if (io.peek() == '-') return io.read(), -read_unsigned(io); 340 | else return read_unsigned(io); 341 | } 342 | 343 | static double read_float(stream& io) { 344 | while (isspace(io.peek())) io.read(); // consume leading spaces 345 | buffer b; 346 | while (io.peek() && !isspace(io.peek()) && io.peek() != '.') { 347 | b.write(io.read()); 348 | } 349 | if (io.peek() != '.') return read_unsigned((stream&)b); 350 | else io.read(); 351 | u64 i; 352 | fread(b, i); 353 | double pow = 0.1; 354 | double result = i; 355 | while (io.peek() && !isspace(io.peek())) { 356 | result += (io.read() - '0') * pow; 357 | pow *= 0.1; 358 | } 359 | return result; 360 | } 361 | 362 | void read(stream& io, u8& c) { 363 | c = io.read(); 364 | } 365 | 366 | void read(stream& io, u16& n) { 367 | n = read_unsigned(io); 368 | } 369 | 370 | void read(stream& io, u32& n) { 371 | n = read_unsigned(io); 372 | } 373 | 374 | void read(stream& io, u64& n) { 375 | n = read_unsigned(io); 376 | } 377 | 378 | void read(stream& io, i8& c) { 379 | c = io.read(); 380 | } 381 | 382 | void read(stream& io, i16& n) { 383 | n = read_signed(io); 384 | } 385 | 386 | void read(stream& io, i32& n) { 387 | n = read_signed(io); 388 | } 389 | 390 | void read(stream& io, i64& n) { 391 | n = read_signed(io); 392 | } 393 | 394 | void read(stream& io, float& f) { 395 | f = read_float(io); 396 | } 397 | 398 | void read(stream& io, double& d) { 399 | d = read_float(io); 400 | } 401 | 402 | void read(stream& io, char& c) { 403 | c = io.read(); 404 | } 405 | 406 | void read(stream& io, bool& c) { 407 | while (isspace(io.peek())) io.read(); // consume leading spaces 408 | string s; 409 | while (!isspace(io.peek())) s += io.read(); 410 | if (s == "true") c = true; 411 | else if (s == "false") c = false; 412 | } 413 | 414 | void read(stream& io, u8* s) { 415 | while (isspace(io.peek())) io.read(); // consume leading spaces 416 | while (u8 c = io.read()) *(s ++) = c; 417 | } 418 | 419 | void read(stream& io, char* s) { 420 | while (isspace(io.peek())) io.read(); // consume leading spaces 421 | while (u8 c = io.read()) *(s ++) = c; 422 | } 423 | 424 | void read(u8& c) { 425 | read(_stdin, c); 426 | } 427 | 428 | void read(u16& n) { 429 | read(_stdin, n); 430 | } 431 | 432 | void read(u32& n) { 433 | read(_stdin, n); 434 | } 435 | 436 | void read(u64& n) { 437 | read(_stdin, n); 438 | } 439 | 440 | void read(i8& c) { 441 | read(_stdin, c); 442 | } 443 | 444 | void read(i16& n) { 445 | read(_stdin, n); 446 | } 447 | 448 | void read(i32& n) { 449 | read(_stdin, n); 450 | } 451 | 452 | void read(i64& n) { 453 | read(_stdin, n); 454 | } 455 | 456 | void read(float& f) { 457 | read(_stdin, f); 458 | } 459 | 460 | void read(double& d) { 461 | read(_stdin, d); 462 | } 463 | 464 | void read(char& c) { 465 | read(_stdin, c); 466 | } 467 | 468 | void read(bool& b) { 469 | read(_stdin, b); 470 | } 471 | 472 | void read(u8* s) { 473 | read(_stdin, s); 474 | } 475 | 476 | void read(char* s) { 477 | read(_stdin, s); 478 | } 479 | -------------------------------------------------------------------------------- /src/lex.cpp: -------------------------------------------------------------------------------- 1 | #include "lex.h" 2 | 3 | namespace basil { 4 | 5 | const u32 6 | TOKEN_NONE = 0, 7 | TOKEN_IDENT = 1, 8 | TOKEN_STRING = 2, 9 | TOKEN_CHAR = 3, 10 | TOKEN_NUMBER = 4, 11 | TOKEN_SYMBOL = 5, 12 | TOKEN_LPAREN = 6, 13 | TOKEN_RPAREN = 7, 14 | TOKEN_LBRACE = 8, 15 | TOKEN_RBRACE = 9, 16 | TOKEN_LBRACK = 10, 17 | TOKEN_RBRACK = 11, 18 | TOKEN_COLON = 12, 19 | TOKEN_SEMI = 13, 20 | TOKEN_NEWLINE = 14, 21 | TOKEN_ASSIGN = 15, 22 | TOKEN_LAMBDA = 16, 23 | TOKEN_DOT = 17, 24 | TOKEN_PLUS = 18, 25 | TOKEN_MINUS = 19, 26 | TOKEN_EVAL = 20, 27 | TOKEN_BOOL = 21, 28 | TOKEN_REF = 22, 29 | TOKEN_QUOTE = 23; 30 | 31 | const char* TOKEN_NAMES[24] = { 32 | "none", 33 | "ident", 34 | "string", 35 | "char", 36 | "number", 37 | "symbol", 38 | "left paren", 39 | "right paren", 40 | "left brace", 41 | "right brace", 42 | "left bracket", 43 | "right bracket", 44 | "colon", 45 | "semicolon", 46 | "newline", 47 | "assign", 48 | "lambda", 49 | "dot", 50 | "plus", 51 | "minus", 52 | "eval", 53 | "bool", 54 | "ref", 55 | "quote" 56 | }; 57 | 58 | Token::Token(): type(TOKEN_NONE) { 59 | // 60 | } 61 | 62 | Token::Token(const ustring& value_in, u32 type_in, 63 | u32 line_in, u32 column_in): 64 | value(value_in), type(type_in), line(line_in), column(column_in) { 65 | // 66 | } 67 | 68 | Token::operator bool() const { 69 | return type != TOKEN_NONE; 70 | } 71 | 72 | const Token TokenCache::NONE; 73 | 74 | TokenCache::TokenCache(Source* src): _src(src) { 75 | // 76 | } 77 | 78 | void TokenCache::push(const Token& t) { 79 | tokens.push(t); 80 | } 81 | 82 | TokenCache::View::View(TokenCache* cache): _cache(cache), i(0) { 83 | // 84 | } 85 | 86 | TokenCache::View::View(TokenCache* cache, u32 index): 87 | _cache(cache), i(index) { 88 | // 89 | } 90 | 91 | const Token& TokenCache::View::read() { 92 | if (i >= _cache->size()) return TokenCache::NONE; 93 | return _cache->tokens[i ++]; 94 | } 95 | 96 | const Token& TokenCache::View::peek() const { 97 | if (i >= _cache->size()) return TokenCache::NONE; 98 | return _cache->tokens[i]; 99 | } 100 | 101 | TokenCache& TokenCache::View::cache() { 102 | return *_cache; 103 | } 104 | 105 | TokenCache::View::operator bool() const { 106 | return i < _cache->size(); 107 | } 108 | 109 | const Token* TokenCache::begin() const { 110 | return tokens.begin(); 111 | } 112 | 113 | const Token* TokenCache::end() const { 114 | return tokens.end(); 115 | } 116 | 117 | Source* TokenCache::source() { 118 | return _src; 119 | } 120 | 121 | TokenCache::View TokenCache::view() { 122 | return View(this); 123 | } 124 | 125 | TokenCache::View TokenCache::expand(stream& io) { 126 | auto v = _src->expand(io); 127 | println(io, "Source: ", *_src); 128 | View mv(this, tokens.size()); 129 | while (v.peek()) { 130 | Token t = scan(v); 131 | if (t) push(t); 132 | } 133 | 134 | return mv; 135 | } 136 | 137 | u32 TokenCache::size() const { 138 | return tokens.size(); 139 | } 140 | 141 | static bool isDelimiter(Source::View& view) { 142 | uchar c = view.peek(); 143 | if (c == ':') { 144 | view.read(); 145 | bool delim = isspace(view.peek()); 146 | view.rewind(); 147 | return delim; 148 | } 149 | return !c || isspace(c) 150 | || c == '(' || c == ')' || c == '{' || c == '}' 151 | || c == ';' || c == ',' || c == '[' 152 | || c == ']' || c == '\''|| c == '"' || c == '.'; 153 | } 154 | 155 | static bool isClosingDelimiter(Source::View& view) { 156 | uchar c = view.peek(); 157 | return !c || isspace(c) 158 | || c == ')'|| c == '}' 159 | || c == ';' || c == ',' 160 | || c == ']' || c == '.'; 161 | } 162 | 163 | static bool isDelimiterToken(Source::View& view) { 164 | uchar c = view.peek(); 165 | if (c == ':') { 166 | view.read(); 167 | bool delim = isspace(view.peek()); 168 | view.rewind(); 169 | return delim; 170 | } 171 | return c == '(' || c == ')' || c == '{' || c == '}' 172 | || c == ';' || c == ',' || c == '[' 173 | || c == ']' || c == '\n'|| c == '.'; 174 | } 175 | 176 | static Token fromType(u32 type, const Source::View& view) { 177 | return Token("", type, view.line(), view.column()); 178 | } 179 | 180 | static Token fromValue(u32 type, const ustring& val, const Source::View& view) { 181 | return Token(val, type, view.line(), view.column()); 182 | } 183 | 184 | static Token getDelimiterToken(uchar c, const Source::View& view) { 185 | if (c == '(') return fromType(TOKEN_LPAREN, view); 186 | else if (c == ')') return fromType(TOKEN_RPAREN, view); 187 | else if (c == '{') return fromType(TOKEN_LBRACE, view); 188 | else if (c == '}') return fromType(TOKEN_RBRACE, view); 189 | else if (c == '[') return fromType(TOKEN_LBRACK, view); 190 | else if (c == ']') return fromType(TOKEN_RBRACK, view); 191 | else if (c == ':') return fromType(TOKEN_COLON, view); 192 | else if (c == ';') return fromType(TOKEN_SEMI, view); 193 | else if (c == '\n') return fromType(TOKEN_NEWLINE, view); 194 | else if (c == '.') return fromType(TOKEN_DOT, view); 195 | else if (c == ',') return fromValue(TOKEN_IDENT, ",", view); 196 | else return Token(); 197 | } 198 | 199 | void scanNumberTail(Token& t, Source::View& view) { 200 | while (!isDelimiter(view)) { 201 | if (isdigit(view.peek())) t.value += view.read(); 202 | else { 203 | err(PHASE_LEX, view.source(), view.line(), view.column(), 204 | "Unexpected symbol '", view.peek(), 205 | "' in numeric literal."); 206 | break; 207 | } 208 | } 209 | } 210 | 211 | void scanNumberHead(Token& t, Source::View& view) { 212 | while (!isDelimiter(view) || view.peek() == '.') { 213 | if (isdigit(view.peek())) t.value += view.read(); 214 | else if (view.peek() == '.') { 215 | uchar dot = view.read(); 216 | if (isdigit(view.peek())) { 217 | return t.value += dot, scanNumberTail(t, view); 218 | } 219 | else { 220 | view.rewind(); 221 | break; 222 | } 223 | } 224 | else { 225 | err(PHASE_LEX, view.source(), view.line(), view.column(), 226 | "Unexpected symbol '", view.peek(), 227 | "' in numeric literal."); 228 | break; 229 | } 230 | } 231 | } 232 | 233 | void scanIdentifier(Token& t, Source::View& view); 234 | 235 | void scanEscape(Token& t, Source::View& view) { 236 | view.read(); // consume backslash 237 | if (view.peek() == 'n') t.value += '\n', view.read(); 238 | else if (view.peek() == 't') t.value += '\t', view.read(); 239 | else if (view.peek() == 'r') t.value += '\r', view.read(); 240 | else if (view.peek() == '0') t.value += '\0', view.read(); 241 | else if (view.peek() == '\\') t.value += '\\', view.read(); 242 | else if (view.peek() == '\"') t.value += '\"', view.read(); 243 | else if (view.peek() == '\'') t.value += '\'', view.read(); 244 | else { 245 | err(PHASE_LEX, view.source(), view.line(), view.column(), 246 | "Invalid escape sequence '\\", view.peek(), "'."); 247 | } 248 | } 249 | 250 | void scanString(Token& t, Source::View& view) { 251 | view.read(); // consume preceding quote 252 | while (view.peek() != '"') { 253 | if (!view.peek()) { 254 | err(PHASE_LEX, view.source(), view.line(), view.column(), 255 | "Unexpected end of input in string literal."); 256 | break; 257 | } 258 | else if (view.peek() == '\n') { 259 | err(PHASE_LEX, view.source(), view.line(), view.column(), 260 | "Unexpected end of line in string literal."); 261 | break; 262 | } 263 | else if (view.peek() == '\\') scanEscape(t, view); 264 | else t.value += view.read(); 265 | } 266 | view.read(); // consume trailing quote 267 | } 268 | 269 | void scanChar(Token& t, Source::View& view) { 270 | view.read(); // consume preceding quote 271 | if (!view.peek()) { 272 | err(PHASE_LEX, view.source(), view.line(), view.column(), 273 | "Unexpected end of input in character literal."); 274 | } 275 | else if (view.peek() == '\\') scanEscape(t, view); 276 | else if (view.peek() == '\n') { 277 | err(PHASE_LEX, view.source(), view.line(), view.column(), 278 | "Unexpected end of line in character literal."); 279 | } 280 | else t.value += view.read(); 281 | if (view.peek() != '\'') { 282 | err(PHASE_LEX, view.source(), view.line(), view.column(), 283 | "Expected closing quote in character literal, ", 284 | "found unexpected symbol '", view.peek(), "'."); 285 | } 286 | view.read(); // consume trailing quote 287 | } 288 | 289 | void scanDot(Token& t, Source::View& view) { 290 | t.value += view.read(); 291 | if (view.peek() == '.') scanDot(t, view); 292 | else { 293 | if (t.value == ".") t = fromType(TOKEN_DOT, view); 294 | else t = fromValue(TOKEN_IDENT, t.value, view); 295 | } 296 | } 297 | 298 | void scanPrefixColon(Token& t, Source::View& view) { 299 | if (isDelimiterToken(view)) 300 | return t = getDelimiterToken(view.read(), view), void(); 301 | 302 | t.value += view.read(); 303 | 304 | // special colon cases 305 | if (view.peek() == ':') { 306 | view.read(); 307 | 308 | // :: is identifier 309 | if (view.peek() != ':' && isDelimiter(view)) 310 | t.type = TOKEN_IDENT, t.value += ':'; 311 | else view.rewind(); 312 | } 313 | } 314 | 315 | void scanPrefixOp(Token& t, Source::View& view) { 316 | t.value += view.read(); 317 | 318 | // treat as identifier if followed by special symbol 319 | if (view.peek() == '-' || view.peek() == '+' || view.peek() == '=' 320 | || view.peek() == '>' || view.peek() == '!' 321 | || view.peek() == '~' || isClosingDelimiter(view)) { 322 | t.type = TOKEN_IDENT, scanIdentifier(t, view); 323 | } 324 | else if (isspace(view.peek())) t.type = TOKEN_IDENT; 325 | } 326 | 327 | void scanIdentifier(Token& t, Source::View& view) { 328 | while (!isDelimiter(view) || 329 | (view.peek() == ':' 330 | && t.value[t.value.size() - 1] == ':')) { 331 | if (issym(view.peek())) t.value += view.read(); 332 | else { 333 | err(PHASE_LEX, view.source(), view.line(), view.column(), 334 | "Unexpected symbol '", view.peek(), 335 | "' in identifier."); 336 | break; 337 | } 338 | } 339 | if (t.value[0] == '_') 340 | err(PHASE_LEX, view.source(), view.line(), view.column(), 341 | "Identifiers may not begin with underscores."); 342 | if (t.value == "->") t.type = TOKEN_LAMBDA, t.value = ""; 343 | if (t.value == "=") t.type = TOKEN_ASSIGN, t.value = ""; 344 | if (t.value == "true" || t.value == "false") t.type = TOKEN_BOOL; 345 | } 346 | 347 | Token scan(Source::View& view) { 348 | uchar c = view.peek(); 349 | Token t; 350 | if (c == '#') { 351 | // line comments 352 | while (view.peek() != '\n') view.read(); 353 | } 354 | else if (c == '.') { 355 | t = fromType(TOKEN_IDENT, view); 356 | scanDot(t, view); 357 | } 358 | else if (c == '-') { 359 | t = fromType(TOKEN_MINUS, view); 360 | scanPrefixOp(t, view); 361 | } 362 | else if (c == '+') { 363 | t = fromType(TOKEN_PLUS, view); 364 | scanPrefixOp(t, view); 365 | } 366 | else if (c == ':') { 367 | t = fromType(TOKEN_QUOTE, view); 368 | scanPrefixColon(t, view); 369 | } 370 | else if (c == '!') { 371 | t = fromType(TOKEN_EVAL, view); 372 | scanPrefixOp(t, view); 373 | } 374 | else if (c == '~') { 375 | t = fromType(TOKEN_REF, view); 376 | scanPrefixOp(t, view); 377 | } 378 | else if (isdigit(c)) { 379 | t = fromType(TOKEN_NUMBER, view); 380 | scanNumberHead(t, view); 381 | } 382 | else if (isDelimiterToken(view)) { 383 | t = getDelimiterToken(c, view); 384 | view.read(); 385 | } 386 | else if (c == '"') { 387 | t = fromType(TOKEN_STRING, view); 388 | scanString(t, view); 389 | } 390 | else if (c == '\'') { 391 | t = fromType(TOKEN_CHAR, view); 392 | scanChar(t, view); 393 | } 394 | else if (issym(c)) { 395 | t = fromType(TOKEN_IDENT, view); 396 | scanIdentifier(t, view); 397 | } 398 | else if (isspace(c)) view.read(); 399 | else { 400 | err(PHASE_LEX, view.source(), view.line(), view.column(), 401 | "Unexpected symbol '", view.peek(), "' in input."); 402 | view.read(); 403 | } 404 | 405 | return t; 406 | } 407 | 408 | TokenCache lex(Source& src) { 409 | auto view = src.view(); 410 | TokenCache cache(&src); 411 | while (view.peek()) { 412 | Token t = scan(view); 413 | if (t) cache.push(t); 414 | } 415 | 416 | if (countErrors() > 0) { 417 | return TokenCache(); 418 | } 419 | 420 | return cache; 421 | } 422 | } 423 | 424 | void print(stream& io, const basil::Token& t) { 425 | print(io, "[", t.line, ":", t.column, "]\t"); 426 | print(io, "token ", t.type); // token info 427 | print(io, " (", basil::TOKEN_NAMES[t.type], ")"); // type desc 428 | if (t.value.size() > 0) { 429 | print(io, ":\t\"", t.value, "\""); 430 | } 431 | } 432 | 433 | void print(const basil::Token& t) { 434 | print(_stdout, t); 435 | } 436 | 437 | void print(stream& io, const basil::TokenCache& c) { 438 | println(io, c.size(), " tokens"); 439 | for (const basil::Token& t : c) println(io, t); 440 | println(io, "----"); 441 | } 442 | 443 | void print(const basil::TokenCache& c) { 444 | print(_stdout, c); 445 | } 446 | 447 | void read(stream& io, basil::Token& t) { 448 | string key; 449 | u32 line, column, type; 450 | ustring value; 451 | while (isspace(io.peek())) io.read(); // consume leading spaces 452 | 453 | buffer num; 454 | io.read(); 455 | while (io.peek() != ':') num.write(io.read()); 456 | io.read(), num.write(' '); 457 | while (io.peek() != ']') num.write(io.read()); 458 | io.read(); 459 | 460 | fread(num, line, column); 461 | 462 | read(io, key, type); // read prefix and info 463 | 464 | while (io.peek() != ')') io.read(); // consume type desc 465 | io.read(); 466 | 467 | if (io.peek() == ':') { // if value present 468 | while (io.peek() != '"') io.read(); // consume preceding junk 469 | io.read(); 470 | 471 | while (io.peek() != '"') { // read value string 472 | uchar c; 473 | read(io, c); 474 | value += c; 475 | } 476 | io.read(); // consume closing quote 477 | } 478 | t = basil::Token(value, type, line, column); 479 | } 480 | 481 | void read(basil::Token& t) { 482 | read(_stdin, t); 483 | } 484 | 485 | void read(stream& io, basil::TokenCache& c) { 486 | while (isspace(io.peek())) io.read(); // consume leading spaces 487 | 488 | u32 count; 489 | string trash; 490 | read(io, count, trash); // remove header 491 | 492 | for (u32 i = 0; i < count; ++ i) { 493 | basil::Token t; 494 | read(io, t); 495 | c.push(t); 496 | } 497 | read(io, trash); // consume "----" 498 | } 499 | 500 | void read(basil::TokenCache& c) { 501 | read(_stdin, c); 502 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "vec.h" 2 | #include "utf8.h" 3 | #include "hash.h" 4 | #include "stdio.h" 5 | #include "stdlib.h" 6 | #include "source.h" 7 | #include "time.h" 8 | #include "lex.h" 9 | #include "parse.h" 10 | #include "type.h" 11 | #include "term.h" 12 | #include "value.h" 13 | #include "ir.h" 14 | 15 | using namespace basil; 16 | 17 | u32 LEX = 1, 18 | PARSE = 2, 19 | AST = 3, 20 | IR = 4, 21 | ASM = 5; 22 | 23 | bool interactive = true; 24 | bool silent = false; 25 | u32 level = ASM; 26 | Source* src = nullptr; 27 | ustring outfile; 28 | ustring infile; 29 | 30 | ustring stripEnding(const ustring& path) { 31 | ustring n; 32 | i64 lastDot = -1; 33 | for (u32 i = 0; i < path.size(); i ++) { 34 | if (path[i] == '.') lastDot = i; 35 | } 36 | for (u32 i = 0; i < path.size(); i ++) { 37 | if (i < lastDot || lastDot <= 0) n += path[i]; 38 | } 39 | return n; 40 | } 41 | 42 | void repl() { 43 | CodeGenerator gen; 44 | useSource(src); 45 | TokenCache cache(src); 46 | ProgramTerm* program = new ProgramTerm({}, 1, 1); 47 | while (!countErrors()) { 48 | if (level < LEX) return; 49 | print("? "); 50 | TokenCache::View view = cache.expand(_stdin); 51 | if (countErrors()) { 52 | printErrors(_stdout); 53 | continue; 54 | } 55 | else if (level == LEX && !silent) { 56 | println(""); 57 | println(_stdout, cache); 58 | println(""); 59 | } 60 | 61 | if (view.peek().type == TOKEN_IDENT && view.peek().value == "quit") { 62 | println("Leaving REPL..."); 63 | break; 64 | } 65 | 66 | if (level < PARSE) continue; 67 | 68 | Term* t = parse(view, true); 69 | if (countErrors()) { 70 | printErrors(_stdout); 71 | continue; 72 | } 73 | else if (level == PARSE && !silent) { 74 | println(""); 75 | t->format(_stdout); 76 | println(""); 77 | } 78 | 79 | if (level < AST) continue; 80 | 81 | Stack* s = new Stack(); 82 | program->add(t); 83 | program->evalChild(*s, t); 84 | for (Value* v : *s) { 85 | Meta m = v->fold(program->scope()); 86 | if (m && !m.isVoid() && !v->is()) 87 | println(_stdout, m); 88 | } 89 | println(_stdout, ""); 90 | 91 | if (countErrors()) { 92 | printErrors(_stdout); 93 | continue; 94 | } 95 | else if (level == AST && !silent) { 96 | println(""); 97 | for (Value* v : *s) println(_stdout, v); 98 | println(""); 99 | } 100 | 101 | if (level < IR) continue; 102 | 103 | for (Value* v : *s) v->gen(program->scope(), gen, gen); 104 | gen.finalize(gen); 105 | 106 | if (level < ASM) { 107 | if (!silent) gen.format(_stdout); 108 | continue; 109 | } 110 | 111 | gen.allocate(); 112 | buffer text, data; 113 | gen.emitX86(text, data); 114 | if (!silent) fprint(_stdout, data, text); 115 | 116 | delete s; 117 | } 118 | 119 | delete program; 120 | delete src; 121 | } 122 | 123 | int parseArgs(int argc, char** argv) { 124 | while (argc) { 125 | if (string(*argv) == "-o") { 126 | if (argc == 1) { 127 | println("Error: '-o' was provided without an argument."); 128 | return 1; 129 | } 130 | outfile = ustring(*(--argc, ++argv)); 131 | } 132 | else if (string(*argv) == "-silent") { 133 | silent = true; 134 | } 135 | else if (string(*argv) == "-ir") { 136 | level = IR; 137 | } 138 | else if (string(*argv) == "-ast") { 139 | level = AST; 140 | } 141 | else if (string(*argv) == "-lex") { 142 | level = LEX; 143 | } 144 | else if (string(*argv) == "-parse") { 145 | level = PARSE; 146 | } 147 | else { 148 | if (!exists(*argv)) { 149 | println(_stdout, "Error: cannot open source file '", (const char*)*argv, "'."); 150 | return 1; 151 | } 152 | infile = ustring(*argv); 153 | interactive = false; 154 | src = new Source(*argv); 155 | } 156 | --argc, ++ argv; 157 | } 158 | if (!src) { 159 | if (interactive) src = new Source(); 160 | } 161 | return 0; 162 | } 163 | 164 | int main(int argc, char** argv) { 165 | if (auto code = parseArgs(argc - 1, argv + 1)) return code; 166 | if (outfile.size() == 0) { 167 | if (infile.size() > 0) { 168 | outfile = stripEnding(infile) + ".s"; 169 | } 170 | else outfile = "output.s"; 171 | } 172 | 173 | if (interactive) { 174 | repl(); 175 | return countErrors(); 176 | } 177 | 178 | useSource(src); 179 | TokenCache cache(src); 180 | ProgramTerm* program = new ProgramTerm({}, 1, 1); 181 | Stack s; 182 | CodeGenerator gen; 183 | 184 | cache = lex(*src); 185 | if (countErrors()) { 186 | printErrors(_stdout); 187 | return 1; 188 | } 189 | else if (level == LEX && !silent) println(""), println(_stdout, cache), println(""); 190 | 191 | TokenCache::View v = cache.view(); 192 | 193 | if (level >= PARSE) { 194 | program = parseFull(v); 195 | if (countErrors()) { 196 | printErrors(_stdout); 197 | return 1; 198 | } 199 | else if (level == PARSE && !silent) program->format(_stdout); 200 | } 201 | 202 | if (level >= AST) { 203 | program->eval(s); 204 | if (countErrors()) { 205 | printErrors(_stdout); 206 | return 1; 207 | } 208 | else if (level == AST && !silent) for (Value* v : s) println(_stdout, v); 209 | for (Value* v : s) v->pure(program->scope()); 210 | } 211 | 212 | if (level >= IR) { 213 | for (Value* v : s) v->gen(program->scope(), gen, gen); 214 | gen.finalize(gen); 215 | 216 | if (level < ASM) { 217 | if (!silent) gen.format(_stdout); 218 | } 219 | else { 220 | gen.allocate(); 221 | buffer text, data; 222 | gen.emitX86(text, data); 223 | if (!silent) fprint(_stdout, data, text); 224 | } 225 | } 226 | 227 | delete src; 228 | return countErrors(); 229 | } 230 | -------------------------------------------------------------------------------- /src/parse.cpp: -------------------------------------------------------------------------------- 1 | #include "parse.h" 2 | #include "vec.h" 3 | #include "term.h" 4 | #include "io.h" 5 | 6 | namespace basil { 7 | static bool repl_mode; 8 | 9 | template 10 | static void err(TokenCache::View& t, Args... message) { 11 | if (t.cache().source()) { 12 | err(PHASE_PARSE, t.cache().source(), t.peek().line, 13 | t.peek().column, message...); 14 | } 15 | else { 16 | err(PHASE_PARSE, t.peek().line, t.peek().column, message...); 17 | } 18 | } 19 | 20 | void parsePrimary(vector& terms, TokenCache::View& view, u32 indent); 21 | 22 | u32 parseChunk(vector& terms, TokenCache::View& view, 23 | u32 indent, bool consume = true) { 24 | const Token first = view.peek(); 25 | while (true) { 26 | if (view.peek().type == TOKEN_NEWLINE 27 | || view.peek().type == TOKEN_SEMI 28 | || view.peek().type == TOKEN_RPAREN 29 | || view.peek().type == TOKEN_RBRACK 30 | || view.peek().type == TOKEN_RBRACE 31 | || !view.peek()) { 32 | if (view.peek().type == TOKEN_SEMI && consume) 33 | return view.read().type; 34 | return view.peek().type; 35 | } 36 | parsePrimary(terms, view, indent); 37 | } 38 | return TOKEN_NONE; 39 | } 40 | 41 | u32 parseLine(vector& terms, TokenCache::View& view, 42 | u32 indent, bool consume = true) { 43 | vector contents; 44 | u32 l = view.peek().line, c = view.peek().column; 45 | auto terminator = parseChunk(contents, view, indent); 46 | while (terminator == TOKEN_SEMI) { 47 | if (contents.size()) terms.push(new BlockTerm(contents, l, c)); 48 | l = view.peek().line, c = view.peek().column; 49 | contents.clear(); 50 | terminator = parseChunk(contents, view, indent); 51 | } 52 | if (contents.size()) { 53 | if (terms.size()) terms.push(new BlockTerm(contents, l, c)); 54 | else for (Term* t : contents) terms.push(t); 55 | } 56 | 57 | // consume newline 58 | if (consume && terminator == TOKEN_NEWLINE) view.read(); 59 | return terminator; 60 | } 61 | 62 | u32 parseEnclosed(vector& terms, TokenCache::View& view, 63 | u32 closer, u32 indent) { 64 | vector contents; 65 | u32 l = view.peek().line, c = view.peek().column; 66 | auto terminator = parseLine(contents, view, indent); 67 | while (terminator == TOKEN_NEWLINE) { 68 | if (contents.size()) terms.push(new BlockTerm(contents, l, c)); 69 | l = view.peek().line, c = view.peek().column; 70 | contents.clear(); 71 | terminator = parseLine(contents, view, indent); 72 | if (terminator == TOKEN_NONE && repl_mode) { 73 | print(". "); 74 | view.cache().expand(_stdin); 75 | terminator = TOKEN_NEWLINE; 76 | } 77 | } 78 | if (contents.size()) { 79 | if (terms.size() > 0) terms.push(new BlockTerm(contents, l, c)); 80 | else for (Term* t : contents) terms.push(t); 81 | } 82 | 83 | // consume closer 84 | if (terminator == TOKEN_NONE) { 85 | err(view, "Unexpected end of input."); 86 | } 87 | else if (terminator != closer) { 88 | err(view, "Expected '", TOKEN_NAMES[closer], "', found '", 89 | TOKEN_NAMES[terminator], "' at end of enclosed block."); 90 | } 91 | view.read(); 92 | return terminator; 93 | } 94 | 95 | void parseIndented(vector& terms, TokenCache::View& view, 96 | u32 indent, u32 prev) { 97 | const Token first = view.peek(); 98 | 99 | vector contents; 100 | u32 l = view.peek().line, c = view.peek().column; 101 | auto terminator = parseLine(contents, view, c); 102 | 103 | if ((!view.peek() || 104 | (terminator == TOKEN_NONE && view.peek().column > prev)) 105 | && repl_mode) { 106 | print(". "); 107 | view.cache().expand(_stdin); 108 | } 109 | 110 | while (view.peek() && view.peek().column > prev) { 111 | if (contents.size()) terms.push(new BlockTerm(contents, l, c)); 112 | l = view.peek().line, c = view.peek().column; 113 | contents.clear(); 114 | terminator = parseLine(contents, view, c, false); 115 | if (view.peek().type == TOKEN_NEWLINE && view.peek().column > prev) 116 | view.read(); 117 | if ((!view.peek() || 118 | (terminator == TOKEN_NONE && view.peek().column > prev)) 119 | && repl_mode) { 120 | print(". "); 121 | view.cache().expand(_stdin); 122 | } 123 | } 124 | if (contents.size()) { 125 | if (terms.size() > 0) terms.push(new BlockTerm(contents, l, c)); 126 | else for (Term* t : contents) terms.push(t); 127 | } 128 | } 129 | 130 | void parsePrimary(vector& terms, TokenCache::View& view, 131 | u32 indent) { 132 | buffer b; 133 | const Token t = view.peek(); 134 | if (t.type == TOKEN_NUMBER) { 135 | view.read(); 136 | fprint(b, t.value); 137 | for (u32 i = 0; i < t.value.size(); ++ i) { 138 | if (t.value[i] == '.') { 139 | double v; 140 | fread(b, v); 141 | terms.push(new RationalTerm(v, t.line, t.column)); 142 | return; 143 | } 144 | } 145 | i64 v; 146 | fread(b, v); 147 | terms.push(new IntegerTerm(v, t.line, t.column)); 148 | } 149 | else if (t.type == TOKEN_STRING) { 150 | view.read(); 151 | terms.push(new StringTerm(t.value, t.line, t.column)); 152 | } 153 | else if (t.type == TOKEN_CHAR) { 154 | view.read(); 155 | terms.push(new CharTerm(t.value[0], t.line, t.column)); 156 | } 157 | else if (t.type == TOKEN_BOOL) { 158 | view.read(); 159 | terms.push(new BoolTerm(t.value == "true", t.line, t.column)); 160 | } 161 | else if (t.type == TOKEN_IDENT) { 162 | view.read(); 163 | terms.push(new VariableTerm(t.value, t.line, t.column)); 164 | } 165 | else if (t.type == TOKEN_LPAREN) { 166 | view.read(); 167 | vector contents; 168 | parseEnclosed(contents, view, TOKEN_RPAREN, indent); 169 | if (contents.size() == 0) 170 | terms.push(new VoidTerm(t.line, t.column)); 171 | else 172 | terms.push(new BlockTerm(contents, t.line, t.column)); 173 | } 174 | else if (t.type == TOKEN_LBRACE) { 175 | view.read(); 176 | vector contents; 177 | parseEnclosed(contents, view, TOKEN_RBRACE, indent); 178 | Term* vals = new BlockTerm({ 179 | new VariableTerm("record", t.line, t.column), 180 | new BlockTerm(contents, t.line, t.column) 181 | }, t.line, t.column); 182 | terms.push(vals); 183 | } 184 | else if (t.type == TOKEN_LBRACK) { 185 | view.read(); 186 | vector contents; 187 | parseEnclosed(contents, view, TOKEN_RBRACK, indent); 188 | Term* vals = new BlockTerm({ 189 | new VariableTerm("array", t.line, t.column), 190 | new BlockTerm(contents, t.line, t.column) 191 | }, t.line, t.column); 192 | terms.push(vals); 193 | } 194 | else if (t.type == TOKEN_QUOTE) { 195 | view.read(); 196 | if (view.peek().type == TOKEN_LAMBDA 197 | || view.peek().type == TOKEN_ASSIGN) { 198 | err(view, "Cannot quote operator '", view.peek().value, "'."); 199 | return; 200 | } 201 | vector temp; 202 | parsePrimary(temp, view, indent); 203 | if (temp.size() == 0) { 204 | err(view, "Quote prefix ':' requires operand, none provided."); 205 | return; 206 | } 207 | terms.push(new BlockTerm({ 208 | new VariableTerm("quote", t.line, t.column), 209 | temp[0] 210 | }, t.line, t.column)); 211 | } 212 | else if (t.type == TOKEN_MINUS) { 213 | view.read(); 214 | vector temp; 215 | parsePrimary(temp, view, indent); 216 | terms.push(new BlockTerm({ 217 | new IntegerTerm(0, t.line, t.column), 218 | new VariableTerm("-", t.line, t.column), 219 | temp[0] 220 | }, t.line, t.column)); 221 | } 222 | else if (t.type == TOKEN_PLUS) { 223 | view.read(); 224 | vector temp; 225 | parsePrimary(temp, view, indent); 226 | terms.push(new BlockTerm({ 227 | new IntegerTerm(0, t.line, t.column), 228 | new VariableTerm("+", t.line, t.column), 229 | temp[0] 230 | }, t.line, t.column)); 231 | } 232 | else if (t.type == TOKEN_EVAL) { 233 | view.read(); 234 | vector temp; 235 | parsePrimary(temp, view, indent); 236 | terms.push(new BlockTerm({ 237 | new VariableTerm("eval", t.line, t.column), 238 | temp[0] 239 | }, t.line, t.column)); 240 | } 241 | else if (t.type == TOKEN_REF) { 242 | view.read(); 243 | vector temp; 244 | parsePrimary(temp, view, indent); 245 | terms.push(new BlockTerm({ 246 | new VariableTerm("~", t.line, t.column), 247 | temp[0] 248 | }, t.line, t.column)); 249 | } 250 | else if (t.type == TOKEN_DOT) { 251 | view.read(); 252 | if (!terms.size()) { 253 | err(view, "Expected term to the left of dot."); 254 | return; 255 | } 256 | Term* left = terms.back(); 257 | terms.pop(); 258 | vector temp; 259 | parsePrimary(temp, view, indent); 260 | if (!temp.size()) { 261 | err(view, "Expected term to the right of dot."); 262 | return; 263 | } 264 | terms.push(new BlockTerm({ 265 | left, 266 | temp.size() == 1 ? temp[0] 267 | : new BlockTerm(temp, temp[0]->line(), temp[0]->column()) 268 | }, t.line, t.column)); 269 | } 270 | else if (t.type == TOKEN_LAMBDA) { 271 | if (terms.size() == 0) { 272 | err(view, "No argument provided in function definition."); 273 | return; 274 | } 275 | view.read(); 276 | Term* arg = new BlockTerm(terms, terms[0]->line(), terms[0]->column()); 277 | terms.clear(); 278 | vector temp; 279 | if (view.peek().type == TOKEN_NEWLINE 280 | || view.peek().type == TOKEN_NONE) { 281 | view.read(); 282 | u32 c = view.peek().column; 283 | parseIndented(temp, view, c, indent); 284 | } 285 | else parseLine(temp, view, indent, false); 286 | terms.push(new BlockTerm({ 287 | new VariableTerm( 288 | "lambda", 289 | t.line, t.column 290 | ), 291 | arg, 292 | temp.size() == 1 ? temp[0] 293 | : new BlockTerm(temp, temp[0]->line(), temp[0]->column()) 294 | }, t.line, t.column)); 295 | } 296 | else if (t.type == TOKEN_ASSIGN) { 297 | view.read(); 298 | if (terms.size() == 0) { 299 | err(view, "No left term provided to assignment operator."); 300 | return; 301 | } 302 | Term* dst = terms.size() == 1 ? terms[0] 303 | : new BlockTerm(terms, terms[0]->line(), terms[0]->column()); 304 | terms.clear(); 305 | vector temp; 306 | if (view.peek().type == TOKEN_NEWLINE 307 | || view.peek().type == TOKEN_NONE) { 308 | view.read(); 309 | u32 c = view.peek().column; 310 | parseIndented(temp, view, c, indent); 311 | } 312 | else parseChunk(temp, view, indent, false); 313 | terms.push(new BlockTerm({ 314 | new VariableTerm("assign", t.line, t.column), 315 | dst, 316 | temp.size() == 1 ? temp[0] 317 | : new BlockTerm(temp, temp[0]->line(), temp[0]->column()) 318 | }, t.line, t.column)); 319 | } 320 | else if (t.type == TOKEN_COLON) { 321 | view.read(); 322 | vector temp; 323 | if (view.peek().type == TOKEN_NEWLINE 324 | || view.peek().type == TOKEN_NONE) { 325 | view.read(); 326 | u32 c = view.peek().column; 327 | parseIndented(temp, view, c, indent); 328 | } 329 | else parseChunk(temp, view, indent, false); 330 | terms.push(new BlockTerm(temp, t.line, t.column)); 331 | } 332 | else if (t.type == TOKEN_NEWLINE) { 333 | view.read(); 334 | if (repl_mode) { 335 | print(". "); 336 | view.cache().expand(_stdin); 337 | } 338 | } 339 | else { 340 | view.read(); 341 | err(view, "Unexpected token '", t.value, "'."); 342 | } 343 | } 344 | 345 | Term* parse(TokenCache::View& view, bool repl) { 346 | repl_mode = repl; 347 | vector terms; 348 | parseLine(terms, view, 1); 349 | 350 | if (countErrors() > 0) return nullptr; 351 | return terms.size() == 1 ? terms[0] : 352 | new BlockTerm(terms, terms[0]->line(), terms[0]->column()); 353 | } 354 | 355 | ProgramTerm* parseFull(TokenCache::View& view, bool repl) { 356 | repl_mode = repl; 357 | ProgramTerm* p = new ProgramTerm({}, view.peek().line, 358 | view.peek().column); 359 | while (view.peek()) { 360 | vector terms; 361 | parseLine(terms, view, 1); 362 | if (terms.size()) { 363 | p->add(terms.size() == 1 ? terms[0] : 364 | new BlockTerm(terms, terms[0]->line(), terms[0]->column())); 365 | } 366 | } 367 | 368 | if (countErrors() > 0) return nullptr; 369 | 370 | return p; 371 | } 372 | } -------------------------------------------------------------------------------- /src/source.cpp: -------------------------------------------------------------------------------- 1 | #include "source.h" 2 | 3 | namespace basil { 4 | void Source::add(uchar c) { 5 | if (c == '\t') lines.back() += " "; // expand tabs to 4 spaces 6 | else lines.back() += c; 7 | if (c == '\n') lines.push(""); 8 | } 9 | 10 | void Source::checkNewline() { 11 | if (lines.size() == 0) { 12 | lines.push("\n"); 13 | return; 14 | } 15 | i64 i = lines.size() - 1; 16 | while (i > 0 && lines[i].size() == 0) -- i; 17 | if (lines[i].size() == 0) { 18 | lines[i] += '\n'; 19 | } 20 | else if (lines[i][lines[i].size() - 1] != '\n') add('\n'); 21 | } 22 | 23 | Source::Source() { 24 | lines.push(""); 25 | } 26 | 27 | Source::Source(const char* path) { 28 | lines.push(""); 29 | file f(path, "r"); 30 | load(f); 31 | } 32 | 33 | Source::Source(stream& f) { 34 | lines.push(""); 35 | load(f); 36 | } 37 | 38 | Source::View::View(const Source* src_in): src(src_in), _line(0), _column(0) { 39 | // 40 | } 41 | 42 | Source::View::View(const Source* src_in, u32 line, u32 column): 43 | src(src_in), _line(line), _column(column) { 44 | // 45 | } 46 | 47 | void Source::View::rewind() { 48 | if (_column == 0 && _line > 0) { 49 | -- _line, _column = src->lines[_line].size(); 50 | } 51 | else if (_column > 0) -- _column; 52 | } 53 | 54 | uchar Source::View::read() { 55 | if (_line >= src->lines.size()) return '\0'; 56 | if (_line == src->lines.size() - 1 57 | && _column >= src->line(_line).size()) return '\0'; 58 | uchar c = src->lines[_line][_column]; 59 | ++ _column; 60 | if (_column >= src->lines[_line].size() && _line < src->size() - 1) { 61 | _column = 0, ++ _line; 62 | } 63 | return c; 64 | } 65 | 66 | uchar Source::View::peek() const { 67 | if (_line >= src->lines.size()) return '\0'; 68 | if (_line == src->lines.size() - 1 69 | && _column >= src->line(_line).size()) return '\0'; 70 | return src->lines[_line][_column]; 71 | } 72 | 73 | u32 Source::View::line() const { 74 | return _line + 1; 75 | } 76 | 77 | u32 Source::View::column() const { 78 | return _column + 1; 79 | } 80 | 81 | const Source* Source::View::source() const { 82 | return src; 83 | } 84 | 85 | void Source::load(stream& f) { 86 | uchar c; 87 | while (f.peek()) read(f, c), add(c); 88 | checkNewline(); 89 | } 90 | 91 | void Source::add(const ustring& line) { 92 | for (u32 i = 0; i < line.size(); ++ i) add(line[i]); 93 | checkNewline(); 94 | } 95 | 96 | ustring& Source::line(u32 line) { 97 | return lines[line]; 98 | } 99 | 100 | const ustring& Source::line(u32 line) const { 101 | return lines[line]; 102 | } 103 | 104 | u32 Source::size() const { 105 | return lines.size(); 106 | } 107 | 108 | Source::View Source::view() const { 109 | return Source::View(this); 110 | } 111 | 112 | Source::View Source::expand(stream& io) { 113 | Source::View view(this, lines.size() - 1, 0); 114 | while (io.peek() && io.peek() != '\n') { 115 | uchar c; 116 | read(io, c); 117 | add(c); 118 | } 119 | if (io.peek() == '\n') add(io.read()); 120 | return view; 121 | } 122 | } 123 | 124 | void print(stream& io, const basil::Source& src) { 125 | for (u32 i = 0; i < src.size(); ++ i) print(io, src.line(i)); 126 | } 127 | 128 | void print(const basil::Source& src) { 129 | print(_stdout, src); 130 | } 131 | 132 | void read(stream& io, basil::Source& src) { 133 | src.load(io); 134 | } 135 | 136 | void read(basil::Source& src) { 137 | read(_stdin, src); 138 | } -------------------------------------------------------------------------------- /src/str.cpp: -------------------------------------------------------------------------------- 1 | #include "str.h" 2 | #include "io.h" 3 | 4 | void string::free() { 5 | if (_capacity > 16) delete[] data; 6 | } 7 | 8 | void string::init(u32 size) { 9 | if (size > 16) { 10 | _size = 0, _capacity = size; 11 | data = new u8[_capacity]; 12 | } 13 | else data = buf, _size = 0, _capacity = 16; 14 | *data = '\0'; 15 | } 16 | 17 | void string::copy(const u8* s) { 18 | u8* dptr = data; 19 | while (*s) *(dptr ++) = *(s ++), ++ _size; 20 | *dptr = '\0'; 21 | } 22 | 23 | void string::grow() { 24 | u8* old = data; 25 | init(_capacity * 2); 26 | copy(old); 27 | if (_capacity / 2 > 16) delete[] old; 28 | } 29 | 30 | i32 string::cmp(const u8* s) const { 31 | u8* dptr = data; 32 | while (*s && *s == *dptr) ++ s, ++ dptr; 33 | return i32(*dptr) - i32(*s); 34 | } 35 | 36 | i32 string::cmp(const char* s) const { 37 | u8* dptr = data; 38 | while (*s && *s == *dptr) ++ s, ++ dptr; 39 | return i32(*dptr) - i32(*s); 40 | } 41 | 42 | string::string() { 43 | init(16); 44 | } 45 | 46 | string::~string() { 47 | free(); 48 | } 49 | 50 | string::string(const string& other) { 51 | init(other._capacity); 52 | copy(other.data); 53 | } 54 | 55 | string::string(const char* s): string() { 56 | operator+=(s); 57 | } 58 | 59 | string& string::operator=(const string& other) { 60 | if (this != &other) { 61 | free(); 62 | init(other._capacity); 63 | copy(other.data); 64 | } 65 | return *this; 66 | } 67 | 68 | string& string::operator+=(u8 c) { 69 | if (_size + 1 >= _capacity) grow(); 70 | data[_size ++] = c; 71 | data[_size] = '\0'; 72 | return *this; 73 | } 74 | 75 | string& string::operator+=(char c) { 76 | while (_size + 1 >= _capacity) grow(); 77 | data[_size ++] = c; 78 | data[_size] = '\0'; 79 | return *this; 80 | } 81 | 82 | string& string::operator+=(const u8* s) { 83 | u32 size = 0; 84 | const u8* sptr = s; 85 | while (*sptr) ++ size, ++ sptr; 86 | ++ size; // account for null char 87 | while (_size + size >= _capacity) grow(); 88 | u8* dptr = data + _size; 89 | sptr = s; 90 | while (*sptr) *(dptr ++) = *(sptr ++); 91 | *dptr = '\0'; 92 | _size += size; 93 | return *this; 94 | } 95 | 96 | string& string::operator+=(const char* s) { 97 | u32 size = 0; 98 | const char* sptr = s; 99 | while (*sptr) ++ size, ++ sptr; 100 | ++ size; // account for null char 101 | while (_size + size >= _capacity) grow(); 102 | u8* dptr = data + _size; 103 | sptr = s; 104 | while (*sptr) *(dptr ++) = *(sptr ++); 105 | *dptr = '\0'; 106 | _size += size; 107 | return *this; 108 | } 109 | 110 | string& string::operator+=(const string& s) { 111 | return operator+=(s.data); 112 | } 113 | 114 | u32 string::size() const { 115 | return _size; 116 | } 117 | 118 | u32 string::capacity() const { 119 | return _capacity; 120 | } 121 | 122 | u8 string::operator[](u32 i) const { 123 | return data[i]; 124 | } 125 | 126 | u8& string::operator[](u32 i) { 127 | return data[i]; 128 | } 129 | 130 | const u8* string::raw() const { 131 | return data; 132 | } 133 | 134 | bool string::operator==(const u8* s) const { 135 | return cmp(s) == 0; 136 | } 137 | 138 | bool string::operator==(const char* s) const { 139 | return cmp(s) == 0; 140 | } 141 | 142 | bool string::operator==(const string& s) const { 143 | return cmp(s.data) == 0; 144 | } 145 | 146 | bool string::operator<(const u8* s) const { 147 | return cmp(s) < 0; 148 | } 149 | 150 | bool string::operator<(const char* s) const { 151 | return cmp(s) < 0; 152 | } 153 | 154 | bool string::operator<(const string& s) const { 155 | return cmp(s.data) < 0; 156 | } 157 | 158 | bool string::operator>(const u8* s) const { 159 | return cmp(s) > 0; 160 | } 161 | 162 | bool string::operator>(const char* s) const { 163 | return cmp(s) > 0; 164 | } 165 | 166 | bool string::operator>(const string& s) const { 167 | return cmp(s.data) > 0; 168 | } 169 | 170 | bool string::operator!=(const u8* s) const { 171 | return cmp(s) != 0; 172 | } 173 | 174 | bool string::operator!=(const char* s) const { 175 | return cmp(s) != 0; 176 | } 177 | 178 | bool string::operator!=(const string& s) const { 179 | return cmp(s.data) != 0; 180 | } 181 | 182 | bool string::operator<=(const u8* s) const { 183 | return cmp(s) <= 0; 184 | } 185 | 186 | bool string::operator<=(const char* s) const { 187 | return cmp(s) <= 0; 188 | } 189 | 190 | bool string::operator<=(const string& s) const { 191 | return cmp(s.data) <= 0; 192 | } 193 | 194 | bool string::operator>=(const u8* s) const { 195 | return cmp(s) >= 0; 196 | } 197 | 198 | bool string::operator>=(const char* s) const { 199 | return cmp(s) >= 0; 200 | } 201 | 202 | bool string::operator>=(const string& s) const { 203 | return cmp(s.data) >= 0; 204 | } 205 | 206 | string operator+(string s, char c) { 207 | s += c; 208 | return s; 209 | } 210 | 211 | string operator+(string s, const char* cs) { 212 | s += cs; 213 | return s; 214 | } 215 | 216 | string operator+(string s, const string& cs) { 217 | s += cs; 218 | return s; 219 | } 220 | 221 | void print(stream& io, const string& s) { 222 | print(io, s.raw()); 223 | } 224 | 225 | void print(const string& s) { 226 | print(_stdout, s); 227 | } 228 | 229 | void read(stream& io, string& s) { 230 | while (isspace(io.peek())) io.read(); // consume leading spaces 231 | while (io.peek() && !isspace(io.peek())) s += io.read(); 232 | } 233 | 234 | void read(string& s) { 235 | read(_stdin, s); 236 | } -------------------------------------------------------------------------------- /src/type.cpp: -------------------------------------------------------------------------------- 1 | #include "type.h" 2 | #include "io.h" 3 | 4 | namespace basil { 5 | map typemap; 6 | 7 | // TypeClass 8 | 9 | TypeClass::TypeClass(): _parent(nullptr) { 10 | // 11 | } 12 | 13 | TypeClass::TypeClass(const TypeClass& parent): _parent(&parent) { 14 | // 15 | } 16 | 17 | const TypeClass* TypeClass::parent() const { 18 | return _parent; 19 | } 20 | 21 | u32 Type::nextid = 0; 22 | 23 | const TypeClass Type::CLASS; 24 | 25 | Type::Type(const ustring& key, u32 size, const TypeClass* tc): 26 | _typeclass(tc), _key(key), _size(size) { 27 | _id = nextid ++; 28 | } 29 | 30 | u32 Type::size() const { 31 | return _size; 32 | } 33 | 34 | u32 Type::id() const { 35 | return _id; 36 | } 37 | 38 | bool Type::implicitly(const Type* other) const { 39 | return other == this || other == ANY; 40 | } 41 | 42 | bool Type::explicitly(const Type* other) const { 43 | return other == this || other == ANY; 44 | } 45 | 46 | bool Type::conflictsWith(const Type* other) const { 47 | return this == other; 48 | } 49 | 50 | const ustring& Type::key() const { 51 | return _key; 52 | } 53 | 54 | void Type::format(stream& io) const { 55 | print(io, _key); 56 | } 57 | 58 | bool Type::wildcard() const { 59 | return this == ANY; 60 | } 61 | 62 | // NumericType 63 | 64 | const TypeClass NumericType::CLASS(Type::CLASS); 65 | 66 | NumericType::NumericType(u32 size, bool floating, 67 | const TypeClass* tc): 68 | Type("", size, tc), _floating(floating) { 69 | if (floating) _key += "f"; 70 | else _key += "i"; 71 | buffer b; 72 | fprint(b, size * 8); 73 | while (b) _key += b.read(); 74 | } 75 | 76 | bool NumericType::floating() const { 77 | return _floating; 78 | } 79 | 80 | bool NumericType::implicitly(const Type* other) const { 81 | if (Type::implicitly(other)) return true; 82 | if (!other->is()) return false; 83 | const NumericType* nt = other->as(); 84 | 85 | if (_floating) { 86 | return nt->_floating && nt->_size >= _size; 87 | } 88 | else { 89 | return !nt->_floating; 90 | } 91 | } 92 | 93 | bool NumericType::explicitly(const Type* other) const { 94 | if (Type::explicitly(other)) return true; 95 | return other->is() && 96 | (_floating ? other->as()->_floating : true); 97 | } 98 | 99 | // TupleType 100 | 101 | const TypeClass TupleType::CLASS(Type::CLASS); 102 | 103 | TupleType::TupleType(const vector& members, 104 | const TypeClass* tc): 105 | Type("", 0, tc), _members(members) { 106 | _key += "[T"; 107 | for (const Type* t : members) { 108 | _offsets.push(_size); 109 | _size += t->size(); 110 | _key += " ", _key += t->key(); 111 | } 112 | _key += "]"; 113 | } 114 | 115 | const vector& TupleType::members() const { 116 | return _members; 117 | } 118 | 119 | const Type* TupleType::member(u32 i) const { 120 | return _members[i]; 121 | } 122 | 123 | u32 TupleType::offset(u32 i) const { 124 | return _offsets[i]; 125 | } 126 | 127 | u32 TupleType::count() const { 128 | return _members.size(); 129 | } 130 | 131 | bool TupleType::implicitly(const Type* other) const { 132 | if (Type::implicitly(other)) return true; 133 | if (!other->is()) return false; 134 | 135 | const TupleType* tt = other->as(); 136 | if (tt->_members.size() != _members.size()) return false; 137 | 138 | for (u32 i = 0; i < _members.size(); ++ i) { 139 | if (!_members[i]->implicitly(tt->_members[i])) return false; 140 | } 141 | 142 | return true; 143 | } 144 | 145 | bool TupleType::explicitly(const Type* other) const { 146 | if (Type::explicitly(other)) return true; 147 | if (other == TYPE) { 148 | bool anyNonType = false; 149 | for (const Type* t : _members) if (t != TYPE) anyNonType = true; 150 | if (!anyNonType) return true; 151 | } 152 | if (!other->is()) return false; 153 | 154 | const TupleType* tt = other->as(); 155 | if (tt->_members.size() != _members.size()) return false; 156 | 157 | for (u32 i = 0; i < _members.size(); ++ i) { 158 | if (!_members[i]->explicitly(tt->_members[i])) return false; 159 | } 160 | 161 | return true; 162 | } 163 | 164 | void TupleType::format(stream& io) const { 165 | print(io, "("); 166 | bool first = true; 167 | for (const Type* t : _members) { 168 | if (!first) print(io, ", "); 169 | first = false; 170 | t->format(io); 171 | } 172 | print(io, ")"); 173 | } 174 | 175 | // ArrayType 176 | 177 | const TypeClass ArrayType::CLASS(Type::CLASS); 178 | 179 | ArrayType::ArrayType(const Type* element, const TypeClass* tc): 180 | Type("", 0, tc), _element(element), _count(-1) { 181 | _size = 8; // ref 182 | _key = _element->key() + "[]"; 183 | } 184 | 185 | ArrayType::ArrayType(const Type* element, u32 size, 186 | const TypeClass* tc): 187 | Type("", 0, tc), _element(element), _count(size) { 188 | _size = _element->size() * _count; 189 | buffer b; 190 | fprint(b, _element->key(), "[", _count, "]"); 191 | fread(b, _key); 192 | } 193 | 194 | const Type* unionOf(const vector& elements) { 195 | set types; 196 | for (const Type* t : elements) types.insert(t); 197 | if (types.size() == 1) return *types.begin(); 198 | return find(types); 199 | } 200 | 201 | ArrayType::ArrayType(const vector& elements, 202 | const TypeClass* tc): 203 | ArrayType(unionOf(elements), elements.size(), tc) { 204 | // 205 | } 206 | 207 | const Type* ArrayType::element() const { 208 | return _element; 209 | } 210 | 211 | i64 ArrayType::count() const { 212 | return _count; 213 | } 214 | 215 | bool ArrayType::sized() const { 216 | return _count >= 0; 217 | } 218 | 219 | bool ArrayType::implicitly(const Type* other) const { 220 | if (Type::implicitly(other)) return true; 221 | if (other->is()) { 222 | const TupleType* tt = other->as(); 223 | for (const Type* t : tt->members()) { 224 | if (!_element->implicitly(t)) return false; 225 | } 226 | return _count == tt->count(); 227 | } 228 | if (!other->is()) return false; 229 | 230 | const ArrayType* at = other->as(); 231 | if (!sized() && at->sized()) return false; 232 | if (sized() && at->sized() && _count != at->_count) return false; 233 | return _element->implicitly(at->element()); 234 | } 235 | 236 | bool ArrayType::explicitly(const Type* other) const { 237 | if (Type::implicitly(other)) return true; 238 | if (other->is()) { 239 | const TupleType* tt = other->as(); 240 | for (const Type* t : tt->members()) { 241 | if (!_element->explicitly(t)) return false; 242 | } 243 | return _count == tt->count(); 244 | } 245 | if (!other->is()) return false; 246 | 247 | const ArrayType* at = other->as(); 248 | if (!sized() && at->sized()) return false; 249 | if (sized() && at->sized() && _count != at->_count) return false; 250 | return _element->explicitly(at->element()); 251 | } 252 | 253 | void ArrayType::format(stream& io) const { 254 | if (_count < 0) print(io, _element, "[]"); 255 | else print(io, _element, "[", _count, "]"); 256 | } 257 | 258 | bool ArrayType::wildcard() const { 259 | return _element == ANY; 260 | } 261 | 262 | // UnionType 263 | 264 | const TypeClass UnionType::CLASS(Type::CLASS); 265 | 266 | UnionType::UnionType(const set& members, 267 | const TypeClass* tc): 268 | Type("", 0, tc), _members(members) { 269 | _key += "[U"; 270 | for (const Type* t : members) { 271 | if (t->size() > _size) _size = t->size(); 272 | _key += " ", _key += t->key(); 273 | } 274 | _key += "]"; 275 | } 276 | 277 | const set& UnionType::members() const { 278 | return _members; 279 | } 280 | 281 | bool UnionType::has(const Type* t) const { 282 | return _members.find(t) != _members.end(); 283 | } 284 | 285 | bool UnionType::implicitly(const Type* other) const { 286 | if (Type::implicitly(other)) return true; 287 | return other == this; 288 | } 289 | 290 | bool UnionType::explicitly(const Type* other) const { 291 | if (Type::explicitly(other)) return true; 292 | if (other == TYPE) { 293 | bool anyNonType = false; 294 | for (const Type* t : _members) if (t != TYPE) anyNonType = true; 295 | if (!anyNonType) return true; 296 | } 297 | return has(other); 298 | } 299 | 300 | void UnionType::format(stream& io) const { 301 | print(io, "("); 302 | bool first = true; 303 | for (const Type* t : _members) { 304 | if (!first) print(io, "|"); 305 | first = false; 306 | t->format(io); 307 | } 308 | print(io, ")"); 309 | } 310 | 311 | // IntersectionType 312 | 313 | const TypeClass IntersectionType::CLASS(Type::CLASS); 314 | 315 | IntersectionType::IntersectionType(const set& members, 316 | const TypeClass* tc): 317 | Type("", 0, tc), _members(members) { 318 | _key += "[I"; 319 | bool overload = true; 320 | const Type* prev = nullptr; 321 | for (const Type* t : members) { 322 | if (prev && 323 | (!prev->is() 324 | || !t->is() 325 | || t->as()->conflictsWith(prev))) { 326 | overload = false; 327 | } 328 | prev = t; 329 | _size += t->size(); 330 | _key += " ", _key += t->key(); 331 | } 332 | if (overload) _size = prev->size(); 333 | _key += "]"; 334 | } 335 | 336 | const set& IntersectionType::members() const { 337 | return _members; 338 | } 339 | 340 | bool IntersectionType::has(const Type* t) const { 341 | return _members.find(t) != _members.end(); 342 | } 343 | 344 | bool IntersectionType::implicitly(const Type* other) const { 345 | if (Type::implicitly(other)) return true; 346 | return this == other; 347 | } 348 | 349 | bool IntersectionType::explicitly(const Type* other) const { 350 | if (Type::explicitly(other)) return true; 351 | if (other == TYPE) { 352 | bool anyNonType = false; 353 | for (const Type* t : _members) if (t != TYPE) anyNonType = true; 354 | if (!anyNonType) return true; 355 | } 356 | return this == other; 357 | } 358 | 359 | bool IntersectionType::conflictsWith(const Type* other) const { 360 | for (const Type* t : _members) { 361 | if (t->conflictsWith(other)) return true; 362 | } 363 | return Type::conflictsWith(other); 364 | } 365 | 366 | void IntersectionType::format(stream& io) const { 367 | print(io, "("); 368 | bool first = true; 369 | for (const Type* t : _members) { 370 | if (!first) print(io, " & "); 371 | first = false; 372 | t->format(io); 373 | } 374 | print(io, ")"); 375 | } 376 | 377 | // ListType 378 | 379 | const TypeClass ListType::CLASS(Type::CLASS); 380 | 381 | ListType::ListType(const Type* element, const TypeClass* tc): 382 | Type("", 8, tc), _element(element) { 383 | _key += "[L "; 384 | _key += element->key(); 385 | _key += "]"; 386 | } 387 | 388 | const Type* ListType::element() const { 389 | return _element; 390 | } 391 | 392 | bool ListType::implicitly(const Type* other) const { 393 | if (Type::implicitly(other)) return true; 394 | return other == this; 395 | } 396 | 397 | bool ListType::explicitly(const Type* other) const { 398 | return implicitly(other); // todo: consider reification? 399 | } 400 | 401 | void ListType::format(stream& io) const { 402 | print(io, "[", _element, "]"); 403 | } 404 | 405 | // ReferenceType 406 | 407 | const TypeClass ReferenceType::CLASS(Type::CLASS); 408 | 409 | ReferenceType::ReferenceType(const Type* element, const TypeClass* tc): 410 | Type("", 8, tc), _element(element) { 411 | _key += "[R "; 412 | _key += element->key(); 413 | _key += "]"; 414 | } 415 | 416 | const Type* ReferenceType::element() const { 417 | return _element; 418 | } 419 | 420 | bool ReferenceType::implicitly(const Type* other) const { 421 | if (Type::implicitly(other)) return true; 422 | return other == _element || other == this; 423 | } 424 | 425 | bool ReferenceType::explicitly(const Type* other) const { 426 | if (other == TYPE) return _element == TYPE; 427 | return implicitly(other); // todo: consider reification? 428 | } 429 | 430 | void ReferenceType::format(stream& io) const { 431 | print(io, "~", _element); 432 | } 433 | 434 | // EmptyType 435 | 436 | const TypeClass EmptyType::CLASS(Type::CLASS); 437 | 438 | EmptyType::EmptyType(const TypeClass* tc): 439 | Type("[empty]", 8, tc) { 440 | // 441 | } 442 | 443 | bool EmptyType::implicitly(const Type* other) const { 444 | if (Type::implicitly(other)) return true; 445 | return other == this || other->is(); 446 | } 447 | 448 | bool EmptyType::explicitly(const Type* other) const { 449 | return implicitly(other); 450 | } 451 | 452 | void EmptyType::format(stream& io) const { 453 | print(io, "[]"); 454 | } 455 | 456 | // FunctionType 457 | 458 | const TypeClass FunctionType::CLASS(Type::CLASS); 459 | 460 | FunctionType::FunctionType(const Type* arg, const Type* ret, 461 | const vector& cons, 462 | const TypeClass* tc): 463 | FunctionType(arg, ret, false, cons, tc) { 464 | // 465 | } 466 | 467 | FunctionType::FunctionType(const Type* arg, const Type* ret, 468 | bool quoting, const vector& cons, 469 | const TypeClass* tc): 470 | Type("", 0, tc), _arg(arg), _ret(ret), 471 | _cons(cons), _quoting(quoting) { 472 | if (!_cons.size()) _cons.push(Constraint()); 473 | _key += (quoting ? "[QF " : "[F "); 474 | _key += arg->key(); 475 | _key += " "; 476 | _key += ret->key(); 477 | _key += " { "; 478 | for (auto& con : cons) _key += con.key() + " "; 479 | _key += "} "; 480 | _key += "]"; 481 | _size = 8; 482 | } 483 | 484 | const Type* FunctionType::FunctionType::arg() const { 485 | return _arg; 486 | } 487 | 488 | const Type* FunctionType::ret() const { 489 | return _ret; 490 | } 491 | 492 | bool FunctionType::quoting() const { 493 | return _quoting; 494 | } 495 | 496 | const vector& FunctionType::constraints() const { 497 | return _cons; 498 | } 499 | 500 | bool FunctionType::total() const { 501 | vector vals; 502 | for (const Constraint& c : _cons) { 503 | if (c.type() == OF_TYPE || c.type() == UNKNOWN) return true; 504 | else vals.push(c.value()); 505 | } 506 | // todo: exhaustive cases 507 | return false; 508 | } 509 | 510 | bool FunctionType::conflictsWith(const Type* other) const { 511 | if (!other->is()) return false; 512 | const FunctionType* ft = other->as(); 513 | if (ft->_arg != _arg && ft->_ret != _ret) return false; 514 | for (auto& a : _cons) for (auto& b : ft->_cons) { 515 | if (a.conflictsWith(b)) return true; 516 | } 517 | return false; 518 | } 519 | 520 | bool FunctionType::implicitly(const Type* other) const { 521 | if (Type::implicitly(other)) return true; 522 | if (!other->is()) return false; 523 | 524 | const FunctionType* ft = other->as(); 525 | 526 | if (ft->_quoting != _quoting) return false; 527 | 528 | if (ft->_ret != _ret || ft->_arg != _arg) return false; 529 | return ft->constraints().size() == 1 530 | && ft->constraints()[0].type() == UNKNOWN; 531 | } 532 | 533 | bool FunctionType::explicitly(const Type* other) const { 534 | if (Type::explicitly(other)) return true; 535 | if (other == TYPE) { 536 | return _arg == TYPE && _ret == TYPE && _cons.size() == 1 537 | && _cons[0].type() == EQUALS_VALUE 538 | && _cons[0].value().isType(); 539 | } 540 | return implicitly(other); 541 | } 542 | 543 | void FunctionType::format(stream& io) const { 544 | print(io, "("); 545 | if (_cons.size() == 1 && _cons[0].type() == EQUALS_VALUE) { 546 | print(io, _cons[0].value().toString()); 547 | } 548 | else _arg->format(io); 549 | print(io, _quoting ? " => " : " -> "); 550 | _ret->format(io); 551 | print(io, ")"); 552 | } 553 | 554 | Constraint FunctionType::matches(Meta fr) const { 555 | return maxMatch(_cons, fr); 556 | } 557 | 558 | Constraint maxMatch(const vector& cons, Meta fr) { 559 | if (cons.size() == 0) return Constraint(); 560 | Constraint ret = Constraint::NONE; 561 | for (const Constraint& c : cons) if (c.matches(fr)) { 562 | if (c.precedes(ret) || ret.type() == UNKNOWN 563 | || ret.type() == NULL_CONSTRAINT) ret = c; 564 | } 565 | return ret; 566 | } 567 | 568 | // Constraint 569 | 570 | Constraint::Constraint(ConstraintType type): 571 | _type(type) { 572 | _key = ""; 573 | } 574 | 575 | Constraint::Constraint(Meta value): 576 | _type(EQUALS_VALUE), _value(value) { 577 | _key = "(= "; 578 | _key += value.toString(); 579 | _key += ")"; 580 | } 581 | 582 | Constraint::Constraint(const Type* type): 583 | _type(OF_TYPE), _value(Meta(TYPE, type)) { 584 | _key = "(: "; 585 | _key += type->key(); 586 | _key += ")"; 587 | } 588 | 589 | Constraint::Constraint(): 590 | _type(UNKNOWN) { 591 | _key = "(?)"; 592 | } 593 | 594 | const Constraint Constraint::NONE(NULL_CONSTRAINT); 595 | 596 | ConstraintType Constraint::type() const { 597 | return _type; 598 | } 599 | 600 | Meta Constraint::value() const { 601 | return _value; 602 | } 603 | 604 | Constraint::operator bool() const { 605 | return _type != NULL_CONSTRAINT; 606 | } 607 | 608 | bool Constraint::conflictsWith(const Constraint& other) const { 609 | if (_type == UNKNOWN) return true; 610 | if (_type == other._type && _type == EQUALS_VALUE) { 611 | return _value == other._value; 612 | } 613 | if (_type == OF_TYPE) return other._type == OF_TYPE; 614 | return false; 615 | } 616 | 617 | bool Constraint::precedes(const Constraint& other) const { 618 | return _type < other._type; 619 | } 620 | 621 | const ustring& Constraint::key() const { 622 | return _key; 623 | } 624 | 625 | bool Constraint::matches(Meta value) const { 626 | if (_type == UNKNOWN || _type == OF_TYPE) return true; 627 | if (_type == EQUALS_VALUE) return value == _value; 628 | return false; 629 | } 630 | 631 | const Type* join(const Type* a, const Type* b) { 632 | if (a == b) return a; 633 | else if (a->implicitly(b)) return b; 634 | else if (b->implicitly(a)) return a; 635 | else if (a->explicitly(b)) return b; 636 | else if (b->explicitly(a)) return a; 637 | else return nullptr; 638 | } 639 | 640 | const Type *I8 = find(1, false), 641 | *I16 = find(2, false), 642 | *I32 = find(4, false), 643 | *I64 = find(8, false), 644 | *FLOAT = find(4, true), 645 | *DOUBLE = find(8, true), 646 | *BOOL = find("bool", 1), 647 | *TYPE = find("type", 4), 648 | *SYMBOL = find("symbol", 4), 649 | *ERROR = find("error", 1), 650 | *EMPTY = find(), 651 | *VOID = find("void", 1), 652 | *ANY = find("any", 1), 653 | *UNDEFINED = find("undefined", 1), 654 | *STRING = find("string", 8), 655 | *CHAR = find("char", 4), 656 | *END = find("end", 1); 657 | 658 | bool shouldAlloca(const Type* t) { 659 | return t == STRING 660 | || t->is(); 661 | } 662 | } 663 | 664 | void print(stream& io, const basil::Type* t) { 665 | t->format(io); 666 | } 667 | 668 | void print(const basil::Type* t) { 669 | print(_stdout, t); 670 | } -------------------------------------------------------------------------------- /src/utf8.cpp: -------------------------------------------------------------------------------- 1 | #include "utf8.h" 2 | #include "io.h" 3 | 4 | uchar::uchar(u8 a, u8 b, u8 c, u8 d) { 5 | data[0] = a; 6 | data[1] = b; 7 | data[2] = c; 8 | data[3] = d; 9 | } 10 | 11 | uchar::uchar(const char* str): uchar((u8)'\0') { 12 | data[0] = *str ++; 13 | for (uint8_t i = 1; i < size(); i ++) data[i] = *str ++; 14 | } 15 | 16 | u32 uchar::size() const { 17 | if (!(data[0] >> 7 & 1)) return 1; 18 | else if ((data[0] >> 5 & 7) == 6) return 2; 19 | else if ((data[0] >> 4 & 15) == 14) return 3; 20 | else if ((data[0] >> 3 & 31) == 30) return 4; 21 | else return 0; 22 | } 23 | 24 | u32 uchar::point() const { 25 | switch (size()) { 26 | case 1: 27 | return data[0]; 28 | case 2: 29 | return (data[0] & 31) << 6 | (data[1] & 63); 30 | case 3: 31 | return (data[0] & 15) << 12 32 | | (data[1] & 63) << 6 33 | | (data[2] & 63); 34 | case 4: 35 | return (data[0] & 7) << 18 36 | | (data[1] & 63) << 12 37 | | (data[2] & 63) << 6 38 | | (data[3] & 63); 39 | } 40 | return 0; 41 | } 42 | 43 | u8& uchar::operator[](u32 i) { 44 | return data[i]; 45 | } 46 | 47 | u8 uchar::operator[](u32 i) const { 48 | return data[i]; 49 | } 50 | 51 | bool uchar::operator==(char c) const { 52 | return data[0] < 128 && c == data[0]; 53 | } 54 | 55 | bool uchar::operator!=(char c) const { 56 | return c != data[0]; 57 | } 58 | 59 | bool uchar::operator==(uchar c) const { 60 | for (u32 i = 0; i < 4; i ++) { 61 | if (data[i] != c[i]) return false; 62 | if (!data[i] && !data[c]) return true;; 63 | } 64 | return true; 65 | } 66 | 67 | bool uchar::operator!=(uchar c) const { 68 | return !operator==(c); 69 | } 70 | 71 | bool uchar::operator<(uchar c) const { 72 | for (u32 i = 0; i < 4 && data[i]; i ++) { 73 | if (data[i] != c[i]) return data[i] < c[i]; 74 | } 75 | return false; 76 | } 77 | 78 | bool uchar::operator>(uchar c) const { 79 | return !operator<(c) && !operator==(c); 80 | } 81 | 82 | bool uchar::operator<=(uchar c) const { 83 | return operator<(c) || operator==(c); 84 | } 85 | 86 | bool uchar::operator>=(uchar c) const { 87 | return !operator<(c); 88 | } 89 | 90 | uchar::operator bool() const { 91 | return data[0]; 92 | } 93 | 94 | static const u32 SPACES[] = { 95 | 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x0020, 0x00A0, 96 | 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 97 | 0x200A, 0x200B, 0x2028, 0x2029, 0x202D, 0x202E, 0x202F, 0x205F, 98 | 0x3000, 0xFEFF 99 | }; 100 | static const u32 NUM_SPACES = sizeof(SPACES) / sizeof(u32); 101 | 102 | static const u32 CONTROLS[] = { 103 | 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 104 | 0x007F 105 | }; 106 | static const u32 NUM_CONTROLS = sizeof(CONTROLS) / sizeof(u32); 107 | 108 | static const u32 DIGITS[] = { 109 | 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 110 | 0x0038, 0x0039, // ASCII 111 | 0x0660, 0x0661, 0x0662, 0x0663, 0x0664, 0x0665, 0x0666, 0x0667, 112 | 0x0668, 0x0669, // Arabic-Indic 113 | 0x06F0, 0x06F1, 0x06F2, 0x06F3, 0x06F4, 0x06F5, 0x06F6, 0x06F7, 114 | 0x06F8, 0x06F9, // Extended Arabic-Indic 115 | 0x07C0, 0x07C1, 0x07C2, 0x07C3, 0x07C4, 0x07C5, 0x07C6, 0x07C7, 116 | 0x07C8, 0x07C9, // N'ko 117 | 0x0966, 0x0967, 0x0968, 0x0969, 0x096A, 0x096B, 0x096C, 0x096D, 118 | 0x096E, 0x096F, // Devanagari 119 | 0x09E6, 0x09E7, 0x09E8, 0x09E9, 0x09EA, 0x09EB, 0x09EC, 0x09ED, 120 | 0x09EE, 0x09EF, // Bengali 121 | 0x0A66, 0x0A67, 0x0A68, 0x0A69, 0x0A6A, 0x0A6B, 0x0A6C, 0x0A6D, 122 | 0x0A6E, 0x0A6F, // Gurmukhi 123 | 0x0AE6, 0x0AE7, 0x0AE8, 0x0AE9, 0x0AEA, 0x0AEB, 0x0AEC, 0x0AED, 124 | 0x0AEE, 0x0AEF, // Gujarati 125 | 0x0B66, 0x0B67, 0x0B68, 0x0B69, 0x0B6A, 0x0B6B, 0x0B6C, 0x0B6D, 126 | 0x0B6E, 0x0B6F, // Oriya 127 | 0x0BE6, 0x0BE7, 0x0BE8, 0x0BE9, 0x0BEA, 0x0BEB, 0x0BEC, 0x0BED, 128 | 0x0BEE, 0x0BEF, // Tamil 129 | 0x0C66, 0x0C67, 0x0C68, 0x0C69, 0x0C6A, 0x0C6B, 0x0C6C, 0x0C6D, 130 | 0x0C6E, 0x0C6F, // Telugu 131 | 0x0CE6, 0x0CE7, 0x0CE8, 0x0CE9, 0x0CEA, 0x0CEB, 0x0CEC, 0x0CED, 132 | 0x0CEE, 0x0CEF, // Kannada 133 | 0x0D66, 0x0D67, 0x0D68, 0x0D69, 0x0D6A, 0x0D6B, 0x0D6C, 0x0D6D, 134 | 0x0D6E, 0x0D6F, // Malayalam 135 | 0x0DE6, 0x0DE7, 0x0DE8, 0x0DE9, 0x0DEA, 0x0DEB, 0x0DEC, 0x0DED, 136 | 0x0DEE, 0x0DEF, // Sinhala 137 | 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 138 | 0x0E58, 0x0E59, // Thai 139 | 0x0ED0, 0x0ED1, 0x0ED2, 0x0ED3, 0x0ED4, 0x0ED5, 0x0ED6, 0x0ED7, 140 | 0x0ED8, 0x0ED9, // Lao 141 | 0x0F20, 0x0F21, 0x0F22, 0x0F23, 0x0F24, 0x0F25, 0x0F26, 0x0F27, 142 | 0x0F28, 0x0F29, // Tibetan 143 | 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047, 144 | 0x1048, 0x1049, // Myanmar 145 | 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097, 146 | 0x1098, 0x1099, // Myanmar Shan 147 | 0x1369, 0x136A, 0x136B, 0x136C, 0x136D, 0x136E, 0x136F, 0x1370, 148 | 0x1371, // Ethiopic 149 | 0x17E0, 0x17E1, 0x17E2, 0x17E3, 0x17E4, 0x17E5, 0x17E6, 0x17E7, 150 | 0x17E8, 0x17E9, // Khmer 151 | 0x1810, 0x1811, 0x1812, 0x1813, 0x1814, 0x1815, 0x1816, 0x1817, 152 | 0x1818, 0x1819, // Mongolian 153 | }; 154 | static const u32 NUM_DIGITS = sizeof(DIGITS) / sizeof(u32); 155 | 156 | static bool in(u32 code, const u32* array, u32 length) { 157 | u32 l = 0, h = length - 1; 158 | while (true) { 159 | if (code == array[l] || code == array[h]) return true; 160 | u32 m = (h + l) / 2; 161 | if (l == m || h == m) return false; 162 | if (code > array[m]) l = m; 163 | else if (code < array[m]) h = m; 164 | else return true; 165 | } 166 | return false; 167 | } 168 | 169 | bool isspace(uchar c) { 170 | return in(c.point(), SPACES, NUM_SPACES); 171 | } 172 | 173 | bool iscontrol(uchar c) { 174 | return in(c.point(), CONTROLS, NUM_CONTROLS); 175 | } 176 | 177 | bool isdigit(uchar c) { 178 | return in(c.point(), DIGITS, NUM_DIGITS); 179 | } 180 | 181 | bool isalpha(uchar c) { 182 | return (c[0] >= 0x0041 && c[0] <= 0x005A) 183 | || (c[0] >= 0x0061 && c[0] <= 0x007A); 184 | } 185 | 186 | bool isalnum(uchar c) { 187 | return isalpha(c) || isdigit(c); 188 | } 189 | 190 | bool issym(uchar c) { 191 | return !isspace(c) && !iscontrol(c); 192 | } 193 | 194 | bool isprint(uchar c) { 195 | return !iscontrol(c); 196 | } 197 | 198 | void ustring::free() { 199 | delete[] data; 200 | } 201 | 202 | void ustring::init(u32 size) { 203 | _size = 0, _capacity = size; 204 | data = new uchar[_capacity]; 205 | } 206 | 207 | void ustring::copy(const uchar* s) { 208 | uchar* dptr = data; 209 | while (*s) *(dptr ++) = *(s ++), ++ _size; 210 | *dptr = '\0'; 211 | } 212 | 213 | void ustring::grow() { 214 | uchar* old = data; 215 | init(_capacity * 2); 216 | copy(old); 217 | delete[] old; 218 | } 219 | 220 | i32 ustring::cmp(const uchar* s) const { 221 | uchar* dptr = data; 222 | while (*s && *s == *dptr) ++ s, ++ dptr; 223 | if (*dptr == *s) return 0; 224 | else if (*dptr > *s) return 1; 225 | else return -1; 226 | } 227 | 228 | i32 ustring::cmp(const char* s) const { 229 | uchar* dptr = data; 230 | while (*s) { 231 | uchar c(s); 232 | if (c != *dptr) break; 233 | s += c.size(), ++ dptr; 234 | } 235 | uchar c(s); 236 | if (*dptr == c) return 0; 237 | else if (*dptr > c) return 1; 238 | else return -1; 239 | } 240 | 241 | ustring::ustring() { 242 | init(8); 243 | } 244 | 245 | ustring::~ustring() { 246 | free(); 247 | } 248 | 249 | ustring::ustring(const ustring& other) { 250 | init(other._capacity); 251 | copy(other.data); 252 | } 253 | 254 | ustring::ustring(const char* s): ustring() { 255 | operator+=(s); 256 | } 257 | 258 | ustring& ustring::operator=(const ustring& other) { 259 | if (this != &other) { 260 | free(); 261 | init(other._capacity); 262 | copy(other.data); 263 | } 264 | return *this; 265 | } 266 | 267 | ustring& ustring::operator+=(uchar c) { 268 | if (!c) return *this; 269 | if (_size + 1 >= _capacity) grow(); 270 | data[_size ++] = c; 271 | return *this; 272 | } 273 | 274 | ustring& ustring::operator+=(char c) { 275 | if (!c) return *this; 276 | while (_size + 1 >= _capacity) grow(); 277 | data[_size ++] = c; 278 | return *this; 279 | } 280 | 281 | ustring& ustring::operator+=(const uchar* s) { 282 | u32 size = 0; 283 | const uchar* sptr = s; 284 | while (*sptr) ++ size, ++ sptr; 285 | while (_size + size >= _capacity) grow(); 286 | uchar* dptr = data + _size; 287 | sptr = s; 288 | while (*sptr) *(dptr ++) = *(sptr ++); 289 | *dptr = '\0'; 290 | _size += size; 291 | return *this; 292 | } 293 | 294 | ustring& ustring::operator+=(const char* s) { 295 | u32 size = 0; 296 | const char* sptr = s; 297 | while (*sptr) { 298 | uchar c(sptr); 299 | ++ size; 300 | sptr += c.size(); 301 | } 302 | while (_size + size >= _capacity) grow(); 303 | uchar* dptr = data + _size; 304 | sptr = s; 305 | while (*sptr) { 306 | uchar c(sptr); 307 | *(dptr ++) = c; 308 | sptr += c.size(); 309 | } 310 | *dptr = '\0'; 311 | _size += size; 312 | return *this; 313 | } 314 | 315 | ustring& ustring::operator+=(const ustring& s) { 316 | return operator+=(s.data); 317 | } 318 | 319 | void ustring::pop() { 320 | _size --; 321 | data[_size] == '\0'; 322 | } 323 | 324 | u32 ustring::size() const { 325 | return _size; 326 | } 327 | 328 | u32 ustring::capacity() const { 329 | return _capacity; 330 | } 331 | 332 | uchar ustring::operator[](u32 i) const { 333 | return data[i]; 334 | } 335 | 336 | uchar& ustring::operator[](u32 i) { 337 | return data[i]; 338 | } 339 | 340 | const uchar* ustring::raw() const { 341 | return data; 342 | } 343 | 344 | bool ustring::operator==(const uchar* s) const { 345 | return cmp(s) == 0; 346 | } 347 | 348 | bool ustring::operator==(const char* s) const { 349 | return cmp(s) == 0; 350 | } 351 | 352 | bool ustring::operator==(const ustring& s) const { 353 | return cmp(s.data) == 0; 354 | } 355 | 356 | bool ustring::operator<(const uchar* s) const { 357 | return cmp(s) < 0; 358 | } 359 | 360 | bool ustring::operator<(const char* s) const { 361 | return cmp(s) < 0; 362 | } 363 | 364 | bool ustring::operator<(const ustring& s) const { 365 | return cmp(s.data) < 0; 366 | } 367 | 368 | bool ustring::operator>(const uchar* s) const { 369 | return cmp(s) > 0; 370 | } 371 | 372 | bool ustring::operator>(const char* s) const { 373 | return cmp(s) > 0; 374 | } 375 | 376 | bool ustring::operator>(const ustring& s) const { 377 | return cmp(s.data) > 0; 378 | } 379 | 380 | bool ustring::operator!=(const uchar* s) const { 381 | return cmp(s) != 0; 382 | } 383 | 384 | bool ustring::operator!=(const char* s) const { 385 | return cmp(s) != 0; 386 | } 387 | 388 | bool ustring::operator!=(const ustring& s) const { 389 | return cmp(s.data) != 0; 390 | } 391 | 392 | bool ustring::operator<=(const uchar* s) const { 393 | return cmp(s) <= 0; 394 | } 395 | 396 | bool ustring::operator<=(const char* s) const { 397 | return cmp(s) <= 0; 398 | } 399 | 400 | bool ustring::operator<=(const ustring& s) const { 401 | return cmp(s.data) <= 0; 402 | } 403 | 404 | bool ustring::operator>=(const uchar* s) const { 405 | return cmp(s) >= 0; 406 | } 407 | 408 | bool ustring::operator>=(const char* s) const { 409 | return cmp(s) >= 0; 410 | } 411 | 412 | bool ustring::operator>=(const ustring& s) const { 413 | return cmp(s.data) >= 0; 414 | } 415 | 416 | ustring operator+(ustring s, uchar c) { 417 | s += c; 418 | return s; 419 | } 420 | 421 | ustring operator+(ustring s, const char* cs) { 422 | s += cs; 423 | return s; 424 | } 425 | 426 | ustring operator+(ustring s, const ustring& cs) { 427 | s += cs; 428 | return s; 429 | } 430 | 431 | ustring escape(const ustring& s) { 432 | ustring n = ""; 433 | for (u32 i = 0; i < s.size(); i ++) { 434 | if (s[i] == '\n') n += "\\n"; 435 | else if (s[i] == '\t') n += "\\t"; 436 | else if (s[i] == '\r') n += "\\r"; 437 | else if (s[i] == '\\') n += "\\\\"; 438 | else if (s[i] == '\"') n += "\\\""; 439 | else if (s[i] == '\0') n += "\\0"; 440 | else n += s[i]; 441 | } 442 | return n; 443 | } 444 | 445 | template<> 446 | u64 hash(const ustring& s) { 447 | return raw_hash(s.raw(), sizeof(uchar) * s.size()); 448 | } 449 | 450 | void print(stream& io, uchar c) { 451 | for (u32 i = 0; i < c.size(); ++ i) io.write(c[i]); 452 | } 453 | 454 | void print(uchar c) { 455 | print(_stdout, c); 456 | } 457 | 458 | void print(stream& io, const ustring& s) { 459 | for (u32 i = 0; i < s.size(); ++ i) print(io, s[i]); 460 | } 461 | 462 | void print(const ustring& s) { 463 | print(_stdout, s); 464 | } 465 | 466 | void read(stream& io, uchar& c) { 467 | char buf[] = { '\0', '\0', '\0', '\0' }; 468 | u32 size = uchar(io.peek()).size(); 469 | for (u32 i = 0; i < size; i ++) buf[i] = io.read(); 470 | c = uchar(buf); 471 | } 472 | 473 | void read(uchar& c) { 474 | read(_stdin, c); 475 | } 476 | 477 | void read(stream& io, ustring& s) { 478 | while (io.peek()) { 479 | uchar c; 480 | read(io, c); 481 | if (isspace(c)) { 482 | for (i32 i = c.size() - 1; i >= 0; -- i) io.unget(c[i]); 483 | break; 484 | } 485 | s += c; 486 | } 487 | } 488 | 489 | void read(ustring& s) { 490 | read(_stdin, s); 491 | } 492 | -------------------------------------------------------------------------------- /src/x64.cpp: -------------------------------------------------------------------------------- 1 | #include "x64.h" 2 | #include "hash.h" 3 | 4 | namespace basil { 5 | namespace x64 { 6 | const char* CONDITION_NAMES[8] = { 7 | "e", 8 | "ne", 9 | "l", 10 | "le", 11 | "g", 12 | "ge", 13 | "z", 14 | "nz" 15 | }; 16 | 17 | Size typeSize(const Type* t) { 18 | switch (t->size()) { 19 | case 0: 20 | return VOID; 21 | case 1: 22 | return BYTE; 23 | case 2: 24 | return WORD; 25 | case 4: 26 | if (t->is() && 27 | t->as()->floating()) 28 | return SINGLE; 29 | return DWORD; 30 | case 8: 31 | if (t->is() && 32 | t->as()->floating()) 33 | return DOUBLE; 34 | return QWORD; 35 | default: 36 | return ERROR; 37 | } 38 | } 39 | 40 | namespace printer { 41 | void printSized(buffer& b, const char* opcode, Size kind) { 42 | fprint(b, opcode); 43 | switch (kind) { 44 | case BYTE: 45 | return fprint(b, 'b'); 46 | case WORD: 47 | return fprint(b, 'w'); 48 | case DWORD: 49 | return fprint(b, 'l'); 50 | case QWORD: 51 | return fprint(b, 'q'); 52 | case SINGLE: 53 | return fprint(b, "ss"); 54 | case DOUBLE: 55 | return fprint(b, "sd"); 56 | default: 57 | return; 58 | } 59 | } 60 | 61 | void printUnsized(buffer& b, const char* opcode) { 62 | fprint(b, opcode); 63 | } 64 | 65 | void printSized(buffer& b, Register reg, Size kind) { 66 | fprint(b, "%"); 67 | switch (reg) { 68 | case RAX: 69 | case RCX: 70 | case RDX: 71 | case RBX: 72 | if (kind == QWORD) fprint(b, 'r'); 73 | else if (kind == DWORD) fprint(b, 'e'); 74 | fprint(b, REGISTER_NAMES[reg][1]); 75 | if (kind == BYTE) fprint(b, 'l'); 76 | else fprint(b, 'x'); 77 | break; 78 | case RBP: 79 | case RSP: 80 | case RSI: 81 | case RDI: 82 | if (kind == QWORD) fprint(b, 'r'); 83 | else if (kind == DWORD) fprint(b, 'e'); 84 | fprint(b, REGISTER_NAMES[reg] + 1); 85 | if (kind == BYTE) fprint(b, 'l'); 86 | break; 87 | case R8: 88 | case R9: 89 | case R10: 90 | case R11: 91 | case R12: 92 | case R13: 93 | case R14: 94 | case R15: 95 | fprint(b, REGISTER_NAMES[reg]); 96 | if (kind == DWORD) fprint(b, 'd'); 97 | else if (kind == WORD) fprint(b, 'w'); 98 | else if (kind == BYTE) fprint(b, 'b'); 99 | break; 100 | case XMM0: 101 | case XMM1: 102 | case XMM2: 103 | case XMM3: 104 | case XMM4: 105 | case XMM5: 106 | case XMM6: 107 | case XMM7: 108 | fprint(b, "xmm", (u8)reg - 32); 109 | break; 110 | default: 111 | break; 112 | } 113 | } 114 | 115 | void printArg(buffer& b, Location* loc) { 116 | if (loc->imm) 117 | loc->imm->emitX86Arg(b); 118 | else if (loc->segm == IMMEDIATE) 119 | fprint(b, "$", loc->off); 120 | else if (loc->segm == STACK) 121 | fprint(b, loc->off, "(%rbp)"); 122 | else if (loc->segm == REGISTER) 123 | printSized(b, loc->reg, typeSize(loc->type)); 124 | else if (loc->segm == REGISTER_RELATIVE) { 125 | fprint(b, loc->off, "("); 126 | printSized(b, loc->reg, QWORD); 127 | fprint(b, ")"); 128 | } 129 | else if (loc->segm == RELATIVE) { 130 | if (loc->src->segm == STACK) { 131 | Location newloc(STACK, loc->src->off + loc->off, loc->type); 132 | printArg(b, &newloc); 133 | } 134 | else if (loc->src->segm == REGISTER_RELATIVE) { 135 | Location newloc(loc->src->reg, loc->src->off + loc->off, loc->type); 136 | printArg(b, &newloc); 137 | } 138 | } 139 | } 140 | 141 | void indent(buffer& b) { 142 | fprint(b, " "); 143 | } 144 | 145 | void intconst(buffer& text, buffer& data, i64 value) { 146 | indent(data); 147 | fprintln(data, ".quad ", value); 148 | } 149 | 150 | void fconst(buffer& text, buffer& data, double value) { 151 | indent(data); 152 | fprintln(data, ".double ", value); 153 | } 154 | 155 | void strconst(buffer& text, buffer& data, const ustring& value) { 156 | indent(data); 157 | buffer b; 158 | fprint(b, escape(value)); 159 | for (int i = value.size(); i % 8 != 0; i ++) b.write('\\'), b.write('0'); 160 | fprintln(data, ".ascii \"", b, "\""); 161 | } 162 | 163 | void text(buffer& text, buffer& data) { 164 | fprintln(text, ".text"); 165 | } 166 | 167 | void data(buffer& text, buffer& data) { 168 | fprintln(data, ".data"); 169 | } 170 | 171 | void label(buffer& text, buffer& data, Section section, const ustring& name, bool global) { 172 | if (global) fprintln((section == TEXT ? text : data), ".global ", name); 173 | fprintln((section == TEXT ? text : data), name, ":"); 174 | } 175 | 176 | void binary(buffer& text, buffer& data, 177 | Location* src, Location* dst, const char* opcode, 178 | void (*self)(buffer&, buffer&, Location*, Location*), bool sized = true) { 179 | if (dst->imm) { // don't target immediates 180 | Location* tmp = dst; 181 | dst = src; 182 | src = tmp; 183 | } 184 | if (src->segm == REGISTER || dst->segm == REGISTER) { 185 | indent(text); 186 | if (sized) printSized(text, opcode, typeSize(src->type)); 187 | else printUnsized(text, opcode); 188 | fprint(text, " "); 189 | printArg(text, src); 190 | fprint(text, ", "); 191 | printArg(text, dst); 192 | fprintln(text, ""); 193 | } 194 | else { 195 | Location rax(RAX, src->type); 196 | mov(text, data, src, &rax); 197 | self(text, data, &rax, dst); 198 | } 199 | } 200 | 201 | void mov(buffer& text, buffer& data, Location* src, Location* dst) { 202 | if (*src == *dst) return; 203 | binary(text, data, src, dst, "mov", mov); 204 | } 205 | 206 | void add(buffer& text, buffer& data, Location* src, Location* dst) { 207 | binary(text, data, src, dst, "add", add); 208 | } 209 | 210 | void sub(buffer& text, buffer& data, Location* src, Location* dst) { 211 | binary(text, data, src, dst, "sub", sub); 212 | } 213 | 214 | void imul(buffer& text, buffer& data, Location* src, Location* dst) { 215 | Location* target = dst; 216 | if (dst->segm != REGISTER) { 217 | if (src->segm == REGISTER) { 218 | Location* tmp = dst; 219 | dst = src; 220 | src = tmp; 221 | } 222 | else { 223 | Location rax(RAX, dst->type); 224 | target = &rax; 225 | } 226 | } 227 | binary(text, data, src, target, "imul", imul); 228 | if (target != dst) mov(text, data, target, dst); 229 | } 230 | 231 | void mul(buffer& text, buffer& data, Location* src, Location* dst) { 232 | Location* target = dst; 233 | if (dst->segm != REGISTER) { 234 | if (src->segm == REGISTER) { 235 | Location* tmp = dst; 236 | dst = src; 237 | src = tmp; 238 | } 239 | else { 240 | Location rax(RAX, dst->type); 241 | target = &rax; 242 | } 243 | } 244 | binary(text, data, src, target, "mul", mul); 245 | if (target != dst) mov(text, data, target, dst); 246 | } 247 | 248 | void idiv(buffer& text, buffer& data, Location* src) { 249 | indent(text); 250 | printSized(text, "idiv", typeSize(src->type)); 251 | fprint(text, " "); 252 | printArg(text, src); 253 | fprintln(text, ""); 254 | } 255 | 256 | void div(buffer& text, buffer& data, Location* src) { 257 | indent(text); 258 | printSized(text, "div", typeSize(src->type)); 259 | fprint(text, " "); 260 | printArg(text, src); 261 | fprintln(text, ""); 262 | } 263 | 264 | void fdiv(buffer& text, buffer& data, Location* src, Location* dst) { 265 | binary(text, data, src, dst, "div", fdiv); 266 | } 267 | 268 | void cmp(buffer& text, buffer& data, Location* src, Location* dst) { 269 | binary(text, data, src, dst, "cmp", cmp); 270 | } 271 | 272 | void and_(buffer& text, buffer& data, Location* src, Location* dst) { 273 | binary(text, data, src, dst, "and", and_); 274 | } 275 | 276 | void or_(buffer& text, buffer& data, Location* src, Location* dst) { 277 | binary(text, data, src, dst, "or", or_); 278 | } 279 | 280 | void xor_(buffer& text, buffer& data, Location* src, Location* dst) { 281 | binary(text, data, src, dst, "xor", xor_); 282 | } 283 | 284 | void not_(buffer& text, buffer& data, Location* operand) { 285 | indent(text); 286 | printSized(text, "not", typeSize(operand->type)); 287 | fprint(text, " "); 288 | printArg(text, operand); 289 | fprintln(text, ""); 290 | } 291 | 292 | void movsx(buffer& text, buffer& data, Location* src, Location* dst) { 293 | binary(text, data, src, dst, "movsx", movsx); 294 | } 295 | 296 | void movzx(buffer& text, buffer& data, Location* src, Location* dst) { 297 | binary(text, data, src, dst, "movzx", movzx); 298 | } 299 | 300 | void cvt(buffer& text, buffer& data, Location* src, Location* dst, 301 | const char* opcode, void (*self)(buffer&, buffer&, Location*, Location*), 302 | bool sized = true) { 303 | Location* target = dst; 304 | if (dst->segm != REGISTER) { 305 | Location rax(RAX, dst->type); 306 | Location xmm7(XMM7, dst->type); 307 | if (dst->type->is() && 308 | dst->type->as()->floating()) 309 | target = &xmm7; 310 | else target = &rax; 311 | } 312 | binary(text, data, src, target, opcode, self, sized); 313 | if (target != dst) mov(text, data, target, dst); 314 | } 315 | 316 | void cvttsd2si(buffer& text, buffer& data, Location* src, Location* dst) { 317 | cvt(text, data, src, dst, "cvttsd2si", cvttsd2si); 318 | } 319 | 320 | void cvttss2si(buffer& text, buffer& data, Location* src, Location* dst) { 321 | cvt(text, data, src, dst, "cvttss2si", cvttss2si); 322 | } 323 | 324 | void cvtsd2ss(buffer& text, buffer& data, Location* src, Location* dst) { 325 | cvt(text, data, src, dst, "cvtsd2ss", cvtsd2ss, false); 326 | } 327 | 328 | void cvtss2sd(buffer& text, buffer& data, Location* src, Location* dst) { 329 | cvt(text, data, src, dst, "cvtss2sd", cvtss2sd, false); 330 | } 331 | 332 | void cvtsi2sd(buffer& text, buffer& data, Location* src, Location* dst) { 333 | cvt(text, data, src, dst, "cvtsi2sd", cvtsi2sd); 334 | } 335 | 336 | void cvtsi2ss(buffer& text, buffer& data, Location* src, Location* dst) { 337 | cvt(text, data, src, dst, "cvtsi2ss", cvtsi2ss); 338 | } 339 | 340 | void lea(buffer& text, buffer& data, const ustring& label, Location* dst) { 341 | Location* target = dst; 342 | if (dst->segm != REGISTER) { 343 | Location rax(RAX, dst->type); 344 | target = &rax; 345 | } 346 | indent(text); 347 | printSized(text, "lea", typeSize(dst->type)); 348 | fprint(text, " ", label, "(%rip), "); 349 | printArg(text, target); 350 | fprintln(text, ""); 351 | if (target != dst) mov(text, data, target, dst); 352 | } 353 | 354 | void lea(buffer& text, buffer& data, Location* addr, Location* dst) { 355 | Location* target = dst; 356 | if (dst->segm != REGISTER) { 357 | Location rax(RAX, dst->type); 358 | target = &rax; 359 | } 360 | indent(text); 361 | printSized(text, "lea", typeSize(dst->type)); 362 | fprint(text, " "); 363 | printArg(text, addr); 364 | fprint(text, ", "); 365 | printArg(text, target); 366 | fprintln(text, ""); 367 | if (target != dst) mov(text, data, target, dst); 368 | } 369 | 370 | void jmp(buffer& text, buffer& data, Location* addr) { 371 | indent(text); 372 | fprint(text, "jmp *"); 373 | printArg(text, addr); 374 | fprintln(text, ""); 375 | } 376 | 377 | void jmp(buffer& text, buffer& data, const ustring& label) { 378 | indent(text); 379 | fprintln(text, "jmp ", label); 380 | } 381 | 382 | void jcc(buffer& text, buffer& data, const ustring& label, Condition condition) { 383 | indent(text); 384 | fprintln(text, "j", CONDITION_NAMES[condition], " ", label); 385 | } 386 | 387 | void setcc(buffer& text, buffer& data, Location* dst, Condition condition) { 388 | indent(text); 389 | fprint(text, "set", CONDITION_NAMES[condition], " "); 390 | printArg(text, dst); 391 | fprintln(text, ""); 392 | } 393 | 394 | void syscall(buffer& text, buffer& data) { 395 | indent(text); 396 | fprintln(text, "syscall"); 397 | } 398 | 399 | void ret(buffer& text, buffer& data) { 400 | indent(text); 401 | fprintln(text, "ret"); 402 | } 403 | 404 | void call(buffer& text, buffer& data, Location* function) { 405 | indent(text); 406 | fprint(text, "callq *"); 407 | printArg(text, function); 408 | fprintln(text, ""); 409 | } 410 | 411 | void call(buffer& text, buffer& data, const ustring& function) { 412 | indent(text); 413 | fprintln(text, "callq ", function); 414 | } 415 | 416 | void push(buffer& text, buffer& data, Location* src) { 417 | indent(text); 418 | printSized(text, "push", typeSize(src->type)); 419 | fprint(text, " "); 420 | printArg(text, src); 421 | fprintln(text, ""); 422 | } 423 | 424 | void pop(buffer& text, buffer& data, Location* dst) { 425 | indent(text); 426 | printSized(text, "pop", typeSize(dst->type)); 427 | fprint(text, " "); 428 | printArg(text, dst); 429 | fprintln(text, ""); 430 | } 431 | 432 | void cdq(buffer& text, buffer& data) { 433 | indent(text); 434 | fprintln(text, "cdq"); 435 | } 436 | } 437 | 438 | // namespace assembler { 439 | // void label(buffer& text, buffer& data, Section section, const ustring& name, bool global); 440 | // void mov(buffer& text, buffer& data, Location* src, Location* dst); 441 | // void add(buffer& text, buffer& data, Location* src, Location* dst); 442 | // void sub(buffer& text, buffer& data, Location* src, Location* dst); 443 | // void imul(buffer& text, buffer& data, Location* src, Location* dst); 444 | // void mul(buffer& text, buffer& data, Location* src, Location* dst); 445 | // void idiv(buffer& text, buffer& data, Location* src); 446 | // void div(buffer& text, buffer& data, Location* src); 447 | // void cmp(buffer& text, buffer& data, Location* src, Location* dst); 448 | // void and_(buffer& text, buffer& data, Location* src, Location* dst); 449 | // void or_(buffer& text, buffer& data, Location* src, Location* dst); 450 | // void xor_(buffer& text, buffer& data, Location* src, Location* dst); 451 | // void lea(buffer& text, buffer& data, const ustring& label, Location* dst); 452 | // void lea(buffer& text, buffer& data, Location* addr, Location* dst); 453 | // void syscall(buffer& text, buffer& data, Location* number); 454 | // void syscall(buffer& text, buffer& data, u8 number); 455 | // void ret(buffer& text, buffer& data); 456 | // void call(buffer& text, buffer& data, Location* function); 457 | // void call(buffer& text, buffer& data, const ustring& function); 458 | // void push(buffer& text, buffer& data, Location* src); 459 | // void pop(buffer& text, buffer& data, Location* dst); 460 | // } 461 | } 462 | } -------------------------------------------------------------------------------- /test/array.bl: -------------------------------------------------------------------------------- 1 | println n -> 2 | print n 3 | print "\n" 4 | 5 | arr = [1 2 3 4] 6 | 7 | arr[0] println 8 | arr[3] println -------------------------------------------------------------------------------- /test/binary.bl: -------------------------------------------------------------------------------- 1 | println x -> 2 | print x 3 | print "\n" 4 | 5 | println (1 + 2) 6 | println (1 + 2) 7 | println (1 + 2) -------------------------------------------------------------------------------- /test/logic.bl: -------------------------------------------------------------------------------- 1 | bool a = 10 > 20 2 | 3 | i = 0 4 | while i < 10: 5 | print "loop" 6 | i = i + 1 -------------------------------------------------------------------------------- /test/math.bl: -------------------------------------------------------------------------------- 1 | println x -> 2 | print x 3 | print "\n" 4 | 5 | println (1 + 2) # 3 6 | println (1 + 2.0) # 3.0 7 | println (1.0 + 2) # 3.0 8 | println (1.0 + 2.0) # 3.0 9 | 10 | x = 10 11 | y = 20 12 | 13 | dist = (x * x) + (y * y) 14 | dist-wrong = x * x + y * y 15 | 16 | println dist # (10 * 10) + (20 * 20) => 500 17 | println dist-wrong # ((10 * 10) + 20) * 20 => 2400 18 | 19 | println (5 / 2) # 2 20 | println (5.0 / 2) # 2.5 -------------------------------------------------------------------------------- /test/meta-math.bl: -------------------------------------------------------------------------------- 1 | meta: print (1 + 2) # 3 2 | meta: print (1 + 2.0) # 3.0 3 | meta: print (1.0 + 2) # 3.0 4 | meta: print (1.0 + 2.0) # 3.0 5 | 6 | x = 10 7 | y = 20 8 | 9 | dist = (x * x) + (y * y) 10 | dist-wrong = x * x + y * y 11 | 12 | meta: print dist # (10 * 10) + (20 * 20) => 500 13 | meta: print dist-wrong # ((10 * 10) + 20) * 20 => 2400 14 | 15 | meta: print (5 / 2) # 2 16 | meta: print (5.0 / 2) # 2.5 -------------------------------------------------------------------------------- /test/pure.bl: -------------------------------------------------------------------------------- 1 | inc x -> x + 1 2 | 3 | inc 2 -------------------------------------------------------------------------------- /test/strings.bl: -------------------------------------------------------------------------------- 1 | f() -> 2 | a = "hello world" 3 | b = "lovely weather we're having" 4 | i = 0 5 | while i < 100000: 6 | t = a 7 | a = b 8 | b = t 9 | i = i + 1 10 | a 11 | 12 | j = 0 13 | while j < 1000: 14 | f() print 15 | j = j + 1 --------------------------------------------------------------------------------