├── .gitignore ├── INSTALL ├── README ├── TODO ├── compiler ├── ast.cpp ├── brawl.cpp ├── cityhash │ ├── city.cc │ └── city.h ├── codegen.cpp ├── compat │ ├── compat_amd64.cpp │ └── compat_x86.cpp ├── ctokrawl.cpp ├── gvim.bash ├── krawl.cpp ├── krawl.hpp ├── lemon │ ├── lemon.c │ └── lempar.c ├── lexer.cpp ├── lexer.rl ├── message.cpp ├── misc.cpp ├── parser-code.cpp ├── parser.cpp ├── parser.y ├── semantic.cpp ├── source-loc.cpp ├── token-to-string-gen.py ├── tokens.cpp ├── tokens.hpp ├── update.bash ├── utf8.cpp └── wscript ├── ctokrawl ├── ctokrawl.cpp └── wscript ├── doc ├── Makefile ├── TYPE_SYSTEM ├── krawl_by_example.txt ├── lang.map └── typesystem.txt ├── examples ├── args.krl ├── array-value-semantics.krl ├── fib.krl ├── funcptr.krl ├── loops.krl ├── projecteuler │ ├── problem1.krl │ ├── problem2.krl │ ├── problem8.krl │ └── problem8.txt ├── qsort.krl ├── sdl-grad.krl ├── structs.krl ├── switch.krl ├── tetris.krl └── typedeps.krl ├── extra └── krawl.lang ├── stdlib ├── std │ ├── helloworld │ │ └── hello.krl │ └── string │ │ └── string.krl └── wscript ├── test ├── 0.array.krl ├── 0.basic.krl ├── 0.builtins.krl ├── 0.ifstmt.krl ├── 0.iota.krl ├── 0.mrv.krl ├── 0.op.krl ├── 0.overflow.krl ├── 0.returnstmt.krl ├── 0.sideeffects.krl ├── 0.strings.krl ├── 0.vaargs.krl ├── array.krl ├── basic.krl ├── builtins.krl ├── checker.py ├── ifstmt.krl ├── iota.krl ├── mrv.krl ├── op.krl ├── overflow.krl ├── returnstmt.krl ├── sideeffects.krl ├── strings.krl └── vaargs.krl ├── waf ├── wafscripts ├── krawl.py └── krawl_scan.py └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | frontend/parser.out 3 | frontend/lemon/lemon 4 | *.swp 5 | .waf* 6 | .lock* 7 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | This will install krawl to your home dir at ~/krawl 2 | Executable will be available as ~/krawl/bin/krawl 3 | Add ~/krawl/bin to your PATH 4 | 5 | CXX=clang++ ./waf configure --prefix=~/krawl install 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Krawl was an experimental programming language. It is largely based on the mix 2 | of Go and C programming languages. Currently the project is abandoned, but still 3 | here for educational purposes. 4 | 5 | There is only one document describing krawl: 6 | http://nosmileface.ru/krawl/krawl_by_example.html 7 | 8 | Portability notes: 9 | - works on x86, linux 10 | - va_list is broken on amd64 11 | - almost works on darwin, there are some problems with clang plugin 12 | - haven't tried it on windows, most likely doesn't work at all 13 | 14 | See also big TODO list. 15 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Fix semantic handling of void functions (no return value). 2 | - Optimize code generation for compound literals, use *Constant when possible. 3 | - Think about code execution before "main", should we forbid that? 4 | - Implement anonymous structures and unions. Implement type embedding (ask 5 | nsf). 6 | - Implement support for more than one file per module. Improve code that is 7 | responsible for that. 8 | - Implement proper visibility handling. Probably use Go's scheme, where symbol 9 | which starts from a capital letter is public, otherwise private. Add various 10 | attributes to override visibility settings and to control mangling (for 11 | interfacing with C). 12 | - Add an ability to pass CFLAGS to clang plugin for advanced C import. This 13 | also requires modifying cache scheme. 14 | - Ensure C modules cache is race condition free. 15 | - Built-in functions should not be assignable to any type. 16 | - Implement different ways to initialize struct/union/array (see how it's done 17 | in C). 18 | - Implement proper dependency analysis in waf krawl tool (C modules 19 | dependencies and other stuff). 20 | - Portability: Mac OSX, *BSD, Windows. 21 | - Macros/templates system. Syntax and implementation. 22 | - Packed structs. Custom alignment settings for declarations. Use attributes. 23 | - Volatile and restrict modifiers. Even though attributes are not allowed in 24 | function arguments, ask nsf, he knows how to make that. 25 | - Built-in type 'complex'. Does anyone need this? Why all modern languages 26 | implement that? 27 | - Language specification, language conformance testing. 28 | - Standard library. Design and implementation. 29 | - Syntax for referencing code locations in error messages. Location demangling 30 | is delayed until everything is printed, therefore we simply don't have the 31 | right data in-place. 32 | - C-to-Krawl plugin improvements: extern variable import, etc. 33 | -------------------------------------------------------------------------------- /compiler/brawl.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | 3 | //------------------------------------------------------------------------------ 4 | // FILE_reader_t 5 | //------------------------------------------------------------------------------ 6 | 7 | enum { 8 | VARINT_U16, 9 | VARINT_U32, 10 | VARINT_U64, 11 | 12 | VARINT_BIT = (1 << 7) 13 | }; 14 | 15 | FILE_reader_t::FILE_reader_t(FILE *f): file(f) 16 | { 17 | } 18 | 19 | uint64_t FILE_reader_t::read_varuint() 20 | { 21 | uint8_t n = read_uint8(); 22 | if (n & VARINT_BIT) { 23 | switch (n & ~VARINT_BIT) { 24 | case VARINT_U16: 25 | return read_uint16(); 26 | case VARINT_U32: 27 | return read_uint32(); 28 | case VARINT_U64: 29 | return read_uint64(); 30 | default: 31 | KRAWL_ASSERT(false, "invalid binary format in FILE_reader_t"); 32 | break; 33 | } 34 | } 35 | return n; 36 | } 37 | 38 | std::string &FILE_reader_t::read_string() 39 | { 40 | uint64_t len = read_varuint(); 41 | if (!len) { 42 | strbuf = ""; 43 | return strbuf; 44 | } 45 | 46 | strbuf.resize(len); 47 | size_t read = fread(&strbuf[0], 1, len, file); 48 | KRAWL_ASSERT(read == len, 49 | "failed to read binary data in FILE_reader_t"); 50 | return strbuf; 51 | } 52 | 53 | #define READ_TYPE(ty) \ 54 | do { \ 55 | ty v; \ 56 | size_t read = fread(&v, 1, sizeof(ty), file); \ 57 | KRAWL_ASSERT(read == sizeof(ty), \ 58 | "failed to read binary data in FILE_reader_t"); \ 59 | return v; \ 60 | } while (0) 61 | 62 | uint8_t FILE_reader_t::read_uint8() { READ_TYPE(uint8_t); } 63 | uint16_t FILE_reader_t::read_uint16() { READ_TYPE(uint16_t); } 64 | uint32_t FILE_reader_t::read_uint32() { READ_TYPE(uint32_t); } 65 | uint64_t FILE_reader_t::read_uint64() { READ_TYPE(uint64_t); } 66 | int8_t FILE_reader_t::read_int8() { READ_TYPE(int8_t); } 67 | int16_t FILE_reader_t::read_int16() { READ_TYPE(int16_t); } 68 | int32_t FILE_reader_t::read_int32() { READ_TYPE(int32_t); } 69 | int64_t FILE_reader_t::read_int64() { READ_TYPE(int64_t); } 70 | 71 | //------------------------------------------------------------------------------ 72 | // FILE_writer_t 73 | //------------------------------------------------------------------------------ 74 | 75 | FILE_writer_t::FILE_writer_t(FILE *f): file(f) 76 | { 77 | } 78 | 79 | void FILE_writer_t::write_varuint(uint64_t n) 80 | { 81 | if (n < 128) { 82 | write_uint8(n); 83 | } else { 84 | if (n < 65536) { 85 | write_uint8(VARINT_U16 | VARINT_BIT); 86 | write_uint16(n); 87 | } else if (n < 4294967296) { 88 | write_uint8(VARINT_U32 | VARINT_BIT); 89 | write_uint32(n); 90 | } else { 91 | write_uint8(VARINT_U64 | VARINT_BIT); 92 | write_uint64(n); 93 | } 94 | } 95 | } 96 | 97 | void FILE_writer_t::write_string(const char *str) 98 | { 99 | write_string(str, strlen(str)); 100 | } 101 | 102 | void FILE_writer_t::write_string(const char *str, size_t len) 103 | { 104 | write_varuint(len); 105 | size_t written = fwrite(str, 1, len, file); 106 | KRAWL_ASSERT(written == len, 107 | "failed to write binary data in FILE_writer_t"); 108 | } 109 | 110 | void FILE_writer_t::write_string(const std::string &cppstr) 111 | { 112 | write_string(cppstr.c_str(), cppstr.size()); 113 | } 114 | 115 | #define WRITE_TYPE(ty) \ 116 | do { \ 117 | size_t written = fwrite(&n, 1, sizeof(ty), file); \ 118 | KRAWL_ASSERT(written == sizeof(ty), \ 119 | "failed to write binary data in FILE_writer_t"); \ 120 | } while (0) 121 | 122 | void FILE_writer_t::write_uint8(uint8_t n) { WRITE_TYPE(uint8_t); } 123 | void FILE_writer_t::write_uint16(uint16_t n) { WRITE_TYPE(uint16_t); } 124 | void FILE_writer_t::write_uint32(uint32_t n) { WRITE_TYPE(uint32_t); } 125 | void FILE_writer_t::write_uint64(uint64_t n) { WRITE_TYPE(uint64_t); } 126 | void FILE_writer_t::write_int8(int8_t n) { WRITE_TYPE(int8_t); } 127 | void FILE_writer_t::write_int16(int16_t n) { WRITE_TYPE(int16_t); } 128 | void FILE_writer_t::write_int32(int32_t n) { WRITE_TYPE(int32_t); } 129 | void FILE_writer_t::write_int64(int64_t n) { WRITE_TYPE(int64_t); } 130 | 131 | //------------------------------------------------------------------------------ 132 | // Brawl serializer 133 | //------------------------------------------------------------------------------ 134 | 135 | int32_t brawl_serializer_t::stype_index(stype_t *t) 136 | { 137 | if (IS_STYPE_BUILTIN(t)) 138 | return builtin_stype_index(t); 139 | 140 | stype_map_t::iterator it = stype_map.find(t); 141 | KRAWL_QASSERT(it != stype_map.end()); 142 | return it->second; 143 | } 144 | 145 | int32_t brawl_serializer_t::builtin_stype_index(stype_t *t) 146 | { 147 | if (IS_STYPE_BUILTIN(t)) { 148 | for (int i = 0; i < BUILTIN_N; ++i) { 149 | if (builtin_named_stypes[i] == t) 150 | return (-1 - i); 151 | } 152 | // in case if this is an abstract type (consts) 153 | for (int i = 0; i < BUILTIN_N; ++i) { 154 | if (builtin_stypes[i] == t) 155 | return (-1 - i); 156 | } 157 | } 158 | KRAWL_QASSERT(!"not a built-in type in brawl_stypes_t::builtin_stype_index"); 159 | return 0; 160 | } 161 | 162 | void brawl_serializer_t::serialize_named(FILE_writer_t *cout, named_stype_t *t) 163 | { 164 | cout->write_string(t->name); 165 | cout->write_int32(stype_index(t->real)); 166 | } 167 | 168 | void brawl_serializer_t::serialize_pointer(FILE_writer_t *cout, pointer_stype_t *t) 169 | { 170 | cout->write_int32(stype_index(t->points_to)); 171 | } 172 | 173 | void brawl_serializer_t::serialize_array(FILE_writer_t *cout, array_stype_t *t) 174 | { 175 | cout->write_varuint(t->size); 176 | cout->write_int32(stype_index(t->elem)); 177 | } 178 | 179 | void brawl_serializer_t::serialize_struct(FILE_writer_t *cout, struct_stype_t *t) 180 | { 181 | cout->write_varuint(t->alignment); 182 | cout->write_varuint(t->size); 183 | cout->write_varuint(t->fields.size()); 184 | for (size_t i = 0; i < t->fields.size(); ++i) { 185 | struct_field_t *f = &t->fields[i]; 186 | cout->write_string(f->name); 187 | cout->write_int32(stype_index(f->type)); 188 | cout->write_varuint(f->padding); 189 | } 190 | } 191 | 192 | void brawl_serializer_t::serialize_func(FILE_writer_t *cout, func_stype_t *t) 193 | { 194 | cout->write_uint8(t->varargs ? 1 : 0); 195 | 196 | cout->write_varuint(t->args.size()); 197 | for (size_t i = 0; i < t->args.size(); ++i) 198 | cout->write_int32(stype_index(t->args[i])); 199 | 200 | cout->write_varuint(t->results.size()); 201 | for (size_t i = 0; i < t->results.size(); ++i) 202 | cout->write_int32(stype_index(t->results[i])); 203 | } 204 | 205 | struct stype_queuer_t : stype_visitor_t { 206 | brawl_serializer_t *ctx; 207 | bool skip_first; 208 | 209 | stype_visitor_t *visit(stype_t *t) 210 | { 211 | if (skip_first) { 212 | skip_first = false; 213 | return this; 214 | } 215 | 216 | if (IS_STYPE_BUILTIN(t)) 217 | return 0; 218 | 219 | if (ctx->stype_map.find(t) != ctx->stype_map.end()) 220 | return 0; 221 | 222 | ctx->stype_map[t] = ctx->stypes.size(); 223 | ctx->stypes.push_back(t); 224 | return this; 225 | } 226 | }; 227 | 228 | int32_t brawl_serializer_t::queue_stype(stype_t *t) 229 | { 230 | // shortcut for built-ins 231 | if (IS_STYPE_BUILTIN(t)) 232 | return builtin_stype_index(t); 233 | 234 | // if the type is already here, get it 235 | stype_map_t::iterator it = stype_map.find(t); 236 | if (it != stype_map.end()) 237 | return it->second; 238 | 239 | // otherwise queue it 240 | int32_t curidx = stypes.size(); 241 | stype_map[t] = curidx; 242 | stypes.push_back(t); 243 | 244 | // queue children 245 | stype_queuer_t s; 246 | s.ctx = this; 247 | s.skip_first = true; 248 | s.traverse(t); 249 | 250 | return curidx; 251 | } 252 | 253 | void brawl_serializer_t::serialize(FILE_writer_t *cout, 254 | std::vector *pkgdecls, 255 | const char *prefix, const char *package) 256 | { 257 | // set active prefix 258 | this->cur_prefix = prefix; 259 | 260 | // module header 261 | cout->write_string(prefix); 262 | cout->write_string(package); 263 | 264 | // sdecls 265 | cout->write_uint32(pkgdecls->size()); 266 | for (size_t i = 0, n = pkgdecls->size(); i < n; ++i) { 267 | sdecl_t *d = pkgdecls->at(i); 268 | 269 | // skip imports 270 | if (d->type == SDECL_IMPORT) 271 | continue; 272 | 273 | cout->write_uint8(d->type); 274 | cout->write_string(d->name); 275 | cout->write_int32(queue_stype(d->stype)); // queueing stypes 276 | if (d->type == SDECL_CONST) { 277 | const_sdecl_t *cd = (const_sdecl_t*)d; 278 | cout->write_uint8(cd->value.type); 279 | cout->write_string(cd->value.to_string()); 280 | } 281 | } 282 | 283 | // stypes 284 | cout->write_uint32(stypes.size()); 285 | for (size_t i = 0, n = stypes.size(); i < n; ++i) { 286 | stype_t *t = stypes[i]; 287 | 288 | // hack to save prefixes, sort of like run-length encoding, 289 | // should provide more or less good compression 290 | if (IS_STYPE_NAMED(t)) { 291 | const char *tprefix = t->as_named()->prefix; 292 | if (cur_prefix != tprefix) { 293 | // let's introduce a new prefix first 294 | cout->write_uint16(0xFFFF); 295 | cout->write_string(tprefix); 296 | cur_prefix = tprefix; 297 | } 298 | } 299 | 300 | // write type 301 | cout->write_uint16(t->type); 302 | if (IS_STYPE_NAMED(t)) 303 | serialize_named(cout, t->as_named()); // queueing prefixes 304 | else if (IS_STYPE_POINTER(t)) 305 | serialize_pointer(cout, t->as_pointer()); 306 | else if (IS_STYPE_STRUCT_OR_UNION(t)) 307 | serialize_struct(cout, t->as_struct()); 308 | else if (IS_STYPE_ARRAY(t)) 309 | serialize_array(cout, t->as_array()); 310 | else if (IS_STYPE_FUNC(t)) 311 | serialize_func(cout, t->as_func()); 312 | else 313 | KRAWL_QASSERT(!"unreachable"); 314 | } 315 | } 316 | 317 | //------------------------------------------------------------------------------ 318 | // Brawl deserializer 319 | //------------------------------------------------------------------------------ 320 | 321 | brawl_deserializer_t::brawl_deserializer_t(import_context_t *ctx): ctx(ctx) 322 | { 323 | } 324 | 325 | stype_t *brawl_deserializer_t::index_stype(int32_t idx) 326 | { 327 | if (idx < 0) { 328 | stype_t *t = builtin_named_stypes[-idx - 1]; 329 | if (t) 330 | return t; 331 | return builtin_stypes[-idx - 1]; 332 | } 333 | return stypes[idx]; 334 | } 335 | 336 | const char *brawl_deserializer_t::ctx_prefix(const char *p) 337 | { 338 | unordered_set::iterator it = ctx->prefixes.find(p); 339 | if (it != ctx->prefixes.end()) 340 | return it->c_str(); 341 | 342 | return ctx->prefixes.insert(p).first->c_str(); 343 | } 344 | 345 | void brawl_deserializer_t::deserialize_named(FILE_reader_t *cin) 346 | { 347 | std::string &name = cin->read_string(); 348 | int32_t real = cin->read_int32(); 349 | 350 | std::string fullname = cur_prefix + "." + name; 351 | unordered_map::iterator it; 352 | it = ctx->named_map.find(fullname); 353 | 354 | named_stype_t *t; 355 | if (it != ctx->named_map.end()) { 356 | t = it->second; 357 | } else { 358 | t = new named_stype_t; 359 | t->prefix = ctx_prefix(cur_prefix.c_str()); 360 | t->name = name; 361 | t->real = (stype_t*)real; 362 | ctx->named_map[fullname] = t; 363 | ctx->ttracker->push_back(t); 364 | } 365 | stypes.push_back(t); 366 | /* 367 | printf("named type :: name: %s, real: %d\n", 368 | pb_namedtype.name().c_str(), pb_namedtype.real()); 369 | */ 370 | } 371 | 372 | void brawl_deserializer_t::deserialize_pointer(FILE_reader_t *cin) 373 | { 374 | pointer_stype_t *t = new pointer_stype_t; 375 | t->points_to = (stype_t*)cin->read_int32(); 376 | ctx->ttracker->push_back(t); 377 | stypes.push_back(t); 378 | /* 379 | printf("pointer type :: points to: %d\n", pb_pointertype.points_to()); 380 | */ 381 | } 382 | 383 | void brawl_deserializer_t::deserialize_array(FILE_reader_t *cin) 384 | { 385 | array_stype_t *t = new array_stype_t; 386 | t->size = cin->read_varuint(); 387 | t->elem = (stype_t*)cin->read_int32(); 388 | ctx->ttracker->push_back(t); 389 | stypes.push_back(t); 390 | /* 391 | printf("array type :: size: %llu, elem: %d\n", 392 | pb_arraytype.size(), pb_arraytype.elem()); 393 | */ 394 | } 395 | 396 | void brawl_deserializer_t::deserialize_struct(FILE_reader_t *cin) 397 | { 398 | struct_stype_t *t = new struct_stype_t; 399 | t->alignment = cin->read_varuint(); 400 | t->size = cin->read_varuint(); 401 | 402 | size_t fields_n = cin->read_varuint(); 403 | t->fields.reserve(fields_n); 404 | for (size_t i = 0, n = fields_n; i < n; ++i) { 405 | std::string &name = cin->read_string(); 406 | int32_t type = cin->read_int32(); 407 | uint64_t padding = cin->read_varuint(); 408 | 409 | struct_field_t field = {(stype_t*)type, name, padding}; 410 | t->fields.push_back(field); 411 | } 412 | ctx->ttracker->push_back(t); 413 | stypes.push_back(t); 414 | /* 415 | printf("struct type :: alignment: %u, size: %u, fields:\n", 416 | pb_structtype.alignment(), pb_structtype.size()); 417 | for (size_t i = 0, n = pb_structtype.field_size(); i < n; ++i) { 418 | const StructType_Field &f = pb_structtype.field(i); 419 | printf("\tfield :: name: %s, type: %d, padding: %u\n", 420 | f.name().c_str(), f.type(), f.padding()); 421 | } 422 | */ 423 | } 424 | 425 | void brawl_deserializer_t::deserialize_func(FILE_reader_t *cin) 426 | { 427 | func_stype_t *t = new func_stype_t; 428 | t->varargs = cin->read_uint8() == 1 ? true : false; 429 | 430 | size_t args_n = cin->read_varuint(); 431 | t->args.reserve(args_n); 432 | for (size_t i = 0, n = args_n; i < n; ++i) 433 | t->args.push_back((stype_t*)cin->read_int32()); 434 | 435 | size_t results_n = cin->read_varuint(); 436 | t->results.reserve(results_n); 437 | for (size_t i = 0, n = results_n; i < n; ++i) 438 | t->results.push_back((stype_t*)cin->read_int32()); 439 | ctx->ttracker->push_back(t); 440 | stypes.push_back(t); 441 | /* 442 | printf("func type :: varargs: %d, args:\n", pb_functype.varargs()); 443 | for (size_t i = 0, n = pb_functype.arg_size(); i < n; ++i) 444 | printf("\targ :: type: %d\n", pb_functype.arg(i)); 445 | printf("results:\n"); 446 | for (size_t i = 0, n = pb_functype.result_size(); i < n; ++i) 447 | printf("\tresult :: type: %d\n", pb_functype.result(i)); 448 | */ 449 | } 450 | 451 | void brawl_deserializer_t::deserialize_stypes(FILE_reader_t *cin, size_t n) 452 | { 453 | for (size_t i = 0; i < n; ++i) { 454 | uint16_t type = cin->read_uint16(); 455 | if (type == 0xFFFF) { 456 | cur_prefix = cin->read_string(); 457 | type = cin->read_uint16(); 458 | } 459 | //printf("%d-------------------------------------\n", i); 460 | 461 | if (type & STYPE_NAMED) 462 | deserialize_named(cin); 463 | else if (type & STYPE_POINTER) 464 | deserialize_pointer(cin); 465 | else if (type & STYPE_ARRAY) 466 | deserialize_array(cin); 467 | else if (type & (STYPE_STRUCT | STYPE_UNION)) 468 | deserialize_struct(cin); 469 | else if (type & STYPE_FUNC) 470 | deserialize_func(cin); 471 | else 472 | KRAWL_QASSERT(!"bad type"); 473 | 474 | stypes.back()->type = type; 475 | } 476 | } 477 | 478 | void brawl_deserializer_t::restore_stype_pointers() 479 | { 480 | for (size_t i = 0, n = stypes.size(); i < n; ++i) { 481 | stype_t *st = stypes[i]; 482 | if (IS_STYPE_NAMED(st)) { 483 | named_stype_t *t = st->as_named(); 484 | if (!t->restored) { 485 | t->real = index_stype((int64_t)t->real); 486 | t->restored = true; 487 | } 488 | } else if (IS_STYPE_POINTER(st)) { 489 | pointer_stype_t *t = st->as_pointer(); 490 | t->points_to = index_stype((int64_t)t->points_to); 491 | } else if (IS_STYPE_ARRAY(st)) { 492 | array_stype_t *t = st->as_array(); 493 | t->elem = index_stype((int64_t)t->elem); 494 | } else if (IS_STYPE_STRUCT_OR_UNION(st)) { 495 | struct_stype_t *t = st->as_struct(); 496 | for (size_t i = 0, n = t->fields.size(); i < n; ++i) { 497 | struct_field_t *f = &t->fields[i]; 498 | f->type = index_stype((int64_t)f->type); 499 | } 500 | } else if (IS_STYPE_FUNC(st)) { 501 | func_stype_t *t = st->as_func(); 502 | for (size_t i = 0, n = t->args.size(); i < n; ++i) 503 | t->args[i] = index_stype((int64_t)t->args[i]); 504 | for (size_t i = 0, n = t->results.size(); i < n; ++i) 505 | t->results[i] = index_stype((int64_t)t->results[i]); 506 | } 507 | } 508 | } 509 | 510 | void brawl_deserializer_t::deserialize_sdecls(FILE_reader_t *cin, size_t n) 511 | { 512 | for (size_t i = 0; i < n; ++i) { 513 | uint8_t dtype = cin->read_uint8(); 514 | std::string &name = cin->read_string(); 515 | int32_t type = cin->read_int32(); 516 | 517 | sdecl_t *d = new_sdecl(ctx->dtracker, name.c_str(), 518 | (sdecl_type_t)dtype); 519 | d->stype = (stype_t*)type; 520 | if (dtype == SDECL_CONST) { 521 | uint8_t vtype = cin->read_uint8(); 522 | std::string &value = cin->read_string(); 523 | 524 | const_sdecl_t *cd = (const_sdecl_t*)d; 525 | cd->value = value_t(value.c_str(), (value_type_t)vtype); 526 | } 527 | sdecls.push_back(d); 528 | } 529 | } 530 | 531 | void brawl_deserializer_t::restore_sdecl_pointers() 532 | { 533 | // casting from pointer to int64_t is required for amd64, because in C 534 | // it's ok to truncate implicitly and integer, but it cannot be done 535 | // while converting a pointer to an integer (weird shit) 536 | for (size_t i = 0, n = sdecls.size(); i < n; ++i) 537 | sdecls[i]->stype = index_stype(((int64_t)sdecls[i]->stype)); 538 | } 539 | 540 | void brawl_deserializer_t::deserialize(FILE_reader_t *cin) 541 | { 542 | uint32_t num; 543 | 544 | // module header 545 | prefix = cur_prefix = cin->read_string(); 546 | package = cin->read_string(); 547 | 548 | // sdecls 549 | num = cin->read_uint32(); 550 | deserialize_sdecls(cin, num); 551 | 552 | // stypes 553 | num = cin->read_uint32(); 554 | deserialize_stypes(cin, num); 555 | 556 | // restore pointers 557 | restore_stype_pointers(); 558 | restore_sdecl_pointers(); 559 | } 560 | -------------------------------------------------------------------------------- /compiler/cityhash/city.cc: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file provides CityHash64() and related functions. 24 | // 25 | // It's probably possible to create even faster hash functions by 26 | // writing a program that systematically explores some of the space of 27 | // possible hash functions, by using SIMD instructions, or by 28 | // compromising on hash quality. 29 | 30 | //#include "config.h" 31 | #include "city.h" 32 | 33 | #include 34 | #include // for memcpy and memset 35 | 36 | using namespace std; 37 | 38 | static uint64 UNALIGNED_LOAD64(const char *p) { 39 | uint64 result; 40 | memcpy(&result, p, sizeof(result)); 41 | return result; 42 | } 43 | 44 | static uint32 UNALIGNED_LOAD32(const char *p) { 45 | uint32 result; 46 | memcpy(&result, p, sizeof(result)); 47 | return result; 48 | } 49 | 50 | #if !defined(WORDS_BIGENDIAN) 51 | 52 | #define uint32_in_expected_order(x) (x) 53 | #define uint64_in_expected_order(x) (x) 54 | 55 | #else 56 | 57 | #ifdef _MSC_VER 58 | #include 59 | #define bswap_32(x) _byteswap_ulong(x) 60 | #define bswap_64(x) _byteswap_uint64(x) 61 | 62 | #elif defined(__APPLE__) 63 | // Mac OS X / Darwin features 64 | #include 65 | #define bswap_32(x) OSSwapInt32(x) 66 | #define bswap_64(x) OSSwapInt64(x) 67 | 68 | #else 69 | #include 70 | #endif 71 | 72 | #define uint32_in_expected_order(x) (bswap_32(x)) 73 | #define uint64_in_expected_order(x) (bswap_64(x)) 74 | 75 | #endif // WORDS_BIGENDIAN 76 | 77 | #if !defined(LIKELY) 78 | #if HAVE_BUILTIN_EXPECT 79 | #define LIKELY(x) (__builtin_expect(!!(x), 1)) 80 | #else 81 | #define LIKELY(x) (x) 82 | #endif 83 | #endif 84 | 85 | static uint64 Fetch64(const char *p) { 86 | return uint64_in_expected_order(UNALIGNED_LOAD64(p)); 87 | } 88 | 89 | static uint32 Fetch32(const char *p) { 90 | return uint32_in_expected_order(UNALIGNED_LOAD32(p)); 91 | } 92 | 93 | // Some primes between 2^63 and 2^64 for various uses. 94 | static const uint64 k0 = 0xc3a5c85c97cb3127ULL; 95 | static const uint64 k1 = 0xb492b66fbe98f273ULL; 96 | static const uint64 k2 = 0x9ae16a3b2f90404fULL; 97 | static const uint64 k3 = 0xc949d7c7509e6557ULL; 98 | 99 | // Bitwise right rotate. Normally this will compile to a single 100 | // instruction, especially if the shift is a manifest constant. 101 | static uint64 Rotate(uint64 val, int shift) { 102 | // Avoid shifting by 64: doing so yields an undefined result. 103 | return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); 104 | } 105 | 106 | // Equivalent to Rotate(), but requires the second arg to be non-zero. 107 | // On x86-64, and probably others, it's possible for this to compile 108 | // to a single instruction if both args are already in registers. 109 | static uint64 RotateByAtLeast1(uint64 val, int shift) { 110 | return (val >> shift) | (val << (64 - shift)); 111 | } 112 | 113 | static uint64 ShiftMix(uint64 val) { 114 | return val ^ (val >> 47); 115 | } 116 | 117 | static uint64 HashLen16(uint64 u, uint64 v) { 118 | return Hash128to64(uint128(u, v)); 119 | } 120 | 121 | static uint64 HashLen0to16(const char *s, size_t len) { 122 | if (len > 8) { 123 | uint64 a = Fetch64(s); 124 | uint64 b = Fetch64(s + len - 8); 125 | return HashLen16(a, RotateByAtLeast1(b + len, len)) ^ b; 126 | } 127 | if (len >= 4) { 128 | uint64 a = Fetch32(s); 129 | return HashLen16(len + (a << 3), Fetch32(s + len - 4)); 130 | } 131 | if (len > 0) { 132 | uint8 a = s[0]; 133 | uint8 b = s[len >> 1]; 134 | uint8 c = s[len - 1]; 135 | uint32 y = static_cast(a) + (static_cast(b) << 8); 136 | uint32 z = len + (static_cast(c) << 2); 137 | return ShiftMix(y * k2 ^ z * k3) * k2; 138 | } 139 | return k2; 140 | } 141 | 142 | // This probably works well for 16-byte strings as well, but it may be overkill 143 | // in that case. 144 | static uint64 HashLen17to32(const char *s, size_t len) { 145 | uint64 a = Fetch64(s) * k1; 146 | uint64 b = Fetch64(s + 8); 147 | uint64 c = Fetch64(s + len - 8) * k2; 148 | uint64 d = Fetch64(s + len - 16) * k0; 149 | return HashLen16(Rotate(a - b, 43) + Rotate(c, 30) + d, 150 | a + Rotate(b ^ k3, 20) - c + len); 151 | } 152 | 153 | // Return a 16-byte hash for 48 bytes. Quick and dirty. 154 | // Callers do best to use "random-looking" values for a and b. 155 | static pair WeakHashLen32WithSeeds( 156 | uint64 w, uint64 x, uint64 y, uint64 z, uint64 a, uint64 b) { 157 | a += w; 158 | b = Rotate(b + a + z, 21); 159 | uint64 c = a; 160 | a += x; 161 | a += y; 162 | b += Rotate(a, 44); 163 | return make_pair(a + z, b + c); 164 | } 165 | 166 | // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 167 | static pair WeakHashLen32WithSeeds( 168 | const char* s, uint64 a, uint64 b) { 169 | return WeakHashLen32WithSeeds(Fetch64(s), 170 | Fetch64(s + 8), 171 | Fetch64(s + 16), 172 | Fetch64(s + 24), 173 | a, 174 | b); 175 | } 176 | 177 | // Return an 8-byte hash for 33 to 64 bytes. 178 | static uint64 HashLen33to64(const char *s, size_t len) { 179 | uint64 z = Fetch64(s + 24); 180 | uint64 a = Fetch64(s) + (len + Fetch64(s + len - 16)) * k0; 181 | uint64 b = Rotate(a + z, 52); 182 | uint64 c = Rotate(a, 37); 183 | a += Fetch64(s + 8); 184 | c += Rotate(a, 7); 185 | a += Fetch64(s + 16); 186 | uint64 vf = a + z; 187 | uint64 vs = b + Rotate(a, 31) + c; 188 | a = Fetch64(s + 16) + Fetch64(s + len - 32); 189 | z = Fetch64(s + len - 8); 190 | b = Rotate(a + z, 52); 191 | c = Rotate(a, 37); 192 | a += Fetch64(s + len - 24); 193 | c += Rotate(a, 7); 194 | a += Fetch64(s + len - 16); 195 | uint64 wf = a + z; 196 | uint64 ws = b + Rotate(a, 31) + c; 197 | uint64 r = ShiftMix((vf + ws) * k2 + (wf + vs) * k0); 198 | return ShiftMix(r * k0 + vs) * k2; 199 | } 200 | 201 | uint64 CityHash64(const char *s, size_t len) { 202 | if (len <= 32) { 203 | if (len <= 16) { 204 | return HashLen0to16(s, len); 205 | } else { 206 | return HashLen17to32(s, len); 207 | } 208 | } else if (len <= 64) { 209 | return HashLen33to64(s, len); 210 | } 211 | 212 | // For strings over 64 bytes we hash the end first, and then as we 213 | // loop we keep 56 bytes of state: v, w, x, y, and z. 214 | uint64 x = Fetch64(s); 215 | uint64 y = Fetch64(s + len - 16) ^ k1; 216 | uint64 z = Fetch64(s + len - 56) ^ k0; 217 | pair v = WeakHashLen32WithSeeds(s + len - 64, len, y); 218 | pair w = WeakHashLen32WithSeeds(s + len - 32, len * k1, k0); 219 | z += ShiftMix(v.second) * k1; 220 | x = Rotate(z + x, 39) * k1; 221 | y = Rotate(y, 33) * k1; 222 | 223 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 224 | len = (len - 1) & ~static_cast(63); 225 | do { 226 | x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1; 227 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 228 | x ^= w.second; 229 | y ^= v.first; 230 | z = Rotate(z ^ w.first, 33); 231 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 232 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); 233 | std::swap(z, x); 234 | s += 64; 235 | len -= 64; 236 | } while (len != 0); 237 | return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, 238 | HashLen16(v.second, w.second) + x); 239 | } 240 | 241 | uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) { 242 | return CityHash64WithSeeds(s, len, k2, seed); 243 | } 244 | 245 | uint64 CityHash64WithSeeds(const char *s, size_t len, 246 | uint64 seed0, uint64 seed1) { 247 | return HashLen16(CityHash64(s, len) - seed0, seed1); 248 | } 249 | 250 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 251 | // of any length representable in ssize_t. Based on City and Murmur. 252 | static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { 253 | uint64 a = Uint128Low64(seed); 254 | uint64 b = Uint128High64(seed); 255 | uint64 c = 0; 256 | uint64 d = 0; 257 | ssize_t l = len - 16; 258 | if (l <= 0) { // len <= 16 259 | a = ShiftMix(a * k1) * k1; 260 | c = b * k1 + HashLen0to16(s, len); 261 | d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); 262 | } else { // len > 16 263 | c = HashLen16(Fetch64(s + len - 8) + k1, a); 264 | d = HashLen16(b + len, c + Fetch64(s + len - 16)); 265 | a += d; 266 | do { 267 | a ^= ShiftMix(Fetch64(s) * k1) * k1; 268 | a *= k1; 269 | b ^= a; 270 | c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; 271 | c *= k1; 272 | d ^= c; 273 | s += 16; 274 | l -= 16; 275 | } while (l > 0); 276 | } 277 | a = HashLen16(a, c); 278 | b = HashLen16(d, b); 279 | return uint128(a ^ b, HashLen16(b, a)); 280 | } 281 | 282 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { 283 | if (len < 128) { 284 | return CityMurmur(s, len, seed); 285 | } 286 | 287 | // We expect len >= 128 to be the common case. Keep 56 bytes of state: 288 | // v, w, x, y, and z. 289 | pair v, w; 290 | uint64 x = Uint128Low64(seed); 291 | uint64 y = Uint128High64(seed); 292 | uint64 z = len * k1; 293 | v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); 294 | v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); 295 | w.first = Rotate(y + z, 35) * k1 + x; 296 | w.second = Rotate(x + Fetch64(s + 88), 53) * k1; 297 | 298 | // This is the same inner loop as CityHash64(), manually unrolled. 299 | do { 300 | x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1; 301 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 302 | x ^= w.second; 303 | y ^= v.first; 304 | z = Rotate(z ^ w.first, 33); 305 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 306 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); 307 | std::swap(z, x); 308 | s += 64; 309 | x = Rotate(x + y + v.first + Fetch64(s + 16), 37) * k1; 310 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 311 | x ^= w.second; 312 | y ^= v.first; 313 | z = Rotate(z ^ w.first, 33); 314 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 315 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y); 316 | std::swap(z, x); 317 | s += 64; 318 | len -= 128; 319 | } while (LIKELY(len >= 128)); 320 | y += Rotate(w.first, 37) * k0 + z; 321 | x += Rotate(v.first + z, 49) * k0; 322 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 323 | for (size_t tail_done = 0; tail_done < len; ) { 324 | tail_done += 32; 325 | y = Rotate(y - x, 42) * k0 + v.second; 326 | w.first += Fetch64(s + len - tail_done + 16); 327 | x = Rotate(x, 49) * k0 + w.first; 328 | w.first += v.first; 329 | v = WeakHashLen32WithSeeds(s + len - tail_done, v.first, v.second); 330 | } 331 | // At this point our 48 bytes of state should contain more than 332 | // enough information for a strong 128-bit hash. We use two 333 | // different 48-byte-to-8-byte hashes to get a 16-byte final result. 334 | x = HashLen16(x, v.first); 335 | y = HashLen16(y, w.first); 336 | return uint128(HashLen16(x + v.second, w.second) + y, 337 | HashLen16(x + w.second, y + v.second)); 338 | } 339 | 340 | uint128 CityHash128(const char *s, size_t len) { 341 | if (len >= 16) { 342 | return CityHash128WithSeed(s + 16, 343 | len - 16, 344 | uint128(Fetch64(s) ^ k3, 345 | Fetch64(s + 8))); 346 | } else if (len >= 8) { 347 | return CityHash128WithSeed(NULL, 348 | 0, 349 | uint128(Fetch64(s) ^ (len * k0), 350 | Fetch64(s + len - 8) ^ k1)); 351 | } else { 352 | return CityHash128WithSeed(s, len, uint128(k0, k1)); 353 | } 354 | } 355 | 356 | #ifdef __SSE4_2__ 357 | #include 358 | #include 359 | 360 | // Requires len >= 240. 361 | static void CityHashCrc256Long(const char *s, size_t len, 362 | uint32 seed, uint64 *result) { 363 | uint64 a = Fetch64(s + 56) + k0; 364 | uint64 b = Fetch64(s + 96) + k0; 365 | uint64 c = result[1] = HashLen16(b, len); 366 | uint64 d = result[2] = Fetch64(s + 120) * k0 + len; 367 | uint64 e = Fetch64(s + 184) + seed; 368 | uint64 f = seed; 369 | uint64 g = 0; 370 | uint64 h = 0; 371 | uint64 i = 0; 372 | uint64 j = 0; 373 | uint64 t = c + d; 374 | 375 | // 240 bytes of input per iter. 376 | size_t iters = len / 240; 377 | len -= iters * 240; 378 | do { 379 | #define CHUNK(multiplier, z) \ 380 | { \ 381 | uint64 old_a = a; \ 382 | a = Rotate(b, 41 ^ z) * multiplier + Fetch64(s); \ 383 | b = Rotate(c, 27 ^ z) * multiplier + Fetch64(s + 8); \ 384 | c = Rotate(d, 41 ^ z) * multiplier + Fetch64(s + 16); \ 385 | d = Rotate(e, 33 ^ z) * multiplier + Fetch64(s + 24); \ 386 | e = Rotate(t, 25 ^ z) * multiplier + Fetch64(s + 32); \ 387 | t = old_a; \ 388 | } \ 389 | f = _mm_crc32_u64(f, a); \ 390 | g = _mm_crc32_u64(g, b); \ 391 | h = _mm_crc32_u64(h, c); \ 392 | i = _mm_crc32_u64(i, d); \ 393 | j = _mm_crc32_u64(j, e); \ 394 | s += 40 395 | 396 | CHUNK(1, 1); CHUNK(k0, 0); 397 | CHUNK(1, 1); CHUNK(k0, 0); 398 | CHUNK(1, 1); CHUNK(k0, 0); 399 | } while (--iters > 0); 400 | j += i << 32; 401 | a = HashLen16(a, j); 402 | h += g << 32; 403 | b = b * k0 + h; 404 | c = HashLen16(c, f) + i; 405 | d = HashLen16(d, e); 406 | pair v(j + e, HashLen16(h, t)); 407 | h = v.second + f; 408 | // If 0 < len < 240, hash chunks of 32 bytes each from the end of s. 409 | for (size_t tail_done = 0; tail_done < len; ) { 410 | tail_done += 32; 411 | c = Rotate(c - a, 42) * k0 + v.second; 412 | d += Fetch64(s + len - tail_done + 16); 413 | a = Rotate(a, 49) * k0 + d; 414 | d += v.first; 415 | v = WeakHashLen32WithSeeds(s + len - tail_done, v.first, v.second); 416 | } 417 | 418 | // Final mix. 419 | e = HashLen16(a, d) + v.first; 420 | f = HashLen16(b, c) + a; 421 | g = HashLen16(v.first, v.second) + c; 422 | result[0] = e + f + g + h; 423 | a = ShiftMix((a + g) * k0) * k0 + b; 424 | result[1] += a + result[0]; 425 | a = ShiftMix(a * k0) * k0 + c; 426 | result[2] += a + result[1]; 427 | a = ShiftMix((a + e) * k0) * k0; 428 | result[3] = a + result[2]; 429 | } 430 | 431 | // Requires len < 240. 432 | static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) { 433 | char buf[240]; 434 | memcpy(buf, s, len); 435 | memset(buf + len, 0, 240 - len); 436 | CityHashCrc256Long(buf, 240, ~static_cast(len), result); 437 | } 438 | 439 | void CityHashCrc256(const char *s, size_t len, uint64 *result) { 440 | if (LIKELY(len >= 240)) { 441 | CityHashCrc256Long(s, len, 0, result); 442 | } else { 443 | CityHashCrc256Short(s, len, result); 444 | } 445 | } 446 | 447 | uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { 448 | if (len <= 900) { 449 | return CityHash128WithSeed(s, len, seed); 450 | } else { 451 | uint64 result[4]; 452 | CityHashCrc256(s, len, result); 453 | uint64 u = Uint128High64(seed) + result[0]; 454 | uint64 v = Uint128Low64(seed) + result[1]; 455 | return uint128(HashLen16(u, v + result[2]), 456 | HashLen16(Rotate(v, 32), u * k0 + result[3])); 457 | } 458 | } 459 | 460 | uint128 CityHashCrc128(const char *s, size_t len) { 461 | if (len <= 900) { 462 | return CityHash128(s, len); 463 | } else { 464 | uint64 result[4]; 465 | CityHashCrc256(s, len, result); 466 | return uint128(result[2], result[3]); 467 | } 468 | } 469 | 470 | #endif 471 | -------------------------------------------------------------------------------- /compiler/cityhash/city.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2011 Google, Inc. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | // 21 | // CityHash, by Geoff Pike and Jyrki Alakuijala 22 | // 23 | // This file provides a few functions for hashing strings. On x86-64 24 | // hardware in 2011, CityHash64() is faster than other high-quality 25 | // hash functions, such as Murmur. This is largely due to higher 26 | // instruction-level parallelism. CityHash64() and CityHash128() also perform 27 | // well on hash-quality tests. 28 | // 29 | // CityHash128() is optimized for relatively long strings and returns 30 | // a 128-bit hash. For strings more than about 2000 bytes it can be 31 | // faster than CityHash64(). 32 | // 33 | // Functions in the CityHash family are not suitable for cryptography. 34 | // 35 | // WARNING: This code has not been tested on big-endian platforms! 36 | // It is known to work well on little-endian platforms that have a small penalty 37 | // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. 38 | // 39 | // By the way, for some hash functions, given strings a and b, the hash 40 | // of a+b is easily derived from the hashes of a and b. This property 41 | // doesn't hold for any hash functions in this file. 42 | 43 | #ifndef CITY_HASH_H_ 44 | #define CITY_HASH_H_ 45 | 46 | #include // for size_t. 47 | #include 48 | #include 49 | 50 | typedef uint8_t uint8; 51 | typedef uint32_t uint32; 52 | typedef uint64_t uint64; 53 | typedef std::pair uint128; 54 | 55 | inline uint64 Uint128Low64(const uint128& x) { return x.first; } 56 | inline uint64 Uint128High64(const uint128& x) { return x.second; } 57 | 58 | // Hash function for a byte array. 59 | uint64 CityHash64(const char *buf, size_t len); 60 | 61 | // Hash function for a byte array. For convenience, a 64-bit seed is also 62 | // hashed into the result. 63 | uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); 64 | 65 | // Hash function for a byte array. For convenience, two seeds are also 66 | // hashed into the result. 67 | uint64 CityHash64WithSeeds(const char *buf, size_t len, 68 | uint64 seed0, uint64 seed1); 69 | 70 | // Hash function for a byte array. 71 | uint128 CityHash128(const char *s, size_t len); 72 | 73 | // Hash function for a byte array. For convenience, a 128-bit seed is also 74 | // hashed into the result. 75 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); 76 | 77 | // Hash 128 input bits down to 64 bits of output. 78 | // This is intended to be a reasonably good hash function. 79 | inline uint64 Hash128to64(const uint128& x) { 80 | // Murmur-inspired hashing. 81 | const uint64 kMul = 0x9ddfea08eb382d69ULL; 82 | uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 83 | a ^= (a >> 47); 84 | uint64 b = (Uint128High64(x) ^ a) * kMul; 85 | b ^= (b >> 47); 86 | b *= kMul; 87 | return b; 88 | } 89 | 90 | #endif // CITY_HASH_H_ 91 | -------------------------------------------------------------------------------- /compiler/compat/compat_amd64.cpp: -------------------------------------------------------------------------------- 1 | #include "../krawl.hpp" 2 | 3 | size_t pointer_size() 4 | { 5 | return 64; 6 | } 7 | 8 | stype_t *va_list_structure() 9 | { 10 | return new array_stype_t(builtin_stypes[BUILTIN_VOID], 24); 11 | } 12 | -------------------------------------------------------------------------------- /compiler/compat/compat_x86.cpp: -------------------------------------------------------------------------------- 1 | #include "../krawl.hpp" 2 | 3 | size_t pointer_size() 4 | { 5 | return 32; 6 | } 7 | 8 | stype_t *va_list_structure() 9 | { 10 | return new pointer_stype_t(builtin_stypes[BUILTIN_VOID]); 11 | } 12 | -------------------------------------------------------------------------------- /compiler/ctokrawl.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "cityhash/city.h" 9 | 10 | struct module_cache_entry_t { 11 | std::string filename; 12 | uint64_t mtime; 13 | }; 14 | 15 | struct module_cache_t { 16 | std::string header; 17 | std::vector entries; 18 | }; 19 | 20 | static std::string get_xdg_cache_dir() 21 | { 22 | char *xdg_cache_home = getenv("XDG_CACHE_HOME"); 23 | if (xdg_cache_home) 24 | return xdg_cache_home; 25 | 26 | char *home = getenv("HOME"); 27 | if (!home) 28 | DIE("Missing HOME environment variable :-\\"); 29 | 30 | std::string default_dir = home; 31 | default_dir += "/.cache"; 32 | return default_dir; 33 | } 34 | 35 | static std::string cityhash64str(const char *data) 36 | { 37 | char buf[12] = {0}; 38 | uint64_t hash = CityHash64(data, strlen(data)); 39 | base62_encode64(hash, buf); 40 | return buf; 41 | } 42 | 43 | static uint64_t mtime(const char *file) 44 | { 45 | struct stat st; 46 | if (-1 == stat(file, &st)) 47 | return 0; 48 | 49 | return st.st_mtime; 50 | } 51 | 52 | static bool is_valid_cache(const char *file, const char *header) 53 | { 54 | FILE *f = fopen(file, "rb"); 55 | if (!f) 56 | return false; 57 | 58 | FILE_reader_t fr(f); 59 | 60 | // oops, hash collision 61 | if (fr.read_string() != header) { 62 | fclose(f); 63 | return false; 64 | } 65 | 66 | size_t entries_n = fr.read_varuint(); 67 | for (size_t i = 0, n = entries_n; i < n; ++i) { 68 | std::string &filename = fr.read_string(); 69 | uint64_t now = mtime(filename.c_str()); 70 | if (now != fr.read_uint64()) { 71 | fclose(f); 72 | return false; 73 | } 74 | } 75 | fclose(f); 76 | return true; 77 | } 78 | 79 | static std::string dump_to_tempfile(const char *data) 80 | { 81 | int fd; 82 | llvm::SmallVector tmpfilename; 83 | llvm::sys::fs::unique_file("header-%%%%%%.h", fd, tmpfilename); 84 | tmpfilename.push_back(0); 85 | 86 | FILE *tmp = fdopen(fd, "w"); 87 | if (!tmp) 88 | DIE("failed to open file descriptor returned by unique_file: %s", 89 | &tmpfilename[0]); 90 | 91 | fprintf(tmp, "%s", data); 92 | fclose(tmp); 93 | 94 | return std::string(&tmpfilename[0]); 95 | } 96 | 97 | static void exec_and_capture_stdout(const char *cmd, std::vector *out) 98 | { 99 | FILE *cc = popen(cmd, "r"); 100 | if (!cc) 101 | DIE("failed to execute a command: %s", cmd); 102 | 103 | if (!read_FILE(out, cc)) 104 | DIE("failed to read command's output: %s", cmd); 105 | 106 | int ok = pclose(cc); 107 | if (ok != 0) 108 | DIE("clang exited with non-zero status: %d", ok); 109 | } 110 | 111 | static void prepare_module_cache(module_cache_t *mc, const char *header, 112 | llvm::SmallVector &deps) 113 | { 114 | mc->header = header; 115 | for (size_t i = 0, n = deps.size(); i < n; ++i) { 116 | if (deps[i].empty()) 117 | continue; 118 | std::string dep = deps[i].str(); 119 | module_cache_entry_t entry = {dep, mtime(dep.c_str())}; 120 | mc->entries.push_back(entry); 121 | } 122 | } 123 | 124 | static void generate_lib(const char *filename, 125 | std::vector *pkgdecls, 126 | const char *prefix, const char *package) 127 | { 128 | FILE *f = fopen(filename, "wb"); 129 | if (!f) { 130 | fprintf(stderr, "Failed opening file for writing: %s\n", filename); 131 | exit(1); 132 | } 133 | { 134 | FILE_writer_t cout(f); 135 | brawl_serializer_t s; 136 | s.serialize(&cout, pkgdecls, prefix, package); 137 | } 138 | fclose(f); 139 | } 140 | 141 | static void write_cache(module_cache_t *mc, const char *cachefile) 142 | { 143 | FILE *f = fopen(cachefile, "wb"); 144 | if (!f) 145 | DIE("failed to open cache file for writing: %s", cachefile); 146 | 147 | FILE_writer_t fw(f); 148 | 149 | fw.write_string(mc->header); 150 | fw.write_varuint(mc->entries.size()); 151 | for (size_t i = 0, n = mc->entries.size(); i < n; ++i) { 152 | fw.write_string(mc->entries[i].filename); 153 | fw.write_uint64(mc->entries[i].mtime); 154 | } 155 | 156 | fclose(f); 157 | } 158 | 159 | struct mini_all_t { 160 | sdecl_tracker_t dtracker; 161 | stype_tracker_t ttracker; 162 | scope_block_tracker_t stracker; 163 | 164 | source_group_t srcinfo; 165 | scope_block_t globalscope; 166 | scope_block_t pkgscope; 167 | std::vector pkgdecls; 168 | diagnostic_t diag; 169 | node_t *ast; 170 | 171 | import_context_t ictx; 172 | 173 | mini_all_t(): ast(0) 174 | { 175 | fill_global_scope(&globalscope, &dtracker, &ttracker); 176 | pkgscope.parent = &globalscope; 177 | ictx.ttracker = &ttracker; 178 | ictx.dtracker = &dtracker; 179 | } 180 | 181 | ~mini_all_t() 182 | { 183 | free_tracked_stypes(&ttracker); 184 | free_tracked_sdecls(&dtracker); 185 | free_tracked_scope_blocks(&stracker); 186 | delete ast; 187 | } 188 | }; 189 | 190 | std::string update_c_module_hash(const char *header, const char *clang_path, 191 | const char *clang_plugin_path) 192 | { 193 | // compute paths 194 | std::string cachedir = get_xdg_cache_dir() + "/krawl"; 195 | std::string cachefile = cachedir + "/" + cityhash64str(header); 196 | std::string brlfile = cachefile + ".brl"; 197 | 198 | // if the cache is valid, we're done here 199 | if (is_valid_cache(cachefile.c_str(), header)) 200 | return brlfile; 201 | 202 | // dump an includer file to a temporary file 203 | std::string includer; 204 | cppsprintf(&includer, "#include \"%s\"\n", header); 205 | 206 | std::string headerfile = dump_to_tempfile(includer.c_str()); 207 | 208 | std::string plugin_ext; 209 | std::string hostTripple = llvm::sys::getHostTriple(); 210 | if (hostTripple.find("linux") != std::string::npos) { 211 | plugin_ext = "so"; 212 | } else if (hostTripple.find("darwin") != std::string::npos) { 213 | plugin_ext = "dylib"; 214 | } else { 215 | DIE("FUUU"); 216 | } 217 | 218 | // execute clang plugin and grab its output 219 | std::string p_clang = "clang"; 220 | if (clang_path) 221 | p_clang = clang_path; 222 | std::string p_clang_plugin; 223 | cppsprintf(&p_clang_plugin, 224 | KRAWL_INSTALL_PREFIX "/lib/krawl/libctokrawl.%s", 225 | plugin_ext.c_str()); 226 | if (clang_plugin_path) 227 | p_clang_plugin = clang_plugin_path; 228 | 229 | std::string cmd; 230 | cppsprintf(&cmd, "%s -cc1 -load %s -plugin c-to-krawl -x c %s", 231 | p_clang.c_str(), p_clang_plugin.c_str(), headerfile.c_str()); 232 | 233 | std::vector output; 234 | exec_and_capture_stdout(cmd.c_str(), &output); 235 | 236 | // split output, module info + headers list 237 | llvm::StringRef output_str(&output[0], output.size()); 238 | std::pair splitted; 239 | splitted = output_str.split("-*-"); 240 | 241 | // prepare module cache entry 242 | llvm::SmallVector deps; 243 | splitted.second.split(deps, "\n"); 244 | 245 | module_cache_t mc; 246 | prepare_module_cache(&mc, header, deps); 247 | 248 | // prepare input for parser 249 | output.resize(splitted.first.size()); 250 | 251 | // parse 252 | mini_all_t d; 253 | parser_t p(&d.srcinfo, &d.diag); 254 | p.set_input(header, &output); 255 | d.ast = p.parse(); 256 | 257 | if (!d.diag.empty()) { 258 | d.diag.print_to_stderr(&d.srcinfo); 259 | DIE("failed to parse C imported module"); 260 | } 261 | 262 | // PASS 1 263 | pass1_opts_t p1opts = { 264 | &d.stracker, 265 | &d.dtracker, 266 | &d.pkgscope, 267 | &d.pkgdecls, 268 | &d.diag, 269 | &d.ictx 270 | }; 271 | pass1_t p1(&p1opts); 272 | p1.pass(d.ast); 273 | 274 | // PASS 2 275 | pass2_opts_t p2opts = { 276 | "", 277 | &d.stracker, 278 | &d.ttracker, 279 | &d.dtracker, 280 | &d.pkgscope, 281 | &d.diag, 282 | }; 283 | pass2_t p2(&p2opts); 284 | p2.pass(&d.pkgdecls); 285 | 286 | if (!d.diag.empty()) { 287 | d.diag.print_to_stderr(&d.srcinfo); 288 | DIE("failed to parse C imported module"); 289 | } 290 | 291 | // write library 292 | bool existed_unused; 293 | llvm::sys::fs::create_directories(cachedir, existed_unused); 294 | generate_lib(brlfile.c_str(), &d.pkgdecls, "", 295 | llvm::sys::path::stem(header).str().c_str()); 296 | 297 | // write cache entry 298 | write_cache(&mc, cachefile.c_str()); 299 | llvm::sys::fs::remove(headerfile, existed_unused); 300 | return brlfile; 301 | } 302 | -------------------------------------------------------------------------------- /compiler/gvim.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | GTK2_RC_FILES="/home/nsf/.gtkrc.gvim" gvim *.hpp *.cpp *.rl *.y compat/*.cpp 3 | -------------------------------------------------------------------------------- /compiler/krawl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "krawl.hpp" 4 | #include "cityhash/city.h" 5 | #include 6 | 7 | struct options_t { 8 | // specified from the command line 9 | const char *uid; 10 | const char *hash_uid; 11 | std::string package; 12 | bool print_ast; 13 | std::string out_name; 14 | std::vector files; 15 | std::vector include_dirs; 16 | bool dump; 17 | bool time; 18 | bool deps; 19 | 20 | const char *clang_path; 21 | const char *clang_plugin_path; 22 | 23 | // calculated on the fly 24 | std::string out_lib; 25 | std::string theuid; 26 | 27 | bool is_lib() const { return uid || hash_uid; } 28 | }; 29 | 30 | struct all_t { 31 | // memory trackers for semantic declarations, types and other stuff 32 | sdecl_tracker_t dtracker; 33 | stype_tracker_t ttracker; 34 | scope_block_tracker_t stracker; 35 | 36 | // this structure contains all the information about files 37 | // including their contents 38 | source_group_t srcinfo; 39 | 40 | // global scope for predeclared identifiers 41 | scope_block_t globalscope; 42 | 43 | // package scope, a parent of all file scopes 44 | scope_block_t pkgscope; 45 | std::vector pkgdecls; 46 | 47 | // diagnostic stack and ASTs 48 | diagnostic_t diag; 49 | node_t* ast; 50 | 51 | // import 52 | import_context_t ictx; 53 | 54 | // timers 55 | llvm::Timer t_parse; 56 | llvm::Timer t_pass1; 57 | llvm::Timer t_pass2; 58 | llvm::Timer t_pass3; 59 | llvm::TimerGroup t_group; 60 | 61 | all_t(options_t *opts): ast(0), t_group("Krawl") 62 | { 63 | init_builtin_stypes(); 64 | fill_global_scope(&globalscope, &dtracker, &ttracker); 65 | pkgscope.parent = &globalscope; 66 | ictx.ttracker = &ttracker; 67 | ictx.dtracker = &dtracker; 68 | ictx.include_dirs = &opts->include_dirs; 69 | ictx.clang_path = opts->clang_path; 70 | ictx.clang_plugin_path = opts->clang_plugin_path; 71 | 72 | t_parse.init("Parse", t_group); 73 | t_pass1.init("Pass1", t_group); 74 | t_pass2.init("Pass2", t_group); 75 | t_pass3.init("Pass3", t_group); 76 | } 77 | 78 | ~all_t() 79 | { 80 | free_builtin_stypes(); 81 | free_tracked_stypes(&ttracker); 82 | free_tracked_sdecls(&dtracker); 83 | free_tracked_scope_blocks(&stracker); 84 | delete ast; 85 | } 86 | }; 87 | 88 | static const struct option longopts[] = { 89 | {"uid", required_argument, 0, 'u'}, 90 | {"hash-uid", required_argument, 0, 'U'}, 91 | {"obj-out", required_argument, 0, 'o'}, 92 | {"ast", no_argument, 0, 'a'}, 93 | {"package", required_argument, 0, 'P'}, 94 | {"help", no_argument, 0, 'h'}, 95 | {"version", no_argument, 0, 'v'}, 96 | {"dump", no_argument, 0, 'D'}, 97 | {"time", no_argument, 0, 't'}, 98 | {"deps", no_argument, 0, 'm'}, 99 | {"clang", required_argument, 0, 'c'}, 100 | {"clang-plugin", required_argument, 0, 'C'}, 101 | {0, 0, 0, 0} 102 | }; 103 | 104 | static void print_help_and_exit(const char *app) 105 | { 106 | printf("" 107 | "usage: %s [options] \n" 108 | "\n" 109 | " -h, --help print this message and exit\n" 110 | " -v, --version print version and exit\n" 111 | " -u, --uid= unique prefix id for symbols\n" 112 | " -U, --hash-uid= unique hash-based prefix id for symbols\n" 113 | " -P, --package= package name\n" 114 | " -o, --obj-out= object file output destination\n" 115 | " -b, --brl-out= brawl interface file output destination\n" 116 | " -I search path for modules importer\n" 117 | " --ast print AST and exit (output is in the yaml format)\n" 118 | " --dump dump LLVM assembly to stdout during compilation\n" 119 | " --time enable timers\n" 120 | " --deps print source file module dependencies\n" 121 | "\n" 122 | " --clang= clang executable location\n" 123 | " --clang-plugin= clang c-to-krawl plugin location\n" 124 | , app); 125 | exit(0); 126 | } 127 | 128 | static void print_version_and_exit() 129 | { 130 | printf("krawl version 0.1\n"); 131 | exit(0); 132 | } 133 | 134 | static std::string get_module_uid(const char *uid, const char *hash_uid) 135 | { 136 | if (uid) 137 | return uid; 138 | 139 | if (hash_uid) { 140 | char buf[12] = {0}; 141 | uint64_t hash = CityHash64(hash_uid, strlen(hash_uid)); 142 | base62_encode64(hash, buf); 143 | return buf; 144 | } 145 | 146 | return ""; 147 | } 148 | 149 | 150 | static bool parse_options(options_t *opts, int argc, char **argv) 151 | { 152 | opts->uid = 0; 153 | opts->hash_uid = 0; 154 | opts->print_ast = false; 155 | opts->dump = false; 156 | opts->time = false; 157 | opts->deps = false; 158 | opts->clang_path = 0; 159 | opts->clang_plugin_path = 0; 160 | 161 | int c; 162 | while ((c = getopt_long(argc, argv, "vho:b:I:P:u:U:", longopts, 0)) != -1) { 163 | switch (c) { 164 | case 'v': 165 | print_version_and_exit(); 166 | break; 167 | case 'h': 168 | print_help_and_exit(argv[0]); 169 | break; 170 | case 'u': 171 | opts->uid = optarg; 172 | break; 173 | case 'U': 174 | opts->hash_uid = optarg; 175 | break; 176 | case 'P': 177 | opts->package = optarg; 178 | break; 179 | case 'a': 180 | opts->print_ast = true; 181 | break; 182 | case 'o': 183 | opts->out_name = optarg; 184 | break; 185 | case 'b': 186 | opts->out_lib = optarg; 187 | break; 188 | case 'I': 189 | opts->include_dirs.push_back(optarg); 190 | break; 191 | case 'D': 192 | opts->dump = true; 193 | break; 194 | case 't': 195 | opts->time = true; 196 | break; 197 | case 'm': 198 | opts->deps = true; 199 | break; 200 | case 'c': 201 | opts->clang_path = optarg; 202 | break; 203 | case 'C': 204 | opts->clang_plugin_path = optarg; 205 | break; 206 | default: 207 | return false; 208 | } 209 | } 210 | 211 | for (int i = optind; i < argc; ++i) 212 | opts->files.push_back(argv[i]); 213 | 214 | // figure out different names 215 | if (opts->out_name.empty() && !opts->files.empty()) 216 | opts->out_name = replace_extension(opts->files[0], "o"); 217 | if (opts->out_lib.empty()) 218 | opts->out_lib = replace_extension(opts->out_name.c_str(), "brl"); 219 | 220 | // choose UID 221 | opts->theuid = get_module_uid(opts->uid, opts->hash_uid); 222 | 223 | // if package name wasn't provided, try to figure out it 224 | if (opts->package.empty()) { 225 | std::string stem = extract_stem(opts->out_name.c_str()); 226 | for (size_t i = 0, n = stem.size(); i < n; ++i) { 227 | if (i == 0 && isdigit(stem[i])) { 228 | opts->package.append("_"); 229 | opts->package.append(1, stem[i]); 230 | continue; 231 | } 232 | 233 | if (!isalnum(stem[i])) { 234 | opts->package.append("_"); 235 | continue; 236 | } 237 | 238 | opts->package.append(1, stem[i]); 239 | } 240 | } 241 | 242 | return true; 243 | } 244 | 245 | static void generate_lib(const char *filename, 246 | std::vector *pkgdecls, 247 | const char *prefix, const char *package) 248 | { 249 | FILE *f = fopen(filename, "wb"); 250 | if (!f) { 251 | fprintf(stderr, "Failed opening file for writing: %s\n", filename); 252 | exit(1); 253 | } 254 | { 255 | FILE_writer_t cout(f); 256 | brawl_serializer_t s; 257 | s.serialize(&cout, pkgdecls, prefix, package); 258 | } 259 | fclose(f); 260 | } 261 | 262 | static void extract_and_print_deps(std::vector *data) 263 | { 264 | unordered_set modules; 265 | source_group_t srcinfo; 266 | diagnostic_t diag; 267 | 268 | parser_t p(&srcinfo, &diag); 269 | p.set_input("tmp", data); 270 | node_t *ast = p.parse(); 271 | if (!ast) 272 | DIE("failed to parse source file"); 273 | 274 | KRAWL_QASSERT(ast->type == node_t::PROGRAM); 275 | program_t *prog = (program_t*)ast; 276 | for (size_t i = 0, n = prog->decls.size(); i < n; ++i) { 277 | node_t *d = prog->decls[i]; 278 | if (d->type == node_t::IMPORT_DECL) { 279 | import_decl_t *id = (import_decl_t*)d; 280 | for (size_t i = 0, n = id->specs.size(); i < n; ++i) { 281 | import_spec_t *spec = id->specs[i]; 282 | std::string p = spec->path->val.to_string(); 283 | if (modules.find(p) == modules.end()) { 284 | printf("%s\n", p.c_str()); 285 | modules.insert(p); 286 | } 287 | } 288 | } 289 | } 290 | } 291 | 292 | int main(int argc, char **argv) 293 | { 294 | options_t opts; 295 | if (!parse_options(&opts, argc, argv)) 296 | return 1; 297 | 298 | const char *data_name = "stdin"; 299 | std::vector data; 300 | if (!opts.files.empty()) { 301 | if (opts.files.size() > 1) { 302 | fprintf(stderr, "At the moment only one file " 303 | "as input is supported\n"); 304 | return 1; 305 | } 306 | 307 | if (!read_file(&data, opts.files[0])) { 308 | fprintf(stderr, "Failed to read data from file: %s\n", 309 | opts.files[0]); 310 | return 1; 311 | } 312 | data_name = opts.files[0]; 313 | } else if (!read_stdin(&data) || data.empty()) { 314 | fprintf(stderr, "Failed to read data from stdin\n"); 315 | return 1; 316 | } 317 | 318 | data.push_back('\0'); 319 | 320 | if (opts.deps) { 321 | extract_and_print_deps(&data); 322 | return 0; 323 | } 324 | 325 | all_t d(&opts); 326 | 327 | #define START_TIMER(timer) if (opts.time) d.timer.startTimer() 328 | #define STOP_TIMER(timer) if (opts.time) d.timer.stopTimer() 329 | 330 | //=========================================================== PARSING 331 | START_TIMER(t_parse); 332 | parser_t p(&d.srcinfo, &d.diag); 333 | p.set_input(data_name, &data); 334 | d.ast = p.parse(); 335 | 336 | if (opts.print_ast && d.ast) { 337 | print_ast(d.ast); 338 | return 0; 339 | } 340 | 341 | if (!d.diag.empty()) { 342 | d.diag.print_to_stderr(&d.srcinfo); 343 | return 1; 344 | } 345 | STOP_TIMER(t_parse); 346 | 347 | //=========================================================== PASS 1 348 | START_TIMER(t_pass1); 349 | pass1_opts_t p1opts = { 350 | &d.stracker, 351 | &d.dtracker, 352 | &d.pkgscope, 353 | &d.pkgdecls, 354 | &d.diag, 355 | &d.ictx, 356 | }; 357 | pass1_t p1(&p1opts); 358 | p1.pass(d.ast); 359 | STOP_TIMER(t_pass1); 360 | 361 | //=========================================================== PASS 2 362 | START_TIMER(t_pass2); 363 | pass2_opts_t p2opts = { 364 | opts.theuid.c_str(), 365 | &d.stracker, 366 | &d.ttracker, 367 | &d.dtracker, 368 | &d.pkgscope, 369 | &d.diag, 370 | }; 371 | pass2_t p2(&p2opts); 372 | p2.pass(&d.pkgdecls); 373 | 374 | if (!d.diag.empty()) { 375 | d.diag.print_to_stderr(&d.srcinfo); 376 | return 1; 377 | } 378 | 379 | if (opts.is_lib()) 380 | generate_lib(opts.out_lib.c_str(), &d.pkgdecls, 381 | opts.theuid.c_str(), opts.package.c_str()); 382 | STOP_TIMER(t_pass2); 383 | 384 | //=========================================================== PASS 3 385 | START_TIMER(t_pass3); 386 | pass3_opts_t p3opts = { 387 | opts.theuid.c_str(), 388 | &d.pkgscope, 389 | &p2.used_extern_sdecls, 390 | opts.out_name.c_str(), 391 | opts.dump, 392 | opts.time, 393 | }; 394 | pass3_t p3(&p3opts); 395 | p3.pass(&d.pkgdecls); 396 | STOP_TIMER(t_pass3); 397 | 398 | return 0; 399 | } 400 | -------------------------------------------------------------------------------- /compiler/lexer.rl: -------------------------------------------------------------------------------- 1 | // vim: filetype=ragel 2 | 3 | #include "krawl.hpp" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | %%{ 10 | machine krawl; 11 | 12 | action append_oct { append_oct(fpc-2); } 13 | action append_hex2 { append_hex(fpc-1, 2); } 14 | action append_hex4 { append_hex(fpc-3, 4); } 15 | action append_hex8 { append_hex(fpc-7, 8); } 16 | action append_escaped { append_escaped(fc); } 17 | action start_interp { interp.clear(); } 18 | action interp_any { interp.append(1, fc); } 19 | 20 | count_newline = '\n' @{ 21 | if (file) 22 | file->add_line((fpc+1) - &file->data.front()); 23 | line++; 24 | 25 | // for non empty lines 26 | if (last_tline == line-1) { 27 | // automatic semicolon insertion 28 | switch (last_tok) { 29 | case TOK_IDENT: 30 | case TOK_INT: 31 | case TOK_FLOAT: 32 | case TOK_CHAR: 33 | case TOK_STRING: 34 | case TOK_BREAK: 35 | case TOK_CONTINUE: 36 | case TOK_FALLTHROUGH: 37 | case TOK_RETURN: 38 | case TOK_INC: 39 | case TOK_DEC: 40 | case TOK_RPAREN: 41 | case TOK_RSB: 42 | case TOK_RCURLY: 43 | tok_op(TOK_SEMICOLON); 44 | fbreak; 45 | break; 46 | default: 47 | break; 48 | } 49 | } 50 | }; 51 | any_count_newline = any | count_newline; 52 | 53 | decimal_lit = [1-9] digit*; 54 | octal_lit = '0' [0-7]*; 55 | hex_lit = '0x' xdigit+; 56 | int_lit = decimal_lit | octal_lit | hex_lit; 57 | 58 | exponent = [eE] [+\-]? digit+; 59 | float_lit = digit+ '.' digit* exponent? | digit+ exponent | '.' digit+ exponent?; 60 | 61 | # hack for integer type casts '5.(int)' 62 | # TODO: skip whitespaces between '.' and '(' 63 | int_type_cast = digit+ '.' '('; 64 | 65 | # different interpretation variants, all start with '\' 66 | octal_byte_value = '\\' [0-7] {3} @append_oct; 67 | hex_byte_value = '\\x' xdigit {2} @append_hex2; 68 | little_u_value = '\\u' xdigit {4} @append_hex4; 69 | big_u_value = '\\U' xdigit {8} @append_hex8; 70 | escaped_char = '\\' [abfnrtv\\'"] @append_escaped; 71 | 72 | interpreted = octal_byte_value | hex_byte_value | 73 | little_u_value | big_u_value | escaped_char; 74 | 75 | alnum_u = alnum | '_'; 76 | alpha_u = alpha | '_'; 77 | 78 | main := |* 79 | #------------------------------------------------------------------------------ 80 | # keywords 81 | #------------------------------------------------------------------------------ 82 | 83 | 'if' { tok_op(TOK_IF); fbreak; }; 84 | 'else' { tok_op(TOK_ELSE); fbreak; }; 85 | 'for' { tok_op(TOK_FOR); fbreak; }; 86 | 'switch' { tok_op(TOK_SWITCH); fbreak; }; 87 | 'return' { tok_op(TOK_RETURN); fbreak; }; 88 | 'continue' { tok_op(TOK_CONTINUE); fbreak; }; 89 | 'fallthrough' { tok_op(TOK_FALLTHROUGH); fbreak; }; 90 | 'break' { tok_op(TOK_BREAK); fbreak; }; 91 | 'case' { tok_op(TOK_CASE); fbreak; }; 92 | 'default' { tok_op(TOK_DEFAULT); fbreak; }; 93 | 94 | 'func' { tok_op(TOK_FUNC); fbreak; }; 95 | 'type' { tok_op(TOK_TYPE); fbreak; }; 96 | 'import' { tok_op(TOK_IMPORT); fbreak; }; 97 | 'const' { tok_op(TOK_CONST); fbreak; }; 98 | 'var' { tok_op(TOK_VAR); fbreak; }; 99 | 'struct' { tok_op(TOK_STRUCT); fbreak; }; 100 | 'union' { tok_op(TOK_UNION); fbreak; }; 101 | 102 | #------------------------------------------------------------------------------ 103 | # operators 104 | #------------------------------------------------------------------------------ 105 | 106 | '...' { tok_op(TOK_ELLIPSIS); fbreak; }; 107 | 108 | '++' { tok_op(TOK_INC); fbreak; }; 109 | '--' { tok_op(TOK_DEC); fbreak; }; 110 | ':=' { tok_op(TOK_DECLARIZE); fbreak; }; 111 | 112 | '{' { tok_op(TOK_LCURLY); fbreak; }; 113 | '}' { tok_op(TOK_RCURLY); fbreak; }; 114 | '[' { tok_op(TOK_LSB); fbreak; }; 115 | ']' { tok_op(TOK_RSB); fbreak; }; 116 | '(' { tok_op(TOK_LPAREN); fbreak; }; 117 | ')' { tok_op(TOK_RPAREN); fbreak; }; 118 | ';' { tok_op(TOK_SEMICOLON); fbreak; }; 119 | ':' { tok_op(TOK_COLON); fbreak; }; 120 | '.' { tok_op(TOK_DOT); fbreak; }; 121 | ',' { tok_op(TOK_COMMA); fbreak; }; 122 | '!' { tok_op(TOK_NOT); fbreak; }; 123 | '=' { tok_op(TOK_ASSIGN); fbreak; }; 124 | 125 | '*' { tok_op(TOK_TIMES); fbreak; }; 126 | '/' { tok_op(TOK_DIVIDE); fbreak; }; 127 | '%' { tok_op(TOK_MOD); fbreak; }; 128 | '<<' { tok_op(TOK_SHIFTL); fbreak; }; 129 | '>>' { tok_op(TOK_SHIFTR); fbreak; }; 130 | '&' { tok_op(TOK_AND); fbreak; }; 131 | '&^' { tok_op(TOK_ANDNOT); fbreak; }; 132 | 133 | '+' { tok_op(TOK_PLUS); fbreak; }; 134 | '-' { tok_op(TOK_MINUS); fbreak; }; 135 | '|' { tok_op(TOK_OR); fbreak; }; 136 | '^' { tok_op(TOK_XOR); fbreak; }; 137 | 138 | '*=' { tok_op(TOK_A_TIMES); fbreak; }; 139 | '/=' { tok_op(TOK_A_DIVIDE); fbreak; }; 140 | '%=' { tok_op(TOK_A_MOD); fbreak; }; 141 | '<<=' { tok_op(TOK_A_SHIFTL); fbreak; }; 142 | '>>=' { tok_op(TOK_A_SHIFTR); fbreak; }; 143 | '&=' { tok_op(TOK_A_AND); fbreak; }; 144 | '&^=' { tok_op(TOK_A_ANDNOT); fbreak; }; 145 | 146 | '+=' { tok_op(TOK_A_PLUS); fbreak; }; 147 | '-=' { tok_op(TOK_A_MINUS); fbreak; }; 148 | '|=' { tok_op(TOK_A_OR); fbreak; }; 149 | '^=' { tok_op(TOK_A_XOR); fbreak; }; 150 | 151 | '==' { tok_op(TOK_EQ); fbreak; }; 152 | '!=' { tok_op(TOK_NEQ); fbreak; }; 153 | '<=' { tok_op(TOK_LE); fbreak; }; 154 | '>=' { tok_op(TOK_GE); fbreak; }; 155 | '<' { tok_op(TOK_LT); fbreak; }; 156 | '>' { tok_op(TOK_GT); fbreak; }; 157 | 158 | '&&' { tok_op(TOK_ANDAND); fbreak; }; 159 | 160 | '||' { tok_op(TOK_OROR); fbreak; }; 161 | 162 | #------------------------------------------------------------------------------ 163 | # comments, whitespaces 164 | #------------------------------------------------------------------------------ 165 | 166 | # skip spaces 167 | any_count_newline - 0x21..0x7E; 168 | 169 | # skip C++ comments 170 | '//' [^\n]* count_newline; 171 | 172 | # skip C comments 173 | '/*' any_count_newline* :>> '*/'; 174 | 175 | #------------------------------------------------------------------------------ 176 | # int literals (decimal, octal, hex) 177 | #------------------------------------------------------------------------------ 178 | 179 | int_lit { tok_int(ts, te); fbreak; }; 180 | 181 | #------------------------------------------------------------------------------ 182 | # float literal 183 | #------------------------------------------------------------------------------ 184 | 185 | float_lit { tok_float(ts, te); fbreak; }; 186 | 187 | # hack for integer type casts '5.(int)' 188 | int_type_cast { 189 | tok_int(ts, te-2); 190 | tok_op_pos(TOK_DOT, te-2, te-1); 191 | tok_op_pos(TOK_LPAREN, te-1, te); 192 | fbreak; 193 | }; 194 | 195 | #------------------------------------------------------------------------------ 196 | # raw string, string and char literal 197 | #------------------------------------------------------------------------------ 198 | 199 | # raw strings are quite simple, no interpretation 200 | '`' any_count_newline* :>> '`' { 201 | beg = ts; end = te; 202 | interp.assign(ts+1, (te-ts)-2); 203 | tok_string(TOK_STRING); 204 | fbreak; 205 | }; 206 | 207 | # ordinary string uses string interpreter 208 | '"' @start_interp { beg = fpc; fgoto string_interp; }; 209 | 210 | # char 211 | "'" @start_interp ([^'\\\n] @interp_any | interpreted) "'" { 212 | beg = ts; end = te; 213 | tok_string(TOK_CHAR); 214 | fbreak; 215 | }; 216 | 217 | #------------------------------------------------------------------------------ 218 | # identifier 219 | #------------------------------------------------------------------------------ 220 | 221 | alpha_u alnum_u* { 222 | beg = ts; end = te; 223 | interp.assign(ts, te-ts); 224 | tok_string(TOK_IDENT); 225 | fbreak; 226 | }; 227 | *|; 228 | 229 | #------------------------------------------------------------------------------ 230 | # String interpreter (I just need to use longest-match here with backtracking) 231 | # 232 | # In case if there is an error in escape sequence, like: \u123k, the scanner 233 | # will parse it as an ordinary string without performing escape procedure. 234 | # Simply handy to use it as a bullet-proof string interpreter. 235 | #------------------------------------------------------------------------------ 236 | string_interp := |* 237 | octal_byte_value; 238 | hex_byte_value; 239 | little_u_value; 240 | big_u_value; 241 | escaped_char; 242 | [^"\n] => interp_any; 243 | '"' => { 244 | end = te; 245 | tok_string(TOK_STRING); 246 | fnext main; 247 | fbreak; 248 | }; 249 | *|; 250 | }%% 251 | 252 | %% write data; 253 | 254 | static uint32_t parse_hex(const char *p, size_t len) 255 | { 256 | uint32_t out = 0; 257 | while (len--) { 258 | char c = *p; 259 | unsigned char n = 0; 260 | if (c >= '0' && c <= '9') 261 | n = c - '0'; 262 | else if (c >= 'a' && c <= 'f') 263 | n = 10 + (c - 'a'); 264 | else if (c >= 'A' && c <= 'F') 265 | n = 10 + (c - 'A'); 266 | out = (out << 4) | n; 267 | p++; 268 | } 269 | return out; 270 | } 271 | 272 | static uint32_t parse_oct(const char *p) 273 | { 274 | uint32_t a, b, c; 275 | a = p[0] - '0'; 276 | b = p[1] - '0'; 277 | c = p[2] - '0'; 278 | return (a << 6) + (b << 3) + c; 279 | } 280 | 281 | //------------------------------------------------------------------------------ 282 | 283 | void lexer_t::set_input(source_file_t *f) 284 | { 285 | beg = 0; 286 | end = 0; 287 | file = f; 288 | p = &file->data.front(); 289 | pe = &file->data.back(); 290 | eof = pe; 291 | line = 1; 292 | last_tok = 0; 293 | last_tline = 0; 294 | 295 | %% write init; 296 | } 297 | 298 | token_t *lexer_t::next_token() 299 | { 300 | token_t *n = 0; 301 | if (!next.empty()) { 302 | n = next.back(); 303 | next.pop_back(); 304 | return n; 305 | } 306 | 307 | %% write exec; 308 | 309 | if (!next.empty()) { 310 | n = next.back(); 311 | next.pop_back(); 312 | } 313 | return n; 314 | } 315 | 316 | // interp functions 317 | void lexer_t::append_hex(const char *p, size_t len) 318 | { 319 | char utfbuf[8]; 320 | uint32_t hex = parse_hex(p, len); 321 | len = unicode_to_utf8(utfbuf, hex); 322 | interp.append(utfbuf, len); 323 | } 324 | 325 | void lexer_t::append_oct(const char *p) 326 | { 327 | char utfbuf[8]; 328 | uint32_t oct = parse_oct(p); 329 | size_t len = unicode_to_utf8(utfbuf, oct); 330 | interp.append(utfbuf, len); 331 | } 332 | 333 | void lexer_t::append_escaped(const char c) 334 | { 335 | switch (c) { 336 | case '"': interp.append(1, '"'); break; 337 | case 'a': interp.append(1, '\a'); break; 338 | case 'b': interp.append(1, '\b'); break; 339 | case 'f': interp.append(1, '\f'); break; 340 | case 'n': interp.append(1, '\n'); break; 341 | case 'r': interp.append(1, '\r'); break; 342 | case 't': interp.append(1, '\t'); break; 343 | case 'v': interp.append(1, '\v'); break; 344 | case '\\': interp.append(1, '\\'); break; 345 | case '\'': interp.append(1, '\''); break; 346 | } 347 | } 348 | 349 | void lexer_t::tok_int(char *b, char *e) 350 | { 351 | beg = b; end = e; 352 | interp.assign(b, e-b); 353 | token_t *n = new token_t(interp.c_str(), TOK_INT, 354 | loc(), beg, end); 355 | next.insert(next.begin(), n); 356 | last_tok = n->type; 357 | last_tline = line; 358 | } 359 | 360 | void lexer_t::tok_float(char *b, char *e) 361 | { 362 | beg = b; end = e; 363 | interp.assign(b, e-b); 364 | token_t *n = new token_t(interp.c_str(), TOK_FLOAT, 365 | loc(), beg, end); 366 | next.insert(next.begin(), n); 367 | last_tok = n->type; 368 | last_tline = line; 369 | } 370 | 371 | void lexer_t::tok_string(int type) 372 | { 373 | token_t *n = new token_t(interp.c_str(), type, 374 | loc(), beg, end); 375 | next.insert(next.begin(), n); 376 | last_tok = n->type; 377 | last_tline = line; 378 | } 379 | 380 | void lexer_t::tok_op(int optype) 381 | { 382 | beg = ts; end = te; 383 | token_t *n = new token_t(0, optype, loc(), beg, end); 384 | next.insert(next.begin(), n); 385 | last_tok = n->type; 386 | last_tline = line; 387 | } 388 | 389 | void lexer_t::tok_op_pos(int optype, char *b, char *e) 390 | { 391 | beg = b; end = e; 392 | token_t *n = new token_t(0, optype, loc(), beg, end); 393 | next.insert(next.begin(), n); 394 | last_tok = n->type; 395 | last_tline = line; 396 | } 397 | 398 | source_loc_t lexer_t::loc() 399 | { 400 | source_loc_t loc = beg - &file->data.front(); 401 | if (file) 402 | loc += file->offset; 403 | return loc; 404 | } 405 | -------------------------------------------------------------------------------- /compiler/message.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | #include 3 | 4 | int istty = -1; 5 | 6 | void cppsprintf(std::string *out, const char *fmt, ...) 7 | { 8 | va_list va; 9 | va_start(va, fmt); 10 | cppvsprintf(out, fmt, va); 11 | va_end(va); 12 | } 13 | 14 | void cppvsprintf(std::string *out, const char *fmt, va_list va) 15 | { 16 | va_list va2; 17 | va_copy(va2, va); 18 | 19 | int len = vsnprintf(0, 0, fmt, va); 20 | size_t size = out->size(); 21 | out->resize(size + len); 22 | vsnprintf(&(*out)[size], len+1, fmt, va2); 23 | 24 | va_end(va2); 25 | } 26 | 27 | static bool in_highlight_range(source_loc_range_t *ranges, size_t ranges_n, 28 | source_loc_t offset) 29 | { 30 | for (size_t i = 0; i < ranges_n; ++i) { 31 | source_loc_range_t r = ranges[i]; 32 | if (r.beg <= offset && r.end >= offset) 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | void highlight_code(std::string *out, source_file_t *file, source_loc_t tok, 39 | source_loc_range_t *ranges, size_t ranges_n, bool hltok) 40 | { 41 | source_pos_t tokp = file->get_pos_by_loc(tok); 42 | char *input_beg = &file->data.front(); 43 | char *input_end = &file->data.back(); 44 | char *b = input_beg + tokp.line_offset; 45 | char *e = b; 46 | while (*e != '\n' && e != input_end) 47 | e++; 48 | 49 | // append line 50 | out->append(b, e-b); 51 | out->append(1, '\n'); 52 | 53 | // perform highlights 54 | if (istty) 55 | out->append(CONSOLE_GREEN); 56 | while (b < e) { 57 | size_t clen = utf8_char_length(*b); 58 | if (clen == 1 && *b == '\t') 59 | out->append(1, '\t'); 60 | else { 61 | source_loc_t offset = (b - input_beg) + file->offset; 62 | if (hltok && tok == offset) { 63 | if (istty) out->append(CONSOLE_YELLOW); 64 | out->append(1, '^'); 65 | if (istty) out->append(CONSOLE_GREEN); 66 | } else if (in_highlight_range(ranges, ranges_n, offset)) 67 | out->append(1, '~'); 68 | else 69 | out->append(1, ' '); 70 | } 71 | b += clen; 72 | } 73 | if (istty) 74 | out->append(CONSOLE_NOCOL); 75 | } 76 | 77 | static void append_message_header_nocol(std::string *out, message_type_t type, 78 | const char *filename, int line, int col) 79 | { 80 | cppsprintf(out, "%s:%d:%d: ", filename, line, col); 81 | switch (type) { 82 | case MESSAGE_ERROR: 83 | out->append("error: "); 84 | break; 85 | case MESSAGE_WARNING: 86 | out->append("warning: "); 87 | break; 88 | } 89 | } 90 | 91 | static void append_message_header(std::string *out, message_type_t type, 92 | const char *filename, int line, int col) 93 | { 94 | if (!istty) { 95 | append_message_header_nocol(out, type, filename, line, col); 96 | return; 97 | } 98 | cppsprintf(out, CONSOLE_WHITE "%s:%d:%d: " CONSOLE_NOCOL, 99 | filename, line, col); 100 | switch (type) { 101 | case MESSAGE_ERROR: 102 | out->append(CONSOLE_RED "error: " CONSOLE_NOCOL); 103 | break; 104 | case MESSAGE_WARNING: 105 | out->append(CONSOLE_MAGENTA "warning: " CONSOLE_NOCOL); 106 | break; 107 | } 108 | } 109 | 110 | message_t *new_message(message_type_t type, 111 | source_loc_t tok, bool hltok, 112 | source_loc_range_t *ranges, size_t ranges_n, 113 | const char *fmt, ...) 114 | { 115 | message_t *m = new message_t; 116 | m->type = type; 117 | m->location = tok; 118 | m->ranges.assign(ranges, ranges+ranges_n); 119 | m->hltok = hltok; 120 | 121 | va_list va; 122 | va_start(va, fmt); 123 | cppvsprintf(&m->text, fmt, va); 124 | va_end(va); 125 | return m; 126 | } 127 | 128 | void message_t::print_to_stderr(source_group_t *group) 129 | { 130 | // ugly hack, check whether stderr is a tty, disable colors if so 131 | if (istty == -1) 132 | istty = isatty(fileno(stderr)); 133 | source_file_t *f = group->get_file_by_loc(location); 134 | source_pos_t ptok = f->get_pos_by_loc(location); 135 | 136 | std::string out; 137 | append_message_header(&out, type, ptok.filename, ptok.line, 138 | ptok.col(&f->data.front())); 139 | if (istty) 140 | out.append(CONSOLE_WHITE); 141 | out.append(text); 142 | if (istty) 143 | out.append(CONSOLE_NOCOL); 144 | out.append("\n"); 145 | highlight_code(&out, f, location, &ranges[0], ranges.size(), hltok); 146 | fprintf(stderr, "%s\n", out.c_str()); 147 | } 148 | 149 | diagnostic_t::~diagnostic_t() 150 | { 151 | for (size_t i = 0, n = messages.size(); i < n; ++i) 152 | delete messages[i]; 153 | } 154 | 155 | void diagnostic_t::report(message_t *m) 156 | { 157 | messages.push_back(m); 158 | } 159 | 160 | bool diagnostic_t::empty() 161 | { 162 | return messages.empty(); 163 | } 164 | 165 | void diagnostic_t::print_to_stderr(source_group_t *srcinfo) 166 | { 167 | for (size_t i = 0, n = messages.size(); i < n; ++i) 168 | messages[i]->print_to_stderr(srcinfo); 169 | } 170 | -------------------------------------------------------------------------------- /compiler/misc.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | #include 3 | #include 4 | 5 | const char *pretty_print_FILE(const char *file) 6 | { 7 | const char *base = strstr(file, PRETTY_PRINT_FILE_BASE); 8 | return ((base) ? base + strlen(PRETTY_PRINT_FILE_BASE) + 1 : file); 9 | } 10 | 11 | std::string extract_extension(const char *path) 12 | { 13 | const char *dot = strrchr(path, '.'); 14 | if (!dot) 15 | return ""; 16 | 17 | return dot+1; 18 | } 19 | 20 | std::string extract_stem(const char *path) 21 | { 22 | const char *end = strrchr(path, '.'); 23 | if (!end) 24 | end = path + strlen(path); 25 | 26 | const char *beg = strrchr(path, '/'); 27 | if (!beg) 28 | beg = path; 29 | else 30 | beg++; 31 | 32 | return std::string(beg, end-beg); 33 | } 34 | 35 | std::string extract_dir(const char *path) 36 | { 37 | const char *sep = strrchr(path, '/'); 38 | if (sep) 39 | return std::string(path, sep+1 - path); 40 | 41 | return ""; 42 | } 43 | 44 | std::string replace_extension(const char *path, const char *newext) 45 | { 46 | const char *dot = strrchr(path, '.'); 47 | if (!dot) 48 | return path; 49 | 50 | std::string out(path, dot+1-path); 51 | out.append(newext); 52 | return out; 53 | } 54 | 55 | static uint64_t u64pow(uint64_t base, uint64_t exp) 56 | { 57 | uint64_t result = 1; 58 | while (exp) { 59 | if (exp & 1) 60 | result *= base; 61 | exp >>= 1; 62 | base *= base; 63 | } 64 | 65 | return result; 66 | } 67 | 68 | static const char base62_alphabet[] = "0123456789abcdefghijklmnopqrstuvwxyz" 69 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 70 | 71 | void base62_encode64(uint64_t n, char *out) 72 | { 73 | for (size_t i = 0; i < 11; ++i) { 74 | uint64_t rem = n % 62; 75 | n = (n - rem) / 62; 76 | out[10-i] = base62_alphabet[rem]; 77 | } 78 | } 79 | 80 | uint64_t base62_decode64(const char *in) 81 | { 82 | uint64_t val = 0; 83 | for (size_t i = 0; i < 11; ++i) { 84 | uint64_t num = 0; 85 | char c = in[i]; 86 | if (c >= '0' && c <= '9') 87 | num = c - '0'; 88 | else if (c >= 'a' && c <= 'z') 89 | num = c - 'a' + 10; 90 | else if (c >= 'A' && c <= 'Z') 91 | num = c - 'A' + 36; 92 | 93 | val += num * u64pow(62, 10-i); 94 | } 95 | return val; 96 | } 97 | 98 | bool read_stdin(std::vector *out) 99 | { 100 | size_t read_n = 0; 101 | 102 | while (1) { 103 | if (feof(stdin)) 104 | break; 105 | if (ferror(stdin)) 106 | return false; 107 | 108 | out->resize(read_n + 1024); 109 | size_t n = fread(&(*out)[read_n], 1, 1024, stdin); 110 | read_n += n; 111 | // shrink vector in case if we've read less than 1024 112 | if (n != 1024) 113 | out->resize(read_n); 114 | } 115 | return true; 116 | } 117 | 118 | bool read_file(std::vector *out, const char *filename) 119 | { 120 | struct stat st; 121 | FILE *f = fopen(filename, "r"); 122 | if (!f) 123 | return false; 124 | 125 | if (-1 == fstat(fileno(f), &st)) { 126 | fclose(f); 127 | return false; 128 | } 129 | 130 | out->resize(st.st_size); 131 | if ((int)st.st_size != fread(&(*out)[0], 1, st.st_size, f)) { 132 | fclose(f); 133 | return false; 134 | } 135 | 136 | return true; 137 | } 138 | 139 | bool read_FILE(std::vector *out, FILE *f) 140 | { 141 | size_t read_n = 0; 142 | 143 | while (1) { 144 | if (feof(f)) 145 | break; 146 | if (ferror(f)) 147 | return false; 148 | 149 | out->resize(read_n + 1024); 150 | size_t n = fread(&(*out)[read_n], 1, 1024, f); 151 | read_n += n; 152 | // shrink vector in case if we've read less than 1024 153 | if (n != 1024) 154 | out->resize(read_n); 155 | } 156 | return true; 157 | } 158 | -------------------------------------------------------------------------------- /compiler/parser-code.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | #include 3 | 4 | //------------------------------------------------------------------------------ 5 | // parser_t 6 | //------------------------------------------------------------------------------ 7 | 8 | // lemon parser definitions 9 | void *ParseAlloc(void*(*)(size_t)); 10 | void ParseFree(void*, void(*)(void*)); 11 | void Parse(void*, int, token_t*, parser_t*); 12 | void ParseTrace(FILE*, char*); 13 | 14 | parser_t::parser_t(source_group_t *srcinfo, diagnostic_t *diag): 15 | srcinfo(srcinfo), diag(diag), ast(0) 16 | { 17 | lemon = ParseAlloc(::malloc); 18 | //ParseTrace(stderr, (char*)""); 19 | } 20 | 21 | parser_t::~parser_t() 22 | { 23 | ParseFree(lemon, ::free); 24 | } 25 | 26 | node_t *parser_t::parse() 27 | { 28 | lex.set_input(srcinfo->files.back()); 29 | 30 | while (1) { 31 | token_t *t = lex.next_token(); 32 | if (!t) { 33 | break; 34 | } 35 | last_token = t->type; 36 | last_pos = t->pos; 37 | 38 | Parse(lemon, t->type, t, this); 39 | } 40 | 41 | Parse(lemon, 0, 0, this); 42 | return ast; 43 | } 44 | 45 | void parser_t::set_input(const char *filename, std::vector *buf) 46 | { 47 | srcinfo->add_file(filename, buf); 48 | } 49 | 50 | //------------------------------------------------------------------------------ 51 | // nametype_vector_t to field_vector_t conversion 52 | // 53 | // Basically it's a bunch of weird code with does the following: 54 | // converts nametype form: [a], [b], [c int], [d], [e], [f float] 55 | // to field form: [a, b, c int], [d, e, f float] 56 | // Issuing error messages on the way. 57 | //------------------------------------------------------------------------------ 58 | 59 | static field_t *nametype_to_field(nametype_vector_t *accum, 60 | nametype_t *d, 61 | nametype_vector_t *invalid) 62 | { 63 | // At the end of this function, every value from 'accum' and 'd' end up 64 | // somewhere in the 'invalid' buffer and in the newly created field_t. 65 | nametype_vector_t::iterator it, end; 66 | ident_expr_vector_t v; 67 | 68 | for (it = accum->begin(), end = accum->end(); it != end; ++it) { 69 | if (it->type->type == node_t::IDENT_EXPR) 70 | v.push_back((ident_expr_t*)it->type); 71 | else 72 | invalid->push_back(*it); 73 | } 74 | 75 | // at least one correct argument always exists (syntax forces that) 76 | v.push_back(d->name); 77 | 78 | field_t *f = new field_t(0, d->type); 79 | f->names.swap(v); 80 | return f; 81 | } 82 | 83 | field_vector_t *nametypev_to_fieldv(diagnostic_t *diag, nametype_vector_t *nv, 84 | bool ellipsis) 85 | { 86 | nametype_vector_t invalid_names; 87 | nametype_vector_t accum; 88 | field_vector_t *fv = new field_vector_t; 89 | nametype_vector_t::iterator it, end; 90 | 91 | for (it = nv->begin(), end = nv->end(); it != end; ++it) { 92 | switch (it->classify()) { 93 | case nametype_t::SINGLE: 94 | accum.push_back(*it); 95 | break; 96 | case nametype_t::DOUBLE: 97 | field_t *f = nametype_to_field(&accum, &*it, &invalid_names); 98 | fv->push_back(f); 99 | accum.clear(); 100 | break; 101 | } 102 | } 103 | 104 | // If there are some invalid names, issue an error and free that list. 105 | if (!invalid_names.empty()) { 106 | std::vector ranges; 107 | ranges.reserve(invalid_names.size()); 108 | for (size_t i = 0, n = invalid_names.size(); i < n; ++i) { 109 | switch (invalid_names[i].classify()) { 110 | case nametype_t::SINGLE: 111 | ranges.push_back(invalid_names[i].type->source_loc_range()); 112 | break; 113 | case nametype_t::DOUBLE: 114 | ranges.push_back(invalid_names[i].name->source_loc_range()); 115 | break; 116 | } 117 | } 118 | 119 | const char *msg = "bad name for a function argument"; 120 | if (invalid_names.size() > 1) 121 | msg = "bad names for function arguments"; 122 | 123 | message_t *m; 124 | m = new_message(MESSAGE_ERROR, 125 | ranges.front().beg, false, 126 | &ranges[0], ranges.size(), 127 | msg); 128 | diag->report(m); 129 | for (size_t i = 0, n = invalid_names.size(); i < n; ++i) { 130 | switch (invalid_names[i].classify()) { 131 | case nametype_t::SINGLE: 132 | delete invalid_names[i].type; 133 | break; 134 | case nametype_t::DOUBLE: 135 | delete invalid_names[i].name; 136 | delete invalid_names[i].type; 137 | break; 138 | } 139 | } 140 | } 141 | 142 | // If 'fv' contains valid fields at this point, it's the case with 143 | // named fields: 144 | // "a, b, c int" 145 | // But if 'accum' is not empty, it means there are names without type, 146 | // issue an error and free the accum buffer. Everything else takes 147 | // place in the 'fv' (trying to leave in AST as much as possible). 148 | if (!accum.empty() && !fv->empty()) { 149 | std::vector ranges; 150 | ranges.reserve(accum.size()); 151 | for (size_t i = 0, n = accum.size(); i < n; ++i) 152 | ranges.push_back(accum[i].type->source_loc_range()); 153 | 154 | message_t *m; 155 | m = new_message(MESSAGE_ERROR, 156 | ranges.back().end, false, 157 | &ranges[0], ranges.size(), 158 | "missing type (or mixing named/unnamed arguments)"); 159 | diag->report(m); 160 | for (size_t i = 0, n = accum.size(); i < n; ++i) 161 | delete accum[i].type; 162 | } 163 | 164 | if (fv->empty()) { 165 | // unnamed type list 166 | for (it = nv->begin(), end = nv->end(); it != end; ++it) 167 | fv->push_back(new field_t(0, it->type)); 168 | } 169 | 170 | if (ellipsis) 171 | fv->push_back(0); 172 | 173 | delete nv; 174 | return fv; 175 | } 176 | 177 | //------------------------------------------------------------------------------ 178 | // additional syntax checks 179 | //------------------------------------------------------------------------------ 180 | 181 | void syntax_check_const_decl(diagnostic_t *diag, const_decl_t *d) 182 | { 183 | // first (or the only) constant spec must have a specified value 184 | value_spec_t *first = d->specs[0]; 185 | if (first->values.empty()) { 186 | std::vector ranges; 187 | ranges.reserve(first->names.size()); 188 | for (size_t i = 0, n = first->names.size(); i < n; ++i) 189 | ranges.push_back(first->names[i]->source_loc_range()); 190 | 191 | const char *msg = "constant specification must have " 192 | "a specified value"; 193 | if (d->specs.size() > 1) 194 | msg = "first constant specification must have " 195 | "a specified value"; 196 | 197 | message_t *m; 198 | m = new_message(MESSAGE_ERROR, 199 | ranges.back().end, false, 200 | &ranges[0], ranges.size(), 201 | msg); 202 | diag->report(m); 203 | } 204 | 205 | // a number of names should match to a number of values 206 | for (size_t i = 0, n = d->specs.size(); i < n; ++i) { 207 | value_spec_t *s = d->specs[i]; 208 | if (s->type && s->values.empty()) { 209 | std::vector ranges; 210 | ranges.reserve(s->names.size() + s->values.size()); 211 | for (size_t i = 0, n = s->names.size(); i < n; ++i) 212 | ranges.push_back(s->names[i]->source_loc_range()); 213 | source_loc_t pos = s->type->source_loc_range().beg; 214 | message_t *m; 215 | m = new_message(MESSAGE_ERROR, 216 | pos, true, 217 | &ranges[0], ranges.size(), 218 | "constant declaration cannot have type " 219 | "without initializer expression"); 220 | diag->report(m); 221 | } 222 | if (!s->values.empty() && s->names.size() != s->values.size()) { 223 | std::vector ranges; 224 | ranges.reserve(s->names.size() + s->values.size()); 225 | for (size_t i = 0, n = s->names.size(); i < n; ++i) 226 | ranges.push_back(s->names[i]->source_loc_range()); 227 | for (size_t i = 0, n = s->values.size(); i < n; ++i) 228 | ranges.push_back(s->values[i]->source_loc_range()); 229 | 230 | message_t *m; 231 | m = new_message(MESSAGE_ERROR, 232 | s->pos, true, 233 | &ranges[0], ranges.size(), 234 | "a number of names should match to a " 235 | "number of values"); 236 | diag->report(m); 237 | } 238 | } 239 | } 240 | 241 | void syntax_check_var_decl(diagnostic_t *diag, var_decl_t *d) 242 | { 243 | for (size_t i = 0, n = d->specs.size(); i < n; ++i) { 244 | value_spec_t *s = d->specs[i]; 245 | if (s->values.empty() && s->type == 0) { 246 | std::vector ranges; 247 | ranges.reserve(s->names.size()); 248 | for (size_t i = 0, n = s->names.size(); i < n; ++i) 249 | ranges.push_back(s->names[i]->source_loc_range()); 250 | 251 | message_t *m; 252 | m = new_message(MESSAGE_ERROR, 253 | ranges.front().beg, false, 254 | &ranges[0], ranges.size(), 255 | "type is missing in value specification " 256 | "and cannot be inferred"); 257 | diag->report(m); 258 | } 259 | } 260 | // a number of names should match to a number of values 261 | for (size_t i = 0, n = d->specs.size(); i < n; ++i) { 262 | value_spec_t *s = d->specs[i]; 263 | if (!s->values.empty()) { 264 | if (s->values.size() == 1 && 265 | s->names.size() > 1) 266 | { 267 | if (!is_call_expr(s->values[0])) { 268 | std::vector ranges; 269 | ranges.reserve(s->names.size() + s->values.size()); 270 | for (size_t i = 0, n = s->names.size(); i < n; ++i) 271 | ranges.push_back(s->names[i]->source_loc_range()); 272 | for (size_t i = 0, n = s->values.size(); i < n; ++i) 273 | ranges.push_back(s->values[i]->source_loc_range()); 274 | 275 | message_t *m; 276 | m = new_message(MESSAGE_ERROR, 277 | s->pos, true, 278 | &ranges[0], ranges.size(), 279 | "assignment count mismatch: " 280 | "%d = %d", s->names.size(), 281 | s->values.size()); 282 | diag->report(m); 283 | } 284 | continue; 285 | } 286 | 287 | if (s->values.size() == s->names.size()) 288 | continue; 289 | 290 | std::vector ranges; 291 | ranges.reserve(s->names.size() + s->values.size()); 292 | for (size_t i = 0, n = s->names.size(); i < n; ++i) 293 | ranges.push_back(s->names[i]->source_loc_range()); 294 | for (size_t i = 0, n = s->values.size(); i < n; ++i) 295 | ranges.push_back(s->values[i]->source_loc_range()); 296 | 297 | message_t *m; 298 | m = new_message(MESSAGE_ERROR, 299 | s->pos, true, 300 | &ranges[0], ranges.size(), 301 | "assignment count mismatch: %d = %d", 302 | s->names.size(), 303 | s->values.size()); 304 | diag->report(m); 305 | } 306 | } 307 | } 308 | -------------------------------------------------------------------------------- /compiler/parser.y: -------------------------------------------------------------------------------- 1 | %token_type { token_t* } 2 | %token_destructor { delete $$; } 3 | 4 | %left OROR. 5 | %left ANDAND. 6 | %left EQ NEQ LT LE GT GE. 7 | %left PLUS MINUS OR XOR. 8 | %left DIVIDE TIMES MOD SHIFTL SHIFTR AND ANDNOT. 9 | 10 | %include { 11 | #include "krawl.hpp" 12 | #include 13 | #include 14 | #include 15 | } 16 | 17 | %token_prefix TOK_ 18 | 19 | %extra_argument { parser_t *ctx } 20 | 21 | %syntax_error { 22 | message_t *m; 23 | m = new_message(MESSAGE_ERROR, 24 | ctx->last_pos, true, 0, 0, 25 | "unexpected token: %s", 26 | token_to_string(ctx->last_token)); 27 | ctx->diag->report(m); 28 | } 29 | 30 | %type program { program_t* } 31 | program(P) ::= decl_list(L) osemi. { 32 | P = new program_t(L); 33 | ctx->ast = P; 34 | } 35 | 36 | 37 | 38 | 39 | //------------------------------------------------------------------------------ 40 | // Attrubutes 41 | // 42 | // , , , , 43 | //------------------------------------------------------------------------------ 44 | 45 | %type attr { attribute_t* } 46 | attr(A) ::= IDENT(N) ASSIGN INT(T). { A = new attribute_t(N, T); } 47 | attr(A) ::= IDENT(N) ASSIGN FLOAT(T). { A = new attribute_t(N, T); } 48 | attr(A) ::= IDENT(N) ASSIGN STRING(T). { A = new attribute_t(N, T); } 49 | attr(A) ::= IDENT(N) ASSIGN CHAR(T). { A = new attribute_t(N, T); } 50 | attr(A) ::= IDENT(N) ASSIGN IDENT(T). { A = new attribute_t(N, T); } 51 | attr(A) ::= IDENT(N). { A = new attribute_t(N, 0); } 52 | 53 | %type attr_list { attribute_vector_t* } 54 | attr_list(AL) ::= attr(A). { AL = new attribute_vector_t(1, A); } 55 | attr_list(AL) ::= attr_list(AL2) COMMA attr(A). { AL2->push_back(A); AL = AL2; } 56 | 57 | %type attrs { attributes_t* } 58 | attrs(A) ::= LT attr_list(AL) GT. { A = new attributes_t(AL); } 59 | 60 | %type oattrs { attributes_t* } 61 | oattrs(A) ::= . { A = 0; } 62 | oattrs(A) ::= attrs(AA). { A = AA; } 63 | 64 | 65 | 66 | 67 | //------------------------------------------------------------------------------ 68 | // Statement list 69 | //------------------------------------------------------------------------------ 70 | 71 | %type stmt_list { node_vector_t* } 72 | stmt_list(L) ::= stmt(S). { 73 | L = new node_vector_t; 74 | if (S) L->push_back(S); 75 | } 76 | stmt_list(L) ::= stmt_list(L2) SEMICOLON stmt(S). { 77 | if (S) L2->push_back(S); 78 | L = L2; 79 | } 80 | 81 | 82 | 83 | 84 | //------------------------------------------------------------------------------ 85 | // Simple statement 86 | //------------------------------------------------------------------------------ 87 | 88 | %type s_stmt { node_t* } 89 | //s_stmt(S) ::= . { S = 0; } 90 | s_stmt(S) ::= expr(E). { S = new expr_stmt_t(E); } 91 | s_stmt(S) ::= expr_list(L) ASSIGN(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 92 | s_stmt(S) ::= expr_list(L) DECLARIZE(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 93 | s_stmt(S) ::= expr_list(L) A_DIVIDE(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 94 | s_stmt(S) ::= expr_list(L) A_TIMES(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 95 | s_stmt(S) ::= expr_list(L) A_MOD(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 96 | s_stmt(S) ::= expr_list(L) A_SHIFTL(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 97 | s_stmt(S) ::= expr_list(L) A_SHIFTR(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 98 | s_stmt(S) ::= expr_list(L) A_AND(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 99 | s_stmt(S) ::= expr_list(L) A_ANDNOT(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 100 | s_stmt(S) ::= expr_list(L) A_PLUS(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 101 | s_stmt(S) ::= expr_list(L) A_MINUS(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 102 | s_stmt(S) ::= expr_list(L) A_OR(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 103 | s_stmt(S) ::= expr_list(L) A_XOR(T) iexpr_list(R). { S = new assign_stmt_t(L, R, T); } 104 | s_stmt(S) ::= expr(E) INC(T). { S = new incdec_stmt_t(E, T); } 105 | s_stmt(S) ::= expr(E) DEC(T). { S = new incdec_stmt_t(E, T); } 106 | 107 | 108 | 109 | 110 | //------------------------------------------------------------------------------ 111 | // Statement 112 | //------------------------------------------------------------------------------ 113 | 114 | %type stmt { node_t* } 115 | stmt(S) ::= s_stmt(SS). { S = SS; } 116 | stmt(S) ::= decl(D). { S = new decl_stmt_t(D); } 117 | stmt(S) ::= block_stmt(BS). { S = BS; } 118 | stmt(S) ::= BREAK(TOK). { S = new flow_stmt_t(TOK); } 119 | stmt(S) ::= CONTINUE(TOK). { S = new flow_stmt_t(TOK); } 120 | stmt(S) ::= FALLTHROUGH(TOK). { S = new flow_stmt_t(TOK); } 121 | stmt(S) ::= RETURN(TOK) iexpr_list(L). { S = new return_stmt_t(L, TOK); } 122 | stmt(S) ::= RETURN(TOK). { S = new return_stmt_t(0, TOK); } 123 | 124 | stmt(S) ::= IF(TI) expr(E) block_stmt(BS). { 125 | S = new ifelse_stmt_t(E, BS, TI); 126 | } 127 | stmt(S) ::= IF(TI) expr(E) block_stmt(BS) ELSE(TE) stmt(ES). { 128 | S = new ifelse_stmt_t(E, BS, TI, TE, ES); 129 | } 130 | stmt(S) ::= FOR(T) s_stmt(I) SEMICOLON oexpr(C) SEMICOLON s_stmt(P) block_stmt(B). { 131 | S = new for_stmt_t(I, C, P, B, T); 132 | } 133 | stmt(S) ::= FOR(T) expr(C) block_stmt(B). { 134 | S = new for_stmt_t(0, C, 0, B, T); 135 | } 136 | stmt(S) ::= FOR(T) block_stmt(B). { 137 | S = new for_stmt_t(0, 0, 0, B, T); 138 | } 139 | stmt(S) ::= SWITCH(T) expr(E) sw_block_stmt(B). { 140 | S = new switch_stmt_t(E, B, T); 141 | } 142 | 143 | 144 | 145 | 146 | //------------------------------------------------------------------------------ 147 | // Switch block statement 148 | //------------------------------------------------------------------------------ 149 | 150 | %type sw_block_stmt { block_stmt_t* } 151 | sw_block_stmt(BS) ::= LCURLY(L) sw_case_list(STMTS) RCURLY(R). { 152 | BS = new block_stmt_t(STMTS, L, R); 153 | } 154 | 155 | %type sw_case_list { node_vector_t* } 156 | sw_case_list(L) ::= sw_case(C). { L = new node_vector_t(1, C); } 157 | sw_case_list(L) ::= sw_case_list(L2) sw_case(C). { L2->push_back(C); L = L2; } 158 | 159 | %type sw_case { switch_stmt_case_t* } 160 | sw_case(C) ::= CASE(TOK) expr_list(EL) COLON(COL) stmt_list(SL) osemi. { 161 | C = new switch_stmt_case_t(EL, SL, TOK, COL); 162 | } 163 | sw_case(C) ::= CASE(TOK) expr_list(EL) COLON(COL). { 164 | C = new switch_stmt_case_t(EL, 0, TOK, COL); 165 | } 166 | sw_case(C) ::= DEFAULT(TOK) COLON(COL) stmt_list(SL) osemi. { 167 | C = new switch_stmt_case_t(0, SL, TOK, COL); 168 | } 169 | sw_case(C) ::= DEFAULT(TOK) COLON(COL). { 170 | C = new switch_stmt_case_t(0, 0, TOK, COL); 171 | } 172 | 173 | 174 | 175 | 176 | //------------------------------------------------------------------------------ 177 | // Block statement 178 | //------------------------------------------------------------------------------ 179 | 180 | %type block_stmt { block_stmt_t* } 181 | block_stmt(BS) ::= LCURLY(L) stmt_list(STMTS) osemi RCURLY(R). { 182 | BS = new block_stmt_t(STMTS, L, R); 183 | } 184 | block_stmt(BS) ::= LCURLY(L) RCURLY(R). { 185 | BS = new block_stmt_t(0, L, R); 186 | } 187 | 188 | 189 | 190 | 191 | //------------------------------------------------------------------------------ 192 | // Declaration list 193 | //------------------------------------------------------------------------------ 194 | 195 | %type decl_list { node_vector_t* } 196 | decl_list(L) ::= decl(D). { L = new node_vector_t(1, D); } 197 | decl_list(L) ::= decl_list(L2) SEMICOLON decl(D). { L2->push_back(D); L = L2; } 198 | 199 | 200 | 201 | 202 | //------------------------------------------------------------------------------ 203 | // Declaration 204 | // 205 | // Always starts with a keyword: const, var, func, type. 206 | // 207 | // var x, y = 5, 6 208 | // type X struct { a, b, c int } 209 | // func main(argc int, argv ->->byte) int { return 0 } 210 | //------------------------------------------------------------------------------ 211 | 212 | %type decl { node_t* } 213 | decl(D) ::= oattrs(A) IMPORT(TOK) import_spec(IS). { 214 | D = new import_decl_t(A, IS, TOK); 215 | } 216 | decl(D) ::= oattrs(A) IMPORT(TOK) LPAREN(L) import_spec_list(ISL) osemi RPAREN(R). { 217 | D = new import_decl_t(A, ISL, TOK, L, R); 218 | } 219 | decl(D) ::= TYPE(TOK) type_spec(TS). { 220 | D = new type_decl_t(TS, TOK); 221 | } 222 | decl(D) ::= TYPE(TOK) LPAREN(L) type_spec_list(TSL) osemi RPAREN(R). { 223 | D = new type_decl_t(TSL, TOK, L, R); 224 | } 225 | decl(D) ::= CONST(TOK) value_spec(VS). { 226 | const_decl_t *d = new const_decl_t(VS, TOK); 227 | syntax_check_const_decl(ctx->diag, d); 228 | D = d; 229 | } 230 | decl(D) ::= CONST(TOK) LPAREN(L) value_spec_list(VSL) osemi RPAREN(R). { 231 | const_decl_t *d = new const_decl_t(VSL, TOK, L, R); 232 | syntax_check_const_decl(ctx->diag, d); 233 | D = d; 234 | } 235 | decl(D) ::= VAR(TOK) value_spec(VS). { 236 | var_decl_t *d = new var_decl_t(VS, TOK); 237 | syntax_check_var_decl(ctx->diag, d); 238 | D = d; 239 | } 240 | decl(D) ::= VAR(TOK) LPAREN(L) value_spec_list(VSL) osemi RPAREN(R). { 241 | var_decl_t *d = new var_decl_t(VSL, TOK, L, R); 242 | syntax_check_var_decl(ctx->diag, d); 243 | D = d; 244 | } 245 | decl(D) ::= FUNC(TOK) ident(N) 246 | LPAREN oargs_comma_list(A) RPAREN 247 | func_results(R). 248 | { 249 | func_type_t *ftype = new func_type_t(A, R, TOK); 250 | D = new func_decl_t(N, ftype); 251 | } 252 | decl(D) ::= FUNC(TOK) ident(N) 253 | LPAREN oargs_comma_list(A) RPAREN 254 | func_results(R) block_stmt(B). 255 | { 256 | func_type_t *ftype = new func_type_t(A, R, TOK); 257 | D = new func_decl_t(N, ftype, B); 258 | } 259 | 260 | 261 | 262 | 263 | //------------------------------------------------------------------------------ 264 | // Function results 265 | //------------------------------------------------------------------------------ 266 | 267 | %type func_results { field_vector_t* } 268 | func_results(FR) ::= type(T). { 269 | field_t *f = new field_t(0, T); 270 | FR = new field_vector_t(1, f); 271 | } 272 | func_results(FR) ::= LPAREN field_comma_list(FL) RPAREN. { FR = FL; } 273 | func_results(FR) ::= . { FR = 0; } 274 | 275 | 276 | 277 | 278 | //------------------------------------------------------------------------------ 279 | // Import specification 280 | // 281 | // ast "go/ast" 282 | // "fmt" 283 | //------------------------------------------------------------------------------ 284 | 285 | %type import_spec_list { import_spec_vector_t* } 286 | import_spec_list(L) ::= import_spec(S). { L = new import_spec_vector_t(1, S); } 287 | import_spec_list(L) ::= import_spec_list(L2) SEMICOLON import_spec(S). { 288 | L2->push_back(S); L = L2; 289 | } 290 | 291 | %type import_spec { import_spec_t* } 292 | import_spec(IS) ::= IDENT(I) STRING(S). { IS = new import_spec_t(I, S); } 293 | import_spec(IS) ::= STRING(S). { IS = new import_spec_t(0, S); } 294 | 295 | 296 | 297 | 298 | //------------------------------------------------------------------------------ 299 | // Type specification 300 | // 301 | // pfloat ->float 302 | // iarray [10]int 303 | //------------------------------------------------------------------------------ 304 | 305 | %type type_spec_list { type_spec_vector_t* } 306 | type_spec_list(L) ::= type_spec(S). { L = new type_spec_vector_t(1, S); } 307 | type_spec_list(L) ::= type_spec_list(L2) SEMICOLON type_spec(S). { 308 | L2->push_back(S); L = L2; 309 | } 310 | 311 | %type type_spec { type_spec_t* } 312 | type_spec(TS) ::= IDENT(I) type(T). { TS = new type_spec_t(I, T); } 313 | 314 | 315 | 316 | 317 | //------------------------------------------------------------------------------ 318 | // Value specification (works as const or var spec) 319 | // 320 | // n1, n2, n3 int = 1, 2, 3 321 | // x float = 3.14 322 | // s = "123" 323 | // n 324 | //------------------------------------------------------------------------------ 325 | 326 | %type value_spec_list { value_spec_vector_t* } 327 | value_spec_list(L) ::= value_spec(S). { L = new value_spec_vector_t(1, S); } 328 | value_spec_list(L) ::= value_spec_list(L2) SEMICOLON value_spec(S). { 329 | L2->push_back(S); L = L2; 330 | } 331 | 332 | %type value_spec { value_spec_t* } 333 | value_spec(VS) ::= ident_list(IL) otype(OT). { 334 | VS = new value_spec_t(IL, OT); 335 | } 336 | value_spec(VS) ::= ident_list(IL) otype(OT) ASSIGN(TOK) iexpr_list(EL). { 337 | VS = new value_spec_t(IL, OT, EL, TOK); 338 | } 339 | 340 | 341 | 342 | 343 | //------------------------------------------------------------------------------ 344 | // Type 345 | // 346 | // lib.Type 347 | // int, float, uint32 348 | // struct { a, b, c int; d, e, f float; } 349 | // union { a, b, c int; } 350 | // [10]int, [5]float 351 | // *float 352 | // **byte 353 | // func(int, *float) (bool, bool) 354 | //------------------------------------------------------------------------------ 355 | 356 | %type type { node_t* } 357 | type(T) ::= TIMES(TOK) type(X). { T = new unary_expr_t(X, TOK); } 358 | type(T) ::= LSB(L) expr(LEN) RSB(R) type(EL). { T = new array_type_t(LEN, EL, L, R); } 359 | type(T) ::= LSB(L) RSB(R) type(EL). { T = new array_type_t(0, EL, L, R); } 360 | type(T) ::= ident(P) DOT ident(SEL). { T = new selector_expr_t(P, SEL); } 361 | type(T) ::= ident(I). { T = I; } 362 | type(T) ::= STRUCT(TOK) LCURLY(L) ofield_semi_list_and_osemi(FL) RCURLY(R). { 363 | T = new struct_type_t(FL, TOK, L, R); 364 | } 365 | type(T) ::= UNION(TOK) LCURLY(L) ofield_semi_list_and_osemi(FL) RCURLY(R). { 366 | T = new struct_type_t(FL, TOK, L, R); 367 | } 368 | type(T) ::= FUNC(TOK) LPAREN oargs_comma_list(A) RPAREN func_results(R). { 369 | T = new func_type_t(A, R, TOK); 370 | } 371 | 372 | 373 | 374 | 375 | //------------------------------------------------------------------------------ 376 | // Field lists 377 | // 378 | // n1, n2, n3 type; n4 type; n5, n6 type (semicolon separated) 379 | // n1, n2, n3 type, n4 type, n5, n6 type (comma separated) 380 | //------------------------------------------------------------------------------ 381 | 382 | %type field_comma_list { field_vector_t* } 383 | field_comma_list(L) ::= nametype_list(NTL). { 384 | L = nametypev_to_fieldv(ctx->diag, NTL, false); 385 | } 386 | 387 | %type args_comma_list { field_vector_t* } 388 | args_comma_list(L) ::= nametype_list(NTL) ofunc_ellipsis(FE). { 389 | L = nametypev_to_fieldv(ctx->diag, NTL, FE); 390 | } 391 | args_comma_list(L) ::= ELLIPSIS. { 392 | L = nametypev_to_fieldv(ctx->diag, new nametype_vector_t, true); 393 | } 394 | 395 | %type field_semi_list { field_vector_t* } 396 | field_semi_list(L) ::= field(F). { L = new field_vector_t(1, F); } 397 | field_semi_list(L) ::= field_semi_list(L2) SEMICOLON field(F). { 398 | L2->push_back(F); L = L2; 399 | } 400 | 401 | // this is a temporary list for comma separated field list 402 | %type nametype_list { nametype_vector_t* } 403 | nametype_list(FL) ::= nametype(F). { FL = new nametype_vector_t(1, F); } 404 | nametype_list(FL) ::= nametype_list(FL2) COMMA nametype(F). { 405 | FL2->push_back(F); FL = FL2; 406 | } 407 | 408 | %type nametype { nametype_t } 409 | nametype(F) ::= ident(I) type(T). { F.name = I; F.type = T; } 410 | nametype(F) ::= type(T). { F.name = 0; F.type = T; } 411 | 412 | 413 | 414 | 415 | //------------------------------------------------------------------------------ 416 | // Function optional ellipsis (optional: , ... at the end of the function) 417 | //------------------------------------------------------------------------------ 418 | 419 | %type ofunc_ellipsis { bool } 420 | ofunc_ellipsis(FE) ::= COMMA ELLIPSIS. { FE = true; } 421 | ofunc_ellipsis(FE) ::= . { FE = false; } 422 | 423 | 424 | 425 | 426 | //------------------------------------------------------------------------------ 427 | // Field 428 | // 429 | // name1, name2 type 430 | // name type 431 | //------------------------------------------------------------------------------ 432 | 433 | %type field { field_t* } 434 | field(F) ::= ident_list(IL) type(T). { F = new field_t(IL, T); } 435 | field(F) ::= type(T). { F = new field_t(0, T); } 436 | 437 | 438 | 439 | 440 | //------------------------------------------------------------------------------ 441 | // Basic literal (expr) 442 | // 443 | // 3.14, 5, "string", `raw string`, 'c', etc. 444 | //------------------------------------------------------------------------------ 445 | %type basic_lit { basic_lit_expr_t* } 446 | basic_lit(A) ::= INT(B). { A = new basic_lit_expr_t(B); } 447 | basic_lit(A) ::= FLOAT(B). { A = new basic_lit_expr_t(B); } 448 | basic_lit(A) ::= STRING(B). { A = new basic_lit_expr_t(B); } 449 | basic_lit(A) ::= CHAR(B). { A = new basic_lit_expr_t(B); } 450 | 451 | 452 | 453 | 454 | //------------------------------------------------------------------------------ 455 | // Primary expression 456 | // 457 | // Usually a part of a unary or binary expression. 458 | // 459 | // math.PI, 5, (7+4), cos(3.14), array[5], etc. 460 | //------------------------------------------------------------------------------ 461 | %type pexpr { node_t* } 462 | pexpr(A) ::= basic_lit(E). { A = E; } 463 | pexpr(A) ::= ident(I). { A = I; } 464 | pexpr(A) ::= LPAREN(L) expr(E) RPAREN(R). { A = new paren_expr_t(E, L, R); } 465 | pexpr(A) ::= pexpr(P) LSB(L) expr(I) RSB(R). { A = new index_expr_t(P, I, L, R); } 466 | 467 | // selector expr 468 | pexpr(A) ::= pexpr(P) DOT IDENT(I). { 469 | A = new selector_expr_t(P, new ident_expr_t(I)); 470 | } 471 | 472 | // type cast expr 473 | pexpr(A) ::= pexpr(P) DOT(D) LPAREN(L) type(T) RPAREN(R). { 474 | A = new type_cast_expr_t(P, T, D, L, R); 475 | } 476 | 477 | 478 | // call expr 479 | pexpr(A) ::= pexpr(P) LPAREN(L) RPAREN(R). { A = new call_expr_t(P, 0, L, R); } 480 | 481 | // call expr with non-empty expr list 482 | pexpr(A) ::= pexpr(P) LPAREN(L) iexpr_list(EL) ocomma RPAREN(R). { 483 | A = new call_expr_t(P, EL, L, R); 484 | } 485 | 486 | 487 | 488 | 489 | //------------------------------------------------------------------------------ 490 | // Unary expression 491 | // 492 | // -5, +2, !true, &x, *ptr 493 | //------------------------------------------------------------------------------ 494 | 495 | %type uexpr { node_t* } 496 | uexpr(A) ::= pexpr(B). { A = B; } 497 | uexpr(A) ::= PLUS(T) uexpr(E). { A = new unary_expr_t(E, T); } 498 | uexpr(A) ::= MINUS(T) uexpr(E). { A = new unary_expr_t(E, T); } 499 | uexpr(A) ::= NOT(T) uexpr(E). { A = new unary_expr_t(E, T); } 500 | uexpr(A) ::= AND(T) uexpr(E). { A = new unary_expr_t(E, T); } 501 | uexpr(A) ::= TIMES(T) uexpr(E). { A = new unary_expr_t(E, T); } 502 | uexpr(A) ::= XOR(T) uexpr(E). { A = new unary_expr_t(E, T); } 503 | 504 | 505 | 506 | 507 | //------------------------------------------------------------------------------ 508 | // Binary expression 509 | // 510 | // 1 + 2, 7 / 8, 4 * 8, 5 << 1, true || false, true && true, 5 <= 6, etc. 511 | //------------------------------------------------------------------------------ 512 | 513 | %type expr { node_t* } 514 | expr(E) ::= uexpr(U). { E = U; } 515 | expr(E) ::= expr(L) DIVIDE(T) expr(R). { E = new binary_expr_t(L, R, T); } 516 | expr(E) ::= expr(L) TIMES(T) expr(R). { E = new binary_expr_t(L, R, T); } 517 | expr(E) ::= expr(L) MOD(T) expr(R). { E = new binary_expr_t(L, R, T); } 518 | expr(E) ::= expr(L) SHIFTL(T) expr(R). { E = new binary_expr_t(L, R, T); } 519 | expr(E) ::= expr(L) SHIFTR(T) expr(R). { E = new binary_expr_t(L, R, T); } 520 | expr(E) ::= expr(L) AND(T) expr(R). { E = new binary_expr_t(L, R, T); } 521 | expr(E) ::= expr(L) ANDNOT(T) expr(R). { E = new binary_expr_t(L, R, T); } 522 | expr(E) ::= expr(L) PLUS(T) expr(R). { E = new binary_expr_t(L, R, T); } 523 | expr(E) ::= expr(L) MINUS(T) expr(R). { E = new binary_expr_t(L, R, T); } 524 | expr(E) ::= expr(L) OR(T) expr(R). { E = new binary_expr_t(L, R, T); } 525 | expr(E) ::= expr(L) XOR(T) expr(R). { E = new binary_expr_t(L, R, T); } 526 | expr(E) ::= expr(L) EQ(T) expr(R). { E = new binary_expr_t(L, R, T); } 527 | expr(E) ::= expr(L) NEQ(T) expr(R). { E = new binary_expr_t(L, R, T); } 528 | expr(E) ::= expr(L) LT(T) expr(R). { E = new binary_expr_t(L, R, T); } 529 | expr(E) ::= expr(L) LE(T) expr(R). { E = new binary_expr_t(L, R, T); } 530 | expr(E) ::= expr(L) GT(T) expr(R). { E = new binary_expr_t(L, R, T); } 531 | expr(E) ::= expr(L) GE(T) expr(R). { E = new binary_expr_t(L, R, T); } 532 | expr(E) ::= expr(L) ANDAND(T) expr(R). { E = new binary_expr_t(L, R, T); } 533 | expr(E) ::= expr(L) OROR(T) expr(R). { E = new binary_expr_t(L, R, T); } 534 | 535 | 536 | 537 | 538 | //------------------------------------------------------------------------------ 539 | // Combined type and expression 540 | // 541 | // Here we use a big copy & paste of expr, uexpr, pexpr and type. If anyone 542 | // knows how to make it simpler, do it! 543 | //------------------------------------------------------------------------------ 544 | 545 | %type ty_pexpr { node_t* } 546 | ty_pexpr(T) ::= LSB(L) expr(LEN) RSB(R) type(EL). { T = new array_type_t(LEN, EL, L, R); } 547 | ty_pexpr(T) ::= LSB(L) RSB(R) type(EL). { T = new array_type_t(0, EL, L, R); } 548 | ty_pexpr(A) ::= basic_lit(E). { A = E; } 549 | ty_pexpr(A) ::= ident(I). { A = I; } 550 | ty_pexpr(A) ::= LPAREN(L) expr(E) RPAREN(R). { A = new paren_expr_t(E, L, R); } 551 | ty_pexpr(A) ::= pexpr(P) LSB(L) expr(I) RSB(R). { A = new index_expr_t(P, I, L, R); } 552 | 553 | // selector expr 554 | ty_pexpr(A) ::= pexpr(P) DOT IDENT(I). { 555 | A = new selector_expr_t(P, new ident_expr_t(I)); 556 | } 557 | 558 | // type cast expr 559 | ty_pexpr(A) ::= pexpr(P) DOT(D) LPAREN(L) type(T) RPAREN(R). { 560 | A = new type_cast_expr_t(P, T, D, L, R); 561 | } 562 | 563 | // call expr 564 | ty_pexpr(A) ::= pexpr(P) LPAREN(L) RPAREN(R). { A = new call_expr_t(P, 0, L, R); } 565 | 566 | // call expr with non-empty expr list 567 | ty_pexpr(A) ::= pexpr(P) LPAREN(L) iexpr_list(EL) ocomma RPAREN(R). { 568 | A = new call_expr_t(P, EL, L, R); 569 | } 570 | 571 | ty_pexpr(T) ::= STRUCT(TOK) LCURLY(L) ofield_semi_list_and_osemi(FL) RCURLY(R). { 572 | T = new struct_type_t(FL, TOK, L, R); 573 | } 574 | ty_pexpr(T) ::= UNION(TOK) LCURLY(L) ofield_semi_list_and_osemi(FL) RCURLY(R). { 575 | T = new struct_type_t(FL, TOK, L, R); 576 | } 577 | ty_pexpr(T) ::= FUNC(TOK) LPAREN oargs_comma_list(A) RPAREN func_results(R). { 578 | T = new func_type_t(A, R, TOK); 579 | } 580 | 581 | %type ty_uexpr { node_t* } 582 | ty_uexpr(A) ::= ty_pexpr(B). { A = B; } 583 | ty_uexpr(A) ::= PLUS(T) uexpr(E). { A = new unary_expr_t(E, T); } 584 | ty_uexpr(A) ::= MINUS(T) uexpr(E). { A = new unary_expr_t(E, T); } 585 | ty_uexpr(A) ::= NOT(T) uexpr(E). { A = new unary_expr_t(E, T); } 586 | ty_uexpr(A) ::= AND(T) uexpr(E). { A = new unary_expr_t(E, T); } 587 | ty_uexpr(A) ::= TIMES(T) ty_uexpr(E). { A = new unary_expr_t(E, T); } 588 | ty_uexpr(A) ::= XOR(T) uexpr(E). { A = new unary_expr_t(E, T); } 589 | 590 | %type ty_expr { node_t* } 591 | ty_expr(E) ::= ty_uexpr(U). { E = U; } 592 | ty_expr(E) ::= expr(L) DIVIDE(T) expr(R). { E = new binary_expr_t(L, R, T); } 593 | ty_expr(E) ::= expr(L) TIMES(T) expr(R). { E = new binary_expr_t(L, R, T); } 594 | ty_expr(E) ::= expr(L) MOD(T) expr(R). { E = new binary_expr_t(L, R, T); } 595 | ty_expr(E) ::= expr(L) SHIFTL(T) expr(R). { E = new binary_expr_t(L, R, T); } 596 | ty_expr(E) ::= expr(L) SHIFTR(T) expr(R). { E = new binary_expr_t(L, R, T); } 597 | ty_expr(E) ::= expr(L) AND(T) expr(R). { E = new binary_expr_t(L, R, T); } 598 | ty_expr(E) ::= expr(L) ANDNOT(T) expr(R). { E = new binary_expr_t(L, R, T); } 599 | ty_expr(E) ::= expr(L) PLUS(T) expr(R). { E = new binary_expr_t(L, R, T); } 600 | ty_expr(E) ::= expr(L) MINUS(T) expr(R). { E = new binary_expr_t(L, R, T); } 601 | ty_expr(E) ::= expr(L) OR(T) expr(R). { E = new binary_expr_t(L, R, T); } 602 | ty_expr(E) ::= expr(L) XOR(T) expr(R). { E = new binary_expr_t(L, R, T); } 603 | ty_expr(E) ::= expr(L) EQ(T) expr(R). { E = new binary_expr_t(L, R, T); } 604 | ty_expr(E) ::= expr(L) NEQ(T) expr(R). { E = new binary_expr_t(L, R, T); } 605 | ty_expr(E) ::= expr(L) LT(T) expr(R). { E = new binary_expr_t(L, R, T); } 606 | ty_expr(E) ::= expr(L) LE(T) expr(R). { E = new binary_expr_t(L, R, T); } 607 | ty_expr(E) ::= expr(L) GT(T) expr(R). { E = new binary_expr_t(L, R, T); } 608 | ty_expr(E) ::= expr(L) GE(T) expr(R). { E = new binary_expr_t(L, R, T); } 609 | ty_expr(E) ::= expr(L) ANDAND(T) expr(R). { E = new binary_expr_t(L, R, T); } 610 | ty_expr(E) ::= expr(L) OROR(T) expr(R). { E = new binary_expr_t(L, R, T); } 611 | 612 | 613 | 614 | 615 | //------------------------------------------------------------------------------ 616 | // Compound literal 617 | // 618 | // {iexpr, iexpr, iexpr} 619 | // {iexpr, iexpr, iexpr}.(type) 620 | //------------------------------------------------------------------------------ 621 | 622 | %type compound_lit { node_t* } 623 | compound_lit(CL) ::= LCURLY(L) iexpr_list(CEL) ocomma RCURLY(R). { 624 | CL = new compound_lit_t(CEL, 0, L, R); 625 | } 626 | compound_lit(CL) ::= LCURLY(L) iexpr_list(CEL) ocomma RCURLY DOT LPAREN type(T) RPAREN(R). { 627 | CL = new compound_lit_t(CEL, T, L, R); 628 | } 629 | 630 | 631 | 632 | 633 | //------------------------------------------------------------------------------ 634 | // Compound/expression list (comma separated) 635 | // 636 | // expr, compound, expr 637 | //------------------------------------------------------------------------------ 638 | 639 | %type iexpr { node_t* } 640 | iexpr(CE) ::= ty_expr(E). { CE = E; } 641 | iexpr(CE) ::= compound_lit(CL). { CE = CL; } 642 | 643 | %type iexpr_list { node_vector_t* } 644 | iexpr_list(L) ::= iexpr(E). { L = new node_vector_t(1, E); } 645 | iexpr_list(L) ::= iexpr_list(L2) COMMA iexpr(E). { L2->push_back(E); L = L2; } 646 | 647 | %type expr_list { node_vector_t* } 648 | expr_list(L) ::= expr(E). { L = new node_vector_t(1, E); } 649 | expr_list(L) ::= expr_list(L2) COMMA expr(E). { L2->push_back(E); L = L2; } 650 | 651 | 652 | 653 | 654 | //------------------------------------------------------------------------------ 655 | // Misc 656 | //------------------------------------------------------------------------------ 657 | 658 | // optional semicolon 659 | osemi ::= . 660 | osemi ::= SEMICOLON. 661 | 662 | // optional comma 663 | ocomma ::= . 664 | ocomma ::= COMMA. 665 | 666 | // optional expression 667 | %type oexpr { node_t* } 668 | oexpr(OE) ::= . { OE = 0; } 669 | oexpr(OE) ::= expr(E). { OE = E; } 670 | 671 | // optional type 672 | %type otype { node_t* } 673 | otype(OT) ::= . { OT = 0; } 674 | otype(OT) ::= type(T). { OT = T; } 675 | 676 | // identifier converted to an AST node 677 | %type ident { ident_expr_t* } 678 | ident(A) ::= IDENT(B). { A = new ident_expr_t(B); } 679 | 680 | // identifier list 681 | %type ident_list { ident_expr_vector_t* } 682 | ident_list(L) ::= ident(I). { L = new ident_expr_vector_t(1, I); } 683 | ident_list(L) ::= ident_list(L2) COMMA ident(I). { L2->push_back(I); L = L2; } 684 | 685 | // optional comma-separated args list 686 | %type oargs_comma_list { field_vector_t* } 687 | oargs_comma_list(L) ::= . { L = 0; } 688 | oargs_comma_list(L) ::= args_comma_list(R). { L = R; } 689 | 690 | // optional semi-separated field list 691 | %type ofield_semi_list_and_osemi { field_vector_t* } 692 | ofield_semi_list_and_osemi(L) ::= . { L = 0; } 693 | ofield_semi_list_and_osemi(L) ::= field_semi_list(R) osemi. { L = R; } 694 | -------------------------------------------------------------------------------- /compiler/source-loc.cpp: -------------------------------------------------------------------------------- 1 | #include "krawl.hpp" 2 | #include 3 | #include 4 | 5 | //------------------------------------------------------------------------------ 6 | // source_pos_t 7 | //------------------------------------------------------------------------------ 8 | 9 | unsigned int source_pos_t::col(char *input) 10 | { 11 | unsigned int col = 1; 12 | char *c = input + line_offset; 13 | char *e = input + offset; 14 | while (c < e) { 15 | size_t len = utf8_char_length(*c); 16 | c += len; 17 | col++; 18 | } 19 | return col; 20 | } 21 | 22 | //------------------------------------------------------------------------------ 23 | // source_file_t 24 | //------------------------------------------------------------------------------ 25 | 26 | static bool source_line_offset_cmp(const source_line_t &l, const source_line_t &r) 27 | { 28 | return l.offset <= r.offset; 29 | } 30 | 31 | source_file_t::source_file_t() 32 | { 33 | source_line_t firstline = {0, 1}; 34 | lines.push_back(firstline); 35 | } 36 | 37 | source_line_t *source_file_t::get_line_by_loc(source_loc_t offset) 38 | { 39 | std::vector::iterator it; 40 | source_line_t proto; 41 | proto.offset = offset - this->offset; 42 | 43 | it = std::lower_bound(lines.begin(), lines.end(), proto, 44 | source_line_offset_cmp); 45 | it--; 46 | return &*it; 47 | } 48 | 49 | source_pos_t source_file_t::get_pos_by_loc(source_loc_t offset) 50 | { 51 | source_pos_t out; 52 | source_line_t *line = get_line_by_loc(offset); 53 | 54 | out.filename = filename.c_str(); 55 | out.offset = offset - this->offset; 56 | out.line = line->line; 57 | out.line_offset = line->offset; 58 | return out; 59 | } 60 | 61 | void source_file_t::add_line(unsigned int offset) 62 | { 63 | source_line_t newline = {offset, 1}; 64 | if (!lines.empty()) 65 | newline.line = lines.back().line+1; 66 | 67 | lines.push_back(newline); 68 | } 69 | 70 | //------------------------------------------------------------------------------ 71 | // source_group_t 72 | //------------------------------------------------------------------------------ 73 | 74 | static bool source_file_offset_cmp(const source_file_t *l, const source_file_t *r) 75 | { 76 | return l->offset <= r->offset; 77 | } 78 | 79 | source_group_t::~source_group_t() 80 | { 81 | for (size_t i = 0, n = files.size(); i < n; ++i) 82 | delete files[i]; 83 | } 84 | 85 | source_file_t *source_group_t::get_file_by_name(const char *filename) 86 | { 87 | for (size_t i = 0, n = files.size(); i < n; ++i) { 88 | if (files[i]->filename == filename) 89 | return files[i]; 90 | } 91 | return 0; 92 | } 93 | 94 | source_file_t *source_group_t::get_file_by_loc(source_loc_t offset) 95 | { 96 | std::vector::iterator it; 97 | source_file_t proto; 98 | proto.offset = offset; 99 | 100 | it = std::lower_bound(files.begin(), files.end(), &proto, 101 | source_file_offset_cmp); 102 | it--; 103 | return *it; 104 | } 105 | 106 | source_pos_t source_group_t::get_pos_by_loc(source_loc_t offset) 107 | { 108 | source_file_t *f = get_file_by_loc(offset); 109 | if (!f) { 110 | source_pos_t empty = {"", 0, 0, 0}; 111 | return empty; 112 | } 113 | 114 | return f->get_pos_by_loc(offset); 115 | } 116 | 117 | source_file_t *source_group_t::add_file(const char *filename, 118 | std::vector *data) 119 | { 120 | source_file_t *f = new source_file_t; 121 | f->filename = filename; 122 | f->offset = 0; 123 | if (!files.empty()) { 124 | source_file_t *last = files.back(); 125 | f->offset = last->offset + last->data.size(); 126 | } 127 | f->data.swap(*data); 128 | 129 | files.push_back(f); 130 | return f; 131 | } 132 | -------------------------------------------------------------------------------- /compiler/token-to-string-gen.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from string import Template 4 | import re 5 | 6 | def read_file(fname, m='r'): 7 | f = open(fname, m) 8 | try: 9 | return f.read() 10 | finally: 11 | f.close() 12 | 13 | tpl = Template(""" 14 | #include "tokens.hpp" 15 | 16 | const char *token_to_string(int tok) 17 | { 18 | switch (tok) { 19 | $tokens 20 | } 21 | return "???"; 22 | } 23 | """.strip()) 24 | 25 | toks = re.findall(r'TOK_[A-Z_]+', read_file('tokens.hpp')) 26 | toks = '\n'.join(['\tcase %s: return "%s";' % (t, t) for t in toks]) 27 | print(tpl.substitute(tokens=toks)) 28 | -------------------------------------------------------------------------------- /compiler/tokens.cpp: -------------------------------------------------------------------------------- 1 | #include "tokens.hpp" 2 | 3 | const char *token_to_string(int tok) 4 | { 5 | switch (tok) { 6 | case TOK_OROR: return "TOK_OROR"; 7 | case TOK_ANDAND: return "TOK_ANDAND"; 8 | case TOK_EQ: return "TOK_EQ"; 9 | case TOK_NEQ: return "TOK_NEQ"; 10 | case TOK_LT: return "TOK_LT"; 11 | case TOK_LE: return "TOK_LE"; 12 | case TOK_GT: return "TOK_GT"; 13 | case TOK_GE: return "TOK_GE"; 14 | case TOK_PLUS: return "TOK_PLUS"; 15 | case TOK_MINUS: return "TOK_MINUS"; 16 | case TOK_OR: return "TOK_OR"; 17 | case TOK_XOR: return "TOK_XOR"; 18 | case TOK_DIVIDE: return "TOK_DIVIDE"; 19 | case TOK_TIMES: return "TOK_TIMES"; 20 | case TOK_MOD: return "TOK_MOD"; 21 | case TOK_SHIFTL: return "TOK_SHIFTL"; 22 | case TOK_SHIFTR: return "TOK_SHIFTR"; 23 | case TOK_AND: return "TOK_AND"; 24 | case TOK_ANDNOT: return "TOK_ANDNOT"; 25 | case TOK_IDENT: return "TOK_IDENT"; 26 | case TOK_ASSIGN: return "TOK_ASSIGN"; 27 | case TOK_INT: return "TOK_INT"; 28 | case TOK_FLOAT: return "TOK_FLOAT"; 29 | case TOK_STRING: return "TOK_STRING"; 30 | case TOK_CHAR: return "TOK_CHAR"; 31 | case TOK_COMMA: return "TOK_COMMA"; 32 | case TOK_SEMICOLON: return "TOK_SEMICOLON"; 33 | case TOK_DECLARIZE: return "TOK_DECLARIZE"; 34 | case TOK_A_DIVIDE: return "TOK_A_DIVIDE"; 35 | case TOK_A_TIMES: return "TOK_A_TIMES"; 36 | case TOK_A_MOD: return "TOK_A_MOD"; 37 | case TOK_A_SHIFTL: return "TOK_A_SHIFTL"; 38 | case TOK_A_SHIFTR: return "TOK_A_SHIFTR"; 39 | case TOK_A_AND: return "TOK_A_AND"; 40 | case TOK_A_ANDNOT: return "TOK_A_ANDNOT"; 41 | case TOK_A_PLUS: return "TOK_A_PLUS"; 42 | case TOK_A_MINUS: return "TOK_A_MINUS"; 43 | case TOK_A_OR: return "TOK_A_OR"; 44 | case TOK_A_XOR: return "TOK_A_XOR"; 45 | case TOK_INC: return "TOK_INC"; 46 | case TOK_DEC: return "TOK_DEC"; 47 | case TOK_BREAK: return "TOK_BREAK"; 48 | case TOK_CONTINUE: return "TOK_CONTINUE"; 49 | case TOK_FALLTHROUGH: return "TOK_FALLTHROUGH"; 50 | case TOK_RETURN: return "TOK_RETURN"; 51 | case TOK_IF: return "TOK_IF"; 52 | case TOK_ELSE: return "TOK_ELSE"; 53 | case TOK_FOR: return "TOK_FOR"; 54 | case TOK_SWITCH: return "TOK_SWITCH"; 55 | case TOK_LCURLY: return "TOK_LCURLY"; 56 | case TOK_RCURLY: return "TOK_RCURLY"; 57 | case TOK_CASE: return "TOK_CASE"; 58 | case TOK_COLON: return "TOK_COLON"; 59 | case TOK_DEFAULT: return "TOK_DEFAULT"; 60 | case TOK_IMPORT: return "TOK_IMPORT"; 61 | case TOK_LPAREN: return "TOK_LPAREN"; 62 | case TOK_RPAREN: return "TOK_RPAREN"; 63 | case TOK_TYPE: return "TOK_TYPE"; 64 | case TOK_CONST: return "TOK_CONST"; 65 | case TOK_VAR: return "TOK_VAR"; 66 | case TOK_FUNC: return "TOK_FUNC"; 67 | case TOK_LSB: return "TOK_LSB"; 68 | case TOK_RSB: return "TOK_RSB"; 69 | case TOK_DOT: return "TOK_DOT"; 70 | case TOK_STRUCT: return "TOK_STRUCT"; 71 | case TOK_UNION: return "TOK_UNION"; 72 | case TOK_ELLIPSIS: return "TOK_ELLIPSIS"; 73 | case TOK_NOT: return "TOK_NOT"; 74 | } 75 | return "???"; 76 | } 77 | -------------------------------------------------------------------------------- /compiler/tokens.hpp: -------------------------------------------------------------------------------- 1 | #define TOK_OROR 1 2 | #define TOK_ANDAND 2 3 | #define TOK_EQ 3 4 | #define TOK_NEQ 4 5 | #define TOK_LT 5 6 | #define TOK_LE 6 7 | #define TOK_GT 7 8 | #define TOK_GE 8 9 | #define TOK_PLUS 9 10 | #define TOK_MINUS 10 11 | #define TOK_OR 11 12 | #define TOK_XOR 12 13 | #define TOK_DIVIDE 13 14 | #define TOK_TIMES 14 15 | #define TOK_MOD 15 16 | #define TOK_SHIFTL 16 17 | #define TOK_SHIFTR 17 18 | #define TOK_AND 18 19 | #define TOK_ANDNOT 19 20 | #define TOK_IDENT 20 21 | #define TOK_ASSIGN 21 22 | #define TOK_INT 22 23 | #define TOK_FLOAT 23 24 | #define TOK_STRING 24 25 | #define TOK_CHAR 25 26 | #define TOK_COMMA 26 27 | #define TOK_SEMICOLON 27 28 | #define TOK_DECLARIZE 28 29 | #define TOK_A_DIVIDE 29 30 | #define TOK_A_TIMES 30 31 | #define TOK_A_MOD 31 32 | #define TOK_A_SHIFTL 32 33 | #define TOK_A_SHIFTR 33 34 | #define TOK_A_AND 34 35 | #define TOK_A_ANDNOT 35 36 | #define TOK_A_PLUS 36 37 | #define TOK_A_MINUS 37 38 | #define TOK_A_OR 38 39 | #define TOK_A_XOR 39 40 | #define TOK_INC 40 41 | #define TOK_DEC 41 42 | #define TOK_BREAK 42 43 | #define TOK_CONTINUE 43 44 | #define TOK_FALLTHROUGH 44 45 | #define TOK_RETURN 45 46 | #define TOK_IF 46 47 | #define TOK_ELSE 47 48 | #define TOK_FOR 48 49 | #define TOK_SWITCH 49 50 | #define TOK_LCURLY 50 51 | #define TOK_RCURLY 51 52 | #define TOK_CASE 52 53 | #define TOK_COLON 53 54 | #define TOK_DEFAULT 54 55 | #define TOK_IMPORT 55 56 | #define TOK_LPAREN 56 57 | #define TOK_RPAREN 57 58 | #define TOK_TYPE 58 59 | #define TOK_CONST 59 60 | #define TOK_VAR 60 61 | #define TOK_FUNC 61 62 | #define TOK_LSB 62 63 | #define TOK_RSB 63 64 | #define TOK_DOT 64 65 | #define TOK_STRUCT 65 66 | #define TOK_UNION 66 67 | #define TOK_ELLIPSIS 67 68 | #define TOK_NOT 68 69 | -------------------------------------------------------------------------------- /compiler/update.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clang -o lemon/lemon lemon/lemon.c 4 | ragel -o lexer.cpp lexer.rl 5 | ./lemon/lemon parser.y 6 | mv parser.h tokens.hpp 7 | mv parser.c parser.cpp 8 | ./token-to-string-gen.py > tokens.cpp 9 | -------------------------------------------------------------------------------- /compiler/utf8.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static const unsigned char utf8_length[256] = { 5 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 6 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 7 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 8 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 10 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 11 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 12 | 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1 13 | }; 14 | 15 | static const unsigned char utf8_mask[6] = { 16 | 0x7F, 17 | 0x1F, 18 | 0x0F, 19 | 0x07, 20 | 0x03, 21 | 0x01 22 | }; 23 | 24 | size_t utf8_char_length(char c) 25 | { 26 | return utf8_length[(unsigned char)c]; 27 | } 28 | 29 | size_t utf8_to_unicode(uint32_t *out, const char *c) 30 | { 31 | if (*c == 0) 32 | return 0; 33 | 34 | int i; 35 | unsigned char len = utf8_char_length(*c); 36 | unsigned char mask = utf8_mask[len-1]; 37 | uint32_t result = c[0] & mask; 38 | for (i = 1; i < len; ++i) { 39 | result <<= 6; 40 | result |= c[i] & 0x3f; 41 | } 42 | 43 | *out = result; 44 | return (size_t)len; 45 | } 46 | 47 | size_t unicode_to_utf8(char *out, uint32_t c) 48 | { 49 | size_t len = 0; 50 | int first; 51 | int i; 52 | 53 | if (c < 0x80) { 54 | first = 0; 55 | len = 1; 56 | } else if (c < 0x800) { 57 | first = 0xc0; 58 | len = 2; 59 | } else if (c < 0x10000) { 60 | first = 0xe0; 61 | len = 3; 62 | } else if (c < 0x200000) { 63 | first = 0xf0; 64 | len = 4; 65 | } else if (c < 0x4000000) { 66 | first = 0xf8; 67 | len = 5; 68 | } else { 69 | first = 0xfc; 70 | len = 6; 71 | } 72 | 73 | for (i = len - 1; i > 0; --i) { 74 | out[i] = (c & 0x3f) | 0x80; 75 | c >>= 6; 76 | } 77 | out[0] = c | first; 78 | 79 | return len; 80 | } 81 | -------------------------------------------------------------------------------- /compiler/wscript: -------------------------------------------------------------------------------- 1 | def build(bld): 2 | compiler_src = bld.path.ant_glob("*.cpp cityhash/*.cc") 3 | if bld.env.OPT_ARCH == 'amd64': 4 | compiler_src.append('compat/compat_amd64.cpp') 5 | elif bld.env.OPT_ARCH == 'x86': 6 | compiler_src.append('compat/compat_x86.cpp') 7 | 8 | rpath = [] 9 | if bld.env.OPT_LENSTR: 10 | rpath = ['/usr/lib/llvm'] 11 | bld.program( 12 | source = compiler_src, 13 | target = 'krawl', 14 | use = 'LLVM', 15 | lib = 'mpfr gmp', 16 | rpath = rpath 17 | ) 18 | -------------------------------------------------------------------------------- /ctokrawl/ctokrawl.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | using std::tr1::unordered_set; 30 | using std::tr1::unordered_map; 31 | using namespace clang; 32 | 33 | namespace { 34 | 35 | const char *keywords[] = {"fallthrough","func","type","var","import"}; 36 | 37 | void die(const char *why) 38 | { 39 | fprintf(stderr, "%s\n", why); 40 | exit(1); 41 | } 42 | 43 | void cppvsprintf(std::string *out, const char *fmt, va_list va) 44 | { 45 | va_list va2; 46 | va_copy(va2, va); 47 | 48 | int len = vsnprintf(0, 0, fmt, va); 49 | size_t size = out->size(); 50 | out->resize(size + len); 51 | vsnprintf(&(*out)[size], len+1, fmt, va2); 52 | 53 | va_end(va2); 54 | } 55 | 56 | void cppsprintf(std::string *out, const char *fmt, ...) 57 | { 58 | va_list va; 59 | va_start(va, fmt); 60 | cppvsprintf(out, fmt, va); 61 | va_end(va); 62 | } 63 | 64 | const Type *is_function_proto(const Type *t) 65 | { 66 | switch (t->getTypeClass()) { 67 | case Type::FunctionProto: 68 | return cast(t); 69 | case Type::Paren: 70 | return is_function_proto(cast(t)->getInnerType().getTypePtr()); 71 | default: 72 | break; 73 | } 74 | return 0; 75 | } 76 | 77 | struct CToKrawlContext { 78 | ASTContext *ctx; 79 | SourceManager *srcm; 80 | unordered_set filenames; 81 | unordered_set defined; 82 | unordered_map structs; 83 | unordered_map consts; 84 | std::string macros; 85 | std::string funcmock; 86 | 87 | CToKrawlContext() 88 | { 89 | macros.reserve(65536); 90 | funcmock.reserve(65536); 91 | funcmock.append("void ___foo___() {\n"); 92 | } 93 | }; 94 | 95 | struct CToKrawlASTConsumer : ASTConsumer { 96 | CToKrawlContext *ctx; 97 | 98 | CToKrawlASTConsumer(CToKrawlContext *ctx): ctx(ctx) 99 | { 100 | } 101 | 102 | bool was_defined(const char *what) 103 | { 104 | unordered_set::iterator it; 105 | it = ctx->defined.find(what); 106 | return it != ctx->defined.end(); 107 | } 108 | 109 | void define(const char *what) 110 | { 111 | ctx->defined.insert(what); 112 | } 113 | 114 | std::string *struct_was_defined(const char *what) 115 | { 116 | unordered_map::iterator it; 117 | it = ctx->structs.find(what); 118 | if (it == ctx->structs.end()) 119 | return 0; 120 | 121 | return &it->second; 122 | } 123 | 124 | std::string struct_body_to_krawl(RecordDecl *rd) 125 | { 126 | // union case 127 | std::string out; 128 | if (rd->getTypeForDecl()->isUnionType()) 129 | out += "union { "; 130 | else 131 | out += "struct { "; 132 | RecordDecl::field_iterator it, end; 133 | for (it = rd->field_begin(), end = rd->field_end(); it != end; ++it) { 134 | llvm::StringRef keyword = it->getName(); 135 | for (size_t i = 0, n = sizeof(keywords)/sizeof(keywords[0]); i < n; ++i) { 136 | if (keyword == keywords[i]) { 137 | out += "_"; 138 | break; 139 | } 140 | } 141 | 142 | out += keyword.str(); 143 | out += " "; 144 | out += clang_type_to_krawl(it->getType().getTypePtr()); 145 | out += "; "; 146 | } 147 | out += "}"; 148 | return out; 149 | } 150 | 151 | std::string clang_type_to_krawl(const Type *t) 152 | { 153 | std::string out; 154 | 155 | switch (t->getTypeClass()) { 156 | case Type::Builtin: 157 | { 158 | const BuiltinType *bt = cast(t); 159 | if (bt->isInteger()) { 160 | if (bt->getKind() == BuiltinType::Char_S || 161 | bt->getKind() == BuiltinType::Char_U) 162 | { 163 | // special case, unspecified char is uint8 in krawl 164 | out.append("uint8"); 165 | return out; 166 | } 167 | 168 | if (bt->isUnsignedInteger()) 169 | out.append("u"); 170 | switch (ctx->ctx->getTypeSize(bt)) { 171 | case 8: out.append("int8"); break; 172 | case 16: out.append("int16"); break; 173 | case 32: out.append("int32"); break; 174 | case 64: out.append("int64"); break; 175 | default: 176 | die("unsupported integer type"); 177 | } 178 | return out; 179 | } else if (bt->isFloatingPoint()) { 180 | switch (ctx->ctx->getTypeSize(bt)) { 181 | case 32: out.append("float32"); break; 182 | case 64: out.append("float64"); break; 183 | default: 184 | // fucking ugly hack for long doubles 185 | cppsprintf(&out, "struct { _ [%d]byte; }", 186 | ctx->ctx->getTypeSize(bt) / 8); 187 | break; 188 | } 189 | return out; 190 | } else if (bt->getKind() == BuiltinType::Void) { 191 | out.append("void"); 192 | return out; 193 | } 194 | break; 195 | } 196 | case Type::Pointer: 197 | { 198 | const PointerType *pt = cast(t); 199 | 200 | // check if it's a function pointer 201 | const Type *isft = is_function_proto(pt->getPointeeType().getTypePtr()); 202 | if (isft) 203 | return clang_type_to_krawl(isft); 204 | 205 | out += "*"; 206 | out += clang_type_to_krawl(pt->getPointeeType().getTypePtr()); 207 | return out; 208 | } 209 | case Type::Typedef: 210 | { 211 | const TypedefType *tt = cast(t); 212 | return tt->getDecl()->getDeclName().getAsString(); 213 | } 214 | case Type::Record: 215 | { 216 | const RecordType *rt = cast(t); 217 | RecordDecl *rd = rt->getDecl(); 218 | if (!rd->getName().empty()) { 219 | std::string name = rd->getNameAsString(); 220 | if (!struct_was_defined(name.c_str())) 221 | ctx->structs[name] = ""; 222 | out += "_struct__"; 223 | out += name; 224 | return out; 225 | } 226 | 227 | out += struct_body_to_krawl(rd); 228 | 229 | return out; 230 | } 231 | case Type::TypeOfExpr: 232 | { 233 | const TypeOfExprType *tot = cast(t); 234 | return clang_type_to_krawl(tot->getUnderlyingExpr()->getType().getTypePtr()); 235 | } 236 | case Type::ConstantArray: 237 | { 238 | const ConstantArrayType *cat = cast(t); 239 | cppsprintf(&out, "[%d]", cat->getSize().getZExtValue()); 240 | out += clang_type_to_krawl(cat->getElementType().getTypePtr()); 241 | return out; 242 | } 243 | case Type::FunctionProto: 244 | { 245 | const FunctionProtoType *fpt = cast(t); 246 | out += "func ("; 247 | for (size_t i = 0, n = fpt->getNumArgs(); i < n; ++i) { 248 | out += clang_type_to_krawl(fpt->getArgType(i).getTypePtr()); 249 | if (i != n-1) 250 | out += ", "; 251 | } 252 | if (fpt->isVariadic()) 253 | out += ", ..."; 254 | out += ") "; 255 | out += clang_type_to_krawl(fpt->getResultType().getTypePtr()); 256 | return out; 257 | } 258 | case Type::Paren: 259 | { 260 | const ParenType *pt = cast(t); 261 | return clang_type_to_krawl(pt->getInnerType().getTypePtr()); 262 | } 263 | case Type::Enum: 264 | { 265 | const EnumType *et = cast(t); 266 | const EnumDecl *ed = et->getDecl(); 267 | return clang_type_to_krawl(ed->getIntegerType().getTypePtr()); 268 | } 269 | default: 270 | break; 271 | } 272 | 273 | printf("!!!!!!!!!!!!%s\n", t->getTypeClassName()); 274 | return "???"; 275 | } 276 | 277 | void process_function_decl(FunctionDecl *fd) 278 | { 279 | std::string name = fd->getDeclName().getAsString(); 280 | if (was_defined(name.c_str())) 281 | return; 282 | 283 | define(name.c_str()); 284 | 285 | std::string out = "func "; 286 | out += name; 287 | out += "("; 288 | for (size_t i = 0, n = fd->getNumParams(); i < n; ++i) { 289 | ParmVarDecl *arg = fd->getParamDecl(i); 290 | /* 291 | don't care about name 292 | if (!arg->getName().empty()) { 293 | out += arg->getNameAsString(); 294 | out += " "; 295 | } 296 | */ 297 | out += clang_type_to_krawl(arg->getType().getTypePtr()); 298 | if (i != n-1) 299 | out += ", "; 300 | } 301 | if (fd->isVariadic()) 302 | out += ", ..."; 303 | out += ")"; 304 | std::string result = clang_type_to_krawl(fd->getResultType().getTypePtr()); 305 | if (result != "void") { 306 | out += " "; 307 | out += result; 308 | } 309 | printf("%s;\n", out.c_str()); 310 | } 311 | 312 | void process_typedef_decl(TypedefDecl *td) 313 | { 314 | std::string name = td->getDeclName().getAsString(); 315 | if (was_defined(name.c_str())) 316 | return; 317 | 318 | define(name.c_str()); 319 | 320 | std::string out = "type "; 321 | out += name; 322 | out += " "; 323 | out += clang_type_to_krawl(td->getUnderlyingType().getTypePtr()); 324 | printf("%s;\n", out.c_str()); 325 | } 326 | 327 | void process_record_decl(RecordDecl *rd) 328 | { 329 | if (rd->getName().empty()) 330 | return; 331 | if (!rd->isDefinition()) 332 | return; 333 | 334 | std::string name = rd->getNameAsString(); 335 | std::string *def = struct_was_defined(name.c_str()); 336 | if (!def) { 337 | ctx->structs[name] = ""; 338 | def = &ctx->structs[name]; 339 | } 340 | 341 | if (!def->empty()) { 342 | die("should never happen"); 343 | return; 344 | } 345 | 346 | *def = struct_body_to_krawl(rd); 347 | } 348 | 349 | void process_enum_decl(EnumDecl *ed) 350 | { 351 | ctx->macros.append("enum {\n"); 352 | EnumDecl::enumerator_iterator it, end; 353 | for (it = ed->enumerator_begin(), end = ed->enumerator_end(); it != end; ++it) { 354 | llvm::SmallVector tmp; 355 | it->getInitVal().toString(tmp); 356 | std::string name = it->getNameAsString(); 357 | 358 | ctx->macros.append("\t"); 359 | ctx->macros.append(name); 360 | ctx->macros.append(" = "); 361 | ctx->macros.append(&tmp[0], tmp.size()); 362 | ctx->macros.append(",\n"); 363 | 364 | if (ctx->consts.find(name) != ctx->consts.end()) 365 | continue; 366 | ctx->consts[name] = std::string(tmp.begin(), tmp.end()); 367 | } 368 | ctx->macros.append("};\n"); 369 | } 370 | 371 | void HandleTopLevelDecl(DeclGroupRef d) 372 | { 373 | DeclGroupRef::iterator it, end; 374 | for (it = d.begin(), end = d.end(); it != end; ++it) { 375 | Decl *d = *it; 376 | switch (d->getKind()) { 377 | case Decl::Function: 378 | process_function_decl(cast(*it)); 379 | break; 380 | case Decl::Typedef: 381 | process_typedef_decl(cast(*it)); 382 | break; 383 | case Decl::Record: 384 | process_record_decl(cast(*it)); 385 | break; 386 | case Decl::Enum: 387 | process_enum_decl(cast(*it)); 388 | break; 389 | default: 390 | break; 391 | } 392 | 393 | } 394 | } 395 | 396 | void HandleTranslationUnit(ASTContext &unused) 397 | { 398 | unordered_map::iterator it, end; 399 | it = ctx->structs.begin(); 400 | end = ctx->structs.end(); 401 | 402 | for (; it != end; ++it) { 403 | std::string def = "struct {}"; 404 | if (!it->second.empty()) 405 | def = it->second; 406 | 407 | printf("type _struct__%s %s;\n", it->first.c_str(), def.c_str()); 408 | } 409 | } 410 | }; 411 | 412 | struct ConstantExprExtractor : ASTConsumer { 413 | ASTContext *ctx; 414 | SourceManager *srcm; 415 | unordered_map *consts; 416 | 417 | llvm::StringRef extract_expr_name(Expr *e) 418 | { 419 | SourceLocation loc = e->getExprLoc(); 420 | SourceLocation instance_loc = srcm->getInstantiationLoc(loc); 421 | const char *beg = srcm->getCharacterData(instance_loc); 422 | const char *end = strchr(beg, ';'); 423 | return llvm::StringRef(beg, end-beg); 424 | } 425 | 426 | void process_function_decl(FunctionDecl *fd) 427 | { 428 | Stmt *s = fd->getBody(); 429 | Stmt::child_iterator it, end; 430 | for (it = s->child_begin(), end = s->child_end(); it != end; ++it) { 431 | Expr *e = dyn_cast(*it); 432 | if (!e) 433 | continue; 434 | 435 | Expr::EvalResult er; 436 | bool ok = e->Evaluate(er, *ctx); 437 | if (!ok) 438 | continue; 439 | 440 | llvm::SmallVector tmp; 441 | APValue &v = er.Val; 442 | switch (v.getKind()) { 443 | case APValue::Int: 444 | v.getInt().toString(tmp); 445 | break; 446 | case APValue::Float: 447 | v.getFloat().toString(tmp); 448 | break; 449 | default: 450 | break; 451 | } 452 | 453 | if (!tmp.empty()) { 454 | std::string name = extract_expr_name(e).str(); 455 | if (consts->find(name) != consts->end()) 456 | continue; 457 | 458 | (*consts)[name] = std::string(tmp.begin(), tmp.end()); 459 | } 460 | } 461 | } 462 | 463 | void HandleTopLevelDecl(DeclGroupRef d) 464 | { 465 | DeclGroupRef::iterator it, end; 466 | for (it = d.begin(), end = d.end(); it != end; ++it) { 467 | Decl *d = *it; 468 | if (d->isFunctionOrFunctionTemplate()) 469 | process_function_decl(cast(d)); 470 | } 471 | } 472 | }; 473 | 474 | struct PPIncludeCollector : PPCallbacks { 475 | CToKrawlContext *ctx; 476 | 477 | PPIncludeCollector(CToKrawlContext *ctx): ctx(ctx) 478 | { 479 | } 480 | 481 | void FileChanged(SourceLocation loc, FileChangeReason reason, SrcMgr::CharacteristicKind filetype) 482 | { 483 | if (reason == EnterFile && loc.isFileID() && 484 | !ctx->srcm->isFromMainFile(loc)) 485 | { 486 | PresumedLoc pl = ctx->srcm->getPresumedLoc(loc); 487 | if (pl.getFilename()[0] != '<') 488 | ctx->filenames.insert(pl.getFilename()); 489 | } 490 | } 491 | 492 | bool is_builtin(const MacroInfo *mi) 493 | { 494 | SourceLocation loc = mi->getDefinitionLoc(); 495 | PresumedLoc ploc = ctx->srcm->getPresumedLoc(loc); 496 | if (strcmp("", ploc.getFilename()) == 0) 497 | return true; 498 | return false; 499 | } 500 | 501 | void append_macro(const MacroInfo *mi) 502 | { 503 | if (mi->tokens_empty()) 504 | return; 505 | if (is_builtin(mi)) 506 | return; 507 | 508 | MacroInfo::tokens_iterator last = mi->tokens_end() - 1; 509 | SourceLocation beg = mi->getDefinitionLoc(); 510 | SourceLocation end = last->getLocation(); 511 | 512 | const char *s = ctx->srcm->getCharacterData(beg); 513 | const char *e = ctx->srcm->getCharacterData(end) + last->getLength(); 514 | 515 | ctx->macros.append("#define "); 516 | ctx->macros.append(s, e-s); 517 | ctx->macros.append("\n"); 518 | } 519 | 520 | void MacroDefined(const Token &name, const MacroInfo *mi) 521 | { 522 | append_macro(mi); 523 | 524 | // we support only zero arg macros, which are constant 525 | // definitions, most likely 526 | if (mi->getNumArgs() != 0) 527 | return; 528 | 529 | if (mi->tokens_empty()) 530 | return; 531 | 532 | // for some reason mi->isBuiltinMacro() doesn't work 533 | if (is_builtin(mi)) 534 | return; 535 | 536 | llvm::StringRef nm = name.getIdentifierInfo()->getName(); 537 | ctx->funcmock.append("\t"); 538 | ctx->funcmock.append(nm.data(), nm.size()); 539 | ctx->funcmock.append(";\n"); 540 | } 541 | 542 | void EndOfMainFile() 543 | { 544 | ctx->funcmock.append("}\n"); 545 | ctx->macros.append(ctx->funcmock); 546 | 547 | // Parse macros mock 548 | CompilerInstance ci; 549 | ci.getLangOpts().C99 = 1; 550 | ci.getLangOpts().CPlusPlus = 0; 551 | 552 | ci.getTargetOpts().Triple = llvm::sys::getHostTriple(); 553 | 554 | ci.createDiagnostics(0, 0); 555 | ci.setTarget(TargetInfo::CreateTargetInfo(ci.getDiagnostics(), 556 | ci.getTargetOpts())); 557 | ci.getDiagnostics().setSuppressAllDiagnostics(); 558 | ci.getDiagnostics().setErrorsAsFatal(false); 559 | 560 | ci.createFileManager(); 561 | ci.createSourceManager(ci.getFileManager()); 562 | ci.createPreprocessor(); 563 | ci.createASTContext(); 564 | 565 | llvm::MemoryBuffer *mb; 566 | mb = llvm::MemoryBuffer::getMemBuffer(ctx->macros, "macrosmock.c"); 567 | ci.getSourceManager().createMainFileIDForMemBuffer(mb); 568 | 569 | ConstantExprExtractor consumer; 570 | consumer.ctx = &ci.getASTContext(); 571 | consumer.srcm = &ci.getSourceManager(); 572 | consumer.consts = &ctx->consts; 573 | 574 | ParseAST(ci.getPreprocessor(), &consumer, ci.getASTContext()); 575 | // DONE 576 | 577 | { 578 | unordered_map::iterator it, end; 579 | it = ctx->consts.begin(); 580 | end = ctx->consts.end(); 581 | for (; it != end; ++it) { 582 | printf("const %s = %s;\n", 583 | it->first.c_str(), it->second.c_str()); 584 | } 585 | } 586 | 587 | printf("-*-\n"); 588 | unordered_set::iterator it, end; 589 | it = ctx->filenames.begin(); 590 | end = ctx->filenames.end(); 591 | for (; it != end; ++it) 592 | printf("%s\n", it->c_str()); 593 | } 594 | }; 595 | 596 | struct CToKrawl : PluginASTAction { 597 | CToKrawlContext context; 598 | 599 | bool BeginSourceFileAction(CompilerInstance &CI, llvm::StringRef filename) 600 | { 601 | PPIncludeCollector *ppi = new PPIncludeCollector(&context); 602 | CI.getPreprocessor().addPPCallbacks(ppi); 603 | return true; 604 | } 605 | 606 | ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef) 607 | { 608 | context.ctx = &CI.getASTContext(); 609 | context.srcm = &CI.getSourceManager(); 610 | return new CToKrawlASTConsumer(&context); 611 | } 612 | 613 | bool ParseArgs(const CompilerInstance &CI, 614 | const std::vector &args) 615 | { 616 | return true; 617 | } 618 | 619 | void PrintHelp(llvm::raw_ostream &out) 620 | { 621 | out << "Help for CToKrawl goes here\n"; 622 | } 623 | }; 624 | 625 | } // anonymous namespace 626 | 627 | static FrontendPluginRegistry::Add 628 | X("c-to-krawl", "convert C declarations to Krawl"); 629 | -------------------------------------------------------------------------------- /ctokrawl/wscript: -------------------------------------------------------------------------------- 1 | def build(bld): 2 | bld.shlib( 3 | source = 'ctokrawl.cpp', 4 | target = 'ctokrawl', 5 | defines = bld.env.DEFINES_LLVM, 6 | install_path = '${PREFIX}/lib/krawl', 7 | use = 'CLANG_PLUGIN' 8 | ) 9 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | ASCIIDOC_FLAGS := -a theme=flask -a toc2 -a icons -a iconsdir=icons 2 | DOCS := \ 3 | krawl_by_example.txt \ 4 | typesystem.txt \ 5 | 6 | DOCS_HTML := $(patsubst %.txt,%.html,$(DOCS)) 7 | 8 | all: $(DOCS_HTML) 9 | deploy: $(addprefix $(HOME)/storage/web/,$(DOCS_HTML)) 10 | 11 | krawl.lang: ../extra/krawl.lang 12 | cp ../extra/krawl.lang . 13 | 14 | $(HOME)/storage/web/%.html: %.html 15 | cp $*.html $(HOME)/storage/web/$*.html 16 | 17 | %.html: %.txt krawl.lang 18 | asciidoc $(ASCIIDOC_FLAGS) $*.txt 19 | 20 | clean: 21 | rm -rf $(DOCS_HTML) 22 | -------------------------------------------------------------------------------- /doc/TYPE_SYSTEM: -------------------------------------------------------------------------------- 1 | glossary for rules: 2 | # - means any without restrictions 3 | (can't use *, because it is a pointer symbol) 4 | [A-Z] - means any, but with restrictions 5 | (specified in parens) 6 | 7 | 8 | ========================================================================== 9 | = Assignable (implicitly convertable) 10 | ========================================================================== 11 | Cases: 12 | var X = Y; 13 | X := Y; 14 | foo(X, Y); 15 | return X, Y; 16 | 17 | 0. aint, afloat to int#, uint#, float# 18 | 1. abool, bool to bool 19 | 2. intX to intY (Y >= X) 20 | 3. uintX to intY (Y > X), uintZ (Z >= X) 21 | 4. float32 to float64 22 | 5. astring to *byte 23 | 6. *#, func(#)# to *void 24 | 7. *void to *#, func(#)# 25 | 8. *[#]T to *T 26 | 9. X to Y (if X and Y have the same underlying types) 27 | 28 | ========================================================================== 29 | = Convertable (explicitly) 30 | ========================================================================== 31 | Cases: 32 | X.(T); 33 | 34 | 1. int#, uint#, float# to int#, uint#, float# 35 | 2. int#, uint#, *# to int#, uint#, *# 36 | 3. assignable(X, Y) 37 | 38 | ========================================================================== 39 | = Arithmetic implicit conversion 40 | ========================================================================== 41 | Valid only for int#, uint#, float# operands 42 | 43 | (rules should be applied in the given order, each rule in case of success, 44 | terminates conversion) 45 | 46 | 1. If one operand is float64, the other operand is converted to float64. 47 | 2. If one operand is float32, the other operand is converted to float32. 48 | 3. If both operands are uint# or int#, smaller is converted to the bigger. 49 | 4. If the bigger operand is int#, smaller is converted to the bigger. 50 | 5. (The case where one operand is uintX and the other operand is intY, and 51 | X >= Y is not binary op compatible) 52 | 53 | ========================================================================== 54 | = Binary op compatible 55 | ========================================================================== 56 | Cases: 57 | X op Y 58 | 59 | op(==), op(!=), 60 | defined for: int#, uint#, float#, *#, string, bool 61 | 1. int#, uint# OP int#, uint# 62 | 2. float# OP float# 63 | 3. *# OP *# 64 | 4. stirng OP string 65 | 5. bool OP bool 66 | 67 | op(+), 68 | defined for: int#, uint#, float#, *#, string 69 | 1. int#, uint# OP int#, uint# 70 | 2. float# OP float# 71 | 3. int#, uint#, *# OP int#, uint#, *# 72 | 4. string OP string 73 | 74 | op(-), 75 | defined for: int#, uint#, float#, *# 76 | 1. int#, uint# OP int#, uint# 77 | 2. float# OP float# 78 | 3. *# OP int#, uint#, *# 79 | 80 | op(<), op(<=), op(>), op(>=), 81 | defined for: int#, uint#, float#, *# 82 | 1. int#, uint# OP int#, uint# 83 | 2. float# OP float# 84 | 3. *# OP *# 85 | 86 | op(*), op(/), 87 | defined for: int#, uint#, float# 88 | 1. int#, uint# OP int#, uint# 89 | 2. float# OP float# 90 | 91 | op(&), op(|), op(^), op(%), 92 | defined for: int#, uint# 93 | 1. int#, uint# OP int#, uint# 94 | 95 | op(<<), op(>>), 96 | defined for: int#, uint# 97 | 1. int#, uint# OP uint# 98 | 99 | op(&&), op(||), 100 | defined for: bool 101 | 1. bool OP bool 102 | -------------------------------------------------------------------------------- /doc/lang.map: -------------------------------------------------------------------------------- 1 | krawl = krawl.lang 2 | c = c.lang 3 | -------------------------------------------------------------------------------- /doc/typesystem.txt: -------------------------------------------------------------------------------- 1 | Type system 2 | =========== 3 | 4 | Krawl's type system mostly follows C's type system, but there are some differences. 5 | 6 | Types 7 | ----- 8 | A list of predefined primitive types: 9 | [cols="1,4,1"] 10 | |=========================================================================== 11 | | Type name | Description | Default value 12 | | `void` | No type | `-` 13 | | `bool` | Boolean type | `false` 14 | | `byte` | Alias to uint8 | `0` 15 | | `uint8` | Unsigned 8 bits integer | `0` 16 | | `int8` | Signed 8 bits integer | `0` 17 | | `uint16` | Unsigned 16 bits integer | `0` 18 | | `int16` | Signed 16 bits integer | `0` 19 | | `uint` | Alias to uint32 | `0` 20 | | `int` | Alias to int32 | `0` 21 | | `uint32` | Unsigned 32 bits integer | `0` 22 | | `int32` | Signed 32 bits integer | `0` 23 | | `uint64` | Unsigned 64 bits integer | `0` 24 | | `int64` | Signed 64 bits integer | `0` 25 | | `float` | Alias to float32 | `0.0` 26 | | `double` | Alias to float64 | `0.0` 27 | | `float32` | Single-precision 32 bits floating point number | `0.0` 28 | | `float64` | Double-precision 64 bits floating point number | `0.0` 29 | |=========================================================================== 30 | 31 | NOTE: Predefined types are not keywords, you can have a local variable named 32 | `int` if you want to. 33 | 34 | A list of aggregate (composite) types: 35 | [cols="1,5"] 36 | |=========================================================================== 37 | | Name | Syntax 38 | | Pointer | `*T` 39 | | Array | `[N]T` 40 | | Struct | `struct { field1 T; field2 T; ...; fieldN T; }` 41 | | Union | `union { field1 T; field2 T; ...; fieldN T; }` 42 | | Function | `func(arg1 T, arg2 T) T` 43 | |=========================================================================== 44 | 45 | Defining new types 46 | ------------------ 47 | A new type can be defined using type declaration. It has this form: "`type 48 | ;`". Few examples: 49 | 50 | [source,krawl] 51 | ---------------------------------------------------------------------------- 52 | type Size uint 53 | type Person struct { 54 | name *byte 55 | address Address 56 | age uint 57 | }; 58 | type StrCmpFunc func(a, b *byte) int 59 | type Vector3 [3]float 60 | ---------------------------------------------------------------------------- 61 | 62 | You can also define multiple types at once, using this syntax: 63 | 64 | [source,krawl] 65 | ---------------------------------------------------------------------------- 66 | type ( 67 | Size uint 68 | Person struct { 69 | name *byte 70 | address Address 71 | age uint 72 | }; 73 | StrCmpFunc func(a, b *byte) int 74 | Vector3 [3]float 75 | ) 76 | ---------------------------------------------------------------------------- 77 | 78 | Implicit conversions 79 | -------------------- 80 | There are several cases when one type gets converted into another implicitly. 81 | 82 | Assignment 83 | ~~~~~~~~~~ 84 | When you're assigning a result of an expression to a variable or passing an 85 | argument to a function or returning a value from a function. It's all is 86 | considered as assignment. During that assignment, implicit conversion rules are 87 | applied, here they are: 88 | 89 | 1. Any integer type can be converted to any integer type. 90 | 2. Any floating point type can be converted to any floating point type. 91 | 3. Any pointer type (including function pointer type) can be converted to `*void`. 92 | 4. `*void` can be converted to any pointer type (including function pointer type). 93 | 5. A pointer to an array type can be converted to a pointer to the array's 94 | element type. 95 | 96 | Some examples: 97 | 98 | [source,krawl] 99 | ---------------------------------------------------------------------------- 100 | var ip *int 101 | var vp *void = ip 102 | 103 | var vec [3]float 104 | var pfloat *float = &vec 105 | 106 | var a uint64 107 | var b int16 = a 108 | 109 | func foo() byte { 110 | var i int 111 | return i // int truncated to byte 112 | } 113 | 114 | func bar(a int8, b int16, c uint32) 115 | 116 | // somewhere in the code 117 | var x int 118 | bar(x, x, x) 119 | ---------------------------------------------------------------------------- 120 | 121 | Arithmetic 122 | ~~~~~~~~~~ 123 | Most of the binary expression operators require both arguments of the same 124 | type. Therefore it is necessary to find a common type and to cast both operands 125 | to that type before evaluation. These are the rules for finding a common type 126 | of a binary expression: 127 | 128 | 1. If both operands are floating point types, the common type of a binary 129 | expression is the biggest floating point type amongst these two. 130 | 2. If both operands are integer types, the common type of a binary expression 131 | is found using these rules: 132 | + 133 | - If both are signed or both are unsigned, the smaller type is converted to the larger. 134 | - If the signed type is larger or the same size as the unsigned type, the 135 | unsigned type is converted to the signed type. 136 | - If the unsigned type is larger than the signed type, both are converted to 137 | a signed type as large as unsigned type. 138 | 139 | Some examples: 140 | 141 | [source,krawl] 142 | ---------------------------------------------------------------------------- 143 | var a float32 144 | var b float64 145 | var c = a + b // a is converted to float64 146 | 147 | var a int 148 | var b uint 149 | var c = a - b // b is converted to int32 (int is an alias to int32) 150 | 151 | var a int32 152 | var b uint8 153 | var c = a * b // b is converted to int32 154 | 155 | var a uint32 156 | var b int16 157 | var c = a / b // a and b are converted to int32 158 | ---------------------------------------------------------------------------- 159 | 160 | Resulting type of an expression 161 | ------------------------------- 162 | The resulting type of an expression is the same as the common type in all 163 | cases, except these ones: 164 | 165 | - For comparison operators `==`, `!=`, `>`, `>=`, `<` and `<=` the resulting 166 | type is always `bool`. 167 | - For shift operators `<<` and `>>` the resulting type is the type of the left 168 | operand. 169 | - The resulting type of a pointer difference is an integer type as large as the 170 | pointer type. 171 | - The resulting type of other kinds of pointer arithmetic is the pointer type. 172 | 173 | Integer promotion 174 | ----------------- 175 | In Krawl integer promotion rule applies silently behind the scenes when doing 176 | expressions evaluation with smaller than 32 bits operands. All of these 177 | (`uint8`, `int8`, `uint16`, `int16`) types are promoted to `int32`. It prevents 178 | some of the overflow errors, but keep in mind that integer promotion doesn't 179 | affect the resulting type of an expression. Few good examples: 180 | 181 | [source,krawl] 182 | ---------------------------------------------------------------------------- 183 | var a, b uint8 = 250, 200 184 | var average = (a + b) / 2 // the result is 225, average's type is uint8 185 | 186 | type Packet { 187 | size uint8 188 | }; 189 | 190 | func SizeRequired(p1, p2, p3 *Packet) int { 191 | // this expression doesn't overflow and the result can be bigger than 192 | // uint8 can hold, because it is assigned to an int at the end 193 | return p1.size + p2.size + p3.size 194 | } 195 | ---------------------------------------------------------------------------- 196 | 197 | -------------------------------------------------------------------------------- /examples/args.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func main(argc int, argv **byte) int { 4 | C.printf("Command line arguments are:\n") 5 | for i := 0; i < argc; i++ { 6 | C.printf("%d: %s\n", i, argv[i]) 7 | } 8 | return 0 9 | } 10 | -------------------------------------------------------------------------------- /examples/array-value-semantics.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func print_array(a [5]int) { 4 | for i := 0; i < 5; i++ { 5 | C.printf("%d\n", a[i]) 6 | } 7 | } 8 | 9 | func mutate_array(a [5]int) { 10 | C.printf("in mutator, before:\n") 11 | print_array(a) 12 | for i := 0; i < 5; i++ { 13 | a[i] *= 1000 14 | } 15 | C.printf("in mutator, after:\n") 16 | print_array(a) 17 | } 18 | 19 | func main(argc int, argv **byte) int { 20 | var a []int = {1, 2, 3, 4, 5} 21 | C.printf("before mutation:\n") 22 | print_array(a) 23 | 24 | mutate_array(a) 25 | 26 | C.printf("after mutation:\n") 27 | print_array(a) 28 | return 0 29 | } 30 | -------------------------------------------------------------------------------- /examples/fib.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func fib(n int) int { 4 | if n < 2 { 5 | return n 6 | } else { 7 | return fib(n - 1) + fib(n - 2) 8 | } 9 | } 10 | 11 | func main(argc int, argv **byte) int { 12 | for i := 0; i < 20; i++ { 13 | C.printf("fib(%d): %d\n", i, fib(i)) 14 | } 15 | return 0 16 | } 17 | -------------------------------------------------------------------------------- /examples/funcptr.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func GetFive() int { 4 | return 5 5 | } 6 | 7 | func main(argc int, argv **byte) int { 8 | x := GetFive 9 | C.printf("%d\n", x()) 10 | return 0 11 | } 12 | -------------------------------------------------------------------------------- /examples/loops.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func geti(i int) int { 4 | return i 5 | } 6 | 7 | func increment(i int) int { 8 | i++ 9 | return i 10 | } 11 | 12 | func main(argc int, argv **byte) int { 13 | i := 0 14 | // while (1) kind of loop 15 | for { 16 | if i > 5 { 17 | break 18 | } 19 | C.printf("first loop i: %d\n", i) 20 | i++ 21 | } 22 | 23 | // while (expr) kind of loop 24 | for i > 5 && i < 10 { 25 | C.printf("second loop i: %d\n", i) 26 | i++ 27 | } 28 | 29 | // C's standard for loop 30 | for i := 0; i < 10; i++ { 31 | if i % 2 == 0 { 32 | // codegen check here 33 | i := 50 34 | i = 7 35 | continue 36 | } 37 | C.printf("third loop i: %d\n", i) 38 | } 39 | 40 | // loop with side effects in its header statements 41 | for i := 0; geti(i) < 5; i = increment(geti(i)) { 42 | C.printf("fourth loop i: %d\n", i) 43 | } 44 | return 0 45 | } 46 | -------------------------------------------------------------------------------- /examples/projecteuler/problem1.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | const below = 1000 4 | 5 | func main(argc int, argv **byte) int { 6 | sum := 0 7 | for i := 0; i < below; i++ { 8 | if i % 3 == 0 || i % 5 == 0 { 9 | sum += i 10 | } 11 | } 12 | C.printf("sum: %d\n", sum) 13 | return 0 14 | } 15 | -------------------------------------------------------------------------------- /examples/projecteuler/problem2.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func fib(n int) int { 4 | a, b := 1, 2 5 | res := 0 6 | 7 | for b <= n { 8 | if b % 2 == 0 { 9 | res += b 10 | } 11 | a, b = b, a + b 12 | } 13 | 14 | return res 15 | } 16 | 17 | func main(argc int, argv **byte) int { 18 | lim := 4000000 19 | C.printf("%d\n", fib(lim)) 20 | return 0 21 | } 22 | -------------------------------------------------------------------------------- /examples/projecteuler/problem8.krl: -------------------------------------------------------------------------------- 1 | import ( 2 | "stdio.h" 3 | "stdlib.h" 4 | "ctype.h" 5 | ) 6 | 7 | func read_file(filename *byte) (*byte, int) { 8 | f := stdio.fopen(filename, "r") 9 | if f == nil { 10 | return nil, 0 11 | } 12 | 13 | var read_n stdio.size_t 14 | var alloc_n stdio.size_t = 1024 15 | var buf *byte = stdlib.malloc(1024) 16 | 17 | for stdio.feof(f) == 0 { 18 | if stdio.ferror(f) != 0 { 19 | stdio.fclose(f) 20 | stdlib.free(buf) 21 | return nil, 0 22 | } 23 | 24 | if alloc_n < read_n + 1024 { 25 | buf = stdlib.realloc(buf, alloc_n) 26 | alloc_n *= 2 27 | } 28 | n := stdio.fread(buf + read_n, 1, 1024, f) 29 | read_n += n 30 | } 31 | 32 | stdio.fclose(f) 33 | return buf, read_n 34 | } 35 | 36 | func remove_nondigits(data *byte, size int) int { 37 | j := 0 38 | for i := 0; i < size; i++ { 39 | if ctype.isdigit(data[i]) == 0 { 40 | continue 41 | } 42 | data[j] = data[i] 43 | j++ 44 | } 45 | return j 46 | } 47 | 48 | func find_max(data *byte, size int) { 49 | var maxprod int 50 | var maxset [5]byte 51 | var curset [5]byte 52 | 53 | for i := 0; i < size-4; i++ { 54 | for j := 0; j < 5; j++ { 55 | curset[j] = data[i+j] - '0' 56 | } 57 | curprod := 1 58 | for j := 0; j < 5; j++ { 59 | curprod *= curset[j] 60 | } 61 | if curprod > maxprod { 62 | maxprod = curprod 63 | maxset = curset 64 | } 65 | } 66 | 67 | stdio.printf("the greatest product is: %d\n", maxprod) 68 | stdio.printf("received using this set: %d%d%d%d%d\n", 69 | maxset[0], maxset[1], maxset[2], maxset[3], maxset[4]) 70 | } 71 | 72 | func main(argc int, argv **byte) int { 73 | data, size := read_file("problem8.txt") 74 | if data == nil { 75 | stdio.printf("failed to read a file\n") 76 | return 1 77 | } 78 | size = remove_nondigits(data, size) 79 | find_max(data, size) 80 | return 0 81 | } 82 | -------------------------------------------------------------------------------- /examples/projecteuler/problem8.txt: -------------------------------------------------------------------------------- 1 | 73167176531330624919225119674426574742355349194934 2 | 96983520312774506326239578318016984801869478851843 3 | 85861560789112949495459501737958331952853208805511 4 | 12540698747158523863050715693290963295227443043557 5 | 66896648950445244523161731856403098711121722383113 6 | 62229893423380308135336276614282806444486645238749 7 | 30358907296290491560440772390713810515859307960866 8 | 70172427121883998797908792274921901699720888093776 9 | 65727333001053367881220235421809751254540594752243 10 | 52584907711670556013604839586446706324415722155397 11 | 53697817977846174064955149290862569321978468622482 12 | 83972241375657056057490261407972968652414535100474 13 | 82166370484403199890008895243450658541227588666881 14 | 16427171479924442928230863465674813919123162824586 15 | 17866458359124566529476545682848912883142607690042 16 | 24219022671055626321111109370544217506941658960408 17 | 07198403850962455444362981230987879927244284909188 18 | 84580156166097919133875499200524063689912560717606 19 | 05886116467109405077541002256983155200055935729725 20 | 71636269561882670428252483600823257530420752963450 21 | -------------------------------------------------------------------------------- /examples/qsort.krl: -------------------------------------------------------------------------------- 1 | import ( 2 | "stdio.h" 3 | "stdlib.h" 4 | ) 5 | 6 | func sort_ints(a, b *void) int { 7 | if *a.(*int) > *b.(*int) { 8 | return 1 9 | } 10 | return 0 11 | } 12 | 13 | func main(argc int, argv **byte) int { 14 | var a []int = {5, 3, 1, 4, 2} 15 | stdlib.qsort(&a, 5, 4, sort_ints) 16 | 17 | for i := 0; i < 5; i++ { 18 | stdio.printf("%d\n", a[i]) 19 | } 20 | return 0 21 | } 22 | -------------------------------------------------------------------------------- /examples/sdl-grad.krl: -------------------------------------------------------------------------------- 1 | 2 | import "SDL/SDL.h" 3 | 4 | func put_pixel(screen *SDL.Surface, x, y, r, g, b int) { 5 | p := screen.pixels.(*byte) + 6 | y * screen.pitch + 7 | x * screen.format.BytesPerPixel 8 | 9 | p[0] = r 10 | p[1] = g 11 | p[2] = b 12 | } 13 | 14 | func fill(screen *SDL.Surface) { 15 | step := 1.0 / 512 16 | col := 0.0 17 | 18 | for y := 0; y < 512; y++ { 19 | for x := 0; x < 512; x++ { 20 | icol := (col * 255).(int) 21 | put_pixel(screen, x, y, icol, icol, icol) 22 | } 23 | col += step 24 | } 25 | } 26 | 27 | func main(argc int, argv **byte) int { 28 | SDL.Init(SDL.INIT_VIDEO) 29 | screen := SDL.SetVideoMode(512, 512, 24, SDL.SWSURFACE) 30 | fill(screen) 31 | SDL.Flip(screen) 32 | 33 | var e SDL.Event 34 | for { 35 | for SDL.PollEvent(&e) != 0 { 36 | switch e._type { 37 | case SDL.QUIT: 38 | return 0 39 | case SDL.KEYDOWN: 40 | return 0 41 | } 42 | } 43 | } 44 | 45 | return 0 46 | } 47 | -------------------------------------------------------------------------------- /examples/structs.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | type Data struct { 4 | name *byte 5 | age int 6 | } 7 | 8 | type Weird struct { 9 | array [10]int 10 | inner struct { 11 | a, b, c int 12 | } 13 | } 14 | 15 | func main(argc int, argv **byte) int { 16 | var d Data = {"nsf", 23} 17 | var w Weird = {{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}, {11, 22, 33}} 18 | 19 | // pointer to an inner anonymous struct 20 | in := &w.inner 21 | in.a, in.b, in.c = 111, 222, 333 22 | 23 | // print everything 24 | C.printf("name: %s, age: %d\n", d.name, d.age) 25 | for i := 0; i < 10; i++ { 26 | C.printf("w.array[%d] == %d\n", i, w.array[i]) 27 | } 28 | C.printf("a: %d, b: %d, c: %d\n", w.inner.a, w.inner.b, w.inner.c) 29 | return 0 30 | } 31 | -------------------------------------------------------------------------------- /examples/switch.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | const ( 4 | STRING = iota 5 | INT 6 | FLOAT 7 | UNKNOWN 8 | ) 9 | 10 | func print_type(t int) { 11 | switch t { 12 | case STRING: 13 | C.printf("This is a string\n") 14 | case INT: 15 | C.printf("This is an int\n") 16 | case FLOAT: 17 | C.printf("This is a float\n") 18 | default: 19 | C.printf("Unknown type\n") 20 | } 21 | } 22 | 23 | func print_generic_type(t int) { 24 | switch t { 25 | case STRING: 26 | C.printf("This is a string\n") 27 | case INT, FLOAT: 28 | C.printf("This is a number\n") 29 | default: 30 | C.printf("Unknown type\n") 31 | } 32 | } 33 | 34 | func print_less_than_3(t int) { 35 | switch t { 36 | case 0: 37 | C.printf("0") 38 | fallthrough 39 | case 1: 40 | C.printf("1") 41 | fallthrough 42 | case 2: 43 | C.printf("2\n") 44 | } 45 | } 46 | 47 | func main(argc int, argv **byte) int { 48 | for i := 0; i < 5; i++ { 49 | print_type(i) 50 | } 51 | for i := 0; i < 5; i++ { 52 | print_generic_type(i) 53 | } 54 | for i := 0; i < 5; i++ { 55 | print_less_than_3(i) 56 | } 57 | return 0 58 | } 59 | -------------------------------------------------------------------------------- /examples/tetris.krl: -------------------------------------------------------------------------------- 1 | 2 | import "SDL/SDL.h" 3 | 4 | import ( 5 | "stdlib.h" 6 | "string.h" 7 | "stdio.h" 8 | ) 9 | 10 | const blockSize = 15 11 | const smallBlockSize = 9 12 | const smallBlockOffset = (blockSize - smallBlockSize) / 2 13 | 14 | // ████ 15 | // ████ 16 | const specN = ` 17 | 0110 18 | 1200 19 | 0000 20 | 0000 21 | ` 22 | // ████ 23 | // ████ 24 | const specNMirrored = ` 25 | 1100 26 | 0210 27 | 0000 28 | 0000 29 | ` 30 | // ██ 31 | // ██████ 32 | const specT = ` 33 | 0100 34 | 1210 35 | 0000 36 | 0000 37 | ` 38 | // ████████ 39 | const specI = ` 40 | 0100 41 | 0200 42 | 0100 43 | 0100 44 | ` 45 | // ████ 46 | // ████ 47 | const specB = ` 48 | 1100 49 | 1100 50 | 0000 51 | 0000 52 | ` 53 | // ██████ 54 | // ██ 55 | const specL = ` 56 | 0100 57 | 0200 58 | 0110 59 | 0000 60 | ` 61 | // ██████ 62 | // ██ 63 | const specLMirrored = ` 64 | 0100 65 | 0200 66 | 1100 67 | 0000 68 | ` 69 | 70 | var screen *SDL.Surface 71 | 72 | type TetrisBlockColor struct { 73 | r, g, b byte 74 | } 75 | 76 | var specColors []TetrisBlockColor = { 77 | {255, 0, 0}, 78 | {0, 255, 0}, 79 | {100, 100, 255}, 80 | {255, 255, 255}, 81 | {255, 0, 255}, 82 | {255, 255, 0}, 83 | {0, 255, 255}, 84 | } 85 | 86 | var specs []*byte = { 87 | specN, 88 | specNMirrored, 89 | specT, 90 | specI, 91 | specB, 92 | specL, 93 | specLMirrored, 94 | } 95 | 96 | func BlockDraw(x, y int, color TetrisBlockColor) { 97 | chalf := SDL.MapRGB(screen.format, 98 | color.r / 2, color.g / 2, color.b / 2) 99 | cfull := SDL.MapRGB(screen.format, 100 | color.r, color.g, color.b) 101 | 102 | r := {x, y, blockSize, blockSize}.(SDL.Rect) 103 | SDL.FillRect(screen, &r, chalf) 104 | 105 | r.x += smallBlockOffset 106 | r.y += smallBlockOffset 107 | r.w, r.h = smallBlockSize, smallBlockSize 108 | 109 | SDL.FillRect(screen, &r, cfull) 110 | } 111 | 112 | //------------------------------------------------------------------------- 113 | // TetrisBlock 114 | //------------------------------------------------------------------------- 115 | 116 | type TetrisBlock struct { 117 | filled bool 118 | color TetrisBlockColor 119 | } 120 | 121 | func TetrisBlockDraw(b *TetrisBlock, x, y int) { 122 | if !b.filled { 123 | return 124 | } 125 | 126 | BlockDraw(x, y, b.color) 127 | } 128 | 129 | //------------------------------------------------------------------------- 130 | // TetrisFigure 131 | //------------------------------------------------------------------------- 132 | 133 | type TetrisFigure struct { 134 | // center of the figure (range: 0..3 0..3) 135 | cx, cy int 136 | 137 | // position in blocks relative to top left tetris field block 138 | x, y int 139 | blocks [16]TetrisBlock 140 | class uint 141 | } 142 | 143 | func NewTetrisFigure(spec *byte, color TetrisBlockColor) (f *TetrisFigure) { 144 | f = stdlib.malloc(sizeof(TetrisFigure)) 145 | string.memset(f, 0, sizeof(*f)) 146 | f.x = 3 147 | f.cx = -1 148 | f.cy = -1 149 | 150 | len := string.strlen(spec) 151 | i := 0 152 | for *spec != 0 { 153 | switch *spec { 154 | case '2': 155 | f.cx = i % 4 156 | f.cy = i / 4 157 | fallthrough 158 | case '1': 159 | f.blocks[i].filled = true 160 | f.blocks[i].color = color 161 | fallthrough 162 | case '0': 163 | i++ 164 | } 165 | spec++ 166 | } 167 | } 168 | 169 | func NewRandomTetrisFigure() (f *TetrisFigure) { 170 | ri := stdlib.rand() % (sizeof(specs)/sizeof(specs[0])) 171 | f = NewTetrisFigure(specs[ri], specColors[ri]) 172 | f.class = ri 173 | } 174 | 175 | func NewRandomTetrisFigureNot(templ *TetrisFigure) (f *TetrisFigure) { 176 | var ri int 177 | for { 178 | ri = stdlib.rand() % (sizeof(specs)/sizeof(specs[0])) 179 | if ri != templ.class { 180 | break 181 | } 182 | } 183 | 184 | f = NewTetrisFigure(specs[ri], specColors[ri]) 185 | f.class = ri 186 | } 187 | 188 | func TetrisFigureSetColor(f *TetrisFigure, color TetrisBlockColor) { 189 | for i := 0; i < 16; i++ { 190 | if !f.blocks[i].filled { 191 | continue 192 | } 193 | 194 | f.blocks[i].color = color 195 | } 196 | } 197 | 198 | func RotateCWBlock(x, y int) (ox, oy int) { 199 | ox, oy = -y, x 200 | } 201 | 202 | func RotateCCWBlock(x, y int) (ox, oy int) { 203 | ox, oy = y, -x 204 | } 205 | 206 | type RotateFunc func(int, int) (int, int) 207 | 208 | 209 | func TetrisFigureGetRotNum(f *TetrisFigure, rotate RotateFunc) int { 210 | const ( 211 | Rotate1 = 1 << iota 212 | Rotate2 213 | Rotate3 214 | Rotate4 215 | ) 216 | 217 | validrots := ^0.(uint) 218 | 219 | // first we rotate each visible block four times around the center 220 | // and checking whether each rotation is valid, then we make a list 221 | // of valid rotation counts (like: [3, 4] or [1, 2, 3, 4]) 222 | for y := 0; y < 4; y++ { 223 | for x := 0; x < 4; x++ { 224 | bmask := 0 225 | if !f.blocks[y*4 + x].filled { 226 | continue 227 | } 228 | bx, by := x - f.cx, y - f.cy 229 | for i := 0; i < 4; i++ { 230 | bx, by = rotate(bx, by) 231 | rbx, rby := f.cx + bx, f.cy + by 232 | 233 | // check whether a rotation is valid an record it 234 | if rbx >= 0 && rbx <= 4 && rby >= 0 && rby <= 4 { 235 | bmask |= 1 << i 236 | } 237 | } 238 | 239 | // apply mask to global mask 240 | validrots &= bmask 241 | } 242 | } 243 | 244 | // determine number of rotations 245 | if validrots & Rotate1 > 0 { 246 | return 1 247 | } else if validrots & Rotate2 > 0 { 248 | return 2 249 | } else if validrots & Rotate3 > 0 { 250 | return 3 251 | } else if validrots & Rotate4 > 0 { 252 | return 4 253 | } 254 | 255 | return 0 256 | } 257 | 258 | func TetrisFigureRotate(f *TetrisFigure, rotate RotateFunc) { 259 | // if there is no center, then the figure cannot be rotated 260 | if f.cx == -1 { 261 | return 262 | } 263 | 264 | rotnum := TetrisFigureGetRotNum(f, rotate) 265 | 266 | var newblocks [16]TetrisBlock 267 | for i := 0; i < 16; i++ { 268 | if !f.blocks[i].filled { 269 | continue 270 | } 271 | 272 | x := i % 4 273 | y := i / 4 274 | x, y = x - f.cx, y - f.cy 275 | 276 | for j := 0; j < rotnum; j++ { 277 | x, y = rotate(x, y) 278 | } 279 | 280 | x, y = x + f.cx, y + f.cy 281 | newblocks[y*4+x] = f.blocks[i] 282 | } 283 | 284 | f.blocks = newblocks 285 | } 286 | 287 | func TetrisFigureDraw(f *TetrisFigure, ox, oy int) { 288 | ox += (f.x + 1) * blockSize 289 | oy += f.y * blockSize 290 | for y := 0; y < 4; y++ { 291 | for x := 0; x < 4; x++ { 292 | offset := y*4+x 293 | TetrisBlockDraw(&f.blocks[offset], 294 | ox + x * blockSize, 295 | oy + y * blockSize) 296 | } 297 | } 298 | } 299 | 300 | //------------------------------------------------------------------------- 301 | // TetrisField 302 | //------------------------------------------------------------------------- 303 | 304 | type TetrisField struct { 305 | width uint 306 | height uint 307 | blocks *TetrisBlock 308 | } 309 | 310 | func NewTetrisField(w, h int) (f *TetrisField) { 311 | f = stdlib.malloc(sizeof(TetrisField)) 312 | f.width = w 313 | f.height = h 314 | f.blocks = stdlib.malloc(sizeof(TetrisBlock) * w * h) 315 | TetrisFieldClear(f) 316 | } 317 | 318 | func TetrisFieldFree(f *TetrisField) { 319 | stdlib.free(f.blocks) 320 | stdlib.free(f) 321 | } 322 | 323 | func TetrisFieldClear(f *TetrisField) { 324 | for i, n := 0, f.width * f.height; i < n; i++ { 325 | f.blocks[i].filled = false 326 | } 327 | } 328 | 329 | func TetrisFieldPixelsWidth(f *TetrisField) int { 330 | return (f.width + 2) * blockSize 331 | } 332 | 333 | func TetrisFieldPixelsHeight(f *TetrisField) int { 334 | return (f.height + 1) * blockSize 335 | } 336 | 337 | func TetrisFieldDraw(f *TetrisField, ox, oy int) { 338 | leftwallx := TetrisFieldPixelsWidth(f) - blockSize 339 | grey := {80, 80, 80}.(TetrisBlockColor) 340 | for y := 0; y < f.height + 1; y++ { 341 | BlockDraw(ox, oy + y * blockSize, grey) 342 | BlockDraw(ox + leftwallx, oy + y * blockSize, grey) 343 | } 344 | bottomwally := TetrisFieldPixelsHeight(f) - blockSize 345 | for x := 0; x < f.width; x++ { 346 | BlockDraw(ox + (x + 1) * blockSize, oy + bottomwally, grey) 347 | } 348 | 349 | ox += blockSize 350 | for y := 0; y < f.height; y++ { 351 | for x := 0; x < f.width; x++ { 352 | offset := y * f.width + x 353 | TetrisBlockDraw(&f.blocks[offset], 354 | ox + x * blockSize, 355 | oy + y * blockSize) 356 | } 357 | } 358 | } 359 | 360 | func TetrisFieldCollide(f *TetrisField, fig *TetrisFigure) bool { 361 | for y := 0; y < 4; y++ { 362 | for x := 0; x < 4; x++ { 363 | offset := y * 4 + x 364 | if !fig.blocks[offset].filled { 365 | continue 366 | } 367 | 368 | fx, fy := fig.x + x, fig.y + y 369 | if fx < 0 || fy < 0 || fx >= f.width || fy >= f.height { 370 | return true 371 | } 372 | fieldoffset := fy * f.width + fx 373 | if f.blocks[fieldoffset].filled { 374 | return true 375 | } 376 | } 377 | } 378 | return false 379 | } 380 | 381 | func TetrisFieldStepCollideAndMerge(f *TetrisField, fig *TetrisFigure) bool { 382 | fig.y++ 383 | if !TetrisFieldCollide(f, fig) { 384 | return false 385 | } 386 | fig.y-- 387 | 388 | for y := 0; y < 4; y++ { 389 | for x := 0; x < 4; x++ { 390 | offset := y * 4 + x 391 | if !fig.blocks[offset].filled { 392 | continue 393 | } 394 | fx, fy := fig.x + x, fig.y + y 395 | fieldoffset := fy * f.width + fx 396 | f.blocks[fieldoffset] = fig.blocks[offset] 397 | } 398 | } 399 | return true 400 | } 401 | 402 | func TetrisFieldCheckForLines(f *TetrisField) int { 403 | lines := 0 404 | for y := 0; y < f.height; y++ { 405 | full := true 406 | for x := 0; x < f.width; x++ { 407 | offset := y * f.width + x 408 | if !f.blocks[offset].filled { 409 | full = false 410 | break 411 | } 412 | } 413 | 414 | if !full { 415 | continue 416 | } 417 | 418 | // if the line is full, increment counter and move all those 419 | // that are above this line one line down 420 | lines++ 421 | 422 | for y2 := y - 1; y2 >= 0; y2-- { 423 | for x := 0; x < f.width; x++ { 424 | offset := y2 * f.width + x 425 | f.blocks[offset + f.width] = f.blocks[offset] 426 | } 427 | } 428 | } 429 | return lines 430 | } 431 | 432 | //------------------------------------------------------------------------- 433 | // GameSession 434 | //------------------------------------------------------------------------- 435 | 436 | type GameSession struct { 437 | field *TetrisField 438 | figure *TetrisFigure 439 | next *TetrisFigure 440 | 441 | time uint 442 | cx, cy int 443 | gameover bool 444 | } 445 | 446 | func NewGameSession() (gs *GameSession) { 447 | gs = stdlib.malloc(sizeof(GameSession)) 448 | gs.field = NewTetrisField(10, 25) 449 | gs.figure = NewRandomTetrisFigure() 450 | gs.next = NewRandomTetrisFigureNot(gs.figure) 451 | gs.time = 0 452 | 453 | gs.cx = (640 - TetrisFieldPixelsWidth(gs.field)) / 2 454 | gs.cy = (480 - TetrisFieldPixelsHeight(gs.field)) / 2 455 | gs.gameover = false 456 | } 457 | 458 | func GameSessionFree(gs *GameSession) { 459 | TetrisFieldFree(gs.field) 460 | if gs.figure != nil { 461 | stdlib.free(gs.figure) 462 | } 463 | if gs.next != nil { 464 | stdlib.free(gs.next) 465 | } 466 | } 467 | 468 | func GameSessionUpdate(gs *GameSession, delta uint) { 469 | gs.time += delta 470 | if gs.time > 200 { 471 | gs.time -= 200 472 | if TetrisFieldStepCollideAndMerge(gs.field, gs.figure) { 473 | TetrisFieldCheckForLines(gs.field) 474 | stdlib.free(gs.figure) 475 | gs.figure = gs.next 476 | gs.next = nil 477 | 478 | if TetrisFieldCollide(gs.field, gs.figure) { 479 | gs.gameover = true 480 | return 481 | } 482 | 483 | gs.next = NewRandomTetrisFigureNot(gs.figure) 484 | } 485 | } 486 | } 487 | 488 | func GameSessionHandleKey(gs *GameSession, key uint) bool { 489 | switch key { 490 | case SDL.SDLK_LEFT, SDL.SDLK_a, SDL.SDLK_j: 491 | gs.figure.x-- 492 | if TetrisFieldCollide(gs.field, gs.figure) { 493 | gs.figure.x++ 494 | } 495 | case SDL.SDLK_RIGHT, SDL.SDLK_d, SDL.SDLK_l: 496 | gs.figure.x++ 497 | if TetrisFieldCollide(gs.field, gs.figure) { 498 | gs.figure.x-- 499 | } 500 | case SDL.SDLK_UP, SDL.SDLK_w, SDL.SDLK_i: 501 | TetrisFigureRotate(gs.figure, RotateCWBlock) 502 | if TetrisFieldCollide(gs.field, gs.figure) { 503 | TetrisFigureRotate(gs.figure, RotateCCWBlock) 504 | } 505 | case SDL.SDLK_DOWN, SDL.SDLK_s, SDL.SDLK_k, SDL.SDLK_SPACE: 506 | for { 507 | if TetrisFieldCollide(gs.field, gs.figure) { 508 | gs.figure.y-- 509 | break 510 | } else { 511 | gs.figure.y++ 512 | } 513 | } 514 | case SDL.SDLK_ESCAPE: 515 | return false 516 | } 517 | return true 518 | } 519 | 520 | func GameSessionDraw(gs *GameSession) { 521 | TetrisFieldDraw(gs.field, gs.cx, gs.cy) 522 | TetrisFigureDraw(gs.figure, gs.cx, gs.cy) 523 | TetrisFigureDraw(gs.next, gs.cx + TetrisFieldPixelsWidth(gs.field), gs.cy + 50) 524 | } 525 | 526 | func main(argc int, argv **byte) int { 527 | SDL.Init(SDL.INIT_VIDEO | SDL.INIT_TIMER) 528 | screen = SDL.SetVideoMode(640, 480, 24, SDL.HWSURFACE | SDL.DOUBLEBUF) 529 | stdlib.srand(SDL.GetTicks()) 530 | 531 | SDL.WM_SetCaption("KrawlTris", "KrawlTris") 532 | SDL.EnableKeyRepeat(250, 45) 533 | 534 | gs := NewGameSession() 535 | last := SDL.GetTicks() 536 | running := true 537 | 538 | var e SDL.Event 539 | for running { 540 | for SDL.PollEvent(&e) != 0 { 541 | switch e._type { 542 | case SDL.QUIT: 543 | running = false 544 | case SDL.KEYDOWN: 545 | running = GameSessionHandleKey(gs, e.key.keysym.sym) 546 | } 547 | } 548 | 549 | now := SDL.GetTicks() 550 | delta := now - last 551 | last = now 552 | 553 | black := SDL.MapRGB(screen.format, 0, 0, 0) 554 | all := {0, 0, 640, 480}.(SDL.Rect) 555 | SDL.FillRect(screen, &all, black) 556 | 557 | GameSessionUpdate(gs, delta) 558 | if gs.gameover { 559 | break 560 | } 561 | 562 | GameSessionDraw(gs) 563 | SDL.Flip(screen) 564 | } 565 | 566 | return 0 567 | } 568 | -------------------------------------------------------------------------------- /examples/typedeps.krl: -------------------------------------------------------------------------------- 1 | import C "stdio.h" 2 | 3 | func test() ([2]int, bool) { 4 | return {5, 10}, true 5 | } 6 | 7 | func main(argc int, argv **byte) int { 8 | a, b := test() 9 | C.printf("%d %d\n", a[0], a[1]) 10 | return 0 11 | } 12 | -------------------------------------------------------------------------------- /extra/krawl.lang: -------------------------------------------------------------------------------- 1 | # use C comments 2 | include "c_comment.lang" 3 | 4 | #label = '^[[:blank:]]*[[:alnum:]]+:[[:blank:]]*\z' 5 | 6 | #(keyword,normal,classname) = 7 | # `(\ small 2 | !(small > big) 3 | false 4 | true 5 | all ok 6 | interesting 7 | very interesting 8 | 1 2 3 9 | 10 20 3 10 | -------------------------------------------------------------------------------- /test/0.iota.krl: -------------------------------------------------------------------------------- 1 | 0 0 0 2 | 4 3 2 1 0 3 | 254 253 251 247 239 4 | 1 2 4 8 16 5 | 1 2 8 12 6 | A B C D E 7 | 0.000000 4.500000 9.000000 6 8 10 8 | 0 1 8 27 64 9 | -------------------------------------------------------------------------------- /test/0.mrv.krl: -------------------------------------------------------------------------------- 1 | a b 2 | -------------------------------------------------------------------------------- /test/0.op.krl: -------------------------------------------------------------------------------- 1 | ok 2 | ok 3 | ok 4 | 3.141500 / 2 = 1.570750 5 | ok 6 | ok 7 | ok 8 | -------------------------------------------------------------------------------- /test/0.overflow.krl: -------------------------------------------------------------------------------- 1 | 260 2 | 360 3 | 225 1 4 | -------------------------------------------------------------------------------- /test/0.returnstmt.krl: -------------------------------------------------------------------------------- 1 | 5 2 | 10 3 | 15 4 | 20 5 | 5 3.141500 6 | 1 0 7 | 7 4.525000 8 | 7.333000 127 9 | 1 0 0 10 | 0 2 0 11 | 0 0 3 12 | 4 13 | if 1 0 14 | switch 1 3 0 15 | switch2 2 3 0 16 | -------------------------------------------------------------------------------- /test/0.sideeffects.krl: -------------------------------------------------------------------------------- 1 | in get1 2 | in get2 3 | in get3 4 | 7 5 | in get0 6 | in get1 7 | in get2 8 | in get3 9 | in getargs1 10 | in get4 11 | in get5 12 | in getargs0 13 | 30 15 14 | -------------------------------------------------------------------------------- /test/0.strings.krl: -------------------------------------------------------------------------------- 1 | len: 0, cap: 0, data: '' 2 | len: 10, cap: 10, data: 'hello, nsf' 3 | len: 20, cap: 20, data: 'Hello: 333, 3.141500' 4 | -------------------------------------------------------------------------------- /test/0.vaargs.krl: -------------------------------------------------------------------------------- 1 | hello: 3 1 3 3 7 2 | -------------------------------------------------------------------------------- /test/array.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func mutate(a [5]int) { 4 | a[2] = 10 5 | } 6 | 7 | func print_array(a [5]int) { 8 | stdio.printf("%d %d %d %d %d\n", 9 | a[0], a[1], a[2], a[3], a[4]) 10 | } 11 | 12 | func main(argc int, argv **byte) int { 13 | var a []int = {1, 2, 3, 4, 5} 14 | mutate(a) 15 | print_array(a) 16 | a[1] = 17 17 | print_array(a) 18 | return 0 19 | } 20 | -------------------------------------------------------------------------------- /test/basic.krl: -------------------------------------------------------------------------------- 1 | import ( 2 | "stdio.h" 3 | "stdlib.h" 4 | ) 5 | 6 | func sort_ints(a, b *void) int { 7 | if *a.(*int) > *b.(*int) { 8 | return 1 9 | } 10 | return 0 11 | } 12 | 13 | func main(argc int, argv **byte) int { 14 | var a []int = {5, 3, 1, 4, 2} 15 | stdlib.qsort(&a, 5, 4, sort_ints) 16 | 17 | for i := 0; i < 5; i++ { 18 | stdio.printf("%d\n", a[i]) 19 | } 20 | return 0 21 | } 22 | -------------------------------------------------------------------------------- /test/builtins.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | type Dummy struct { 4 | a, b, c int 5 | d *Dummy 6 | e double 7 | } 8 | 9 | func main(argc int, argv **byte) int { 10 | var d Dummy 11 | 12 | stdio.printf("sizeof(type Dummy) == %d\n", sizeof(Dummy)) 13 | stdio.printf("sizeof(type [3]Dummy) == %d\n", sizeof([3]Dummy)) 14 | stdio.printf("sizeof(d) == %d\n", sizeof(d)) 15 | stdio.printf("sizeof(&d) == %d\n", sizeof(&d)) 16 | return 0 17 | } 18 | -------------------------------------------------------------------------------- /test/checker.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import subprocess, sys, os 4 | from glob import glob 5 | 6 | errors = 0 7 | problems = [] 8 | 9 | RED = '\033[0;31m' 10 | GRN = '\033[0;32m' 11 | NC = '\033[0m' 12 | 13 | def read_file(fname, m='r', default=''): 14 | try: 15 | f = open(fname, m) 16 | try: 17 | return f.read() 18 | finally: 19 | f.close() 20 | except IOError: 21 | return default 22 | 23 | def colored(color, text): 24 | return color + text + NC 25 | 26 | def run_shell(cmd): 27 | p = subprocess.Popen(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, shell=True) 28 | out = str(p.communicate()[0], 'utf-8') 29 | return p.returncode, out 30 | 31 | def run_test(f): 32 | global errors 33 | ret, out = run_shell("krawl -o test.o %s && clang -o test test.o" % f) 34 | if ret != 0: 35 | problems.append(colored(RED, "%s: failed to compile\n") % f + out) 36 | errors += 1 37 | return False 38 | 39 | gold = read_file("0.%s" % f) 40 | ret, out = run_shell("./test") 41 | if out != gold: 42 | problems.append(colored(RED, "%s: gold mismatch: %s\nVS\n%s") % (f, out, gold)) 43 | errors += 1 44 | return False 45 | return True 46 | 47 | files = [f for f in glob("*.krl") if not f.startswith("0.")] 48 | files.sort() 49 | for f in files: 50 | if run_test(f): 51 | sys.stdout.write(colored(GRN, "+")) 52 | sys.stdout.flush() 53 | else: 54 | sys.stdout.write(colored(RED, "-")) 55 | sys.stdout.flush() 56 | print() 57 | 58 | if errors != 0: 59 | for p in problems: 60 | print(p) 61 | print("---------------------------------------------") 62 | if errors == 1: 63 | print(colored(RED, "There was 1 unexpected ERROR!")) 64 | else: 65 | print(colored(RED, "There were %d unexpected ERRORS!") % errors) 66 | 67 | os.unlink("test") 68 | os.unlink("test.o") 69 | -------------------------------------------------------------------------------- /test/ifstmt.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func main(argc int, argv **byte) int { 4 | big, small := 100, 1 5 | 6 | if big > small { 7 | stdio.printf("big > small\n") 8 | } 9 | 10 | if small > big {} else { 11 | stdio.printf("!(small > big)\n") 12 | } 13 | 14 | if big == small { 15 | stdio.printf("true\n") 16 | } else { 17 | stdio.printf("false\n") 18 | } 19 | 20 | if big != small { 21 | stdio.printf("true\n") 22 | } else { 23 | stdio.printf("false\n") 24 | } 25 | 26 | if big != 0 { 27 | if small == 1 { 28 | stdio.printf("all ok\n") 29 | } 30 | } else { 31 | } 32 | 33 | if big == -1 { stdio.printf("omg, no!\n") } else { 34 | if small != 15 { 35 | stdio.printf("interesting\n") 36 | } 37 | 38 | if small != 1 {} else { 39 | stdio.printf("very interesting\n") 40 | } 41 | } 42 | 43 | a, b, c := 10, 20, 30 44 | if big > small { 45 | a, b, (c) := 1, 2, 3 46 | stdio.printf("%d %d %d\n", a, b, c) 47 | } 48 | stdio.printf("%d %d %d\n", a, b, c) 49 | 50 | return 0 51 | } 52 | -------------------------------------------------------------------------------- /test/iota.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | const ( 4 | A = 0 5 | B 6 | C 7 | ) 8 | 9 | const ( 10 | RA = LAST - (iota + 1) 11 | RB 12 | RC 13 | RD 14 | RE 15 | 16 | LAST = iota 17 | ) 18 | 19 | const ( 20 | BIT1 = 1 << iota 21 | BIT2 22 | BIT3 23 | BIT4 24 | BIT5 25 | ) 26 | 27 | const ( 28 | ALL_BUT_BIT1 = ^(1 << iota).(uint8) 29 | ALL_BUT_BIT2 30 | ALL_BUT_BIT3 31 | ALL_BUT_BIT4 32 | ALL_BUT_BIT5 33 | ) 34 | 35 | const ( 36 | a = 1 37 | b = iota << a 38 | c = iota << b 39 | d 40 | ) 41 | 42 | const ( 43 | ca = iota + 'A' 44 | cb 45 | cc 46 | cd 47 | ce 48 | ) 49 | 50 | const ( 51 | f1 = iota.(double) * 4.5 52 | f2 53 | f3 54 | u1 = iota.(uint32) * 2 55 | u2 56 | u3 57 | ) 58 | 59 | const ( 60 | c1 = iota * iota * iota 61 | c2 62 | c3 63 | c4 64 | c5 65 | ) 66 | 67 | func main(argc int, argv **byte) int { 68 | stdio.printf("%d %d %d\n", A, B, C) 69 | stdio.printf("%d %d %d %d %d\n", RA, RB, RC, RD, RE) 70 | stdio.printf("%d %d %d %d %d\n", 71 | ALL_BUT_BIT1, 72 | ALL_BUT_BIT2, 73 | ALL_BUT_BIT3, 74 | ALL_BUT_BIT4, 75 | ALL_BUT_BIT5) 76 | stdio.printf("%d %d %d %d %d\n", BIT1, BIT2, BIT3, BIT4, BIT5) 77 | stdio.printf("%d %d %d %d\n", a, b, c, d) 78 | stdio.printf("%c %c %c %c %c\n", ca, cb, cc, cd, ce) 79 | stdio.printf("%f %f %f %u %u %u\n", f1, f2, f3, u1, u2, u3) 80 | stdio.printf("%d %d %d %d %d\n", c1, c2, c3, c4, c5) 81 | return 0 82 | } 83 | -------------------------------------------------------------------------------- /test/mrv.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func getab() (byte, byte) { 4 | return 'a', 'b' 5 | } 6 | 7 | func gettwo() (byte, byte) { 8 | return getab() 9 | } 10 | 11 | func propagate(a, b byte) (byte, byte) { 12 | return a, b 13 | } 14 | 15 | func main(argc int, argv **byte) int { 16 | a, b := propagate(gettwo()) 17 | stdio.printf("%c %c\n", a, b) 18 | return 0 19 | } 20 | -------------------------------------------------------------------------------- /test/op.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | import "stdlib.h" 3 | 4 | func assert(cond bool) { 5 | if !cond { 6 | stdio.printf("fail\n") 7 | stdlib.exit(1) 8 | } 9 | stdio.printf("ok\n") 10 | } 11 | 12 | func main(argc int, argv **byte) int { 13 | { 14 | // random math ops 15 | a := 5 16 | b := 8 17 | c := (((((a + b) * 4) / 2 - 6) << 7) % 113) >> 2 18 | assert(c == 18) 19 | } 20 | { 21 | // unsigned overflow 22 | a := 245.(uint8) 23 | b := 15.(uint8) 24 | c := a + b 25 | assert(c == 4) 26 | } 27 | { 28 | // signed overflow 29 | a := -125.(int8) 30 | b := 20.(int8) 31 | c := a - b 32 | assert(c == 111) 33 | } 34 | { 35 | pi := 3.1415 36 | halfpi := pi / 2 37 | // TODO: varargs type promotion 38 | stdio.printf("%f / 2 = %f\n", pi, halfpi) 39 | } 40 | { 41 | // some logic operations 42 | a := 10 > 5 && 2 != 3 && (77-7) == 70 43 | b := 10 < 5 || 7 > 0 44 | c := a && !b 45 | assert(!c) 46 | } 47 | { 48 | ten, five := 10, 5 49 | A := five <= ten || ten == five 50 | B := five == 5 || ten == 5 51 | C := A && B 52 | assert(C) 53 | } 54 | { 55 | a := 50 56 | b := 50.(uint8) 57 | c := 50.(uint16) 58 | a, b, c = ^a, ^b, ^c 59 | assert(a == -51 && b == 205 && c == 65485) 60 | } 61 | return 0 62 | } 63 | -------------------------------------------------------------------------------- /test/overflow.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func testadd(i1, i2, i3 int8) int { 4 | return i1 + i2 + i3 5 | } 6 | 7 | func main(argc int, argv **byte) int { 8 | { 9 | var a uint8 = 250 10 | var b uint16 = a + 10 11 | stdio.printf("%d\n", b) 12 | } 13 | 14 | { 15 | var a, b, c int8 = 120, 120, 120 16 | d := testadd(a, b, c) 17 | stdio.printf("%d\n", d) 18 | } 19 | { 20 | var a, b uint8 = 250, 200 21 | avg := (a + b) / 2 22 | stdio.printf("%d %d\n", avg, sizeof(avg)) 23 | } 24 | return 0 25 | } 26 | -------------------------------------------------------------------------------- /test/returnstmt.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func single_return() int { return 5 } 4 | func single_return_named() (i int) { i = 10; return i } 5 | func single_return_named2() (i int) { i = 15 } 6 | func single_return_named3() (i int) { i = 20; return } 7 | func multi_return() (int, double) { return 5, 3.1415 } 8 | func multi_return_named() (a, b bool) { a, b = true, false; return a, b } 9 | func multi_return_named2() (a int, b double) { a, b = 7, 4.525; return } 10 | func multi_return_named3() (a double, b int8) { a, b = 7.333, 127 } 11 | 12 | func multi_return_named_defa() (a, b, c int) { a = 1 } 13 | func multi_return_named_defb() (a, b, c int) { b = 2 } 14 | func multi_return_named_defc() (a, b, c int) { c = 3 } 15 | 16 | func countA(str *byte) (count int) { 17 | for *str != 0 { 18 | if *str == 'A' { 19 | count++ 20 | } 21 | str++ 22 | } 23 | } 24 | 25 | func test_if(wantone bool) int { 26 | if wantone { 27 | return 1 28 | } else { 29 | return 0 30 | } 31 | } 32 | 33 | const ( 34 | ONE = iota 35 | TWO 36 | THREE 37 | FOUR 38 | FIVE 39 | ) 40 | 41 | func test_switch(what int) int { 42 | switch what { 43 | case ONE: 44 | return 1 45 | case TWO: 46 | return 2 47 | case THREE: 48 | return 3 49 | case FOUR: 50 | return 4 51 | case FIVE: 52 | return 5 53 | default: 54 | return 0 55 | } 56 | } 57 | 58 | func test_switch2(what int) int { 59 | switch what { 60 | case ONE: 61 | return 1 62 | case TWO: 63 | return 2 64 | case THREE: 65 | return 3 66 | case FOUR: 67 | return 4 68 | case FIVE: 69 | return 5 70 | } 71 | return 0 72 | } 73 | 74 | func main(argc int, argv **byte) int { 75 | stdio.printf("%d\n", single_return()) 76 | stdio.printf("%d\n", single_return_named()) 77 | stdio.printf("%d\n", single_return_named2()) 78 | stdio.printf("%d\n", single_return_named3()) 79 | 80 | { 81 | a, b := multi_return() 82 | stdio.printf("%d %f\n", a, b) 83 | } 84 | 85 | { 86 | a, b := multi_return_named() 87 | stdio.printf("%d %d\n", a, b) 88 | } 89 | 90 | { 91 | a, b := multi_return_named2() 92 | stdio.printf("%d %f\n", a, b) 93 | } 94 | { 95 | a, b := multi_return_named3() 96 | stdio.printf("%f %d\n", a, b) 97 | } 98 | { 99 | a1, b1, c1 := multi_return_named_defa() 100 | stdio.printf("%d %d %d\n", a1, b1, c1) 101 | a2, b2, c2 := multi_return_named_defb() 102 | stdio.printf("%d %d %d\n", a2, b2, c2) 103 | a3, b3, c3 := multi_return_named_defc() 104 | stdio.printf("%d %d %d\n", a3, b3, c3) 105 | } 106 | 107 | stdio.printf("%d\n", countA("AAbbccAdfA")) 108 | stdio.printf("if %d %d\n", test_if(true), test_if(false)) 109 | stdio.printf("switch %d %d %d\n", 110 | test_switch(ONE), test_switch(THREE), test_switch(99)) 111 | stdio.printf("switch2 %d %d %d\n", 112 | test_switch2(TWO), test_switch2(THREE), test_switch2(99)) 113 | return 0 114 | } 115 | -------------------------------------------------------------------------------- /test/sideeffects.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | func get0() int { stdio.printf("in get0\n"); return 0 } 4 | func get1() int { stdio.printf("in get1\n"); return 1 } 5 | func get2() int { stdio.printf("in get2\n"); return 2 } 6 | func get3() int { stdio.printf("in get3\n"); return 3 } 7 | func get4() int { stdio.printf("in get4\n"); return 4 } 8 | func get5() int { stdio.printf("in get5\n"); return 5 } 9 | func getargs0(a, b int) int { stdio.printf("in getargs0\n"); return 0 } 10 | func getargs1(a, b int) int { stdio.printf("in getargs1\n"); return 1 } 11 | 12 | func main(argc int, argv **byte) int { 13 | a := get1() + get2() * get3() 14 | stdio.printf("%d\n", a) 15 | 16 | var pair []int = {15, 30} 17 | pair[get0()], pair[get1()] = pair[getargs1(get2(), get3())], pair[getargs0(get4(), get5())] 18 | stdio.printf("%d %d\n", pair[0], pair[1]) 19 | return 0 20 | } 21 | -------------------------------------------------------------------------------- /test/strings.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | import "stdlib.h" 3 | import "string.h" 4 | 5 | type String struct { 6 | data *byte 7 | len, cap uint 8 | } 9 | 10 | func StringNew(cap uint) (s *String) { 11 | s = stdlib.malloc(sizeof(String)) 12 | s.len = 0 13 | s.cap = cap 14 | s.data = stdlib.malloc(cap + 1) 15 | s.data[0] = 0 16 | } 17 | 18 | func StringFree(s *String) { 19 | stdlib.free(s.data) 20 | stdlib.free(s) 21 | } 22 | 23 | func StringFromCStr(cstr *byte) (s *String) { 24 | s = StringFromCStrLen(cstr, string.strlen(cstr).(uint)) 25 | } 26 | 27 | func StringFromCStrLen(cstr *byte, len uint) (s *String) { 28 | s = stdlib.malloc(sizeof(String)) 29 | s.len = len 30 | s.cap = len 31 | s.data = stdlib.malloc(s.cap + 1) 32 | if len != 0 { 33 | string.memcpy(s.data, cstr, len) 34 | } 35 | s.data[len] = 0 36 | } 37 | 38 | func StringPrintf(fmt *byte, ...) (s *String) { 39 | var va va_list 40 | va_start(&va) 41 | len := stdio.vsnprintf(nil, 0, fmt, va).(uint) 42 | va_end(&va) 43 | 44 | s = stdlib.malloc(sizeof(String)) 45 | s.len = len 46 | s.cap = len 47 | s.data = stdlib.malloc(s.cap + 1) 48 | va_start(&va) 49 | stdio.vsnprintf(s.data, len+1, fmt, va) 50 | va_end(&va) 51 | } 52 | 53 | func main(argc int, argv **byte) int { 54 | s := StringNew(0) 55 | stdio.printf("len: %d, cap: %d, data: '%s'\n", 56 | s.len, s.cap, s.data) 57 | StringFree(s) 58 | 59 | s = StringFromCStr("hello, nsf") 60 | stdio.printf("len: %d, cap: %d, data: '%s'\n", 61 | s.len, s.cap, s.data) 62 | StringFree(s) 63 | 64 | s = StringPrintf("Hello: %d, %f", 333, 3.1415.(double)) 65 | stdio.printf("len: %d, cap: %d, data: '%s'\n", 66 | s.len, s.cap, s.data) 67 | StringFree(s) 68 | return 0 69 | } 70 | -------------------------------------------------------------------------------- /test/vaargs.krl: -------------------------------------------------------------------------------- 1 | import "stdio.h" 2 | 3 | const ( 4 | N_END = iota 5 | N_ONE 6 | N_TWO 7 | N_THREE 8 | N_FOUR 9 | N_FIVE 10 | N_SIX 11 | N_SEVEN 12 | ) 13 | 14 | func test_vaarg(msg *byte, ...) { 15 | stdio.printf("%s: ", msg) 16 | 17 | var vl va_list 18 | va_start(&vl) 19 | for { 20 | i := va_arg(&vl, int) 21 | if i == N_END { 22 | break 23 | } 24 | 25 | stdio.printf("%d ", i) 26 | } 27 | va_end(&vl) 28 | stdio.printf("\n") 29 | } 30 | 31 | func main(argc int, argv **byte) int { 32 | test_vaarg("hello", N_THREE, N_ONE, N_THREE, N_THREE, N_SEVEN, N_END) 33 | return 0 34 | } 35 | -------------------------------------------------------------------------------- /waf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nsf/krawl/c4fdba937a503cc85b690175698c6fb1caa221a5/waf -------------------------------------------------------------------------------- /wafscripts/krawl.py: -------------------------------------------------------------------------------- 1 | from waflib import Utils 2 | from waflib.Task import Task 3 | from waflib.TaskGen import feature, before, extension, after_method 4 | from waflib.Tools import ccroot 5 | import krawl_scan 6 | 7 | def to_dict(list_or_dict): 8 | if isinstance(list_or_dict, list): 9 | d = {} 10 | for i in list_or_dict: 11 | d[i] = True 12 | return d 13 | return list_or_dict 14 | 15 | def configure(conf): 16 | opts = to_dict(getattr(conf.env, 'KRAWL_CONF_OPTS', {})) 17 | if 'KRAWL_CONF_OPTS' in conf.env: 18 | del conf.env.KRAWL_CONF_OPTS 19 | 20 | if 'bootstrap' not in opts: 21 | conf.fatal("looking for a compiler is not implemented") 22 | 23 | conf.env.KRAWL_UID_ST = '-u%s' 24 | conf.env.KRAWL_HASH_UID_ST = '-U%s' 25 | conf.env.KRAWL_PKG_ST = '-P%s' 26 | conf.env.KRAWL_CLANG_ST = '--clang=%s' 27 | conf.env.KRAWL_CLANG_PLUGIN_ST = '--clang-plugin=%s' 28 | conf.env.KRAWL_BRL_OUT_ST = '-b%s' 29 | conf.env.KRAWL_I_ST = '-I%s' 30 | if 'bootstrap' in opts: 31 | conf.env.KRAWL_BOOTSTRAP = True 32 | 33 | class krawl(Task): 34 | run_str = """ 35 | ${KRAWL} 36 | ${KRAWL_UID_ST:UID} 37 | ${KRAWL_HASH_UID_ST:HASH_UID} 38 | ${KRAWL_PKG_ST:PKG} 39 | ${KRAWL_CLANG_ST:CLANG} 40 | ${KRAWL_CLANG_PLUGIN_ST:CLANG_PLUGIN} 41 | ${KRAWL_BRL_OUT_ST:BRL_OUT} 42 | ${KRAWL_I_ST:KRAWL_INCPATHS} 43 | -o ${TGT[0].bldpath()} ${SRC} 44 | """.replace("\n", "") 45 | 46 | color = 'GREEN' 47 | scan = krawl_scan.scan 48 | 49 | @feature('krawl') 50 | @after_method('propagate_uselib_vars', 'process_source') 51 | def apply_krawl_incpaths(self): 52 | lst = self.to_incnodes(self.to_list(getattr(self, 'krawl_includes', [])) + self.env['KRAWL_INCLUDES']) 53 | self.includes_nodes = lst 54 | self.env['KRAWL_INCPATHS'] = [x.abspath() for x in lst] 55 | 56 | #============================================================================== 57 | # Params: 58 | # uid - unique id 59 | # hash_uid - hashed unique id 60 | # package - package name 61 | # install_path - installation path 62 | # target_brl - override default location for interface file 63 | # includes - dirs to use as includes 64 | #============================================================================== 65 | @feature('krawl') 66 | @before('process_source') 67 | def apply_krawl(self): 68 | """Create krawl task""" 69 | # extract .krl nodes from 'source' 70 | src_nodes = [] 71 | no_nodes = [] 72 | for n in self.to_nodes(self.source): 73 | if n.name.endswith('.krl'): 74 | src_nodes.append(n) 75 | else: 76 | no_nodes.append(n) 77 | self.source = no_nodes 78 | 79 | # if there were no nodes, return 80 | if len(src_nodes) == 0: 81 | return 82 | 83 | # object file 84 | obj_node = src_nodes[0].change_ext('.o') 85 | 86 | # brawl interface file 87 | target_brl = getattr(self, 'target_brl', None) 88 | if target_brl: 89 | brl_node = self.path.find_or_declare(target_brl) 90 | else: 91 | brl_node = src_nodes[0].change_ext('.brl') 92 | 93 | # create task 94 | task = self.create_task('krawl', src_nodes, [obj_node, brl_node]) 95 | if target_brl: 96 | task.env.BRL_OUT = [brl_node.bldpath()] 97 | 98 | # uid 99 | uid = getattr(self, 'uid', None) 100 | if uid: 101 | task.env.UID = [uid] 102 | 103 | # hash uid 104 | hash_uid = getattr(self, 'hash_uid', None) 105 | if hash_uid: 106 | task.env.HASH_UID = [hash_uid] 107 | 108 | # package 109 | pkg = getattr(self, 'package', None) 110 | if pkg: 111 | task.env.PKG = [pkg] 112 | 113 | # get path to krawl executable and clang plugin 114 | if self.env.KRAWL_BOOTSTRAP: 115 | tg = self.bld.get_tgen_by_name('krawl') 116 | krawlc = tg.link_task.outputs[0] 117 | task.env.KRAWL = krawlc.bldpath() 118 | task.dep_nodes.append(krawlc) 119 | 120 | tg = self.bld.get_tgen_by_name('ctokrawl') 121 | clang_plugin = tg.link_task.outputs[0] 122 | task.env.CLANG_PLUGIN = [clang_plugin.bldpath()] 123 | task.dep_nodes.append(clang_plugin) 124 | 125 | try: 126 | self.compiled_tasks.append(task) 127 | except AttributeError: 128 | self.compiled_tasks = [task] 129 | 130 | #------------------------------------------------------ 131 | # installation 132 | #------------------------------------------------------ 133 | 134 | if getattr(self.bld, 'is_install', None) and (uid or hash_uid): 135 | pkg = getattr(self, 'package', src_nodes[0].parent.name) 136 | inst_to = getattr(self, 'install_path', None) 137 | 138 | if inst_to: 139 | self.header_install_task = self.bld.install_as( 140 | dest = inst_to + '/' + pkg + '.brl', 141 | srcfile = brl_node, 142 | env = self.env, 143 | chmod = Utils.O644 144 | ) 145 | 146 | 147 | -------------------------------------------------------------------------------- /wafscripts/krawl_scan.py: -------------------------------------------------------------------------------- 1 | import re, collections 2 | from waflib import Utils, Errors 3 | import traceback 4 | 5 | Tok = collections.namedtuple('Tok', 'tok value pos') 6 | ImportSpec = collections.namedtuple('ImportSpec', 'name path') 7 | 8 | class ParserError(Exception): 9 | def __init__(self, expected, t): 10 | self.value = 'expected %s, got: %s' % (expected, t.tok) 11 | def __str__(self): 12 | return repr(self.value) 13 | 14 | def post_process_path(path): 15 | # TODO perform escape sequences interpretation here as well 16 | # rarely (never?) used in paths though 17 | 18 | # remove quotes 19 | return path[1:-1] 20 | 21 | def token_iter(source): 22 | pos = source.find("import") 23 | if pos == -1: 24 | return 25 | 26 | tok_spec = [ 27 | ('ID', r'[A-Za-z]+'), 28 | ('LPAREN', r'\('), 29 | ('RPAREN', r'\)'), 30 | ('STRING', r'("([^"\\]|\\.)*"|`[^`]*`)'), 31 | ('NEWLINE', r'\n'), 32 | ('SKIP', r'[ \t\r]|//.*?\n|/\*.*?\*/'), 33 | ] 34 | 35 | tok_re = '|'.join('(?P<%s>%s)' % spec for spec in tok_spec) 36 | gettok = re.compile(tok_re).match 37 | 38 | lasttok = None 39 | 40 | mo = gettok(source, pos) 41 | while mo is not None: 42 | tok = mo.lastgroup 43 | pos = mo.end() 44 | 45 | # automatic semicolon insertion 46 | if tok == 'NEWLINE' and lasttok in ['STRING', 'RAW_STRING', 'RPAREN']: 47 | yield Tok('SEMI', ';', mo.start()) 48 | lasttok = 'SEMI' 49 | 50 | # got a real token 51 | if tok not in ['SKIP', 'NEWLINE']: 52 | yield Tok(tok, mo.group(), mo.start()) 53 | lasttok = tok 54 | 55 | mo = gettok(source, pos) 56 | 57 | def next_expected(it, expected, value=None): 58 | t = next(it) 59 | if t.tok != expected or (value and t.value != value): 60 | raise ParserError(expected, t) 61 | return t 62 | 63 | def parse_import_spec(t, it): 64 | name = "" 65 | path = "" 66 | if t.tok == 'ID': 67 | # got an ID, the next one should be the path 68 | name = t.value 69 | t = next_expected(it, 'STRING') 70 | # got path, consume semicolon 71 | path = post_process_path(t.value) 72 | t = next_expected(it, 'SEMI') 73 | return ImportSpec(name, path), t.pos 74 | 75 | def parse_import_group(it): 76 | specs = [] 77 | while 1: 78 | t = next(it) 79 | if t.tok in ['ID', 'STRING']: 80 | specs.append(parse_import_spec(t, it)[0]) 81 | elif t.tok == 'RPAREN': 82 | t = next_expected(it, 'SEMI') 83 | return specs, t.pos 84 | else: 85 | raise ParserError('RPAREN or ID or STRING', t) 86 | 87 | def parse_import_spec_or_group(it): 88 | t = next(it) 89 | if t.tok == 'LPAREN': 90 | return parse_import_group(it) 91 | elif t.tok in ['ID', 'STRING']: 92 | s, pos = parse_import_spec(t, it) 93 | return [s], pos 94 | raise ParserError('LPAREN or ID or STRING', t) 95 | 96 | def parse_import_specs(source): 97 | specs = [] 98 | while 1: 99 | try: 100 | it = token_iter(source) 101 | next_expected(it, 'ID', 'import') 102 | ss, pos = parse_import_spec_or_group(it) 103 | specs.extend(ss) 104 | source = source[pos:] 105 | except StopIteration: 106 | return specs 107 | 108 | def try_find(path, incnodes): 109 | for n in incnodes: 110 | ret = n.find_resource(path + ".brl") 111 | if ret: 112 | return ret 113 | return None 114 | 115 | def scan(task): 116 | try: 117 | incnodes = task.generator.includes_nodes 118 | except AttributeError: 119 | raise Error.WafError("%r must execute apply_incpaths before doing scan" % task.generator) 120 | 121 | nodes = [] 122 | for i in task.inputs: 123 | filename = i.abspath() 124 | code = Utils.readf(filename) 125 | try: 126 | specs = parse_import_specs(code) 127 | except ParserError: 128 | specs = [] 129 | 130 | for s in specs: 131 | if s.path[-2:] == '.h' or s.path[:2] == './': 132 | continue 133 | n = try_find(s.path, incnodes) 134 | if n: 135 | nodes.append(n) 136 | return (nodes, []) 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | import sys, platform 2 | 3 | top = '.' 4 | out = 'build' 5 | 6 | def options(opt): 7 | opt.load('compiler_cxx') 8 | opt.load('compiler_c') 9 | opt.add_option( 10 | '--lenstr', 11 | action = 'store_true', 12 | default = False, 13 | help = 'Add LLVM RPATH to the executable' 14 | ) 15 | opt.add_option( 16 | '--stdlib', 17 | action = 'store_true', 18 | default = False, 19 | help = 'Build Krawl standard library as well' 20 | ) 21 | 22 | def configure(conf): 23 | conf.define('KRAWL_INSTALL_PREFIX', conf.env.PREFIX) 24 | 25 | conf.env.OPT_LENSTR = conf.options.lenstr 26 | conf.env.OPT_STDLIB = conf.options.stdlib 27 | 28 | machine = platform.machine() 29 | if machine == 'x86_64': 30 | conf.env.OPT_ARCH = 'amd64' 31 | else: 32 | conf.env.OPT_ARCH = 'x86' 33 | 34 | # TODO: if new archs are added, this will be necessary 35 | #machine in ['i386', 'i486', 'i586', 'i686']: 36 | # conf.env.OPT_ARCH = 'x86' 37 | 38 | 39 | if sys.platform == "darwin": 40 | conf.env.append_unique('LINKFLAGS_CLANG_PLUGIN', '-Wl,-undefined,dynamic_lookup') 41 | 42 | conf.env.KRAWL_CONF_OPTS = ['bootstrap'] 43 | conf.load('krawl', tooldir='wafscripts') 44 | 45 | conf.load('compiler_cxx') 46 | conf.load('compiler_c') 47 | conf.check_cfg( 48 | path = 'llvm-config', 49 | args = '--cxxflags --ldflags --libs', 50 | package = '', 51 | uselib_store = 'LLVM', 52 | ) 53 | 54 | conf.write_config_header('config.hpp') 55 | conf.env.append_unique('INCLUDES', conf.bldnode.abspath()) 56 | 57 | def build(bld): 58 | bld.recurse('ctokrawl compiler') 59 | if bld.env.OPT_STDLIB: 60 | bld.add_group() 61 | bld.recurse('stdlib') 62 | --------------------------------------------------------------------------------