Compare commits

...

520 Commits

Author SHA1 Message Date
gingerBill 57565b78a6 v0.11.0 2019-11-01 19:45:39 +00:00
gingerBill b5b085914a Merge pull request #404 from hazeycode/master
Impl time for macOS
2019-11-01 19:44:50 +00:00
Chris Heyes 044e64beb0 Add missing foreign import to time_darwin 2019-11-01 19:39:26 +00:00
Chris Heyes adca5b57cd Move time code from os to time package 2019-11-01 19:33:48 +00:00
gingerBill 3b898e5224 Add @force to foreign import 2019-11-01 19:26:22 +00:00
Chris Heyes 153e7525b5 Merge remote-tracking branch 'upstream/master' 2019-11-01 19:18:33 +00:00
gingerBill 44a303e577 Add dummy packages purely for documentation of builtin and intrinsics 2019-11-01 19:00:23 +00:00
gingerBill d63878d0dd Merge pull request #437 from nakst/master
updated os_essence.odin
2019-11-01 10:20:29 +00:00
gingerBill a20c31d6b5 Fix polymorphic record parameter determination bug caused by polymorphic constants not being handled correctly #447 2019-10-31 22:58:38 +00:00
gingerBill 560bdc339b Fix stack overflow bug caused by polymorphic record with polymorphic constant parameters. #447
DOES NOT FIX THE UNDERLYING BUG
2019-10-31 22:39:12 +00:00
gingerBill 01dfb1dac8 Fix double calling of lhs of logical binary expressions 2019-10-31 20:17:50 +00:00
gingerBill ee8d3e03f8 Delay determination of procedure abi types until as late as possible to prevent type undetermination in self-referential data types #454 2019-10-31 18:25:39 +00:00
gingerBill 4aad45e3e7 Merge pull request #453 from kevinw/master
Remove unused variables in utf8.odin.
2019-10-29 11:47:37 +00:00
gingerBill fe5c642d9f Fix runtime.cstring_len 2019-10-29 08:47:05 +00:00
Kevin Watters b5fdb3f855 Remove unused variables in utf8.odin. 2019-10-28 11:13:47 -04:00
gingerBill 416ff149bd Fix procedure group "best fit" algorithm for polymorphic procedures 2019-10-27 19:42:21 +00:00
gingerBill 233c6e2d3a Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-27 18:43:53 +00:00
gingerBill a29a6d9285 Fix typos in package linalg; Fix norm_float64 in package rand 2019-10-27 18:43:40 +00:00
Mikkel Hjortshøj a7a31e4c23 Set CI shell on windows to cmd.exe 2019-10-27 18:12:23 +01:00
Mikkel Hjortshøj 72cad591bd Update ci.yml 2019-10-27 18:07:14 +01:00
Mikkel Hjortshøj 684bf7aa61 Update ci.yml 2019-10-27 18:04:14 +01:00
Mikkel Hjortshøj d760b95e4f Update ci.yml 2019-10-27 18:02:39 +01:00
gingerBill 5e81fc72b9 New package math and package math/linalg 2019-10-27 10:35:35 +00:00
gingerBill 0977ac111a Fix typo in package utf8; add wchar_t to package c 2019-10-27 08:34:20 +00:00
gingerBill 2db16d6a32 Add for package utf8: rune_at_pos, rune_string_at_pos, rune_at, rune_offset 2019-10-26 15:05:36 +01:00
gingerBill 5aa46d31b2 Move package decimal to be a subpackage of package strconv 2019-10-26 14:46:21 +01:00
gingerBill 14e8b299b7 Fix slice and dynamic array lengths determined from ranged compound literals 2019-10-26 14:43:06 +01:00
gingerBill c7cb754514 Fix typos 2019-10-26 14:37:27 +01:00
gingerBill a5e42a0465 Add ranged_fields_for_array_compound_literals 2019-10-26 14:36:28 +01:00
gingerBill d808f9eccf Add range_cache.cpp 2019-10-26 14:29:04 +01:00
gingerBill 1734eb949f Update utf8.accept_sizes to use ranged fields 2019-10-26 14:18:38 +01:00
gingerBill 7fae890ef9 Allow ranges for array-like compound literals 2019-10-26 14:06:29 +01:00
gingerBill 94879ed149 Fix Compiler assertion when applying using to _ procedure parameter. #451 2019-10-26 12:14:04 +01:00
gingerBill 2c75fe2314 Allow for cycles in record polymorphic parameters but not in actualized fields 2019-10-26 11:50:42 +01:00
gingerBill 1da0668653 Add utf8.rune_index 2019-10-26 11:50:01 +01:00
gingerBill c60fb10a6a Move old demos and old stuff to /misc 2019-10-26 10:40:17 +01:00
gingerBill 7140f42915 Modify runtime to reduce dependencies on other packages 2019-10-23 21:43:13 +01:00
gingerBill ad92fbfd4e Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-23 21:42:42 +01:00
gingerBill 1416946757 Add token.odin 2019-10-23 21:42:28 +01:00
gingerBill 6a8c4ee04c Merge pull request #450 from ThisDrunkDane/master
Setup Github Actions instead of Travis and Appveyor
2019-10-21 22:17:05 +01:00
Mikkel Hjortshøj 516df9123d Update README.md 2019-10-21 23:08:16 +02:00
Mikkel Hjortshøj 818d6dbbea Update README.md with new CI badge 2019-10-21 23:05:18 +02:00
Mikkel Hjortshoej 286c5b7b24 Remove old CI runners 2019-10-21 23:01:32 +02:00
Mikkel Hjortshøj c0e8113f6f Add github CI action 2019-10-21 23:00:24 +02:00
gingerBill e15dfa8eb6 Fix missing check for zero elements 2019-10-20 18:26:30 +01:00
gingerBill f12ded54f2 Support for named indices for array-like compound literals {3 = a, 1 = b} 2019-10-20 13:11:28 +01:00
gingerBill b4951c9b39 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-10-20 10:50:30 +01:00
gingerBill 9f0a28017d Fix typo in string_to_string16 #444 2019-10-20 10:50:18 +01:00
Mikkel Hjortshøj d7c8a3a9dd Merge pull request #445 from Tetralux/patch-2
Clarify that you can pass a directory to `odin build`
2019-10-19 18:57:23 +02:00
Tetralux 10b109e25f Clarify that you can pass a directory to odin build
Changes the usage information to this:
```
D:\Software\odin\odin.exe is a tool for managing Odin source code
Usage:
        D:\Software\odin\odin.exe command [arguments]
Commands:
        build     compile .odin file, or directory of .odin files, as an executable.
                  one must contain the program's entry point, all must be in the same package.
        run       same as 'build', but also then runs the newly compiled executable.
        check     parse and type check .odin file
        query     parse, type check, and output a .json file containing information about the program
        docs      generate documentation for a .odin file
        version   print version
```
2019-10-19 17:55:05 +01:00
gingerBill 2afe4bea67 Add instrincs.type_is_valid_map_key 2019-10-15 22:43:04 +01:00
gingerBill 12ae5ed09e Fix missing typeid conversion case for variadic parameters 2019-10-15 20:47:27 +01:00
gingerBill 0bdc3b4f21 Fix Values coerce to typeid #443 2019-10-15 17:54:37 +01:00
gingerBill eaabb888d4 Update Odin Logo 2019-10-15 17:42:55 +01:00
gingerBill b53fe14c22 Change error to syntax_error in parser 2019-10-13 16:06:41 +01:00
gingerBill 45683703ea Fix System V for certain structs 2019-10-13 14:29:56 +01:00
gingerBill 03053a18ce Fix IR generation bug for nested foreign procedure declaration 2019-10-13 12:51:47 +01:00
gingerBill 2a6d9e8927 #panic; Minor change to demo.odin; Fix #assert bug at file scope 2019-10-13 12:38:23 +01:00
gingerBill fa81061db0 Minor fix to Odin types with System V ABI 2019-10-10 21:50:20 +01:00
gingerBill 39b3c8c80f Update System V ABI to for more Odin types 2019-10-10 21:39:46 +01:00
gingerBill 3139151935 Minor fix to systemv_distribute_struct_fields 2019-10-10 21:24:32 +01:00
gingerBill 672a8f5dbd Add Odin types for System V ABI 2019-10-10 21:13:06 +01:00
gingerBill 8672ff1c55 Fix typo in System V ABI determination 2019-10-10 20:57:31 +01:00
gingerBill abfa894566 Fix general IR parameter case 2019-10-10 20:52:07 +01:00
gingerBill 5b52fed268 Correct (experimental) System V AMD64 ABI support 2019-10-10 20:41:16 +01:00
Mikkel Hjortshøj 94a638b436 Update README.md
Remove license badge (Github shows it themselves now) and add discord badge
2019-10-10 11:10:10 +02:00
gingerBill 1b8c3ca22a Fix typos and make demo work with -vet 2019-10-08 20:28:45 +01:00
gingerBill 71b32ae117 Update demo.odin 2019-10-06 19:20:00 +01:00
gingerBill 939459b635 Change implicit semicolon rules for record types within procedure bodies; Update package odin/* 2019-10-06 19:16:55 +01:00
gingerBill 562b518394 Change print*_err to eprint* in core library 2019-10-06 18:54:29 +01:00
gingerBill d62503d031 Change precedence for in and notin to match + - | ~ 2019-10-06 18:14:02 +01:00
gingerBill 4e8a801b35 strings.split; strings.index; eprint* over print*_err; 2019-10-06 18:13:15 +01:00
gingerBill e1b711b3b3 Update demo.odin with more information 2019-10-06 15:07:16 +01:00
gingerBill 6c69e8c043 Make typeid semantics consistent across variables and constants 2019-10-06 14:55:25 +01:00
gingerBill 7fa2d25eea Fix #complete switch with pointer case doesn't compile #416 2019-10-05 10:22:41 +01:00
gingerBill dae514a2c9 Fix Using any in if statement asserts compiler #441 2019-10-05 09:40:05 +01:00
gingerBill 068993a819 Remove os_osx.odin 2019-10-01 20:55:15 +01:00
gingerBill 66ae4e5afc Change ODIN_OS string for osx from "osx" to "darwin" to allow for other platforms 2019-10-01 20:38:50 +01:00
gingerBill 218d1131e8 Change how foreign imports work for mac 2019-09-29 09:25:33 +01:00
gingerBill f4f6e9ad49 Fix -debug crash on windows caused by missing debug info for files. 2019-09-25 21:07:56 +01:00
gingerBill 48ab7f876c Fix Implicit Selector Expressions do not work for parameteric struct parameters. #438 2019-09-25 20:52:47 +01:00
nakst 1e53a6fa96 updated os_essence.odin 2019-09-18 16:19:19 +01:00
gingerBill 4cef160c87 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-09-17 19:47:13 +01:00
gingerBill 68582c5ad1 Add suggestions to errors on casts and assignments. 2019-09-17 19:47:04 +01:00
gingerBill a9a2dafca5 Merge pull request #430 from nakst/master
New Essence OS layer; cross-compiling improvements
2019-09-09 14:39:35 +01:00
gingerBill da3467c25f Merge pull request #434 from odin-lang/ThisDrunkDane-patch-1
Make `odin run` return the process exit code
2019-09-08 20:28:10 +01:00
gingerBill 5fc42bf9c9 Update demo.odin 2019-09-08 19:15:12 +01:00
gingerBill 42bbd31df1 Disallow where clauses on non-polymorphic records 2019-09-08 19:03:57 +01:00
gingerBill c7cc38b7d8 Remove assert 2019-09-08 15:47:57 +01:00
gingerBill 4afc78efc6 Add where clauses to struct and union 2019-09-08 12:12:41 +01:00
Mikkel Hjortshøj bc34083c9c Also return on unix 2019-09-08 01:10:54 +02:00
Mikkel Hjortshøj 08dd8414c1 Make odin run return the process exit code 2019-09-08 01:09:04 +02:00
gingerBill d54255505a Fix Compiler does not complain about missing semicolon #433 2019-09-04 18:10:02 +01:00
gingerBill d4914c3546 Fix Ir panic on using append() from within anonymous function #432 2019-09-04 18:06:02 +01:00
gingerBill 772c8779fa Clean up thread pool code 2019-09-03 22:11:21 +01:00
gingerBill c92b2e9612 Fix semaphore posting 2019-09-03 21:17:46 +01:00
gingerBill 1af143b749 Fix debug mode for build.bat 2019-09-02 21:16:17 +01:00
gingerBill 1370c10d1b Fix Converting addresses to function pointers produces llvm-opt error #431 2019-09-02 18:59:07 +01:00
gingerBill 1348d8a8cd Improve thread pool (volatile hints, etc) 2019-09-02 18:49:23 +01:00
gingerBill 495aaacb81 Fix build.bat 2019-09-02 18:35:00 +01:00
nakst 22e982c8fb New Essence OS layer; cross-compiling improvements 2019-09-02 16:46:50 +01:00
gingerBill 6d614ef07c Remove thread naming on thread pool 2019-09-01 23:16:01 +01:00
gingerBill 723f351a6d Remove custom allocator for thread pool 2019-09-01 23:13:29 +01:00
gingerBill c93872cc13 Thread pool fixes 2019-09-01 22:57:53 +01:00
gingerBill 97dece15d7 Minor changes 2019-09-01 22:18:55 +01:00
gingerBill 657103c4cf ThreadPool for the parser 2019-09-01 20:02:39 +01:00
gingerBill b9d3129fb3 where clauses for procedure literals 2019-08-31 20:13:28 +01:00
gingerBill b311540b16 Make require_results an attribute rather than a suffix tag for procedures 2019-08-31 14:48:56 +01:00
gingerBill 07ced1cf0e Fix variable dependency ordering issues caused by procedure literals 2019-08-31 11:12:41 +01:00
gingerBill a1d4ea7718 Merge pull request #425 from thebirk/parser-threading
Fixed parser creating a new thread for each file.
2019-08-29 20:44:48 +01:00
thebirk f921a91fc8 Properly removed the semaphore. 2019-08-29 20:35:12 +02:00
thebirk 4dade34603 Removed unused semaphore on Parser. 2019-08-29 20:34:09 +02:00
thebirk d76249d90b Cleaned up parse_packages and the worker proc. 2019-08-29 20:27:38 +02:00
gingerBill d118fc569a Add intrinsincs.type_is_quaternion 2019-08-29 16:45:36 +01:00
gingerBill 2dc39a5cbd Improve demo.odin 2019-08-29 15:25:46 +01:00
gingerBill c89fc35e94 Fix global variable initialization ordering
(related to #427)
2019-08-29 14:36:42 +01:00
gingerBill 614d209824 Add debug information for quaternion types (fixes #428) 2019-08-29 10:39:03 +01:00
gingerBill f1a7b31209 Fix nested raw_union with using #428 2019-08-28 13:34:55 +01:00
thebirk 6a8b3fee38 Removed gb_thread_set_name because it segfaults on linux. 2019-08-26 20:23:52 +02:00
thebirk 4551521b2c Im just trying things at this point, Bill should just squash this PR at merge time ;) 2019-08-26 19:51:33 +02:00
thebirk 97dfcffa76 Fixed error where the parser would end early. 2019-08-26 19:09:52 +02:00
gingerBill 6d3feb4531 Fix typo in tokenizer (no actual bug) 2019-08-26 16:18:26 +01:00
thebirk c44d25d14f Fixed parser creating a new thread for each file. 2019-08-26 16:47:41 +02:00
gingerBill 25dd00cd0b Add complex/quaternion raw layouts to mem/raw.odin 2019-08-26 14:40:04 +01:00
gingerBill 01c10aa944 inline for loops (only for 'in' based for loops) 2019-08-26 13:54:35 +01:00
gingerBill 4908d1ebdd Update odin_tokenizer to support quaternion literals 2019-08-26 11:56:04 +01:00
gingerBill 7bc146e6fd Built-in Quaternions (Not just an April Fool's Joke any more) 2019-08-26 11:33:05 +01:00
gingerBill 59ab51acec Fix typo 2019-08-23 11:54:23 +01:00
gingerBill cf23954297 Improve #assert to show the procedure and signature it was called with; Allow the ability to print ExactValue correct now. 2019-08-23 11:51:04 +01:00
gingerBill d1cc6534cd Remove the rule that made any declaration prefixed with an underscore private to that package. 2019-08-23 10:32:05 +01:00
gingerBill 1b454c7ded Merge branch 'master' of https://github.com/odin-lang/Odin 2019-08-23 10:24:25 +01:00
gingerBill 150d4e343d Fix ~(1 << x) type inference bug 2019-08-23 10:24:18 +01:00
gingerBill 28ada801a0 Merge pull request #419 from zhibog/master
Added an implementation for Base64. Also provides the ability to supp…
2019-08-16 19:42:03 +01:00
zhibog a7676bff6e Added an implementation for Base64. Also provides the ability to supply your own alphabet and decoding table. 2019-08-15 22:05:06 +02:00
gingerBill 4369298e96 Add reflect/types.odin 2019-08-13 23:21:51 +01:00
gingerBill a58c29582e Add new stuff to package reflect; fix -vet for odin_parser 2019-08-13 23:21:33 +01:00
gingerBill 3ad20a2d2d Remove package types and merge with package reflect 2019-08-13 22:59:07 +01:00
gingerBill b86dfa7af7 Fix compiler crash with #defined #417 2019-08-13 22:51:04 +01:00
gingerBill 980890ee8a Make -vet happy on *nix 2019-08-13 22:39:53 +01:00
gingerBill 0a63690b39 Fix typo in ? array lengths error 2019-08-13 22:34:02 +01:00
gingerBill 0076a4df62 Fix compound literal printing for structs with custom alignment requirements 2019-08-13 22:33:05 +01:00
gingerBill 4c065a7e99 Keep -vet happy 2019-08-13 22:27:52 +01:00
gingerBill 04036aba9c package reflect; fix substring type bug; fix scoping rules for using on procedure parameter 2019-08-11 23:58:49 +01:00
gingerBill b08aa857b3 Remove dead keywords in odin_token 2019-08-09 23:01:22 +01:00
gingerBill 2d26278a65 Make structs with the same fields but with different tags distinct types 2019-08-09 22:52:19 +01:00
gingerBill 27a3c5449a Fix global variable initialization for certain types. 2019-08-09 22:35:48 +01:00
gingerBill 9c63212824 Struct field tags 2019-08-09 21:59:58 +01:00
gingerBill 65d41d4248 Fix bit_field comparison against nil #414 2019-08-09 20:31:11 +01:00
gingerBill b04231dd95 Improve implementation of intrinsics.type_* procedures 2019-08-04 14:54:23 +01:00
gingerBill 37633c1d2a intrinsics.type_* constant evaluation procedures 2019-08-04 11:02:00 +01:00
gingerBill 5877017d30 Add error message for non-constant polymorphic name parameters 2019-08-03 10:15:31 +01:00
gingerBill 132fdf14b8 Fix min, max, clamp final type bug 2019-08-03 10:07:09 +01:00
gingerBill e7d3001dd1 Fix constant default value error #408 (typo) 2019-07-29 18:33:06 +01:00
gingerBill f163181204 Add extra hints for LLVM for implicit reference parameters 2019-07-29 10:43:07 +01:00
gingerBill 2c5c8192f8 Fix parsing for procedure literals expression statements; improve assert performance; other minor fixes 2019-07-28 22:58:56 +01:00
gingerBill 162c87b1b8 Minor code clean-up 2019-07-28 18:44:50 +01:00
gingerBill 77734ea967 Improve the performance of simple array comparisons 2019-07-27 11:59:50 +01:00
gingerBill 912fc2890b Fix array comparisons and fix f32 literal LLVM issue regarding accurate representation 2019-07-27 11:33:22 +01:00
gingerBill 14059583cd Fix array comparisons 2019-07-27 10:44:40 +01:00
gingerBill f3bffb9810 Improvement to the Odin calling conventions to pass certain things by "implicit reference" (const & in C++) 2019-07-27 10:20:11 +01:00
gingerBill 540730c0be Merge branch 'master' of https://github.com/odin-lang/Odin 2019-07-27 00:45:44 +01:00
gingerBill 40f0e74b8c Change scoping rules to allow for shadowing of procedure parameters but not named return values 2019-07-27 00:45:36 +01:00
Mikkel Hjortshøj 5ca0cd60d0 Merge pull request #406 from JoshuaManton/master
Fix `scale_f32` and `scale_vec3` returning the wrong variable
2019-07-20 22:21:58 +02:00
Joshua Mark Manton 96f0a08725 Fix scale_f32 and scale_vec3 from returning the wrong variable. 2019-07-20 13:15:51 -07:00
Chris Heyes d85893954d Impl time for macOS 2019-07-16 23:49:53 +01:00
gingerBill d26033eb23 Revert demo.odin 2019-07-15 22:41:25 +01:00
gingerBill c7a70be824 Fix __get_map_key 2019-07-15 22:38:51 +01:00
gingerBill 08c490d9ac Fix bounds checking on slices for constant parameters 2019-07-15 22:26:51 +01:00
gingerBill 8ee7ee7120 Fix core library for the new procedure parameter addressing mode 2019-07-15 22:16:27 +01:00
gingerBill d471a59041 IR fix array comparisons 2019-07-15 21:32:38 +01:00
gingerBill f25818e923 Make procedure parameters just named values rather than copied variables 2019-07-15 21:18:37 +01:00
gingerBill 3d531be711 Improve type hinting for named parameters in call expressions 2019-07-13 15:38:50 +01:00
gingerBill 56d365a4e7 Improve type inference for procedure group parameters 2019-07-13 13:34:21 +01:00
gingerBill 308300c1fc Add extra error handling for parsing slices 2019-07-09 11:18:50 +01:00
gingerBill 927d6814f2 slice_data_cast 2019-07-09 11:09:46 +01:00
gingerBill 7c99f52187 Add minimum requirement of 2 variants for #no_nil 2019-07-09 10:49:45 +01:00
gingerBill 4ab9edeb53 union #no_nil 2019-07-09 10:28:13 +01:00
gingerBill c5b3d7a736 Update package odin_parser 2019-07-07 16:20:58 +01:00
gingerBill d7172e168e Fix target list branch rules for name-labelled block/if statements 2019-07-07 16:06:41 +01:00
gingerBill d99ffe604f Fix unions with zero variants 2019-07-07 14:38:11 +01:00
gingerBill b77c79294c Merge branch 'master' of https://github.com/odin-lang/Odin 2019-07-07 14:14:36 +01:00
gingerBill 8e722274f0 Disallow blank identifier polymorphic types $_ 2019-07-07 14:14:28 +01:00
gingerBill ebe7fc23a5 Merge pull request #400 from asmoaesl/patch-1
Correct two typos
2019-06-29 12:09:46 +01:00
Luke I. Wilson 4d40f564ef Correct two typos 2019-06-28 20:42:59 -05:00
gingerBill fd62959bf4 Fix procedure constant declaration value type assignment checking 2019-06-21 23:11:14 +01:00
gingerBill 8b8cada33e Fix procedure group compiler assert with no matching arguments #393 2019-06-21 22:55:00 +01:00
gingerBill aaa24894b6 Fix double-pointer indexing bug #396 2019-06-21 22:50:29 +01:00
gingerBill 2af19c496e Fix comparison for bit field values #386 2019-06-21 22:48:37 +01:00
gingerBill fea34b32ea Merge branch 'master' of https://github.com/odin-lang/Odin 2019-06-21 22:40:20 +01:00
gingerBill b891c0feab Fix ranges in switch statement for strings 2019-06-21 22:40:11 +01:00
gingerBill 9f039c323e Update FUNDING.yml 2019-06-20 09:29:18 +01:00
gingerBill b0dd3ac599 v0.10.0 2019-06-02 12:27:36 +01:00
gingerBill b38a8cfb12 Move internal 128-bit stuff to a windows specific file 2019-06-01 12:26:54 +01:00
gingerBill 4c79b52867 Update README.md 2019-05-30 18:08:35 +01:00
gingerBill 6dba05b00d Update README.md 2019-05-30 18:07:54 +01:00
gingerBill 32a29d627a Create FUNDING.yml 2019-05-30 17:42:15 +01:00
gingerBill caf9bc6be9 Pedantic conversions on query data 2019-05-29 16:59:28 +01:00
gingerBill 654740d5b1 Fixes to ABI 2019-05-29 16:49:26 +01:00
gingerBill b894e2b378 Fix bit set size with 128-bit integers 2019-05-28 20:57:02 +01:00
gingerBill c40acd008e Add i128/u128 support for bit sets 2019-05-28 20:53:56 +01:00
gingerBill 3d2279fba0 Support 128-bit integers i128 u128 2019-05-28 20:27:45 +01:00
gingerBill 2b080dbbc2 sync_atomic_* make most ordering parameters compile-time constant 2019-05-28 15:57:06 +01:00
gingerBill 9cadd58465 Improve tokenizer slightly 2019-05-28 14:44:32 +01:00
gingerBill 65e9b4d5f0 Update parsers 2019-05-28 12:55:55 +01:00
gingerBill fb3d73cb20 Make core library use a..<b rather than doing a..b-1 2019-05-28 12:52:20 +01:00
gingerBill 222941727f Add ..< operator for ranges; Add extra checking for bit set assignments 2019-05-28 12:45:20 +01:00
gingerBill 5697d6df74 -go-to-definitions (OGTD file format) 2019-05-26 15:16:45 +01:00
gingerBill 426c1ed6f4 -compact flag for 'odin query' 2019-05-25 20:28:49 +01:00
gingerBill 458ec5922e odin query
Output .json file containing information about the program
2019-05-25 20:24:19 +01:00
gingerBill f5fdd031f9 Fix polymorphic procedure return by pointer values #374 2019-05-19 10:45:27 +01:00
gingerBill ceb58ae04f Change import name rules 2019-05-19 10:01:35 +01:00
gingerBill 868603f617 Merge pull request #375 from Zilarrezko/master
make_builder function now properly uses given allocator
2019-05-19 09:55:03 +01:00
Zilarrezko d2faa9bef1 make_builder function now properly uses given allocator 2019-05-18 18:57:34 -07:00
gingerBill b1663a14e9 Add an error for C-style pointer selector expressions using '->' when parsing 2019-05-09 22:59:17 +01:00
gingerBill 3fc60930e6 Fix constant representability rules 2019-05-09 22:46:50 +01:00
gingerBill 665734f04f Fix package odin/parser 2019-05-09 16:35:35 +01:00
gingerBill 16f3bc2c0b Allow comparisons with bit field values 2019-05-09 13:18:57 +01:00
gingerBill 71a733e3b5 Allow booleans to be assigned to a 1-bit bit field value 2019-05-09 13:04:15 +01:00
gingerBill a66612e8ae Remove test code 2019-05-09 11:07:26 +01:00
gingerBill 00c0ce45b3 Fix bug with clamp 2019-05-06 18:29:22 +01:00
gingerBill ab0afa548b Fix ||= and &&= 2019-05-06 11:32:35 +01:00
gingerBill ea1690b7a1 Improve type inference for variadic parameters 2019-05-04 14:01:04 +01:00
gingerBill a5ff983266 Fix parapoly related bugs #370 2019-05-04 13:11:56 +01:00
gingerBill a46a1f5f34 Minor change to bit_field assignment rules 2019-05-04 13:02:15 +01:00
gingerBill 40135cbc66 Add float64_range and float32_range to package math/rand 2019-04-28 20:35:14 +01:00
gingerBill c61fd3a70a Modify type_set_offsets to patch minor bug 2019-04-28 20:34:51 +01:00
gingerBill 45fbc4e8c5 Add #load to package odin/parser 2019-04-21 22:36:52 +01:00
gingerBill 9ce8f124bb Slight change to determine_path_from_string rules 2019-04-20 16:52:09 +01:00
gingerBill 63bbb9b62f Change the file name rules for imports (use / rather than \ on windows) 2019-04-20 16:47:28 +01:00
gingerBill 56c4039e72 #load directive (request from #368) (Basic implementation) 2019-04-20 15:05:35 +01:00
gingerBill 0755dfbd5d Merge branch 'master' of https://github.com/odin-lang/Odin 2019-04-19 11:39:12 +01:00
gingerBill 2780f82f30 Fix is_operand_value to support more addressing modes 2019-04-19 11:39:01 +01:00
Jeroen van Rijn 5dcb5c2ba3 Merge pull request #367 from kevinw/fix-json-parsing
Fix some JSON parsing bugs.
2019-04-06 16:35:46 +02:00
Jeroen van Rijn 9d552d04ea Merge pull request #358 from kevinw/master
Fix some -vet warnings; change import to core:math/bits
2019-04-06 16:34:01 +02:00
Kevin Watters 88e1b93786 Fix som JSON parsing bugs.
- Single digit integer keys `{"a": 5}`
` Negative float keys `{"b": -42.0}`
2019-04-06 09:24:53 -04:00
Kevin Watters 62f5eb5bca Fix som JSON parsing bugs.
- Single digit integer keys `{"a": 5}`
` Negative float keys `{"b": -42.0}`
2019-04-06 09:19:09 -04:00
Kevin Watters 6ab471ff87 Merge branch 'master' of github.com:odin-lang/Odin 2019-04-06 08:41:56 -04:00
Jeroen van Rijn 155b138aa4 call_external_process cleanup 2019-04-05 13:18:50 +02:00
Jeroen van Rijn a730b04bf1 Add helpers to launch process and open website. 2019-04-05 13:02:49 +02:00
Kevin Watters 957e1e1f07 Merge branch 'master' of github.com:odin-lang/Odin 2019-04-01 09:34:25 -04:00
gingerBill 133f88406f The Proposal Process 2019-03-31 23:50:56 +01:00
gingerBill ecd2eacd75 Remove dead keywords; Fix min and max for enum types 2019-03-31 22:35:05 +01:00
gingerBill 2614830c69 Minor code organization change 2019-03-31 18:29:57 +01:00
Kevin Watters 381fbd3daf Merge branch 'master' of github.com:odin-lang/Odin 2019-03-31 12:03:22 -04:00
gingerBill dd9113786c Remove -keep-temp-files from the build.bat 2019-03-31 13:06:01 +01:00
gingerBill 1354f53d02 Remove derived from context; Fix parsing issue for typeid specializations in record parameters; Fix runtime printing of types 2019-03-31 11:58:54 +01:00
gingerBill 564e85ee29 Modify ir_generate_array name logic slightly 2019-03-31 11:22:27 +01:00
gingerBill ef04d13337 Use context for assert-based things. 2019-03-30 15:10:40 +00:00
gingerBill 68d4bde82f Overrideable stdin, stdout, stderr at the context level 2019-03-30 14:51:42 +00:00
gingerBill a019059975 Fix -vet for demo.odin 2019-03-30 10:52:53 +00:00
gingerBill 7580ec494b Disallow ambiguous singularly variadic polymorphic parameters #361 2019-03-30 10:43:53 +00:00
gingerBill a9b20c29b1 Fix slicing issue; Change path of math/bits in package json #363 2019-03-30 10:17:31 +00:00
Kevin Watters 76a2807b56 Remove unused import from demo.odin. 2019-03-26 11:20:49 -04:00
Kevin Watters 14ff561f6c Merge branch 'master' of github.com:odin-lang/Odin 2019-03-26 11:17:58 -04:00
gingerBill 1fd677b42e Remove *_remove from demo and use built-in versions 2019-03-25 21:29:21 +00:00
gingerBill 6b18b90222 Fix possible buffer overflows in package strconv 2019-03-25 21:26:23 +00:00
gingerBill 9e6d488063 Modify slice_expr_error_* logic to change depending on which parameters are passed 2019-03-25 21:20:12 +00:00
gingerBill 4a15689776 Remove bounds checks for slice expressions with both indices empty 2019-03-25 21:00:45 +00:00
gingerBill c785c3569f Fix runtime.*_expr_error error 2019-03-25 20:42:47 +00:00
Kevin Watters e6f9b4fb11 Fix some -vet warnings; change import to core:math/bits 2019-03-25 09:23:46 -04:00
gingerBill b978959fae Improve package strconv 2019-03-24 20:58:01 +00:00
gingerBill 8b09ab6fe7 Move core:bits to core:math/bits 2019-03-24 20:39:01 +00:00
gingerBill 2347dca9d9 Improve package math/rand 2019-03-24 20:36:39 +00:00
gingerBill 2ada90e094 Improve a tokenizer error message; Add extra tokens for in and notin for fun! 2019-03-24 19:12:41 +00:00
gingerBill a137a06b00 Allow implicit selectors to unions with only one enum variant 2019-03-24 12:14:45 +00:00
gingerBill b1684fe455 @(private) for foreign blocks; Improve foreign signature similarity rules 2019-03-24 11:58:26 +00:00
gingerBill 886054f0f8 Add error message for when trying to assign a type to a variable. 2019-03-22 13:55:29 +00:00
gingerBill 0e1cfa5a0a Disallow casting to and from cstring/pointers
TODO: get a better error message
2019-03-19 20:34:06 +00:00
gingerBill 400558abcd Fix fmt.println a rawptr causes access violation #356 2019-03-19 20:27:29 +00:00
gingerBill d75634ff5e Merge branch 'master' of https://github.com/odin-lang/Odin 2019-03-19 20:22:32 +00:00
gingerBill 0c04b9398a Fix bug with assigning certain integers to a bit_field #353 2019-03-19 20:22:25 +00:00
Jeroen van Rijn 290c111206 Merge pull request #355 from mattt-b/master
Replace calls to deprecated string functions on linux
2019-03-18 15:27:07 +01:00
matt 314d5a778e Replace calls to deprecated string functions on linux 2019-03-18 07:13:52 -07:00
Jeroen van Rijn dc706d8a6b Vet CEL 2019-03-17 23:27:13 +01:00
gingerBill a6fb2dd587 Fix Erroneous redeclaration error with using import #354 2019-03-17 20:43:54 +00:00
gingerBill 4e93b70f8a Fix bit_field scoping bug 2019-03-17 14:48:32 +00:00
gingerBill 1eaa47ebae Fix using import behaviour - #352 2019-03-17 13:03:39 +00:00
gingerBill 3a31444656 Minor changes to fmt of auto deferencing 2019-03-16 11:00:37 +00:00
gingerBill f7efaf2ba2 fmt.printf support for pointer to container (one level deep) 2019-03-16 00:10:57 +00:00
gingerBill 14c6f2f258 Add extra pointer printing options to fmt.printf 2019-03-15 23:49:47 +00:00
gingerBill 231f3cc15a %h support in fmt.printf for hexadecimal floats 2019-03-15 23:22:05 +00:00
gingerBill 332e598357 %e and %g support in fmt.printf 2019-03-15 23:13:06 +00:00
gingerBill 716373836c Disallow attributes on alias declarations 2019-03-15 19:01:36 +00:00
gingerBill fdb60b2d51 Improve package strings 2019-03-15 18:30:39 +00:00
gingerBill 885c5dc8b7 Fix issue with deferred_* attributes 2019-03-15 16:39:49 +00:00
gingerBill 394baa9ddd Merge branch 'master' of https://github.com/odin-lang/Odin 2019-03-15 15:41:15 +00:00
gingerBill 3d86fc2f2f Minor adjustments 2019-03-15 15:41:06 +00:00
Jeroen van Rijn 61b07335d8 Fix build error on !Windows. 2019-03-15 07:37:20 +01:00
gingerBill 712744ef36 Fix ir_copy_value_to_ptr usage in ir_emit_call #350 2019-03-14 23:41:48 +00:00
gingerBill dbcd49acfc Add -pdb-name for custom names of PDBs 2019-03-14 23:26:32 +00:00
gingerBill 291bf0c143 Fix #raw_union bug caused by typo #349 2019-03-14 23:25:55 +00:00
gingerBill bdab5e00da Minor code clean up 2019-03-11 19:52:40 +00:00
gingerBill e781056df1 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-03-11 09:32:24 +00:00
gingerBill b08d944c33 Fix typo in demo.odin 2019-03-11 09:32:15 +00:00
Jeroen van Rijn 4f24f1172e Stylistic improvements to new comdlg helpers. 2019-03-09 13:48:48 +01:00
Jeroen van Rijn 4446a1431a Merge pull request #348 from Kelimion/master
Add win32.get_cwd for current working directory and convenience functions for open + save dialogs.
2019-03-09 12:51:14 +01:00
Jeroen van Rijn 090937f8af Add convenience functions for open + save dialogs. 2019-03-09 12:45:17 +01:00
Jeroen van Rijn d852b0c948 Add win32.get_cwd to return the current working directory 2019-03-09 11:08:50 +01:00
gingerBill 007a7989b8 Add implicit selector expression examples to demo.odin 2019-03-06 20:06:37 +00:00
gingerBill 5c04800831 Add type inference to index expressions for maps 2019-03-06 20:01:46 +00:00
gingerBill c634d4a96d Using implicit selector expressions in the core library 2019-03-06 19:13:50 +00:00
gingerBill c67ea97845 Add implicit selector expressions for in/notin 2019-03-06 19:08:37 +00:00
gingerBill 15d3f4c190 Allow implicit selector expressions in switch statements 2019-03-06 16:23:50 +00:00
gingerBill 1b3ec66fa2 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-03-06 16:19:55 +00:00
gingerBill ad3b6ab718 Implicit Selector Expressions: .A 2019-03-06 16:19:47 +00:00
Jeroen van Rijn 8c618225bf Merge pull request #347 from Kelimion/master
Fix typo in `cel` tokeniser.
2019-03-04 13:46:51 +01:00
Jeroen van Rijn 1f5ab0b5f1 Fix typo in cel tokeniser. 2019-03-04 13:41:47 +01:00
gingerBill 1652d5033b Merge branch 'master' of https://github.com/odin-lang/Odin 2019-03-03 12:09:06 +00:00
gingerBill 9b4b20e8b1 package dynlib 2019-03-03 12:08:26 +00:00
Jeroen van Rijn 8fb8b5ed7e Merge pull request #346 from Kelimion/fix-issue-345
Fix #345: panic when using enum as map key
2019-03-03 13:00:00 +01:00
Jeroen van Rijn 7bd86bb3ec well, that was a stupid copy/paste bug 2019-03-02 13:24:11 +01:00
Jeroen van Rijn b6d6eb6ae2 Fix #345: Panic when using enum as map key
Also add a little map demo.
2019-03-02 13:21:01 +01:00
Jeroen van Rijn a126d2ba16 Merge remote-tracking branch 'upstream/master' into fix-issue-345 2019-03-02 12:31:45 +01:00
Jeroen van Rijn 6faab8e47a Fix #345: panic when using enum as map key 2019-02-26 13:51:56 +01:00
gingerBill 76a6757ee9 Add os.file_size_from_path 2019-02-25 18:03:44 +00:00
gingerBill 0c8746ada6 Add support for custom keywords in core:odin/* packages 2019-02-25 12:41:43 +00:00
gingerBill a0c81c79ad Fix bugs: Array Literals with constant elements; IR printing of raw procedure types 2019-02-24 10:30:58 +00:00
gingerBill cdfaa643cc Reimplement -collection; remove static from Odin tokenizer/parser in core library 2019-02-23 23:30:03 +00:00
gingerBill 989cc893ef FIX TYPO! 2019-02-23 23:25:46 +00:00
gingerBill 2878cd8241 New build flag: -define:foo=123 2019-02-23 23:21:27 +00:00
gingerBill a9ab90bd24 Make static an attribute rather than a keyword prefix 2019-02-23 22:17:27 +00:00
gingerBill e551d2b25e Replace foreign export {} with @export 2019-02-23 21:39:47 +00:00
gingerBill 38ae2e9efa Allow basic arithmetic operations for vectors 2019-02-23 18:05:41 +00:00
gingerBill 684945ea57 Fix calling conventions for simd vector types 2019-02-23 17:45:30 +00:00
gingerBill 4c51384ad6 intrinsics.vector type (Experimental) 2019-02-23 16:44:16 +00:00
gingerBill 64bd884d94 Add "none" calling convention 2019-02-23 14:42:44 +00:00
gingerBill a07232ea63 Fix missing break in switch statement for deferred_in in the IR 2019-02-23 14:11:48 +00:00
gingerBill 79b585ada8 Add minor additions to mem, sync, and sys/win32 2019-02-21 21:45:33 +00:00
gingerBill f917935f9d Disallow compound literals for struct #raw_union (fix) 2019-02-19 10:04:36 +00:00
gingerBill dbd0638853 Fix untyped ternary string IR conversion 2019-02-14 11:11:05 +00:00
gingerBill 53d8216311 Reorganize package mem 2019-02-10 22:15:34 +00:00
gingerBill 6a0c3d5599 Add default_parser procedure to package odin_parser 2019-02-10 22:05:18 +00:00
gingerBill 9647fb2a4b Add Stack and Small_Stack allocators to package mem 2019-02-10 22:04:58 +00:00
gingerBill 42f936742e Add extra dead code elimination 2019-02-10 21:23:02 +00:00
gingerBill e2d4667639 Fix data layout issue on Windows; Remove unused loads in SSA 2019-02-10 20:51:19 +00:00
gingerBill b74d828af7 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-02-09 22:34:37 +00:00
gingerBill e16409f88a Fix package odin_parser bugs 2019-02-09 22:34:27 +00:00
thebirk 6571f07c7e Fixed typo in copy_sign_f64. 2019-02-08 12:58:30 +01:00
gingerBill 4b7a09b92e Merge branch 'master' of https://github.com/odin-lang/Odin 2019-02-06 22:45:04 +00:00
gingerBill 26fb1fa18c Fix lead_comment issue is package odin_parser 2019-02-06 22:44:54 +00:00
Mikkel Hjortshøj 24c43d33bb Export LLVM path on macOS (CI) 2019-02-06 19:08:32 +01:00
Mikkel Hjortshøj 7343f0c7ff Fix recursive variable in makefile on macOS 2019-02-06 18:56:02 +01:00
Mikkel Hjortshøj 3c8cda514b Fix makefile for macOS 2019-02-06 18:49:21 +01:00
gingerBill bc954df80e Merge branch 'master' of https://github.com/odin-lang/Odin 2019-02-06 16:07:32 +00:00
gingerBill e1ae359a77 Replace redundant code 2019-02-06 16:06:48 +00:00
Mikkel Hjortshoej c9602953aa last fixes to bats and README 2019-02-06 15:53:40 +01:00
Mikkel Hjortshoej 0185b43c2f Create CI files 2019-02-06 15:53:40 +01:00
gingerBill bc5c37ebb1 Extra checks to reduce mem.zero calls 2019-02-06 13:47:52 +00:00
gingerBill 0133dd9034 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-02-06 13:33:52 +00:00
gingerBill a194aa5a9e Minimize mem.zero use 2019-02-06 13:33:31 +00:00
gingerBill 766f3e259f Merge pull request #334 from odin-lang/ThisDrunkDane-patch-1
Add issue templates
2019-02-05 22:21:27 +00:00
Mikkel Hjortshøj 6092cc0497 Add issue templates 2019-02-05 21:19:56 +01:00
gingerBill fa5d00521b Remove inline from many of the mem.* procedures 2019-02-04 12:15:51 +00:00
gingerBill d1e29400d3 Merge pull request #331 from thebirk/fix-out-linux
Fixed macOS not compiling
2019-02-02 23:14:26 +00:00
gingerBill 2dc7aaec82 Merge pull request #332 from thebirk/fix-big-string-printing
Fix #322, now correctly printing big strings.
2019-02-02 23:14:08 +00:00
thebirk b7242f9d4b Fix #322, now correctly printing big strings. 2019-02-02 00:11:41 +01:00
thebirk 7ea7fc10db Fixed macOS not compiling. 2019-02-01 15:33:27 +01:00
gingerBill 141da818ba Merge pull request #330 from thebirk/fix-out-linux
Fixed -out dropping extension on linux. Issue #305
2019-02-01 14:25:36 +00:00
thebirk dc3d62d437 Fixed -out dropping extension on linux. 2019-02-01 15:12:20 +01:00
gingerBill dee28d998f Allow for @indent for attributes that don't require any parameters; Add -ignore-unknown-attributes 2019-01-30 14:24:14 +00:00
gingerBill 96ef6aa7f3 Merge pull request #327 from Tetralux/tet/pass-args-to-run
Provide a way to pass arguments to compiled executable during 'odin run program.odin'.
2019-01-29 22:12:01 +00:00
gingerBill 238a40321a inline certain mem.* procedures 2019-01-29 22:08:48 +00:00
gingerBill 1aea59a0fc Fix typo in parser.cpp 2019-01-29 21:53:36 +00:00
gingerBill c6dee52abe Finish up package odin_parser 2019-01-29 21:53:16 +00:00
Tetralux 1e180d611d Allow 'odin run program.odin -- <args-for-program.exe> 2019-01-28 17:58:48 +00:00
gingerBill e452765d28 Patch minor IR bug 2019-01-28 12:13:37 +00:00
gingerBill 2b80683fc7 Vet odin/* packages 2019-01-27 19:01:33 +00:00
gingerBill 5f840ea2fc package odin_parser 2019-01-26 20:17:03 +00:00
gingerBill c72427fd1e package odin_parser 2019-01-26 20:14:06 +00:00
gingerBill 44b959648c Pass any and union "by pointer" to make the tag a pointer 2019-01-26 20:13:43 +00:00
gingerBill a96bf08266 Fix tokenizer.odin bugs 2019-01-24 21:40:09 +00:00
gingerBill ebaf48c07d Fix subtyping rules for heavily nested using in structs 2019-01-24 21:17:30 +00:00
gingerBill c197a27185 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-01-24 15:53:27 +00:00
gingerBill 5ccccf8816 Add package odin/token; package odin/tokenizer 2019-01-24 15:53:17 +00:00
gingerBill 345e790f52 Remove dead code 2019-01-24 15:52:51 +00:00
gingerBill fd529b97be Merge pull request #324 from Tetralux/tet/ptr-endianness
Fix assert when printing IR of pointer with endianness
2019-01-18 14:24:19 +00:00
Jeroen van Rijn be1a3488a4 Initial support for GetVersionExA 2019-01-18 13:37:40 +01:00
Tetralux 46c610d6e5 Fix printing IR of integer as a pointer with endianness.
When converting an integer value into a pointer and writing out the IR
for it in 'ir_print_exact_value', 'is_type_integer_endian_{little,big}'
did not correct handle being asked about pointer types.

They now reply with whether the endianness of 'uintptr' matches the
endianness being asked about.
2019-01-16 17:22:25 +00:00
gingerBill db2eff6847 Fix typo in json/parser.odin 2019-01-14 20:44:27 +00:00
gingerBill e047d9eb5e Update package json parser to store the end position on the values 2019-01-14 15:51:52 +00:00
gingerBill 3113e8c892 Minimize buffer size for write_u64 and write_i64 2019-01-13 21:40:18 +00:00
gingerBill 19e37c852e Change deferred_* scoping behaviour for if 2019-01-13 20:51:26 +00:00
gingerBill 8fc24fd6f2 Replace deferred with deferred_none, deferred_in, deferred_out 2019-01-13 19:34:08 +00:00
gingerBill 493f11521d Check for _ for import names too 2019-01-13 17:44:38 +00:00
gingerBill 3363e2c199 Change import name determination rules
Use custom name if given, then directory name, then the package name
2019-01-13 11:54:25 +00:00
gingerBill cf94d1735d Add extra explicit entity usage 2019-01-13 11:24:03 +00:00
gingerBill d9245a6af3 Update Makefile to reflect build.sh 2019-01-13 11:19:01 +00:00
gingerBill d453b9a5b1 Fix checking _ constant declarations with a procedure 2019-01-10 11:22:52 +00:00
gingerBill 5af20aa467 Make encoding/json use []byte rather than string 2019-01-07 23:08:38 +00:00
gingerBill cd2c4c02e1 Merge pull request #320 from thebirk/add-diff-to-time
Added diff() to core:time.
2019-01-07 15:52:33 +00:00
gingerBill 6c21e99832 json.marshal 2019-01-06 23:32:50 +00:00
gingerBill 08598b9425 Support NaN and Infinity for JSON5 2019-01-06 22:25:02 +00:00
gingerBill 6295f6747f strings.write_quoted_string 2019-01-06 22:16:14 +00:00
gingerBill 64f84ef9a3 fmt.printf("%q", str); (quotes strings) 2019-01-06 22:11:45 +00:00
gingerBill d1b9f3ac74 package json; JSON5 support 2019-01-06 21:48:13 +00:00
gingerBill d732a51587 Add json.is_valid file 2019-01-06 20:44:52 +00:00
gingerBill 9487f8c92e Add json.is_valid 2019-01-06 20:44:39 +00:00
gingerBill c5def60224 Begin work on package json 2019-01-06 20:37:12 +00:00
thebirk ca2220214e Added diff() to core:time. 2019-01-06 19:40:57 +01:00
gingerBill 6e6a053823 Add strings.destroy_builder 2019-01-06 17:59:42 +00:00
gingerBill 686e0ef3d1 Merge pull request #319 from mattt-b/master
Vet time_linux
2019-01-06 15:22:21 +00:00
gingerBill 594238a86c Reorganize fmt and strings; Replace fmt.String_Buffer with strings.Builder 2019-01-06 14:41:42 +00:00
matt c60766f8e6 Vet time_linux 2019-01-06 05:25:27 -07:00
gingerBill 5acea1bceb Source_Code_Location.hash; %#v printing for Source_Code_Location; allow typeid for map keys 2019-01-05 15:56:47 +00:00
gingerBill aac643f476 Remove debug printing text 2019-01-05 11:30:13 +00:00
gingerBill 37edbfeb74 Add missing types for min dep 2019-01-05 11:18:43 +00:00
gingerBill 51da3e469b Add win32 cursor stuff 2019-01-05 11:16:14 +00:00
gingerBill 9156af2bab Add missing types to minimum dependency checking 2019-01-05 11:15:23 +00:00
gingerBill 3a18ae3978 Remove alignment experiment 2019-01-04 17:39:20 +00:00
gingerBill f294c1adee ir_print: Ignore load's align
(experimental idea as it might not be needed)
2019-01-04 10:39:39 +00:00
gingerBill bb93a8b131 Fix TODO ParameterValue_Location 2019-01-04 10:19:39 +00:00
gingerBill 5bfe5ad82e Remove unused directive 2019-01-03 12:21:11 +00:00
gingerBill dd28fe6e82 Update CEL 2019-01-03 00:12:24 +00:00
gingerBill cda0f4d8f3 Fix using struct cycle check 2019-01-02 23:55:21 +00:00
gingerBill 0546b5c218 Add sys/win32/comdlg32.odin 2019-01-02 20:51:48 +00:00
gingerBill 61a3e50d1b Reorganize sys/win32 2019-01-02 19:17:27 +00:00
gingerBill 75aeb02c39 Merge pull request #315 from thebirk/fix-bud-for-lazy-bill
Fixed bug for Bill because he is a lazy boi.
2019-01-02 15:20:35 +00:00
thebirk a32f024d94 Fixed bug for Bill because he is a lazy boi. 2019-01-02 16:18:55 +01:00
gingerBill 37d993c417 Merge pull request #314 from thebirk/int_from_arg_fix
Fixed int_from_arg not consuming argument.
2019-01-02 15:01:45 +00:00
thebirk bcbb59dc11 Fixed int_from_arg not consuming argument. 2019-01-02 15:56:35 +01:00
gingerBill c1ec45dc0a Update sys/win32; Add Menu stuff 2019-01-01 20:18:48 +00:00
gingerBill 0778d18bc7 Fix using with bit_field 2019-01-01 15:11:54 +00:00
gingerBill d7e9b8d374 Update README.md 2019-01-01 14:06:15 +00:00
gingerBill f647187e53 Fix defer on branching with new scoping rules 2019-01-01 11:59:09 +00:00
gingerBill 9dabbc2c95 Add entity use when using is applied to a variable declaration 2018-12-31 16:43:58 +00:00
gingerBill 4167168c63 Fix vetting 2018-12-31 16:37:27 +00:00
gingerBill aa156e4bfc Vet demo.odin 2018-12-31 15:51:53 +00:00
gingerBill 1c9656aedb Vet core library 2018-12-31 15:50:49 +00:00
gingerBill 8b2f902f3d Fix parsing issue with stray } and case at the file scope 2018-12-31 15:20:47 +00:00
gingerBill bbece7e910 Remove some unneeded zero emits 2018-12-31 13:00:55 +00:00
gingerBill e5f188241c Move error handling for bounds checking into separate procedures (eliminate caching issues) 2018-12-31 11:41:56 +00:00
gingerBill 6d3203c11b Remove useless assertion in find_import_path 2018-12-30 15:40:45 +00:00
gingerBill 5ba3d90893 Fix os_windows.odin bugs 2018-12-30 14:43:39 +00:00
gingerBill 894f267bbf Merge pull request #311 from mattt-b/master
Fix bugs and inconsistencies with linux versions of os package
2018-12-30 09:50:00 +00:00
matt e084799b31 Fix bugs and inconsistencies with linux versions of os package 2018-12-30 02:06:41 -07:00
gingerBill 3ba3421f5f Fix static procedure name mangling 2018-12-29 19:57:25 +00:00
gingerBill 2bbad5903f Add static to fix_advance_to_next_stmt 2018-12-28 13:32:59 +00:00
gingerBill a240a3d146 static variable declarations (Experimental) 2018-12-28 13:31:06 +00:00
gingerBill 775f1e2c95 Fix default parameter assignment checking 2018-12-28 11:20:31 +00:00
gingerBill 7c982b6e10 min & max for types 2018-12-27 12:12:14 +00:00
gingerBill cc14180e9d Update README.md 2018-12-27 10:51:15 +00:00
gingerBill b2d40680c8 Fix join and concatenate to use the supplied allocator 2018-12-26 19:38:05 +00:00
gingerBill 8662df2b7f Update package strings 2018-12-26 19:33:56 +00:00
gingerBill 6abbc9f1b5 Merge pull request #310 from mattt-b/master
Fix os.open on linux/osx
2018-12-26 17:37:20 +00:00
gingerBill 66a9fde12c Remove #[...] attribute syntax.
(Not really worth the change)
2018-12-26 16:23:25 +00:00
gingerBill eb5af2876a Support #[...] as an alternative attribute syntax
(Experimentation between `@()` and `#[]`)
2018-12-26 12:19:12 +00:00
gingerBill 1f2fdddc6d Support #! comments 2018-12-26 12:00:16 +00:00
matt 0bcf53b513 Fix os.open on linux/osx 2018-12-26 04:31:12 -07:00
gingerBill 956dd26aa0 Fix race condition; Change for in addressing mode 2018-12-24 16:11:24 +00:00
gingerBill b504d6e12a notin operator 2018-12-21 11:34:15 +00:00
gingerBill b4e83a430a Add card procedure to measure cardinality of a bit_set 2018-12-21 10:31:10 +00:00
gingerBill e3d7e6f76a Fix typo in modf_f32 2018-12-20 18:11:27 +00:00
gingerBill 5c3dc30dc0 More correct floor and ceil procedures. 2018-12-20 10:54:56 +00:00
gingerBill c508e46ed9 Merge pull request #308 from hasenj/master
fix missing declaration in osx
2018-12-18 13:05:44 +00:00
Hasen Judy 9d85f236b8 fix missing declaration in osx 2018-12-18 21:47:23 +09:00
gingerBill 3a05a2e562 Fix not for bit sets 2018-12-17 13:12:48 +00:00
gingerBill 68384a452f Fix scoping determination for IR 2018-12-17 11:36:15 +00:00
gingerBill 34b6486361 Fix constant out of bounds bug 2018-12-15 22:30:52 +00:00
gingerBill 1ce90b2166 Remove weird bit_set shorthand; Add extra type hinting 2018-12-15 21:46:27 +00:00
gingerBill 9d6666f333 Disallow casting between cstring and []u8 2018-12-14 21:58:12 +00:00
gingerBill d29335ecec Add deferred procedure associations to demo.odin 2018-12-14 21:17:32 +00:00
gingerBill 95873e66ab deferred procedure attribute 2018-12-14 21:05:02 +00:00
gingerBill b7eebe5d00 Fix polymorphic record types with constant value parameters 2018-12-14 18:36:06 +00:00
gingerBill 57d4333ed3 Fix polymorphic procedure generation with debug information 2018-12-14 15:45:14 +00:00
gingerBill 26f11f12ab Fix polymorphic type parameter argument count checking #298 2018-12-14 15:05:26 +00:00
gingerBill 0b6fc19fb0 Allow polymorphic cast on fields with _ #302 2018-12-14 14:57:04 +00:00
gingerBill f2dae7023f Fix polymorphic cast with pointers #303 2018-12-14 14:53:31 +00:00
gingerBill f36775ffd8 Add endian specific integers to ir_debug_encoding_for_basic #307 2018-12-14 14:46:26 +00:00
gingerBill 8702a8a477 Merge pull request #299 from CaptainKraft/master
Add the missing INVALID_HANDLE so that the demo will build and run on Linux
2018-12-14 14:44:34 +00:00
gingerBill 47e31c3de8 Remove return value from append 2018-12-14 14:40:27 +00:00
gingerBill b1d0d82254 Fix #raw_union bug #306 2018-12-14 14:38:38 +00:00
gingerBill 542e524a87 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-12-14 14:35:48 +00:00
gingerBill b54c35639b Fix issue with mixture of named and unnamed parameters for a struct literal 2018-12-14 14:35:23 +00:00
CaptainKraft cfcb0514bf Add the missing INVALID_HANDLE so that the demo will build and run on Linux. 2018-12-09 20:52:03 -06:00
Mikkel Hjortshoej 1a6b7f9945 set time_linux IS_SUPPORTED to true 2018-12-08 21:12:01 +01:00
Mikkel Hjortshoej 03957cee64 Merge branch 'log_pr' 2018-12-08 21:08:27 +01:00
Jeroen van Rijn 1584260886 Add Linux support for core:time
In addition to sleep() and now(), it also defines nanosleep(), boot_time() and seconds_since_boot()
2018-12-08 20:39:33 +01:00
Mikkel Hjortshoej a565d842da Copy instead of loop 2018-12-08 16:12:20 +01:00
Mikkel Hjortshoej 411d1450b0 Add timestamp support using the new core:time 2018-12-08 16:02:33 +01:00
Mikkel Hjortshoej 984fa1c672 remove ident from logger struct 2018-12-08 15:32:53 +01:00
Mikkel Hjortshoej 12c810f85d Add a file-, console- and multi-logger 2018-12-08 15:32:53 +01:00
gingerBill 3bf01c8498 package time (windows only at the moment) 2018-12-08 14:32:00 +00:00
gingerBill d05837ab6d Labels for block and if statements (break only) 2018-12-08 14:12:52 +00:00
gingerBill 4369a1714e Fix automatic subtype casting bug 2018-12-08 13:31:25 +00:00
gingerBill 13f084a219 Fix foreign export #294 2018-12-08 11:45:08 +00:00
gingerBill 4205f0f0b1 Remove dummy testing code 2018-12-08 11:26:13 +00:00
gingerBill bd62bceca6 Fix BigInt normalization issue #293 2018-12-08 11:25:35 +00:00
gingerBill ff6ec860b3 Fix typo 2018-12-08 11:06:32 +00:00
gingerBill 2bf60d3337 Merge pull request #291 from dimenus/master
fixed typo in 'GetMonitorInfoA' & added additional window styles
2018-12-08 11:05:59 +00:00
dimenus f288614eaf style fixes & PR changes 2018-12-06 11:44:59 -06:00
dimenus 9761d54c24 added win32 vk codes 2018-12-05 14:37:47 -06:00
dimenus 3794914478 fixed typo in 'GetMonitorInfoA' & added additional window styles 2018-12-05 11:13:43 -06:00
gingerBill 3e11b4fe1e Reorganize decl attribute code 2018-12-04 21:02:12 +00:00
gingerBill 50c3f4d74e Add package encoding/cel 2018-12-03 20:26:10 +00:00
gingerBill 304c7594cd Ignore ir_emit_byte_swap for constant values 2018-12-02 20:59:08 +00:00
gingerBill d02b050850 Fix typos for OS X debug builds 2018-12-02 19:39:21 +00:00
gingerBill 17b0e3a1a1 Fix bit sets with custom endian underlying type 2018-12-02 19:27:42 +00:00
gingerBill 28583bfff8 Change procedure group syntax from proc[] to proc{}; deprecate proc[] (raises warning currently) 2018-12-02 18:01:03 +00:00
gingerBill b2df48dadb Fix typo for little endian integers 2018-12-02 16:14:57 +00:00
gingerBill 04a853c6fe Fix double declarations of bswaps in LLVM IR 2018-12-02 16:02:00 +00:00
gingerBill 84f0c975b5 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-12-02 15:54:04 +00:00
gingerBill 00161023cd Endian specific integers: e.g. i32 i32le i32be 2018-12-02 15:53:52 +00:00
gingerBill 7f063eb5e7 Add new demonstration for Packages, Bit Sets, and cstring 2018-12-02 13:18:55 +00:00
gingerBill 784c48c9e3 Redefine how union tag size is calculated to match alignment of the union 2018-11-29 23:00:16 +00:00
gingerBill 008d8f25c8 Fix assertion on union assignment in compound literal 2018-11-29 22:50:08 +00:00
gingerBill 7ffcf34dca Modify how custom alignment is printed for LLVM IR 2018-11-29 22:47:08 +00:00
gingerBill f3a4904f21 Hack: union compound literal fix 2018-11-29 22:23:30 +00:00
gingerBill 3aec78b1d4 Lock on possible race condition in parser 2018-11-29 20:27:48 +00:00
gingerBill a3e6e8d304 Allow single field struct #raw_union 2018-11-29 19:46:45 +00:00
gingerBill a747c03f29 Fix #complete switch on pointers to unions #286 2018-11-29 18:36:45 +00:00
gingerBill 2301ae157c Fix recursive loop bug for is_type_polymorphic 2018-11-28 16:47:20 +00:00
gingerBill d3c7d6d485 Fix #284 2018-11-26 10:55:15 +00:00
gingerBill 9b063ad9a3 Fix poly proc determination by cloning the signature node 2018-11-25 17:57:49 +00:00
gingerBill c2f9bf489e Fix debug information for entities without an associated identifier 2018-11-25 17:31:53 +00:00
gingerBill e496b95881 Subset and superset operators for bit_set: < <= > >= 2018-11-25 16:19:17 +00:00
gingerBill 444f4f446a -vet flag to do basic vetting of code 2018-11-25 14:14:58 +00:00
gingerBill 41ad896f3f Update README.md 2018-11-25 11:21:11 +00:00
gingerBill 0a4b88f9a6 Fix Issue with referencing a polymorphic struct in another package referencing itself #283 2018-11-25 10:35:49 +00:00
gingerBill 4c2f03b1f2 Fix compile time bounds check test 2018-11-23 10:38:17 +00:00
gingerBill 52dcaeb1e9 Fix transmute with cstring and integers 2018-11-22 20:59:24 +00:00
123 changed files with 30050 additions and 6243 deletions
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
patreon: gingerbill
+39
View File
@@ -0,0 +1,39 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
## Context
Please provide any relevant information about your setup. This is important in case the issue is not reproducible except for under certain conditions.
* Operating System:
* Odin version/Commit:
## Expected Behavior
Please describe the behavior you are expecting
## Current Behavior
What is the current behavior?
## Failure Information (for bugs)
Please help provide information about the failure if this is a bug. If it is not a bug, please remove the rest of this template.
### Steps to Reproduce
Please provide detailed steps for reproducing the issue.
1. step 1
2. step 2
3. you get it...
### Failure Logs
Please include any relevant log snippets or files here.
+17
View File
@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.
+57
View File
@@ -0,0 +1,57 @@
name: CI
on: [push, pull_request]
jobs:
build_unix:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
steps:
- uses: actions/checkout@v1
- name: (macOS) Download LLVM and setup PATH
if: startsWith(matrix.os, 'macOS')
run: |
brew install llvm
echo ::add-path::/usr/local/opt/llvm/bin
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
- name: (Linux) Download LLVM
if: startsWith(matrix.os, 'ubuntu')
run: |
sudo apt-get install llvm
- name: build odin
run: make release
- name: Odin run
run: ./odin run examples/demo/demo.odin
- name: Odin check
run: ./odin check examples/demo/demo.odin -vet
build_windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
- name: Install cURL
run: choco install curl
- name: Download and unpack LLVM bins
shell: cmd
run: |
cd bin
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
7z x llvm-binaries.zip > nul
- name: build Odin
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
./build_ci.bat
- name: Odin run
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
- name: Odin check
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo/demo.odin -vet
+2 -2
View File
@@ -18,7 +18,7 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
![Cc]ore/[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
@@ -264,10 +264,10 @@ bin/
odin
odin.dSYM
# shared collection
shared/
# temp files
* .ll
*.bc
*.ll
+6 -6
View File
@@ -1,24 +1,24 @@
DISABLED_WARNINGS=-Wno-switch -Wno-writable-strings -Wno-tautological-compare -Wno-macro-redefined #-Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings
LDFLAGS=-pthread -ldl -lm -lstdc++
CFLAGS=-std=c++11
CC=clang
OS=$(shell uname)
ifeq ($(OS), DARWIN)
LDFLAGS=$(LDFLAGS) -liconv
ifeq ($(OS), Darwin)
LDFLAGS:=$(LDFLAGS) -liconv
endif
all: debug demo
demo:
./odin run examples/demo
./odin run examples/demo/demo.odin
debug:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
release:
$(CC) src/main.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
+64
View File
@@ -0,0 +1,64 @@
# The Proposal Process
## Introduction
The Odin project's development process is driven by design and pragmatism. Significant changes to the language, libraries, or tools _must_ be first discussed, and maybe formally documented, before they can be implemented.
This document describes the process for proposing, documenting, and implementing changes to the Odin project.
## The Proposal Process
The proposal process is the process for reviewing a proposal and reaching a decision about whether to accept or decline the proposal.
1. [Ginger Bill](https://github.com/gingerBill) is [BDFL](https://wikipedia.org/wiki/Benevolent_dictator_for_life) and significant changes _must_ be passed by him.
2. The proposal author creates a brief issue describing the proposal.
Note: There is no need for a design document at this point.<br>
Note: A non-proposal issue can be turned into a proposal by simply adding the _proposal_ label.
3. A discussion on the issue tracker will classify the proposal into one of three outcomes:
* Accept proposal
* Decline proposal
* Ask for a design document.
If the proposal is accepted or declined, the process is done. Otherwise the discussion around the process is expected to identify issues that ought to be addressed in a more detailed design.
4. The proposal author writes a design document to work out details of the proposed design and address the concerns raised in the initial discussion.
5. Once comments and revisions on the design document calm, there is a final discussion on the issue, to reach one of two outcomes:
* Accept proposal
* Decline proposal
After the proposal is accepted or declined, implementation of the proprosal proceeds in the same way as any other contribution to the project.
## Design Documents
The design document should follow this template:
```
# Proposal: [Title]
Author(s): [Author Name, Co-Author Name]
Last updated: [Date ISO-8601]
Discussion at https://github.com/odin-lang/Odin/issues/######
## Abstract
## Background
## Proposal
## Rationale
## Compatibility
## Implementation
```
## Help
If you need help with this process, please contact an Odin contributor by posting an issue to the [issue tracker](https://github.com/odin-lang/Odin/issues).
+89 -19
View File
@@ -10,8 +10,12 @@
<a href="https://github.com/odin-lang/odin/releases/latest">
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
</a>
<a href="https://github.com/odin-lang/odin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/odin-lang/odin.svg">
<br>
<a href="https://discord.gg/hnwN2Rj">
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
</a>
<a href="https://github.com/odin-lang/odin/actions">
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg">
</a>
</p>
@@ -22,9 +26,8 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* high performance
* built for modern systems
* joy of programming
* metaprogramming
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
Website: [https://odin-lang.org/](https://odin-lang.org/)
```go
package main
@@ -52,26 +55,76 @@ main :: proc() {
```
## Demonstrations:
* First Talk & Demo
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
- [Demo](https://youtu.be/TMCkT-uASaE?t=1800)
- [Q&A](https://youtu.be/TMCkT-uASaE?t=5749)
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
## Documentation
* [Tutorial](https://odin.handmade.network/wiki/3329-odin_tutorial)
* [Frequently Asked Questions](https://github.com/odin-lang/Odin/wiki/Frequently-Asked-Questions-(FAQ))
#### [Getting Started](https://odin-lang.org/docs/install)
Instructions for downloading and install the Odin compiler and libraries.
### Learning Odin
#### [Overview of Odin](https://odin-lang.org/docs/overview)
An overview of the Odin programming language.
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
Answers to common questions about Odin.
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
A wiki maintained by the Odin community.
#### [Odin Discord](https://discord.gg/sVBPHEv)
Get live support and talk with other odiners on the Odin Discord.
### References
#### [Language Specification](https://odin-lang.org/ref/spec)
The official Odin Language specification.
### Articles
#### [The Odin Blog](https://odin-lang.org/blog)
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
## Setup
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
In addition, the following platform-specific steps are necessary:
- Windows
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
* Open a valid command prompt:
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
- MacOS
* Have LLVM explicitly installed (`brew install llvm`)
* Have XCode installed (version X.X or later, for linking)
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
- GNU/Linux
* Have LLVM installed (opt/llc)
* Have Clang installed (version X.X or later, for linking)
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
## Requirements to build and run
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
- Windows
* x86-64
* MSVC 2010 installed (C++11 support)
@@ -94,3 +147,20 @@ main :: proc() {
* This is still highly in development and the language's design is quite volatile.
* Syntax is not fixed.
## Demonstrations:
* First Talk & Demo
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
- [Demo](https://youtu.be/TMCkT-uASaE?t=1800)
- [Q&A](https://youtu.be/TMCkT-uASaE?t=5749)
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
* [Default and Named Arguments; Explicit Parametric Polymorphism](https://www.youtube.com/watch?v=-XQZE6S6zUU)
* [Loadsachanges](https://www.youtube.com/watch?v=ar0vFMoMtrI)
* [Packages, Bit Sets, cstring](https://youtu.be/b8bJbjiXZrQ)
- [Q&A](https://youtu.be/5jmxyIfyyTk)
+30
View File
@@ -0,0 +1,30 @@
# The Odin Programming Language
## Setup
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
In addition, the following platform-specific steps are necessary:
- Windows
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
* Open a valid command prompt:
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
- MacOS
* Have LLVM explicitly installed (`brew install llvm`)
* Have XCode installed (version X.X or later, for linking)
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
- GNU/Linux
* Have LLVM installed (opt/llc)
* Have Clang installed (version X.X or later, for linking)
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
+1 -3
View File
@@ -4,7 +4,7 @@
set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=1
set release_mode=0
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR-
if %release_mode% EQU 0 ( rem Debug
@@ -39,7 +39,6 @@ set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run examples/demo/demo.odin
@@ -47,4 +46,3 @@ cl %compiler_settings% "src\main.cpp" ^
del *.obj > NUL 2> NUL
:end_of_build
+24
View File
@@ -0,0 +1,24 @@
@echo off
set exe_name=odin.exe
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR- -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
-wd4201 -wd4204 ^
-wd4456 -wd4457 -wd4480 ^
-wd4512
set compiler_includes=
set libs= ^
kernel32.lib
set linker_flags= -incremental:no -opt:ref -subsystem:console -debug
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
set linker_settings=%libs% %linker_flags%
cl %compiler_settings% "src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
+105
View File
@@ -0,0 +1,105 @@
// This is purely for documentation
package builtin
nil :: nil;
false :: 0!==0;
true :: 0==0;
ODIN_OS :: ODIN_OS;
ODIN_ARCH :: ODIN_ARCH;
ODIN_ENDIAN :: ODIN_ENDIAN;
ODIN_VENDOR :: ODIN_VENDOR;
ODIN_VERSION :: ODIN_VERSION;
ODIN_ROOT :: ODIN_ROOT;
ODIN_DEBUG :: ODIN_DEBUG;
byte :: u8; // alias
bool :: bool;
b8 :: b8;
b16 :: b16;
b32 :: b32;
b64 :: b64;
i8 :: i8;
u8 :: u8;
i16 :: i16;
u16 :: u16;
i32 :: i32;
u32 :: u32;
i64 :: i64;
u64 :: u64;
i128 :: i128;
u128 :: u128;
rune :: rune;
f16 :: f16;
f32 :: f32;
f64 :: f64;
complex32 :: complex32;
complex64 :: complex64;
complex128 :: complex128;
quaternion128 :: quaternion128;
quaternion256 :: quaternion256;
int :: int;
uint :: uint;
uintptr :: uintptr;
rawptr :: rawptr;
string :: string;
cstring :: cstring;
any :: any;
typeid :: typeid;
// Endian Specific Types
i16le :: i16le;
u16le :: u16le;
i32le :: i32le;
u32le :: u32le;
i64le :: i64le;
u64le :: u64le;
i128le :: i128le;
u128le :: u128le;
i16be :: i16be;
u16be :: u16be;
i32be :: i32be;
u32be :: u32be;
i64be :: i64be;
u64be :: u64be;
i128be :: i128be;
u128be :: u128be;
// Procedures
len :: proc(array: Array_Type) -> int ---
cap :: proc(array: Array_Type) -> int ---
size_of :: proc($T: typeid) -> int ---
align_of :: proc($T: typeid) -> int ---
offset_of :: proc($T: typeid) -> uintptr ---
type_of :: proc(x: expr) -> type ---
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
typeid_of :: proc($T: typeid) -> typeid ---
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
complex :: proc(real, imag: Float) -> Complex_Type ---
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
real :: proc(value: Complex_Or_Quaternion) -> Float ---
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
jmag :: proc(value: Quaternion) -> Float ---
kmag :: proc(value: Quaternion) -> Float ---
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
min :: proc(values: ..T) -> T ---
max :: proc(values: ..T) -> T ---
abs :: proc(value: T) -> T ---
clamp :: proc(value, minimum, maximum: T) -> T ---
+4 -3
View File
@@ -1,7 +1,6 @@
package c
import b "core:builtin"
import "core:os"
CHAR_BIT :: 8;
@@ -15,8 +14,8 @@ ushort :: b.u16;
int :: b.i32;
uint :: b.u32;
long :: (os.OS == "windows" || size_of(b.rawptr) == 4) ? b.i32 : b.i64;
ulong :: (os.OS == "windows" || size_of(b.rawptr) == 4) ? b.u32 : b.u64;
long :: (ODIN_OS == "windows" || size_of(b.rawptr) == 4) ? b.i32 : b.i64;
ulong :: (ODIN_OS == "windows" || size_of(b.rawptr) == 4) ? b.u32 : b.u64;
longlong :: b.i64;
ulonglong :: b.u64;
@@ -32,3 +31,5 @@ ssize_t :: b.int;
ptrdiff_t :: b.int;
uintptr_t :: b.uintptr;
intptr_t :: b.int;
wchar_t :: (ODIN_OS == "windows") ? b.u16 : b.u32;
+3
View File
@@ -0,0 +1,3 @@
package dynlib
Library :: opaque rawptr;
+24
View File
@@ -0,0 +1,24 @@
package dynlib
import "core:sys/win32"
import "core:strings"
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, context.temp_allocator);
handle := cast(Library)win32.load_library_w(wide_path);
return handle, handle != nil;
}
unload_library :: proc(library: Library) -> bool {
ok := win32.free_library(cast(win32.Hmodule)library);
return bool(ok);
}
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
ptr = win32.get_proc_address(cast(win32.Hmodule)library, c_str);
found = ptr != nil;
return;
}
+93
View File
@@ -0,0 +1,93 @@
package base64
// @note(zh): Encoding utility for Base64
// A secondary param can be used to supply a custom alphabet to
// @link(encode) and a matching decoding table to @link(decode).
// If none is supplied it just uses the standard Base64 alphabet.
// Incase your specific version does not use padding, you may
// truncate it from the encoded output.
ENC_TABLE := [64]byte {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'
};
PADDING :: '=';
DEC_TABLE := [128]int {
-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,
-1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59,
60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, -1, -1, -1, -1, -1
};
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
length := len(data);
if length == 0 do return "";
out_length := ((4 * length / 3) + 3) &~ 3;
out := make([]byte, out_length, allocator);
c0, c1, c2, block: int;
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
c0, c1, c2 = int(data[i]), 0, 0;
if i + 1 < length do c1 = int(data[i + 1]);
if i + 2 < length do c2 = int(data[i + 2]);
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
out[d] = ENC_TBL[block >> 18 & 63];
out[d + 1] = ENC_TBL[block >> 12 & 63];
out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63];
out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63];
}
return string(out);
}
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
length := len(data);
if length == 0 do return []byte{};
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
out_length := ((length * 6) >> 3) - pad_count;
out := make([]byte, out_length, allocator);
c0, c1, c2, c3: int;
b0, b1, b2: int;
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
c0 = DEC_TBL[data[i]];
c1 = DEC_TBL[data[i + 1]];
c2 = DEC_TBL[data[i + 2]];
c3 = DEC_TBL[data[i + 3]];
b0 = (c0 << 2) | (c1 >> 4);
b1 = (c1 << 4) | (c2 >> 2);
b2 = (c2 << 6) | c3;
out[j] = byte(b0);
out[j + 1] = byte(b1);
out[j + 2] = byte(b2);
}
return out;
}
+840
View File
@@ -0,0 +1,840 @@
package cel;
import "core:fmt"
import "core:strconv"
import "core:unicode/utf8"
import "core:strings"
Array :: []Value;
Dict :: map[string]Value;
Nil_Value :: struct{};
Value :: union {
Nil_Value,
bool, i64, f64, string,
Array, Dict,
}
Parser :: struct {
tokens: [dynamic]Token,
prev_token: Token,
curr_token: Token,
curr_token_index: int,
allocated_strings: [dynamic]string,
error_count: int,
root: Dict,
dict_stack: [dynamic]^Dict, // NOTE: Pointers may be stored on the stack
}
print_value :: proc(value: Value, pretty := true, indent := 0) {
print_indent :: proc(indent: int) {
for _ in 0..<indent do fmt.print("\t");
}
switch v in value {
case bool: fmt.print(v);
case i64: fmt.print(v);
case f64: fmt.print(v);
case string: fmt.print(v);
case Array:
fmt.print("[");
if pretty do fmt.println();
for e, i in v {
if pretty {
print_indent(indent+1);
print_value(e, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 do fmt.print(", ");
print_value(e);
}
}
if pretty do print_indent(indent);
fmt.print("]");
case Dict:
fmt.print("{");
if pretty do fmt.println();
i := 0;
for name, val in v {
if pretty {
print_indent(indent+1);
fmt.printf("%s = ", name);
print_value(val, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 do fmt.print(", ");
fmt.printf("%s = ", name);
print_value(val, pretty, indent+1);
i += 1;
}
}
if pretty do print_indent(indent);
fmt.print("}");
case:
fmt.print("nil");
case Nil_Value:
fmt.print("nil");
}
}
print :: proc(p: ^Parser, pretty := false) {
for name, val in p.root {
fmt.printf("%s = ", name);
print_value(val, pretty);
fmt.println(";");
}
}
create_from_string :: proc(src: string) -> (^Parser, bool) {
return init(cast([]byte)src);
}
init :: proc(src: []byte) -> (^Parser, bool) {
t: Tokenizer;
tokenizer_init(&t, src);
return create_from_tokenizer(&t);
}
create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p := new(Parser);
for {
tok := scan(t);
if tok.kind == Kind.Illegal {
return p, false;
}
append(&p.tokens, tok);
if tok.kind == Kind.EOF {
break;
}
}
if t.error_count > 0 {
return p, false;
}
if len(p.tokens) == 0 {
tok := Token{kind = Kind.EOF};
tok.line, tok.column = 1, 1;
append(&p.tokens, tok);
return p, true;
}
p.curr_token_index = 0;
p.prev_token = p.tokens[p.curr_token_index];
p.curr_token = p.tokens[p.curr_token_index];
p.root = Dict{};
p.dict_stack = make([dynamic]^Dict, 0, 4);
append(&p.dict_stack, &p.root);
for p.curr_token.kind != Kind.EOF &&
p.curr_token.kind != Kind.Illegal &&
p.curr_token_index < len(p.tokens) {
if !parse_assignment(p) {
break;
}
}
return p, true;
}
destroy :: proc(p: ^Parser) {
destroy_value :: proc(value: Value) {
switch v in value {
case Array:
for elem in v do destroy_value(elem);
delete(v);
case Dict:
for _, dv in v do destroy_value(dv);
delete(v);
}
}
delete(p.tokens);
for s in p.allocated_strings do delete(s);
delete(p.allocated_strings);
delete(p.dict_stack);
destroy_value(p.root);
free(p);
}
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintln();
p.error_count += 1;
}
next_token :: proc(p: ^Parser) -> Token {
p.prev_token = p.curr_token;
prev := p.prev_token;
if p.curr_token_index+1 < len(p.tokens) {
p.curr_token_index += 1;
p.curr_token = p.tokens[p.curr_token_index];
return prev;
}
p.curr_token_index = len(p.tokens);
p.curr_token = p.tokens[p.curr_token_index-1];
error(p, prev.pos, "Token is EOF");
return prev;
}
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
hex_to_int :: proc(c: byte) -> int {
switch c {
case '0'..'9': return int(c-'0');
case 'a'..'f': return int(c-'a')+10;
case 'A'..'F': return int(c-'A')+10;
}
return -1;
}
w: int;
if str[0] == quote && quote == '"' {
return;
} else if str[0] >= 0x80 {
r, w = utf8.decode_rune_in_string(str);
return r, true, str[w:], true;
} else if str[0] != '\\' {
return rune(str[0]), false, str[1:], true;
}
if len(str) <= 1 {
return;
}
s := str;
c := s[1];
s = s[2:];
switch c {
case:
return;
case 'a': r = '\a';
case 'b': r = '\b';
case 'f': r = '\f';
case 'n': r = '\n';
case 'r': r = '\r';
case 't': r = '\t';
case 'v': r = '\v';
case '\\': r = '\\';
case '"': r = '"';
case '\'': r = '\'';
case '0'..'7':
v := int(c-'0');
if len(s) < 2 {
return;
}
for i in 0..<len(s) {
d := int(s[i]-'0');
if d < 0 || d > 7 {
return;
}
v = (v<<3) | d;
}
s = s[2:];
if v > 0xff {
return;
}
r = rune(v);
case 'x', 'u', 'U':
count: int;
switch c {
case 'x': count = 2;
case 'u': count = 4;
case 'U': count = 8;
}
if len(s) < count {
return;
}
for i in 0..<count {
d := hex_to_int(s[i]);
if d < 0 {
return;
}
r = (r<<4) | rune(d);
}
s = s[count:];
if c == 'x' {
break;
}
if r > utf8.MAX_RUNE {
return;
}
multiple_bytes = true;
}
success = true;
tail_string = s;
return;
}
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
if t.kind != Kind.String {
return t.lit, true;
}
s := t.lit;
quote := '"';
if s == `""` {
return "", true;
}
if strings.contains_rune(s, '\n') >= 0 {
return s, false;
}
if strings.contains_rune(s, '\\') < 0 && strings.contains_rune(s, quote) < 0 {
if quote == '"' {
return s, true;
}
}
buf_len := 3*len(s) / 2;
buf := make([]byte, buf_len);
offset := 0;
for len(s) > 0 {
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote));
if !ok {
delete(buf);
return s, false;
}
s = tail_string;
if r < 0x80 || !multiple_bytes {
buf[offset] = byte(r);
offset += 1;
} else {
b, w := utf8.encode_rune(r);
copy(buf[offset:], b[:w]);
offset += w;
}
}
new_string := string(buf[:offset]);
append(&p.allocated_strings, new_string);
return new_string, true;
}
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
if p.curr_token.kind == kind {
next_token(p);
return true;
}
return false;
}
expect_token :: proc(p: ^Parser, kind: Kind) -> Token {
prev := p.curr_token;
if prev.kind != kind {
got := prev.lit;
if got == "\n" do got = ";";
error(p, prev.pos, "Expected %s, got %s", kind_to_string[kind], got);
}
next_token(p);
return prev;
}
expect_operator :: proc(p: ^Parser) -> Token {
prev := p.curr_token;
if !is_operator(prev.kind) {
error(p, prev.pos, "Expected an operator, got %s", prev.lit);
}
next_token(p);
return prev;
}
fix_advance :: proc(p: ^Parser) {
for {
switch t := p.curr_token; t.kind {
case Kind.EOF, Kind.Semicolon:
return;
}
next_token(p);
}
}
copy_value :: proc(value: Value) -> Value {
switch v in value {
case Array:
a := make(Array, len(v));
for elem, idx in v {
a[idx] = copy_value(elem);
}
return a;
case Dict:
d := make(Dict, cap(v));
for key, val in v {
d[key] = copy_value(val);
}
return d;
}
return value;
}
lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
for i := len(p.dict_stack)-1; i >= 0; i -= 1 {
d := p.dict_stack[i];
if val, ok := d[name]; ok {
return copy_value(val), true;
}
}
return nil, false;
}
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
tok := p.curr_token;
switch p.curr_token.kind {
case Kind.Ident:
next_token(p);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return v, tok.pos;
case Kind.True:
next_token(p);
return true, tok.pos;
case Kind.False:
next_token(p);
return false, tok.pos;
case Kind.Nil:
next_token(p);
return Nil_Value{}, tok.pos;
case Kind.Integer:
next_token(p);
return strconv.parse_i64(tok.lit), tok.pos;
case Kind.Float:
next_token(p);
return strconv.parse_f64(tok.lit), tok.pos;
case Kind.String:
next_token(p);
str, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
return string(str), tok.pos;
case Kind.Open_Paren:
expect_token(p, Kind.Open_Paren);
expr, _ := parse_expr(p);
expect_token(p, Kind.Close_Paren);
return expr, tok.pos;
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
elems := make([dynamic]Value, 0, 4);
for p.curr_token.kind != Kind.Close_Bracket &&
p.curr_token.kind != Kind.EOF {
elem, _ := parse_expr(p);
append(&elems, elem);
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
break;
}
}
expect_token(p, Kind.Close_Bracket);
return Array(elems[:]), tok.pos;
case Kind.Open_Brace:
expect_token(p, Kind.Open_Brace);
dict := Dict{};
append(&p.dict_stack, &dict);
defer pop(&p.dict_stack);
for p.curr_token.kind != Kind.Close_Brace &&
p.curr_token.kind != Kind.EOF {
name_tok := p.curr_token;
if !allow_token(p, Kind.Ident) && !allow_token(p, Kind.String) {
name_tok = expect_token(p, Kind.Ident);
}
name, ok := unquote_string(p, name_tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expect_token(p, Kind.Assign);
elem, _ := parse_expr(p);
if _, ok2 := dict[name]; ok2 {
error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
} else {
dict[name] = elem;
}
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
break;
}
}
expect_token(p, Kind.Close_Brace);
return dict, tok.pos;
}
return nil, tok.pos;
}
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
loop := true;
for operand := operand; loop; {
switch p.curr_token.kind {
case Kind.Period:
next_token(p);
tok := next_token(p);
switch tok.kind {
case Kind.Ident:
d, ok := operand.(Dict);
if !ok || d == nil {
error(p, tok.pos, "Expected a dictionary");
operand = nil;
continue;
}
name, usok := unquote_string(p, tok);
if !usok do error(p, tok.pos, "Unable to unquote string");
val, found := d[name];
if !found {
error(p, tok.pos, "Field %s not found in dictionary", name);
operand = nil;
continue;
}
operand = val;
case:
error(p, tok.pos, "Expected a selector, got %s", tok.kind);
operand = nil;
}
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
index, index_pos := parse_expr(p);
expect_token(p, Kind.Close_Bracket);
switch a in operand {
case Array:
i, ok := index.(i64);
if !ok {
error(p, index_pos, "Index must be an integer for an array");
operand = nil;
continue;
}
if 0 <= i && i < i64(len(a)) {
operand = a[i];
} else {
error(p, index_pos, "Index %d out of bounds range 0..%d", i, len(a));
operand = nil;
continue;
}
case Dict:
key, ok := index.(string);
if !ok {
error(p, index_pos, "Index must be a string for a dictionary");
operand = nil;
continue;
}
val, found := a[key];
if found {
operand = val;
} else {
error(p, index_pos, "`%s` was not found in the dictionary", key);
operand = nil;
continue;
}
case:
error(p, index_pos, "Indexing is only allowed on an array or dictionary");
}
case:
loop = false;
}
}
return operand, pos;
}
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
op := p.curr_token;
switch p.curr_token.kind {
case Kind.At:
next_token(p);
tok := expect_token(p, Kind.String);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return parse_atom_expr(p, v, tok.pos);
case Kind.Add, Kind.Sub:
next_token(p);
// TODO(bill): Calcuate values as you go!
expr, pos := parse_unary_expr(p);
switch e in expr {
case i64: if op.kind == Kind.Sub do return -e, pos;
case f64: if op.kind == Kind.Sub do return -e, pos;
case:
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
return nil, op.pos;
}
return expr, op.pos;
case Kind.Not:
next_token(p);
expr, _ := parse_unary_expr(p);
if v, ok := expr.(bool); ok {
return !v, op.pos;
}
error(p, op.pos, "Unary operator %s can only be used on booleans", op.lit);
return nil, op.pos;
}
return parse_atom_expr(p, parse_operand(p));
}
value_order :: proc(v: Value) -> int {
switch _ in v {
case bool, string:
return 1;
case i64:
return 2;
case f64:
return 3;
}
return 0;
}
match_values :: proc(left, right: ^Value) -> bool {
if value_order(right^) < value_order(left^) {
return match_values(right, left);
}
switch x in left^ {
case:
right^ = left^;
case bool, string:
return true;
case i64:
switch y in right^ {
case i64:
return true;
case f64:
left^ = f64(x);
return true;
}
case f64:
switch y in right {
case f64:
return true;
}
}
return false;
}
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
// TODO(bill): Calculate value as you go!
x, y := a, b;
match_values(&x, &y);
switch a in x {
case: return x, true;
case bool:
b, ok := y.(bool);
if !ok do return nil, false;
switch op {
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.And: return a && b, true;
case Kind.Or: return a || b, true;
}
case i64:
b, ok := y.(i64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Rem: return a % b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
}
case f64:
b, ok := y.(f64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
}
case string:
b, ok := y.(string);
if !ok do return nil, false;
switch op {
case Kind.Add:
n := len(a) + len(b);
data := make([]byte, n);
copy(data[:], cast([]byte)a);
copy(data[len(a):], cast([]byte)b);
s := string(data);
append(&p.allocated_strings, s);
return s, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
}
}
return nil, false;
}
parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
expr, pos := parse_unary_expr(p);
for prec := precedence(p.curr_token.kind); prec >= prec_in; prec -= 1 {
for {
op := p.curr_token;
op_prec := precedence(op.kind);
if op_prec != prec {
break;
}
expect_operator(p);
if op.kind == Kind.Question {
cond := expr;
x, _ := parse_expr(p);
expect_token(p, Kind.Colon);
y, _ := parse_expr(p);
if t, ok := cond.(bool); ok {
expr = t ? x : y;
} else {
error(p, pos, "Condition must be a boolean");
}
} else {
right, right_pos := parse_binary_expr(p, prec+1);
if right == nil {
error(p, right_pos, "Expected expression on the right-hand side of the binary operator %s", op.lit);
}
left := expr;
ok: bool;
expr, ok = calculate_binary_value(p, op.kind, left, right);
if !ok {
error(p, pos, "Invalid binary operation");
}
}
}
}
return expr, pos;
}
parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
return parse_binary_expr(p, 1);
}
expect_semicolon :: proc(p: ^Parser) {
kind := p.curr_token.kind;
switch kind {
case Kind.Comma:
error(p, p.curr_token.pos, "Expected ';', got ','");
next_token(p);
case Kind.Semicolon:
next_token(p);
case Kind.EOF:
// okay
case:
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
fix_advance(p);
}
}
parse_assignment :: proc(p: ^Parser) -> bool {
top_dict :: proc(p: ^Parser) -> ^Dict {
assert(len(p.dict_stack) > 0);
return p.dict_stack[len(p.dict_stack)-1];
}
if p.curr_token.kind == Kind.Semicolon {
next_token(p);
return true;
}
if p.curr_token.kind == Kind.EOF {
return false;
}
tok := p.curr_token;
if allow_token(p, Kind.Ident) || allow_token(p, Kind.String) {
expect_token(p, Kind.Assign);
name, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expr, _ := parse_expr(p);
d := top_dict(p);
if _, ok2 := d[name]; ok2 {
error(p, tok.pos, "Previous declaration of %s", name);
} else {
d[name] = expr;
}
expect_semicolon(p);
return true;
}
error(p, tok.pos, "Expected an assignment, got %s", kind_to_string[tok.kind]);
fix_advance(p);
return false;
}
+51
View File
@@ -0,0 +1,51 @@
/*
package cel
sample := `
x = 123;
y = 321.456;
z = x * (y - 1) / 2;
w = "foo" + "bar";
# This is a comment
asd = "Semicolons are optional"
a = {id = {b = 123}} # Dict
b = a.id.b
f = [1, 4, 9] # Array
g = f[2]
h = x < y and w == "foobar"
i = h ? 123 : "google"
j = nil
"127.0.0.1" = "value" # Keys can be strings
"foo" = {
"bar" = {
"baz" = 123, # optional commas if newline is present
"zab" = 456,
"abz" = 789,
},
};
bar = @"foo"["bar"].baz
`;
main :: proc() {
p, ok := create_from_string(sample);
if !ok {
return;
}
defer destroy(p);
if p.error_count == 0 {
print(p);
}
}
*/
package cel
+523
View File
@@ -0,0 +1,523 @@
package cel
import "core:fmt"
import "core:unicode/utf8"
using Kind :: enum {
Illegal,
EOF,
Comment,
_literal_start,
Ident,
Integer,
Float,
Char,
String,
_literal_end,
_keyword_start,
True, // true
False, // false
Nil, // nil
_keyword_end,
_operator_start,
Question, // ?
And, // and
Or, // or
Add, // +
Sub, // -
Mul, // *
Quo, // /
Rem, // %
Not, // !
Eq, // ==
NotEq, // !=
Lt, // <
Gt, // >
LtEq, // <=
GtEq, // >=
At, // @
_operator_end,
_punc_start,
Assign, // =
Open_Paren, // (
Close_Paren, // )
Open_Bracket, // [
Close_Bracket, // ]
Open_Brace, // {
Close_Brace, // }
Colon, // :
Semicolon, // ;
Comma, // ,
Period, // .
_punc_end,
}
Pos :: struct {
file: string,
line: int,
column: int,
}
Token :: struct {
kind: Kind,
using pos: Pos,
lit: string,
}
Tokenizer :: struct {
src: []byte,
file: string, // May not be used
curr_rune: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
insert_semi: bool,
error_count: int,
}
keywords := map[string]Kind{
"true" = True,
"false" = False,
"nil" = Nil,
"and" = And,
"or" = Or,
};
kind_to_string := [len(Kind)]string{
"illegal",
"EOF",
"comment",
"",
"identifier",
"integer",
"float",
"character",
"string",
"",
"",
"true", "false", "nil",
"",
"",
"?", "and", "or",
"+", "-", "*", "/", "%",
"!",
"==", "!=", "<", ">", "<=", ">=",
"@",
"",
"",
"=",
"(", ")",
"[", "]",
"{", "}",
":", ";", ",", ".",
"",
};
precedence :: proc(op: Kind) -> int {
switch op {
case Question:
return 1;
case Or:
return 2;
case And:
return 3;
case Eq, NotEq, Lt, Gt, LtEq, GtEq:
return 4;
case Add, Sub:
return 5;
case Mul, Quo, Rem:
return 6;
}
return 0;
}
token_lookup :: proc(ident: string) -> Kind {
if tok, is_keyword := keywords[ident]; is_keyword {
return tok;
}
return Ident;
}
is_literal :: proc(tok: Kind) -> bool do return _literal_start < tok && tok < _literal_end;
is_operator :: proc(tok: Kind) -> bool do return _operator_start < tok && tok < _operator_end;
is_keyword :: proc(tok: Kind) -> bool do return _keyword_start < tok && tok < _keyword_end;
tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
t.src = src;
t.file = file;
t.curr_rune = ' ';
t.offset = 0;
t.read_offset = 0;
t.line_offset = 0;
t.line_count = 1;
advance_to_next_rune(t);
if t.curr_rune == utf8.RUNE_BOM {
advance_to_next_rune(t);
}
}
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
fmt.eprintf(msg, ..args);
fmt.eprintln();
t.error_count += 1;
}
advance_to_next_rune :: proc(t: ^Tokenizer) {
if t.read_offset < len(t.src) {
t.offset = t.read_offset;
if t.curr_rune == '\n' {
t.line_offset = t.offset;
t.line_count += 1;
}
r, w := rune(t.src[t.read_offset]), 1;
switch {
case r == 0:
token_error(t, "Illegal character NUL");
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(t.src[t.read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 {
token_error(t, "Illegal utf-8 encoding");
} else if r == utf8.RUNE_BOM && t.offset > 0 {
token_error(t, "Illegal byte order mark");
}
}
t.read_offset += w;
t.curr_rune = r;
} else {
t.offset = len(t.src);
if t.curr_rune == '\n' {
t.line_offset = t.offset;
t.line_count += 1;
}
t.curr_rune = utf8.RUNE_EOF;
}
}
get_pos :: proc(t: ^Tokenizer) -> Pos {
return Pos {
file = t.file,
line = t.line_count,
column = t.offset - t.line_offset + 1,
};
}
is_letter :: proc(r: rune) -> bool {
switch r {
case 'a'..'z', 'A'..'Z', '_':
return true;
}
return false;
}
is_digit :: proc(r: rune) -> bool {
switch r {
case '0'..'9':
return true;
}
return false;
}
skip_whitespace :: proc(t: ^Tokenizer) {
loop: for {
switch t.curr_rune {
case '\n':
if t.insert_semi {
break loop;
}
fallthrough;
case ' ', '\t', '\r', '\v', '\f':
advance_to_next_rune(t);
case:
break loop;
}
}
}
scan_identifier :: proc(t: ^Tokenizer) -> string {
offset := t.offset;
for is_letter(t.curr_rune) || is_digit(t.curr_rune) {
advance_to_next_rune(t);
}
return string(t.src[offset : t.offset]);
}
digit_value :: proc(r: rune) -> int {
switch r {
case '0'..'9': return int(r - '0');
case 'a'..'f': return int(r - 'a' + 10);
case 'A'..'F': return int(r - 'A' + 10);
}
return 16;
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
for digit_value(t.curr_rune) < base || t.curr_rune == '_' {
advance_to_next_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == 'e' || t.curr_rune == 'E' {
kind = Float;
advance_to_next_rune(t);
if t.curr_rune == '-' || t.curr_rune == '+' {
advance_to_next_rune(t);
}
if digit_value(t.curr_rune) < 10 {
scan_mantissa(t, 10);
} else {
token_error(t, "Illegal floating point exponent");
}
}
text = string(t.src[offset : t.offset]);
return;
}
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
kind = tok;
if t.curr_rune == '.' {
kind = Float;
advance_to_next_rune(t);
scan_mantissa(t, 10);
}
return scan_exponent(t, kind, offset);
}
offset := t.offset;
tok := Integer;
if seen_decimal_point {
offset -= 1;
tok = Float;
scan_mantissa(t, 10);
return scan_exponent(t, tok, offset);
}
if t.curr_rune == '0' {
offset = t.offset;
advance_to_next_rune(t);
switch t.curr_rune {
case 'b', 'B':
advance_to_next_rune(t);
scan_mantissa(t, 2);
if t.offset - offset <= 2 {
token_error(t, "Illegal binary number");
}
case 'o', 'O':
advance_to_next_rune(t);
scan_mantissa(t, 8);
if t.offset - offset <= 2 {
token_error(t, "Illegal octal number");
}
case 'x', 'X':
advance_to_next_rune(t);
scan_mantissa(t, 16);
if t.offset - offset <= 2 {
token_error(t, "Illegal hexadecimal number");
}
case:
scan_mantissa(t, 10);
switch t.curr_rune {
case '.', 'e', 'E':
return scan_fraction(t, tok, offset);
}
}
return tok, string(t.src[offset:t.offset]);
}
scan_mantissa(t, 10);
return scan_fraction(t, tok, offset);
}
scan :: proc(t: ^Tokenizer) -> Token {
skip_whitespace(t);
offset := t.offset;
tok: Kind;
pos := get_pos(t);
lit: string;
insert_semi := false;
switch r := t.curr_rune; {
case is_letter(r):
insert_semi = true;
lit = scan_identifier(t);
tok = Ident;
if len(lit) > 1 {
tok = token_lookup(lit);
}
case '0' <= r && r <= '9':
insert_semi = true;
tok, lit = scan_number(t, false);
case:
advance_to_next_rune(t);
switch r {
case -1:
if t.insert_semi {
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
}
return Token{EOF, pos, "\n"};
case '\n':
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
case '"':
insert_semi = true;
quote := r;
tok = String;
for {
this_r := t.curr_rune;
if this_r == '\n' || r < 0 {
token_error(t, "String literal not terminated");
break;
}
advance_to_next_rune(t);
if this_r == quote {
break;
}
// TODO(bill); Handle properly
if this_r == '\\' && t.curr_rune == quote {
advance_to_next_rune(t);
}
}
lit = string(t.src[offset+1:t.offset-1]);
case '#':
for t.curr_rune != '\n' && t.curr_rune >= 0 {
advance_to_next_rune(t);
}
if t.insert_semi {
t.insert_semi = false;
return Token{Semicolon, pos, "\n"};
}
// Recursive!
return scan(t);
case '?': tok = Question;
case ':': tok = Colon;
case '@': tok = At;
case ';':
tok = Semicolon;
lit = ";";
case ',': tok = Comma;
case '(':
tok = Open_Paren;
case ')':
insert_semi = true;
tok = Close_Paren;
case '[':
tok = Open_Bracket;
case ']':
insert_semi = true;
tok = Close_Bracket;
case '{':
tok = Open_Brace;
case '}':
insert_semi = true;
tok = Close_Brace;
case '+': tok = Add;
case '-': tok = Sub;
case '*': tok = Mul;
case '/': tok = Quo;
case '%': tok = Rem;
case '!':
tok = Not;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = NotEq;
}
case '=':
tok = Assign;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = Eq;
}
case '<':
tok = Lt;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = LtEq;
}
case '>':
tok = Gt;
if t.curr_rune == '=' {
advance_to_next_rune(t);
tok = GtEq;
}
case '.':
if '0' <= t.curr_rune && t.curr_rune <= '9' {
insert_semi = true;
tok, lit = scan_number(t, true);
} else {
tok = Period;
}
case:
if r != utf8.RUNE_BOM {
token_error(t, "Illegal character '%r'", r);
}
insert_semi = t.insert_semi;
tok = Illegal;
}
}
t.insert_semi = insert_semi;
if lit == "" {
lit = string(t.src[offset:t.offset]);
}
return Token{tok, pos, lit};
}
+330
View File
@@ -0,0 +1,330 @@
package json
import "core:mem"
import "core:math/bits"
import "core:runtime"
import "core:strconv"
import "core:strings"
import "core:reflect"
Marshal_Error :: enum {
None,
Unsupported_Type,
}
marshal :: proc(v: any, allocator := context.allocator) -> ([]byte, Marshal_Error) {
b := strings.make_builder(allocator);
err := marshal_arg(&b, v);
if err != Marshal_Error.None {
strings.destroy_builder(&b);
return nil, err;
}
if len(b.buf) == 0 {
strings.destroy_builder(&b);
return nil, err;
}
return b.buf[:], err;
}
marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
using strings;
using runtime;
if v == nil {
write_string(b, "null");
return Marshal_Error.None;
}
ti := type_info_base(type_info_of(v.id));
a := any{v.data, ti.id};
switch info in ti.variant {
case Type_Info_Named:
panic("Unreachable");
case Type_Info_Integer:
buf: [21]byte;
u: u64;
switch i in a {
case i8: u = u64(i);
case i16: u = u64(i);
case i32: u = u64(i);
case i64: u = u64(i);
case int: u = u64(i);
case u8: u = u64(i);
case u16: u = u64(i);
case u32: u = u64(i);
case u64: u = u64(i);
case uint: u = u64(i);
case uintptr: u = u64(i);
case i16le: u = u64(i);
case i32le: u = u64(i);
case i64le: u = u64(i);
case u16le: u = u64(i);
case u32le: u = u64(i);
case u64le: u = u64(i);
case i16be: u = u64(i);
case i32be: u = u64(i);
case i64be: u = u64(i);
case u16be: u = u64(i);
case u32be: u = u64(i);
case u64be: u = u64(i);
}
s := strconv.append_bits(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil);
write_string(b, s);
case Type_Info_Rune:
r := a.(rune);
write_byte(b, '"');
write_escaped_rune(b, r, '"', true);
write_byte(b, '"');
case Type_Info_Float:
val: f64;
switch f in a {
case f32: val = f64(f);
case f64: val = f64(f);
}
buf: [386]byte;
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
str = string(buf[:len(str)+1]);
if str[1] == '+' || str[1] == '-' {
str = str[1:];
} else {
str[0] = '+';
}
if str[0] == '+' {
str = str[1:];
}
write_string(b, str);
case Type_Info_Complex:
return Marshal_Error.Unsupported_Type;
case Type_Info_String:
switch s in a {
case string: write_quoted_string(b, s);
case cstring: write_quoted_string(b, string(s));
}
case Type_Info_Boolean:
val: bool;
switch b in a {
case bool: val = bool(b);
case b8: val = bool(b);
case b16: val = bool(b);
case b32: val = bool(b);
case b64: val = bool(b);
}
write_string(b, val ? "true" : "false");
case Type_Info_Any:
return Marshal_Error.Unsupported_Type;
case Type_Info_Type_Id:
return Marshal_Error.Unsupported_Type;
case Type_Info_Pointer:
return Marshal_Error.Unsupported_Type;
case Type_Info_Procedure:
return Marshal_Error.Unsupported_Type;
case Type_Info_Tuple:
return Marshal_Error.Unsupported_Type;
case Type_Info_Array:
write_byte(b, '[');
for i in 0..<info.count {
if i > 0 do write_string(b, ", ");
data := uintptr(v.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
case Type_Info_Dynamic_Array:
write_byte(b, '[');
array := cast(^mem.Raw_Dynamic_Array)v.data;
for i in 0..<array.len {
if i > 0 do write_string(b, ", ");
data := uintptr(array.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
case Type_Info_Slice:
write_byte(b, '[');
slice := cast(^mem.Raw_Slice)v.data;
for i in 0..<slice.len {
if i > 0 do write_string(b, ", ");
data := uintptr(slice.data) + uintptr(i*info.elem_size);
marshal_arg(b, any{rawptr(data), info.elem.id});
}
write_byte(b, ']');
case Type_Info_Map:
m := (^mem.Raw_Map)(v.data);
write_byte(b, '{');
if m != nil {
if info.generated_struct == nil {
return Marshal_Error.Unsupported_Type;
}
entries := &m.entries;
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct);
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array);
entry_type := ed.elem.variant.(Type_Info_Struct);
entry_size := ed.elem_size;
for i in 0..<entries.len {
if i > 0 do write_string(b, ", ");
data := uintptr(entries.data) + uintptr(i*entry_size);
header := cast(^Map_Entry_Header)data;
if reflect.is_string(info.key) {
marshal_arg(b, header.key.str);
} else {
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
}
write_string(b, ": ");
value := data + entry_type.offsets[2];
marshal_arg(b, any{rawptr(value), info.value.id});
}
}
write_byte(b, '}');
case Type_Info_Struct:
write_byte(b, '{');
for name, i in info.names {
if i > 0 do write_string(b, ", ");
write_quoted_string(b, name);
write_string(b, ": ");
id := info.types[i].id;
data := rawptr(uintptr(v.data) + info.offsets[i]);
marshal_arg(b, any{data, id});
}
write_byte(b, '}');
case Type_Info_Union:
tag_ptr := uintptr(v.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
tag: i64 = -1;
switch i in tag_any {
case u8: tag = i64(i);
case i8: tag = i64(i);
case u16: tag = i64(i);
case i16: tag = i64(i);
case u32: tag = i64(i);
case i32: tag = i64(i);
case u64: tag = i64(i);
case i64: tag = i64(i);
case: panic("Invalid union tag type");
}
if v.data == nil || tag == 0 {
write_string(b, "null");
} else {
id := info.variants[tag-1].id;
marshal_arg(b, any{v.data, id});
}
case Type_Info_Enum:
return marshal_arg(b, any{v.data, info.base.id});
case Type_Info_Bit_Field:
data: u64 = 0;
switch ti.size {
case 1: data = cast(u64) (^u8)(v.data)^;
case 2: data = cast(u64)(^u16)(v.data)^;
case 4: data = cast(u64)(^u32)(v.data)^;
case 8: data = cast(u64)(^u64)(v.data)^;
}
write_byte(b, '{');
for name, i in info.names {
if i > 0 do write_string(b, ", ");
bits := u64(info.bits[i]);
offset := u64(info.offsets[i]);
marshal_arg(b, name);
write_string(b, ": ");
n := 8*u64(size_of(u64));
sa := n - bits;
u := data>>offset;
u <<= sa;
u >>= sa;
write_u64(b, u, 10);
}
write_byte(b, '}');
case Type_Info_Bit_Set:
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
return false;
}
t := runtime.type_info_base(ti);
switch info in t.variant {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false;
case .Little: return ODIN_ENDIAN != "little";
case .Big: return ODIN_ENDIAN != "big";
}
}
return false;
}
bit_data: u64;
bit_size := u64(8*ti.size);
do_byte_swap := is_bit_set_different_endian_to_platform(info.underlying);
switch bit_size {
case 0: bit_data = 0;
case 8:
x := (^u8)(v.data)^;
bit_data = u64(x);
case 16:
x := (^u16)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
bit_data = u64(x);
case 32:
x := (^u32)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
bit_data = u64(x);
case 64:
x := (^u64)(v.data)^;
if do_byte_swap do x = bits.byte_swap(x);
bit_data = u64(x);
case: panic("unknown bit_size size");
}
write_u64(b, bit_data);
return Marshal_Error.Unsupported_Type;
case Type_Info_Opaque:
return Marshal_Error.Unsupported_Type;
}
return Marshal_Error.None;
}
+455
View File
@@ -0,0 +1,455 @@
package json
import "core:mem"
import "core:unicode/utf8"
import "core:strconv"
Parser :: struct {
tok: Tokenizer,
prev_token: Token,
curr_token: Token,
spec: Specification,
allocator: mem.Allocator,
}
make_parser :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> Parser {
p: Parser;
p.tok = make_tokenizer(data, spec);
p.spec = spec;
p.allocator = allocator;
assert(p.allocator.procedure != nil);
advance_token(&p);
return p;
}
parse :: proc(data: []byte, spec := Specification.JSON, allocator := context.allocator) -> (Value, Error) {
context.allocator = allocator;
p := make_parser(data, spec, allocator);
if p.spec == Specification.JSON5 {
return parse_value(&p);
}
return parse_object(&p);
}
token_end_pos :: proc(tok: Token) -> Pos {
end := tok.pos;
end.offset += len(tok.text);
return end;
}
advance_token :: proc(p: ^Parser) -> (Token, Error) {
err: Error;
p.prev_token = p.curr_token;
p.curr_token, err = get_token(&p.tok);
return p.prev_token, err;
}
allow_token :: proc(p: ^Parser, kind: Kind) -> bool {
if p.curr_token.kind == kind {
advance_token(p);
return true;
}
return false;
}
expect_token :: proc(p: ^Parser, kind: Kind) -> Error {
prev := p.curr_token;
advance_token(p);
if prev.kind == kind {
return Error.None;
}
return Error.Unexpected_Token;
}
parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
token := p.curr_token;
switch token.kind {
case Kind.Null:
value.value = Null{};
advance_token(p);
return;
case Kind.False:
value.value = Boolean(false);
advance_token(p);
return;
case Kind.True:
value.value = Boolean(true);
advance_token(p);
return;
case Kind.Integer:
value.value = Integer(strconv.parse_i64(token.text));
advance_token(p);
return;
case Kind.Float:
value.value = Float(strconv.parse_f64(token.text));
advance_token(p);
return;
case Kind.String:
value.value = String(unquote_string(token, p.spec, p.allocator));
advance_token(p);
return;
case Kind.Open_Brace:
return parse_object(p);
case Kind.Open_Bracket:
return parse_array(p);
case:
if p.spec == Specification.JSON5 {
switch token.kind {
case Kind.Infinity:
inf: u64 = 0x7ff0000000000000;
if token.text[0] == '-' {
inf = 0xfff0000000000000;
}
value.value = transmute(f64)inf;
advance_token(p);
return;
case Kind.NaN:
nan: u64 = 0x7ff7ffffffffffff;
if token.text[0] == '-' {
nan = 0xfff7ffffffffffff;
}
value.value = transmute(f64)nan;
advance_token(p);
return;
}
}
}
err = Error.Unexpected_Token;
advance_token(p);
return;
}
parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
if err = expect_token(p, Kind.Open_Bracket); err != Error.None {
return;
}
array: Array;
array.allocator = p.allocator;
defer if err != Error.None {
for elem in array {
destroy_value(elem);
}
delete(array);
}
for p.curr_token.kind != Kind.Close_Bracket {
elem, elem_err := parse_value(p);
if elem_err != Error.None {
err = elem_err;
return;
}
append(&array, elem);
// Disallow trailing commas for the time being
if allow_token(p, Kind.Comma) {
continue;
} else {
break;
}
}
if err = expect_token(p, Kind.Close_Bracket); err != Error.None {
return;
}
value.value = array;
return;
}
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
n := len(s);
b := make([]byte, n+1, allocator);
copy(b, cast([]byte)s);
b[n] = 0;
return string(b[:n]);
}
parse_object_key :: proc(p: ^Parser) -> (key: string, err: Error) {
tok := p.curr_token;
if p.spec == Specification.JSON5 {
if tok.kind == Kind.String {
expect_token(p, Kind.String);
key = unquote_string(tok, p.spec, p.allocator);
return;
} else if tok.kind == Kind.Ident {
expect_token(p, Kind.Ident);
key = clone_string(tok.text, p.allocator);
return;
}
}
if tok_err := expect_token(p, Kind.String); tok_err != Error.None {
err = Error.Expected_String_For_Object_Key;
return;
}
key = unquote_string(tok, p.spec, p.allocator);
return;
}
parse_object :: proc(p: ^Parser) -> (value: Value, err: Error) {
value.pos = p.curr_token.pos;
defer value.end = token_end_pos(p.prev_token);
if err = expect_token(p, Kind.Open_Brace); err != Error.None {
value.pos = p.curr_token.pos;
return;
}
obj: Object;
obj.allocator = p.allocator;
defer if err != Error.None {
for key, elem in obj {
delete(key, p.allocator);
destroy_value(elem);
}
delete(obj);
}
for p.curr_token.kind != Kind.Close_Brace {
key: string;
key, err = parse_object_key(p);
if err != Error.None {
delete(key, p.allocator);
value.pos = p.curr_token.pos;
return;
}
if colon_err := expect_token(p, Kind.Colon); colon_err != Error.None {
err = Error.Expected_Colon_After_Key;
value.pos = p.curr_token.pos;
return;
}
elem, elem_err := parse_value(p);
if elem_err != Error.None {
err = elem_err;
value.pos = p.curr_token.pos;
return;
}
if key in obj {
err = Error.Duplicate_Object_Key;
value.pos = p.curr_token.pos;
delete(key, p.allocator);
return;
}
obj[key] = elem;
if p.spec == Specification.JSON5 {
// Allow trailing commas
if allow_token(p, Kind.Comma) {
continue;
}
} else {
// Disallow trailing commas
if allow_token(p, Kind.Comma) {
continue;
} else {
break;
}
}
}
if err = expect_token(p, Kind.Close_Brace); err != Error.None {
value.pos = p.curr_token.pos;
return;
}
value.value = obj;
return;
}
// IMPORTANT NOTE(bill): unquote_string assumes a mostly valid string
unquote_string :: proc(token: Token, spec: Specification, allocator := context.allocator) -> string {
get_u2_rune :: proc(s: string) -> rune {
if len(s) < 4 || s[0] != '\\' || s[1] != 'x' {
return -1;
}
r: rune;
for c in s[2:4] {
x: rune;
switch c {
case '0'..'9': x = c - '0';
case 'a'..'f': x = c - 'a' + 10;
case 'A'..'F': x = c - 'A' + 10;
case: return -1;
}
r = r*16 + x;
}
return r;
}
get_u4_rune :: proc(s: string) -> rune {
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
return -1;
}
r: rune;
for c in s[2:6] {
x: rune;
switch c {
case '0'..'9': x = c - '0';
case 'a'..'f': x = c - 'a' + 10;
case 'A'..'F': x = c - 'A' + 10;
case: return -1;
}
r = r*16 + x;
}
return r;
}
if token.kind != Kind.String {
return "";
}
s := token.text;
if len(s) <= 2 {
return "";
}
quote := s[0];
if s[0] != s[len(s)-1] {
// Invalid string
return "";
}
s = s[1:len(s)-1];
i := 0;
for i < len(s) {
c := s[i];
if c == '\\' || c == quote || c < ' ' {
break;
}
if c < utf8.RUNE_SELF {
i += 1;
continue;
}
r, w := utf8.decode_rune_in_string(s);
if r == utf8.RUNE_ERROR && w == 1 {
break;
}
i += w;
}
if i == len(s) {
return clone_string(s, allocator);
}
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
w := copy(b, cast([]byte)s[0:i]);
loop: for i < len(s) {
c := s[i];
switch {
case c == '\\':
i += 1;
if i >= len(s) {
break loop;
}
switch s[i] {
case: break loop;
case '"', '\'', '\\', '/':
b[w] = s[i];
i += 1;
w += 1;
case 'b':
b[w] = '\b';
i += 1;
w += 1;
case 'f':
b[w] = '\f';
i += 1;
w += 1;
case 'r':
b[w] = '\r';
i += 1;
w += 1;
case 't':
b[w] = '\t';
i += 1;
w += 1;
case 'n':
b[w] = '\n';
i += 1;
w += 1;
case 'u':
i -= 1; // Include the \u in the check for sanity sake
r := get_u4_rune(s[i:]);
if r < 0 {
break loop;
}
i += 6;
buf, buf_width := utf8.encode_rune(r);
copy(b[w:], buf[:buf_width]);
w += buf_width;
case '0':
if spec == Specification.JSON5 {
b[w] = '\x00';
i += 1;
w += 1;
} else {
break loop;
}
case 'v':
if spec == Specification.JSON5 {
b[w] = '\v';
i += 1;
w += 1;
} else {
break loop;
}
case 'x':
if spec == Specification.JSON5 {
i -= 1; // Include the \x in the check for sanity sake
r := get_u2_rune(s[i:]);
if r < 0 {
break loop;
}
i += 4;
buf, buf_width := utf8.encode_rune(r);
copy(b[w:], buf[:buf_width]);
w += buf_width;
} else {
break loop;
}
}
case c == quote, c < ' ':
break loop;
case c < utf8.RUNE_SELF:
b[w] = c;
i += 1;
w += 1;
case:
r, width := utf8.decode_rune_in_string(s[i:]);
i += width;
buf, buf_width := utf8.encode_rune(r);
assert(buf_width <= width);
copy(b[w:], buf[:buf_width]);
w += buf_width;
}
}
return string(b[:w]);
}
+473
View File
@@ -0,0 +1,473 @@
package json
import "core:unicode/utf8"
Token :: struct {
using pos: Pos,
kind: Kind,
text: string,
}
Kind :: enum {
Invalid,
Null,
False,
True,
Infinity,
NaN,
Ident,
Integer,
Float,
String,
Colon,
Comma,
Open_Brace,
Close_Brace,
Open_Bracket,
Close_Bracket,
}
Tokenizer :: struct {
using pos: Pos,
data: []byte,
r: rune, // current rune
w: int, // current rune width in bytes
curr_line_offset: int,
spec: Specification,
}
make_tokenizer :: proc(data: []byte, spec := Specification.JSON) -> Tokenizer {
t := Tokenizer{pos = {line=1}, data = data, spec = spec};
next_rune(&t);
if t.r == utf8.RUNE_BOM {
next_rune(&t);
}
return t;
}
next_rune :: proc(t: ^Tokenizer) -> rune #no_bounds_check {
if t.offset >= len(t.data) {
return utf8.RUNE_EOF;
}
t.offset += t.w;
t.r, t.w = utf8.decode_rune(t.data[t.offset:]);
t.pos.column = t.offset - t.curr_line_offset;
return t.r;
}
get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_digits :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
if '0' <= t.r && t.r <= '9' {
// Okay
} else {
return;
}
next_rune(t);
}
}
skip_hex_digits :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
next_rune(t);
switch t.r {
case '0'..'9', 'a'..'f', 'A'..'F':
// Okay
case:
return;
}
}
}
scan_espace :: proc(t: ^Tokenizer) -> bool {
switch t.r {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
next_rune(t);
return true;
case 'u':
// Expect 4 hexadecimal digits
for i := 0; i < 4; i += 1 {
r := next_rune(t);
switch r {
case '0'..'9', 'a'..'f', 'A'..'F':
// Okay
case:
return false;
}
}
case:
// Ignore the next rune regardless
next_rune(t);
}
return false;
}
skip_whitespace :: proc(t: ^Tokenizer) -> rune {
loop: for t.offset < len(t.data) {
switch t.r {
case ' ', '\t', '\v', '\f', '\r':
next_rune(t);
case '\n':
t.line += 1;
t.curr_line_offset = t.offset;
t.pos.column = 1;
next_rune(t);
case:
if t.spec == Specification.JSON5 {
switch t.r {
case 0x2028, 0x2029, 0xFEFF:
next_rune(t);
continue loop;
}
}
break loop;
}
}
return t.r;
}
skip_to_next_line :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
r := next_rune(t);
if r == '\n' {
return;
}
}
}
skip_alphanum :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
switch next_rune(t) {
case 'A'..'Z', 'a'..'z', '0'..'9', '_':
continue;
}
return;
}
}
skip_whitespace(t);
token.pos = t.pos;
token.kind = Kind.Invalid;
curr_rune := t.r;
next_rune(t);
block: switch curr_rune {
case utf8.RUNE_ERROR:
err = Error.Illegal_Character;
case utf8.RUNE_EOF, '\x00':
err = Error.EOF;
case 'A'..'Z', 'a'..'z', '_':
token.kind = Kind.Ident;
skip_alphanum(t);
switch str := string(t.data[token.offset:t.offset]); str {
case "null": token.kind = Kind.Null;
case "false": token.kind = Kind.False;
case "true": token.kind = Kind.True;
case:
if t.spec == Specification.JSON5 do switch str {
case "Infinity": token.kind = Kind.Infinity;
case "NaN": token.kind = Kind.NaN;
}
}
case '+':
err = Error.Illegal_Character;
if t.spec != Specification.JSON5 {
break;
}
fallthrough;
case '-':
switch t.r {
case '0'..'9':
// Okay
case:
// Illegal use of +/-
err = Error.Illegal_Character;
if t.spec == Specification.JSON5 {
if t.r == 'I' || t.r == 'N' {
skip_alphanum(t);
}
switch string(t.data[token.offset:t.offset]) {
case "-Infinity": token.kind = Kind.Infinity;
case "-NaN": token.kind = Kind.NaN;
}
}
break block;
}
fallthrough;
case '0'..'9':
token.kind = Kind.Integer;
if t.spec == Specification.JSON5 { // Hexadecimal Numbers
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
next_rune(t);
skip_hex_digits(t);
break;
}
}
skip_digits(t);
if t.r == '.' {
token.kind = Kind.Float;
next_rune(t);
skip_digits(t);
}
if t.r == 'e' || t.r == 'E' {
switch r := next_rune(t); r {
case '+', '-':
next_rune(t);
}
skip_digits(t);
}
str := string(t.data[token.offset:t.offset]);
if !is_valid_number(str, t.spec) {
err = Error.Invalid_Number;
}
case '.':
err = Error.Illegal_Character;
if t.spec == Specification.JSON5 { // Allow leading decimal point
skip_digits(t);
if t.r == 'e' || t.r == 'E' {
switch r := next_rune(t); r {
case '+', '-':
next_rune(t);
}
skip_digits(t);
}
str := string(t.data[token.offset:t.offset]);
if !is_valid_number(str, t.spec) {
err = Error.Invalid_Number;
}
}
case '\'':
err = Error.Illegal_Character;
if t.spec != Specification.JSON5 {
break;
}
fallthrough;
case '"':
token.kind = Kind.String;
quote := curr_rune;
for t.offset < len(t.data) {
r := t.r;
if r == '\n' || r < 0 {
err = Error.String_Not_Terminated;
break;
}
next_rune(t);
if r == quote {
break;
}
if r == '\\' {
scan_espace(t);
}
}
str := string(t.data[token.offset : t.offset]);
if !is_valid_string_literal(str, t.spec) {
err = Error.Invalid_String;
}
case ',': token.kind = Kind.Comma;
case ':': token.kind = Kind.Colon;
case '{': token.kind = Kind.Open_Brace;
case '}': token.kind = Kind.Close_Brace;
case '[': token.kind = Kind.Open_Bracket;
case ']': token.kind = Kind.Close_Bracket;
case '/':
err = Error.Illegal_Character;
if t.spec == Specification.JSON5 {
switch t.r {
case '/':
// Single-line comments
skip_to_next_line(t);
return get_token(t);
case '*':
// None-nested multi-line comments
for t.offset < len(t.data) {
next_rune(t);
if t.r == '*' {
next_rune(t);
if t.r == '/' {
next_rune(t);
return get_token(t);
}
}
}
err = Error.EOF;
}
}
case: err = Error.Illegal_Character;
}
token.text = string(t.data[token.offset : t.offset]);
return;
}
is_valid_number :: proc(str: string, spec: Specification) -> bool {
s := str;
if s == "" {
return false;
}
if s[0] == '-' {
s = s[1:];
if s == "" {
return false;
}
} else if spec == Specification.JSON5 {
if s[0] == '+' { // Allow positive sign
s = s[1:];
if s == "" {
return false;
}
}
}
switch s[0] {
case '0':
s = s[1:];
case '1'..'9':
s = s[1:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
case '.':
if spec == Specification.JSON5 { // Allow leading decimal point
s = s[1:];
} else {
return false;
}
case:
return false;
}
if spec == Specification.JSON5 {
if len(s) == 1 && s[0] == '.' { // Allow trailing decimal point
return true;
}
}
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
s = s[2:];
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
}
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
s = s[1:];
switch s[0] {
case '+', '-':
s = s[1:];
if s == "" {
return false;
}
}
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' do s = s[1:];
}
// The string should be empty now to be valid
return s == "";
}
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
s := str;
if len(s) < 2 {
return false;
}
quote := s[0];
if s[0] != s[len(s)-1] {
return false;
}
if s[0] != '"' || s[len(s)-1] != '"' {
if spec == Specification.JSON5 {
if s[0] != '\'' || s[len(s)-1] != '\'' {
return false;
}
} else {
return false;
}
}
s = s[1 : len(s)-1];
i := 0;
for i < len(s) {
c := s[i];
switch {
case c == '\\':
i += 1;
if i >= len(s) {
return false;
}
switch s[i] {
case '"', '\'', '\\', '/', 'b', 'n', 'r', 't', 'f':
i += 1;
case 'u':
if i >= len(s) {
return false;
}
hex := s[i+1:];
if len(hex) < 4 {
return false;
}
hex = hex[:4];
i += 5;
for j := 0; j < 4; j += 1 {
c2 := hex[j];
switch c2 {
case '0'..'9', 'a'..'z', 'A'..'Z':
// Okay
case:
return false;
}
}
case: return false;
}
case c == quote, c < ' ':
return false;
case c < utf8.RUNE_SELF:
i += 1;
case:
r, width := utf8.decode_rune_in_string(s[i:]);
if r == utf8.RUNE_ERROR && width == 1 {
return false;
}
i += width;
}
}
if i == len(s) {
return true;
}
return true;
}
+73
View File
@@ -0,0 +1,73 @@
package json
Specification :: enum {
JSON,
JSON5,
}
Null :: distinct rawptr;
Integer :: i64;
Float :: f64;
Boolean :: bool;
String :: string;
Array :: distinct [dynamic]Value;
Object :: distinct map[string]Value;
Value :: struct {
pos, end: Pos,
value: union {
Null,
Integer,
Float,
Boolean,
String,
Array,
Object,
}
}
Pos :: struct {
offset: int,
line: int,
column: int,
}
Error :: enum {
None,
EOF, // Not necessarily an error
// Tokenizing Errors
Illegal_Character,
Invalid_Number,
String_Not_Terminated,
Invalid_String,
// Parsing Errors
Unexpected_Token,
Expected_String_For_Object_Key,
Duplicate_Object_Key,
Expected_Colon_After_Key,
}
destroy_value :: proc(value: Value) {
switch v in value.value {
case Object:
for key, elem in v {
delete(key);
destroy_value(elem);
}
delete(v);
case Array:
for elem in v do destroy_value(elem);
delete(v);
case String:
delete(v);
}
}
+123
View File
@@ -0,0 +1,123 @@
package json
import "core:mem"
// NOTE(bill): is_valid will not check for duplicate keys
is_valid :: proc(data: []byte, spec := Specification.JSON) -> bool {
p := make_parser(data, spec, mem.nil_allocator());
if p.spec == Specification.JSON5 {
return validate_value(&p);
}
return validate_object(&p);
}
validate_object_key :: proc(p: ^Parser) -> bool {
tok := p.curr_token;
if p.spec == Specification.JSON5 {
if tok.kind == Kind.String {
expect_token(p, Kind.String);
return true;
} else if tok.kind == Kind.Ident {
expect_token(p, Kind.Ident);
return true;
}
}
err := expect_token(p, Kind.String);
return err == Error.None;
}
validate_object :: proc(p: ^Parser) -> bool {
if err := expect_token(p, Kind.Open_Brace); err != Error.None {
return false;
}
for p.curr_token.kind != Kind.Close_Brace {
if !validate_object_key(p) {
return false;
}
if colon_err := expect_token(p, Kind.Colon); colon_err != Error.None {
return false;
}
if !validate_value(p) {
return false;
}
if p.spec == Specification.JSON5 {
// Allow trailing commas
if allow_token(p, Kind.Comma) {
continue;
}
} else {
// Disallow trailing commas
if allow_token(p, Kind.Comma) {
continue;
} else {
break;
}
}
}
if err := expect_token(p, Kind.Close_Brace); err != Error.None {
return false;
}
return true;
}
validate_array :: proc(p: ^Parser) -> bool {
if err := expect_token(p, Kind.Open_Bracket); err != Error.None {
return false;
}
for p.curr_token.kind != Kind.Close_Bracket {
if !validate_value(p) {
return false;
}
// Disallow trailing commas for the time being
if allow_token(p, Kind.Comma) {
continue;
} else {
break;
}
}
if err := expect_token(p, Kind.Close_Bracket); err != Error.None {
return false;
}
return true;
}
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token;
using Kind;
switch token.kind {
case Null, False, True:
advance_token(p);
return true;
case Integer, Float:
advance_token(p);
return true;
case String:
advance_token(p);
return is_valid_string_literal(token.text, p.spec);
case Open_Brace:
return validate_object(p);
case Open_Bracket:
return validate_array(p);
case:
if p.spec == Specification.JSON5 {
switch token.kind {
case Infinity, NaN:
advance_token(p);
return true;
}
}
}
return false;
}
+676 -606
View File
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
// This is purely for documentation
package intrinsics
vector :: proc() ---
atomic_fence :: proc() ---
atomic_fence_acq :: proc() ---
atomic_fence_rel :: proc() ---
atomic_fence_acqrel :: proc() ---
atomic_store :: proc(dst: ^$T, val: $T) ---
atomic_store_rel :: proc(dst: ^$T, val: $T) ---
atomic_store_relaxed :: proc(dst: ^$T, val: $T) ---
atomic_store_unordered :: proc(dst: ^$T, val: $T) ---
atomic_load :: proc(dst: ^$T) -> T ---
atomic_load_acq :: proc(dst: ^$T) -> T ---
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
atomic_load_unordered :: proc(dst: ^$T) -> T ---
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
// Constant type tests
type_base_type :: proc($T: typeid) -> type ---
type_core_type :: proc($T: typeid) -> type ---
type_elem_type :: proc($T: typeid) -> type ---
type_is_boolean :: proc($T: typeid) -> bool ---
type_is_integer :: proc($T: typeid) -> bool ---
type_is_rune :: proc($T: typeid) -> bool ---
type_is_float :: proc($T: typeid) -> bool ---
type_is_complex :: proc($T: typeid) -> bool ---
type_is_quaternion :: proc($T: typeid) -> bool ---
type_is_string :: proc($T: typeid) -> bool ---
type_is_typeid :: proc($T: typeid) -> bool ---
type_is_any :: proc($T: typeid) -> bool ---
type_is_endian_little :: proc($T: typeid) -> bool ---
type_is_endian_big :: proc($T: typeid) -> bool ---
type_is_numeric :: proc($T: typeid) -> bool ---
type_is_ordered :: proc($T: typeid) -> bool ---
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
type_is_indexable :: proc($T: typeid) -> bool ---
type_is_sliceable :: proc($T: typeid) -> bool ---
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
type_is_dereferenceable :: proc($T: typeid) -> bool ---
type_is_valid_map_key :: proc($T: typeid) -> bool ---
type_is_named :: proc($T: typeid) -> bool ---
type_is_pointer :: proc($T: typeid) -> bool ---
type_is_opaque :: proc($T: typeid) -> bool ---
type_is_array :: proc($T: typeid) -> bool ---
type_is_slice :: proc($T: typeid) -> bool ---
type_is_dynamic_array :: proc($T: typeid) -> bool ---
type_is_map :: proc($T: typeid) -> bool ---
type_is_struct :: proc($T: typeid) -> bool ---
type_is_union :: proc($T: typeid) -> bool ---
type_is_enum :: proc($T: typeid) -> bool ---
type_is_proc :: proc($T: typeid) -> bool ---
type_is_bit_field :: proc($T: typeid) -> bool ---
type_is_bit_field_value :: proc($T: typeid) -> bool ---
type_is_bit_set :: proc($T: typeid) -> bool ---
type_is_simd_vector :: proc($T: typeid) -> bool ---
type_has_nil :: proc($T: typeid) -> bool ---
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
+143
View File
@@ -0,0 +1,143 @@
package log
import "core:fmt";
import "core:strings";
import "core:os";
import "core:time";
Level_Headers := []string{
"[DEBUG] --- ",
"[INFO ] --- ",
"[WARN ] --- ",
"[ERROR] --- ",
"[FATAL] --- ",
};
Default_Console_Logger_Opts :: Options{
.Level,
.Terminal_Color,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
Default_File_Logger_Opts :: Options{
.Level,
.Short_File_Path,
.Line,
.Procedure,
} | Full_Timestamp_Opts;
File_Console_Logger_Data :: struct {
lowest_level: Level,
file_handle: os.Handle,
ident : string,
}
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
data.lowest_level = lowest;
data.file_handle = h;
data.ident = ident;
return Logger{file_console_logger_proc, data, opt};
}
destroy_file_logger ::proc(log : ^Logger) {
data := cast(^File_Console_Logger_Data)log.data;
if data.file_handle != os.INVALID_HANDLE do os.close(data.file_handle);
free(data);
log^ = nil_logger();
}
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
data := new(File_Console_Logger_Data);
data.lowest_level = lowest;
data.file_handle = os.INVALID_HANDLE;
data.ident = ident;
return Logger{file_console_logger_proc, data, opt};
}
destroy_console_logger ::proc(log : ^Logger) {
free(log.data);
log^ = nil_logger();
}
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
data := cast(^File_Console_Logger_Data)logger_data;
if level < data.lowest_level do return;
h : os.Handle;
if(data.file_handle != os.INVALID_HANDLE) do h = data.file_handle;
else do h = level <= Level.Error ? context.stdout : context.stderr;
backing: [1024]byte; //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
buf := strings.builder_from_slice(backing[:]);
do_level_header(options, level, &buf);
when time.IS_SUPPORTED {
if Full_Timestamp_Opts & options != nil {
fmt.sbprint(&buf, "[");
t := time.now();
y, m, d := time.date(t);
h, min, s := time.clock(t);
if Option.Date in options do fmt.sbprintf(&buf, "%d-%02d-%02d ", y, m, d);
if Option.Time in options do fmt.sbprintf(&buf, "%02d:%02d:%02d", h, min, s);
fmt.sbprint(&buf, "] ");
}
}
do_location_header(options, &buf, location);
if data.ident != "" do fmt.sbprintf(&buf, "[%s] ", data.ident);
//TODO(Hoej): When we have better atomics and such, make this thread-safe
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text);
}
do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) {
RESET :: "\x1b[0m";
RED :: "\x1b[31m";
YELLOW :: "\x1b[33m";
DARK_GREY :: "\x1b[90m";
col := RESET;
switch level {
case Level.Debug : col = DARK_GREY;
case Level.Info : col = RESET;
case Level.Warning : col = YELLOW;
case Level.Error, Level.Fatal : col = RED;
}
if .Level in opts {
if .Terminal_Color in opts do fmt.sbprint(str, col);
fmt.sbprint(str, Level_Headers[level]);
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
}
}
do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := #caller_location) {
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
file := location.file_path;
if .Short_File_Path in opts {
when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/';
last := 0;
for r, i in location.file_path do if r == delimiter do last = i+1;
file = location.file_path[last:];
}
if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
if .Procedure in opts {
if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
fmt.sbprintf(buf, "%s()", location.procedure);
}
if .Line in opts {
if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":");
fmt.sbprint(buf, location.line);
}
fmt.sbprint(buf, "] ");
}
+65 -5
View File
@@ -1,5 +1,7 @@
package log
import "core:fmt"
Level :: enum {
Debug,
Info,
@@ -10,25 +12,83 @@ Level :: enum {
Option :: enum {
Level,
Date,
Time,
File,
Short_File_Path,
Long_File_Path,
Line,
Procedure,
Terminal_Color
}
Options :: bit_set[Option];
Logger_Proc :: #type proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location);
Options :: bit_set[Option];
Full_Timestamp_Opts :: Options{
.Date,
.Time
};
Location_Header_Opts :: Options{
.Short_File_Path,
.Long_File_Path,
.Line,
.Procedure,
};
Location_File_Opts :: Options{
.Short_File_Path,
.Long_File_Path
};
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
Logger :: struct {
procedure: Logger_Proc,
data: rawptr,
options: Options,
}
Multi_Logger_Data :: struct {
loggers : []Logger,
}
nil_logger_proc :: proc(data: rawptr, level: Level, ident, text: string, options: Options, location := #caller_location) {
create_multi_logger :: proc(logs: ..Logger) -> Logger {
data := new(Multi_Logger_Data);
data.loggers = make([]Logger, len(logs));
copy(data.loggers, logs);
return Logger{multi_logger_proc, data, nil};
}
destroy_multi_logger :: proc(log : ^Logger) {
free(log.data);
log^ = nil_logger();
}
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
options: Options, location := #caller_location) {
data := cast(^Multi_Logger_Data)logger_data;
if data.loggers == nil || len(data.loggers) == 0 {
return;
}
for log in data.loggers {
log.procedure(log.data, level, text, log.options, location);
}
}
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
// Do nothing
}
nil_logger :: proc() -> Logger {
return Logger{nil_logger_proc, nil};
return Logger{nil_logger_proc, nil, nil};
}
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
error :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Error, fmt_str=fmt_str, args=args, location=location);
fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
logger := context.logger;
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location);
}
@@ -22,6 +22,7 @@ I16_MAX :: 1 << 15 - 1;
I32_MAX :: 1 << 31 - 1;
I64_MAX :: 1 << 63 - 1;
@(default_calling_convention="none")
foreign {
@(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 ---
@(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 ---
@@ -49,6 +50,8 @@ foreign {
@(link_name="llvm.bswap.i16") byte_swap_i16 :: proc(i16) -> i16 ---
@(link_name="llvm.bswap.i32") byte_swap_i32 :: proc(i32) -> i32 ---
@(link_name="llvm.bswap.i64") byte_swap_i64 :: proc(i64) -> i64 ---
@(link_name="llvm.bswap.i128") byte_swap_u128 :: proc(u128) -> u128 ---
@(link_name="llvm.bswap.i128") byte_swap_i128 :: proc(i128) -> i128 ---
}
byte_swap_uint :: proc(i: uint) -> uint {
@@ -66,16 +69,18 @@ byte_swap_int :: proc(i: int) -> int {
}
}
byte_swap :: proc[
byte_swap :: proc{
byte_swap_u16,
byte_swap_u32,
byte_swap_u64,
byte_swap_u128,
byte_swap_i16,
byte_swap_i32,
byte_swap_i64,
byte_swap_i128,
byte_swap_uint,
byte_swap_int,
];
};
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
@@ -120,6 +125,7 @@ to_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } e
to_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
@(default_calling_convention="none")
foreign {
@(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
@(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
@@ -150,14 +156,15 @@ overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
}
}
overflowing_add :: proc[
overflowing_add :: proc{
overflowing_add_u8, overflowing_add_i8,
overflowing_add_u16, overflowing_add_i16,
overflowing_add_u32, overflowing_add_i32,
overflowing_add_u64, overflowing_add_i64,
overflowing_add_uint, overflowing_add_int,
];
};
@(default_calling_convention="none")
foreign {
@(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
@(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
@@ -187,15 +194,15 @@ overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
}
}
overflowing_sub :: proc[
overflowing_sub :: proc{
overflowing_sub_u8, overflowing_sub_i8,
overflowing_sub_u16, overflowing_sub_i16,
overflowing_sub_u32, overflowing_sub_i32,
overflowing_sub_u64, overflowing_sub_i64,
overflowing_sub_uint, overflowing_sub_int,
];
};
@(default_calling_convention="none")
foreign {
@(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
@(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
@@ -225,13 +232,13 @@ overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
}
}
overflowing_mul :: proc[
overflowing_mul :: proc{
overflowing_mul_u8, overflowing_mul_i8,
overflowing_mul_u16, overflowing_mul_i16,
overflowing_mul_u32, overflowing_mul_i32,
overflowing_mul_u64, overflowing_mul_i64,
overflowing_mul_uint, overflowing_mul_int,
];
};
is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
@@ -244,10 +251,10 @@ is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0
is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc[
is_power_of_two :: proc{
is_power_of_two_u8, is_power_of_two_i8,
is_power_of_two_u16, is_power_of_two_i16,
is_power_of_two_u32, is_power_of_two_i32,
is_power_of_two_u64, is_power_of_two_i64,
is_power_of_two_uint, is_power_of_two_int,
]
};
+283
View File
@@ -0,0 +1,283 @@
package linalg
import "core:math"
import "intrinsics"
// Generic
dot_vector :: proc(a, b: $T/[$N]$E) -> (c: E) {
for i in 0..<N {
c += a[i] * b[i];
}
return;
}
dot_quaternion128 :: proc(a, b: $T/quaternion128) -> (c: f32) {
return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b);
}
dot_quaternion256 :: proc(a, b: $T/quaternion256) -> (c: f64) {
return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b);
}
dot :: proc{dot_vector, dot_quaternion128, dot_quaternion256};
cross2 :: proc(a, b: $T/[2]$E) -> E {
return a[0]*b[1] - b[0]*a[1];
}
cross3 :: proc(a, b: $T/[3]$E) -> (c: T) {
c[0] = +(a[1]*b[2] - b[1]*a[2]);
c[1] = -(a[2]*b[3] - b[2]*a[3]);
c[2] = +(a[3]*b[1] - b[3]*a[1]);
return;
}
cross :: proc{cross2, cross3};
normalize_vector :: proc(v: $T/[$N]$E) -> T {
return v / length(v);
}
normalize_quaternion128 :: proc(q: $Q/quaternion128) -> Q {
return q/abs(q);
}
normalize_quaternion256 :: proc(q: $Q/quaternion256) -> Q {
return q/abs(q);
}
normalize :: proc{normalize_vector, normalize_quaternion128, normalize_quaternion256};
normalize0_vector :: proc(v: $T/[$N]$E) -> T {
m := length(v);
return m == 0 ? 0 : v/m;
}
normalize0_quaternion128 :: proc(q: $Q/quaternion128) -> Q {
m := abs(q);
return m == 0 ? 0 : q/m;
}
normalize0_quaternion256 :: proc(q: $Q/quaternion256) -> Q {
m := abs(q);
return m == 0 ? 0 : q/m;
}
normalize0 :: proc{normalize0_vector, normalize0_quaternion128, normalize0_quaternion256};
length :: proc(v: $T/[$N]$E) -> E {
return math.sqrt(dot(v, v));
}
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
for i in 0..<N do m[i][i] = E(1);
return m;
}
transpose :: proc(a: $T/[$N][$M]$E) -> (m: [M][N]E) {
for j in 0..<M {
for i in 0..<N {
m[j][i] = a[i][j];
}
}
return;
}
mul_matrix :: proc(a, b: $M/[$N][N]$E) -> (c: M)
where !intrinsics.type_is_array(E),
intrinsics.type_is_numeric(E) {
for i in 0..<N {
for k in 0..<N {
for j in 0..<N {
c[i][k] += a[i][j] * b[j][k];
}
}
}
return;
}
mul_matrix_differ :: proc(a: $A/[$I][$J]$E, b: $B/[J][$K]E) -> (c: [I][K]E)
where !intrinsics.type_is_array(E),
intrinsics.type_is_numeric(E),
I != J {
for i in 0..<I {
for k in 0..<K {
for j in 0..<J {
c[i][k] += a[i][j] * b[j][k];
}
}
}
return;
}
mul_matrix_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
where !intrinsics.type_is_array(E),
intrinsics.type_is_numeric(E) {
for i in 0..<I {
for j in 0..<J {
c[i] += a[i][j] * b[i];
}
}
return;
}
mul_quaternion128_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
q := transmute(Raw_Quaternion)q;
v := transmute([3]f32)v;
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
}
mul_quaternion256_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
q := transmute(Raw_Quaternion)q;
v := transmute([3]f64)v;
t := cross(2*q.xyz, v);
return V(v + q.r*t + cross(q.xyz, t));
}
mul_quaternion_vector3 :: proc{mul_quaternion128_vector3, mul_quaternion256_vector3};
mul :: proc{
mul_matrix,
mul_matrix_differ,
mul_matrix_vector,
mul_quaternion128_vector3,
mul_quaternion256_vector3,
};
// Specific
Float :: f32;
Vector2 :: distinct [2]Float;
Vector3 :: distinct [3]Float;
Vector4 :: distinct [4]Float;
Matrix2x1 :: distinct [2][1]Float;
Matrix2x2 :: distinct [2][2]Float;
Matrix2x3 :: distinct [2][3]Float;
Matrix2x4 :: distinct [2][4]Float;
Matrix3x1 :: distinct [3][1]Float;
Matrix3x2 :: distinct [3][2]Float;
Matrix3x3 :: distinct [3][3]Float;
Matrix3x4 :: distinct [3][4]Float;
Matrix4x1 :: distinct [4][1]Float;
Matrix4x2 :: distinct [4][2]Float;
Matrix4x3 :: distinct [4][3]Float;
Matrix4x4 :: distinct [4][4]Float;
Matrix2 :: Matrix2x2;
Matrix3 :: Matrix3x3;
Matrix4 :: Matrix4x4;
Quaternion :: distinct (size_of(Float) == size_of(f32) ? quaternion128 : quaternion256);
translate_matrix4 :: proc(v: Vector3) -> Matrix4 {
m := identity(Matrix4);
m[3][0] = v[0];
m[3][1] = v[1];
m[3][2] = v[2];
return m;
}
rotate_matrix4 :: proc(v: Vector3, angle_radians: Float) -> Matrix4 {
c := math.cos(angle_radians);
s := math.sin(angle_radians);
a := normalize(v);
t := a * (1-c);
rot := identity(Matrix4);
rot[0][0] = c + t[0]*a[0];
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
rot[0][3] = 0;
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
rot[1][1] = c + t[1]*a[1];
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
rot[1][3] = 0;
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
rot[2][2] = c + t[2]*a[2];
rot[2][3] = 0;
return rot;
}
scale_matrix4 :: proc(m: Matrix4, v: Vector3) -> Matrix4 {
mm := m;
mm[0][0] *= v[0];
mm[1][1] *= v[1];
mm[2][2] *= v[2];
return mm;
}
look_at :: proc(eye, centre, up: Vector3) -> Matrix4 {
f := normalize(centre - eye);
s := normalize(cross(f, up));
u := cross(s, f);
return Matrix4{
{+s.x, +u.x, -f.x, 0},
{+s.y, +u.y, -f.y, 0},
{+s.z, +u.z, -f.z, 0},
{-dot(s, eye), -dot(u, eye), +dot(f, eye), 1},
};
}
perspective :: proc(fovy, aspect, near, far: Float) -> (m: Matrix4) {
tan_half_fovy := math.tan(0.5 * fovy);
m[0][0] = 1 / (aspect*tan_half_fovy);
m[1][1] = 1 / (tan_half_fovy);
m[2][2] = -(far + near) / (far - near);
m[2][3] = -1;
m[3][2] = -2*far*near / (far - near);
return;
}
ortho3d :: proc(left, right, bottom, top, near, far: Float) -> (m: Matrix4) {
m[0][0] = +2 / (right - left);
m[1][1] = +2 / (top - bottom);
m[2][2] = -2 / (far - near);
m[3][0] = -(right + left) / (right - left);
m[3][1] = -(top + bottom) / (top - bottom);
m[3][2] = -(far + near) / (far- near);
m[3][3] = 1;
return;
}
axis_angle :: proc(axis: Vector3, angle_radians: Float) -> Quaternion {
t := angle_radians*0.5;
w := math.cos(t);
v := normalize(axis) * math.sin(t);
return quaternion(w, v.x, v.y, v.z);
}
angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion {
t := angle_radians*0.5;
w := math.cos(t);
v := normalize(axis) * math.sin(t);
return quaternion(w, v.x, v.y, v.z);
}
euler_angles :: proc(pitch, yaw, roll: Float) -> Quaternion {
p := axis_angle({1, 0, 0}, pitch);
y := axis_angle({0, 1, 0}, yaw);
r := axis_angle({0, 0, 1}, roll);
return (y * p) * r;
}
+479 -376
View File
@@ -1,36 +1,41 @@
package math
import "intrinsics"
Float_Class :: enum {
Normal, // an ordinary nonzero floating point value
Subnormal, // a subnormal floating point value
Zero, // zero
Neg_Zero, // the negative zero
NaN, // Not-A-Number (NaN)
Inf, // positive infinity
Neg_Inf // negative infinity
};
TAU :: 6.28318530717958647692528676655900576;
PI :: 3.14159265358979323846264338327950288;
E :: 2.71828182845904523536;
τ :: TAU;
π :: PI;
e :: E;
SQRT_TWO :: 1.41421356237309504880168872420969808;
SQRT_THREE :: 1.73205080756887729352744634150587236;
SQRT_FIVE :: 2.23606797749978969640917366873127623;
LOG_TWO :: 0.693147180559945309417232121458176568;
LOG_TEN :: 2.30258509299404568401799145468436421;
LN2 :: 0.693147180559945309417232121458176568;
LN10 :: 2.30258509299404568401799145468436421;
EPSILON :: 1.19209290e-7;
MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64'
MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32'
τ :: TAU;
π :: PI;
Vec2 :: distinct [2]f32;
Vec3 :: distinct [3]f32;
Vec4 :: distinct [4]f32;
// Column major
Mat2 :: distinct [2][2]f32;
Mat3 :: distinct [3][3]f32;
Mat4 :: distinct [4][4]f32;
Quat :: struct {x, y, z, w: f32};
QUAT_IDENTITY := Quat{x = 0, y = 0, z = 0, w = 1};
RAD_PER_DEG :: TAU/360.0;
DEG_PER_RAD :: 360.0/TAU;
@(default_calling_convention="c")
@(default_calling_convention="none")
foreign _ {
@(link_name="llvm.sqrt.f32")
sqrt_f32 :: proc(x: f32) -> f32 ---;
@@ -58,24 +63,50 @@ foreign _ {
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
@(link_name="llvm.log.f32")
log_f32 :: proc(x: f32) -> f32 ---;
ln_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.log.f64")
log_f64 :: proc(x: f64) -> f64 ---;
ln_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.exp.f32")
exp_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.exp.f64")
exp_f64 :: proc(x: f64) -> f64 ---;
}
log :: proc[log_f32, log_f64];
sqrt :: proc{sqrt_f32, sqrt_f64};
sin :: proc{sin_f32, sin_f64};
cos :: proc{cos_f32, cos_f64};
pow :: proc{pow_f32, pow_f64};
fmuladd :: proc{fmuladd_f32, fmuladd_f64};
ln :: proc{ln_f32, ln_f64};
exp :: proc{exp_f32, exp_f64};
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
log :: proc{log_f32, log_f64};
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
log2 :: proc{log2_f32, log2_f64};
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
log10 :: proc{log10_f32, log10_f64};
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
tan :: proc{tan_f32, tan_f64};
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
unlerp :: proc{unlerp_f32, unlerp_f64};
sign_f32 :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
sign_f64 :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
sign :: proc{sign_f32, sign_f64};
copy_sign_f32 :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
@@ -84,388 +115,460 @@ copy_sign_f32 :: proc(x, y: f32) -> f32 {
ix |= iy & 0x8000_0000;
return transmute(f32)ix;
}
copy_sign_f64 :: proc(x, y: f64) -> f64 {
ix := transmute(u64)x;
iy := transmute(u64)y;
ix &= 0x7fff_ffff_ffff_ff;
ix &= 0x7fff_ffff_ffff_ffff;
ix |= iy & 0x8000_0000_0000_0000;
return transmute(f64)ix;
}
copy_sign :: proc{copy_sign_f32, copy_sign_f64};
sqrt :: proc[sqrt_f32, sqrt_f64];
sin :: proc[sin_f32, sin_f64];
cos :: proc[cos_f32, cos_f64];
tan :: proc[tan_f32, tan_f64];
pow :: proc[pow_f32, pow_f64];
fmuladd :: proc[fmuladd_f32, fmuladd_f64];
sign :: proc[sign_f32, sign_f64];
copy_sign :: proc[copy_sign_f32, copy_sign_f64];
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
to_radians :: proc{to_radians_f32, to_radians_f64};
to_degrees :: proc{to_degrees_f32, to_degrees_f64};
trunc_f32 :: proc(x: f32) -> f32 {
trunc_internal :: proc(f: f32) -> f32 {
mask :: 0xff;
shift :: 32 - 9;
bias :: 0x7f;
if f < 1 {
switch {
case f < 0: return -trunc_internal(-f);
case f == 0: return f;
case: return 0;
}
}
x := transmute(u32)f;
e := (x >> shift) & mask - bias;
if e < shift {
x &= ~(1 << (shift-e)) - 1;
}
return transmute(f32)x;
}
switch classify(x) {
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
return x;
}
return trunc_internal(x);
}
trunc_f64 :: proc(x: f64) -> f64 {
trunc_internal :: proc(f: f64) -> f64 {
mask :: 0x7ff;
shift :: 64 - 12;
bias :: 0x3ff;
if f < 1 {
switch {
case f < 0: return -trunc_internal(-f);
case f == 0: return f;
case: return 0;
}
}
x := transmute(u64)f;
e := (x >> shift) & mask - bias;
if e < shift {
x &= ~(1 << (shift-e)) - 1;
}
return transmute(f64)x;
}
switch classify(x) {
case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf:
return x;
}
return trunc_internal(x);
}
trunc :: proc{trunc_f32, trunc_f64};
round_f32 :: proc(x: f32) -> f32 {
return x < 0 ? ceil(x - 0.5) : floor(x + 0.5);
}
round_f64 :: proc(x: f64) -> f64 {
return x < 0 ? ceil(x - 0.5) : floor(x + 0.5);
}
round :: proc{round_f32, round_f64};
round_f32 :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
round_f64 :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
round :: proc[round_f32, round_f64];
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
ceil :: proc{ceil_f32, ceil_f64};
floor_f32 :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
floor_f64 :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
floor :: proc[floor_f32, floor_f64];
floor_f32 :: proc(x: f32) -> f32 {
if x == 0 || is_nan(x) || is_inf(x) {
return x;
}
if x < 0 {
d, fract := modf(-x);
if fract != 0.0 {
d = d + 1;
}
return -d;
}
d, _ := modf(x);
return d;
}
floor_f64 :: proc(x: f64) -> f64 {
if x == 0 || is_nan(x) || is_inf(x) {
return x;
}
if x < 0 {
d, fract := modf(-x);
if fract != 0.0 {
d = d + 1;
}
return -d;
}
d, _ := modf(x);
return d;
}
floor :: proc{floor_f32, floor_f64};
ceil_f32 :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); }// TODO: Get accurate versions
ceil_f64 :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); }// TODO: Get accurate versions
ceil :: proc[ceil_f32, ceil_f64];
floor_div :: proc(x, y: $T) -> T
where intrinsics.type_is_integer(T) {
a := x / y;
r := x % y;
if (r > 0 && y < 0) || (r < 0 && y > 0) {
a -= 1;
}
return a;
}
floor_mod :: proc(x, y: $T) -> T
where intrinsics.type_is_integer(T) {
r := x % y;
if (r > 0 && y < 0) || (r < 0 && y > 0) {
r += y;
}
return r;
}
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
shift :: 32 - 8 - 1;
mask :: 0xff;
bias :: 127;
if x < 1 {
switch {
case x < 0:
int, frac = modf(-x);
return -int, -frac;
case x == 0:
return x, x;
}
return 0, x;
}
i := transmute(u32)x;
e := uint(i>>shift)&mask - bias;
if e < shift {
i &~= 1<<(shift-e) - 1;
}
int = transmute(f32)i;
frac = x - int;
return;
}
modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
shift :: 64 - 11 - 1;
mask :: 0x7ff;
bias :: 1023;
if x < 1 {
switch {
case x < 0:
int, frac = modf(-x);
return -int, -frac;
case x == 0:
return x, x;
}
return 0, x;
}
i := transmute(u64)x;
e := uint(i>>shift)&mask - bias;
if e < shift {
i &~= 1<<(shift-e) - 1;
}
int = transmute(f64)i;
frac = x - int;
return;
}
modf :: proc{modf_f32, modf_f64};
split_decimal :: modf;
mod_f32 :: proc(x, y: f32) -> (n: f32) {
z := abs(y);
n = remainder(abs(x), z);
if sign(n) < 0 {
n += z;
}
return copy_sign(n, x);
}
mod_f64 :: proc(x, y: f64) -> (n: f64) {
z := abs(y);
n = remainder(abs(x), z);
if sign(n) < 0 {
n += z;
}
return copy_sign(n, x);
}
mod :: proc{mod_f32, mod_f64};
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
remainder :: proc[remainder_f32, remainder_f64];
remainder :: proc{remainder_f32, remainder_f64};
mod_f32 :: proc(x, y: f32) -> f32 {
result: f32;
y = abs(y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
gcd :: proc(x, y: $T) -> T
where intrinsics.type_is_ordered_numeric(T) {
x, y := x, y;
for y != 0 {
x %= y;
x, y = y, x;
}
return copy_sign(result, x);
return abs(x);
}
mod_f64 :: proc(x, y: f64) -> f64 {
result: f64;
y = abs(y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
lcm :: proc(x, y: $T) -> T
where intrinsics.type_is_ordered_numeric(T) {
return x / gcd(x, y) * y;
}
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
switch {
case x == 0:
return 0, 0;
case x < 0:
significand, exponent = frexp(-x);
return -significand, exponent;
}
return copy_sign(result, x);
}
mod :: proc[mod_f32, mod_f64];
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
mul :: proc[
mat3_mul,
mat4_mul, mat4_mul_vec4,
quat_mul, quat_mulf,
];
div :: proc[
quat_div, quat_divf,
];
inverse :: proc[mat4_inverse, quat_inverse];
dot :: proc[vec_dot, quat_dot];
cross :: proc[cross2, cross3];
vec_dot :: proc(a, b: $T/[$N]$E) -> E {
res: E;
for i in 0..N-1 {
res += a[i] * b[i];
ex := trunc(log2(x));
exponent = int(ex);
significand = x / pow(2.0, ex);
if abs(significand) >= 1 {
exponent += 1;
significand /= 2;
}
return res;
if exponent == 1024 && significand == 0 {
significand = 0.99999999999999988898;
}
return;
}
frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
switch {
case x == 0:
return 0, 0;
case x < 0:
significand, exponent = frexp(-x);
return -significand, exponent;
}
ex := trunc(log2(x));
exponent = int(ex);
significand = x / pow(2.0, ex);
if abs(significand) >= 1 {
exponent += 1;
significand /= 2;
}
if exponent == 1024 && significand == 0 {
significand = 0.99999999999999988898;
}
return;
}
frexp :: proc{frexp_f32, frexp_f64};
binomial :: proc(n, k: int) -> int {
switch {
case k <= 0: return 1;
case 2*k > n: return binomial(n, n-k);
}
b := n;
for i in 2..<k {
b = (b * (n+1-i))/i;
}
return b;
}
cross2 :: proc(a, b: $T/[2]$E) -> E {
return a[0]*b[1] - a[1]*b[0];
factorial :: proc(n: int) -> int {
when size_of(int) == size_of(i64) {
@static table := [21]int{
1,
1,
2,
6,
24,
120,
720,
5_040,
40_320,
362_880,
3_628_800,
39_916_800,
479_001_600,
6_227_020_800,
87_178_291_200,
1_307_674_368_000,
20_922_789_888_000,
355_687_428_096_000,
6_402_373_705_728_000,
121_645_100_408_832_000,
2_432_902_008_176_640_000,
};
} else {
@static table := [13]int{
1,
1,
2,
6,
24,
120,
720,
5_040,
40_320,
362_880,
3_628_800,
39_916_800,
479_001_600,
};
}
assert(n >= 0, "parameter must not be negative");
assert(n < len(table), "parameter is too large to lookup in the table");
return 0;
}
cross3 :: proc(a, b: $T/[3]$E) -> T {
i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
return T(i - j);
classify_f32 :: proc(x: f32) -> Float_Class {
switch {
case x == 0:
i := transmute(i32)x;
if i < 0 {
return .Neg_Zero;
}
return .Zero;
case x*0.5 == x:
if x < 0 {
return .Neg_Inf;
}
return .Inf;
case x != x:
return .NaN;
}
u := transmute(u32)x;
exp := int(u>>23) & (1<<8 - 1);
if exp == 0 {
return .Subnormal;
}
return .Normal;
}
classify_f64 :: proc(x: f64) -> Float_Class {
switch {
case x == 0:
i := transmute(i64)x;
if i < 0 {
return .Neg_Zero;
}
return .Zero;
case x*0.5 == x:
if x < 0 {
return .Neg_Inf;
}
return .Inf;
case x != x:
return .NaN;
}
u := transmute(u64)x;
exp := int(u>>52) & (1<<11 - 1);
if exp == 0 {
return .Subnormal;
}
return .Normal;
}
classify :: proc{classify_f32, classify_f64};
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
is_nan :: proc{is_nan_f32, is_nan_f64};
is_inf_f32 :: proc(x: f32) -> bool { return classify(abs(x)) == .Inf; }
is_inf_f64 :: proc(x: f64) -> bool { return classify(abs(x)) == .Inf; }
is_inf :: proc{is_inf_f32, is_inf_f64};
is_power_of_two :: proc(x: int) -> bool {
return x > 0 && (x & (x-1)) == 0;
}
next_power_of_two :: proc(x: int) -> int {
k := x -1;
when size_of(int) == 8 {
k = k | (k >> 32);
}
k = k | (k >> 16);
k = k | (k >> 8);
k = k | (k >> 4);
k = k | (k >> 2);
k = k | (k >> 1);
k += 1 + int(x <= 0);
return k;
}
sum :: proc(x: $T/[]$E) -> (res: E)
where intrinsics.BuiltinProc_type_is_numeric(E) {
for i in x {
res += i;
}
return;
}
prod :: proc(x: $T/[]$E) -> (res: E)
where intrinsics.BuiltinProc_type_is_numeric(E) {
for i in x {
res *= i;
}
return;
}
cumsum_inplace :: proc(x: $T/[]$E) -> T
where intrinsics.BuiltinProc_type_is_numeric(E) {
for i in 1..<len(x) {
x[i] = x[i-1] + x[i];
}
}
length :: proc(v: $T/[$N]$E) -> E { return sqrt(dot(v, v)); }
norm :: proc(v: $T/[$N]$E) -> T { return v / length(v); }
norm0 :: proc(v: $T/[$N]$E) -> T {
m := length(v);
return m == 0 ? 0 : v/m;
}
identity :: proc($T: typeid/[$N][N]$E) -> T {
m: T;
for i in 0..N-1 do m[i][i] = E(1);
return m;
}
transpose :: proc(m: $M/[$N][N]f32) -> M {
for j in 0..N-1 {
for i in 0..N-1 {
m[i][j], m[j][i] = m[j][i], m[i][j];
cumsum :: proc(dst, src: $T/[]$E) -> T
where intrinsics.BuiltinProc_type_is_numeric(E) {
N := min(len(dst), len(src));
if N > 0 {
dst[0] = src[0];
for i in 1..<N {
dst[i] = dst[i-1] + src[i];
}
}
return m;
return dst[:N];
}
mat3_mul :: proc(a, b: Mat3) -> Mat3 {
c: Mat3;
for j in 0..2 {
for i in 0..2 {
c[j][i] = a[0][i]*b[j][0] +
a[1][i]*b[j][1] +
a[2][i]*b[j][2];
}
}
return c;
}
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
c: Mat4;
for j in 0..3 {
for i in 0..3 {
c[j][i] = a[0][i]*b[j][0] +
a[1][i]*b[j][1] +
a[2][i]*b[j][2] +
a[3][i]*b[j][3];
}
}
return c;
}
mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
return Vec4{
m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3],
m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3],
m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3],
m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3],
};
}
mat4_inverse :: proc(m: Mat4) -> Mat4 {
o: Mat4;
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
ood := 1.0 / (m[0][0] * o[0][0] +
m[0][1] * o[0][1] +
m[0][2] * o[0][2] +
m[0][3] * o[0][3]);
o[0][0] *= ood;
o[0][1] *= ood;
o[0][2] *= ood;
o[0][3] *= ood;
o[1][0] *= ood;
o[1][1] *= ood;
o[1][2] *= ood;
o[1][3] *= ood;
o[2][0] *= ood;
o[2][1] *= ood;
o[2][2] *= ood;
o[2][3] *= ood;
o[3][0] *= ood;
o[3][1] *= ood;
o[3][2] *= ood;
o[3][3] *= ood;
return o;
}
mat4_translate :: proc(v: Vec3) -> Mat4 {
m := identity(Mat4);
m[3][0] = v[0];
m[3][1] = v[1];
m[3][2] = v[2];
m[3][3] = 1;
return m;
}
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
c := cos(angle_radians);
s := sin(angle_radians);
a := norm(v);
t := a * (1-c);
rot := identity(Mat4);
rot[0][0] = c + t[0]*a[0];
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
rot[0][3] = 0;
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
rot[1][1] = c + t[1]*a[1];
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
rot[1][3] = 0;
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
rot[2][2] = c + t[2]*a[2];
rot[2][3] = 0;
return rot;
}
scale_vec3 :: proc(m: Mat4, v: Vec3) -> Mat4 {
m[0][0] *= v[0];
m[1][1] *= v[1];
m[2][2] *= v[2];
return m;
}
scale_f32 :: proc(m: Mat4, s: f32) -> Mat4 {
m[0][0] *= s;
m[1][1] *= s;
m[2][2] *= s;
return m;
}
scale :: proc[scale_vec3, scale_f32];
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
f := norm(centre - eye);
s := norm(cross(f, up));
u := cross(s, f);
return Mat4{
{+s.x, +u.x, -f.x, 0},
{+s.y, +u.y, -f.y, 0},
{+s.z, +u.z, -f.z, 0},
{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
};
}
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
m: Mat4;
tan_half_fovy := tan(0.5 * fovy);
m[0][0] = 1.0 / (aspect*tan_half_fovy);
m[1][1] = 1.0 / (tan_half_fovy);
m[2][2] = -(far + near) / (far - near);
m[2][3] = -1.0;
m[3][2] = -2.0*far*near / (far - near);
return m;
}
ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
m := identity(Mat4);
m[0][0] = +2.0 / (right - left);
m[1][1] = +2.0 / (top - bottom);
m[2][2] = -2.0 / (far - near);
m[3][0] = -(right + left) / (right - left);
m[3][1] = -(top + bottom) / (top - bottom);
m[3][2] = -(far + near) / (far - near);
return m;
}
// Quaternion operations
conj :: proc(q: Quat) -> Quat {
return Quat{-q.x, -q.y, -q.z, q.w};
}
quat_mul :: proc(q0, q1: Quat) -> Quat {
d: Quat;
d.x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y;
d.y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x;
d.z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w;
d.w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z;
return d;
}
quat_mulf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x*f, q.y*f, q.z*f, q.w*f}; }
quat_divf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x/f, q.y/f, q.z/f, q.w/f}; }
quat_div :: proc(q0, q1: Quat) -> Quat { return mul(q0, quat_inverse(q1)); }
quat_inverse :: proc(q: Quat) -> Quat { return div(conj(q), dot(q, q)); }
quat_dot :: proc(q0, q1: Quat) -> f32 { return q0.x*q1.x + q0.y*q1.y + q0.z*q1.z + q0.w*q1.w; }
quat_norm :: proc(q: Quat) -> Quat {
m := sqrt(dot(q, q));
return div(q, m);
}
axis_angle :: proc(axis: Vec3, angle_radians: f32) -> Quat {
v := norm(axis) * sin(0.5*angle_radians);
w := cos(0.5*angle_radians);
return Quat{v.x, v.y, v.z, w};
}
euler_angles :: proc(pitch, yaw, roll: f32) -> Quat {
p := axis_angle(Vec3{1, 0, 0}, pitch);
y := axis_angle(Vec3{0, 1, 0}, yaw);
r := axis_angle(Vec3{0, 0, 1}, roll);
return mul(mul(y, p), r);
}
quat_to_mat4 :: proc(q: Quat) -> Mat4 {
a := quat_norm(q);
xx := a.x*a.x; yy := a.y*a.y; zz := a.z*a.z;
xy := a.x*a.y; xz := a.x*a.z; yz := a.y*a.z;
wx := a.w*a.x; wy := a.w*a.y; wz := a.w*a.z;
m := identity(Mat4);
m[0][0] = 1 - 2*(yy + zz);
m[0][1] = 2*(xy + wz);
m[0][2] = 2*(xz - wy);
m[1][0] = 2*(xy - wz);
m[1][1] = 1 - 2*(xx + zz);
m[1][2] = 2*(yz + wx);
m[2][0] = 2*(xz + wy);
m[2][1] = 2*(yz - wx);
m[2][2] = 1 - 2*(xx + yy);
return m;
}
F32_DIG :: 6;
F32_EPSILON :: 1.192092896e-07;
+146
View File
@@ -0,0 +1,146 @@
package rand
import "core:math"
//
// Normal distribution
//
// "The Ziggurat Method for Generating Random Variables"
// Authors: George Marsaglia, Wai Wan Tsang
// Submitted: 2000-04-15. Published: 2000-10-02.
// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
// https://www.jstatsoft.org/article/view/v005i08 [web page]
//
// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
// with a standard normal distribution with a mean of 0 and standard deviation of 1.
//
// sample = norm_float64() * std_dev + mean
//
norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
rn :: 3.442619855899;
@(static)
kn := [128]u32{
0x76ad2212, 0x00000000, 0x600f1b53, 0x6ce447a6, 0x725b46a2,
0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,
0x7adf629f, 0x7b5682a6, 0x7bb8a8c6, 0x7c0ae722, 0x7c50cce7,
0x7c8cec5b, 0x7cc12cd6, 0x7ceefed2, 0x7d177e0b, 0x7d3b8883,
0x7d5bce6c, 0x7d78dd64, 0x7d932886, 0x7dab0e57, 0x7dc0dd30,
0x7dd4d688, 0x7de73185, 0x7df81cea, 0x7e07c0a3, 0x7e163efa,
0x7e23b587, 0x7e303dfd, 0x7e3beec2, 0x7e46db77, 0x7e51155d,
0x7e5aabb3, 0x7e63abf7, 0x7e6c222c, 0x7e741906, 0x7e7b9a18,
0x7e82adfa, 0x7e895c63, 0x7e8fac4b, 0x7e95a3fb, 0x7e9b4924,
0x7ea0a0ef, 0x7ea5b00d, 0x7eaa7ac3, 0x7eaf04f3, 0x7eb3522a,
0x7eb765a5, 0x7ebb4259, 0x7ebeeafd, 0x7ec2620a, 0x7ec5a9c4,
0x7ec8c441, 0x7ecbb365, 0x7ece78ed, 0x7ed11671, 0x7ed38d62,
0x7ed5df12, 0x7ed80cb4, 0x7eda175c, 0x7edc0005, 0x7eddc78e,
0x7edf6ebf, 0x7ee0f647, 0x7ee25ebe, 0x7ee3a8a9, 0x7ee4d473,
0x7ee5e276, 0x7ee6d2f5, 0x7ee7a620, 0x7ee85c10, 0x7ee8f4cd,
0x7ee97047, 0x7ee9ce59, 0x7eea0eca, 0x7eea3147, 0x7eea3568,
0x7eea1aab, 0x7ee9e071, 0x7ee98602, 0x7ee90a88, 0x7ee86d08,
0x7ee7ac6a, 0x7ee6c769, 0x7ee5bc9c, 0x7ee48a67, 0x7ee32efc,
0x7ee1a857, 0x7edff42f, 0x7ede0ffa, 0x7edbf8d9, 0x7ed9ab94,
0x7ed7248d, 0x7ed45fae, 0x7ed1585c, 0x7ece095f, 0x7eca6ccb,
0x7ec67be2, 0x7ec22eee, 0x7ebd7d1a, 0x7eb85c35, 0x7eb2c075,
0x7eac9c20, 0x7ea5df27, 0x7e9e769f, 0x7e964c16, 0x7e8d44ba,
0x7e834033, 0x7e781728, 0x7e6b9933, 0x7e5d8a1a, 0x7e4d9ded,
0x7e3b737a, 0x7e268c2f, 0x7e0e3ff5, 0x7df1aa5d, 0x7dcf8c72,
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
0x7ba90bdc, 0x7a722176, 0x77d664e5,
};
@(static)
wn := [128]f32{
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,
2.9073963e-10, 3.042997e-10, 3.1699796e-10, 3.289802e-10,
3.4035738e-10, 3.5121603e-10, 3.616251e-10, 3.7164058e-10,
3.8130857e-10, 3.9066758e-10, 3.9975012e-10, 4.08584e-10,
4.1719309e-10, 4.2559822e-10, 4.338176e-10, 4.418672e-10,
4.497613e-10, 4.5751258e-10, 4.651324e-10, 4.7263105e-10,
4.8001775e-10, 4.87301e-10, 4.944885e-10, 5.015873e-10,
5.0860405e-10, 5.155446e-10, 5.2241467e-10, 5.2921934e-10,
5.359635e-10, 5.426517e-10, 5.4928817e-10, 5.5587696e-10,
5.624219e-10, 5.6892646e-10, 5.753941e-10, 5.818282e-10,
5.882317e-10, 5.946077e-10, 6.00959e-10, 6.072884e-10,
6.135985e-10, 6.19892e-10, 6.2617134e-10, 6.3243905e-10,
6.386974e-10, 6.449488e-10, 6.511956e-10, 6.5744005e-10,
6.6368433e-10, 6.699307e-10, 6.7618144e-10, 6.824387e-10,
6.8870465e-10, 6.949815e-10, 7.012715e-10, 7.075768e-10,
7.1389966e-10, 7.202424e-10, 7.266073e-10, 7.329966e-10,
7.394128e-10, 7.4585826e-10, 7.5233547e-10, 7.58847e-10,
7.653954e-10, 7.719835e-10, 7.7861395e-10, 7.852897e-10,
7.920138e-10, 7.987892e-10, 8.0561924e-10, 8.125073e-10,
8.194569e-10, 8.2647167e-10, 8.3355556e-10, 8.407127e-10,
8.479473e-10, 8.55264e-10, 8.6266755e-10, 8.7016316e-10,
8.777562e-10, 8.8545243e-10, 8.932582e-10, 9.0117996e-10,
9.09225e-10, 9.174008e-10, 9.2571584e-10, 9.341788e-10,
9.427997e-10, 9.515889e-10, 9.605579e-10, 9.697193e-10,
9.790869e-10, 9.88676e-10, 9.985036e-10, 1.0085882e-09,
1.0189509e-09, 1.0296151e-09, 1.0406069e-09, 1.0519566e-09,
1.063698e-09, 1.0758702e-09, 1.0885183e-09, 1.1016947e-09,
1.1154611e-09, 1.1298902e-09, 1.1450696e-09, 1.1611052e-09,
1.1781276e-09, 1.1962995e-09, 1.2158287e-09, 1.2369856e-09,
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
};
@(static)
fn := [128]f32{
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
0.87324303, 0.8555006, 0.8387836, 0.8229072, 0.8077383,
0.793177, 0.7791461, 0.7655842, 0.7524416, 0.73967725,
0.7272569, 0.7151515, 0.7033361, 0.69178915, 0.68049186,
0.6694277, 0.658582, 0.6479418, 0.63749546, 0.6272325,
0.6171434, 0.6072195, 0.5974532, 0.58783704, 0.5783647,
0.56903, 0.5598274, 0.5507518, 0.54179835, 0.5329627,
0.52424055, 0.5156282, 0.50712204, 0.49871865, 0.49041483,
0.48220766, 0.4740943, 0.46607214, 0.4581387, 0.45029163,
0.44252872, 0.43484783, 0.427247, 0.41972435, 0.41227803,
0.40490642, 0.39760786, 0.3903808, 0.3832238, 0.37613547,
0.36911446, 0.3621595, 0.35526937, 0.34844297, 0.34167916,
0.33497685, 0.3283351, 0.3217529, 0.3152294, 0.30876362,
0.30235484, 0.29600215, 0.28970486, 0.2834622, 0.2772735,
0.27113807, 0.2650553, 0.25902456, 0.2530453, 0.24711695,
0.241239, 0.23541094, 0.22963232, 0.2239027, 0.21822165,
0.21258877, 0.20700371, 0.20146611, 0.19597565, 0.19053204,
0.18513499, 0.17978427, 0.17447963, 0.1692209, 0.16400786,
0.15884037, 0.15371831, 0.14864157, 0.14361008, 0.13862377,
0.13368265, 0.12878671, 0.12393598, 0.119130544, 0.11437051,
0.10965602, 0.104987256, 0.10036444, 0.095787846, 0.0912578,
0.08677467, 0.0823389, 0.077950984, 0.073611505, 0.06932112,
0.06508058, 0.06089077, 0.056752663, 0.0526674, 0.048636295,
0.044660863, 0.040742867, 0.03688439, 0.033087887, 0.029356318,
0.025693292, 0.022103304, 0.018592102, 0.015167298, 0.011839478,
0.008624485, 0.005548995, 0.0026696292,
};
for {
j := i32(uint32(r));
i := j & 0x7f;
x := f64(j) * f64(wn[i]);
if u32(abs(j)) < kn[i] {
// 99% of the time this will be hit
return x;
}
if i == 0 {
for {
x = -math.ln(float64(r)) * (1.0/ rn);
y := -math.ln(float64(r));
if y+y >= x*x {
break;
}
}
return j > 0 ? rn + x : -rn - x;
}
if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
return x;
}
}
return 0; // NOTE(bill): Will never be hit but this is here for sanity's sake
}
+77 -9
View File
@@ -5,7 +5,25 @@ Rand :: struct {
inc: u64,
}
init :: proc(r: ^Rand, seed: u64 = 8675309) {
@(private, static)
_GLOBAL_SEED_DATA := 1234567890;
@(private, static)
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
@(private, static)
global_rand_ptr := &global_rand;
set_global_seed :: proc(seed: u64) {
init(global_rand_ptr, seed);
}
create :: proc(seed: u64) -> Rand {
r: Rand;
init(&r, seed);
return r;
}
init :: proc(r: ^Rand, seed: u64) {
r.state = 0;
r.inc = (seed << 1) | 1;
_random(r);
@@ -21,18 +39,18 @@ _random :: proc(r: ^Rand) -> u32 {
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
}
uint32 :: proc(r: ^Rand) -> u32 { return _random(r); }
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
uint64 :: proc(r: ^Rand) -> u64 {
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
a := u64(_random(r));
b := u64(_random(r));
return (a<<32) | b;
}
int31 :: proc(r: ^Rand) -> i32 { return i32(uint32(r) << 1 >> 1); }
int63 :: proc(r: ^Rand) -> i64 { return i64(uint64(r) << 1 >> 1); }
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
int31_max :: proc(r: ^Rand, n: i32) -> i32 {
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
if n <= 0 do panic("Invalid argument to int31_max");
if n&(n-1) == 0 {
return int31(r) & (n-1);
@@ -45,7 +63,7 @@ int31_max :: proc(r: ^Rand, n: i32) -> i32 {
return v % n;
}
int63_max :: proc(r: ^Rand, n: i64) -> i64 {
int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
if n <= 0 do panic("Invalid argument to int63_max");
if n&(n-1) == 0 {
return int63(r) & (n-1);
@@ -58,5 +76,55 @@ int63_max :: proc(r: ^Rand, n: i64) -> i64 {
return v % n;
}
float64 :: proc(r: ^Rand) -> f64 { return f64(int63_max(r, 1<<53)) / (1 << 53); }
float32 :: proc(r: ^Rand) -> f32 { return f32(float64(r)); }
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
if n <= 0 do panic("Invalid argument to int_max");
when size_of(int) == 4 {
return int(int31_max(i32(n), r));
} else {
return int(int63_max(i64(n), r));
}
}
float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53); }
float32 :: proc(r: ^Rand = global_rand_ptr) -> f32 { return f32(float64(r)); }
float64_range :: proc(lo, hi: f64, r: ^Rand = global_rand_ptr) -> f64 { return (hi-lo)*float64(r) + lo; }
float32_range :: proc(lo, hi: f32, r: ^Rand = global_rand_ptr) -> f32 { return (hi-lo)*float32(r) + lo; }
read :: proc(p: []byte, r: ^Rand = global_rand_ptr) -> (n: int) {
pos := i8(0);
val := i64(0);
for n = 0; n < len(p); n += 1 {
if pos == 0 {
val = int63(r);
pos = 7;
}
p[n] = byte(val);
val >>= 8;
pos -= 1;
}
return;
}
// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
perm :: proc(n: int, r: ^Rand = global_rand_ptr) -> []int {
m := make([]int, n);
for i := 0; i < n; i += 1 {
j := int_max(i+1);
m[i] = m[j];
m[j] = i;
}
return m;
}
shuffle :: proc(array: $T/[]$E, r: ^Rand = global_rand_ptr) {
n := i64(len(array));
if n < 2 do return;
for i := i64(0); i < n; i += 1 {
j := int63_max(n, r);
array[i], array[j] = array[j], array[i];
}
}
+4 -279
View File
@@ -74,13 +74,13 @@ delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
}
delete :: proc[
delete :: proc{
delete_string,
delete_cstring,
delete_dynamic_array,
delete_slice,
delete_map,
];
};
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
@@ -122,13 +122,13 @@ make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := con
return m;
}
make :: proc[
make :: proc{
make_slice,
make_dynamic_array,
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
];
};
@@ -150,278 +150,3 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
return new_memory;
}
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
return nil;
}
nil_allocator :: proc() -> Allocator {
return Allocator{
procedure = nil_allocator_proc,
data = nil,
};
}
Scratch_Allocator :: struct {
data: []byte,
curr_offset: int,
prev_offset: int,
backup_allocator: Allocator,
leaked_allocations: [dynamic]rawptr,
}
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
scratch.data = data;
scratch.curr_offset = 0;
scratch.prev_offset = 0;
scratch.backup_allocator = backup_allocator;
}
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
scratch := (^Scratch_Allocator)(allocator_data);
if scratch.data == nil {
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
scratch_allocator_init(scratch, make([]byte, 1<<22));
}
switch mode {
case Allocator_Mode.Alloc:
switch {
case scratch.curr_offset+size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
case size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
}
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
a := scratch.backup_allocator;
if a.procedure == nil {
a = context.allocator;
scratch.backup_allocator = a;
}
ptr := alloc(size, alignment, a, loc);
if scratch.leaked_allocations == nil {
scratch.leaked_allocations = make([dynamic]rawptr, a);
}
append(&scratch.leaked_allocations, ptr);
return ptr;
case Allocator_Mode.Free:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr {
size := scratch.curr_offset - scratch.prev_offset;
scratch.curr_offset = scratch.prev_offset;
zero(last_ptr, size);
return nil;
}
// NOTE(bill): It's scratch memory, don't worry about freeing
case Allocator_Mode.Free_All:
scratch.curr_offset = 0;
scratch.prev_offset = 0;
for ptr in scratch.leaked_allocations {
free(ptr, scratch.backup_allocator);
}
clear(&scratch.leaked_allocations);
case Allocator_Mode.Resize:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
scratch.curr_offset = scratch.prev_offset+size;
return old_memory;
}
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
}
return nil;
}
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
return Allocator{
procedure = scratch_allocator_proc,
data = scratch,
};
}
Pool :: struct {
block_size: int,
out_band_size: int,
alignment: int,
unused_blocks: [dynamic]rawptr,
used_blocks: [dynamic]rawptr,
out_band_allocations: [dynamic]rawptr,
current_block: rawptr,
current_pos: rawptr,
bytes_left: int,
block_allocator: Allocator,
}
POOL_BLOCK_SIZE_DEFAULT :: 65536;
POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
pool := (^Pool)(allocator_data);
switch mode {
case Allocator_Mode.Alloc:
return pool_alloc(pool, size);
case Allocator_Mode.Free:
panic("Allocator_Mode.Free is not supported for a pool");
case Allocator_Mode.Free_All:
pool_free_all(pool);
case Allocator_Mode.Resize:
panic("Allocator_Mode.Resize is not supported for a pool");
if old_size >= size {
return old_memory;
}
ptr := pool_alloc(pool, size);
copy(ptr, old_memory, old_size);
return ptr;
}
return nil;
}
pool_allocator :: proc(pool: ^Pool) -> Allocator {
return Allocator{
procedure = pool_allocator_proc,
data = pool,
};
}
pool_init :: proc(pool: ^Pool,
block_allocator := Allocator{} , array_allocator := Allocator{},
block_size := POOL_BLOCK_SIZE_DEFAULT, out_band_size := POOL_OUT_OF_BAND_SIZE_DEFAULT,
alignment := 8) {
pool.block_size = block_size;
pool.out_band_size = out_band_size;
pool.alignment = alignment;
if block_allocator.procedure == nil {
block_allocator = context.allocator;
}
if array_allocator.procedure == nil {
array_allocator = context.allocator;
}
pool.block_allocator = block_allocator;
pool.out_band_allocations.allocator = array_allocator;
pool. unused_blocks.allocator = array_allocator;
pool. used_blocks.allocator = array_allocator;
}
pool_destroy :: proc(using pool: ^Pool) {
pool_free_all(pool);
delete(unused_blocks);
delete(used_blocks);
zero(pool, size_of(pool^));
}
pool_alloc :: proc(using pool: ^Pool, bytes: int) -> rawptr {
cycle_new_block :: proc(using pool: ^Pool) {
if block_allocator.procedure == nil {
panic("You must call pool_init on a Pool before using it");
}
if current_block != nil {
append(&used_blocks, current_block);
}
new_block: rawptr;
if len(unused_blocks) > 0 {
new_block = pop(&unused_blocks);
} else {
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
}
bytes_left = block_size;
current_pos = new_block;
current_block = new_block;
}
extra := alignment - (bytes % alignment);
bytes += extra;
if bytes >= out_band_size {
assert(block_allocator.procedure != nil);
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
if memory != nil {
append(&out_band_allocations, (^byte)(memory));
}
return memory;
}
if bytes_left < bytes {
cycle_new_block(pool);
if current_block == nil {
return nil;
}
}
memory := current_pos;
current_pos = ptr_offset((^byte)(current_pos), bytes);
bytes_left -= bytes;
return memory;
}
pool_reset :: proc(using pool: ^Pool) {
if current_block != nil {
append(&unused_blocks, current_block);
current_block = nil;
}
for block in used_blocks {
append(&unused_blocks, block);
}
clear(&used_blocks);
for a in out_band_allocations {
free(a, block_allocator);
}
clear(&out_band_allocations);
}
pool_free_all :: proc(using pool: ^Pool) {
pool_reset(pool);
for block in unused_blocks {
free(block, block_allocator);
}
clear(&unused_blocks);
}
+621
View File
@@ -0,0 +1,621 @@
package mem
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
return nil;
}
nil_allocator :: proc() -> Allocator {
return Allocator{
procedure = nil_allocator_proc,
data = nil,
};
}
// Custom allocators
Arena :: struct {
data: []byte,
offset: int,
peak_used: int,
temp_count: int,
}
Arena_Temp_Memory :: struct {
arena: ^Arena,
prev_offset: int,
}
init_arena :: proc(a: ^Arena, data: []byte) {
a.data = data;
a.offset = 0;
a.peak_used = 0;
a.temp_count = 0;
}
arena_allocator :: proc(arena: ^Arena) -> Allocator {
return Allocator{
procedure = arena_allocator_proc,
data = arena,
};
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
arena := cast(^Arena)allocator_data;
switch mode {
case .Alloc:
total_size := size + alignment;
if arena.offset + total_size > len(arena.data) {
return nil;
}
#no_bounds_check end := &arena.data[len(arena.data)];
ptr := align_forward(end, uintptr(alignment));
arena.offset += total_size;
arena.peak_used = max(arena.peak_used, arena.offset);
return zero(ptr, size);
case .Free:
// NOTE(bill): Free all at once
// Use Arena_Temp_Memory if you want to free a block
case .Free_All:
arena.offset = 0;
case .Resize:
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
}
return nil;
}
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory;
tmp.arena = a;
tmp.prev_offset = a.offset;
a.temp_count += 1;
return tmp;
}
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(arena.offset >= prev_offset);
assert(arena.temp_count > 0);
arena.offset = prev_offset;
arena.temp_count -= 1;
}
Scratch_Allocator :: struct {
data: []byte,
curr_offset: int,
prev_offset: int,
backup_allocator: Allocator,
leaked_allocations: [dynamic]rawptr,
}
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
scratch.data = data;
scratch.curr_offset = 0;
scratch.prev_offset = 0;
scratch.backup_allocator = backup_allocator;
}
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
scratch := (^Scratch_Allocator)(allocator_data);
if scratch.data == nil {
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
scratch_allocator_init(scratch, make([]byte, 1<<22));
}
switch mode {
case Allocator_Mode.Alloc:
switch {
case scratch.curr_offset+size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
case size <= len(scratch.data):
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
ptr := &scratch.data[offset];
zero(ptr, size);
scratch.prev_offset = int(offset);
scratch.curr_offset = int(offset) + size;
return ptr;
}
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
a := scratch.backup_allocator;
if a.procedure == nil {
a = context.allocator;
scratch.backup_allocator = a;
}
ptr := alloc(size, alignment, a, loc);
if scratch.leaked_allocations == nil {
scratch.leaked_allocations = make([dynamic]rawptr, a);
}
append(&scratch.leaked_allocations, ptr);
return ptr;
case Allocator_Mode.Free:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr {
full_size := scratch.curr_offset - scratch.prev_offset;
scratch.curr_offset = scratch.prev_offset;
zero(last_ptr, full_size);
return nil;
}
// NOTE(bill): It's scratch memory, don't worry about freeing
case Allocator_Mode.Free_All:
scratch.curr_offset = 0;
scratch.prev_offset = 0;
for ptr in scratch.leaked_allocations {
free(ptr, scratch.backup_allocator);
}
clear(&scratch.leaked_allocations);
case Allocator_Mode.Resize:
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
scratch.curr_offset = scratch.prev_offset+size;
return old_memory;
}
return scratch_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
}
return nil;
}
scratch_allocator :: proc(scratch: ^Scratch_Allocator) -> Allocator {
return Allocator{
procedure = scratch_allocator_proc,
data = scratch,
};
}
Stack_Allocation_Header :: struct {
prev_offset: int,
padding: int,
}
// Stack is a stack-like allocator which has a strict memory freeing order
Stack :: struct {
data: []byte,
prev_offset: int,
curr_offset: int,
peak_used: int,
}
init_stack :: proc(s: ^Stack, data: []byte) {
s.data = data;
s.prev_offset = 0;
s.curr_offset = 0;
s.peak_used = 0;
}
stack_allocator :: proc(stack: ^Stack) -> Allocator {
return Allocator{
procedure = stack_allocator_proc,
data = stack,
};
}
stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
s := cast(^Stack)allocator_data;
if s.data == nil {
return nil;
}
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> rawptr {
curr_addr := uintptr(&s.data[0]) + uintptr(s.curr_offset);
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header));
if s.curr_offset + padding + size > len(s.data) {
return nil;
}
s.prev_offset = s.curr_offset;
s.curr_offset += padding;
next_addr := curr_addr + uintptr(padding);
header := (^Stack_Allocation_Header)(next_addr - size_of(Stack_Allocation_Header));
header.padding = auto_cast padding;
header.prev_offset = auto_cast s.prev_offset;
s.curr_offset += size;
s.peak_used = max(s.peak_used, s.curr_offset);
return zero(rawptr(next_addr), size);
}
switch mode {
case .Alloc:
return raw_alloc(s, size, alignment);
case .Free:
if old_memory == nil {
return nil;
}
start := uintptr(&s.data[0]);
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (free)");
return nil;
}
if curr_addr >= start+uintptr(s.curr_offset) {
// NOTE(bill): Allow double frees
return nil;
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
if old_offset != int(header.prev_offset) {
panic("Out of order stack allocator free");
return nil;
}
s.curr_offset = int(old_offset);
s.prev_offset = int(header.prev_offset);
case .Free_All:
s.prev_offset = 0;
s.curr_offset = 0;
case .Resize:
if old_memory == nil {
return raw_alloc(s, size, alignment);
}
if size == 0 {
return nil;
}
start := uintptr(&s.data[0]);
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (resize)");
return nil;
}
if curr_addr >= start+uintptr(s.curr_offset) {
// NOTE(bill): Allow double frees
return nil;
}
if old_size == size {
return old_memory;
}
header := (^Stack_Allocation_Header)(curr_addr - size_of(Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
if old_offset != int(header.prev_offset) {
ptr := raw_alloc(s, size, alignment);
copy(ptr, old_memory, min(old_size, size));
return ptr;
}
old_memory_size := uintptr(s.curr_offset) - (curr_addr - start);
assert(old_memory_size == uintptr(old_size));
diff := size - old_size;
s.curr_offset += diff; // works for smaller sizes too
if diff > 0 {
zero(rawptr(curr_addr + uintptr(diff)), diff);
}
return old_memory;
}
return nil;
}
Small_Stack_Allocation_Header :: struct {
padding: u8,
}
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
Small_Stack :: struct {
data: []byte,
offset: int,
peak_used: int,
}
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
s.data = data;
s.offset = 0;
s.peak_used = 0;
}
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
return Allocator{
procedure = small_stack_allocator_proc,
data = stack,
};
}
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
s := cast(^Small_Stack)allocator_data;
if s.data == nil {
return nil;
}
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
curr_addr := uintptr(&s.data[0]) + uintptr(s.offset);
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header));
if s.offset + padding + size > len(s.data) {
return nil;
}
s.offset += padding;
next_addr := curr_addr + uintptr(padding);
header := (^Small_Stack_Allocation_Header)(next_addr - size_of(Small_Stack_Allocation_Header));
header.padding = auto_cast padding;
s.offset += size;
s.peak_used = max(s.peak_used, s.offset);
return zero(rawptr(next_addr), size);
}
switch mode {
case .Alloc:
return raw_alloc(s, size, align);
case .Free:
if old_memory == nil {
return nil;
}
start := uintptr(&s.data[0]);
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (free)");
return nil;
}
if curr_addr >= start+uintptr(s.offset) {
// NOTE(bill): Allow double frees
return nil;
}
header := (^Small_Stack_Allocation_Header)(curr_addr - size_of(Small_Stack_Allocation_Header));
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(&s.data[0]));
s.offset = int(old_offset);
case .Free_All:
s.offset = 0;
case .Resize:
if old_memory == nil {
return raw_alloc(s, size, align);
}
if size == 0 {
return nil;
}
start := uintptr(&s.data[0]);
end := start + uintptr(len(s.data));
curr_addr := uintptr(old_memory);
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (resize)");
return nil;
}
if curr_addr >= start+uintptr(s.offset) {
// NOTE(bill): Treat as a double free
return nil;
}
if old_size == size {
return old_memory;
}
ptr := raw_alloc(s, size, align);
copy(ptr, old_memory, min(old_size, size));
return ptr;
}
return nil;
}
Dynamic_Pool :: struct {
block_size: int,
out_band_size: int,
alignment: int,
unused_blocks: [dynamic]rawptr,
used_blocks: [dynamic]rawptr,
out_band_allocations: [dynamic]rawptr,
current_block: rawptr,
current_pos: rawptr,
bytes_left: int,
block_allocator: Allocator,
}
DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: 65536;
DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: 6554;
dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
flags: u64 = 0, loc := #caller_location) -> rawptr {
pool := (^Dynamic_Pool)(allocator_data);
switch mode {
case Allocator_Mode.Alloc:
return dynamic_pool_alloc(pool, size);
case Allocator_Mode.Free:
panic("Allocator_Mode.Free is not supported for a pool");
case Allocator_Mode.Free_All:
dynamic_pool_free_all(pool);
case Allocator_Mode.Resize:
panic("Allocator_Mode.Resize is not supported for a pool");
if old_size >= size {
return old_memory;
}
ptr := dynamic_pool_alloc(pool, size);
copy(ptr, old_memory, old_size);
return ptr;
}
return nil;
}
dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> Allocator {
return Allocator{
procedure = dynamic_pool_allocator_proc,
data = pool,
};
}
dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
block_allocator := context.allocator,
array_allocator := context.allocator,
block_size := DYNAMIC_POOL_BLOCK_SIZE_DEFAULT,
out_band_size := DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT,
alignment := 8) {
pool.block_size = block_size;
pool.out_band_size = out_band_size;
pool.alignment = alignment;
pool.block_allocator = block_allocator;
pool.out_band_allocations.allocator = array_allocator;
pool. unused_blocks.allocator = array_allocator;
pool. used_blocks.allocator = array_allocator;
}
dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_free_all(pool);
delete(unused_blocks);
delete(used_blocks);
zero(pool, size_of(pool^));
}
dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) {
if block_allocator.procedure == nil {
panic("You must call pool_init on a Pool before using it");
}
if current_block != nil {
append(&used_blocks, current_block);
}
new_block: rawptr;
if len(unused_blocks) > 0 {
new_block = pop(&unused_blocks);
} else {
new_block = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
}
bytes_left = block_size;
current_pos = new_block;
current_block = new_block;
}
n := bytes;
extra := alignment - (n % alignment);
n += extra;
if n >= out_band_size {
assert(block_allocator.procedure != nil);
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0);
if memory != nil {
append(&out_band_allocations, (^byte)(memory));
}
return memory;
}
if bytes_left < n {
cycle_new_block(pool);
if current_block == nil {
return nil;
}
}
memory := current_pos;
current_pos = ptr_offset((^byte)(current_pos), n);
bytes_left -= n;
return memory;
}
dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
if current_block != nil {
append(&unused_blocks, current_block);
current_block = nil;
}
for block in used_blocks {
append(&unused_blocks, block);
}
clear(&used_blocks);
for a in out_band_allocations {
free(a, block_allocator);
}
clear(&out_band_allocations);
}
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_reset(pool);
for block in unused_blocks {
free(block, block_allocator);
}
clear(&unused_blocks);
}
+88 -136
View File
@@ -5,7 +5,7 @@ foreign _ {
@(link_name = "llvm.bswap.i32") swap32 :: proc(b: u32) -> u32 ---;
@(link_name = "llvm.bswap.i64") swap64 :: proc(b: u64) -> u64 ---;
}
swap :: proc[swap16, swap32, swap64];
swap :: proc{swap16, swap32, swap64};
@@ -24,9 +24,12 @@ set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
llvm_memset(data, byte(value), len, 1, false);
return data;
}
zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
zero :: inline proc "contextless" (data: rawptr, len: int) -> rawptr {
return set(data, 0, len);
}
zero_item :: inline proc "contextless" (item: $P/^$T) {
set(item, 0, size_of(T));
}
zero_slice :: proc "contextless" (data: $T/[]$E) {
if n := len(data); n > 0 {
zero(&data[0], size_of(E)*n);
@@ -64,15 +67,40 @@ copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawpt
llvm_memcpy(dst, src, len, 1, false);
return dst;
}
compare :: proc "contextless" (a, b: []byte) -> int {
compare :: inline proc "contextless" (a, b: []byte) -> int {
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
}
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int {
pa :: ptr_offset;
for i in 0..n-1 do switch {
case pa(a, i)^ < pa(b, i)^: return -1;
case pa(a, i)^ > pa(b, i)^: return +1;
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
x := slice_ptr(a, n);
y := slice_ptr(b, n);
SU :: size_of(uintptr);
fast := n/SU + 1;
offset := (fast-1)*SU;
curr_block := 0;
if n < SU {
fast = 0;
}
la := slice_ptr((^uintptr)(a), fast);
lb := slice_ptr((^uintptr)(b), fast);
for /**/; curr_block < fast; curr_block += 1 {
if la[curr_block] ~ lb[curr_block] != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
if x[pos] ~ y[pos] != 0 {
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
if x[offset] ~ y[offset] != 0 {
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1;
}
}
return 0;
}
@@ -80,29 +108,39 @@ compare_ptrs :: inline proc "contextless" (a, b: rawptr, n: int) -> int {
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
}
ptr_offset :: proc "contextless" (ptr: $P/^$T, n: int) -> P {
ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
new := int(uintptr(ptr)) + size_of(T)*n;
return P(uintptr(new));
}
ptr_sub :: proc "contextless" (a, b: $P/^$T) -> int {
ptr_sub :: inline proc "contextless" (a, b: $P/^$T) -> int {
return (int(uintptr(a)) - int(uintptr(b)))/size_of(T);
}
slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
slice_ptr :: inline proc "contextless" (ptr: ^$T, len: int) -> []T {
assert(len >= 0);
slice := Raw_Slice{data = ptr, len = len};
return transmute([]T)slice;
}
slice_to_bytes :: proc "contextless" (slice: $E/[]$T) -> []byte {
slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
s := transmute(Raw_Slice)slice;
s.len *= size_of(T);
return transmute([]byte)s;
}
slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
when size_of(A) == 0 || size_of(B) == 0 {
return nil;
} else {
s := transmute(Raw_Slice)slice;
s.len = (len(slice) * size_of(B)) / size_of(A);
return transmute(T)s;
}
}
buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
s := transmute(Raw_Slice)backing;
d := Raw_Dynamic_Array{
data = s.data,
@@ -113,12 +151,12 @@ buffer_from_slice :: proc(backing: $T/[]$E) -> [dynamic]E {
return transmute([dynamic]E)d;
}
ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
ptr_to_bytes :: inline proc "contextless" (ptr: ^$T, len := 1) -> []byte {
assert(len >= 0);
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)};
}
any_to_bytes :: proc "contextless" (val: any) -> []byte {
any_to_bytes :: inline proc "contextless" (val: any) -> []byte {
ti := type_info_of(val.id);
size := ti != nil ? ti.size : 0;
return transmute([]byte)Raw_Slice{val.data, size};
@@ -130,7 +168,7 @@ megabytes :: inline proc "contextless" (x: int) -> int do return kilobytes(x) *
gigabytes :: inline proc "contextless" (x: int) -> int do return megabytes(x) * 1024;
terabytes :: inline proc "contextless" (x: int) -> int do return gigabytes(x) * 1024;
is_power_of_two :: proc(x: uintptr) -> bool {
is_power_of_two :: inline proc(x: uintptr) -> bool {
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
@@ -156,25 +194,19 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
}
AllocationHeader :: struct {size: int};
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
header.size = size;
ptr := cast(^uint)(ptr_offset(header, 1));
n := ptr_sub(cast(^uint)data, ptr);
for i in 0..n-1 {
ptr_offset(ptr, i)^ = ~uint(0);
}
align_forward_int :: inline proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
if data == nil do return nil;
p := cast(^uint)data;
for ptr_offset(p, -1)^ == ~uint(0) do p = ptr_offset(p, -1);
return (^AllocationHeader)(ptr_offset(p, -1));
align_forward_uint :: inline proc(ptr, align: uint) -> uint {
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
context.allocator = a;
return context;
}
Fixed_Byte_Buffer :: distinct [dynamic]byte;
@@ -190,111 +222,31 @@ make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
// Custom allocators
Arena :: struct {
backing: Allocator,
memory: Fixed_Byte_Buffer,
temp_count: int,
}
Arena_Temp_Memory :: struct {
arena: ^Arena,
original_count: int,
}
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
backing = Allocator{};
memory = make_fixed_byte_buffer(data);
temp_count = 0;
}
init_arena_from_context :: proc(using a: ^Arena, size: int) {
backing = context.allocator;
memory = make_fixed_byte_buffer(make([]byte, size));
temp_count = 0;
}
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
context.allocator = a;
return context;
}
destroy_arena :: proc(using a: ^Arena) {
if backing.procedure != nil {
context.allocator = backing;
if memory != nil {
free(&memory[0]);
}
memory = nil;
}
}
arena_allocator :: proc(arena: ^Arena) -> Allocator {
return Allocator{
procedure = arena_allocator_proc,
data = arena,
};
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64, location := #caller_location) -> rawptr {
using Allocator_Mode;
arena := cast(^Arena)allocator_data;
switch mode {
case Alloc:
total_size := size + alignment;
if len(arena.memory) + total_size > cap(arena.memory) {
return nil;
}
#no_bounds_check end := &arena.memory[len(arena.memory)];
ptr := align_forward(end, uintptr(alignment));
(^Raw_Slice)(&arena.memory).len += total_size;
return zero(ptr, size);
case Free:
// NOTE(bill): Free all at once
// Use Arena_Temp_Memory if you want to free a block
case Free_All:
(^Raw_Slice)(&arena.memory).len = 0;
case Resize:
return default_resize_align(old_memory, old_size, size, alignment, arena_allocator(arena));
}
return nil;
}
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory;
tmp.arena = a;
tmp.original_count = len(a.memory);
a.temp_count += 1;
return tmp;
}
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(len(arena.memory) >= original_count);
assert(arena.temp_count > 0);
(^Raw_Dynamic_Array)(&arena.memory).len = original_count;
arena.temp_count -= 1;
}
align_formula :: proc(size, align: int) -> int {
result := size + align-1;
return result - result%align;
}
calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int) -> int {
p := uintptr(ptr);
a := uintptr(align);
modulo := p & (a-1);
padding := uintptr(0);
if modulo != 0 do padding = a - modulo;
needed_space := uintptr(header_size);
if padding < needed_space {
needed_space -= padding;
if needed_space & (a-1) > 0 {
padding += align * (1+(needed_space/align));
} else {
padding += align * (needed_space/align);
}
}
return int(padding);
}
+10 -4
View File
@@ -31,21 +31,27 @@ Raw_Map :: struct {
entries: Raw_Dynamic_Array,
}
Raw_Complex64 :: struct {real, imag: f32};
Raw_Complex128 :: struct {real, imag: f64};
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32};
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
make_any :: inline proc(data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id};
}
raw_string_data :: inline proc(s: $T/string) -> ^byte {
return (^Raw_String)(&s).data;
return (transmute(Raw_String)s).data;
}
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
return cast(^E)(^Raw_Slice)(&a).data;
return cast(^E)(transmute(Raw_Slice)a).data;
}
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(^Raw_Dynamic_Array)(&a).data;
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
}
raw_data :: proc[raw_string_data, raw_slice_data, raw_dynamic_array_data];
raw_data :: proc{raw_string_data, raw_slice_data, raw_dynamic_array_data};
+627
View File
@@ -0,0 +1,627 @@
package odin_ast
import "core:odin/tokenizer"
Proc_Tag :: enum {
Bounds_Check,
No_Bounds_Check,
}
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
Proc_Inlining :: enum u32 {
None = 0,
Inline = 1,
No_Inline = 2,
}
Proc_Calling_Convention :: enum i32 {
Invalid = 0,
Odin,
Contextless,
C_Decl,
Std_Call,
Fast_Call,
Foreign_Block_Default = -1,
}
Node_State_Flag :: enum {
Bounds_Check,
No_Bounds_Check,
}
Node_State_Flags :: distinct bit_set[Node_State_Flag];
Comment_Group :: struct {
list: []tokenizer.Token,
}
Node :: struct {
pos: tokenizer.Pos,
end: tokenizer.Pos,
derived: any,
state_flags: Node_State_Flags,
}
Expr :: struct {
using expr_base: Node,
}
Stmt :: struct {
using stmt_base: Node,
}
Decl :: struct {
using decl_base: Stmt,
}
// Expressions
Bad_Expr :: struct {
using node: Expr,
}
Ident :: struct {
using node: Expr,
name: string,
}
Implicit :: struct {
using node: Expr,
tok: tokenizer.Token,
}
Undef :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
}
Basic_Lit :: struct {
using node: Expr,
tok: tokenizer.Token,
}
Basic_Directive :: struct {
using node: Expr,
tok: tokenizer.Token,
name: string,
}
Ellipsis :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
expr: ^Expr,
}
Proc_Lit :: struct {
using node: Expr,
type: ^Proc_Type,
body: ^Stmt,
tags: Proc_Tags,
inlining: Proc_Inlining,
where_token: tokenizer.Token,
where_clauses: []^Expr,
}
Comp_Lit :: struct {
using node: Expr,
type: ^Expr,
open: tokenizer.Pos,
elems: []^Expr,
close: tokenizer.Pos,
}
Tag_Expr :: struct {
using node: Expr,
op: tokenizer.Token,
name: string,
expr: ^Expr,
}
Unary_Expr :: struct {
using node: Expr,
op: tokenizer.Token,
expr: ^Expr,
}
Binary_Expr :: struct {
using node: Expr,
left: ^Expr,
op: tokenizer.Token,
right: ^Expr,
}
Paren_Expr :: struct {
using node: Expr,
open: tokenizer.Pos,
expr: ^Expr,
close: tokenizer.Pos,
}
Selector_Expr :: struct {
using node: Expr,
expr: ^Expr,
field: ^Ident,
}
Implicit_Selector_Expr :: struct {
using node: Expr,
field: ^Ident,
}
Index_Expr :: struct {
using node: Expr,
expr: ^Expr,
open: tokenizer.Pos,
index: ^Expr,
close: tokenizer.Pos,
}
Deref_Expr :: struct {
using node: Expr,
expr: ^Expr,
op: tokenizer.Token,
}
Slice_Expr :: struct {
using node: Expr,
expr: ^Expr,
open: tokenizer.Pos,
low: ^Expr,
interval: tokenizer.Token,
high: ^Expr,
close: tokenizer.Pos,
}
Call_Expr :: struct {
using node: Expr,
inlining: Proc_Inlining,
expr: ^Expr,
open: tokenizer.Pos,
args: []^Expr,
ellipsis: tokenizer.Token,
close: tokenizer.Pos,
}
Field_Value :: struct {
using node: Expr,
field: ^Expr,
sep: tokenizer.Pos,
value: ^Expr,
}
Ternary_Expr :: struct {
using node: Expr,
cond: ^Expr,
op1: tokenizer.Token,
x: ^Expr,
op2: tokenizer.Token,
y: ^Expr,
}
Type_Assertion :: struct {
using node: Expr,
expr: ^Expr,
dot: tokenizer.Pos,
open: tokenizer.Pos,
type: ^Expr,
close: tokenizer.Pos,
}
Type_Cast :: struct {
using node: Expr,
tok: tokenizer.Token,
open: tokenizer.Pos,
type: ^Expr,
close: tokenizer.Pos,
expr: ^Expr,
}
Auto_Cast :: struct {
using node: Expr,
op: tokenizer.Token,
expr: ^Expr,
}
// Statements
Bad_Stmt :: struct {
using node: Stmt,
}
Empty_Stmt :: struct {
using node: Stmt,
semicolon: tokenizer.Pos, // Position of the following ';'
}
Expr_Stmt :: struct {
using node: Stmt,
expr: ^Expr,
}
Tag_Stmt :: struct {
using node: Stmt,
op: tokenizer.Token,
name: string,
stmt: ^Stmt,
}
Assign_Stmt :: struct {
using node: Stmt,
lhs: []^Expr,
op: tokenizer.Token,
rhs: []^Expr,
}
Block_Stmt :: struct {
using node: Stmt,
label: ^Expr,
open: tokenizer.Pos,
stmts: []^Stmt,
close: tokenizer.Pos,
}
If_Stmt :: struct {
using node: Stmt,
label: ^Expr,
if_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
else_stmt: ^Stmt,
}
When_Stmt :: struct {
using node: Stmt,
when_pos: tokenizer.Pos,
cond: ^Expr,
body: ^Stmt,
else_stmt: ^Stmt,
}
Return_Stmt :: struct {
using node: Stmt,
results: []^Expr,
}
Defer_Stmt :: struct {
using node: Stmt,
stmt: ^Stmt,
}
For_Stmt :: struct {
using node: Stmt,
label: ^Expr,
for_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
post: ^Stmt,
body: ^Stmt,
}
Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
in_pos: tokenizer.Pos,
expr: ^Expr,
body: ^Stmt,
}
Case_Clause :: struct {
using node: Stmt,
case_pos: tokenizer.Pos,
list: []^Expr,
terminator: tokenizer.Token,
body: []^Stmt,
}
Switch_Stmt :: struct {
using node: Stmt,
label: ^Expr,
switch_pos: tokenizer.Pos,
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
complete: bool,
}
Type_Switch_Stmt :: struct {
using node: Stmt,
label: ^Expr,
switch_pos: tokenizer.Pos,
tag: ^Stmt,
expr: ^Expr,
body: ^Stmt,
complete: bool,
}
Branch_Stmt :: struct {
using node: Stmt,
tok: tokenizer.Token,
label: ^Ident,
}
Using_Stmt :: struct {
using node: Stmt,
list: []^Expr,
}
// Declarations
Bad_Decl :: struct {
using node: Decl,
}
Value_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
names: []^Expr,
type: ^Expr,
values: []^Expr,
comment: ^Comment_Group,
is_using: bool,
is_mutable: bool,
}
Package_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
token: tokenizer.Token,
name: string,
comment: ^Comment_Group,
}
Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
is_using: bool,
import_tok: tokenizer.Token,
name: tokenizer.Token,
relpath: tokenizer.Token,
fullpath: string,
comment: ^Comment_Group,
}
Foreign_Block_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
tok: tokenizer.Token,
foreign_library: ^Expr,
body: ^Stmt,
}
Foreign_Import_Decl :: struct {
using node: Decl,
docs: ^Comment_Group,
foreign_tok: tokenizer.Token,
import_tok: tokenizer.Token,
name: ^Ident,
collection_name: string,
fullpaths: []string,
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
comment: ^Comment_Group,
}
// Other things
unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
val = expr;
if expr == nil {
return;
}
for {
e, ok := val.derived.(Paren_Expr);
if !ok do break;
val = e.expr;
}
return;
}
Field_Flag :: enum {
Ellipsis,
Using,
No_Alias,
C_Vararg,
Auto_Cast,
In,
Results,
Tags,
Default_Parameters,
Typeid_Token,
}
Field_Flags :: distinct bit_set[Field_Flag];
Field_Flags_Struct :: Field_Flags{
.Using,
.Tags,
};
Field_Flags_Record_Poly_Params :: Field_Flags{
.Typeid_Token,
};
Field_Flags_Signature :: Field_Flags{
.Ellipsis,
.Using,
.No_Alias,
.C_Vararg,
.Auto_Cast,
.Default_Parameters,
};
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
Field_Flags_Signature_Results :: Field_Flags_Signature;
Proc_Group :: struct {
using node: Expr,
tok: tokenizer.Token,
open: tokenizer.Pos,
args: []^Expr,
close: tokenizer.Pos,
}
Attribute :: struct {
using node: Node,
tok: tokenizer.Token_Kind,
open: tokenizer.Pos,
elems: []^Expr,
close: tokenizer.Pos,
}
Field :: struct {
using node: Node,
docs: ^Comment_Group,
names: []^Expr, // Could be polymorphic
type: ^Expr,
default_value: ^Expr,
tag: tokenizer.Token,
flags: Field_Flags,
comment: ^Comment_Group,
}
Field_List :: struct {
using node: Node,
open: tokenizer.Pos,
list: []^Field,
close: tokenizer.Pos,
}
// Types
Typeid_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
specialization: ^Expr,
}
Helper_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Distinct_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Opaque_Type :: struct {
using node: Expr,
tok: tokenizer.Token_Kind,
type: ^Expr,
}
Poly_Type :: struct {
using node: Expr,
dollar: tokenizer.Pos,
type: ^Ident,
specialization: ^Expr,
}
Proc_Type :: struct {
using node: Expr,
tok: tokenizer.Token,
calling_convention: Proc_Calling_Convention,
params: ^Field_List,
arrow: tokenizer.Pos,
results: ^Field_List,
tags: Proc_Tags,
generic: bool,
diverging: bool,
}
Pointer_Type :: struct {
using node: Expr,
pointer: tokenizer.Pos,
elem: ^Expr,
}
Array_Type :: struct {
using node: Expr,
open: tokenizer.Pos,
len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types
close: tokenizer.Pos,
elem: ^Expr,
}
Dynamic_Array_Type :: struct {
using node: Expr,
open: tokenizer.Pos,
dynamic_pos: tokenizer.Pos,
close: tokenizer.Pos,
elem: ^Expr,
}
Struct_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
fields: ^Field_List,
name_count: int,
where_token: tokenizer.Token,
where_clauses: []^Expr,
is_packed: bool,
is_raw_union: bool,
}
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
variants: []^Expr,
where_token: tokenizer.Token,
where_clauses: []^Expr,
}
Enum_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
base_type: ^Expr,
open: tokenizer.Pos,
fields: []^Expr,
close: tokenizer.Pos,
is_using: bool,
}
Bit_Field_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
align: ^Expr,
open: tokenizer.Pos,
fields: []^Field_Value, // Field_Value with ':' rather than '='
close: tokenizer.Pos,
}
Bit_Set_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
open: tokenizer.Pos,
elem: ^Expr,
underlying: ^Expr,
close: tokenizer.Pos,
}
Map_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
key: ^Expr,
value: ^Expr,
}
+314
View File
@@ -0,0 +1,314 @@
package odin_ast
import "core:mem"
import "core:fmt"
import "core:odin/tokenizer"
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n := mem.new(T);
n.pos = pos;
n.end = end;
n.derived = n^;
base: ^Node = n; // dummy check
_ = base; // "Use" type to make -vet happy
return n;
}
clone :: proc{
clone_node,
clone_expr,
clone_stmt,
clone_decl,
clone_array,
clone_dynamic_array,
};
clone_array :: proc(array: $A/[]^$T) -> A {
if len(array) == 0 {
return nil;
}
res := make(A, len(array));
for elem, i in array {
res[i] = auto_cast clone(elem);
}
return res;
}
clone_dynamic_array :: proc(array: $A/[dynamic]^$T) -> A {
if len(array) == 0 {
return nil;
}
res := make(A, len(array));
for elem, i in array {
res[i] = auto_cast clone(elem);
}
return res;
}
clone_expr :: proc(node: ^Expr) -> ^Expr {
return cast(^Expr)clone_node(node);
}
clone_stmt :: proc(node: ^Stmt) -> ^Stmt {
return cast(^Stmt)clone_node(node);
}
clone_decl :: proc(node: ^Decl) -> ^Decl {
return cast(^Decl)clone_node(node);
}
clone_node :: proc(node: ^Node) -> ^Node {
if node == nil {
return nil;
}
size := size_of(Node);
align := align_of(Node);
ti := type_info_of(node.derived.id);
if ti != nil {
size = ti.size;
align = ti.align;
}
res := cast(^Node)mem.alloc(size, align);
src: rawptr = node;
if node.derived != nil {
src = node.derived.data;
}
mem.copy(res, src, size);
res.derived.data = rawptr(res);
switch n in node.derived {
case Bad_Expr:
case Ident:
case Implicit:
case Undef:
case Basic_Lit:
case Ellipsis:
r := cast(^Ellipsis)res;
r.expr = clone(r.expr);
case Proc_Lit:
r := cast(^Proc_Lit)res;
r.type = auto_cast clone(r.type);
r.body = clone(r.body);
case Comp_Lit:
r := cast(^Comp_Lit)res;
r.type = clone(r.type);
r.elems = clone(r.elems);
case Tag_Expr:
r := cast(^Tag_Expr)res;
r.expr = clone(r.expr);
case Unary_Expr:
r := cast(^Unary_Expr)res;
r.expr = clone(r.expr);
case Binary_Expr:
r := cast(^Binary_Expr)res;
r.left = clone(r.left);
r.right = clone(r.right);
case Paren_Expr:
r := cast(^Paren_Expr)res;
r.expr = clone(r.expr);
case Selector_Expr:
r := cast(^Selector_Expr)res;
r.expr = clone(r.expr);
r.field = auto_cast clone(r.field);
case Index_Expr:
r := cast(^Index_Expr)res;
r.expr = clone(r.expr);
r.index = clone(r.index);
case Deref_Expr:
r := cast(^Deref_Expr)res;
r.expr = clone(r.expr);
case Slice_Expr:
r := cast(^Slice_Expr)res;
r.expr = clone(r.expr);
r.low = clone(r.low);
r.high = clone(r.high);
case Call_Expr:
r := cast(^Call_Expr)res;
r.expr = clone(r.expr);
r.args = clone(r.args);
case Field_Value:
r := cast(^Field_Value)res;
r.field = clone(r.field);
r.value = clone(r.value);
case Ternary_Expr:
r := cast(^Ternary_Expr)res;
r.cond = clone(r.cond);
r.x = clone(r.x);
r.y = clone(r.y);
case Type_Assertion:
r := cast(^Type_Assertion)res;
r.expr = clone(r.expr);
r.type = clone(r.type);
case Type_Cast:
r := cast(^Type_Cast)res;
r.type = clone(r.type);
r.expr = clone(r.expr);
case Auto_Cast:
r := cast(^Auto_Cast)res;
r.expr = clone(r.expr);
case Bad_Stmt:
case Empty_Stmt:
case Expr_Stmt:
r := cast(^Expr_Stmt)res;
r.expr = clone(r.expr);
case Tag_Stmt:
r := cast(^Expr_Stmt)res;
r.expr = clone(r.expr);
case Assign_Stmt:
r := cast(^Assign_Stmt)res;
r.lhs = clone(r.lhs);
r.rhs = clone(r.rhs);
case Block_Stmt:
r := cast(^Block_Stmt)res;
r.label = auto_cast clone(r.label);
r.stmts = clone(r.stmts);
case If_Stmt:
r := cast(^If_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
case When_Stmt:
r := cast(^When_Stmt)res;
r.cond = clone(r.cond);
r.body = clone(r.body);
r.else_stmt = clone(r.else_stmt);
case Return_Stmt:
r := cast(^Return_Stmt)res;
r.results = clone(r.results);
case Defer_Stmt:
r := cast(^Defer_Stmt)res;
r.stmt = clone(r.stmt);
case For_Stmt:
r := cast(^For_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.post = clone(r.post);
r.body = clone(r.body);
case Range_Stmt:
r := cast(^Range_Stmt)res;
r.label = auto_cast clone(r.label);
r.val0 = clone(r.val0);
r.val1 = clone(r.val1);
r.expr = clone(r.expr);
r.body = clone(r.body);
case Case_Clause:
r := cast(^Case_Clause)res;
r.list = clone(r.list);
r.body = clone(r.body);
case Switch_Stmt:
r := cast(^Switch_Stmt)res;
r.label = auto_cast clone(r.label);
r.init = clone(r.init);
r.cond = clone(r.cond);
r.body = clone(r.body);
case Type_Switch_Stmt:
r := cast(^Type_Switch_Stmt)res;
r.label = auto_cast clone(r.label);
r.tag = clone(r.tag);
r.expr = clone(r.expr);
r.body = clone(r.body);
case Branch_Stmt:
r := cast(^Branch_Stmt)res;
r.label = auto_cast clone(r.label);
case Using_Stmt:
r := cast(^Using_Stmt)res;
r.list = clone(r.list);
case Bad_Decl:
case Value_Decl:
r := cast(^Value_Decl)res;
r.attributes = clone(r.attributes);
r.names = clone(r.names);
r.type = clone(r.type);
r.values = clone(r.values);
case Package_Decl:
case Import_Decl:
case Foreign_Block_Decl:
r := cast(^Foreign_Block_Decl)res;
r.attributes = clone(r.attributes);
r.foreign_library = clone(r.foreign_library);
r.body = clone(r.body);
case Foreign_Import_Decl:
r := cast(^Foreign_Import_Decl)res;
r.name = auto_cast clone(r.name);
case Proc_Group:
r := cast(^Proc_Group)res;
r.args = clone(r.args);
case Attribute:
r := cast(^Attribute)res;
r.elems = clone(r.elems);
case Field:
r := cast(^Field)res;
r.names = clone(r.names);
r.type = clone(r.type);
r.default_value = clone(r.default_value);
case Field_List:
r := cast(^Field_List)res;
r.list = clone(r.list);
case Typeid_Type:
r := cast(^Typeid_Type)res;
r.specialization = clone(r.specialization);
case Helper_Type:
r := cast(^Helper_Type)res;
r.type = clone(r.type);
case Distinct_Type:
r := cast(^Distinct_Type)res;
r.type = clone(r.type);
case Opaque_Type:
r := cast(^Opaque_Type)res;
r.type = clone(r.type);
case Poly_Type:
r := cast(^Poly_Type)res;
r.type = auto_cast clone(r.type);
r.specialization = clone(r.specialization);
case Proc_Type:
r := cast(^Proc_Type)res;
r.params = auto_cast clone(r.params);
r.results = auto_cast clone(r.results);
case Pointer_Type:
r := cast(^Pointer_Type)res;
r.elem = clone(r.elem);
case Array_Type:
r := cast(^Array_Type)res;
r.len = clone(r.len);
r.elem = clone(r.elem);
case Dynamic_Array_Type:
r := cast(^Dynamic_Array_Type)res;
r.elem = clone(r.elem);
case Struct_Type:
r := cast(^Struct_Type)res;
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.fields = auto_cast clone(r.fields);
case Union_Type:
r := cast(^Union_Type)res;
r.poly_params = auto_cast clone(r.poly_params);
r.align = clone(r.align);
r.variants = clone(r.variants);
case Enum_Type:
r := cast(^Enum_Type)res;
r.base_type = clone(r.base_type);
r.fields = clone(r.fields);
case Bit_Field_Type:
r := cast(^Bit_Field_Type)res;
r.fields = clone(r.fields);
case Bit_Set_Type:
r := cast(^Bit_Set_Type)res;
r.elem = clone(r.elem);
r.underlying = clone(r.underlying);
case Map_Type:
r := cast(^Map_Type)res;
r.key = clone(r.key);
r.value = clone(r.value);
case:
fmt.panicf("Unhandled node kind: %T", n);
}
return res;
}
+40
View File
@@ -0,0 +1,40 @@
package odin_ast
import "core:odin/tokenizer"
Package_Kind :: enum {
Normal,
Runtime,
Init,
}
Package :: struct {
kind: Package_Kind,
id: int,
name: string,
fullpath: string,
files: []^File,
user_data: rawptr,
}
File :: struct {
id: int,
pkg: ^Package,
fullpath: string,
src: []byte,
pkg_decl: ^Package_Decl,
pkg_token: tokenizer.Token,
pkg_name: string,
decls: [dynamic]^Stmt,
imports: [dynamic]^Import_Decl,
directive_count: int,
comments: [dynamic]^Comment_Group,
syntax_warning_count: int,
syntax_error_count: int,
}
File diff suppressed because it is too large Load Diff
+335
View File
@@ -0,0 +1,335 @@
package odin_tokenizer
import "core:strings"
Token :: struct {
kind: Token_Kind,
text: string,
pos: Pos,
}
Pos :: struct {
file: string,
offset: int, // starting at 0
line: int, // starting at 1
column: int, // starting at 1
}
pos_compare :: proc(lhs, rhs: Pos) -> int {
if lhs.offset != rhs.offset {
return (lhs.offset < rhs.offset) ? -1 : +1;
}
if lhs.line != rhs.line {
return (lhs.line < rhs.line) ? -1 : +1;
}
if lhs.column != rhs.column {
return (lhs.column < rhs.column) ? -1 : +1;
}
return strings.compare(lhs.file, rhs.file);
}
Token_Kind :: enum u32 {
Invalid,
EOF,
Comment,
B_Literal_Begin,
Ident,
Integer,
Float,
Imag,
Rune,
String,
B_Literal_End,
B_Operator_Begin,
Eq,
Not,
Hash,
At,
Dollar,
Pointer,
Question,
Add,
Sub,
Mul,
Quo,
Mod,
Mod_Mod,
And,
Or,
Xor,
And_Not,
Shl,
Shr,
Cmp_And,
Cmp_Or,
B_Assign_Op_Begin,
Add_Eq,
Sub_Eq,
Mul_Eq,
Quo_Eq,
Mod_Eq,
Mod_Mod_Eq,
And_Eq,
Or_Eq,
Xor_Eq,
And_Not_Eq,
Shl_Eq,
Shr_Eq,
Cmp_And_Eq,
Cmp_Or_Eq,
B_Assign_Op_End,
Arrow_Right,
Arrow_Left,
Double_Arrow_Right,
Undef,
B_Comparison_Begin,
Cmp_Eq,
Not_Eq,
Lt,
Gt,
Lt_Eq,
Gt_Eq,
B_Comparison_End,
Open_Paren,
Close_Paren,
Open_Bracket,
Close_Bracket,
Open_Brace,
Close_Brace,
Colon,
Semicolon,
Period,
Comma,
Ellipsis,
Range_Half,
Back_Slash,
B_Operator_End,
B_Keyword_Begin,
Import,
Foreign,
Package,
Typeid,
When,
Where,
If,
Else,
For,
Switch,
In,
Notin,
Do,
Case,
Break,
Continue,
Fallthrough,
Defer,
Return,
Proc,
Macro,
Struct,
Union,
Enum,
Bit_Field,
Bit_Set,
Map,
Dynamic,
Auto_Cast,
Cast,
Transmute,
Distinct,
Opaque,
Using,
Inline,
No_Inline,
Context,
Size_Of,
Align_Of,
Offset_Of,
Type_Of,
Const,
B_Keyword_End,
COUNT,
B_Custom_Keyword_Begin = COUNT+1,
// ... Custom keywords
};
tokens := [Token_Kind.COUNT]string {
"Invalid",
"EOF",
"Comment",
"",
"identifier",
"integer",
"float",
"imaginary",
"rune",
"string",
"",
"",
"=",
"!",
"#",
"@",
"$",
"^",
"?",
"+",
"-",
"*",
"/",
"%",
"%%",
"&",
"|",
"~",
"&~",
"<<",
">>",
"&&",
"||",
"",
"+=",
"-=",
"*=",
"/=",
"%=",
"%%=",
"&=",
"|=",
"~=",
"&~=",
"<<=",
">>=",
"&&=",
"||=",
"",
"->",
"<-",
"=>",
"---",
"",
"==",
"!=",
"<",
">",
"<=",
">=",
"",
"(",
")",
"[",
"]",
"{",
"}",
":",
";",
".",
",",
"..",
"..<",
"\\",
"",
"",
"import",
"foreign",
"package",
"typeid",
"when",
"where",
"if",
"else",
"for",
"switch",
"in",
"notin",
"do",
"case",
"break",
"continue",
"fallthrough",
"defer",
"return",
"proc",
"macro",
"struct",
"union",
"enum",
"bit_field",
"bit_set",
"map",
"dynamic",
"auto_cast",
"cast",
"transmute",
"distinct",
"opaque",
"using",
"inline",
"no_inline",
"context",
"size_of",
"align_of",
"offset_of",
"type_of",
"const",
"",
};
custom_keyword_tokens: []string;
to_string :: proc(kind: Token_Kind) -> string {
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
return tokens[kind];
}
if Token_Kind.B_Custom_Keyword_Begin < kind {
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin));
if n < len(custom_keyword_tokens) {
return custom_keyword_tokens[n];
}
}
return "Invalid";
}
is_literal :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
}
is_operator :: proc(kind: Token_Kind) -> bool {
switch kind {
case .B_Operator_Begin .. .B_Operator_End:
return true;
case .In, .Notin:
return true;
}
return false;
}
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq;
}
is_keyword :: proc(kind: Token_Kind) -> bool {
switch {
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
return true;
case Token_Kind.B_Custom_Keyword_Begin < kind:
return true;
}
return false;
}
+622
View File
@@ -0,0 +1,622 @@
package odin_tokenizer
import "core:fmt"
import "core:unicode/utf8"
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
Tokenizer :: struct {
// Immutable data
path: string,
src: []byte,
err: Error_Handler,
// Tokenizing state
ch: rune,
offset: int,
read_offset: int,
line_offset: int,
line_count: int,
// Mutable data
error_count: int,
}
init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) {
t.src = src;
t.err = err;
t.ch = ' ';
t.offset = 0;
t.read_offset = 0;
t.line_offset = 0;
t.line_count = len(src) > 0 ? 1 : 0;
t.error_count = 0;
t.path = path;
advance_rune(t);
if t.ch == utf8.RUNE_BOM {
advance_rune(t);
}
}
@(private)
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
line := t.line_count;
column := offset - t.line_offset + 1;
return Pos {
file = t.path,
offset = offset,
line = line,
column = column,
};
}
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
fmt.eprintf(msg, ..args);
fmt.eprintf("\n");
}
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
pos := offset_to_pos(t, offset);
if t.err != nil {
t.err(pos, msg, ..args);
}
t.error_count += 1;
}
advance_rune :: proc(using t: ^Tokenizer) {
if read_offset < len(src) {
offset = read_offset;
if ch == '\n' {
line_offset = offset;
line_count += 1;
}
r, w := rune(src[read_offset]), 1;
switch {
case r == 0:
error(t, t.offset, "illegal character NUL");
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(src[read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding");
} else if r == utf8.RUNE_BOM && offset > 0 {
error(t, t.offset, "illegal byte order mark");
}
}
read_offset += w;
ch = r;
} else {
offset = len(src);
if ch == '\n' {
line_offset = offset;
line_count += 1;
}
ch = -1;
}
}
peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
if t.read_offset+offset < len(t.src) {
return t.src[t.read_offset+offset];
}
return 0;
}
skip_whitespace :: proc(t: ^Tokenizer) {
for t.ch == ' ' ||
t.ch == '\t' ||
t.ch == '\n' ||
t.ch == '\r' {
advance_rune(t);
}
}
is_letter :: proc(r: rune) -> bool {
if r < utf8.RUNE_SELF {
switch r {
case '_':
return true;
case 'A'..'Z', 'a'..'z':
return true;
}
}
// TODO(bill): Add unicode lookup tables
return false;
}
is_digit :: proc(r: rune) -> bool {
// TODO(bill): Add unicode lookup tables
return '0' <= r && r <= '9';
}
scan_comment :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
next := -1;
general: {
if t.ch == '/' || t.ch == '!' { // // #! comments
advance_rune(t);
for t.ch != '\n' && t.ch >= 0 {
advance_rune(t);
}
next = t.offset;
if t.ch == '\n' {
next += 1;
}
break general;
}
/* style comment */
advance_rune(t);
for t.ch >= 0 {
ch := t.ch;
advance_rune(t);
if ch == '*' && t.ch == '/' {
advance_rune(t);
next = t.offset;
break general;
}
}
error(t, offset, "comment not terminated");
}
lit := t.src[offset : t.offset];
// NOTE(bill): Strip CR for line comments
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
lit = lit[:len(lit)-1];
}
return string(lit);
}
scan_identifier :: proc(t: ^Tokenizer) -> string {
offset := t.offset;
for is_letter(t.ch) || is_digit(t.ch) {
advance_rune(t);
}
return string(t.src[offset : t.offset]);
}
scan_string :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
error(t, offset, "string literal was not terminated");
break;
}
advance_rune(t);
if ch == '"' {
break;
}
if ch == '\\' {
scan_escape(t);
}
}
return string(t.src[offset : t.offset]);
}
scan_raw_string :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
error(t, offset, "raw string literal was not terminated");
break;
}
advance_rune(t);
if ch == '`' {
break;
}
}
return string(t.src[offset : t.offset]);
}
digit_val :: proc(r: rune) -> int {
switch r {
case '0'..'9':
return int(r-'0');
case 'A'..'F':
return int(r-'A' + 10);
case 'a'..'f':
return int(r-'a' + 10);
}
return 16;
}
scan_escape :: proc(t: ^Tokenizer) -> bool {
offset := t.offset;
n: int;
base, max: u32;
switch t.ch {
case 'a', 'b', 'e', 'f', 'n', 't', 'v', '\\', '\'', '\"':
advance_rune(t);
return true;
case '0'..'7':
n, base, max = 3, 8, 255;
case 'x':
advance_rune(t);
n, base, max = 2, 16, 255;
case 'u':
advance_rune(t);
n, base, max = 4, 16, utf8.MAX_RUNE;
case 'U':
advance_rune(t);
n, base, max = 8, 16, utf8.MAX_RUNE;
case:
if t.ch < 0 {
error(t, offset, "escape sequence was not terminated");
} else {
error(t, offset, "unknown escape sequence");
}
return false;
}
x: u32;
for n > 0 {
d := u32(digit_val(t.ch));
for d >= base {
if t.ch < 0 {
error(t, t.offset, "escape sequence was not terminated");
} else {
error(t, t.offset, "illegal character %d in escape sequence", t.ch);
}
return false;
}
x = x*base + d;
advance_rune(t);
n -= 1;
}
if x > max || 0xd800 <= x && x <= 0xe000 {
error(t, offset, "escape sequence is an invalid Unicode code point");
return false;
}
return true;
}
scan_rune :: proc(t: ^Tokenizer) -> string {
offset := t.offset-1;
valid := true;
n := 0;
for {
ch := t.ch;
if ch == '\n' || ch < 0 {
if valid {
error(t, offset, "rune literal not terminated");
valid = false;
}
break;
}
advance_rune(t);
if ch == '\'' {
break;
}
n += 1;
if ch == '\\' {
if !scan_escape(t) {
valid = false;
}
}
}
if valid && n != 1 {
error(t, offset, "illegal rune literal");
}
return string(t.src[offset : t.offset]);
}
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
for digit_val(t.ch) < base || t.ch == '_' {
advance_rune(t);
}
}
scan_exponent :: proc(t: ^Tokenizer, kind: ^Token_Kind) {
if t.ch == 'e' || t.ch == 'E' {
kind^ = .Float;
advance_rune(t);
if t.ch == '-' || t.ch == '+' {
advance_rune(t);
}
if digit_val(t.ch) < 10 {
scan_mantissa(t, 10);
} else {
error(t, t.offset, "illegal floating-point exponent");
}
}
// NOTE(bill): This needs to be here for sanity's sake
switch t.ch {
case 'i', 'j', 'k':
kind^ = .Imag;
advance_rune(t);
}
}
scan_fraction :: proc(t: ^Tokenizer, kind: ^Token_Kind) -> (early_exit: bool) {
if t.ch == '.' && peek_byte(t) == '.' {
return true;
}
if t.ch == '.' {
kind^ = .Float;
advance_rune(t);
scan_mantissa(t, 10);
}
return false;
}
offset := t.offset;
kind := Token_Kind.Integer;
seen_point := seen_decimal_point;
if seen_point {
offset -= 1;
kind = .Float;
scan_mantissa(t, 10);
scan_exponent(t, &kind);
} else {
if t.ch == '0' {
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
prev := t.offset;
advance_rune(t);
scan_mantissa(t, base);
if t.offset - prev <= 1 {
kind^ = .Invalid;
error(t, t.offset, msg);
}
}
advance_rune(t);
switch t.ch {
case 'b': int_base(t, &kind, 2, "illegal binary integer");
case 'o': int_base(t, &kind, 8, "illegal octal integer");
case 'd': int_base(t, &kind, 10, "illegal decimal integer");
case 'z': int_base(t, &kind, 12, "illegal dozenal integer");
case 'x': int_base(t, &kind, 16, "illegal hexadecimal integer");
case 'h':
prev := t.offset;
advance_rune(t);
scan_mantissa(t, 16);
if t.offset - prev <= 1 {
kind = .Invalid;
error(t, t.offset, "illegal hexadecimal floating-point number");
} else {
sub := t.src[prev+1 : t.offset];
digit_count := 0;
for d in sub {
if d != '_' {
digit_count += 1;
}
}
switch digit_count {
case 8, 16: break;
case:
error(t, t.offset, "invalid hexadecimal floating-point number, expected 8 or 16 digits, got %d", digit_count);
}
}
case:
seen_point = false;
scan_mantissa(t, 10);
if t.ch == '.' {
seen_point = true;
if scan_fraction(t, &kind) {
return kind, string(t.src[offset : t.offset]);
}
}
scan_exponent(t, &kind);
return kind, string(t.src[offset : t.offset]);
}
}
}
scan_mantissa(t, 10);
if scan_fraction(t, &kind) {
return kind, string(t.src[offset : t.offset]);
}
scan_exponent(t, &kind);
return kind, string(t.src[offset : t.offset]);
}
scan :: proc(t: ^Tokenizer) -> Token {
switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
return tok0;
}
switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
if t.ch == ch2 {
advance_rune(t);
return tok2;
}
return tok0;
}
switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind {
if t.ch == '=' {
advance_rune(t);
return tok1;
}
if t.ch == ch2 {
advance_rune(t);
if t.ch == '=' {
advance_rune(t);
return tok3;
}
return tok2;
}
return tok0;
}
skip_whitespace(t);
offset := t.offset;
kind: Token_Kind;
lit: string;
pos := offset_to_pos(t, offset);
switch ch := t.ch; true {
case is_letter(ch):
lit = scan_identifier(t);
kind = .Ident;
check_keyword: if len(lit) > 1 {
// TODO(bill): Maybe have a hash table lookup rather than this linear search
for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End {
if lit == tokens[i] {
kind = Token_Kind(i);
break check_keyword;
}
}
for keyword, i in custom_keyword_tokens {
if lit == keyword {
kind = Token_Kind(i+1) + .B_Custom_Keyword_Begin;
break check_keyword;
}
}
}
case '0' <= ch && ch <= '9':
kind, lit = scan_number(t, false);
case:
advance_rune(t);
switch ch {
case -1:
kind = .EOF;
case '"':
kind = .String;
lit = scan_string(t);
case '\'':
kind = .Rune;
lit = scan_rune(t);
case '`':
kind = .String;
lit = scan_raw_string(t);
case '=':
if t.ch == '>' {
advance_rune(t);
kind = .Double_Arrow_Right;
} else {
kind = switch2(t, .Eq, .Cmp_Eq);
}
case '!': kind = switch2(t, .Not, .Not_Eq);
case '#':
kind = .Hash;
if t.ch == '!' {
kind = .Comment;
lit = scan_comment(t);
}
case '?': kind = .Question;
case '@': kind = .At;
case '$': kind = .Dollar;
case '^': kind = .Pointer;
case '+': kind = switch2(t, .Add, .Add_Eq);
case '-':
if t.ch == '>' {
advance_rune(t);
kind = .Arrow_Right;
} else if t.ch == '-' && peek_byte(t) == '-' {
advance_rune(t);
advance_rune(t);
kind = .Undef;
} else {
kind = switch2(t, .Sub, .Sub_Eq);
}
case '*': kind = switch2(t, .Mul, .Mul_Eq);
case '/':
if t.ch == '/' || t.ch == '*' {
kind = .Comment;
lit = scan_comment(t);
} else {
kind = switch2(t, .Quo, .Quo_Eq);
}
case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq);
case '&':
if t.ch == '~' {
advance_rune(t);
kind = switch2(t, .And_Not, .And_Not_Eq);
} else {
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
}
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = .Xor;
case '<':
if t.ch == '-' {
advance_rune(t);
kind = .Arrow_Left;
} else {
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
}
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
case '≠': kind = .Not_Eq;
case '≤': kind = .Lt_Eq;
case '≥': kind = .Gt_Eq;
case '∈': kind = .In;
case '∉': kind = .Notin;
case '.':
if '0' <= t.ch && t.ch <= '9' {
kind, lit = scan_number(t, true);
} else {
kind = .Period;
if t.ch == '.' {
advance_rune(t);
kind = .Ellipsis;
if t.ch == '<' {
advance_rune(t);
kind = .Range_Half;
}
}
}
case ':': kind = .Colon;
case ',': kind = .Comma;
case ';': kind = .Semicolon;
case '(': kind = .Open_Paren;
case ')': kind = .Close_Paren;
case '[': kind = .Open_Bracket;
case ']': kind = .Close_Bracket;
case '{': kind = .Open_Brace;
case '}': kind = .Close_Brace;
case '\\': kind = .Back_Slash;
case:
if ch != utf8.RUNE_BOM {
error(t, t.offset, "illegal character '%r': %d", ch, ch);
}
kind = .Invalid;
}
}
if lit == "" {
lit = string(t.src[offset : t.offset]);
}
return Token{kind, lit, pos};
}
+18 -6
View File
@@ -51,6 +51,20 @@ write_encoded_rune :: proc(fd: Handle, r: rune) {
}
file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0);
if err != 0 {
return -1;
}
defer close(fd);
length: i64;
if length, err = file_size(fd); err != 0 {
return -1;
}
return length;
}
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
fd, err := open(name, O_RDONLY, 0);
if err != 0 {
@@ -109,20 +123,18 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
using mem.Allocator_Mode;
switch mode {
case Alloc:
case .Alloc:
return heap_alloc(size);
case Free:
case .Free:
heap_free(old_memory);
return nil;
case Free_All:
case .Free_All:
// NOTE(bill): Does nothing
case Resize:
case .Resize:
if old_memory == nil {
return heap_alloc(size);
}
+43 -45
View File
@@ -6,12 +6,13 @@ foreign import libc "system:c"
import "core:runtime"
import "core:strings"
OS :: "osx";
OS :: "darwin";
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct int;
INVALID_HANDLE :: ~Handle(0);
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
@@ -80,37 +81,37 @@ Stat :: struct {
};
// File type
S_IFMT :: 0170000; // Type of file mask
S_IFIFO :: 0010000; // Named pipe (fifo)
S_IFCHR :: 0020000; // Character special
S_IFDIR :: 0040000; // Directory
S_IFBLK :: 0060000; // Block special
S_IFREG :: 0100000; // Regular
S_IFLNK :: 0120000; // Symbolic link
S_IFSOCK :: 0140000; // Socket
S_IFMT :: 0o170000; // Type of file mask
S_IFIFO :: 0o010000; // Named pipe (fifo)
S_IFCHR :: 0o020000; // Character special
S_IFDIR :: 0o040000; // Directory
S_IFBLK :: 0o060000; // Block special
S_IFREG :: 0o100000; // Regular
S_IFLNK :: 0o120000; // Symbolic link
S_IFSOCK :: 0o140000; // Socket
// File mode
// Read, write, execute/search by owner
S_IRWXU :: 0000700; // RWX mask for owner
S_IRUSR :: 0000400; // R for owner
S_IWUSR :: 0000200; // W for owner
S_IXUSR :: 0000100; // X for owner
S_IRWXU :: 0o0700; // RWX mask for owner
S_IRUSR :: 0o0400; // R for owner
S_IWUSR :: 0o0200; // W for owner
S_IXUSR :: 0o0100; // X for owner
// Read, write, execute/search by group
S_IRWXG :: 0000070; // RWX mask for group
S_IRGRP :: 0000040; // R for group
S_IWGRP :: 0000020; // W for group
S_IXGRP :: 0000010; // X for group
S_IRWXG :: 0o0070; // RWX mask for group
S_IRGRP :: 0o0040; // R for group
S_IWGRP :: 0o0020; // W for group
S_IXGRP :: 0o0010; // X for group
// Read, write, execute/search by others
S_IRWXO :: 0000007; // RWX mask for other
S_IROTH :: 0000004; // R for other
S_IWOTH :: 0000002; // W for other
S_IXOTH :: 0000001; // X for other
S_IRWXO :: 0o0007; // RWX mask for other
S_IROTH :: 0o0004; // R for other
S_IWOTH :: 0o0002; // W for other
S_IXOTH :: 0o0001; // X for other
S_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // Directory restrcted delete
S_ISUID :: 0o4000; // Set user id on execution
S_ISGID :: 0o2000; // Set group id on execution
S_ISVTX :: 0o1000; // Directory restrcted delete
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
S_ISREG :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
@@ -126,7 +127,7 @@ X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
foreign libc {
@(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
@@ -151,22 +152,16 @@ foreign dl {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
}
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_cstring(path);
defer delete(cstr);
handle := _unix_open(cstr, mode);
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.clone_to_cstring(path);
handle := _unix_open(cstr, flags, mode);
delete(cstr);
if handle == -1 {
return 0, 1;
return INVALID_HANDLE, 1;
}
return handle, 0;
}
// NOTE(zangent): This is here for compatability reasons. Should this be here?
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
return open_simple(path, mode);
}
close :: proc(fd: Handle) {
_unix_close(fd);
}
@@ -220,16 +215,20 @@ last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
is_path_separator :: proc(r: rune) -> bool {
return r == '/';
}
stat :: inline proc(path: string) -> (Stat, bool) {
s: Stat;
cstr := strings.new_cstring(path);
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
ret_int := _unix_stat(cstr, &s);
return s, ret_int==0;
}
access :: inline proc(path: string, mask: int) -> bool {
cstr := strings.new_cstring(path);
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
return _unix_access(cstr, mask) == 0;
}
@@ -246,7 +245,7 @@ heap_free :: inline proc(ptr: rawptr) {
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_cstring(name);
path_str := strings.clone_to_cstring(name);
defer delete(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
@@ -259,21 +258,20 @@ exit :: inline proc(code: int) -> ! {
_unix_exit(code);
}
current_thread_id :: proc "contextless" () -> int {
// return int(_unix_gettid());
return 0;
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.new_cstring(filename);
cstr := strings.clone_to_cstring(filename);
defer delete(cstr);
handle := _unix_dlopen(cstr, flags);
return handle;
}
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil);
cstr := strings.new_cstring(symbol);
cstr := strings.clone_to_cstring(symbol);
defer delete(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
@@ -288,9 +286,9 @@ dlerror :: proc() -> string {
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, len(runtime.args__));
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
args[i] = string(arg);
res[i] = string(arg);
}
return args;
return res;
}
+2422 -158
View File
File diff suppressed because it is too large Load Diff
+206 -87
View File
@@ -11,7 +11,41 @@ OS :: "linux";
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct i32;
Syscall :: distinct int;
INVALID_HANDLE :: ~Handle(0);
ERROR_NONE: Errno : 0;
EPERM: Errno : 1;
ENOENT: Errno : 2;
EINTR: Errno : 4;
EIO: Errno : 5;
ENXIO: Errno : 6;
EBADF: Errno : 9;
EAGAIN: Errno : 11;
EWOULDBLOCK: Errno : EAGAIN;
ENOMEM: Errno : 12;
EACCES: Errno : 13;
EFAULT: Errno : 14;
EEXIST: Errno : 17;
ENODEV: Errno : 19;
ENOTDIR: Errno : 20;
EISDIR: Errno : 21;
EINVAL: Errno : 22;
ENFILE: Errno : 23;
EMFILE: Errno : 24;
ETXTBSY: Errno : 26;
EFBIG: Errno : 27;
ENOSPC: Errno : 28;
ESPIPE: Errno : 29;
EROFS: Errno : 30;
EPIPE: Errno : 32;
ENAMETOOLONG: Errno : 36;
ELOOP: Errno : 40;
EOVERFLOW: Errno : 75;
EDESTADDRREQ: Errno : 89;
EOPNOTSUPP: Errno : 95;
EDQUOT: Errno : 122;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
@@ -46,18 +80,13 @@ args := _alloc_command_line_arguments();
_File_Time :: struct {
seconds: i64,
nanoseconds: i32,
reserved: i32,
nanoseconds: i64,
}
// Translated from
// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h
// Validity is not guaranteed.
Stat :: struct {
device_id: u64, // ID of device containing file
serial: u64, // File serial number
nlink: u32, // Number of hard links
nlink: u64, // Number of hard links
mode: u32, // Mode of the file
uid: u32, // User ID of the file's owner
gid: u32, // Group ID of the file's group
@@ -74,42 +103,40 @@ Stat :: struct {
_reserve1,
_reserve2,
_reserve3: i64,
serial_numbe: u64, // File serial number..? Maybe.
_reserve4: i64,
};
// File type
S_IFMT :: 0170000; // Type of file mask
S_IFIFO :: 0010000; // Named pipe (fifo)
S_IFCHR :: 0020000; // Character special
S_IFDIR :: 0040000; // Directory
S_IFBLK :: 0060000; // Block special
S_IFREG :: 0100000; // Regular
S_IFLNK :: 0120000; // Symbolic link
S_IFSOCK :: 0140000; // Socket
S_IFMT :: 0o170000; // Type of file mask
S_IFIFO :: 0o010000; // Named pipe (fifo)
S_IFCHR :: 0o020000; // Character special
S_IFDIR :: 0o040000; // Directory
S_IFBLK :: 0o060000; // Block special
S_IFREG :: 0o100000; // Regular
S_IFLNK :: 0o120000; // Symbolic link
S_IFSOCK :: 0o140000; // Socket
// File mode
// Read, write, execute/search by owner
S_IRWXU :: 0000700; // RWX mask for owner
S_IRUSR :: 0000400; // R for owner
S_IWUSR :: 0000200; // W for owner
S_IXUSR :: 0000100; // X for owner
S_IRWXU :: 0o0700; // RWX mask for owner
S_IRUSR :: 0o0400; // R for owner
S_IWUSR :: 0o0200; // W for owner
S_IXUSR :: 0o0100; // X for owner
// Read, write, execute/search by group
S_IRWXG :: 0000070; // RWX mask for group
S_IRGRP :: 0000040; // R for group
S_IWGRP :: 0000020; // W for group
S_IXGRP :: 0000010; // X for group
S_IRWXG :: 0o0070; // RWX mask for group
S_IRGRP :: 0o0040; // R for group
S_IWGRP :: 0o0020; // W for group
S_IXGRP :: 0o0010; // X for group
// Read, write, execute/search by others
S_IRWXO :: 0000007; // RWX mask for other
S_IROTH :: 0000004; // R for other
S_IWOTH :: 0000002; // W for other
S_IXOTH :: 0000001; // X for other
S_IRWXO :: 0o0007; // RWX mask for other
S_IROTH :: 0o0004; // R for other
S_IWOTH :: 0o0002; // W for other
S_IXOTH :: 0o0001; // X for other
S_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // Directory restrcted delete
S_ISUID :: 0o4000; // Set user id on execution
S_ISGID :: 0o2000; // Set group id on execution
S_ISVTX :: 0o1000; // Directory restrcted delete
S_ISLNK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
@@ -125,70 +152,113 @@ X_OK :: 1; // Test for execute permission
W_OK :: 2; // Test for write permission
R_OK :: 4; // Test for read permission
TimeSpec :: struct {
tv_sec : i64, /* seconds */
tv_nsec : i64, /* nanoseconds */
};
CLOCK_REALTIME :: 0;
CLOCK_MONOTONIC :: 1;
CLOCK_PROCESS_CPUTIME_ID :: 2;
CLOCK_THREAD_CPUTIME_ID :: 3;
CLOCK_MONOTONIC_RAW :: 4;
CLOCK_REALTIME_COARSE :: 5;
CLOCK_MONOTONIC_COARSE :: 6;
CLOCK_BOOTTIME :: 7;
CLOCK_REALTIME_ALARM :: 8;
CLOCK_BOOTTIME_ALARM :: 9;
SYS_GETTID: Syscall : 186;
foreign libc {
@(link_name="open") _unix_open :: proc(path: cstring, mode: int) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> i32 ---;
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> i32 ---;
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> i32 ---;
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---;
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---;
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---;
@(link_name="sleep") _unix_sleep :: proc(seconds: u64) -> int ---;
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
}
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
}
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_cstring(path);
handle := _unix_open(cstr, mode);
is_path_separator :: proc(r: rune) -> bool {
return r == '/';
}
get_last_error :: proc() -> int {
return __errno_location()^;
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.clone_to_cstring(path);
handle := _unix_open(cstr, flags, mode);
delete(cstr);
if(handle == -1) {
return 0, 1;
if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error());
}
return handle, 0;
}
// NOTE(zangent): This is here for compatability reasons. Should this be here?
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
return open_simple(path, mode);
return handle, ERROR_NONE;
}
close :: proc(fd: Handle) {
_unix_close(fd);
close :: proc(fd: Handle) -> Errno {
result := _unix_close(fd);
if result == -1 {
return Errno(get_last_error());
}
return ERROR_NONE;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_read(fd, &data[0], len(data));
return sz, 0;
bytes_read := _unix_read(fd, &data[0], len(data));
if bytes_read == -1 {
return -1, Errno(get_last_error());
}
return bytes_read, ERROR_NONE;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_write(fd, &data[0], len(data));
return sz, 0;
bytes_written := _unix_write(fd, &data[0], len(data));
if bytes_written == -1 {
return -1, Errno(get_last_error());
}
return bytes_written, ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
res := _unix_seek(fd, offset, i32(whence));
return res, 0;
if res == -1 {
return -1, Errno(get_last_error());
}
return res, ERROR_NONE;
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
prev, _ := seek(fd, 0, SEEK_CUR);
size, err := seek(fd, 0, SEEK_END);
seek(fd, prev, SEEK_SET);
return size, err;
s, err := fstat(fd);
if err != ERROR_NONE {
return -1, err;
}
return s.size, ERROR_NONE;
}
@@ -202,20 +272,51 @@ stderr: Handle = 2;
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
s, err := fstat(fd);
if err != ERROR_NONE {
return 0, err;
}
return File_Time(s.modified.nanoseconds), ERROR_NONE;
}
stat :: inline proc(path: string) -> (Stat, int) {
cstr := strings.new_cstring(path);
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
s, err := stat(name);
if err != ERROR_NONE {
return 0, err;
}
return File_Time(s.modified.nanoseconds), ERROR_NONE;
}
stat :: inline proc(path: string) -> (Stat, Errno) {
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
s: Stat;
ret_int := _unix_stat(cstr, &s);
return s, int(ret_int);
result := _unix_stat(cstr, &s);
if result == -1 {
return s, Errno(get_last_error());
}
return s, ERROR_NONE;
}
access :: inline proc(path: string, mask: int) -> bool {
cstr := strings.new_cstring(path);
fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
s: Stat;
result := _unix_fstat(fd, &s);
if result == -1 {
return s, Errno(get_last_error());
}
return s, ERROR_NONE;
}
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
return _unix_access(cstr, mask) == 0;
result := _unix_access(cstr, mask);
if result == -1 {
return false, Errno(get_last_error());
}
return true, ERROR_NONE;
}
heap_alloc :: proc(size: int) -> rawptr {
@@ -232,7 +333,7 @@ heap_free :: proc(ptr: rawptr) {
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_cstring(name);
path_str := strings.clone_to_cstring(name);
defer delete(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
@@ -245,20 +346,38 @@ exit :: proc(code: int) -> ! {
_unix_exit(code);
}
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
ts : TimeSpec;
_unix_clock_gettime(clock_id, &ts);
return ts;
}
sleep :: proc(seconds: u64) -> int {
return _unix_sleep(seconds);
}
nanosleep :: proc(nanoseconds: i64) -> int {
assert(nanoseconds <= 999999999);
requested, remaining : TimeSpec;
requested = TimeSpec{tv_nsec = nanoseconds};
return _unix_nanosleep(&requested, &remaining);
}
current_thread_id :: proc "contextless" () -> int {
// return int(_unix_gettid());
return 0;
return syscall(SYS_GETTID);
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.new_cstring(filename);
cstr := strings.clone_to_cstring(filename);
defer delete(cstr);
handle := _unix_dlopen(cstr, flags);
return handle;
}
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil);
cstr := strings.new_cstring(symbol);
cstr := strings.clone_to_cstring(symbol);
defer delete(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
@@ -273,9 +392,9 @@ dlerror :: proc() -> string {
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, len(runtime.args__));
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
args[i] = string(arg);
res[i] = string(arg);
}
return args;
return res;
}
+56 -12
View File
@@ -32,6 +32,7 @@ ERROR_NONE: Errno : 0;
ERROR_FILE_NOT_FOUND: Errno : 2;
ERROR_PATH_NOT_FOUND: Errno : 3;
ERROR_ACCESS_DENIED: Errno : 5;
ERROR_INVALID_HANDLE: Errno : 6;
ERROR_NO_MORE_FILES: Errno : 18;
ERROR_HANDLE_EOF: Errno : 38;
ERROR_NETNAME_DELETED: Errno : 64;
@@ -60,6 +61,10 @@ ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
args := _alloc_command_line_arguments();
is_path_separator :: proc(r: rune) -> bool {
return r == '/' || r == '\\';
}
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
@@ -106,8 +111,11 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errn
return INVALID_HANDLE, err;
}
close :: proc(fd: Handle) {
win32.close_handle(win32.Handle(fd));
close :: proc(fd: Handle) -> Errno {
if win32.close_handle(win32.Handle(fd)) == 0 {
return Errno(win32.get_last_error());
}
return ERROR_NONE;
}
@@ -203,26 +211,27 @@ get_std_handle :: proc(h: int) -> Handle {
last_write_time :: proc(fd: Handle) -> File_Time {
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
file_info: win32.By_Handle_File_Information;
win32.get_file_information_by_handle(win32.Handle(fd), &file_info);
if !win32.get_file_information_by_handle(win32.Handle(fd), &file_info) {
return 0, Errno(win32.get_last_error());
}
lo := File_Time(file_info.last_write_time.lo);
hi := File_Time(file_info.last_write_time.hi);
return lo | hi << 32;
return lo | hi << 32, ERROR_NONE;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.Filetime;
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
data: win32.File_Attribute_Data;
wide_path := win32.utf8_to_wstring(name);
if win32.get_file_attributes_ex_w(wide_path, win32.GetFileExInfoStandard, &data) {
last_write_time = data.last_write_time;
if !win32.get_file_attributes_ex_w(wide_path, win32.GetFileExInfoStandard, &data) {
return 0, Errno(win32.get_last_error());
}
l := File_Time(last_write_time.lo);
h := File_Time(last_write_time.hi);
return l | h << 32;
l := File_Time(data.last_write_time.lo);
h := File_Time(data.last_write_time.hi);
return l | h << 32, ERROR_NONE;
}
@@ -278,4 +287,39 @@ _alloc_command_line_arguments :: proc() -> []string {
return arg_list;
}
get_windows_version_ansi :: proc() -> win32.OS_Version_Info_Ex_A {
osvi : win32.OS_Version_Info_Ex_A;
osvi.os_version_info_size = size_of(win32.OS_Version_Info_Ex_A);
win32.get_version(&osvi);
return osvi;
}
is_windows_xp :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 5 && osvi.minor_version == 1);
}
is_windows_vista :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 6 && osvi.minor_version == 0);
}
is_windows_7 :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 6 && osvi.minor_version == 1);
}
is_windows_8 :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 6 && osvi.minor_version == 2);
}
is_windows_8_1 :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 6 && osvi.minor_version == 3);
}
is_windows_10 :: proc() -> bool {
osvi := get_windows_version_ansi();
return (osvi.major_version == 10 && osvi.minor_version == 0);
}
+396
View File
@@ -0,0 +1,396 @@
package reflect
import "core:runtime"
import "core:mem"
Type_Kind :: enum {
Invalid,
Named,
Integer,
Rune,
Float,
Complex,
Quaternion,
String,
Boolean,
Any,
Type_Id,
Pointer,
Procedure,
Array,
Dynamic_Array,
Slice,
Tuple,
Struct,
Union,
Enum,
Map,
Bit_Field,
Bit_Set,
Opaque,
Simd_Vector,
}
type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T);
if ti != nil {
#complete switch _ in ti.variant {
case runtime.Type_Info_Named: return .Named;
case runtime.Type_Info_Integer: return .Integer;
case runtime.Type_Info_Rune: return .Rune;
case runtime.Type_Info_Float: return .Float;
case runtime.Type_Info_Complex: return .Complex;
case runtime.Type_Info_Quaternion: return .Quaternion;
case runtime.Type_Info_String: return .String;
case runtime.Type_Info_Boolean: return .Boolean;
case runtime.Type_Info_Any: return .Any;
case runtime.Type_Info_Type_Id: return .Type_Id;
case runtime.Type_Info_Pointer: return .Pointer;
case runtime.Type_Info_Procedure: return .Procedure;
case runtime.Type_Info_Array: return .Array;
case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array;
case runtime.Type_Info_Slice: return .Slice;
case runtime.Type_Info_Tuple: return .Tuple;
case runtime.Type_Info_Struct: return .Struct;
case runtime.Type_Info_Union: return .Union;
case runtime.Type_Info_Enum: return .Enum;
case runtime.Type_Info_Map: return .Map;
case runtime.Type_Info_Bit_Field: return .Bit_Field;
case runtime.Type_Info_Bit_Set: return .Bit_Set;
case runtime.Type_Info_Opaque: return .Opaque;
case runtime.Type_Info_Simd_Vector: return .Simd_Vector;
}
}
return .Invalid;
}
// TODO(bill): Better name
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_base(T));
}
// TODO(bill): Better name
backing_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_core(T));
}
size_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.size;
}
return 0;
}
align_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.align;
}
return 1;
}
to_bytes :: proc(v: any) -> []byte {
if v != nil {
sz := size_of_typeid(v.id);
return mem.slice_ptr((^byte)(v.data), sz);
}
return nil;
}
any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
return v.data, v.id;
}
is_nil :: proc(v: any) -> bool {
data := to_bytes(v);
if data != nil {
return true;
}
for v in data do if v != 0 {
return false;
}
return true;
}
length :: proc(val: any) -> int {
if val == nil do return 0;
v := val;
v.id = runtime.typeid_base(v.id);
switch a in v {
case runtime.Type_Info_Array:
return a.count;
case runtime.Type_Info_Slice:
return (^mem.Raw_Slice)(v.data).len;
case runtime.Type_Info_Dynamic_Array:
return (^mem.Raw_Dynamic_Array)(v.data).len;
case runtime.Type_Info_String:
if a.is_cstring {
return len((^cstring)(v.data)^);
} else {
return (^mem.Raw_String)(v.data).len;
}
}
return 0;
}
index :: proc(val: any, i: int, loc := #caller_location) -> any {
if val == nil do return nil;
v := val;
v.id = runtime.typeid_base(v.id);
switch a in v {
case runtime.Type_Info_Array:
runtime.bounds_check_error_loc(loc, i, a.count);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(v.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_Slice:
raw := (^mem.Raw_Slice)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_Dynamic_Array:
raw := (^mem.Raw_Dynamic_Array)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(a.elem.size * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, a.elem.id};
case runtime.Type_Info_String:
if a.is_cstring do return nil;
raw := (^mem.Raw_String)(v.data);
runtime.bounds_check_error_loc(loc, i, raw.len);
offset := uintptr(size_of(u8) * i);
data := rawptr(uintptr(raw.data) + offset);
return any{data, typeid_of(u8)};
}
return nil;
}
Struct_Tag :: distinct string;
Struct_Field :: struct {
name: string,
type: typeid,
tag: Struct_Tag,
offset: uintptr,
}
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
if 0 <= i && i < len(s.names) {
field.name = s.names[i];
field.type = s.types[i].id;
field.tag = Struct_Tag(s.tags[i]);
field.offset = s.offsets[i];
}
}
return;
}
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
for fname, i in s.names {
if fname == name {
field.name = s.names[i];
field.type = s.types[i].id;
field.tag = Struct_Tag(s.tags[i]);
field.offset = s.offsets[i];
break;
}
}
}
return;
}
struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any {
if a == nil do return nil;
ti := runtime.type_info_base(type_info_of(a.id));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
for name, i in s.names {
if name == field {
return any{
rawptr(uintptr(a.data) + s.offsets[i]),
s.types[i].id,
};
}
if recurse && s.usings[i] {
f := any{
rawptr(uintptr(a.data) + s.offsets[i]),
s.types[i].id,
};
if res := struct_field_value_by_name(f, field, recurse); res != nil {
return res;
}
}
}
}
return nil;
}
struct_field_names :: proc(T: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.names;
}
return nil;
}
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.types;
}
return nil;
}
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return transmute([]Struct_Tag)s.tags;
}
return nil;
}
struct_field_offsets :: proc(T: typeid) -> []uintptr {
ti := runtime.type_info_base(type_info_of(T));
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
return s.offsets;
}
return nil;
}
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
value, _ = struct_tag_lookup(tag, key);
return;
}
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
for t := tag; t != ""; /**/ {
i := 0;
for i < len(t) && t[i] == ' ' { // Skip whitespace
i += 1;
}
t = t[i:];
if len(t) == 0 do break;
i = 0;
loop: for i < len(t) {
switch t[i] {
case ':', '"':
break loop;
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
break loop;
}
i += 1;
}
if i == 0 do break;
if i+1 >= len(t) do break;
if t[i] != ':' || t[i+1] != '"' {
break;
}
name := string(t[:i]);
t = t[i+1:];
i = 1;
for i < len(t) && t[i] != '"' { // find closing quote
if t[i] == '\\' do i += 1; // Skip escaped characters
i += 1;
}
if i >= len(t) do break;
val := string(t[:i+1]);
t = t[i+1:];
if key == name {
return val[1:i], true;
}
}
return;
}
enum_string :: proc(a: any) -> string {
if a == nil do return "";
ti := runtime.type_info_base(type_info_of(a.id));
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
for _, i in e.values {
value := &e.values[i];
n := mem.compare_byte_ptrs((^byte)(a.data), (^byte)(value), ti.size);
if n == 0 {
return e.names[i];
}
}
} else {
panic("expected an enum to reflect.enum_string");
}
return "";
}
union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
id := union_variant_typeid(a);
return type_info_of(id);
}
union_variant_typeid :: proc(a: any) -> typeid {
if a == nil do return nil;
ti := runtime.type_info_base(type_info_of(a.id));
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
tag_ptr := uintptr(a.data) + info.tag_offset;
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
tag: i64 = ---;
switch i in tag_any {
case u8: tag = i64(i);
case i8: tag = i64(i);
case u16: tag = i64(i);
case i16: tag = i64(i);
case u32: tag = i64(i);
case i32: tag = i64(i);
case u64: tag = i64(i);
case i64: tag = i64(i);
case: unimplemented();
}
if a.data != nil && tag != 0 {
return info.variants[tag-1].id;
}
} else {
panic("expected a union to reflect.union_variant_typeid");
}
return nil;
}
@@ -1,6 +1,7 @@
package types
package reflect
import rt "core:runtime"
import "core:strings"
are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
if a == b do return true;
@@ -108,9 +109,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
for _, i in x.types {
xn, yn := x.names[i], y.names[i];
xt, yt := x.types[i], y.types[i];
xl, yl := x.tags[i], y.tags[i];
if xn != yn do return false;
if !are_types_identical(xt, yt) do return false;
if xl != yl do return false;
}
return true;
@@ -267,3 +270,221 @@ is_opaque :: proc(info: ^rt.Type_Info) -> bool {
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Opaque);
return ok;
}
is_simd_vector :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Simd_Vector);
return ok;
}
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
write_type(buf, type_info_of(id));
}
write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
using strings;
if ti == nil {
write_string(buf, "nil");
return;
}
switch info in ti.variant {
case rt.Type_Info_Named:
write_string(buf, info.name);
case rt.Type_Info_Integer:
switch ti.id {
case int: write_string(buf, "int");
case uint: write_string(buf, "uint");
case uintptr: write_string(buf, "uintptr");
case:
write_byte(buf, info.signed ? 'i' : 'u');
write_i64(buf, i64(8*ti.size), 10);
switch info.endianness {
case .Little: write_string(buf, "le");
case .Big: write_string(buf, "be");
}
}
case rt.Type_Info_Rune:
write_string(buf, "rune");
case rt.Type_Info_Float:
write_byte(buf, 'f');
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_Complex:
write_string(buf, "complex");
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_String:
if info.is_cstring {
write_string(buf, "cstring");
} else {
write_string(buf, "string");
}
case rt.Type_Info_Boolean:
switch ti.id {
case bool: write_string(buf, "bool");
case:
write_byte(buf, 'b');
write_i64(buf, i64(8*ti.size), 10);
}
case rt.Type_Info_Any:
write_string(buf, "any");
case rt.Type_Info_Type_Id:
write_string(buf, "typeid");
case rt.Type_Info_Pointer:
if info.elem == nil {
write_string(buf, "rawptr");
} else {
write_string(buf, "^");
write_type(buf, info.elem);
}
case rt.Type_Info_Procedure:
write_string(buf, "proc");
if info.params == nil {
write_string(buf, "()");
} else {
t := info.params.variant.(rt.Type_Info_Tuple);
write_string(buf, "(");
for t, i in t.types {
if i > 0 do write_string(buf, ", ");
write_type(buf, t);
}
write_string(buf, ")");
}
if info.results != nil {
write_string(buf, " -> ");
write_type(buf, info.results);
}
case rt.Type_Info_Tuple:
count := len(info.names);
if count != 1 do write_string(buf, "(");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
t := info.types[i];
if len(name) > 0 {
write_string(buf, name);
write_string(buf, ": ");
}
write_type(buf, t);
}
if count != 1 do write_string(buf, ")");
case rt.Type_Info_Array:
write_string(buf, "[");
write_i64(buf, i64(info.count), 10);
write_string(buf, "]");
write_type(buf, info.elem);
case rt.Type_Info_Dynamic_Array:
write_string(buf, "[dynamic]");
write_type(buf, info.elem);
case rt.Type_Info_Slice:
write_string(buf, "[]");
write_type(buf, info.elem);
case rt.Type_Info_Map:
write_string(buf, "map[");
write_type(buf, info.key);
write_byte(buf, ']');
write_type(buf, info.value);
case rt.Type_Info_Struct:
write_string(buf, "struct ");
if info.is_packed do write_string(buf, "#packed ");
if info.is_raw_union do write_string(buf, "#raw_union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_type(buf, info.types[i]);
}
write_byte(buf, '}');
case rt.Type_Info_Union:
write_string(buf, "union ");
if info.custom_align {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_byte(buf, '{');
for variant, i in info.variants {
if i > 0 do write_string(buf, ", ");
write_type(buf, variant);
}
write_byte(buf, '}');
case rt.Type_Info_Enum:
write_string(buf, "enum ");
write_type(buf, info.base);
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
}
write_byte(buf, '}');
case rt.Type_Info_Bit_Field:
write_string(buf, "bit_field ");
if ti.align != 1 {
write_string(buf, "#align ");
write_i64(buf, i64(ti.align), 10);
write_byte(buf, ' ');
}
write_string(buf, " {");
for name, i in info.names {
if i > 0 do write_string(buf, ", ");
write_string(buf, name);
write_string(buf, ": ");
write_i64(buf, i64(info.bits[i]), 10);
}
write_byte(buf, '}');
case rt.Type_Info_Bit_Set:
write_string(buf, "bit_set[");
switch {
case is_enum(info.elem):
write_type(buf, info.elem);
case is_rune(info.elem):
write_encoded_rune(buf, rune(info.lower));
write_string(buf, "..");
write_encoded_rune(buf, rune(info.upper));
case:
write_i64(buf, info.lower, 10);
write_string(buf, "..");
write_i64(buf, info.upper, 10);
}
if info.underlying != nil {
write_string(buf, "; ");
write_type(buf, info.underlying);
}
write_byte(buf, ']');
case rt.Type_Info_Opaque:
write_string(buf, "opaque ");
write_type(buf, info.elem);
case rt.Type_Info_Simd_Vector:
if info.is_x86_mmx {
write_string(buf, "intrinsics.x86_mmx");
} else {
write_string(buf, "intrinsics.vector(");
write_i64(buf, i64(info.count));
write_string(buf, ", ");
write_type(buf, info.elem);
write_byte(buf, ')');
}
}
}
+233 -156
View File
@@ -6,6 +6,7 @@ package runtime
import "core:os"
import "core:mem"
import "core:log"
import "intrinsics"
// Naming Conventions:
// In general, Ada_Case for types and snake_case for values
@@ -40,17 +41,24 @@ Type_Info_Enum_Value :: union {
u8, u16, u32, u64, uint, uintptr,
};
Platform_Endianness :: enum u8 {
Platform = 0,
Little = 1,
Big = 2,
}
// Variant Types
Type_Info_Named :: struct {name: string, base: ^Type_Info};
Type_Info_Integer :: struct {signed: bool};
Type_Info_Rune :: struct {};
Type_Info_Float :: struct {};
Type_Info_Complex :: struct {};
Type_Info_String :: struct {is_cstring: bool};
Type_Info_Boolean :: struct {};
Type_Info_Any :: struct {};
Type_Info_Type_Id :: struct {};
Type_Info_Pointer :: struct {
Type_Info_Named :: struct {name: string, base: ^Type_Info};
Type_Info_Integer :: struct {signed: bool, endianness: Platform_Endianness};
Type_Info_Rune :: struct {};
Type_Info_Float :: struct {};
Type_Info_Complex :: struct {};
Type_Info_Quaternion :: struct {};
Type_Info_String :: struct {is_cstring: bool};
Type_Info_Boolean :: struct {};
Type_Info_Any :: struct {};
Type_Info_Type_Id :: struct {};
Type_Info_Pointer :: struct {
elem: ^Type_Info // nil -> rawptr
};
Type_Info_Procedure :: struct {
@@ -73,17 +81,19 @@ Type_Info_Tuple :: struct { // Only really used for procedures
Type_Info_Struct :: struct {
types: []^Type_Info,
names: []string,
offsets: []uintptr, // offsets may not be used in tuples
usings: []bool, // usings may not be used in tuples
offsets: []uintptr,
usings: []bool,
tags: []string,
is_packed: bool,
is_raw_union: bool,
custom_align: bool,
};
Type_Info_Union :: struct {
variants: []^Type_Info,
tag_offset: uintptr,
tag_type: ^Type_Info,
variants: []^Type_Info,
tag_offset: uintptr,
tag_type: ^Type_Info,
custom_align: bool,
no_nil: bool,
};
Type_Info_Enum :: struct {
base: ^Type_Info,
@@ -106,9 +116,14 @@ Type_Info_Bit_Set :: struct {
lower: i64,
upper: i64,
};
Type_Info_Opaque :: struct {
elem: ^Type_Info,
};
Type_Info_Simd_Vector :: struct {
elem: ^Type_Info,
elem_size: int,
count: int,
is_x86_mmx: bool,
}
Type_Info :: struct {
@@ -122,6 +137,7 @@ Type_Info :: struct {
Type_Info_Rune,
Type_Info_Float,
Type_Info_Complex,
Type_Info_Quaternion,
Type_Info_String,
Type_Info_Boolean,
Type_Info_Any,
@@ -139,6 +155,7 @@ Type_Info :: struct {
Type_Info_Bit_Field,
Type_Info_Bit_Set,
Type_Info_Opaque,
Type_Info_Simd_Vector,
},
}
@@ -149,6 +166,7 @@ Typeid_Kind :: enum u8 {
Rune,
Float,
Complex,
Quaternion,
String,
Boolean,
Any,
@@ -167,14 +185,16 @@ Typeid_Kind :: enum u8 {
Bit_Set,
Opaque,
}
#assert(len(Typeid_Kind) < 32);
Typeid_Bit_Field :: bit_field #align align_of(uintptr) {
index: 8*size_of(align_of(uintptr)) - 8,
index: 8*size_of(uintptr) - 8,
kind: 5, // Typeid_Kind
named: 1,
special: 1, // signed, cstring, etc
reserved: 1,
}
#assert(size_of(Typeid_Bit_Field) == size_of(uintptr));
// NOTE(bill): only the ones that are needed (not all types)
// This will be set by the compiler
@@ -189,6 +209,7 @@ Source_Code_Location :: struct {
file_path: string,
line, column: int,
procedure: string,
hash: u64,
}
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
@@ -199,9 +220,14 @@ Context :: struct {
assertion_failure_proc: Assertion_Failure_Proc,
logger: log.Logger,
stdin: os.Handle,
stdout: os.Handle,
stderr: os.Handle,
thread_id: int,
user_data: any,
user_ptr: rawptr,
user_index: int,
derived: any, // May be used for derived data types
@@ -211,7 +237,22 @@ global_scratch_allocator_data: mem.Scratch_Allocator;
Raw_Slice :: struct {
data: rawptr,
len: int,
}
Raw_Dynamic_Array :: struct {
data: rawptr,
len: int,
cap: int,
allocator: mem.Allocator,
}
Raw_Map :: struct {
hashes: []int,
entries: Raw_Dynamic_Array,
}
INITIAL_MAP_CAP :: 16;
@@ -235,10 +276,12 @@ Map_Entry_Header :: struct {
}
Map_Header :: struct {
m: ^mem.Raw_Map,
m: ^Raw_Map,
is_key_string: bool,
entry_size: int,
entry_align: int,
value_offset: uintptr,
value_size: int,
}
@@ -246,8 +289,6 @@ Map_Header :: struct {
type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
if info == nil do return nil;
@@ -262,19 +303,21 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
}
type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
if info == nil do return nil;
base := info;
loop: for {
switch i in base.variant {
case Type_Info_Named: base = i.base;
case Type_Info_Enum: base = i.base;
case Type_Info_Named: base = i.base;
case Type_Info_Enum: base = i.base;
case Type_Info_Opaque: base = i.elem;
case: break loop;
}
}
return base;
}
type_info_base_without_enum :: type_info_core;
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
data := transmute(Typeid_Bit_Field)id;
@@ -290,10 +333,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid {
ti = type_info_base(ti);
return ti.id;
}
typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid {
typeid_core :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_base_without_enum(type_info_of(id));
return ti.id;
}
typeid_base_without_enum :: typeid_core;
@@ -336,15 +380,19 @@ __init_context :: proc "contextless" (c: ^Context) {
c.logger.procedure = log.nil_logger_proc;
c.logger.data = nil;
c.stdin = os.stdin;
c.stdout = os.stdout;
c.stderr = os.stderr;
}
@(builtin)
@builtin
init_global_temporary_allocator :: proc(data: []byte, backup_allocator := context.allocator) {
mem.scratch_allocator_init(&global_scratch_allocator_data, data, backup_allocator);
}
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
fd := os.stderr;
fd := context.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " ");
os.write_string(fd, prefix);
@@ -358,25 +406,25 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
@(builtin)
@builtin
copy :: proc "contextless" (dst, src: $T/[]$E) -> int {
n := max(0, min(len(dst), len(src)));
if n > 0 do mem.copy(&dst[0], &src[0], n*size_of(E));
if n > 0 do mem_copy(&dst[0], &src[0], n*size_of(E));
return n;
}
@(builtin)
@builtin
pop :: proc "contextless" (array: ^$T/[dynamic]$E) -> E {
if array == nil do return E{};
assert(len(array) > 0);
res := array[len(array)-1];
(^mem.Raw_Dynamic_Array)(array).len -= 1;
(^Raw_Dynamic_Array)(array).len -= 1;
return res;
}
@(builtin)
@builtin
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
bounds_check_error_loc(loc, index, len(array));
n := len(array)-1;
@@ -386,83 +434,82 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
pop(array);
}
@(builtin)
@builtin
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
bounds_check_error_loc(loc, index, len(array));
copy(array[index:], array[index+1:]);
if index+1 < len(array) {
copy(array[index:], array[index+1:]);
}
pop(array);
}
@(builtin)
clear :: proc[clear_dynamic_array, clear_map];
@builtin
clear :: proc{clear_dynamic_array, clear_map};
@(builtin)
reserve :: proc[reserve_dynamic_array, reserve_map];
@builtin
reserve :: proc{reserve_dynamic_array, reserve_map};
@(builtin)
resize :: proc[resize_dynamic_array];
@builtin
resize :: proc{resize_dynamic_array};
@(builtin)
new :: proc[mem.new];
@builtin
new :: proc{mem.new};
@(builtin)
new_clone :: proc[mem.new_clone];
@builtin
new_clone :: proc{mem.new_clone};
@(builtin)
free :: proc[mem.free];
@builtin
free :: proc{mem.free};
@(builtin)
free_all :: proc[mem.free_all];
@builtin
free_all :: proc{mem.free_all};
@(builtin)
delete :: proc[
@builtin
delete :: proc{
mem.delete_string,
mem.delete_cstring,
mem.delete_dynamic_array,
mem.delete_slice,
mem.delete_map,
];
};
@(builtin)
make :: proc[
@builtin
make :: proc{
mem.make_slice,
mem.make_dynamic_array,
mem.make_dynamic_array_len,
mem.make_dynamic_array_len_cap,
mem.make_map,
];
};
@(builtin)
@builtin
clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil do return;
raw_map := (^mem.Raw_Map)(m);
entries := (^mem.Raw_Dynamic_Array)(&raw_map.entries);
raw_map := (^Raw_Map)(m);
entries := (^Raw_Dynamic_Array)(&raw_map.entries);
entries.len = 0;
for _, i in raw_map.hashes {
raw_map.hashes[i] = -1;
}
}
@(builtin)
@builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
if m != nil do __dynamic_map_reserve(__get_map_header(m), capacity);
}
@(builtin)
@builtin
delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
if m != nil do __dynamic_map_delete_key(__get_map_header(m), __get_map_key(key));
}
@(builtin)
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
if array == nil do return 0;
@builtin
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
if array == nil do return;
arg_len := 1;
@@ -472,20 +519,20 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
a := (^mem.Raw_Dynamic_Array)(array);
a := (^Raw_Dynamic_Array)(array);
data := (^E)(a.data);
assert(data != nil);
mem.copy(mem.ptr_offset(data, a.len), &arg, size_of(E));
val := arg;
mem_copy(mem.ptr_offset(data, a.len), &val, size_of(E));
a.len += arg_len;
}
return len(array);
}
@(builtin)
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
if array == nil do return 0;
@builtin
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
if array == nil do return;
arg_len := len(args);
if arg_len <= 0 do return len(array);
if arg_len <= 0 do return;
if cap(array) <= len(array)+arg_len {
@@ -494,35 +541,33 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
}
arg_len = min(cap(array)-len(array), arg_len);
if arg_len > 0 {
a := (^mem.Raw_Dynamic_Array)(array);
a := (^Raw_Dynamic_Array)(array);
data := (^E)(a.data);
assert(data != nil);
mem.copy(mem.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
mem_copy(mem.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
a.len += arg_len;
}
return len(array);
}
@(builtin) append :: proc[append_elem, append_elems];
@builtin append :: proc{append_elem, append_elems};
@(builtin)
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> int {
@builtin
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
for arg in args {
append(array = array, args = ([]E)(arg), loc = loc);
}
return len(array);
}
@(builtin)
@builtin
clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) {
if array != nil do (^mem.Raw_Dynamic_Array)(array).len = 0;
if array != nil do (^Raw_Dynamic_Array)(array).len = 0;
}
@(builtin)
@builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
if array == nil do return false;
a := (^mem.Raw_Dynamic_Array)(array);
a := (^Raw_Dynamic_Array)(array);
if capacity <= a.cap do return true;
@@ -546,10 +591,10 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
return true;
}
@(builtin)
@builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
if array == nil do return false;
a := (^mem.Raw_Dynamic_Array)(array);
a := (^Raw_Dynamic_Array)(array);
if length <= a.cap {
a.len = max(length, 0);
@@ -579,69 +624,91 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
@(builtin)
@builtin
incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
s^ |= {elem};
return s^;
}
@(builtin)
@builtin
incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
for elem in elems do s^ |= {elem};
return s^;
}
@(builtin)
@builtin
incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
s^ |= other;
return s^;
}
@(builtin)
@builtin
excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
s^ &~= {elem};
return s^;
}
@(builtin)
@builtin
excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
for elem in elems do s^ &~= {elem};
return s^;
}
@(builtin)
@builtin
excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
s^ &~= other;
return s^;
}
@(builtin) incl :: proc[incl_elem, incl_elems, incl_bit_set];
@(builtin) excl :: proc[excl_elem, excl_elems, excl_bit_set];
@builtin incl :: proc{incl_elem, incl_elems, incl_bit_set};
@builtin excl :: proc{excl_elem, excl_elems, excl_bit_set};
@builtin
card :: proc(s: $S/bit_set[$E; $U]) -> int {
when size_of(S) == 1 {
foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
return int(count_ones(transmute(u8)s));
} else when size_of(S) == 2 {
foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
return int(count_ones(transmute(u16)s));
} else when size_of(S) == 4 {
foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
return int(count_ones(transmute(u32)s));
} else when size_of(S) == 8 {
foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
return int(count_ones(transmute(u64)s));
} else {
#assert(false);
return 0;
}
}
@(builtin)
assert :: proc "contextless" (condition: bool, message := "", loc := #caller_location) -> bool {
@builtin
assert :: proc(condition: bool, message := "", loc := #caller_location) -> bool {
if !condition {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("Runtime assertion", message, loc);
proc(message: string, loc: Source_Code_Location) {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("runtime assertion", message, loc);
}(message, loc);
}
return condition;
}
@(builtin)
panic :: proc "contextless" (message: string, loc := #caller_location) -> ! {
@builtin
panic :: proc(message: string, loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
}
p("Panic", message, loc);
p("panic", message, loc);
}
@(builtin)
unimplemented :: proc "contextless" (message := "", loc := #caller_location) -> ! {
@builtin
unimplemented :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
@@ -649,8 +716,8 @@ unimplemented :: proc "contextless" (message := "", loc := #caller_location) ->
p("not yet implemented", message, loc);
}
@(builtin)
unreachable :: proc "contextless" (message := "", loc := #caller_location) -> ! {
@builtin
unreachable :: proc(message := "", loc := #caller_location) -> ! {
p := context.assertion_failure_proc;
if p == nil {
p = default_assertion_failure_proc;
@@ -667,7 +734,7 @@ unreachable :: proc "contextless" (message := "", loc := #caller_location) -> !
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) {
array := (^mem.Raw_Dynamic_Array)(array_);
array := (^Raw_Dynamic_Array)(array_);
array.allocator = context.allocator;
assert(array.allocator.procedure != nil);
@@ -678,7 +745,7 @@ __dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, ca
}
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool {
array := (^mem.Raw_Dynamic_Array)(array_);
array := (^Raw_Dynamic_Array)(array_);
if cap <= array.cap do return true;
@@ -700,7 +767,7 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
}
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
array := (^mem.Raw_Dynamic_Array)(array_);
array := (^Raw_Dynamic_Array)(array_);
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc);
if ok do array.len = len;
@@ -710,7 +777,7 @@ __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len:
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int, loc := #caller_location) -> int {
array := (^mem.Raw_Dynamic_Array)(array_);
array := (^Raw_Dynamic_Array)(array_);
if items == nil do return 0;
if item_count <= 0 do return 0;
@@ -727,13 +794,13 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
assert(array.data != nil);
data := uintptr(array.data) + uintptr(elem_size*array.len);
mem.copy(rawptr(data), items, elem_size * item_count);
mem_copy(rawptr(data), items, elem_size * item_count);
array.len += item_count;
return array.len;
}
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int {
array := (^mem.Raw_Dynamic_Array)(array_);
array := (^Raw_Dynamic_Array)(array_);
ok := true;
if array.cap <= array.len+1 {
@@ -756,15 +823,14 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
// Map
__get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
header := Map_Header{m = (^mem.Raw_Map)(m)};
header := Map_Header{m = (^Raw_Map)(m)};
Entry :: struct {
key: Map_Key,
next: int,
value: V,
}
};
_, is_string := type_info_base(type_info_of(K)).variant.(Type_Info_String);
header.is_key_string = is_string;
header.is_key_string = intrinsics.type_is_string(K);
header.entry_size = int(size_of(Entry));
header.entry_align = int(align_of(Entry));
header.value_offset = uintptr(offset_of(Entry, value));
@@ -772,54 +838,66 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header {
return header;
}
__get_map_key :: proc "contextless" (key: $K) -> Map_Key {
__get_map_key :: proc "contextless" (k: $K) -> Map_Key {
key := k;
map_key: Map_Key;
ti := type_info_base_without_enum(type_info_of(K));
switch _ in ti.variant {
case Type_Info_Integer:
switch 8*size_of(key) {
case 8: map_key.hash = u64(( ^u8)(&key)^);
case 16: map_key.hash = u64(( ^u16)(&key)^);
case 32: map_key.hash = u64(( ^u32)(&key)^);
case 64: map_key.hash = u64(( ^u64)(&key)^);
case: panic("Unhandled integer size");
}
case Type_Info_Rune:
T :: intrinsics.type_core_type(K);
when intrinsics.type_is_integer(T) {
sz :: 8*size_of(T);
when sz == 8 do map_key.hash = u64(( ^u8)(&key)^);
else when sz == 16 do map_key.hash = u64((^u16)(&key)^);
else when sz == 32 do map_key.hash = u64((^u32)(&key)^);
else when sz == 64 do map_key.hash = u64((^u64)(&key)^);
else do #assert(false, "Unhandled integer size");
} else when intrinsics.type_is_rune(T) {
map_key.hash = u64((^rune)(&key)^);
case Type_Info_Pointer:
} else when intrinsics.type_is_pointer(T) {
map_key.hash = u64(uintptr((^rawptr)(&key)^));
case Type_Info_Float:
switch 8*size_of(key) {
case 32: map_key.hash = u64((^u32)(&key)^);
case 64: map_key.hash = u64((^u64)(&key)^);
case: panic("Unhandled float size");
}
case Type_Info_String:
} else when intrinsics.type_is_float(T) {
sz :: 8*size_of(T);
when sz == 32 do map_key.hash = u64((^u32)(&key)^);
else when sz == 64 do map_key.hash = u64((^u64)(&key)^);
else do #assert(false, "Unhandled float size");
} else when intrinsics.type_is_string(T) {
#assert(T == string);
str := (^string)(&key)^;
map_key.hash = default_hash_string(str);
map_key.str = str;
case:
panic("Unhandled map key type");
} else {
#assert(false, "Unhandled map key type");
}
return map_key;
}
_fnv64a :: proc(data: []byte, seed: u64 = 0xcbf29ce484222325) -> u64 {
h: u64 = seed;
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
default_hash :: proc(data: []byte) -> u64 {
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
return fnv64a(data);
return _fnv64a(data);
}
default_hash_string :: proc(s: string) -> u64 do return default_hash(([]byte)(s));
source_code_location_hash :: proc(s: Source_Code_Location) -> u64 {
hash := _fnv64a(cast([]byte)s.file_path);
hash = hash ~ (u64(s.line) * 0x100000001b3);
hash = hash ~ (u64(s.column) * 0x100000001b3);
return hash;
}
__slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: mem.Allocator, loc := #caller_location) -> bool {
array := (^mem.Raw_Slice)(array_);
array := (^Raw_Slice)(array_);
if new_count < array.len do return true;
@@ -840,12 +918,12 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller
old_len := len(m.hashes);
__slice_resize(&m.hashes, cap, m.entries.allocator, loc);
for i in old_len..len(m.hashes)-1 do m.hashes[i] = -1;
for i in old_len..<len(m.hashes) do m.hashes[i] = -1;
}
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check {
new_header: Map_Header = header;
nm := mem.Raw_Map{};
nm := Raw_Map{};
nm.entries.allocator = m.entries.allocator;
new_header.m = &nm;
@@ -857,9 +935,9 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len, loc);
__slice_resize(&nm.hashes, new_count, m.entries.allocator, loc);
for i in 0 .. new_count-1 do nm.hashes[i] = -1;
for i in 0 ..< new_count do nm.hashes[i] = -1;
for i in 0 .. m.entries.len-1 {
for i in 0 ..< m.entries.len {
if len(nm.hashes) == 0 do __dynamic_map_grow(new_header, loc);
entry_header := __dynamic_map_get_entry(header, i);
@@ -877,7 +955,7 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c
e := __dynamic_map_get_entry(new_header, j);
e.next = fr.entry_index;
ndata := uintptr(e);
mem.copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size);
mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size);
if __dynamic_map_full(new_header) do __dynamic_map_grow(new_header, loc);
}
@@ -896,7 +974,6 @@ __dynamic_map_get :: proc(h: Map_Header, key: Map_Key) -> rawptr {
}
__dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #caller_location) #no_bounds_check {
index: int;
assert(value != nil);
@@ -921,7 +998,7 @@ __dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #ca
e := __dynamic_map_get_entry(h, index);
e.key = key;
val := (^byte)(uintptr(e) + h.value_offset);
mem.copy(val, value, h.value_size);
mem_copy(val, value, h.value_size);
}
if __dynamic_map_full(h) {
@@ -1000,7 +1077,7 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds
} else {
old := __dynamic_map_get_entry(h, fr.entry_index);
end := __dynamic_map_get_entry(h, m.entries.len-1);
mem.copy(old, end, entry_size);
mem_copy(old, end, entry_size);
if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 {
last_entry := __dynamic_map_get_entry(h, last.entry_prev);
+348 -75
View File
@@ -1,16 +1,30 @@
package runtime
import "core:mem"
import "core:os"
import "core:unicode/utf8"
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil do return dst;
// NOTE(bill): This _must_ be implemented like C's memmove
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memmove(dst, src, len, 1, false);
return dst;
}
print_u64 :: proc(fd: os.Handle, u: u64) {
print_u64 :: proc(fd: os.Handle, x: u64) {
digits := "0123456789";
a: [129]byte;
i := len(a);
b := u64(10);
u := x;
for u >= b {
i -= 1; a[i] = digits[u % b];
u /= b;
@@ -20,15 +34,16 @@ print_u64 :: proc(fd: os.Handle, u: u64) {
os.write(fd, a[i:]);
}
print_i64 :: proc(fd: os.Handle, u: i64) {
print_i64 :: proc(fd: os.Handle, x: i64) {
digits := "0123456789";
b :: i64(10);
u := x;
neg := u < 0;
u = abs(u);
a: [129]byte;
i := len(a);
b := i64(10);
for u >= b {
i -= 1; a[i] = digits[u % b];
u /= b;
@@ -223,9 +238,96 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
print_type(fd, info.underlying);
}
os.write_byte(fd, ']');
case Type_Info_Opaque:
os.write_string(fd, "opaque ");
print_type(fd, info.elem);
case Type_Info_Simd_Vector:
if info.is_x86_mmx {
os.write_string(fd, "intrinsics.x86_mmx");
} else {
os.write_string(fd, "intrinsics.vector(");
print_u64(fd, u64(info.count));
os.write_string(fd, ", ");
print_type(fd, info.elem);
os.write_byte(fd, ')');
}
}
}
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
x := uintptr(a);
y := uintptr(b);
n := uintptr(n);
SU :: size_of(uintptr);
fast := uintptr(n/SU + 1);
offset := (fast-1)*SU;
curr_block := uintptr(0);
if n < SU {
fast = 0;
}
for /**/; curr_block < fast; curr_block += 1 {
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
vb := (^uintptr)(y + curr_block * size_of(uintptr))^;
if va ~ vb != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
a := (^byte)(x+pos)^;
b := (^byte)(y+pos)^;
if a ~ b != 0 {
return (int(a) - int(b)) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
a := (^byte)(x+offset)^;
b := (^byte)(y+offset)^;
if a ~ b != 0 {
return (int(a) - int(b)) < 0 ? -1 : +1;
}
}
return 0;
}
memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_check {
x := uintptr(a);
n := uintptr(n);
SU :: size_of(uintptr);
fast := uintptr(n/SU + 1);
offset := (fast-1)*SU;
curr_block := uintptr(0);
if n < SU {
fast = 0;
}
for /**/; curr_block < fast; curr_block += 1 {
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
if va ~ 0 != 0 {
for pos := curr_block*SU; pos < n; pos += 1 {
a := (^byte)(x+pos)^;
if a ~ 0 != 0 {
return int(a) < 0 ? -1 : +1;
}
}
}
}
for /**/; offset < n; offset += 1 {
a := (^byte)(x+offset)^;
if a ~ 0 != 0 {
return int(a) < 0 ? -1 : +1;
}
}
return 0;
}
string_eq :: proc "contextless" (a, b: string) -> bool {
switch {
case len(a) != len(b): return false;
@@ -236,7 +338,7 @@ string_eq :: proc "contextless" (a, b: string) -> bool {
}
string_cmp :: proc "contextless" (a, b: string) -> int {
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
return memory_compare(&a[0], &b[0], min(len(a), len(b)));
}
string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
@@ -246,18 +348,23 @@ string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cm
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
cstring_len :: proc "contextless" (s: cstring) -> int {
n := 0;
for p := (^byte)(s); p != nil && p^ != 0; p = mem.ptr_offset(p, 1) {
n += 1;
p0 := uintptr((^byte)(s));
p := p0;
for p != 0 && (^byte)(p)^ != 0 {
p += 1;
}
return n;
return int(p - p0);
}
cstring_to_string :: proc "contextless" (s: cstring) -> string {
Raw_String :: struct {
data: ^byte,
len: int,
};
if s == nil do return "";
ptr := (^byte)(s);
n := cstring_len(s);
return transmute(string)mem.Raw_String{ptr, n};
return transmute(string)Raw_String{ptr, n};
}
@@ -268,24 +375,31 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r
complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
if 0 <= index && index < count do return;
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
os.write_string(fd, " Index ");
print_i64(fd, i64(index));
os.write_string(fd, " is out of bounds range 0:");
print_i64(fd, i64(count));
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
os.write_string(fd, " Index ");
print_i64(fd, i64(index));
os.write_string(fd, " is out of bounds range 0:");
print_i64(fd, i64(count));
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(file, line, column, index, count);
}
slice_expr_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
if 0 <= lo && lo <= hi && hi <= len do return;
slice_handle_error :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
os.write_string(fd, " Invalid slice indices: ");
print_i64(fd, i64(lo));
os.write_string(fd, ":");
@@ -296,45 +410,139 @@ slice_expr_error :: proc "contextless" (file: string, line, column: int, lo, hi:
debug_trap();
}
slice_expr_error_hi :: proc "contextless" (file: string, line, column: int, hi: int, len: int) {
if 0 <= hi && hi <= len do return;
slice_handle_error(file, line, column, 0, hi, len);
}
slice_expr_error_lo_hi :: proc "contextless" (file: string, line, column: int, lo, hi: int, len: int) {
if 0 <= lo && lo <= len && lo <= hi && hi <= len do return;
slice_handle_error(file, line, column, lo, hi, len);
}
dynamic_array_expr_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
if 0 <= low && low <= high && high <= max do return;
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
os.write_string(fd, " Invalid dynamic array values: ");
print_i64(fd, i64(low));
os.write_string(fd, ":");
print_i64(fd, i64(high));
os.write_string(fd, ":");
print_i64(fd, i64(max));
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (file: string, line, column: int, low, high, max: int) {
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
os.write_string(fd, " Invalid dynamic array values: ");
print_i64(fd, i64(low));
os.write_string(fd, ":");
print_i64(fd, i64(high));
os.write_string(fd, ":");
print_i64(fd, i64(max));
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(file, line, column, low, high, max);
}
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: int, from, to: typeid) {
if ok do return;
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, ""});
os.write_string(fd, " Invalid type assertion from ");
print_typeid(fd, from);
os.write_string(fd, " to ");
print_typeid(fd, to);
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (file: string, line, column: int, from, to: typeid) {
fd := os.stderr;
print_caller_location(fd, Source_Code_Location{file, line, column, "", 0});
os.write_string(fd, " Invalid type assertion from ");
print_typeid(fd, from);
os.write_string(fd, " to ");
print_typeid(fd, to);
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(file, line, column, from, to);
}
string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
return utf8.decode_rune_from_string(s);
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
@static accept_sizes := [256]u8{
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
};
Accept_Range :: struct {lo, hi: u8};
@static accept_ranges := [5]Accept_Range{
{0x80, 0xbf},
{0xa0, 0xbf},
{0x80, 0x9f},
{0x90, 0xbf},
{0x80, 0x8f},
};
MASKX :: 0b0011_1111;
MASK2 :: 0b0001_1111;
MASK3 :: 0b0000_1111;
MASK4 :: 0b0000_0111;
LOCB :: 0b1000_0000;
HICB :: 0b1011_1111;
RUNE_ERROR :: '\ufffd';
n := len(s);
if n < 1 {
return RUNE_ERROR, 0;
}
s0 := s[0];
x := accept_sizes[s0];
if x >= 0xF0 {
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
}
sz := x & 7;
accept := accept_ranges[x>>4];
if n < int(sz) {
return RUNE_ERROR, 1;
}
b1 := s[1];
if b1 < accept.lo || accept.hi < b1 {
return RUNE_ERROR, 1;
}
if sz == 2 {
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
}
b2 := s[2];
if b2 < LOCB || HICB < b2 {
return RUNE_ERROR, 1;
}
if sz == 3 {
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
}
b3 := s[3];
if b3 < LOCB || HICB < b3 {
return RUNE_ERROR, 1;
}
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) {
bounds_check_error(file_path, int(line), int(column), index, count);
}
slice_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) {
slice_expr_error(file_path, int(line), int(column), lo, hi, len);
slice_expr_error_hi_loc :: inline proc "contextless" (using loc := #caller_location, hi: int, len: int) {
slice_expr_error_hi(file_path, int(line), int(column), hi, len);
}
slice_expr_error_lo_hi_loc :: inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) {
slice_expr_error_lo_hi(file_path, int(line), int(column), lo, hi, len);
}
dynamic_array_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, low, high, max: int) {
@@ -342,39 +550,45 @@ dynamic_array_expr_error_loc :: inline proc "contextless" (using loc := #caller_
}
make_slice_error_loc :: inline proc "contextless" (using loc := #caller_location, len: int) {
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
if 0 <= len do return;
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid slice length for make: ");
print_i64(fd, i64(len));
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid slice length for make: ");
print_i64(fd, i64(len));
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(loc, len);
}
make_dynamic_array_error_loc :: inline proc "contextless" (using loc := #caller_location, len, cap: int) {
if 0 <= len && len <= cap do return;
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid dynamic array parameters for make: ");
print_i64(fd, i64(len));
os.write_byte(fd, ':');
print_i64(fd, i64(cap));
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid dynamic array parameters for make: ");
print_i64(fd, i64(len));
os.write_byte(fd, ':');
print_i64(fd, i64(cap));
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(loc, len, cap);
}
make_map_expr_error_loc :: inline proc "contextless" (using loc := #caller_location, cap: int) {
make_map_expr_error_loc :: inline proc "contextless" (loc := #caller_location, cap: int) {
if 0 <= cap do return;
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid map capacity for make: ");
print_i64(fd, i64(cap));
os.write_byte(fd, '\n');
debug_trap();
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
fd := os.stderr;
print_caller_location(fd, loc);
os.write_string(fd, " Invalid map capacity for make: ");
print_i64(fd, i64(cap));
os.write_byte(fd, '\n');
debug_trap();
}
handle_error(loc, cap);
}
@@ -431,9 +645,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 {
r, i := real(x), imag(x);
return _sqrt_f64(r*r + i*i);
}
abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f32(r*r + i*i + j*j + k*k);
}
abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f64(r*r + i*i + j*j + k*k);
}
quo_complex64 :: proc(n, m: complex64) -> complex64 {
quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 {
e, f: f32;
if abs(real(m)) >= abs(imag(m)) {
@@ -451,7 +672,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 {
return complex(e, f);
}
quo_complex128 :: proc(n, m: complex128) -> complex128 {
quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
e, f: f64;
if abs(real(m)) >= abs(imag(m)) {
@@ -468,3 +689,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 {
return complex(e, f);
}
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
return quaternion(t0, t1, t2, t3);
}
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
return quaternion(t0, t1, t2, t3);
}
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
return quaternion(t0, t1, t2, t3);
}
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
return quaternion(t0, t1, t2, t3);
}
+202
View File
@@ -0,0 +1,202 @@
package runtime
@(default_calling_convention="none")
foreign {
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_ctz :: proc{
_ctz_u8,
_ctz_u16,
_ctz_u32,
_ctz_u64,
};
@(default_calling_convention="none")
foreign {
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
}
_clz :: proc{
_clz_u8,
_clz_u16,
_clz_u32,
_clz_u64,
};
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
n := transmute([2]u64)a;
d := transmute([2]u64)b;
q, r: [2]u64 = ---, ---;
sr: u32 = 0;
low :: ODIN_ENDIAN == "big" ? 1 : 0;
high :: 1 - low;
U64_BITS :: 8*size_of(u64);
U128_BITS :: 8*size_of(u128);
// Special Cases
if n[high] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[low] % d[low]);
}
return u128(n[low] / d[low]);
}
if rem != nil {
rem^ = u128(n[low]);
}
return 0;
}
if d[low] == 0 {
if d[high] == 0 {
if rem != nil {
rem^ = u128(n[high] % d[low]);
}
return u128(n[high] / d[low]);
}
if n[low] == 0 {
if rem != nil {
r[high] = n[high] % d[high];
r[low] = 0;
rem^ = transmute(u128)r;
}
return u128(n[high] / d[high]);
}
if d[high] & (d[high]-1) == 0 {
if rem != nil {
r[low] = n[low];
r[high] = n[high] & (d[high] - 1);
rem^ = transmute(u128)r;
}
return u128(n[high] >> _ctz(d[high]));
}
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 2 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
q[high] = n[low] << u64(U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
} else {
if d[high] == 0 {
if d[low] & (d[low] - 1) == 0 {
if rem != nil {
rem^ = u128(n[low] & (d[low] - 1));
}
if d[low] == 1 {
return a;
}
sr = u32(_ctz(d[low]));
q[high] = n[high] >> sr;
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
return transmute(u128)q;
}
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
switch {
case sr == U64_BITS:
q[low] = 0;
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
case sr < U64_BITS:
q[low] = 0;
q[high] = n[low] << (U64_BITS - sr);
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
case:
q[low] = n[low] << (U128_BITS - sr);
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
r[high] = 0;
r[low] = n[high] >> (sr - U64_BITS);
}
} else {
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
if sr > U64_BITS - 1 {
if rem != nil {
rem^ = a;
}
return 0;
}
sr += 1;
q[low] = 0;
if sr == U64_BITS {
q[high] = n[low];
r[high] = 0;
r[low] = n[high];
} else {
r[high] = n[high] >> sr;
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
q[high] = n[low] << (U64_BITS - sr);
}
}
}
carry: u32 = 0;
r_all: u128 = ---;
for ; sr > 0; sr -= 1 {
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
q[low] = (q[low] << 1) | u64(carry);
r_all = transmute(u128)r;
s := i128(b - r_all - 1) >> (U128_BITS - 1);
carry = u32(s & 1);
r_all -= b & transmute(u128)s;
r = transmute([2]u64)r_all;
}
q_all := ((transmute(u128)q) << 1) | u128(carry);
if rem != nil {
rem^ = r_all;
}
return q_all;
}
@(link_name="__umodti3")
umodti3 :: proc "c" (a, b: i128) -> i128 {
s_a := a >> (128 - 1);
s_b := b >> (128 - 1);
an := (a ~ s_a) - s_a;
bn := (b ~ s_b) - s_b;
r: u128 = ---;
_ = udivmod128(transmute(u128)an, transmute(u128)bn, &r);
return (transmute(i128)r ~ s_a) - s_a;
}
@(link_name="__udivmodti4")
udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
return udivmod128(a, b, rem);
}
@(link_name="__udivti3")
udivti3 :: proc "c" (a, b: u128) -> u128 {
return udivmodti4(a, b, nil);
}
+20 -11
View File
@@ -1,6 +1,7 @@
package sort
import "core:mem"
import "intrinsics"
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil);
@@ -11,7 +12,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
for {
init_swap, prev_swap := -1, -1;
for j in init_j..last_j-1 {
for j in init_j..<last_j {
if f(array[j], array[j+1]) > 0 {
array[j], array[j+1] = array[j+1], array[j];
prev_swap = j;
@@ -26,7 +27,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
}
}
bubble_sort :: proc(array: $A/[]$T) {
bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
count := len(array);
init_j, last_j := 0, count-1;
@@ -34,7 +35,7 @@ bubble_sort :: proc(array: $A/[]$T) {
for {
init_swap, prev_swap := -1, -1;
for j in init_j..last_j-1 {
for j in init_j..<last_j {
if array[j] > array[j+1] {
array[j], array[j+1] = array[j+1], array[j];
prev_swap = j;
@@ -73,7 +74,7 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
quick_sort_proc(a[i:n], f);
}
quick_sort :: proc(array: $A/[]$T) {
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
a := array;
n := len(a);
if n < 2 do return;
@@ -96,9 +97,9 @@ quick_sort :: proc(array: $A/[]$T) {
quick_sort(a[i:n]);
}
_log2 :: proc(n: int) -> int {
_log2 :: proc(x: int) -> int {
res := 0;
for ; n != 0; n >>= 1 do res += 1;
for n := x; n != 0; n >>= 1 do res += 1;
return res;
}
@@ -106,7 +107,7 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
merge_slices :: proc(arr1, arr2, out: A, f: proc(T, T) -> int) {
N1, N2 := len(arr1), len(arr2);
i, j := 0, 0;
for k in 0..N1+N2-1 {
for k in 0..<N1+N2 {
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
out[k] = arr1[i];
i += 1;
@@ -127,7 +128,7 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M {
for j in 0..a-1 {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:], f);
}
@@ -146,11 +147,11 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
if M & 1 == 0 do copy(arr2, arr1);
}
merge_sort :: proc(array: $A/[]$T) {
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
merge_slices :: proc(arr1, arr2, out: A) {
N1, N2 := len(arr1), len(arr2);
i, j := 0, 0;
for k in 0..N1+N2-1 {
for k in 0..<N1+N2 {
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
out[k] = arr1[i];
i += 1;
@@ -169,7 +170,7 @@ merge_sort :: proc(array: $A/[]$T) {
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M {
for j in 0..a-1 {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:]);
}
@@ -189,6 +190,14 @@ merge_sort :: proc(array: $A/[]$T) {
}
compare_bools :: proc(a, b: bool) -> int {
switch {
case !a && b: return -1;
case a && !b: return +1;
}
return 0;
}
compare_ints :: proc(a, b: int) -> int {
switch delta := a - b; {
@@ -1,6 +1,6 @@
// Multiple precision decimal numbers
// NOTE: This is only for floating point printing and nothing else
package decimal
package strconv_decimal
Decimal :: struct {
digits: [384]byte, // big-endian digits
@@ -20,29 +20,29 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
// TODO(bill): make this work with a buffer that's not big enough
assert(len(buf) >= n);
buf = buf[0:n];
b := buf[0:n];
if a.count == 0 {
buf[0] = '0';
return string(buf[0:1]);
b[0] = '0';
return string(b[0:1]);
}
w := 0;
if a.decimal_point <= 0 {
buf[w] = '0'; w += 1;
buf[w] = '.'; w += 1;
w += digit_zero(buf[w : w-a.decimal_point]);
w += copy(buf[w:], a.digits[0:a.count]);
b[w] = '0'; w += 1;
b[w] = '.'; w += 1;
w += digit_zero(b[w : w-a.decimal_point]);
w += copy(b[w:], a.digits[0:a.count]);
} else if a.decimal_point < a.count {
w += copy(buf[w:], a.digits[0:a.decimal_point]);
buf[w] = '.'; w += 1;
w += copy(buf[w:], a.digits[a.decimal_point : a.count]);
w += copy(b[w:], a.digits[0:a.decimal_point]);
b[w] = '.'; w += 1;
w += copy(b[w:], a.digits[a.decimal_point : a.count]);
} else {
w += copy(buf[w:], a.digits[0:a.count]);
w += digit_zero(buf[w : w+a.decimal_point-a.count]);
w += copy(b[w:], a.digits[0:a.count]);
w += digit_zero(b[w : w+a.decimal_point-a.count]);
}
return string(buf[0:w]);
return string(b[0:w]);
}
// trim trailing zeros
@@ -56,10 +56,10 @@ trim :: proc(a: ^Decimal) {
}
assign :: proc(a: ^Decimal, i: u64) {
assign :: proc(a: ^Decimal, idx: u64) {
buf: [64]byte;
n := 0;
for i > 0 {
for i := idx; i > 0; {
j := i/10;
i -= 10*j;
buf[n] = byte('0'+i);
@@ -130,10 +130,15 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
shift_left :: proc(a: ^Decimal, k: uint) {
delta := int(k/4);
// NOTE(bill): used to determine buffer size required for the decimal from the binary shift
// 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
log10_2 :: 0.301029995663981195213738894724493026768189881462108541310;
capacity := int(f64(k)*log10_2 + 1);
r := a.count; // read index
w := a.count+delta; // write index
r := a.count; // read index
w := a.count+capacity; // write index
d := len(a.digits);
n: uint;
for r -= 1; r >= 0; r -= 1 {
@@ -141,7 +146,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10;
rem := n - 10*quo;
w -= 1;
if w < len(a.digits) {
if w < d {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
@@ -153,7 +158,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10;
rem := n - 10*quo;
w -= 1;
if 0 <= w && w < len(a.digits) {
if w < d {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
@@ -161,17 +166,20 @@ shift_left :: proc(a: ^Decimal, k: uint) {
n = quo;
}
a.count += delta;
a.count = min(a.count, len(a.digits));
a.decimal_point += delta;
// NOTE(bill): Remove unused buffer size
assert(w >= 0);
capacity -= w;
a.count = min(a.count+capacity, d);
a.decimal_point += capacity;
trim(a);
}
shift :: proc(a: ^Decimal, k: int) {
shift :: proc(a: ^Decimal, i: int) {
uint_size :: 8*size_of(uint);
max_shift :: uint_size-4;
switch {
switch k := i; {
case a.count == 0:
// no need to update
case k > 0:
@@ -253,3 +261,4 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
}
return n;
}
+439
View File
@@ -0,0 +1,439 @@
package strconv
using import "decimal"
Int_Flag :: enum {
Prefix,
Plus,
Space,
}
Int_Flags :: bit_set[Int_Flag];
Decimal_Slice :: struct {
digits: []byte,
count: int,
decimal_point: int,
neg: bool,
}
Float_Info :: struct {
mantbits: uint,
expbits: uint,
bias: int,
}
_f16_info := Float_Info{10, 5, -15};
_f32_info := Float_Info{23, 8, -127};
_f64_info := Float_Info{52, 11, -1023};
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
bits: u64;
flt: ^Float_Info;
switch bit_size {
case 32:
bits = u64(transmute(u32)f32(val));
flt = &_f32_info;
case 64:
bits = transmute(u64)val;
flt = &_f64_info;
case:
panic("strconv: invalid bit_size");
}
neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (u64(1) << flt.mantbits - 1);
switch exp {
case 1<<flt.expbits - 1:
s: string;
if mant != 0 {
s = "NaN";
} else if neg {
s = "-Inf";
} else {
s = "+Inf";
}
n := copy(buf, cast([]byte)s);
return buf[:n];
case 0: // denormalized
exp += 1;
case:
mant |= u64(1) << flt.mantbits;
}
exp += flt.bias;
d_: Decimal;
d := &d_;
assign(d, mant);
shift(d, exp - int(flt.mantbits));
digs: Decimal_Slice;
prec := precision;
shortest := prec < 0;
if shortest {
round_shortest(d, mant, exp, flt);
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
switch fmt {
case 'e', 'E': prec = digs.count-1;
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
case 'g', 'G': prec = digs.count;
}
} else {
switch fmt {
case 'e', 'E': round(d, prec+1);
case 'f', 'F': round(d, d.decimal_point+prec);
case 'g', 'G':
if prec == 0 {
prec = 1;
}
round(d, prec);
}
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
}
return format_digits(buf, shortest, neg, digs, prec, fmt);
}
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
Buffer :: struct {
b: []byte,
n: int,
};
to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n];
add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
buf.n += copy(buf.b[buf.n:], bytes);
}
b := Buffer{b = buf};
prec := precision;
switch fmt {
case 'f', 'F':
add_bytes(&b, neg ? '-' : '+');
// integer, padded with zeros when needed
if digs.decimal_point > 0 {
m := min(digs.count, digs.decimal_point);
add_bytes(&b, ..digs.digits[0:m]);
for ; m < digs.decimal_point; m += 1 {
add_bytes(&b, '0');
}
} else {
add_bytes(&b, '0');
}
// fractional part
if prec > 0 {
add_bytes(&b, '.');
for i in 0..<prec {
c: byte = '0';
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
c = digs.digits[j];
}
add_bytes(&b, c);
}
}
return to_bytes(b);
case 'e', 'E':
add_bytes(&b, neg ? '-' : '+');
ch := byte('0');
if digs.count != 0 {
ch = digs.digits[0];
}
add_bytes(&b, ch);
if prec > 0 {
add_bytes(&b, '.');
i := 1;
m := min(digs.count, prec+1);
if i < m {
add_bytes(&b, ..digs.digits[i:m]);
i = m;
}
for ; i <= prec; i += 1 {
add_bytes(&b, '0');
}
}
add_bytes(&b, fmt);
exp := digs.decimal_point-1;
if digs.count == 0 {
// Zero has exponent of 0
exp = 0;
}
ch = '+';
if exp < 0 {
ch = '-';
exp = -exp;
}
add_bytes(&b, ch);
switch {
case exp < 10: add_bytes(&b, '0', byte(exp)+'0'); // add prefix 0
case exp < 100: add_bytes(&b, byte(exp/10)+'0', byte(exp%10)+'0');
case: add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0');
}
return to_bytes(b);
case 'g', 'G':
eprec := prec;
if eprec > digs.count && digs.count >= digs.decimal_point {
eprec = digs.count;
}
if shortest {
eprec = 6;
}
exp := digs.decimal_point - 1;
if exp < -4 || exp >= eprec {
if prec > digs.count {
prec = digs.count;
}
return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g'); // keep the same case
}
if prec > digs.decimal_point {
prec = digs.count;
}
return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f');
case:
add_bytes(&b, '%', fmt);
return to_bytes(b);
}
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0;
return;
}
/*
10^(dp-nd) > 2^(exp-mantbits)
log2(10) * (dp-nd) > exp-mantbits
log(2) >~ 0.332
332*(dp-nd) >= 100*(exp-mantbits)
*/
minexp := flt.bias+1;
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
// Number is already its shortest
return;
}
upper_: Decimal; upper := &upper_;
assign(upper, 2*mant - 1);
shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64;
explo: int;
if mant > 1<<flt.mantbits || exp == minexp {
mantlo = mant-1;
explo = exp;
} else {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower := &lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0;
for i in 0..<d.count {
l: byte = '0'; // lower digit
if i < lower.count {
l = lower.digits[i];
}
m := d.digits[i]; // middle digit
u: byte = '0'; // upper digit
if i < upper.count {
u = upper.digits[i];
}
ok_round_down := l != m || inclusive && i+1 == lower.count;
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
if ok_round_down && ok_round_up {
round(d, i+1);
return;
}
if ok_round_down {
round_down(d, i+1);
return;
}
if ok_round_up {
round_up(d, i+1);
return;
}
}
}
MAX_BASE :: 32;
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) {
u = x;
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u64(abs(i64(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return;
}
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
a: [129]byte;
i := len(a);
u, neg := is_integer_negative(x, is_signed, bit_size);
b := u64(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) {
u = x;
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u128(abs(i128(i)));
case 128:
i := i128(u);
neg = i < 0;
u = u128(abs(i128(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return;
}
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
a: [140]byte;
i := len(a);
u, neg := is_integer_negative_128(x, is_signed, bit_size);
b := u128(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if .Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case .Plus in flags:
i-=1; a[i] = '+';
case .Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
+93 -291
View File
@@ -1,14 +1,6 @@
package strconv
using import "core:decimal"
Int_Flag :: enum {
Prefix,
Plus,
Space,
}
Int_Flags :: bit_set[Int_Flag];
import "core:unicode/utf8"
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
switch s {
@@ -31,7 +23,8 @@ _digit_value :: proc(r: rune) -> int {
return v;
}
parse_i64 :: proc(s: string) -> i64 {
parse_i64 :: proc(str: string) -> i64 {
s := str;
neg := false;
if len(s) > 1 {
switch s[0] {
@@ -74,7 +67,8 @@ parse_i64 :: proc(s: string) -> i64 {
return value;
}
parse_u64 :: proc(s: string) -> u64 {
parse_u64 :: proc(str: string) -> u64 {
s := str;
neg := false;
if len(s) > 1 && s[0] == '+' {
s = s[1:];
@@ -204,303 +198,111 @@ append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil);
}
itoa :: proc(buf: []byte, i: int) -> string do return append_int(buf, i64(i), 10);
itoa :: proc(buf: []byte, i: int) -> string {
return append_int(buf, i64(i), 10);
}
atoi :: proc(s: string) -> int {
return parse_int(s);
}
atof :: proc(s: string) -> f64 {
return parse_f64(s);
}
ftoa :: append_float;
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
}
quote :: proc(buf: []byte, str: string) -> string {
write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) {
if i^ >= len(buf) do return;
n := copy(buf[i^:], bytes[:]);
i^ += n;
}
if buf == nil {
return "";
}
DecimalSlice :: struct {
digits: []byte,
count: int,
decimal_point: int,
neg: bool,
c :: '"';
i := 0;
s := str;
write_byte(buf, &i, c);
for width := 0; len(s) > 0; s = s[width:] {
r := rune(s[0]);
width = 1;
if r >= utf8.RUNE_SELF {
r, width = utf8.decode_rune_in_string(s);
}
if width == 1 && r == utf8.RUNE_ERROR {
write_byte(buf, &i, '\\', 'x');
write_byte(buf, &i, digits[s[0]>>4]);
write_byte(buf, &i, digits[s[0]&0xf]);
}
if i < len(buf) {
x := quote_rune(buf[i:], r);
i += len(x);
}
}
write_byte(buf, &i, c);
return string(buf[:i]);
}
FloatInfo :: struct {
mantbits: uint,
expbits: uint,
bias: int,
}
quote_rune :: proc(buf: []byte, r: rune) -> string {
write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) {
if i^ < len(buf) {
n := copy(buf[i^:], bytes[:]);
i^ += n;
}
}
write_string :: inline proc(buf: []byte, i: ^int, s: string) {
if i^ < len(buf) {
n := copy(buf[i^:], cast([]byte)s);
i^ += n;
}
}
write_rune :: inline proc(buf: []byte, i: ^int, r: rune) {
if i^ < len(buf) {
b, w := utf8.encode_rune(r);
n := copy(buf[i^:], b[:w]);
i^ += n;
}
}
if buf == nil {
return "";
}
_f16_info := FloatInfo{10, 5, -15};
_f32_info := FloatInfo{23, 8, -127};
_f64_info := FloatInfo{52, 11, -1023};
i := 0;
write_byte(buf, &i, '\'');
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte {
bits: u64;
flt: ^FloatInfo;
switch bit_size {
case 32:
bits = u64(transmute(u32)f32(val));
flt = &_f32_info;
case 64:
bits = transmute(u64)val;
flt = &_f64_info;
switch r {
case '\a': write_string(buf, &i, "\\a");
case '\b': write_string(buf, &i, "\\b");
case '\e': write_string(buf, &i, "\\e");
case '\f': write_string(buf, &i, "\\f");
case '\n': write_string(buf, &i, "\\n");
case '\r': write_string(buf, &i, "\\r");
case '\t': write_string(buf, &i, "\\t");
case '\v': write_string(buf, &i, "\\v");
case:
panic("strconv: invalid bit_size");
}
neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (u64(1) << flt.mantbits - 1);
switch exp {
case 1<<flt.expbits - 1:
s: string;
if mant != 0 {
s = "NaN";
} else if neg {
s = "-Inf";
} else {
s = "+Inf";
}
n := copy(buf, cast([]byte)s);
return buf[:n];
case 0: // denormalized
exp += 1;
case:
mant |= u64(1) << flt.mantbits;
}
exp += flt.bias;
d_: Decimal;
d := &d_;
assign(d, mant);
shift(d, exp - int(flt.mantbits));
digs: DecimalSlice;
shortest := prec < 0;
if shortest {
round_shortest(d, mant, exp, flt);
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
switch fmt {
case 'e', 'E': prec = digs.count-1;
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
case 'g', 'G': prec = digs.count;
}
} else {
switch fmt {
case 'e', 'E': round(d, prec+1);
case 'f', 'F': round(d, d.decimal_point+prec);
case 'g', 'G':
if prec == 0 {
prec = 1;
}
round(d, prec);
}
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
}
return format_digits(buf, shortest, neg, digs, prec, fmt);
}
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: byte) -> []byte {
Buffer :: struct {
b: []byte,
n: int,
}
to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n];
add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
buf.n += copy(buf.b[buf.n:], bytes);
}
b := Buffer{b = buf};
switch fmt {
case 'f', 'F':
add_bytes(&b, neg ? '-' : '+');
// integer, padded with zeros when needed
if digs.decimal_point > 0 {
m := min(digs.count, digs.decimal_point);
add_bytes(&b, ..digs.digits[0:m]);
for ; m < digs.decimal_point; m += 1 {
add_bytes(&b, '0');
if r < 32 {
write_string(buf, &i, "\\x");
b: [2]byte;
s := append_bits(b[:], u64(r), 16, true, 64, digits, nil);
switch len(s) {
case 0: write_string(buf, &i, "00");
case 1: write_rune(buf, &i, '0');
case 2: write_string(buf, &i, s);
}
} else {
add_bytes(&b, '0');
write_rune(buf, &i, r);
}
// fractional part
if prec > 0 {
add_bytes(&b, '.');
for i in 0..prec-1 {
c: byte = '0';
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
c = digs.digits[j];
}
add_bytes(&b, c);
}
}
return to_bytes(b);
case 'e', 'E':
panic("strconv: e/E float printing is not yet supported");
return to_bytes(b); // TODO
case 'g', 'G':
panic("strconv: g/G float printing is not yet supported");
return to_bytes(b); // TODO
case:
add_bytes(&b, '%', fmt);
return to_bytes(b);
}
write_byte(buf, &i, '\'');
return string(buf[:i]);
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) {
if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0;
return;
}
/*
10^(dp-nd) > 2^(exp-mantbits)
log2(10) * (dp-nd) > exp-mantbits
log(2) >~ 0.332
332*(dp-nd) >= 100*(exp-mantbits)
*/
minexp := flt.bias+1;
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
// Number is already its shortest
return;
}
upper_: Decimal; upper := &upper_;
assign(upper, 2*mant - 1);
shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64;
explo: int;
if mant > 1<<flt.mantbits || exp == minexp {
mantlo = mant-1;
explo = exp;
} else {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower := &lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0;
for i in 0..d.count-1 {
l: byte = '0'; // lower digit
if i < lower.count {
l = lower.digits[i];
}
m := d.digits[i]; // middle digit
u: byte = '0'; // upper digit
if i < upper.count {
u = upper.digits[i];
}
ok_round_down := l != m || inclusive && i+1 == lower.count;
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
if ok_round_down && ok_round_up {
round(d, i+1);
return;
}
if ok_round_down {
round_down(d, i+1);
return;
}
if ok_round_up {
round_up(d, i+1);
return;
}
}
}
MAX_BASE :: 32;
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
if is_signed {
switch bit_size {
case 8:
i := i8(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 16:
i := i16(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 32:
i := i32(u);
neg = i < 0;
u = u64(abs(i64(i)));
case 64:
i := i64(u);
neg = i < 0;
u = u64(abs(i64(i)));
case:
panic("is_integer_negative: Unknown integer size");
}
}
return u, neg;
}
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
neg: bool;
a: [129]byte;
i := len(a);
u, neg = is_integer_negative(u, is_signed, bit_size);
b := u64(base);
for u >= b {
i-=1; a[i] = digits[u % b];
u /= b;
}
i-=1; a[i] = digits[u % b];
if Int_Flag.Prefix in flags {
ok := true;
switch base {
case 2: i-=1; a[i] = 'b';
case 8: i-=1; a[i] = 'o';
case 10: i-=1; a[i] = 'd';
case 12: i-=1; a[i] = 'z';
case 16: i-=1; a[i] = 'x';
case: ok = false;
}
if ok {
i-=1; a[i] = '0';
}
}
switch {
case neg:
i-=1; a[i] = '-';
case Int_Flag.Plus in flags:
i-=1; a[i] = '+';
case Int_Flag.Space in flags:
i-=1; a[i] = ' ';
}
out := a[i:];
copy(buf, out);
return string(buf[0:len(out)]);
}
+217
View File
@@ -0,0 +1,217 @@
package strings
import "core:mem"
import "core:unicode/utf8"
import "core:strconv"
Builder :: struct {
buf: [dynamic]byte,
}
make_builder :: proc(allocator := context.allocator) -> Builder {
return Builder{make([dynamic]byte, allocator)};
}
destroy_builder :: proc(b: ^Builder) {
delete(b.buf);
clear(&b.buf);
}
grow_builder :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap);
}
reset_builder :: proc(b: ^Builder) {
clear(&b.buf);
}
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing;
d := mem.Raw_Dynamic_Array{
data = s.data,
len = 0,
cap = s.len,
allocator = mem.nil_allocator(),
};
return transmute(Builder)d;
}
to_string :: proc(b: Builder) -> string {
return string(b.buf[:]);
}
builder_len :: proc(b: Builder) -> int {
return len(b.buf);
}
builder_cap :: proc(b: Builder) -> int {
return cap(b.buf);
}
write_byte :: proc(b: ^Builder, x: byte) {
append(&b.buf, x);
}
write_rune :: proc(b: ^Builder, r: rune) -> int {
if r < utf8.RUNE_SELF {
write_byte(b, byte(r));
return 1;
}
s, n := utf8.encode_rune(r);
write_bytes(b, s[:n]);
return n;
}
write_string :: proc(b: ^Builder, s: string) {
write_bytes(b, cast([]byte)s);
}
write_bytes :: proc(b: ^Builder, x: []byte) {
append(&b.buf, ..x);
}
@(private, static)
DIGITS_LOWER := "0123456789abcdefx";
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') {
write_byte(b, quote);
for width, s := 0, str; len(s) > 0; s = s[width:] {
r := rune(s[0]);
width = 1;
if r >= utf8.RUNE_SELF {
r, width = utf8.decode_rune_in_string(s);
}
if width == 1 && r == utf8.RUNE_ERROR {
write_byte(b, '\\');
write_byte(b, 'x');
write_byte(b, DIGITS_LOWER[s[0]>>4]);
write_byte(b, DIGITS_LOWER[s[0]&0xf]);
continue;
}
write_escaped_rune(b, r, quote);
}
write_byte(b, quote);
}
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) {
if write_quote do write_byte(b, '\'');
switch r {
case '\a': write_string(b, `\a"`);
case '\b': write_string(b, `\b"`);
case '\e': write_string(b, `\e"`);
case '\f': write_string(b, `\f"`);
case '\n': write_string(b, `\n"`);
case '\r': write_string(b, `\r"`);
case '\t': write_string(b, `\t"`);
case '\v': write_string(b, `\v"`);
case:
if r < 32 {
write_string(b, `\x`);
buf: [2]byte;
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil);
switch len(s) {
case 0: write_string(b, "00");
case 1: write_byte(b, '0');
case 2: write_string(b, s);
}
} else {
write_rune(b, r);
}
}
if write_quote do write_byte(b, '\'');
}
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) {
is_printable :: proc(r: rune) -> bool {
if r <= 0xff {
switch r {
case 0x20..0x7e:
return true;
case 0xa1..0xff: // ¡ through ÿ except for the soft hyphen
return r != 0xad; //
}
}
// TODO(bill): A proper unicode library will be needed!
return false;
}
if html_safe {
switch r {
case '<', '>', '&':
write_byte(b, '\\');
write_byte(b, 'u');
for s := 12; s >= 0; s -= 4 {
write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
}
return;
}
}
if r == rune(quote) || r == '\\' {
write_byte(b, '\\');
write_byte(b, byte(r));
return;
} else if is_printable(r) {
write_encoded_rune(b, r, false);
return;
}
switch r {
case '\a': write_string(b, `\a`);
case '\b': write_string(b, `\b`);
case '\e': write_string(b, `\e`);
case '\f': write_string(b, `\f`);
case '\n': write_string(b, `\n`);
case '\r': write_string(b, `\r`);
case '\t': write_string(b, `\t`);
case '\v': write_string(b, `\v`);
case:
switch c := r; {
case c < ' ':
write_byte(b, '\\');
write_byte(b, 'x');
write_byte(b, DIGITS_LOWER[byte(c)>>4]);
write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
case c > utf8.MAX_RUNE:
c = 0xfffd;
fallthrough;
case c < 0x10000:
write_byte(b, '\\');
write_byte(b, 'u');
for s := 12; s >= 0; s -= 4 {
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
case:
write_byte(b, '\\');
write_byte(b, 'U');
for s := 28; s >= 0; s -= 4 {
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
}
}
}
}
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) {
buf: [32]byte;
s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil);
write_string(b, s);
}
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) {
buf: [32]byte;
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil);
write_string(b, s);
}
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) {
write_u64(b, u64(i), base);
}
write_int :: proc(b: ^Builder, i: int, base: int = 10) {
write_i64(b, i64(i), base);
}
+816 -4
View File
@@ -1,16 +1,33 @@
package strings
import "core:mem"
import "core:unicode/utf8"
new_string :: proc(s: string) -> string {
c := make([]byte, len(s)+1);
clone :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return string(c[:len(s)]);
}
new_cstring :: proc(s: string) -> cstring {
c := make([]byte, len(s)+1);
clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return cstring(&c[0]);
}
@(deprecated="Please use 'strings.clone'")
new_string :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return string(c[:len(s)]);
}
@(deprecated="Please use 'strings.clone_to_cstring'")
new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return cstring(&c[0]);
@@ -25,9 +42,804 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
return transmute(string)mem.Raw_String{ptr, len};
}
compare :: proc(lhs, rhs: string) -> int {
return mem.compare(cast([]byte)lhs, cast([]byte)rhs);
}
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r do return offset;
}
return -1;
}
contains :: proc(s, substr: string) -> bool {
return index(s, substr) >= 0;
}
contains_any :: proc(s, chars: string) -> bool {
return index_any(s, chars) >= 0;
}
rune_count :: proc(s: string) -> int {
return utf8.rune_count_in_string(s);
}
equal_fold :: proc(u, v: string) -> bool {
s, t := u, v;
loop: for s != "" && t != "" {
sr, tr: rune;
if s[0] < utf8.RUNE_SELF {
sr, s = rune(s[0]), s[1:];
} else {
r, size := utf8.decode_rune_in_string(s);
sr, s = r, s[size:];
}
if t[0] < utf8.RUNE_SELF {
tr, t = rune(t[0]), t[1:];
} else {
r, size := utf8.decode_rune_in_string(t);
tr, t = r, t[size:];
}
if tr == sr { // easy case
continue loop;
}
if tr < sr {
tr, sr = sr, tr;
}
if tr < utf8.RUNE_SELF {
switch sr {
case 'A'..'Z':
if tr == (sr+'a')-'A' {
continue loop;
}
}
return false;
}
// TODO(bill): Unicode folding
return false;
}
return s == t;
}
has_prefix :: proc(s, prefix: string) -> bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix;
}
has_suffix :: proc(s, suffix: string) -> bool {
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix;
}
join :: proc(a: []string, sep: string, allocator := context.allocator) -> string {
if len(a) == 0 {
return "";
}
n := len(sep) * (len(a) - 1);
for s in a {
n += len(s);
}
b := make([]byte, n, allocator);
i := copy(b, cast([]byte)a[0]);
for s in a[1:] {
i += copy(b[i:], cast([]byte)sep);
i += copy(b[i:], cast([]byte)s);
}
return string(b);
}
concatenate :: proc(a: []string, allocator := context.allocator) -> string {
if len(a) == 0 {
return "";
}
n := 0;
for s in a {
n += len(s);
}
b := make([]byte, n, allocator);
i := 0;
for s in a {
i += copy(b[i:], cast([]byte)s);
}
return string(b);
}
@private
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
s, n := s_, n_;
if n == 0 {
return nil;
}
if sep == "" {
l := utf8.rune_count_in_string(s);
if n < 0 || n > l {
n = l;
}
res := make([dynamic]string, n, allocator);
for i := 0; i < n-1; i += 1 {
_, w := utf8.decode_rune_in_string(s);
res[i] = s[:w];
s = s[w:];
}
if n > 0 {
res[n-1] = s;
}
return res[:];
}
if n < 0 {
n = count(s, sep) + 1;
}
res := make([dynamic]string, n, allocator);
n -= 1;
i := 0;
for ; i < n; i += 1 {
m := index(s, sep);
if m < 0 {
break;
}
res[i] = s[:m+sep_save];
s = s[m+len(sep):];
}
res[i] = s;
return res[:i+1];
}
split :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
return _split(s, sep, 0, -1, allocator);
}
split_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
return _split(s, sep, 0, n, allocator);
}
split_after :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), -1, allocator);
}
split_after_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), n, allocator);
}
index_byte :: proc(s: string, c: byte) -> int {
for i := 0; i < len(s); i += 1 {
if s[i] == c do return i;
}
return -1;
}
// Returns i1 if c is not present
last_index_byte :: proc(s: string, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c do return i;
}
return -1;
}
@private PRIME_RABIN_KARP :: 16777619;
index :: proc(s, substr: string) -> int {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
}
sq := u32(PRIME_RABIN_KARP);
for i := len(s); i > 0; i >>= 1 {
if (i & 1) != 0 {
pow *= sq;
}
sq *= sq;
}
return;
}
n := len(substr);
switch {
case n == 0:
return 0;
case n == 1:
return index_byte(s, substr[0]);
case n == len(s):
if s == substr {
return 0;
}
return -1;
case n > len(s):
return -1;
}
hash, pow := hash_str_rabin_karp(substr);
h: u32;
for i := 0; i < n; i += 1 {
h = h*PRIME_RABIN_KARP + u32(s[i]);
}
if h == hash && s[:n] == substr {
return 0;
}
for i := n; i < len(s); /**/ {
h *= PRIME_RABIN_KARP;
h += u32(s[i]);
h -= pow * u32(s[i-n]);
i += 1;
if h == hash && s[i-n:i] == substr {
return i - n;
}
}
return -1;
}
last_index :: proc(s, substr: string) -> int {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
}
sq := u32(PRIME_RABIN_KARP);
for i := len(s); i > 0; i >>= 1 {
if (i & 1) != 0 {
pow *= sq;
}
sq *= sq;
}
return;
}
n := len(substr);
switch {
case n == 0:
return len(s);
case n == 1:
return last_index_byte(s, substr[0]);
case n == len(s):
return substr == s ? 0 : -1;
case n > len(s):
return -1;
}
hash, pow := hash_str_rabin_karp_reverse(substr);
last := len(s) - n;
h: u32;
for i := len(s)-1; i >= last; i -= 1 {
h = h*PRIME_RABIN_KARP + u32(s[i]);
}
if h == hash && s[last:] == substr {
return last;
}
for i := last-1; i >= 0; i -= 1 {
h *= PRIME_RABIN_KARP;
h += u32(s[i]);
h -= pow * u32(s[i+n]);
if h == hash && s[i:i+n] == substr {
return i;
}
}
return -1;
}
index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1;
}
// TODO(bill): Optimize
for r, i in s {
for c in chars {
if r == c {
return i;
}
}
}
return -1;
}
last_index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1;
}
for i := len(s); i > 0; {
r, w := utf8.decode_last_rune_in_string(s[:i]);
i -= w;
for c in chars {
if r == c {
return i;
}
}
}
return -1;
}
count :: proc(s, substr: string) -> int {
if len(substr) == 0 { // special case
return rune_count(s) + 1;
}
if len(substr) == 1 {
c := substr[0];
switch len(s) {
case 0:
return 0;
case 1:
return int(s[0] == c);
}
n := 0;
for i := 0; i < len(s); i += 1 {
if s[i] == c {
n += 1;
}
}
return n;
}
// TODO(bill): Use a non-brute for approach
n := 0;
str := s;
for {
i := index(str, substr);
if i == -1 {
return n;
}
n += 1;
str = str[i+len(substr):];
}
return n;
}
repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
if count < 0 {
panic("strings: negative repeat count");
} else if count > 0 && (len(s)*count)/count != len(s) {
panic("strings: repeat count will cause an overflow");
}
b := make([]byte, len(s)*count, allocator);
i := copy(b, cast([]byte)s);
for i < len(b) { // 2^N trick to reduce the need to copy
copy(b[i:], b[:i]);
i *= 2;
}
return string(b);
}
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, old, new, -1, allocator);
}
// if n < 0, no limit on the number of replacements
replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
if old == new || n == 0 {
was_allocation = false;
output = s;
return;
}
byte_count := n;
if m := count(s, old); m == 0 {
was_allocation = false;
output = s;
return;
} else if n < 0 || m < n {
byte_count = m;
}
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator);
was_allocation = true;
w := 0;
start := 0;
for i := 0; i < byte_count; i += 1 {
j := start;
if len(old) == 0 {
if i > 0 {
_, width := utf8.decode_rune_in_string(s[start:]);
j += width;
}
} else {
j += index(s[start:], old);
}
w += copy(t[w:], cast([]byte)s[start:j]);
w += copy(t[w:], cast([]byte)new);
start = j + len(old);
}
w += copy(t[w:], cast([]byte)s[start:]);
output = string(t[0:w]);
return;
}
is_ascii_space :: proc(r: rune) -> bool {
switch r {
case '\t', '\n', '\v', '\f', '\r', ' ':
return true;
}
return false;
}
is_space :: proc(r: rune) -> bool {
if r < 0x2000 {
switch r {
case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xa0, 0x1680:
return true;
}
} else {
if r <= 0x200a {
return true;
}
switch r {
case 0x2028, 0x2029, 0x202f, 0x205f, 0x3000:
return true;
}
}
return false;
}
is_null :: proc(r: rune) -> bool {
return r == 0x0000;
}
index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
for r, i in s {
if p(r) == truth {
return i;
}
}
return -1;
}
index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
for r, i in s {
if p(state, r) == truth {
return i;
}
}
return -1;
}
last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
r, size := utf8.decode_last_rune_in_string(s[:i]);
i -= size;
if p(r) == truth {
return i;
}
}
return -1;
}
last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
r, size := utf8.decode_last_rune_in_string(s[:i]);
i -= size;
if p(state, r) == truth {
return i;
}
}
return -1;
}
trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := index_proc(s, p, false);
if i == -1 {
return "";
}
return s[i:];
}
index_rune :: proc(s: string, r: rune) -> int {
switch {
case 0 <= r && r < utf8.RUNE_SELF:
return index_byte(s, byte(r));
case r == utf8.RUNE_ERROR:
for c, i in s {
if c == utf8.RUNE_ERROR {
return i;
}
}
return -1;
case !utf8.valid_rune(r):
return -1;
}
b, w := utf8.encode_rune(r);
return index(s, string(b[:w]));
}
trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := index_proc_with_state(s, p, state, false);
if i == -1 {
return "";
}
return s[i:];
}
trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := last_index_proc(s, p, false);
if i >= 0 && s[i] >= utf8.RUNE_SELF {
_, w := utf8.decode_rune_in_string(s[i:]);
i += w;
} else {
i += 1;
}
return s[0:i];
}
trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := last_index_proc_with_state(s, p, state, false);
if i >= 0 && s[i] >= utf8.RUNE_SELF {
_, w := utf8.decode_rune_in_string(s[i:]);
i += w;
} else {
i += 1;
}
return s[0:i];
}
is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
if state == nil {
return false;
}
cutset := (^string)(state)^;
for c in cutset {
if r == c {
return true;
}
}
return false;
}
trim_left :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s;
}
state := cutset;
return trim_left_proc_with_state(s, is_in_cutset, &state);
}
trim_right :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s;
}
state := cutset;
return trim_right_proc_with_state(s, is_in_cutset, &state);
}
trim :: proc(s: string, cutset: string) -> string {
return trim_right(trim_left(s, cutset), cutset);
}
trim_left_space :: proc(s: string) -> string {
return trim_left_proc(s, is_space);
}
trim_right_space :: proc(s: string) -> string {
return trim_right_proc(s, is_space);
}
trim_space :: proc(s: string) -> string {
return trim_right_space(trim_left_space(s));
}
trim_left_null :: proc(s: string) -> string {
return trim_left_proc(s, is_null);
}
trim_right_null :: proc(s: string) -> string {
return trim_right_proc(s, is_null);
}
trim_null :: proc(s: string) -> string {
return trim_right_null(trim_left_null(s));
}
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
// Adjacent invalid bytes are only replaced once
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
str := s;
b := make_builder(allocator);;
grow_builder(&b, len(str));
has_error := false;
cursor := 0;
origin := str;
for len(str) > 0 {
r, w := utf8.decode_rune_in_string(str);
if r == utf8.RUNE_ERROR {
if !has_error {
has_error = true;
write_string(&b, origin[:cursor]);
}
} else if has_error {
has_error = false;
write_string(&b, replacement);
origin = origin[cursor:];
cursor = 0;
}
cursor += w;
str = str[w:];
}
return to_string(b);
}
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s;
n := len(str);
buf := make([]byte, n);
i := 0;
for len(str) > 0 {
_, w := utf8.decode_rune_in_string(str);
copy(buf[i:], cast([]byte)str[:w]);
str = str[w:];
}
return string(buf);
}
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
if tab_size <= 0 {
panic("tab size must be positive");
}
if s == "" {
return "";
}
b := make_builder(allocator);
str := s;
column: int;
for len(str) > 0 {
r, w := utf8.decode_rune_in_string(str);
if r == '\t' {
expand := tab_size - column%tab_size;
for i := 0; i < expand; i += 1 {
write_byte(&b, ' ');
}
column += expand;
} else {
if r == '\n' {
column = 0;
} else {
column += w;
}
write_rune(&b, r);
}
str = str[w:];
}
return to_string(b);
}
partition :: proc(str, sep: string) -> (head, match, tail: string) {
i := index(str, sep);
if i == -1 {
head = str;
return;
}
head = str[:i];
match = str[i:i+len(sep)];
tail = str[i+len(sep):];
return;
}
center_justify :: centre_justify; // NOTE(bill): Because Americans exist
// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length
centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains/2);
write_string(&b, str);
write_pad_string(&b, pad, pad_len, (remains+1)/2);
return to_string(b);
}
// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length
left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_string(&b, str);
write_pad_string(&b, pad, pad_len, remains);
return to_string(b);
}
// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length
right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains);
write_string(&b, str);
return to_string(b);
}
@private
write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
repeats := remains / pad_len;
for i := 0; i < repeats; i += 1 {
write_string(b, pad);
}
n := remains % pad_len;
p := pad;
for i := 0; i < n; i += 1 {
r, w := utf8.decode_rune_in_string(p);
write_rune(b, r);
p = p[w:];
}
}
+118 -91
View File
@@ -11,172 +11,199 @@ Ordering :: enum {
}
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
using Ordering;
#complete switch order {
case Relaxed: return Relaxed;
case Release: return Relaxed;
case Acquire: return Acquire;
case Acquire_Release: return Acquire;
case Sequentially_Consistent: return Sequentially_Consistent;
case .Relaxed: return .Relaxed;
case .Release: return .Relaxed;
case .Acquire: return .Acquire;
case .Acquire_Release: return .Acquire;
case .Sequentially_Consistent: return .Sequentially_Consistent;
}
return Relaxed;
return .Relaxed;
}
fence :: inline proc "contextless" (order: Ordering) {
using Ordering;
fence :: inline proc "contextless" ($order: Ordering) {
#complete switch order {
case Relaxed: panic("there is no such thing as a relaxed fence");
case Release: intrinsics.atomic_fence_rel();
case Acquire: intrinsics.atomic_fence_acq();
case Acquire_Release: intrinsics.atomic_fence_acqrel();
case Sequentially_Consistent: intrinsics.atomic_fence();
case .Relaxed: panic("there is no such thing as a relaxed fence");
case .Release: intrinsics.atomic_fence_rel();
case .Acquire: intrinsics.atomic_fence_acq();
case .Acquire_Release: intrinsics.atomic_fence_acqrel();
case .Sequentially_Consistent: intrinsics.atomic_fence();
case: panic("unknown order");
}
}
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) {
using Ordering;
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
#complete switch order {
case Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case Release: intrinsics.atomic_store_rel(dst, val);
case Sequentially_Consistent: intrinsics.atomic_store(dst, val);
case Acquire: panic("there is not such thing as an acquire store");
case Acquire_Release: panic("there is not such thing as an acquire/release store");
case .Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case .Release: intrinsics.atomic_store_rel(dst, val);
case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
case .Acquire: panic("there is not such thing as an acquire store");
case .Acquire_Release: panic("there is not such thing as an acquire/release store");
case: panic("unknown order");
}
}
atomic_load :: inline proc "contextless" (dst: ^$T, order: Ordering) -> T {
using Ordering;
atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_load_relaxed(dst);
case Acquire: return intrinsics.atomic_load_acq(dst);
case Sequentially_Consistent: return intrinsics.atomic_load(dst);
case Release: panic("there is no such thing as a release load");
case Acquire_Release: panic("there is no such thing as an acquire/release load");
case .Relaxed: return intrinsics.atomic_load_relaxed(dst);
case .Acquire: return intrinsics.atomic_load_acq(dst);
case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
case .Release: panic("there is no such thing as a release load");
case .Acquire_Release: panic("there is no such thing as an acquire/release load");
}
panic("unknown order");
return T{};
}
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case Release: return intrinsics.atomic_xchg_rel(dst, val);
case Acquire: return intrinsics.atomic_xchg_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_xchg_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
case .Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case .Release: return intrinsics.atomic_xchg_rel(dst, val);
case .Acquire: return intrinsics.atomic_xchg_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_xchg_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
}
panic("unknown order");
return T{};
}
atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, success, failure: Ordering) -> (val: T, ok: bool) {
using Ordering;
atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
switch failure {
case Relaxed:
case .Relaxed:
switch success {
case Relaxed: return intrinsics.atomic_cxchg_relaxed(dst, old, new);
case Acquire: return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
case Acquire_Release: return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
case Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
case .Relaxed: return intrinsics.atomic_cxchg_relaxed(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
case .Acquire_Release: return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
case .Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
case .Release: return intrinsics.atomic_cxchg_rel(dst, old, new);
case: panic("an unknown ordering combination");
}
case Acquire:
case .Acquire:
switch success {
case Acquire: return intrinsics.atomic_cxchg_acq(dst, old, new);
case .Release: return intrinsics.atomic_cxchg_acqrel(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchg_acq(dst, old, new);
case: panic("an unknown ordering combination");
}
case Sequentially_Consistent:
case .Sequentially_Consistent:
switch success {
case Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
case .Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
case: panic("an unknown ordering combination");
}
case Acquire_Release:
case .Acquire_Release:
panic("there is not such thing as an acquire/release failure ordering");
case Release:
panic("there is not such thing as an release failure ordering");
case .Release:
switch success {
case .Acquire: return instrinsics.atomic_cxchg_failacq(dst, old, new);
case: panic("an unknown ordering combination");
}
}
return T{}, false;
}
atomic_compare_exchange_weak :: inline proc "contextless" (dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
switch failure {
case .Relaxed:
switch success {
case .Relaxed: return intrinsics.atomic_cxchgweak_relaxed(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new);
case .Acquire_Release: return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new);
case .Sequentially_Consistent: return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new);
case .Release: return intrinsics.atomic_cxchgweak_rel(dst, old, new);
case: panic("an unknown ordering combination");
}
case .Acquire:
switch success {
case .Release: return intrinsics.atomic_cxchgweak_acqrel(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchgweak_acq(dst, old, new);
case: panic("an unknown ordering combination");
}
case .Sequentially_Consistent:
switch success {
case .Sequentially_Consistent: return intrinsics.atomic_cxchgweak(dst, old, new);
case: panic("an unknown ordering combination");
}
case .Acquire_Release:
panic("there is not such thing as an acquire/release failure ordering");
case .Release:
switch success {
case .Acquire: return intrinsics.atomic_cxchgweak_failacq(dst, old, new);
case: panic("an unknown ordering combination");
}
}
return T{}, false;
}
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case Release: return intrinsics.atomic_add_rel(dst, val);
case Acquire: return intrinsics.atomic_add_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_add_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
case .Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case .Release: return intrinsics.atomic_add_rel(dst, val);
case .Acquire: return intrinsics.atomic_add_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_add_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
}
panic("unknown order");
return T{};
}
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case Release: return intrinsics.atomic_sub_rel(dst, val);
case Acquire: return intrinsics.atomic_sub_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_sub_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
case .Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case .Release: return intrinsics.atomic_sub_rel(dst, val);
case .Acquire: return intrinsics.atomic_sub_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_sub_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
}
panic("unknown order");
return T{};
}
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case Release: return intrinsics.atomic_and_rel(dst, val);
case Acquire: return intrinsics.atomic_and_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_and_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
case .Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case .Release: return intrinsics.atomic_and_rel(dst, val);
case .Acquire: return intrinsics.atomic_and_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_and_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
}
panic("unknown order");
return T{};
}
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case Release: return intrinsics.atomic_nand_rel(dst, val);
case Acquire: return intrinsics.atomic_nand_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_nand_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
case .Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case .Release: return intrinsics.atomic_nand_rel(dst, val);
case .Acquire: return intrinsics.atomic_nand_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_nand_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
}
panic("unknown order");
return T{};
}
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case Release: return intrinsics.atomic_or_rel(dst, val);
case Acquire: return intrinsics.atomic_or_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_or_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
case .Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case .Release: return intrinsics.atomic_or_rel(dst, val);
case .Acquire: return intrinsics.atomic_or_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_or_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
}
panic("unknown order");
return T{};
}
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
case Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case Release: return intrinsics.atomic_xor_rel(dst, val);
case Acquire: return intrinsics.atomic_xor_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_xor_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
case .Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case .Release: return intrinsics.atomic_xor_rel(dst, val);
case .Acquire: return intrinsics.atomic_xor_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_xor_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
}
panic("unknown order");
return T{};
+28
View File
@@ -2,6 +2,11 @@ package sync
import "core:sys/win32"
foreign {
@(link_name="llvm.x86.sse2.pause")
yield_processor :: proc() ---
}
Semaphore :: struct {
_handle: win32.Handle,
}
@@ -14,6 +19,12 @@ Condition :: struct {
event: win32.Handle,
}
Ticket_Mutex :: struct {
ticket: u64,
serving: u64,
}
current_thread_id :: proc() -> i32 {
return i32(win32.get_current_thread_id());
}
@@ -81,3 +92,20 @@ condition_destroy :: proc(using c: ^Condition) {
win32.close_handle(event);
}
}
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
atomic_store(&m.ticket, 0, Ordering.Relaxed);
atomic_store(&m.serving, 0, Ordering.Relaxed);
}
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add(&m.ticket, 1, Ordering.Relaxed);
for ticket != m.serving {
yield_processor();
}
}
ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) {
atomic_add(&m.serving, 1, Ordering.Relaxed);
}
-24
View File
@@ -1,24 +0,0 @@
ENTRY(_start)
SECTIONS
{
. = 0x100000;
.text BLOCK(4K) : ALIGN(4K)
{
*(.text)
}
.rodata BLOCK(4K) : ALIGN(4K)
{
*(.rodata)
}
.data BLOCK(4K) : ALIGN(4K)
{
*(.data)
}
.bss BLOCK(4K) : ALIGN(4K)
{
*(COMMON)
*(.bss)
}
}
+188
View File
@@ -0,0 +1,188 @@
// +build windows
package win32
foreign import "system:comdlg32.lib"
import "core:strings"
OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr;
Open_File_Name_A :: struct {
struct_size: u32,
hwnd_owner: Hwnd,
instance: Hinstance,
filter: cstring,
custom_filter: cstring,
max_cust_filter: u32,
filter_index: u32,
file: cstring,
max_file: u32,
file_title: cstring,
max_file_title: u32,
initial_dir: cstring,
title: cstring,
flags: u32,
file_offset: u16,
file_extension: u16,
def_ext: cstring,
cust_data: Lparam,
hook: OFN_Hook_Proc,
template_name: cstring,
pv_reserved: rawptr,
dw_reserved: u32,
flags_ex: u32,
}
Open_File_Name_W :: struct {
struct_size: u32,
hwnd_owner: Hwnd,
instance: Hinstance,
filter: Wstring,
custom_filter: Wstring,
max_cust_filter: u32,
filter_index: u32,
file: Wstring,
max_file: u32,
file_title: Wstring,
max_file_title: u32,
initial_dir: Wstring,
title: Wstring,
flags: u32,
file_offset: u16,
file_extension: u16,
def_ext: Wstring,
cust_data: Lparam,
hook: OFN_Hook_Proc,
template_name: Wstring,
pv_reserved: rawptr,
dw_reserved: u32,
flags_ex: u32,
}
@(default_calling_convention = "c")
foreign comdlg32 {
@(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
@(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
@(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
@(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
@(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 ---
}
OPEN_TITLE :: "Select file to open";
OPEN_FLAGS :: u32(OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
OPEN_FLAGS_MULTI :: u32(OPEN_FLAGS | OFN_ALLOWMULTISELECT | OFN_EXPLORER);
SAVE_TITLE :: "Select file to save";
SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER);
SAVE_EXT :: "txt";
Open_Save_Mode :: enum {
Open = 0,
Save = 1,
}
_open_file_dialog :: proc(title: string, dir: string,
filters: []string, default_filter: u32,
flags: u32, default_ext: string,
mode: Open_Save_Mode, allocator := context.temp_allocator) -> (path: string, ok: bool = true) {
file_buf := make([]u16, MAX_PATH_WIDE, allocator);
// Filters need to be passed as a pair of strings (title, filter)
filter_len := u32(len(filters));
if filter_len % 2 != 0 do return "", false;
filter: string;
filter = strings.join(filters, "\u0000", context.temp_allocator);
filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator);
ofn := Open_File_Name_W{
struct_size = size_of(Open_File_Name_W),
file = Wstring(&file_buf[0]),
max_file = MAX_PATH_WIDE,
title = utf8_to_wstring(title, context.temp_allocator),
filter = utf8_to_wstring(filter, context.temp_allocator),
initial_dir = utf8_to_wstring(dir, context.temp_allocator),
filter_index = u32(clamp(default_filter, 1, filter_len / 2)),
def_ext = utf8_to_wstring(default_ext, context.temp_allocator),
flags = u32(flags),
};
switch mode {
case .Open:
ok = bool(get_open_file_name_w(&ofn));
case .Save:
ok = bool(get_save_file_name_w(&ofn));
case:
ok = false;
}
if !ok {
delete(file_buf);
return "", false;
}
file_name := utf16_to_utf8(file_buf[:], allocator);
path = strings.trim_right_null(file_name);
return;
}
select_file_to_open :: proc(title := OPEN_TITLE, dir := ".",
filters := []string{"All Files", "*.*"}, default_filter := u32(1),
flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) {
path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", Open_Save_Mode.Open, allocator);
return;
}
select_file_to_save :: proc(title := SAVE_TITLE, dir := ".",
filters := []string{"All Files", "*.*"}, default_filter := u32(1),
flags := SAVE_FLAGS, default_ext := SAVE_EXT,
allocator := context.temp_allocator) -> (path: string, ok: bool) {
path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, Open_Save_Mode.Save, allocator);
return;
}
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.
OFN_ALLOWMULTISELECT :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog.
OFN_CREATEPROMPT :: 0x00002000;
OFN_DONTADDTORECENT :: 0x02000000;
OFN_ENABLEHOOK :: 0x00000020;
OFN_ENABLEINCLUDENOTIFY :: 0x00400000;
OFN_ENABLESIZING :: 0x00800000;
OFN_ENABLETEMPLATE :: 0x00000040;
OFN_ENABLETEMPLATEHANDLE :: 0x00000080;
OFN_EXPLORER :: 0x00080000;
OFN_EXTENSIONDIFFERENT :: 0x00000400;
OFN_FILEMUSTEXIST :: 0x00001000;
OFN_FORCESHOWHIDDEN :: 0x10000000;
OFN_HIDEREADONLY :: 0x00000004;
OFN_LONGNAMES :: 0x00200000;
OFN_NOCHANGEDIR :: 0x00000008;
OFN_NODEREFERENCELINKS :: 0x00100000;
OFN_NOLONGNAMES :: 0x00040000;
OFN_NONETWORKBUTTON :: 0x00020000;
OFN_NOREADONLYRETURN :: 0x00008000;
OFN_NOTESTFILECREATE :: 0x00010000;
OFN_NOVALIDATE :: 0x00000100;
OFN_OVERWRITEPROMPT :: 0x00000002;
OFN_PATHMUSTEXIST :: 0x00000800;
OFN_READONLY :: 0x00000001;
OFN_SHAREAWARE :: 0x00004000;
OFN_SHOWHELP :: 0x00000010;
CDERR_DIALOGFAILURE :: 0x0000FFFF;
CDERR_GENERALCODES :: 0x00000000;
CDERR_STRUCTSIZE :: 0x00000001;
CDERR_INITIALIZATION :: 0x00000002;
CDERR_NOTEMPLATE :: 0x00000003;
CDERR_NOHINSTANCE :: 0x00000004;
CDERR_LOADSTRFAILURE :: 0x00000005;
CDERR_FINDRESFAILURE :: 0x00000006;
CDERR_LOADRESFAILURE :: 0x00000007;
CDERR_LOCKRESFAILURE :: 0x00000008;
CDERR_MEMALLOCFAILURE :: 0x00000009;
CDERR_MEMLOCKFAILURE :: 0x0000000A;
CDERR_NOHOOK :: 0x0000000B;
CDERR_REGISTERMSGFAIL :: 0x0000000C;
+14
View File
@@ -0,0 +1,14 @@
package win32
import "core:strings";
foreign {
@(link_name="_wgetcwd") _get_cwd_wide :: proc(buffer: Wstring, buf_len: int) -> ^Wstring ---
}
get_cwd :: proc(allocator := context.temp_allocator) -> string {
buffer := make([]u16, MAX_PATH_WIDE, allocator);
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE);
file := utf16_to_utf8(buffer[:], allocator);
return strings.trim_right_null(file);
}
+23
View File
@@ -0,0 +1,23 @@
// +build windows
package win32
foreign import "system:gdi32.lib"
@(default_calling_convention = "std")
foreign gdi32 {
@(link_name="GetStockObject") get_stock_object :: proc(fn_object: i32) -> Hgdiobj ---;
@(link_name="StretchDIBits")
stretch_dibits :: proc(hdc: Hdc,
x_dst, y_dst, width_dst, height_dst: i32,
x_src, y_src, width_src, header_src: i32,
bits: rawptr, bits_info: ^Bitmap_Info,
usage: u32,
rop: u32) -> i32 ---;
@(link_name="SetPixelFormat") set_pixel_format :: proc(hdc: Hdc, pixel_format: i32, pfd: ^Pixel_Format_Descriptor) -> Bool ---;
@(link_name="ChoosePixelFormat") choose_pixel_format :: proc(hdc: Hdc, pfd: ^Pixel_Format_Descriptor) -> i32 ---;
@(link_name="SwapBuffers") swap_buffers :: proc(hdc: Hdc) -> Bool ---;
}
File diff suppressed because it is too large Load Diff
+29
View File
@@ -0,0 +1,29 @@
// +build windows
package win32
import "core:strings";
call_external_process :: proc(program, command_line: string) -> bool {
si := Startup_Info{ cb=size_of(Startup_Info) };
pi := Process_Information{};
return cast(bool)create_process_w(
utf8_to_wstring(program),
utf8_to_wstring(command_line),
nil,
nil,
Bool(false),
u32(0x10),
nil,
nil,
&si,
&pi
);
}
open_website :: proc(url: string) -> bool {
p :: "C:\\Windows\\System32\\cmd.exe";
arg := []string{"/C", "start", url};
args := strings.join(arg, " ", context.temp_allocator);
return call_external_process(p, args);
}
+221
View File
@@ -0,0 +1,221 @@
// +build windows
package win32
foreign import "system:kernel32.lib"
@(default_calling_convention = "std")
foreign kernel32 {
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
current_direcotry: cstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="CreateProcessW") create_process_w :: proc(application_name, command_line: Wstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
current_direcotry: cstring, startup_info: ^Startup_Info,
process_information: ^Process_Information) -> Bool ---;
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
@(link_name="GetModuleHandleA") get_module_handle_a :: proc(module_name: cstring) -> Hmodule ---;
@(link_name="GetModuleHandleW") get_module_handle_w :: proc(module_name: Wstring) -> Hmodule ---;
@(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---;
@(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---;
@(link_name="Sleep") sleep :: proc(ms: u32) ---;
@(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---;
@(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---;
@(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) ---;
@(link_name="GetCommandLineA") get_command_line_a :: proc() -> cstring ---;
@(link_name="GetCommandLineW") get_command_line_w :: proc() -> Wstring ---;
@(link_name="GetSystemMetrics") get_system_metrics :: proc(index: i32) -> i32 ---;
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
@(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) ---;
@(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool ---;
@(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool ---;
@(link_name="SystemTimeToFileTime") system_time_to_file_time :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool ---;
@(link_name="GetStdHandle") get_std_handle :: proc(h: i32) -> Handle ---;
@(link_name="CreateFileA")
create_file_a :: proc(filename: cstring, desired_access, share_module: u32,
security: rawptr,
creation, flags_and_attribs: u32, template_file: Handle) -> Handle ---;
@(link_name="CreateFileW")
create_file_w :: proc(filename: Wstring, desired_access, share_module: u32,
security: rawptr,
creation, flags_and_attribs: u32, template_file: Handle) -> Handle ---;
@(link_name="ReadFile") read_file :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool ---;
@(link_name="WriteFile") write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool ---;
@(link_name="GetFileSizeEx") get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool ---;
@(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool ---;
@(link_name="CreateDirectoryA") create_directory_a :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool ---;
@(link_name="CreateDirectoryW") create_directory_w :: proc(path: Wstring, security_attributes: ^Security_Attributes) -> Bool ---;
@(link_name="GetFileType") get_file_type :: proc(file_handle: Handle) -> u32 ---;
@(link_name="SetFilePointer") set_file_pointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 ---;
@(link_name="SetHandleInformation") set_handle_information :: proc(obj: Handle, mask, flags: u32) -> Bool ---;
@(link_name="FindFirstFileA") find_first_file_a :: proc(file_name: cstring, data: ^Find_Data_A) -> Handle ---;
@(link_name="FindNextFileA") find_next_file_a :: proc(file: Handle, data: ^Find_Data_A) -> Bool ---;
@(link_name="FindFirstFileW") find_first_file_w :: proc(file_name: Wstring, data: ^Find_Data_W) -> Handle ---;
@(link_name="FindNextFileW") find_next_file_w :: proc(file: Handle, data: ^Find_Data_W) -> Bool ---;
@(link_name="FindClose") find_close :: proc(file: Handle) -> Bool ---;
@(link_name="MoveFileExA") move_file_ex_a :: proc(existing, new: cstring, flags: u32) -> Bool ---;
@(link_name="DeleteFileA") delete_file_a :: proc(file_name: cstring) -> Bool ---;
@(link_name="CopyFileA") copy_file_a :: proc(existing, new: cstring, fail_if_exists: Bool) -> Bool ---;
@(link_name="MoveFileExW") move_file_ex_w :: proc(existing, new: Wstring, flags: u32) -> Bool ---;
@(link_name="DeleteFileW") delete_file_w :: proc(file_name: Wstring) -> Bool ---;
@(link_name="CopyFileW") copy_file_w :: proc(existing, new: Wstring, fail_if_exists: Bool) -> Bool ---;
@(link_name="HeapAlloc") heap_alloc :: proc(h: Handle, flags: u32, bytes: int) -> rawptr ---;
@(link_name="HeapReAlloc") heap_realloc :: proc(h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr ---;
@(link_name="HeapFree") heap_free :: proc(h: Handle, flags: u32, memory: rawptr) -> Bool ---;
@(link_name="GetProcessHeap") get_process_heap :: proc() -> Handle ---;
@(link_name="LocalAlloc") local_alloc :: proc(flags: u32, bytes: int) -> rawptr ---;
@(link_name="LocalReAlloc") local_realloc :: proc(mem: rawptr, bytes: int, flags: uint) -> rawptr ---;
@(link_name="LocalFree") local_free :: proc(mem: rawptr) -> rawptr ---;
@(link_name="FindFirstChangeNotificationA") find_first_change_notification_a :: proc(path: cstring, watch_subtree: Bool, filter: u32) -> Handle ---;
@(link_name="FindNextChangeNotification") find_next_change_notification :: proc(h: Handle) -> Bool ---;
@(link_name="FindCloseChangeNotification") find_close_change_notification :: proc(h: Handle) -> Bool ---;
@(link_name="ReadDirectoryChangesW") read_directory_changes_w :: proc(dir: Handle, buf: rawptr, buf_length: u32,
watch_subtree: Bool, notify_filter: u32,
bytes_returned: ^u32, overlapped: ^Overlapped,
completion: rawptr) -> Bool ---;
@(link_name="WideCharToMultiByte") wide_char_to_multi_byte :: proc(code_page: u32, flags: u32,
wchar_str: Wstring, wchar: i32,
multi_str: cstring, multi: i32,
default_char: cstring, used_default_char: ^Bool) -> i32 ---;
@(link_name="MultiByteToWideChar") multi_byte_to_wide_char :: proc(code_page: u32, flags: u32,
mb_str: cstring, mb: i32,
wc_str: Wstring, wc: i32) -> i32 ---;
@(link_name="CreateSemaphoreA") create_semaphore_a :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle ---;
@(link_name="CreateSemaphoreW") create_semaphore_w :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: cstring) -> Handle ---;
@(link_name="ReleaseSemaphore") release_semaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool ---;
@(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 ---;
}
// @(default_calling_convention = "c")
foreign kernel32 {
@(link_name="GetLastError") get_last_error :: proc() -> i32 ---;
@(link_name="CloseHandle") close_handle :: proc(h: Handle) -> i32 ---;
@(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 ---;
@(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 ---;
@(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="CompareFileTime") compare_file_time :: proc(a, b: ^Filetime) -> i32 ---;
}
@(default_calling_convention = "c")
foreign kernel32 {
@(link_name="InterlockedCompareExchange") interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 ---;
@(link_name="InterlockedExchange") interlocked_exchange :: proc(dst: ^i32, desired: i32) -> i32 ---;
@(link_name="InterlockedExchangeAdd") interlocked_exchange_add :: proc(dst: ^i32, desired: i32) -> i32 ---;
@(link_name="InterlockedAnd") interlocked_and :: proc(dst: ^i32, desired: i32) -> i32 ---;
@(link_name="InterlockedOr") interlocked_or :: proc(dst: ^i32, desired: i32) -> i32 ---;
@(link_name="InterlockedCompareExchange64") interlocked_compare_exchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 ---;
@(link_name="InterlockedExchange64") interlocked_exchange64 :: proc(dst: ^i64, desired: i64) -> i64 ---;
@(link_name="InterlockedExchangeAdd64") interlocked_exchange_add64 :: proc(dst: ^i64, desired: i64) -> i64 ---;
@(link_name="InterlockedAnd64") interlocked_and64 :: proc(dst: ^i64, desired: i64) -> i64 ---;
@(link_name="InterlockedOr64") interlocked_or64 :: proc(dst: ^i64, desired: i64) -> i64 ---;
}
@(default_calling_convention = "std")
foreign kernel32 {
@(link_name="_mm_pause") mm_pause :: proc() ---;
@(link_name="ReadWriteBarrier") read_write_barrier :: proc() ---;
@(link_name="WriteBarrier") write_barrier :: proc() ---;
@(link_name="ReadBarrier") read_barrier :: proc() ---;
@(link_name="CreateThread")
create_thread :: proc(thread_attributes: ^Security_Attributes, stack_size: int, start_routine: rawptr,
parameter: rawptr, creation_flags: u32, thread_id: ^u32) -> Handle ---;
@(link_name="ResumeThread") resume_thread :: proc(thread: Handle) -> u32 ---;
@(link_name="GetThreadPriority") get_thread_priority :: proc(thread: Handle) -> i32 ---;
@(link_name="SetThreadPriority") set_thread_priority :: proc(thread: Handle, priority: i32) -> Bool ---;
@(link_name="GetExitCodeThread") get_exit_code_thread :: proc(thread: Handle, exit_code: ^u32) -> Bool ---;
@(link_name="TerminateThread") terminate_thread :: proc(thread: Handle, exit_code: u32) -> Bool ---;
@(link_name="InitializeCriticalSection") initialize_critical_section :: proc(critical_section: ^Critical_Section) ---;
@(link_name="InitializeCriticalSectionAndSpinCount") initialize_critical_section_and_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) ---;
@(link_name="DeleteCriticalSection") delete_critical_section :: proc(critical_section: ^Critical_Section) ---;
@(link_name="SetCriticalSectionSpinCount") set_critical_section_spin_count :: proc(critical_section: ^Critical_Section, spin_count: u32) -> u32 ---;
@(link_name="TryEnterCriticalSection") try_enter_critical_section :: proc(critical_section: ^Critical_Section) -> Bool ---;
@(link_name="EnterCriticalSection") enter_critical_section :: proc(critical_section: ^Critical_Section) ---;
@(link_name="LeaveCriticalSection") leave_critical_section :: proc(critical_section: ^Critical_Section) ---;
@(link_name="CreateEventA") create_event_a :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: cstring) -> Handle ---;
@(link_name="CreateEventW") create_event_w :: proc(event_attributes: ^Security_Attributes, manual_reset, initial_state: Bool, name: Wstring) -> Handle ---;
@(link_name="PulseEvent") pulse_event :: proc(event: Handle) -> Bool ---;
@(link_name="SetEvent") set_event :: proc(event: Handle) -> Bool ---;
@(link_name="ResetEvent") reset_event :: proc(event: Handle) -> Bool ---;
@(link_name="LoadLibraryA") load_library_a :: proc(c_str: cstring) -> Hmodule ---;
@(link_name="LoadLibraryW") load_library_w :: proc(c_str: Wstring) -> Hmodule ---;
@(link_name="FreeLibrary") free_library :: proc(h: Hmodule) -> Bool ---;
@(link_name="GetProcAddress") get_proc_address :: proc(h: Hmodule, c_str: cstring) -> rawptr ---;
}
Memory_Basic_Information :: struct {
base_address: rawptr,
allocation_base: rawptr,
allocation_protect: u32,
region_size: uint,
state: u32,
protect: u32,
type: u32,
}
@(default_calling_convention = "std")
foreign kernel32 {
@(link_name="VirtualAlloc") virtual_alloc :: proc(address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr ---
@(link_name="VirtualAllocEx") virtual_alloc_ex :: proc(process: Handle, address: rawptr, size: uint, allocation_type: u32, protect: u32) -> rawptr ---
@(link_name="VirtualFree") virtual_free :: proc(address: rawptr, size: uint, free_type: u32) -> Bool ---
@(link_name="VirtualLock") virtual_lock :: proc(address: rawptr, size: uint) -> Bool ---
@(link_name="VirtualProtect") virtual_protect :: proc(address: rawptr, size: uint, new_protect: u32, old_protect: ^u32) -> Bool ---
@(link_name="VirtualQuery") virtual_query :: proc(address: rawptr, buffer: ^Memory_Basic_Information, length: uint) -> uint ---
}
MEM_COMMIT :: 0x00001000;
MEM_RESERVE :: 0x00002000;
MEM_DECOMMIT :: 0x00004000;
MEM_RELEASE :: 0x00008000;
MEM_RESET :: 0x00080000;
MEM_RESET_UNDO :: 0x01000000;
MEM_LARGE_PAGES :: 0x20000000;
MEM_PHYSICAL :: 0x00400000;
MEM_TOP_DOWN :: 0x00100000;
MEM_WRITE_WATCH :: 0x00200000;
PAGE_NOACCESS :: 0x01;
PAGE_READONLY :: 0x02;
PAGE_READWRITE :: 0x04;
PAGE_WRITECOPY :: 0x08;
PAGE_EXECUTE :: 0x10;
PAGE_EXECUTE_READ :: 0x20;
PAGE_EXECUTE_READWRITE :: 0x40;
PAGE_EXECUTE_WRITECOPY :: 0x80;
+18
View File
@@ -0,0 +1,18 @@
// +build windows
package win32
foreign import "system:ole32.lib"
//objbase.h
Com_Init :: enum {
Multi_Threaded = 0x0,
Apartment_Threaded = 0x2,
Disable_OLE1_DDE = 0x4,
Speed_Over_Memory = 0x8,
};
@(default_calling_convention = "std")
foreign ole32 {
@(link_name ="CoInitializeEx") com_init_ex :: proc(reserved: rawptr, co_init: Com_Init) ->Hresult ---;
@(link_name = "CoUninitialize") com_shutdown :: proc() ---;
}
+9
View File
@@ -0,0 +1,9 @@
// +build windows
package win32
foreign import "system:shell32.lib"
@(default_calling_convention = "std")
foreign shell32 {
@(link_name="CommandLineToArgvW") command_line_to_argv_w :: proc(cmd_list: Wstring, num_args: ^i32) -> ^Wstring ---;
}
+270
View File
@@ -0,0 +1,270 @@
// +build windows
package win32
foreign import "system:user32.lib"
Menu_Bar_Info :: struct {
size: u32,
bar: Rect,
menu: Hmenu,
wnd_menu: Hwnd,
using fields: bit_field {
bar_focused: 1,
focuses: 1,
},
}
Menu_Item_Info_A :: struct {
size: u32,
mask: u32,
type: u32,
state: u32,
id: u32,
submenu: Hmenu,
bmp_checked: Hbitmap,
bmp_unchecked: Hbitmap,
item_data: u32,
type_data: cstring,
cch: u32,
}
Menu_Item_Info_W :: struct {
size: u32,
mask: u32,
type: u32,
state: u32,
id: u32,
submenu: Hmenu,
bmp_checked: Hbitmap,
bmp_unchecked: Hbitmap,
item_data: u32,
type_data: Wstring,
cch: u32,
}
MF_BYCOMMAND :: 0x00000000;
MF_BYPOSITION :: 0x00000400;
MF_BITMAP :: 0x00000004;
MF_CHECKED :: 0x00000008;
MF_DISABLED :: 0x00000002;
MF_ENABLED :: 0x00000000;
MF_GRAYED :: 0x00000001;
MF_MENUBARBREAK :: 0x00000020;
MF_MENUBREAK :: 0x00000040;
MF_OWNERDRAW :: 0x00000100;
MF_POPUP :: 0x00000010;
MF_SEPARATOR :: 0x00000800;
MF_STRING :: 0x00000000;
MF_UNCHECKED :: 0x00000000;
MB_ABORTRETRYIGNORE :: 0x00000002;
MB_CANCELTRYCONTINUE :: 0x00000006;
MB_HELP :: 0x00004000;
MB_OK :: 0x00000000;
MB_OKCANCEL :: 0x00000001;
MB_RETRYCANCEL :: 0x00000005;
MB_YESNO :: 0x00000004;
MB_YESNOCANCEL :: 0x00000003;
MB_ICONEXCLAMATION :: 0x00000030;
MB_ICONWARNING :: 0x00000030;
MB_ICONINFORMATION :: 0x00000040;
MB_ICONASTERISK :: 0x00000040;
MB_ICONQUESTION :: 0x00000020;
MB_ICONSTOP :: 0x00000010;
MB_ICONERROR :: 0x00000010;
MB_ICONHAND :: 0x00000010;
MB_DEFBUTTON1 :: 0x00000000;
MB_DEFBUTTON2 :: 0x00000100;
MB_DEFBUTTON3 :: 0x00000200;
MB_DEFBUTTON4 :: 0x00000300;
MB_APPLMODAL :: 0x00000000;
MB_SYSTEMMODAL :: 0x00001000;
MB_TASKMODAL :: 0x00002000;
MB_DEFAULT_DESKTOP_ONLY :: 0x00020000;
MB_RIGHT :: 0x00080000;
MB_RTLREADING :: 0x00100000;
MB_SETFOREGROUND :: 0x00010000;
MB_TOPMOST :: 0x00040000;
MB_SERVICE_NOTIFICATION :: 0x00200000;
@(default_calling_convention = "std")
foreign user32 {
@(link_name="GetDesktopWindow") get_desktop_window :: proc() -> Hwnd ---;
@(link_name="ShowCursor") show_cursor :: proc(show: Bool) ---;
@(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool ---;
@(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool ---;
@(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool ---;
@(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool ---;
@(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) ---;
@(link_name="SetWindowTextA") set_window_text_a :: proc(hwnd: Hwnd, c_string: cstring) -> Bool ---;
@(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---;
@(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---;
@(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---;
@(link_name="CreateWindowExA")
create_window_ex_a :: proc(ex_style: u32,
class_name, title: cstring,
style: u32,
x, y, w, h: i32,
parent: Hwnd, menu: Hmenu, instance: Hinstance,
param: rawptr) -> Hwnd ---;
@(link_name="CreateWindowExW")
create_window_ex_w :: proc(ex_style: u32,
class_name, title: Wstring,
style: u32,
x, y, w, h: i32,
parent: Hwnd, menu: Hmenu, instance: Hinstance,
param: rawptr) -> Hwnd ---;
@(link_name="ShowWindow") show_window :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool ---;
@(link_name="TranslateMessage") translate_message :: proc(msg: ^Msg) -> Bool ---;
@(link_name="DispatchMessageA") dispatch_message_a :: proc(msg: ^Msg) -> Lresult ---;
@(link_name="DispatchMessageW") dispatch_message_w :: proc(msg: ^Msg) -> Lresult ---;
@(link_name="UpdateWindow") update_window :: proc(hwnd: Hwnd) -> Bool ---;
@(link_name="GetMessageA") get_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool ---;
@(link_name="GetMessageW") get_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max: u32) -> Bool ---;
@(link_name="PeekMessageA") peek_message_a :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool ---;
@(link_name="PeekMessageW") peek_message_w :: proc(msg: ^Msg, hwnd: Hwnd, msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool ---;
@(link_name="PostMessageA") post_message_a :: proc(hwnd: Hwnd, msg, wparam, lparam: u32) -> Bool ---;
@(link_name="PostMessageW") post_message_w :: proc(hwnd: Hwnd, msg, wparam, lparam: u32) -> Bool ---;
@(link_name="DefWindowProcA") def_window_proc_a :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---;
@(link_name="DefWindowProcW") def_window_proc_w :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult ---;
@(link_name="AdjustWindowRect") adjust_window_rect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool ---;
@(link_name="GetActiveWindow") get_active_window :: proc() -> Hwnd ---;
@(link_name="DestroyWindow") destroy_window :: proc(wnd: Hwnd) -> Bool ---;
@(link_name="DescribePixelFormat") describe_pixel_format :: proc(dc: Hdc, pixel_format: i32, bytes: u32, pfd: ^Pixel_Format_Descriptor) -> i32 ---;
@(link_name="GetMonitorInfoA") get_monitor_info_a :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool ---;
@(link_name="MonitorFromWindow") monitor_from_window :: proc(wnd: Hwnd, flags: u32) -> Hmonitor ---;
@(link_name="SetWindowPos") set_window_pos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) ---;
@(link_name="GetWindowPlacement") get_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool ---;
@(link_name="SetWindowPlacement") set_window_placement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool ---;
@(link_name="GetWindowRect") get_window_rect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool ---;
@(link_name="GetWindowLongPtrA") get_window_long_ptr_a :: proc(wnd: Hwnd, index: i32) -> Long_Ptr ---;
@(link_name="SetWindowLongPtrA") set_window_long_ptr_a :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr ---;
@(link_name="GetWindowLongPtrW") get_window_long_ptr_w :: proc(wnd: Hwnd, index: i32) -> Long_Ptr ---;
@(link_name="SetWindowLongPtrW") set_window_long_ptr_w :: proc(wnd: Hwnd, index: i32, new: Long_Ptr) -> Long_Ptr ---;
@(link_name="GetWindowText") get_window_text :: proc(wnd: Hwnd, str: cstring, maxCount: i32) -> i32 ---;
@(link_name="GetClientRect") get_client_rect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool ---;
@(link_name="GetDC") get_dc :: proc(h: Hwnd) -> Hdc ---;
@(link_name="ReleaseDC") release_dc :: proc(wnd: Hwnd, hdc: Hdc) -> i32 ---;
@(link_name="MapVirtualKeyA") map_virtual_key_a :: proc(scancode: u32, map_type: u32) -> u32 ---;
@(link_name="MapVirtualKeyW") map_virtual_key_w :: proc(scancode: u32, map_type: u32) -> u32 ---;
@(link_name="GetKeyState") get_key_state :: proc(v_key: i32) -> i16 ---;
@(link_name="GetAsyncKeyState") get_async_key_state :: proc(v_key: i32) -> i16 ---;
@(link_name="SetForegroundWindow") set_foreground_window :: proc(h: Hwnd) -> Bool ---;
@(link_name="SetFocus") set_focus :: proc(h: Hwnd) -> Hwnd ---;
@(link_name="LoadImageA") load_image_a :: proc(instance: Hinstance, name: cstring, type_: u32, x_desired, y_desired : i32, load : u32) -> Handle ---;
@(link_name="LoadIconA") load_icon_a :: proc(instance: Hinstance, icon_name: cstring) -> Hicon ---;
@(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool ---;
@(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---;
@(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor ---;
@(link_name="GetCursor") get_cursor :: proc() -> Hcursor ---;
@(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor ---;
@(link_name="RegisterRawInputDevices") register_raw_input_devices :: proc(raw_input_device: ^Raw_Input_Device, num_devices, size: u32) -> Bool ---;
@(link_name="GetRawInputData") get_raw_input_data :: proc(raw_input: Hrawinput, command: u32, data: rawptr, size: ^u32, size_header: u32) -> u32 ---;
@(link_name="MapVirtualKeyExW") map_virtual_key_ex_w :: proc(code, map_type: u32, hkl: HKL) ---;
@(link_name="MapVirtualKeyExA") map_virtual_key_ex_a :: proc(code, map_type: u32, hkl: HKL) ---;
@(link_name="EnumDisplayMonitors") enum_display_monitors :: proc(hdc: Hdc, rect: ^Rect, enum_proc: Monitor_Enum_Proc, lparam: Lparam) -> bool ---;
}
@(default_calling_convention = "c")
foreign user32 {
@(link_name="CreateMenu") create_menu :: proc() -> Hmenu ---
@(link_name="CreatePopupMenu") create_popup_menu :: proc() -> Hmenu ---
@(link_name="DestroyMenu") destroy_menu :: proc(menu: Hmenu) -> Bool ---
@(link_name="DeleteMenu") delete_menu :: proc(menu: Hmenu, position: u32, flags: u32) -> Bool ---
@(link_name="EnableMenuItem") enable_menu_item :: proc(menu: Hmenu, id_enable_itme: i32, enable: u32) -> Bool ---
@(link_name="EndMenu") end_menu :: proc() -> Bool ---
@(link_name="GetMenu") get_menu :: proc(wnd: Hwnd) -> Hmenu ---
@(link_name="GetMenuBarInfo") get_menu_bar_info :: proc(wnd: Hwnd, id_object, id_item: u32, mbi: ^Menu_Bar_Info) -> Hmenu ---
@(link_name="GetMenuStringA") get_menu_string_a :: proc(menu: Hmenu, id_item: u32, s: string, cch_max: i32, flags: u32) -> i32 ---
@(link_name="GetMenuStringW") get_menu_string_w :: proc(menu: Hmenu, id_item: u32, s: Wstring, cch_max: i32, flags: u32) -> i32 ---
@(link_name="GetMenuState") get_menu_state :: proc(menu: Hmenu, id: u32, flags: u32) -> u32 ---
@(link_name="GetMenuItemRect") get_menu_item_rect :: proc(wnd: Hwnd, menu: Hmenu, id_item: u32, item: ^Rect) -> Bool ---
@(link_name="SetMenu") set_menu :: proc(wnd: Hwnd, menu: Hmenu) -> Hmenu ---
@(link_name="DrawMenuBar") draw_menu_bar :: proc(wnd: Hwnd) -> Bool ---
@(link_name="InsertMenuA") insert_menu_a :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool ---
@(link_name="InsertMenuW") insert_menu_w :: proc(menu: Hmenu, position: u32, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool ---
@(link_name="InsertMenuItemA") insert_menu_item_a :: proc(menu: Hmenu, item: u32, by_position: bool, mi: ^Menu_Item_Info_A) -> Bool ---
@(link_name="InsertMenuItemW") insert_menu_item_w :: proc(menu: Hmenu, item: u32, by_position: bool, mi: ^Menu_Item_Info_W) -> Bool ---
@(link_name="AppendMenuA") append_menu_a :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: cstring) -> Bool ---
@(link_name="AppendMenuW") append_menu_w :: proc(menu: Hmenu, flags: u32, id_new_item: Uint_Ptr, new_item: Wstring) -> Bool ---
@(link_name="CheckMenuItem") check_menu_item :: proc(menu: Hmenu, id_check_item: u32, check: u32) -> u32 ---
@(link_name="CheckMenuRadioItem") check_menu_radio_item :: proc(menu: Hmenu, first, last: u32, check: u32, flags: u32) -> Bool ---
@(link_name="GetPropA") get_prop_a :: proc(wnd: Hwnd, s: cstring) -> Handle ---
@(link_name="GetPropW") get_prop_w :: proc(wnd: Hwnd, s: Wstring) -> Handle ---
@(link_name="MessageBoxExA") message_box_ex_a :: proc(wnd: Hwnd, text, caption: cstring, type: u32, language_id: u16) -> i32 ---
@(link_name="MessageBoxExW") message_box_ex_w :: proc(wnd: Hwnd, text, caption: Wstring, type: u32, language_id: u16) -> i32 ---
}
_IDC_APPSTARTING := rawptr(uintptr(32650));
_IDC_ARROW := rawptr(uintptr(32512));
_IDC_CROSS := rawptr(uintptr(32515));
_IDC_HAND := rawptr(uintptr(32649));
_IDC_HELP := rawptr(uintptr(32651));
_IDC_IBEAM := rawptr(uintptr(32513));
_IDC_ICON := rawptr(uintptr(32641));
_IDC_NO := rawptr(uintptr(32648));
_IDC_SIZE := rawptr(uintptr(32640));
_IDC_SIZEALL := rawptr(uintptr(32646));
_IDC_SIZENESW := rawptr(uintptr(32643));
_IDC_SIZENS := rawptr(uintptr(32645));
_IDC_SIZENWSE := rawptr(uintptr(32642));
_IDC_SIZEWE := rawptr(uintptr(32644));
_IDC_UPARROW := rawptr(uintptr(32516));
_IDC_WAIT := rawptr(uintptr(32514));
IDC_APPSTARTING := cstring(_IDC_APPSTARTING);
IDC_ARROW := cstring(_IDC_ARROW);
IDC_CROSS := cstring(_IDC_CROSS);
IDC_HAND := cstring(_IDC_HAND);
IDC_HELP := cstring(_IDC_HELP);
IDC_IBEAM := cstring(_IDC_IBEAM);
IDC_ICON := cstring(_IDC_ICON);
IDC_NO := cstring(_IDC_NO);
IDC_SIZE := cstring(_IDC_SIZE);
IDC_SIZEALL := cstring(_IDC_SIZEALL);
IDC_SIZENESW := cstring(_IDC_SIZENESW);
IDC_SIZENS := cstring(_IDC_SIZENS);
IDC_SIZENWSE := cstring(_IDC_SIZENWSE);
IDC_SIZEWE := cstring(_IDC_SIZEWE);
IDC_UPARROW := cstring(_IDC_UPARROW);
IDC_WAIT := cstring(_IDC_WAIT);
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
// +build windows
package win32
foreign import "system:winmm.lib"
@(default_calling_convention = "std")
foreign winmm {
@(link_name="timeGetTime") time_get_time :: proc() -> u32 ---;
}
+224
View File
@@ -0,0 +1,224 @@
package time
Duration :: distinct i64;
Nanosecond :: Duration(1);
Microsecond :: 1000 * Nanosecond;
Millisecond :: 1000 * Microsecond;
Second :: 1000 * Millisecond;
Minute :: 60 * Second;
Hour :: 60 * Minute;
MIN_DURATION :: Duration(-1 << 63);
MAX_DURATION :: Duration(1<<63 - 1);
Time :: struct {
_nsec: i64, // zero is 1970-01-01 00:00:00
}
Month :: enum int {
January = 1,
February,
March,
April,
May,
June,
July,
August,
September,
October,
November,
December,
}
Weekday :: enum int {
Sunday = 0,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
}
diff :: proc(start, end: Time) -> Duration {
d := end._nsec - start._nsec;
return Duration(d);
}
duration_nanoseconds :: proc(d: Duration) -> i64 {
return i64(d);
}
duration_seconds :: proc(d: Duration) -> f64 {
sec := d / Second;
nsec := d % Second;
return f64(sec) + f64(nsec)/1e9;
}
duration_minutes :: proc(d: Duration) -> f64 {
min := d / Minute;
nsec := d % Minute;
return f64(min) + f64(nsec)/(60*1e9);
}
duration_hours :: proc(d: Duration) -> f64 {
hour := d / Hour;
nsec := d % Hour;
return f64(hour) + f64(nsec)/(60*60*1e9);
}
_less_than_half :: inline proc(x, y: Duration) -> bool {
return u64(x)+u64(x) < u64(y);
}
duration_round :: proc(d, m: Duration) -> Duration {
if m <= 0 do return d;
r := d % m;
if d < 0 {
r = -r;
if _less_than_half(r, m) {
return d + r;
}
if d1 := d-m+r; d1 < d {
return d1;
}
return MIN_DURATION;
}
if _less_than_half(r, m) {
return d - r;
}
if d1 := d+m-r; d1 > d {
return d1;
}
return MAX_DURATION;
}
duration_truncate :: proc(d, m: Duration) -> Duration {
return m <= 0 ? d : d - d%m;
}
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
year, month, day, _ = _abs_date(_time_abs(t), true);
return;
}
year :: proc(t: Time) -> (year: int) {
year, _, _, _ = _date(t, true);
return;
}
month :: proc(t: Time) -> (month: Month) {
_, month, _, _ = _date(t, true);
return;
}
day :: proc(t: Time) -> (day: int) {
_, _, day, _ = _date(t, true);
return;
}
clock :: proc(t: Time) -> (hour, min, sec: int) {
sec = int(_time_abs(t) % SECONDS_PER_DAY);
hour = sec / SECONDS_PER_HOUR;
sec -= hour * SECONDS_PER_HOUR;
min = sec / SECONDS_PER_MINUTE;
sec -= min * SECONDS_PER_MINUTE;
return;
}
ABSOLUTE_ZERO_YEAR :: -292277022399; // Day is chosen so that 2001-01-01 is Monday in the calculations
UNIX_TO_ABSOLUTE :: (1969*365 + 1969/4 - 1969/100 + 1969/400 - ((ABSOLUTE_ZERO_YEAR - 1) * 365.2425)) * SECONDS_PER_DAY;
_is_leap_year :: proc(year: int) -> bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0);
}
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
year, month, day, yday = _abs_date(_time_abs(t), full);
return;
}
_time_abs :: proc(t: Time) -> u64 {
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE);
}
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
d := abs / SECONDS_PER_DAY;
// 400 year cycles
n := d / DAYS_PER_400_YEARS;
y := 400 * n;
d -= DAYS_PER_400_YEARS * n;
// Cut-off 100 year cycles
n = d / DAYS_PER_100_YEARS;
n -= n >> 2;
y += 100 * n;
d -= DAYS_PER_100_YEARS * n;
// Cut-off 4 year cycles
n = d / DAYS_PER_4_YEARS;
y += 4 * n;
d -= DAYS_PER_4_YEARS * n;
n = d / 365;
n -= n >> 2;
y += n;
d -= 365 * n;
year = int(i64(y) + ABSOLUTE_ZERO_YEAR);
yday = int(d);
if !full {
return;
}
day = yday;
if _is_leap_year(year) do switch {
case day < 31+29-1:
day -= 1;
case day == 31+29-1:
month = Month.February;
day = 29;
return;
}
month = Month(day / 31);
end := int(days_before[int(month)+1]);
begin: int;
if day >= end {
(^int)(&month)^ += 1;
begin = end;
} else {
begin = int(days_before[month]);
}
(^int)(&month)^ += 1; // January is 1
day = day - begin + 1;
return;
}
days_before := [?]i32{
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
};
SECONDS_PER_MINUTE :: 60;
SECONDS_PER_HOUR :: 60 * SECONDS_PER_MINUTE;
SECONDS_PER_DAY :: 24 * SECONDS_PER_HOUR;
SECONDS_PER_WEEK :: 7 * SECONDS_PER_DAY;
DAYS_PER_400_YEARS :: 365*400 + 97;
DAYS_PER_100_YEARS :: 365*100 + 24;
DAYS_PER_4_YEARS :: 365*4 + 1;
+56
View File
@@ -0,0 +1,56 @@
package time
foreign import libc "system:c"
TimeSpec :: struct {
tv_sec : i64, /* seconds */
tv_nsec : i64, /* nanoseconds */
};
CLOCK_SYSTEM :: 0;
CLOCK_CALENDAR :: 1;
IS_SUPPORTED :: true;
foreign libc {
@(link_name="clock_gettime") _clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---;
@(link_name="nanosleep") _nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---;
@(link_name="sleep") _sleep :: proc(seconds: u64) -> int ---;
}
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
ts : TimeSpec;
_clock_gettime(clock_id, &ts);
return ts;
}
now :: proc() -> Time {
time_spec_now := clock_gettime(CLOCK_SYSTEM);
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
return Time{_nsec=ns};
}
seconds_since_boot :: proc() -> f64 {
ts_boottime := clock_gettime(CLOCK_SYSTEM);
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
}
sleep :: proc(d: Duration) {
ds := duration_seconds(d);
seconds := u64(ds);
nanoseconds := i64((ds - f64(seconds)) * 1e9);
if seconds > 0 do _sleep(seconds);
if nanoseconds > 0 do nanosleep(nanoseconds);
}
nanosleep :: proc(nanoseconds: i64) -> int {
assert(nanoseconds <= 999999999);
requested, remaining : TimeSpec;
requested = TimeSpec{tv_nsec = nanoseconds};
return _nanosleep(&requested, &remaining);
}
+2
View File
@@ -0,0 +1,2 @@
package time
IS_SUPPORTED :: false;
+44
View File
@@ -0,0 +1,44 @@
package time
import "core:os";
// NOTE(Jeroen): The times returned are in UTC
IS_SUPPORTED :: true;
now :: proc() -> Time {
time_spec_now := os.clock_gettime(os.CLOCK_REALTIME);
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
return Time{_nsec=ns};
}
boot_time :: proc() -> Time {
ts_now := os.clock_gettime(os.CLOCK_REALTIME);
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
return Time{_nsec=ns};
}
seconds_since_boot :: proc() -> f64 {
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
}
sleep :: proc(d: Duration) {
ds := duration_seconds(d);
seconds := u64(ds);
nanoseconds := i64((ds - f64(seconds)) * 1e9);
if seconds > 0 do os.sleep(seconds);
if nanoseconds > 0 do os.nanosleep(nanoseconds);
}
nanosleep :: proc(d: Duration) {
// NOTE(Jeroen): os.nanosleep returns -1 on failure, 0 on success
// duration needs to be [0, 999999999] nanoseconds.
os.nanosleep(i64(d));
}
+24
View File
@@ -0,0 +1,24 @@
package time
import "core:sys/win32"
IS_SUPPORTED :: true;
now :: proc() -> Time {
file_time: win32.Filetime;
win32.get_system_time_as_file_time(&file_time);
quad := u64(file_time.lo) | u64(file_time.hi) << 32;
UNIX_TIME_START :: 0x019db1ded53e8000;
ns := (1e9/1e7)*(i64(quad) - UNIX_TIME_START);
return Time{_nsec=ns};
}
sleep :: proc(d: Duration) {
win32.sleep(u32(d/Millisecond));
}
+4 -3
View File
@@ -21,7 +21,8 @@ decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
}
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
encode_surrogate_pair :: proc(c: rune) -> (r1, r2: rune) {
r := c;
if r < _surr_self || r > MAX_RUNE {
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
}
@@ -33,7 +34,7 @@ encode :: proc(d: []u16, s: []rune) -> int {
n, m := 0, len(d);
loop: for r in s {
switch r {
case 0.._surr1-1, _surr3 .. _surr_self-1:
case 0..<_surr1, _surr3 ..< _surr_self:
if m+1 < n do break loop;
d[n] = u16(r);
n += 1;
@@ -59,7 +60,7 @@ encode_string :: proc(d: []u16, s: string) -> int {
n, m := 0, len(d);
loop: for r in s {
switch r {
case 0.._surr1-1, _surr3 .. _surr_self-1:
case 0..<_surr1, _surr3 ..< _surr_self:
if m+1 < n do break loop;
d[n] = u16(r);
n += 1;
+71 -26
View File
@@ -41,26 +41,22 @@ accept_ranges := [5]Accept_Range{
};
accept_sizes := [256]u8{
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
0x00..0x7f = 0xf0,
0x80..0xc1 = 0xf1,
0xc2..0xdf = 0x02,
0xe0 = 0x13,
0xe1..0xec = 0x03,
0xed = 0x23,
0xee..0xef = 0x03,
0xf0 = 0x34,
0xf1..0xf3 = 0x04,
0xf4 = 0x44,
0xf5..0xff = 0xf1,
};
encode_rune :: proc(r: rune) -> ([4]u8, int) {
encode_rune :: proc(c: rune) -> ([4]u8, int) {
r := c;
buf: [4]u8;
i := u32(r);
mask :: u8(0x3f);
@@ -90,11 +86,11 @@ encode_rune :: proc(r: rune) -> ([4]u8, int) {
buf[0] = 0xf0 | u8(r>>18);
buf[1] = 0x80 | u8(r>>12) & mask;
buf[2] = 0x80 | u8(r>>6) & mask;
buf[3] = 0x80 | u8(r) & mask;
buf[3] = 0x80 | u8(r) & mask;
return buf, 4;
}
decode_rune_from_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
decode_rune :: proc(s: []u8) -> (rune, int) {
n := len(s);
if n < 1 {
@@ -134,7 +130,7 @@ decode_rune :: proc(s: []u8) -> (rune, int) {
decode_last_rune_from_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
decode_last_rune :: proc(s: []u8) -> (rune, int) {
r: rune;
size: int;
@@ -165,9 +161,58 @@ decode_last_rune :: proc(s: []u8) -> (rune, int) {
return r, size;
}
rune_at_pos :: proc(s: string, pos: int) -> rune {
if pos < 0 {
return RUNE_ERROR;
}
i := 0;
for r in s {
if i == pos {
return r;
}
i += 1;
}
return RUNE_ERROR;
}
rune_string_at_pos :: proc(s: string, pos: int) -> string {
if pos < 0 {
return "";
}
i := 0;
for c, offset in s {
if i == pos {
w := rune_size(c);
return s[offset:][:w];
}
i += 1;
}
return "";
}
rune_at :: proc(s: string, byte_index: int) -> rune {
r, _ := decode_rune_in_string(s[byte_index:]);
return r;
}
// Returns the byte position of rune at position pos in s with an optional start byte position.
// Returns -1 if it runs out of the string.
rune_offset :: proc(s: string, pos: int, start: int = 0) -> int {
if pos < 0 {
return -1;
}
i := 0;
for _, offset in s[start:] {
if i == pos {
return offset+start;
}
i += 1;
}
return -1;
}
valid_rune :: proc(r: rune) -> bool {
if r < 0 {
@@ -201,11 +246,11 @@ valid_string :: proc(s: string) -> bool {
return false;
} else if size == 2 {
// Okay
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
} else if c := s[i+2]; c < 0x80 || 0xbf < c {
return false;
} else if size == 3 {
// Okay
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
} else if d := s[i+3]; b < 0x80 || 0xbf < d {
return false;
}
i += size;
@@ -215,7 +260,7 @@ valid_string :: proc(s: string) -> bool {
rune_start :: inline proc(b: u8) -> bool do return b&0xc0 != 0x80;
rune_count_from_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
rune_count_in_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
rune_count :: proc(s: []u8) -> int {
count := 0;
n := len(s);
@@ -242,11 +287,11 @@ rune_count :: proc(s: []u8) -> int {
size = 1;
} else if size == 2 {
// Okay
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
} else if c := s[i+2]; c < 0x80 || 0xbf < c {
size = 1;
} else if size == 3 {
// Okay
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
} else if d := s[i+3]; d < 0x80 || 0xbf < d {
size = 1;
}
i += size;
+493 -77
View File
@@ -1,29 +1,36 @@
package main
import "core:fmt"
import "core:strconv"
import "core:mem"
import "core:bits"
import "core:hash"
import "core:math"
import "core:math/rand"
import "core:os"
import "core:sort"
import "core:strings"
import "core:types"
import "core:unicode/utf16"
import "core:unicode/utf8"
import "core:c"
import "core:runtime"
import "core:reflect"
import "intrinsics"
when os.OS == "windows" {
import "core:thread"
import "core:sys/win32"
}
@(link_name="general_stuff")
general_stuff :: proc() {
fmt.println("# general_stuff");
/*
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
* simplicity
* high performance
* built for modern systems
* joy of programming
# Installing Odin
Getting Started - https://odin-lang.org/docs/install/
Instructions for downloading and install the Odin compiler and libraries.
# Learning Odin
Overview of Odin - https://odin-lang.org/docs/overview/
An overview of the Odin programming language.
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
Answers to common questions about Odin.
*/
@(link_name="extra_general_stuff")
extra_general_stuff :: proc() {
fmt.println("# extra_general_stuff");
{ // `do` for inline statements rather than block
foo :: proc() do fmt.println("Foo!");
if false do foo();
@@ -43,27 +50,25 @@ general_stuff :: proc() {
i := i32(137);
ptr := &i;
_ = (^f32)(ptr);
_ = (^f32)(ptr); // Call-based syntax
// ^f32(ptr) == ^(f32(ptr))
_ = cast(^f32)ptr;
_ = cast(^f32)ptr; // Operator-based syntax
_ = (^f32)(ptr)^;
_ = (cast(^f32)ptr)^;
// Questions: Should there be two ways to do it?
}
/*
* Remove *_val_of built-in procedures
* size_of, align_of, offset_of
* type_of, type_info_of
* type_of, type_info_of, typeid_of
*/
{ // `expand_to_tuple` built-in procedure
Foo :: struct {
x: int,
b: bool,
}
};
f := Foo{137, true};
x, b := expand_to_tuple(f);
fmt.println(f);
@@ -73,8 +78,10 @@ general_stuff :: proc() {
{
// .. open range
// ..< half-closed range
for in 0..2 {} // 0, 1, 2
for in 0..<2 {} // 0, 1
}
{ // Multiple sized booleans
@@ -108,6 +115,9 @@ general_stuff :: proc() {
My_Struct :: struct{x: int};
#assert(My_Struct != struct{x: int});
My_Struct2 :: My_Struct;
#assert(My_Struct2 == My_Struct);
}
{
@@ -123,6 +133,38 @@ general_stuff :: proc() {
fmt.println("Y is not defined");
}
}
{ // Labelled control blocks
block: {
if true {
fmt.println("break block;");
break block;
}
}
{
branch: if true {
fmt.println("break branch;");
break branch;
}
}
{
loop: for true {
fmt.println("break loop;");
break loop;
}
}
{
cases: switch {
case:
fmt.println("break cases;");
break cases;
}
}
}
}
@@ -167,8 +209,8 @@ union_type :: proc() {
}
}
Vector3 :: struct {x, y, z: f32};
Quaternion :: struct {x, y, z, w: f32};
Vector3 :: distinct [3]f32;
Quaternion :: distinct quaternion128;
// More realistic examples
{
@@ -185,18 +227,18 @@ union_type :: proc() {
orientation: Quaternion,
derived: any,
}
};
Frog :: struct {
using entity: Entity,
jump_height: f32,
}
};
Monster :: struct {
using entity: Entity,
is_robot: bool,
is_zombie: bool,
}
};
// See `parametric_polymorphism` procedure for details
new_entity :: proc($T: typeid) -> ^Entity {
@@ -213,6 +255,7 @@ union_type :: proc() {
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
fmt.println("I'm a monster");
}
}
@@ -229,18 +272,18 @@ union_type :: proc() {
orientation: Quaternion,
derived: union {Frog, Monster},
}
};
Frog :: struct {
using entity: ^Entity,
jump_height: f32,
}
};
Monster :: struct {
using entity: ^Entity,
is_robot: bool,
is_zombie: bool,
}
};
// See `parametric_polymorphism` procedure for details
new_entity :: proc($T: typeid) -> ^Entity {
@@ -277,17 +320,17 @@ union_type :: proc() {
/*
Entity :: struct {
..
...
derived: union{^Frog, ^Monster},
}
Frog :: struct {
using entity: Entity,
..
...
}
Monster :: struct {
using entity: Entity,
..
...
}
new_entity :: proc(T: type) -> ^Entity {
@@ -300,7 +343,7 @@ union_type :: proc() {
}
parametric_polymorphism :: proc() {
fmt.println("# parametric_polymorphism");
fmt.println("\n# parametric_polymorphism");
print_value :: proc(value: $T) {
fmt.printf("print_value: %T %v\n", value, value);
@@ -358,20 +401,19 @@ parametric_polymorphism :: proc() {
hash: u32,
key: Key,
value: Value,
}
};
TABLE_SIZE_MIN :: 32;
Table :: struct(Key, Value: typeid) {
count: int,
allocator: mem.Allocator,
slots: []Table_Slot(Key, Value),
}
};
// Only allow types that are specializations of a (polymorphic) slice
make_slice :: proc($T: typeid/[]$E, len: int) -> T {
return make(T, len);
}
// Only allow types that are specializations of `Table`
allocate :: proc(table: ^$T/Table, capacity: int) {
c := context;
@@ -408,7 +450,6 @@ parametric_polymorphism :: proc() {
}
assert(table.count <= len(table.slots));
hash := get_hash(key);
index = int(hash % u32(len(table.slots)));
for table.slots[index].occupied {
@@ -459,7 +500,7 @@ parametric_polymorphism :: proc() {
get_hash :: proc(s: string) -> u32 { // fnv32a
h: u32 = 0x811c9dc5;
for i in 0..len(s)-1 {
for i in 0..<len(s) {
h = (h ~ u32(s[i])) * 0x01000193;
}
return h;
@@ -490,7 +531,7 @@ parametric_polymorphism :: proc() {
Foo1,
Foo2,
Foo3,
}
};
Para_Union :: union(T: typeid) {T, Error};
r: Para_Union(int);
fmt.println(typeid_of(type_of(r)));
@@ -498,7 +539,7 @@ parametric_polymorphism :: proc() {
fmt.println(r);
r = 123;
fmt.println(r);
r = Error.Foo0;
r = Error.Foo0; // r = .Foo0; is allow too, see implicit selector expressions below
fmt.println(r);
}
@@ -508,9 +549,9 @@ parametric_polymorphism :: proc() {
// `I` is the type of N
// `T` is the type passed
fmt.printf("Generating an array of type %v from the value %v of type %v\n",
typeid_of(type_of(res)), N, typeid_of(I));
for i in 0..N-1 {
res[i] = i*i;
typeid_of(type_of(res)), N, typeid_of(I));
for i in 0..<N {
res[i] = T(i*i);
}
return;
}
@@ -520,6 +561,30 @@ parametric_polymorphism :: proc() {
for v, i in array {
assert(v == T(i*i));
}
// Matrix multiplication
mul :: proc(a: [$M][$N]$T, b: [N][$P]T) -> (c: [M][P]T) {
for i in 0..<M {
for j in 0..<P {
for k in 0..<N {
c[i][j] += a[i][k] * b[k][j];
}
}
}
return;
}
x := [2][3]f32{
{1, 2, 3},
{3, 2, 1},
};
y := [3][2]f32{
{0, 8},
{6, 2},
{8, 4},
};
z := mul(x, y);
assert(z == {{36, 24}, {20, 32}});
}
}
@@ -537,21 +602,7 @@ prefix_table := [?]string{
threading_example :: proc() {
when os.OS == "windows" {
fmt.println("# threading_example");
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, index, len(array));
n := len(array)-1;
if index != n {
array[index] = array[n];
}
pop(array);
}
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, index, len(array));
copy(array[index:], array[index+1:]);
pop(array);
}
fmt.println("\n# threading_example");
worker_proc :: proc(t: ^thread.Thread) -> int {
for iteration in 1..5 {
@@ -591,7 +642,7 @@ threading_example :: proc() {
}
array_programming :: proc() {
fmt.println("# array_programming");
fmt.println("\n# array_programming");
{
a := [3]f32{1, 2, 3};
b := [3]f32{5, 6, 7};
@@ -636,7 +687,7 @@ array_programming :: proc() {
}
named_proc_return_parameters :: proc() {
fmt.println("# named proc return parameters");
fmt.println("\n# named proc return parameters");
foo0 :: proc() -> int {
return 123;
@@ -658,7 +709,7 @@ named_proc_return_parameters :: proc() {
using_enum :: proc() {
fmt.println("# using enum");
fmt.println("\n# using enum");
using Foo :: enum {A, B, C};
@@ -667,15 +718,93 @@ using_enum :: proc() {
f2 := C;
fmt.println(f0, f1, f2);
fmt.println(len(Foo));
}
// Non-comparsion operations are not allowed with enum
// You must convert to an integer if you want to do this
// x := f0 + f1;
y := int(f0) + int(f1);
map_type :: proc() {
fmt.println("\n# map type");
// enums of type u16, u32, i16 & i32 also work
Enum_u8 :: enum u8 {
A = 0,
B = 1 << 8 - 1,
};
Enum_u64 :: enum u64 {
A = 0,
B = 1 << 64 - 1,
};
Enum_i8 :: enum i8 {
A = 0,
B = -(1 << 7),
};
Enum_i64 :: enum i64 {
A = 0,
B = -(1 << 63),
};
map_u8: map[Enum_u8]u8;
map_u8[Enum_u8.A] = u8(Enum_u8.B);
assert(map_u8[Enum_u8.A] == u8(Enum_u8.B));
fmt.println(map_u8);
map_u64: map[Enum_u64]u64;
map_u64[Enum_u64.A] = u64(Enum_u64.B);
assert(map_u64[Enum_u64.A] == u64(Enum_u64.B));
fmt.println(map_u64);
map_i8: map[Enum_i8]i8;
map_i8[Enum_i8.A] = i8(Enum_i8.B);
assert(map_i8[Enum_i8.A] == i8(Enum_i8.B));
fmt.println(map_i8);
map_i64: map[Enum_i64]i64;
map_i64[Enum_i64.A] = i64(Enum_i64.B);
assert(map_i64[Enum_i64.A] == i64(Enum_i64.B));
fmt.println(map_i64);
demo_struct :: struct {
member: Enum_i64,
};
map_string: map[string]demo_struct;
map_string["Hellope!"] = demo_struct{Enum_i64.B};
assert(map_string["Hellope!"].member == Enum_i64.B);
assert("Hellope?" notin map_string);
fmt.println(map_string);
fmt.println("Hellope! in map_string:", "Hellope!" in map_string);
fmt.println("Hellope? in map_string:", "Hellope?" in map_string);
}
implicit_selector_expression :: proc() {
fmt.println("\n# implicit selector expression");
Foo :: enum {A, B, C};
f: Foo;
f = .A;
BAR :: bit_set[Foo]{.B, .C};
switch f {
case .A:
fmt.println("HERE");
case .B:
fmt.println("NEVER");
case .C:
fmt.println("FOREVER");
}
my_map := make(map[Foo]int);
defer delete(my_map);
my_map[.A] = 123;
my_map[Foo.B] = 345;
fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]);
}
explicit_procedure_overloading :: proc() {
fmt.println("# explicit procedure overloading");
fmt.println("\n# explicit procedure overloading");
add_ints :: proc(a, b: int) -> int {
x := a + b;
@@ -693,7 +822,7 @@ explicit_procedure_overloading :: proc() {
return x;
}
add :: proc[add_ints, add_floats, add_numbers];
add :: proc{add_ints, add_floats, add_numbers};
add(int(1), int(2));
add(f32(1), f32(2));
@@ -709,14 +838,14 @@ explicit_procedure_overloading :: proc() {
}
complete_switch :: proc() {
fmt.println("# complete_switch");
fmt.println("\n# complete_switch");
{ // enum
using Foo :: enum {
A,
B,
C,
D,
}
};
b := Foo.B;
f := Foo.A;
@@ -727,6 +856,8 @@ complete_switch :: proc() {
case D: fmt.println("D");
case: fmt.println("?");
}
_ = b;
}
{ // union
Foo :: union {int, bool};
@@ -739,13 +870,15 @@ complete_switch :: proc() {
}
}
cstring_example :: proc() {
fmt.println("\n# cstring_example");
W :: "Hellope";
X :: cstring(W);
Y :: string(X);
w := W;
_ = w;
x: cstring = X;
y: string = Y;
z := string(x);
@@ -754,7 +887,7 @@ cstring_example :: proc() {
fmt.println(len(W), len(X), len(Y));
// IMPORTANT NOTE for cstring variables
// len(cstring) is O(N)
// cast(cstring)string is O(N)
// cast(string)cstring is O(N)
}
deprecated_attribute :: proc() {
@@ -771,6 +904,8 @@ deprecated_attribute :: proc() {
}
bit_set_type :: proc() {
fmt.println("\n# bit_set_type");
{
using Day :: enum {
Sunday,
@@ -780,14 +915,13 @@ bit_set_type :: proc() {
Thursday,
Friday,
Saturday,
}
};
Days :: distinct bit_set[Day];
WEEKEND :: Days{Sunday, Saturday};
d: Days;
d = {Sunday, Monday};
x := Tuesday;
e := d | WEEKEND;
e |= {Monday};
fmt.println(d, e);
@@ -799,10 +933,11 @@ bit_set_type :: proc() {
}
X :: Saturday in WEEKEND; // Constant evaluation
fmt.println(X);
fmt.println("Cardinality:", card(e));
}
{
x: bit_set['A'..'Z'];
assert(size_of(x) == size_of(u32));
#assert(size_of(x) == size_of(u32));
y: bit_set[0..8; u16];
fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
@@ -810,14 +945,30 @@ bit_set_type :: proc() {
incl(&x, 'F');
assert('F' in x);
excl(&x, 'F');
assert(!('F' in x));
assert('F' notin x);
y |= {1, 4, 2};
assert(2 in y);
}
{
Letters :: bit_set['A'..'Z'];
a := Letters{'A', 'B'};
b := Letters{'A', 'B', 'C', 'D', 'F'};
c := Letters{'A', 'B'};
assert(a <= b); // 'a' is a subset of 'b'
assert(b >= a); // 'b' is a superset of 'a'
assert(a < b); // 'a' is a strict subset of 'b'
assert(b > a); // 'b' is a strict superset of 'a'
assert(!(a < c)); // 'a' is a not strict subset of 'c'
assert(!(c > a)); // 'c' is a not strict superset of 'a'
}
}
diverging_procedures :: proc() {
fmt.println("\n# diverging_procedures");
// Diverging procedures may never return
foo :: proc() -> ! {
fmt.println("I'm a diverging procedure");
@@ -826,20 +977,285 @@ diverging_procedures :: proc() {
foo();
}
deferred_procedure_associations :: proc() {
fmt.println("\n# deferred_procedure_associations");
@(deferred_out=closure)
open :: proc(s: string) -> bool {
fmt.println(s);
return true;
}
closure :: proc(ok: bool) {
fmt.println("Goodbye?", ok);
}
if open("Welcome") {
fmt.println("Something in the middle, mate.");
}
}
reflection :: proc() {
fmt.println("\n# reflection");
Foo :: struct {
x: int `tag1`,
y: string `json:"y_field"`,
z: bool, // no tag
};
id := typeid_of(Foo);
names := reflect.struct_field_names(id);
types := reflect.struct_field_types(id);
tags := reflect.struct_field_tags(id);
assert(len(names) == len(types) && len(names) == len(tags));
fmt.println("Foo :: struct {");
for tag, i in tags {
name, type := names[i], types[i];
if tag != "" {
fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
} else {
fmt.printf("\t%s: %T,\n", name, type);
}
}
fmt.println("}");
for tag, i in tags {
if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
fmt.printf("json: %s -> %s\n", names[i], val);
}
}
}
quaternions :: proc() {
fmt.println("\n# quaternions");
{ // Quaternion operations
q := 1 + 2i + 3j + 4k;
r := quaternion(5, 6, 7, 8);
t := q * r;
fmt.printf("(%v) * (%v) = %v\n", q, r, t);
v := q / r;
fmt.printf("(%v) / (%v) = %v\n", q, r, v);
u := q + r;
fmt.printf("(%v) + (%v) = %v\n", q, r, u);
s := q - r;
fmt.printf("(%v) - (%v) = %v\n", q, r, s);
}
{ // The quaternion types
q128: quaternion128; // 4xf32
q256: quaternion256; // 4xf64
q128 = quaternion(1, 0, 0, 0);
q256 = 1; // quaternion(1, 0, 0, 0);
}
{ // Built-in procedures
q := 1 + 2i + 3j + 4k;
fmt.println("q =", q);
fmt.println("real(q) =", real(q));
fmt.println("imag(q) =", imag(q));
fmt.println("jmag(q) =", jmag(q));
fmt.println("kmag(q) =", kmag(q));
fmt.println("conj(q) =", conj(q));
fmt.println("abs(q) =", abs(q));
}
{ // Conversion of a complex type to a quaternion type
c := 1 + 2i;
q := quaternion256(c);
fmt.println(c);
fmt.println(q);
}
{ // Memory layout of Quaternions
q := 1 + 2i + 3j + 4k;
a := transmute([4]f64)q;
fmt.println("Quaternion memory layout: xyzw/(ijkr)");
fmt.println(q); // 1.000+2.000i+3.000j+4.000k
fmt.println(a); // [2.000, 3.000, 4.000, 1.000]
}
}
inline_for_statement :: proc() {
fmt.println("\n#inline for statements");
// 'inline for' works the same as if the 'inline' prefix did not
// exist but these ranged loops are explicitly unrolled which can
// be very very useful for certain optimizations
fmt.println("Ranges");
inline for x, i in 1..<4 {
fmt.println(x, i);
}
fmt.println("Strings");
inline for r, i in "Hello, 世界" {
fmt.println(r, i);
}
fmt.println("Arrays");
inline for elem, idx in ([4]int{1, 4, 9, 16}) {
fmt.println(elem, idx);
}
Foo_Enum :: enum {
A = 1,
B,
C = 6,
D,
};
fmt.println("Enum types");
inline for elem, idx in Foo_Enum {
fmt.println(elem, idx);
}
}
where_clauses :: proc() {
fmt.println("\n#procedure 'where' clauses");
{ // Sanity checks
simple_sanity_check :: proc(x: [2]int)
where len(x) > 1,
type_of(x) == [2]int {
fmt.println(x);
}
}
{ // Parametric polymorphism checks
cross_2d :: proc(a, b: $T/[2]$E) -> E
where intrinsics.type_is_numeric(E) {
return a.x*b.y - a.y*b.x;
}
cross_3d :: proc(a, b: $T/[3]$E) -> T
where intrinsics.type_is_numeric(E) {
x := a.y*b.z - a.z*b.y;
y := a.z*b.x - a.x*b.z;
z := a.x*b.y - a.y*b.z;
return T{x, y, z};
}
a := [2]int{1, 2};
b := [2]int{5, -3};
fmt.println(cross_2d(a, b));
x := [3]f32{1, 4, 9};
y := [3]f32{-5, 0, 3};
fmt.println(cross_3d(x, y));
// Failure case
// i := [2]bool{true, false};
// j := [2]bool{false, true};
// fmt.println(cross_2d(i, j));
}
{ // Procedure groups usage
foo :: proc(x: [$N]int) -> bool
where N > 2 {
fmt.println(#procedure, "was called with the parameter", x);
return true;
}
bar :: proc(x: [$N]int) -> bool
where 0 < N,
N <= 2 {
fmt.println(#procedure, "was called with the parameter", x);
return false;
}
baz :: proc{foo, bar};
x := [3]int{1, 2, 3};
y := [2]int{4, 9};
ok_x := baz(x);
ok_y := baz(y);
assert(ok_x == true);
assert(ok_y == false);
}
{ // Record types
Foo :: struct(T: typeid, N: int)
where intrinsics.type_is_integer(T),
N > 2 {
x: [N]T,
y: [N-2]T,
};
T :: i32;
N :: 5;
f: Foo(T, N);
#assert(size_of(f) == (N+N-2)*size_of(T));
}
}
ranged_fields_for_array_compound_literals :: proc() {
fmt.println("\n#ranged fields for array compound literals");
{ // Normal Array Literal
foo := [?]int{1, 4, 9, 16};
fmt.println(foo);
}
{ // Indexed
foo := [?]int{
3 = 16,
1 = 4,
2 = 9,
0 = 1,
};
fmt.println(foo);
}
{ // Ranges
i := 2;
foo := [?]int {
0 = 123,
5..9 = 54,
10..<16 = i*3 + (i-1)*2,
};
#assert(len(foo) == 16);
fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
}
{ // Slice and Dynamic Array support
i := 2;
foo_slice := []int {
0 = 123,
5..9 = 54,
10..<16 = i*3 + (i-1)*2,
};
assert(len(foo_slice) == 16);
fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
foo_dynamic_array := [dynamic]int {
0 = 123,
5..9 = 54,
10..<16 = i*3 + (i-1)*2,
};
assert(len(foo_dynamic_array) == 16);
fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
}
}
main :: proc() {
when true {
general_stuff();
extra_general_stuff();
union_type();
parametric_polymorphism();
threading_example();
array_programming();
named_proc_return_parameters();
using_enum();
map_type();
implicit_selector_expression();
explicit_procedure_overloading();
complete_switch();
cstring_example();
deprecated_attribute();
bit_set_type();
diverging_procedures();
deferred_procedure_associations();
reflection();
quaternions();
inline_for_statement();
where_clauses();
ranged_fields_for_array_compound_literals();
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 75 KiB

+2 -2
View File
@@ -1,8 +1,8 @@
@echo off
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
set _NO_DEBUG_HEAP=1
View File
+12
View File
@@ -33,6 +33,7 @@ template <typename T> Array<T> array_make (gbAllocator const &a, isize
template <typename T> Array<T> array_make_from_ptr (T *data, isize count, isize capacity);
template <typename T> void array_free (Array<T> *array);
template <typename T> void array_add (Array<T> *array, T const &t);
template <typename T> void array_add_elems (Array<T> *array, T const *elems, isize elem_count);
template <typename T> T array_pop (Array<T> *array);
template <typename T> void array_clear (Array<T> *array);
template <typename T> void array_reserve (Array<T> *array, isize capacity);
@@ -157,6 +158,17 @@ void array_add(Array<T> *array, T const &t) {
array->count++;
}
template <typename T>
void array_add_elems(Array<T> *array, T const *elems, isize elem_count) {
GB_ASSERT(elem_count >= 0);
if (array->capacity < array->count+elem_count) {
array__grow(array, array->count+elem_count);
}
gb_memmove(array->data + array->count, elems, elem_count * gb_size_of(T));
array->count += elem_count;
}
template <typename T>
gb_inline T array_pop(Array<T> *array) {
GB_ASSERT(array->count > 0);
+20 -6
View File
@@ -160,6 +160,10 @@ void big_int_rem_eq(BigInt *dst, BigInt const *x) {
void big_int_normalize(BigInt *dst) {
if (dst->len == 1 && dst->d.word == 0) {
dst->len = 0;
return;
}
u64 const *words = big_int_ptr(dst);
i32 count_minus_one = -1;
@@ -172,6 +176,10 @@ void big_int_normalize(BigInt *dst) {
if (count_minus_one < 0) {
dst->neg = false;
if (words[0] == 0) {
dst->len = 0;
return;
}
}
dst->len = count_minus_one+1;
if (count_minus_one == 0) {
@@ -227,6 +235,7 @@ void big_int_init(BigInt *dst, BigInt const *src) {
big_int_alloc(dst, src->len, src->len);
u64 const *s = big_int_ptr(src);
gb_memmove(dst->d.words, s, gb_size_of(u64)*dst->len);
big_int_normalize(dst);
}
BigInt big_int_make(BigInt const *b, bool abs) {
@@ -258,10 +267,6 @@ BigInt big_int_make_i64(i64 x) {
void big_int_from_string(BigInt *dst, String const &s) {
#if 0
u64 u = u64_from_string(s);
big_int_from_u64(dst, u);
#else
u64 base = 10;
bool has_prefix = false;
if (s.len > 2 && s[0] == '0') {
@@ -299,7 +304,7 @@ void big_int_from_string(BigInt *dst, String const &s) {
big_int_mul_eq(dst, &b);
big_int_add_eq(dst, &val);
}
#endif
big_int_normalize(dst);
}
@@ -436,7 +441,6 @@ void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) {
u64 first_word = dst->d.word;
big_int_alloc(dst, 0, gb_max(x->len, y->len)+1);
GB_ASSERT(dst->len > 1);
dst->d.words[0] = first_word;
i32 i = 1;
@@ -547,6 +551,7 @@ void big_int_sub(BigInt *dst, BigInt const *x, BigInt const *y) {
BigInt neg_y = {};
big_int_neg(&neg_y, y);
big_int_add(dst, x, &neg_y);
big_int_normalize(dst);
return;
}
@@ -585,6 +590,7 @@ void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) {
if (dst->d.word > xd[0]) {
dst->len = 1;
dst->neg = x->neg;
big_int_normalize(dst);
return;
}
}
@@ -606,6 +612,7 @@ void big_int_shl(BigInt *dst, BigInt const *x, BigInt const *y) {
carry = 0;
}
}
big_int_normalize(dst);
}
void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) {
@@ -662,6 +669,7 @@ void big_int_shr(BigInt *dst, BigInt const *x, BigInt const *y) {
carry = v << (64ull - remaining_shift_len);
}
big_int_normalize(dst);
}
void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
@@ -690,6 +698,7 @@ void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) {
big_int_add(&tmp, &shifted, &carry_shifted);
big_int_add(dst, &tmp, &result);
}
big_int_normalize(dst);
}
@@ -1139,11 +1148,13 @@ void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
big_int__and_not_abs(dst, &y1, &x1);
dst->neg = false;
big_int_normalize(dst);
return;
}
big_int__and_not_abs(dst, x, y);
dst->neg = false;
big_int_normalize(dst);
return;
}
@@ -1157,6 +1168,7 @@ void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
big_int_or(&z1, &x1, &y1);
big_int_add(dst, &z1, &BIG_INT_ONE);
dst->neg = true;
big_int_normalize(dst);
return;
}
@@ -1166,6 +1178,7 @@ void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y) {
big_int_sub_eq(&y1, &BIG_INT_ONE);
big_int_and(dst, &x1, &y1);
dst->neg = false;
big_int_normalize(dst);
return;
}
@@ -1177,6 +1190,7 @@ void big_int__xor_abs(BigInt *dst, BigInt const *x, BigInt const *y) {
if (x->len == 1 && y->len == 1) {
dst->len = 1;
dst->d.word = xd[0] ^ yd[0];
big_int_normalize(dst);
return;
}
+73 -14
View File
@@ -2,7 +2,7 @@ enum TargetOsKind {
TargetOs_Invalid,
TargetOs_windows,
TargetOs_osx,
TargetOs_darwin,
TargetOs_linux,
TargetOs_essence,
@@ -30,7 +30,7 @@ enum TargetEndianKind {
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
str_lit("osx"),
str_lit("darwin"),
str_lit("linux"),
str_lit("essence"),
};
@@ -55,9 +55,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
String const ODIN_VERSION = str_lit("0.9.0");
String cross_compile_target = str_lit("");
String cross_compile_lib_dir = str_lit("");
String const ODIN_VERSION = str_lit("0.11.0");
@@ -66,6 +64,20 @@ struct TargetMetrics {
TargetArchKind arch;
isize word_size;
isize max_align;
String target_triplet;
};
enum QueryDataSetKind {
QueryDataSet_Invalid,
QueryDataSet_GlobalDefinitions,
QueryDataSet_GoToDefinitions,
};
struct QueryDataSetSettings {
QueryDataSetKind kind;
bool ok;
bool compact;
};
@@ -80,6 +92,8 @@ struct BuildContext {
String ODIN_ROOT; // Odin ROOT
bool ODIN_DEBUG; // Odin in debug mode
TargetEndianKind endian_kind;
// In bytes
i64 word_size; // Size of a pointer, must be >= 4
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
@@ -90,22 +104,31 @@ struct BuildContext {
String out_filepath;
String resource_filepath;
String pdb_filepath;
bool has_resource;
String opt_flags;
String llc_flags;
String target_triplet;
String link_flags;
bool is_dll;
bool generate_docs;
i32 optimization_level;
bool show_timings;
bool keep_temp_files;
bool ignore_unknown_attributes;
bool no_bounds_check;
bool no_output_files;
bool no_crt;
bool use_lld;
bool vet;
bool cross_compiling;
QueryDataSetSettings query_data_set_settings;
gbAffinity affinity;
isize thread_count;
Map<ExactValue> defined_values; // Key:
};
@@ -113,18 +136,19 @@ struct BuildContext {
gb_global BuildContext build_context = {0};
gb_global TargetMetrics target_windows_386 = {
TargetOs_windows,
TargetArch_386,
4,
8,
str_lit("i686-pc-windows"),
};
gb_global TargetMetrics target_windows_amd64 = {
TargetOs_windows,
TargetArch_amd64,
8,
16,
str_lit("x86_64-pc-windows-gnu"),
};
gb_global TargetMetrics target_linux_386 = {
@@ -132,23 +156,47 @@ gb_global TargetMetrics target_linux_386 = {
TargetArch_386,
4,
8,
str_lit("i686-pc-linux-gnu"),
};
gb_global TargetMetrics target_linux_amd64 = {
TargetOs_linux,
TargetArch_amd64,
8,
16,
str_lit("x86_64-pc-linux-gnu"),
};
gb_global TargetMetrics target_osx_amd64 = {
TargetOs_osx,
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
TargetArch_amd64,
8,
16,
str_lit("x86_64-apple-darwin"),
};
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
8,
16,
str_lit("x86_64-pc-none-elf"),
};
struct NamedTargetMetrics {
String name;
TargetMetrics *metrics;
};
gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("essence_amd64"), &target_essence_amd64 },
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
{ str_lit("linux_386"), &target_linux_386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
{ str_lit("windows_386"), &target_windows_386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
};
NamedTargetMetrics *selected_target_metrics;
TargetOsKind get_target_os_from_string(String str) {
for (isize i = 0; i < TargetOs_COUNT; i++) {
@@ -436,6 +484,13 @@ String path_to_fullpath(gbAllocator a, String s) {
text[len] = 0;
result = string16_to_string(a, make_string16(text, len));
result = string_trim_whitespace(result);
// Replace Windows style separators
for (isize i = 0; i < result.len; i++) {
if (result[i] == '\\') {
result[i] = '/';
}
}
}
return result;
@@ -464,6 +519,7 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
gb_memmove(str+i, path.text, path.len); i += path.len;
str[i] = 0;
String res = make_string(str, i);
res = string_trim_whitespace(res);
return path_to_fullpath(a, res);
@@ -492,7 +548,7 @@ String get_fullpath_core(gbAllocator a, String path) {
void init_build_context(void) {
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
gb_affinity_init(&bc->affinity);
@@ -510,7 +566,7 @@ void init_build_context(void) {
#if defined(GB_SYSTEM_WINDOWS)
metrics = target_windows_amd64;
#elif defined(GB_SYSTEM_OSX)
metrics = target_osx_amd64;
metrics = target_darwin_amd64;
#else
metrics = target_linux_amd64;
#endif
@@ -524,8 +580,9 @@ void init_build_context(void) {
#endif
#endif
if (cross_compile_target.len) {
bc->ODIN_OS = cross_compile_target;
if (cross_target) {
metrics = *cross_target;
bc->cross_compiling = true;
}
GB_ASSERT(metrics.os != TargetOs_Invalid);
@@ -538,10 +595,12 @@ void init_build_context(void) {
bc->ODIN_OS = target_os_names[metrics.os];
bc->ODIN_ARCH = target_arch_names[metrics.arch];
bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics.arch]];
bc->endian_kind = target_endians[metrics.arch];
bc->word_size = metrics.word_size;
bc->max_align = metrics.max_align;
bc->link_flags = str_lit(" ");
bc->opt_flags = str_lit(" ");
bc->target_triplet = metrics.target_triplet;
gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64);
@@ -559,7 +618,7 @@ void init_build_context(void) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x64 ");
break;
case TargetOs_osx:
case TargetOs_darwin:
break;
case TargetOs_linux:
bc->link_flags = str_lit("-arch x86-64 ");
@@ -572,7 +631,7 @@ void init_build_context(void) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
break;
case TargetOs_osx:
case TargetOs_darwin:
gb_printf_err("Unsupported architecture\n");
gb_exit(1);
break;
+298 -114
View File
@@ -40,6 +40,23 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri
return nullptr;
}
if (operand->mode == Addressing_Type) {
if (e->type != nullptr && is_type_typeid(e->type)) {
add_type_info_type(ctx, operand->type);
add_type_and_value(ctx->info, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type));
return e->type;
} else {
gbString t = type_to_string(operand->type);
defer (gb_string_free(t));
error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string));
if (e->type == nullptr) {
error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a type\n", LIT(e->token.string));
}
e->type = operand->type;
return nullptr;
}
}
if (e->type == nullptr) {
@@ -99,12 +116,13 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar
// an extra allocation
auto operands = array_make<Operand>(ctx->allocator, 0, 2*lhs_count);
defer (array_free(&operands));
check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true);
check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
isize rhs_count = operands.count;
for_array(i, operands) {
if (operands[i].mode == Addressing_Invalid) {
rhs_count--;
// TODO(bill): Should I ignore invalid parameters?
// rhs_count--;
}
}
@@ -231,7 +249,7 @@ isize total_attribute_count(DeclInfo *decl) {
}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) {
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
DeclInfo *decl = decl_info_of_entity(e);
@@ -239,9 +257,8 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def)
check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr);
}
bool is_distinct = is_type_distinct(type_expr);
Ast *te = remove_type_alias_clutter(type_expr);
bool is_distinct = is_type_distinct(init_expr);
Ast *te = remove_type_alias_clutter(init_expr);
e->type = t_invalid;
String name = e->token.string;
Type *named = alloc_type_named(name, nullptr, e);
@@ -257,7 +274,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def)
named->Named.base = base_type(bt);
if (is_distinct && is_type_typeid(e->type)) {
error(type_expr, "'distinct' cannot be applied to 'typeid'");
error(init_expr, "'distinct' cannot be applied to 'typeid'");
is_distinct = false;
}
if (!is_distinct) {
@@ -266,6 +283,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def)
e->TypeName.is_type_alias = true;
}
if (decl->type_expr != nullptr) {
Type *t = check_type(ctx, decl->type_expr);
if (t != nullptr && !is_type_typeid(t)) {
Operand operand = {};
operand.mode = Addressing_Type;
operand.type = e->type;
operand.expr = init_expr;
check_assignment(ctx, &operand, t, str_lit("constant declaration"));
}
}
// using decl
if (decl->is_using) {
// NOTE(bill): Must be an enum declaration
@@ -330,7 +360,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
if (type_expr) {
Type *t = check_type(ctx, type_expr);
if (!is_type_constant_type(t)) {
if (!is_type_constant_type(t) && !is_type_proc(t)) {
gbString str = type_to_string(t);
error(type_expr, "Invalid constant type '%s'", str);
gb_string_free(str);
@@ -354,15 +384,14 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
switch (operand.mode) {
case Addressing_Type: {
if (e->type != nullptr && !is_type_typeid(e->type)) {
check_assignment(ctx, &operand, e->type, str_lit("constant declaration"));
}
e->kind = Entity_TypeName;
e->type = nullptr;
DeclInfo *d = ctx->decl;
if (d->type_expr != nullptr) {
error(e->token, "A type declaration cannot have an type parameter");
}
d->type_expr = d->init_expr;
check_type_decl(ctx, e, d->type_expr, named_type);
check_type_decl(ctx, e, ctx->decl->init_expr, named_type);
return;
}
@@ -384,6 +413,25 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
}
if (entity != nullptr) {
if (e->type != nullptr) {
Operand x = {};
x.type = entity->type;
x.mode = Addressing_Variable;
if (!check_is_assignable_to(ctx, &x, e->type)) {
gbString expr_str = expr_to_string(init);
gbString op_type_str = type_to_string(entity->type);
gbString type_str = type_to_string(e->type);
error(e->token,
"Cannot assign '%s' of type '%s' to '%s'",
expr_str,
op_type_str,
type_str);
gb_string_free(type_str);
gb_string_free(op_type_str);
gb_string_free(expr_str);
}
}
// NOTE(bill): Override aliased entity
switch (entity->kind) {
@@ -391,8 +439,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
case Entity_Procedure:
case Entity_LibraryName:
case Entity_ImportName:
override_entity_in_scope(e, entity);
return;
{
override_entity_in_scope(e, entity);
DeclInfo *decl = decl_info_of_entity(e);
if (decl != nullptr) {
if (decl->attributes.count > 0) {
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
}
}
return;
}
}
}
}
@@ -414,6 +472,48 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
}
typedef bool TypeCheckSig(Type *t);
bool sig_compare(TypeCheckSig *a, Type *x, Type *y) {
return (a(x) && a(y));
}
bool sig_compare(TypeCheckSig *a, TypeCheckSig *b, Type *x, Type *y) {
if (a == b) {
return sig_compare(a, x, y);
}
return ((a(x) && b(y)) || (b(x) && a(y)));
}
bool signature_parameter_similar_enough(Type *x, Type *y) {
if (sig_compare(is_type_pointer, x, y)) {
return true;
}
if (sig_compare(is_type_integer, x, y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(x);
i64 sy = type_size_of(y);
if (sx == sy) return true;
}
if (sig_compare(is_type_integer, is_type_boolean, x, y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(x);
i64 sy = type_size_of(y);
if (sx == sy) return true;
}
if (sig_compare(is_type_cstring, is_type_u8_ptr, x, y)) {
return true;
}
if (sig_compare(is_type_uintptr, is_type_rawptr, x, y)) {
return true;
}
return are_types_identical(x, y);
}
bool are_signatures_similar_enough(Type *a_, Type *b_) {
GB_ASSERT(a_->kind == Type_Proc);
@@ -430,36 +530,14 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) {
for (isize i = 0; i < a->param_count; i++) {
Type *x = core_type(a->params->Tuple.variables[i]->type);
Type *y = core_type(b->params->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
if (!signature_parameter_similar_enough(x, y)) {
return false;
}
if (is_type_integer(x) && is_type_integer(y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(x);
i64 sy = type_size_of(y);
if (sx == sy) continue;
}
if (!are_types_identical(x, y)) return false;
}
for (isize i = 0; i < a->result_count; i++) {
Type *x = base_type(a->results->Tuple.variables[i]->type);
Type *y = base_type(b->results->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (is_type_integer(x) && is_type_integer(y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(x);
i64 sy = type_size_of(y);
if (sx == sy) continue;
}
if (!are_types_identical(x, y)) {
if (!signature_parameter_similar_enough(x, y)) {
return false;
}
}
@@ -545,26 +623,59 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
check_open_scope(ctx, pl->type);
defer (check_close_scope(ctx));
Type *decl_type = nullptr;
if (d->type_expr != nullptr) {
decl_type = check_type(ctx, d->type_expr);
if (!is_type_proc(decl_type)) {
gbString str = type_to_string(decl_type);
error(d->type_expr, "Expected a procedure type, got '%s'", str);
gb_string_free(str);
}
}
auto tmp_ctx = *ctx;
tmp_ctx.allow_polymorphic_types = true;
if (decl_type != nullptr) {
tmp_ctx.type_hint = decl_type;
}
check_procedure_type(&tmp_ctx, proc_type, pl->type);
if (decl_type != nullptr) {
Operand x = {};
x.type = e->type;
x.mode = Addressing_Variable;
if (!check_is_assignable_to(ctx, &x, decl_type)) {
gbString expr_str = expr_to_string(d->proc_lit);
gbString op_type_str = type_to_string(e->type);
gbString type_str = type_to_string(decl_type);
error(e->token,
"Cannot assign '%s' of type '%s' to '%s'",
expr_str,
op_type_str,
type_str);
gb_string_free(type_str);
gb_string_free(op_type_str);
gb_string_free(expr_str);
}
}
TypeProc *pt = &proc_type->Proc;
bool is_foreign = e->Procedure.is_foreign;
bool is_export = e->Procedure.is_export;
bool is_require_results = (pl->tags & ProcTag_require_results) != 0;
AttributeContext ac = make_attribute_context(e->Procedure.link_prefix);
if (d != nullptr) {
check_decl_attributes(ctx, d->attributes, proc_decl_attribute, &ac);
}
e->Procedure.is_export = ac.is_export;
e->deprecated_message = ac.deprecated_message;
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
bool is_foreign = e->Procedure.is_foreign;
bool is_export = e->Procedure.is_export;
if (e->pkg != nullptr && e->token.string == "main") {
if (pt->param_count != 0 ||
pt->result_count != 0) {
@@ -623,16 +734,21 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
if (pt->result_count == 0 && is_require_results) {
error(pl->type, "'#require_results' is not needed on a procedure with no results");
if (pt->result_count == 0 && ac.require_results) {
error(pl->type, "'require_results' is not needed on a procedure with no results");
} else {
pt->require_results = is_require_results;
pt->require_results = ac.require_results;
}
if (ac.link_name.len > 0) {
e->Procedure.link_name = ac.link_name;
}
if (ac.deferred_procedure.entity != nullptr) {
e->Procedure.deferred_procedure = ac.deferred_procedure;
array_add(&ctx->checker->procs_with_deferred_to_check, e);
}
if (is_foreign) {
String name = e->token.string;
if (e->Procedure.link_name.len > 0) {
@@ -695,7 +811,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
}
void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Variable);
@@ -709,12 +825,19 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex
ac.init_expr_list_count = init_expr != nullptr ? 1 : 0;
DeclInfo *decl = decl_info_of_entity(e);
GB_ASSERT(decl == ctx->decl);
if (decl != nullptr) {
check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac);
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
e->Variable.thread_local_model = ac.thread_local_model;
e->Variable.is_export = ac.is_export;
if (ac.is_static) {
e->flags |= EntityFlag_Static;
} else {
e->flags &= ~EntityFlag_Static;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
String context_name = str_lit("variable declaration");
@@ -834,7 +957,6 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
ptr_set_destroy(&entity_set);
for_array(j, pge->entities) {
Entity *p = pge->entities[j];
if (p->type == t_invalid) {
@@ -856,28 +978,44 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
continue;
}
begin_error_block();
defer (end_error_block());
ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type);
switch (kind) {
bool both_have_where_clauses = false;
if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) {
GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit);
GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit);
auto pl = &p->decl_info->proc_lit->ProcLit;
auto ql = &q->decl_info->proc_lit->ProcLit;
// Allow collisions if the procedures both have 'where' clauses and are both polymorphic
bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true);
bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true);
both_have_where_clauses = pw && qw;
}
if (!both_have_where_clauses) switch (kind) {
case ProcOverload_Identical:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
// case ProcOverload_CallingConvention:
// error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
// error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
// is_invalid = true;
// break;
case ProcOverload_ParamVariadic:
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name));
error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_ResultCount:
case ProcOverload_ResultTypes:
error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in this scope", LIT(name));
error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name));
is_invalid = true;
break;
case ProcOverload_Polymorphic:
#if 0
error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name));
error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in the procedure group '%.*s' which is not allowed", LIT(name), LIT(proc_group_name));
is_invalid = true;
#endif
break;
@@ -889,7 +1027,7 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d)
}
if (is_invalid) {
gb_printf_err("\tprevious procedure at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column);
error_line("\tprevious procedure at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column);
q->type = t_invalid;
}
}
@@ -946,13 +1084,13 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
switch (e->kind) {
case Entity_Variable:
check_var_decl(&c, e, d->type_expr, d->init_expr);
check_global_variable_decl(&c, e, d->type_expr, d->init_expr);
break;
case Entity_Constant:
check_const_decl(&c, e, d->type_expr, d->init_expr, named_type);
break;
case Entity_TypeName: {
check_type_decl(&c, e, d->type_expr, named_type);
check_type_decl(&c, e, d->init_expr, named_type);
break;
}
case Entity_Procedure:
@@ -969,6 +1107,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_
}
struct ProcUsingVar {
Entity *e;
Entity *uvar;
};
void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) {
if (body == nullptr) {
@@ -993,76 +1136,117 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
ctx->curr_proc_decl = decl;
ctx->curr_proc_sig = type;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for_array(i, params->variables) {
Entity *e = params->variables[i];
if (e->kind != Entity_Variable) {
continue;
}
if (!(e->flags & EntityFlag_Using)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
bool is_value = (e->flags & EntityFlag_Value) != 0;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
if (scope == nullptr) {
scope = scope_of_node(t->Struct.node);
}
GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
if (is_value) uvar->flags |= EntityFlag_Value;
ast_node(bs, BlockStmt, body);
Array<ProcUsingVar> using_entities = {};
using_entities.allocator = heap_allocator();
defer (array_free(&using_entities));
{
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for_array(i, params->variables) {
Entity *e = params->variables[i];
if (e->kind != Entity_Variable) {
continue;
}
if (!(e->flags & EntityFlag_Using)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
if (scope == nullptr) {
scope = scope_of_node(t->Struct.node);
}
GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
uvar->Variable.is_immutable = is_immutable;
if (is_value) uvar->flags |= EntityFlag_Value;
ProcUsingVar puv = {e, uvar};
array_add(&using_entities, puv);
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string));
break;
}
}
} else {
error(e->token, "'using' can only be applied to variables of type struct");
break;
}
} else {
error(e->token, "'using' can only be applied to variables of type struct");
break;
}
}
}
ast_node(bs, BlockStmt, body);
// check_open_scope(ctx, body);
check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) {
error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string));
} else {
// NOTE(bill): Anonymous procedure (lambda)
error(bs->close, "Missing return statement at the end of the procedure");
for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
break;
}
}
bool where_clause_ok = evaluate_where_clauses(ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
if (!where_clause_ok) {
// NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed
return;
}
check_open_scope(ctx, body);
{
for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
// NOTE(bill): Don't err here
}
check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) {
error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string));
} else {
// NOTE(bill): Anonymous procedure (lambda)
error(bs->close, "Missing return statement at the end of the procedure");
}
}
}
}
// check_close_scope(ctx);
check_close_scope(ctx);
check_scope_usage(ctx->checker, ctx->scope);
#if 1
if (decl->parent != nullptr) {
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
for_array(i, decl->deps.entries) {
Entity *e = decl->deps.entries[i].ptr;
ptr_set_add(&decl->parent->deps, e);
}
for_array(i, decl->type_info_deps.entries) {
Type *t = decl->type_info_deps.entries[i].ptr;
ptr_set_add(&decl->parent->type_info_deps, t);
Scope *ps = decl->parent->scope;
if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) {
return;
} else {
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
// But only at the procedure level
for_array(i, decl->deps.entries) {
Entity *e = decl->deps.entries[i].ptr;
ptr_set_add(&decl->parent->deps, e);
}
for_array(i, decl->type_info_deps.entries) {
Type *t = decl->type_info_deps.entries[i].ptr;
ptr_set_add(&decl->parent->type_info_deps, t);
}
}
}
#endif
}
+2093 -435
View File
File diff suppressed because it is too large Load Diff
+322 -96
View File
@@ -132,6 +132,10 @@ bool check_is_terminating(Ast *node) {
}
case_end;
case_ast_node(rs, InlineRangeStmt, node);
return false;
case_end;
case_ast_node(rs, RangeStmt, node);
return false;
case_end;
@@ -172,6 +176,9 @@ bool check_is_terminating(Ast *node) {
return false;
}
Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs) {
if (rhs->mode == Addressing_Invalid) {
return nullptr;
@@ -249,39 +256,19 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
case Addressing_Invalid:
return nullptr;
case Addressing_Variable: {
case Addressing_Variable:
if (is_type_bit_field_value(lhs->type)) {
Type *lt = base_type(lhs->type);
i64 lhs_bits = lt->BitFieldValue.bits;
if (rhs->mode == Addressing_Constant) {
ExactValue v = exact_value_to_integer(rhs->value);
if (v.kind == ExactValue_Integer) {
BigInt i = v.value_integer;
if (!i.neg) {
u64 imax_ = ~cast(u64)0ull;
if (lhs_bits < 64) {
imax_ = (1ull << cast(u64)lhs_bits) - 1ull;
}
BigInt imax = big_int_make_u64(imax_);
if (big_int_cmp(&i, &imax) > 0) {
return rhs->type;
}
}
}
} else if (is_type_integer(rhs->type)) {
// TODO(bill): Any other checks?
return rhs->type;
Type *res = check_assignment_bit_field(ctx, rhs, lhs->type);
if (res == nullptr) {
gbString lhs_expr = expr_to_string(lhs->expr);
gbString rhs_expr = expr_to_string(rhs->expr);
error(rhs->expr, "Cannot assign '%s' to bit field '%s'", rhs_expr, lhs_expr);
gb_string_free(rhs_expr);
gb_string_free(lhs_expr);
}
gbString lhs_expr = expr_to_string(lhs->expr);
gbString rhs_expr = expr_to_string(rhs->expr);
error(rhs->expr, "Cannot assign '%s' to bit field '%s'", rhs_expr, lhs_expr);
gb_string_free(rhs_expr);
gb_string_free(lhs_expr);
return nullptr;
return res;
}
break;
}
case Addressing_MapIndex: {
Ast *ln = unparen_expr(lhs->expr);
@@ -320,9 +307,13 @@ Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, Operand *rhs)
}
}
Entity *e = entity_of_ident(lhs->expr);
gbString str = expr_to_string(lhs->expr);
if (lhs->mode == Addressing_Immutable) {
error(lhs->expr, "Cannot assign to an immutable: '%s'", str);
} else if (e != nullptr && e->flags & EntityFlag_Param) {
error(lhs->expr, "Cannot assign to '%s' which is a procedure parameter", str);
} else {
error(lhs->expr, "Cannot assign to '%s'", str);
}
@@ -396,7 +387,7 @@ void check_when_stmt(CheckerContext *ctx, AstWhenStmt *ws, u32 flags) {
}
}
void check_label(CheckerContext *ctx, Ast *label) {
void check_label(CheckerContext *ctx, Ast *label, Ast *parent) {
if (label == nullptr) {
return;
}
@@ -428,7 +419,7 @@ void check_label(CheckerContext *ctx, Ast *label) {
}
}
Entity *e = alloc_entity_label(ctx->scope, l->name->Ident.token, t_invalid, label);
Entity *e = alloc_entity_label(ctx->scope, l->name->Ident.token, t_invalid, label, parent);
add_entity(ctx->checker, ctx->scope, l->name, e);
e->parent_proc_decl = ctx->curr_proc_decl;
@@ -474,10 +465,11 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
Entity *found = scope_insert(ctx->scope, decl);
Entity *found = scope_insert_with_name(ctx->scope, name, decl);
if (found != nullptr) {
gbString expr_str = expr_to_string(expr);
error(us->token,
@@ -504,8 +496,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
for_array(i, found->elements.entries) {
Entity *f = found->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type);
uvar->using_expr = expr;
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr);
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
gbString expr_str = expr_to_string(expr);
@@ -599,6 +590,162 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *seen, Oper
multi_map_insert(seen, key, tap);
}
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, InlineRangeStmt, node);
check_open_scope(ctx, node);
Type *val0 = nullptr;
Type *val1 = nullptr;
Entity *entities[2] = {};
isize entity_count = 0;
Ast *expr = unparen_expr(irs->expr);
ExactValue inline_for_depth = exact_value_i64(0);
if (is_ast_range(expr)) {
ast_node(ie, BinaryExpr, expr);
Operand x = {};
Operand y = {};
bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth);
if (!ok) {
goto skip_expr;
}
val0 = x.type;
val1 = t_int;
} else {
Operand operand = {Addressing_Invalid};
check_expr_or_type(ctx, &operand, irs->expr);
if (operand.mode == Addressing_Type) {
if (!is_type_enum(operand.type)) {
gbString t = type_to_string(operand.type);
error(operand.expr, "Cannot iterate over the type '%s'", t);
gb_string_free(t);
goto skip_expr;
} else {
val0 = operand.type;
val1 = t_int;
add_type_info_type(ctx, operand.type);
Type *bt = base_type(operand.type);
inline_for_depth = exact_value_i64(bt->Enum.fields.count);
goto skip_expr;
}
} else if (operand.mode != Addressing_Invalid) {
Type *t = base_type(operand.type);
switch (t->kind) {
case Type_Basic:
if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
val0 = t_rune;
val1 = t_int;
inline_for_depth = exact_value_i64(operand.value.value_string.len);
}
break;
case Type_Array:
val0 = t->Array.elem;
val1 = t_int;
inline_for_depth = exact_value_i64(t->Array.count);
break;
}
}
if (val0 == nullptr) {
gbString s = expr_to_string(operand.expr);
gbString t = type_to_string(operand.type);
error(operand.expr, "Cannot iterate over '%s' of type '%s' in an 'inline for' statement", s, t);
gb_string_free(t);
gb_string_free(s);
} else if (operand.mode != Addressing_Constant) {
error(operand.expr, "An 'inline for' expression must be known at compile time");
}
}
skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
Ast * lhs[2] = {irs->val0, irs->val1};
Type *rhs[2] = {val0, val1};
for (isize i = 0; i < 2; i++) {
if (lhs[i] == nullptr) {
continue;
}
Ast * name = lhs[i];
Type *type = rhs[i];
Entity *entity = nullptr;
if (name->kind == Ast_Ident) {
Token token = name->Ident.token;
String str = token.string;
Entity *found = nullptr;
if (!is_blank_ident(str)) {
found = scope_lookup_current(ctx->scope, str);
}
if (found == nullptr) {
bool is_immutable = true;
entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved);
entity->flags |= EntityFlag_Value;
add_entity_definition(&ctx->checker->info, name, entity);
} else {
TokenPos pos = found->token.pos;
error(token,
"Redeclaration of '%.*s' in this scope\n"
"\tat %.*s(%td:%td)",
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
} else {
error(name, "A variable declaration must be an identifier");
}
if (entity == nullptr) {
entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name));
}
entities[entity_count++] = entity;
if (type == nullptr) {
entity->type = t_invalid;
entity->flags |= EntityFlag_Used;
}
}
for (isize i = 0; i < entity_count; i++) {
add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]);
}
// NOTE(bill): Minimize the amount of nesting of an 'inline for'
i64 prev_inline_for_depth = ctx->inline_for_depth;
defer (ctx->inline_for_depth = prev_inline_for_depth);
{
i64 v = exact_value_to_i64(inline_for_depth);
if (v <= 0) {
// Do nothing
} else {
ctx->inline_for_depth = gb_max(ctx->inline_for_depth, 1) * v;
}
if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) {
if (prev_inline_for_depth > 0) {
error(node, "Nested 'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
} else {
error(node, "'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
}
error_line("\tUse a normal 'for' loop instead by removing the 'inline' prefix\n");
ctx->inline_for_depth = MAX_INLINE_FOR_DEPTH;
}
}
check_stmt(ctx, irs->body, mod_flags);
check_close_scope(ctx);
}
void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(ss, SwitchStmt, node);
@@ -608,7 +755,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_open_scope(ctx, node);
defer (check_close_scope(ctx));
check_label(ctx, ss->label); // TODO(bill): What should the label's "scope" be?
check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be?
if (ss->init != nullptr) {
check_stmt(ctx, ss->init, 0);
@@ -683,17 +830,17 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Ast *expr = unparen_expr(cc->list[j]);
if (is_ast_range(expr)) {
ast_node(ie, BinaryExpr, expr);
ast_node(be, BinaryExpr, expr);
Operand lhs = {};
Operand rhs = {};
check_expr(ctx, &lhs, ie->left);
check_expr_with_type_hint(ctx, &lhs, be->left, x.type);
if (x.mode == Addressing_Invalid) {
continue;
}
if (lhs.mode == Addressing_Invalid) {
continue;
}
check_expr(ctx, &rhs, ie->right);
check_expr_with_type_hint(ctx, &rhs, be->right, x.type);
if (rhs.mode == Addressing_Invalid) {
continue;
}
@@ -705,6 +852,13 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
case Token_Ellipsis: upper_op = Token_GtEq; break;
case Token_RangeHalf: upper_op = Token_Gt; break;
default: GB_PANIC("Invalid range operator"); break;
}
Operand a = lhs;
Operand b = rhs;
@@ -713,7 +867,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
check_comparison(ctx, &b, &x, Token_GtEq);
check_comparison(ctx, &b, &x, upper_op);
if (b.mode == Addressing_Invalid) {
continue;
}
@@ -726,13 +880,22 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
add_constant_switch_case(ctx, &seen, lhs);
add_constant_switch_case(ctx, &seen, rhs);
if (upper_op == Token_GtEq) {
add_constant_switch_case(ctx, &seen, rhs);
}
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
add_package_dependency(ctx, "runtime", "string_le");
add_package_dependency(ctx, "runtime", "string_lt");
}
} else {
Operand y = {};
if (is_type_typeid(x.type)) {
check_expr_or_type(ctx, &y, expr, x.type);
} else {
check_expr(ctx, &y, expr);
check_expr_with_type_hint(ctx, &y, expr, x.type);
}
if (x.mode == Addressing_Invalid ||
@@ -799,6 +962,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
if (unhandled.count > 0) {
begin_error_block();
defer (begin_error_block());
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: ");
} else {
@@ -807,11 +973,11 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
for_array(i, unhandled) {
Entity *f = unhandled[i];
if (i > 0) {
gb_printf_err(", ");
error_line(", ");
}
gb_printf_err("%.*s", LIT(f->token.string));
error_line("%.*s", LIT(f->token.string));
}
gb_printf_err("\n");
error_line("\n");
}
}
}
@@ -842,7 +1008,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_open_scope(ctx, node);
defer (check_close_scope(ctx));
check_label(ctx, ss->label); // TODO(bill): What should the label's "scope" be?
check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be?
if (ss->tag->kind != Ast_AssignStmt) {
error(ss->tag, "Expected an 'in' assignment for this type switch statement");
@@ -964,7 +1130,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
GB_PANIC("Unknown type to type switch statement");
}
if (ptr_set_exists(&seen, y.type)) {
if (type_ptr_set_exists(&seen, y.type)) {
TokenPos pos = cc->token.pos;
gbString expr_str = expr_to_string(y.expr);
error(y.expr,
@@ -980,7 +1146,6 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
if (is_ptr &&
!is_type_any(type_deref(x.type)) &&
cc->list.count == 1 &&
case_type != nullptr) {
case_type = alloc_type_pointer(case_type);
@@ -1008,7 +1173,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
if (complete) {
Type *ut = base_type(x.type);
Type *ut = base_type(type_deref(x.type));
GB_ASSERT(is_type_union(ut));
auto variants = ut->Union.variants;
@@ -1017,7 +1182,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
for_array(i, variants) {
Type *t = variants[i];
if (!ptr_set_exists(&seen, t)) {
if (!type_ptr_set_exists(&seen, t)) {
array_add(&unhandled, t);
}
}
@@ -1031,13 +1196,13 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
for_array(i, unhandled) {
Type *t = unhandled[i];
if (i > 0) {
gb_printf_err(", ");
error_line(", ");
}
gbString s = type_to_string(t);
gb_printf_err("%s", s);
error_line("%s", s);
gb_string_free(s);
}
gb_printf_err("\n");
error_line("\n");
}
}
}
@@ -1126,7 +1291,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
isize rhs_count = rhs_operands.count;
for_array(i, rhs_operands) {
if (rhs_operands[i].mode == Addressing_Invalid) {
rhs_count--;
// TODO(bill): Should I ignore invalid parameters?
// rhs_count--;
}
}
@@ -1162,7 +1328,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
be->right = as->rhs[0];
check_expr(ctx, &lhs, as->lhs[0]);
check_binary_expr(ctx, &rhs, &binary_expr, true);
check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true);
if (rhs.mode == Addressing_Invalid) {
return;
}
@@ -1176,6 +1342,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
case_ast_node(bs, BlockStmt, node);
check_open_scope(ctx, node);
check_label(ctx, bs->label, node);
check_stmt_list(ctx, bs->stmts, flags);
check_close_scope(ctx);
case_end;
@@ -1183,6 +1351,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
case_ast_node(is, IfStmt, node);
check_open_scope(ctx, node);
check_label(ctx, is->label, node);
if (is->init != nullptr) {
check_stmt(ctx, is->init, 0);
}
@@ -1243,7 +1413,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
defer (array_free(&operands));
check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true);
check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true, false);
if (result_count == 0 && rs->results.count > 0) {
error(rs->results[0], "No return values expected");
@@ -1264,7 +1434,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(ctx, node);
check_label(ctx, fs->label); // TODO(bill): What should the label's "scope" be?
check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be?
if (fs->init != nullptr) {
check_stmt(ctx, fs->init, 0);
@@ -1279,8 +1449,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (fs->post != nullptr) {
check_stmt(ctx, fs->post, 0);
if (fs->post->kind != Ast_AssignStmt &&
fs->post->kind != Ast_IncDecStmt) {
if (fs->post->kind != Ast_AssignStmt) {
error(fs->post, "'for' statement post statement must be a simple statement");
}
}
@@ -1289,11 +1458,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
check_close_scope(ctx);
case_end;
case_ast_node(rs, RangeStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(ctx, node);
check_label(ctx, rs->label);
check_label(ctx, rs->label, node);
Type *val0 = nullptr;
Type *val1 = nullptr;
@@ -1311,29 +1481,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
check_expr(ctx, &x, ie->left);
if (x.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
check_expr(ctx, &y, ie->right);
if (y.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
convert_to_typed(ctx, &x, y.type);
if (x.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
convert_to_typed(ctx, &y, x.type);
if (y.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
convert_to_typed(ctx, &x, default_type(y.type));
if (x.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
convert_to_typed(ctx, &y, default_type(x.type));
if (y.mode == Addressing_Invalid) {
goto skip_expr;
goto skip_expr_range_stmt;
}
if (!are_types_identical(x.type, y.type)) {
@@ -1347,13 +1517,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
gb_string_free(yt);
gb_string_free(xt);
}
goto skip_expr;
goto skip_expr_range_stmt;
}
Type *type = x.type;
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
goto skip_expr;
goto skip_expr_range_stmt;
}
if (x.mode == Addressing_Constant &&
@@ -1365,25 +1535,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
TokenKind op = Token_Lt;
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_Ellipsis: op = Token_LtEq; break;
case Token_RangeHalf: op = Token_Lt; break;
default: error(ie->op, "Invalid range operator"); break;
}
bool ok = compare_exact_values(op, a, b);
if (!ok) {
// TODO(bill): Better error message
error(ie->op, "Invalid interval range");
goto skip_expr;
goto skip_expr_range_stmt;
}
}
if (x.mode != Addressing_Constant) {
x.value = empty_exact_value;
}
if (y.mode != Addressing_Constant) {
y.value = empty_exact_value;
}
add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value);
add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value);
val0 = type;
@@ -1397,12 +1560,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
gbString t = type_to_string(operand.type);
error(operand.expr, "Cannot iterate over the type '%s'", t);
gb_string_free(t);
goto skip_expr;
goto skip_expr_range_stmt;
} else {
val0 = operand.type;
val1 = t_int;
add_type_info_type(ctx, operand.type);
goto skip_expr;
goto skip_expr_range_stmt;
}
} else if (operand.mode != Addressing_Invalid) {
bool is_ptr = is_type_pointer(operand.type);
@@ -1447,16 +1610,17 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
Ast *lhs[2] = {rs->val0, rs->val1};
Type * rhs[2] = {val0, val1};
skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
Ast * lhs[2] = {rs->val0, rs->val1};
Type *rhs[2] = {val0, val1};
for (isize i = 0; i < 2; i++) {
if (lhs[i] == nullptr) {
continue;
}
Ast *name = lhs[i];
Type * type = rhs[i];
Ast * name = lhs[i];
Type *type = rhs[i];
Entity *entity = nullptr;
if (name->kind == Ast_Ident) {
@@ -1468,8 +1632,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
found = scope_lookup_current(ctx->scope, str);
}
if (found == nullptr) {
bool is_immutable = true;
bool is_immutable = false;
entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved);
entity->flags |= EntityFlag_Value;
add_entity_definition(&ctx->checker->info, name, entity);
} else {
TokenPos pos = found->token.pos;
@@ -1496,7 +1661,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
for (isize i = 0; i < entity_count; i++) {
add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]);
Entity *e = entities[i];
DeclInfo *d = decl_info_of_entity(e);
GB_ASSERT(d == nullptr);
add_entity(ctx->checker, ctx->scope, e->identifier, e);
d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl);
add_entity_and_decl_info(ctx, e->identifier, e, d);
}
check_stmt(ctx, rs->body, new_flags);
@@ -1504,6 +1674,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
check_close_scope(ctx);
case_end;
case_ast_node(irs, InlineRangeStmt, node);
check_inline_range_stmt(ctx, node, mod_flags);
case_end;
case_ast_node(ss, SwitchStmt, node);
check_switch_stmt(ctx, node, mod_flags);
case_end;
@@ -1528,18 +1702,20 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
Token token = bs->token;
switch (token.kind) {
case Token_break:
if ((flags & Stmt_BreakAllowed) == 0) {
if ((flags & Stmt_BreakAllowed) == 0 && bs->label == nullptr) {
error(token, "'break' only allowed in loops or 'switch' statements");
}
break;
case Token_continue:
if ((flags & Stmt_ContinueAllowed) == 0) {
if ((flags & Stmt_ContinueAllowed) == 0 && bs->label == nullptr) {
error(token, "'continue' only allowed in loops");
}
break;
case Token_fallthrough:
if ((flags & Stmt_FallthroughAllowed) == 0) {
error(token, "'fallthrough' statement in illegal position, expected at the end of a 'case' block");
} else if (bs->label != nullptr) {
error(token, "'fallthrough' cannot have a label");
}
break;
default:
@@ -1565,6 +1741,24 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(ident, "'%.*s' is not a label", LIT(name));
return;
}
Ast *parent = e->Label.parent;
GB_ASSERT(parent != nullptr);
switch (parent->kind) {
case Ast_BlockStmt:
case Ast_IfStmt:
case Ast_SwitchStmt:
if (token.kind != Token_break) {
error(bs->label, "Label '%.*s' can only be used with 'break'", LIT(e->token.string));
}
break;
case Ast_RangeStmt:
case Ast_ForStmt:
if ((token.kind != Token_break) && (token.kind != Token_continue)) {
error(bs->label, "Label '%.*s' can only be used with 'break' and 'continue'", LIT(e->token.string));
}
break;
}
}
case_end;
@@ -1683,11 +1877,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(vd->type, "Invalid use of a polymorphic type '%s' in variable declaration", str);
gb_string_free(str);
init_type = t_invalid;
} else if (is_type_empty_union(init_type)) {
gbString str = type_to_string(init_type);
error(vd->type, "An empty union '%s' cannot be instantiated in variable declaration", str);
gb_string_free(str);
init_type = t_invalid;
}
}
@@ -1716,6 +1905,16 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
}
e->flags &= ~EntityFlag_Static;
if (ac.is_static) {
String name = e->token.string;
if (name == "_") {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
}
}
}
check_arity_match(ctx, vd);
@@ -1723,6 +1922,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
if (e->Variable.is_foreign) {
if (vd->values.count > 0) {
error(e->token, "A foreign variable declaration cannot have a default value");
@@ -1755,6 +1955,17 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else {
map_set(fp, key, e);
}
} else if (e->flags & EntityFlag_Static) {
if (vd->values.count > 0) {
if (entity_count != vd->values.count) {
error(e->token, "A static variable declaration with a default value must be constant");
} else {
Ast *value = vd->values[i];
if (value->tav.mode != Addressing_Constant) {
error(e->token, "A static variable declaration with a default value must be constant");
}
}
}
}
add_entity(ctx->checker, ctx->scope, e->identifier, e);
}
@@ -1766,7 +1977,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
// TODO(bill): Should a 'continue' happen here?
}
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
for (isize entity_index = 0; entity_index < 1; entity_index++) {
Entity *e = entities[entity_index];
if (e == nullptr) {
continue;
@@ -1785,7 +1996,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type);
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert(ctx->scope, uvar);
if (prev != nullptr) {
@@ -1794,6 +2005,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
}
add_entity_use(ctx, nullptr, e);
} else {
// NOTE(bill): skip the rest to remove extra errors
error(token, "'using' can only be applied to variables of type struct or raw_union");
@@ -1801,8 +2014,21 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
}
}
}
} else {
// constant value declarations
// constant value declaration
// NOTE(bill): Check `_` declarations
for_array(i, vd->names) {
Ast *name = vd->names[i];
if (is_blank_ident(name)) {
Entity *e = name->Ident.entity;
DeclInfo *d = decl_info_of_entity(e);
if (d != nullptr) {
check_entity_decl(ctx, e, d, nullptr);
}
}
}
}
case_end;
}
+586 -81
View File
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More