Compare commits

...

962 Commits

Author SHA1 Message Date
gingerBill aa796a1032 v0.11.1 2019-11-12 21:20:40 +00:00
gingerBill 5bf71ba75c big_int addition overflow rules 2019-11-11 22:47:25 +00:00
gingerBill 967981aacd Add -show-more-timings 2019-11-10 21:49:02 +00:00
gingerBill 33d05a07de Fix big_int_or and big_int_xor 2019-11-10 20:06:04 +00:00
gingerBill 8da8301b09 Fix big subtraction for very large numbers on edges of overflow. 2019-11-10 19:55:26 +00:00
gingerBill 536cceeef9 Add intrinsics.type_is_unsigned 2019-11-10 18:51:21 +00:00
gingerBill 098684a6fe Add 128-bit random procedures to package math/rand 2019-11-10 18:47:16 +00:00
gingerBill 2ef7bfc06e Remove debug message 2019-11-09 10:45:03 +00:00
gingerBill 7bfdb4f9f4 Fix Compile time assert on non-constant boolean parameters #467 2019-11-09 10:44:42 +00:00
gingerBill 0a35b13411 Fix inline for bug for #468 2019-11-09 10:24:27 +00:00
gingerBill dacfc9de15 Fix //+build for ! e.g. //+build !windows amd64, linux !amd64 2019-11-05 20:09:09 +00:00
gingerBill 689aa4d734 Fix //+build system 2019-11-05 20:02:11 +00:00
gingerBill 8a46b493fd Fix Slice passed incorrectly in LLVM IR to procedure called via procedure pointer #465 2019-11-05 19:40:08 +00:00
gingerBill 86abdc0603 Merge branch 'master' of https://github.com/odin-lang/Odin 2019-11-05 19:37:30 +00:00
gingerBill a634444f99 Fix "Polymorphic parameter declared in return type doesn't compile #464" by giving a conversion error (code wasn't handling polymorphic result types as intended) 2019-11-05 19:37:19 +00:00
vassvik 04a25b11ad Fix incorrect math.linalg.cross3 indices. 2019-11-04 17:39:26 +01:00
gingerBill 40546fbde2 Use runtime.mem_copy in package me 2019-11-03 19:59:41 +00:00
gingerBill c1176c2bcb Fix typeid comparison bug; Add extra messages for pointer address errors 2019-11-03 19:49:21 +00:00
gingerBill 57853fe1b1 Add SOA Struct Layout (experimental) to demo.odin 2019-11-03 12:52:13 +00:00
gingerBill 013b3b9fb3 Fix for -vet 2019-11-03 11:42:00 +00:00
gingerBill dc0f04e53b Fix typo in fmt 2019-11-03 00:50:47 +00:00
gingerBill 40606d9272 Fix fmt printing of anonymous SOA structs 2019-11-03 00:47:27 +00:00
gingerBill ebf7926fa4 SOA support of Structures and Arrays; Runtime information for SOA structs; fmt printing support for SOA structs 2019-11-03 00:32:22 +00:00
gingerBill dfb3101ecf SOA Struct support intrinsics.soa_struct 2019-11-02 21:08:51 +00:00
gingerBill e3d3a81617 multivalued procedure calls allows in for in to allow a pseudo-iterator; @thread_local for variables in procedure 2019-11-02 16:36:46 +00:00
gingerBill dbdbbcd60f Fix range in statement bug caused by incorrectly assigned addressing mode #461 2019-11-02 10:57:42 +00:00
gingerBill f9aaff99c6 Fix linalg.mul; add linalg.Matrix1xN 2019-11-02 10:30:11 +00:00
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
gingerBill f96fbc94c8 v0.9.0 Release 2018-11-22 10:25:01 +00:00
gingerBill bb62bed981 Remove assert that should have never been there 2018-11-22 09:53:08 +00:00
gingerBill bc6b8c5332 Remove dead code 2018-11-22 09:52:25 +00:00
gingerBill 6ab6447791 Fix is_type_polymorphic infinite recursion bug 2018-11-22 09:41:08 +00:00
gingerBill f61c4715c1 Allow opaque to be polymorphic 2018-11-17 10:08:06 +00:00
gingerBill 3061bc8478 Fix error with polymorphic structs #279 2018-11-17 10:05:35 +00:00
gingerBill d035d48c8e Fix issue #280 2018-11-17 09:45:52 +00:00
gingerBill b55b1ffe14 opaque keyword and type 2018-11-11 17:08:30 +00:00
gingerBill 620d5d34f7 Fix issue with complication of -debug that is caused sometimes due to lambda procedures. 2018-11-11 11:44:55 +00:00
gingerBill f9654b6c36 Fix package usage with when on import #278 2018-11-07 16:11:14 +00:00
gingerBill 6659ceb551 Allow comparisons of cstring; Add resize 2018-10-31 10:04:30 +00:00
gingerBill 5aa591d884 Fix debug info issue 2018-10-29 22:16:43 +00:00
gingerBill efe91b1f91 Disable debug info for bit fields 2018-10-29 18:10:44 +00:00
gingerBill 7c99884afb Fix CompositeType for zero length arrays 2018-10-29 12:52:41 +00:00
gingerBill dfd7a194ed Fix big int shifts of 0 2018-10-28 09:32:59 +00:00
gingerBill 2ddb27869b Built-in procedure #defined 2018-10-27 18:44:28 +01:00
gingerBill 5c608b01ba Place optimization level flag in condition again; -memcpyopt -die only in non-debug builds 2018-10-24 10:19:01 +01:00
gingerBill 2bd85e764e Merge pull request #260 from lachsinc/master
[WIP] Provide llvm with more debug info (for Visual Studio debugger support)
2018-10-24 09:40:05 +01:00
gingerBill 822e4894f2 Minor logic change for reserved package names 2018-10-20 21:19:40 +01:00
gingerBill ce2e23849e Fix context initialization 2018-10-20 16:34:56 +01:00
gingerBill 099995e7dd Add basics for context-based Logger 2018-10-20 13:02:30 +01:00
gingerBill 72f4186b21 Fix atomic.odin 2018-10-20 12:55:48 +01:00
gingerBill 3742d9e7e9 Move atomic intrinsics to the new built-in package intrinsics 2018-10-20 10:44:02 +01:00
gingerBill 4ac1218bf8 sync atomics "wrapper" procedures 2018-10-17 21:43:05 +01:00
gingerBill b171cc41e6 __atomic_* "intrinsics" using LLVM instructions 2018-10-17 21:16:01 +01:00
gingerBill efc3a9e69d Merge pull request #274 from hazeycode/fix/245
Use name of source file as output_name
2018-10-17 15:29:56 +01:00
gingerBill 307c58d908 Fix compilation error #272 2018-10-17 15:27:36 +01:00
Chris Heyes ae02e9c34a Use name of source file as output_name 2018-10-16 23:56:19 +01:00
Chris Heyes 139fa55c27 Merge pull request #1 from hazeycode/fix/272
Fix syntax error in core/os/os_linux.odin
2018-10-16 19:30:02 +01:00
Chris Heyes 562bb6e4c4 Fix syntax error in core/os/os_linux.odin 2018-10-15 21:24:42 +01:00
gingerBill ef2931d4a5 Remove AstTypeType 2018-10-13 14:07:00 +01:00
gingerBill 2d4aa2be6d Remove type as being a keyword 2018-10-13 14:04:34 +01:00
gingerBill 42b42db675 Add unimplemented and unreachable procedures; make os.exit a diverging procedure 2018-10-13 13:19:52 +01:00
gingerBill 73e9dbbf8c switch on typeid with type cases 2018-10-13 11:07:56 +01:00
gingerBill 0971a59493 Update runtime printing code 2018-10-11 18:19:29 +01:00
gingerBill 627c91124a Merge pull request #271 from Breush/267-bugfix-linux-heap-alloc-zero
Fixed heap alloc not allowing empty structs on Linux
2018-10-09 21:49:14 +01:00
Alexis Breust 4eba717281 Fixed heap alloc not allowing empty structs on Linux - Fixes #267 2018-10-09 08:34:48 +02:00
gingerBill 9623e5e032 Merge pull request #270 from Breush/263-bugfix-leading-zeros-count
Replaced __builtin_clz by long long version
2018-10-08 09:39:43 +01:00
Alexis Breust 805cc48f03 Replaced __builtin_clz by long long version - Fixes #268 2018-10-08 08:23:50 +02:00
lachsinc d894fb3708 Cleanup comments. 2018-10-06 09:09:12 +10:00
lachsinc b6ca913cff Cleanup hack as all types appear to be handled!.. 2018-10-06 08:58:39 +10:00
lachsinc 39db428603 Add complex debug info. 2018-10-05 17:10:58 +10:00
lachsinc 992502f03b Add debug info for proc ptrs. 2018-10-05 16:49:48 +10:00
lachsinc edc3a9392a Cleanup. 2018-10-05 15:03:13 +10:00
lachsinc f881ebd007 Cleanup unused AllProcs. 2018-10-05 14:51:08 +10:00
lachsinc 11ea03d2e8 Tuple debug info (untested). Generated locals now flow through debug info. 2018-10-05 14:33:39 +10:00
lachsinc 99b4d59f44 Add arg # for proc param locals. 2018-10-05 12:57:06 +10:00
lachsinc dfeefc5179 Fix dgb.declare using different location to it's associated instructions. 2018-10-05 12:46:53 +10:00
lachsinc ab46406f4d Fix debug info for unnamed aggregate types. 2018-10-05 12:27:36 +10:00
lachsinc 48ad147818 Cleanup; Move enums/globals di inside CompileUnit. Minor comment cleanup. 2018-10-04 14:09:17 +10:00
lachsinc 79d49f1955 Lexical block debug info. 2018-10-04 13:43:52 +10:00
lachsinc 1ccc8700e4 bit_set / bit_field debug info. 2018-10-02 10:06:48 +10:00
lachsinc f38d70a235 Cleanup. 2018-10-01 01:21:15 +10:00
lachsinc b37b7a0f72 Cleanup. 2018-10-01 01:10:22 +10:00
lachsinc f8d7f42208 Minor cleanup. 2018-10-01 00:17:42 +10:00
lachsinc db0756a119 Stepping working. Cleanup. 2018-10-01 00:02:41 +10:00
gingerBill 1a4e25f141 Prefix runtime procedures 2018-09-29 21:09:19 +01:00
lachsinc 79ade6ac7b Add various debug location stuff. 2018-09-30 04:47:21 +10:00
lachsinc ecce1d9974 Add debug location stack. 2018-09-30 04:24:24 +10:00
gingerBill 834308d8ce Fix using import override "bug" 2018-09-29 13:07:46 +01:00
lachsinc 9e73189d63 Tagged union debug info. Aggregate type fixes (unions inside structs etc.). 2018-09-27 21:50:57 +10:00
lachsinc 11bddf270c Cleanup debug info 'name' stuff. 2018-09-27 18:34:59 +10:00
lachsinc 0818a272e2 Cleanup hardcoded bytes to bits 2018-09-27 17:55:37 +10:00
lachsinc 9750b1162a Cleanup. 2018-09-27 01:34:15 +10:00
lachsinc 3106aaaa3d Fix pointers to all things debug info. Cleanup param order. Make scope/file optional for relevent types. 2018-09-27 00:02:35 +10:00
lachsinc d31d4c9bd6 (Basic) Map debug info support. Minor slice fix. 2018-09-26 07:51:16 +10:00
lachsinc 6993777d36 Slices. Fix dynamic array data ptr size. 2018-09-26 06:04:33 +10:00
lachsinc 54c044ee09 Add support for any. Fix rawptr debug type. 2018-09-26 05:43:58 +10:00
lachsinc 2e5cecf9e6 Cleanup dynamic array/string bloat. 2018-09-26 04:01:16 +10:00
lachsinc 7acb49eefb Cleanup comments/todos. 2018-09-26 02:17:05 +10:00
lachsinc 0f6c1f3482 Add debug info for globals. Misc debug info cleanup. 2018-09-26 02:01:03 +10:00
lachsinc 1ee0fe7457 Add DebugInfoArray as separate debug info type. Minor cleanup of various debug infos. 2018-09-25 21:24:15 +10:00
gingerBill 1a18481d8b Fix context assignment with selector expressions 2018-09-24 12:04:26 +01:00
gingerBill 28c61c0f5d Merge branch 'master' of https://github.com/odin-lang/Odin 2018-09-24 10:22:33 +01:00
gingerBill 597fb452b1 Minor fixes 2018-09-24 10:22:22 +01:00
lachsinc 5961a63880 Expose dynamic array data/len/cap debug info. Minor cleanup of odin string debug info. 2018-09-19 13:16:56 +10:00
lachsinc cce5e595e5 String debug info. Minor cleanup of derived / composite debug info output. 2018-09-19 01:52:08 +10:00
lachsinc e7d72f6848 Static array debug info. Temporary dynamic array debug info (pointer to data, no len/cap info provided yet). 2018-09-18 23:21:44 +10:00
lachsinc 7dcad45e0d Add proper procedure type support (return types and param proc signature) 2018-09-18 21:28:28 +10:00
lachsinc 3772ea6ae1 Enum debug info support. 2018-09-18 20:12:36 +10:00
lachsinc 2cc2eb1ec0 Fix stepping/jumping between procedures/files. 2018-09-18 18:10:03 +10:00
lachsinc 8a789e33b0 Remove llc/opt hack. XX.bc now contains useful debug info thanks to removal of optimization flags in debug builds. 2018-09-18 14:17:43 +10:00
lachsinc 2f86f8f8e0 Provide llvm ir with more debug info (for Visual Studio debugger support). 2018-09-18 10:50:56 +10:00
gingerBill 02f9a27f46 Merge pull request #264 from lunaticLipid/master
Remove reference to the runtime package within itself
2018-09-16 22:26:34 +01:00
Lipid 6cb605a025 Remove reference to the runtime package within itself 2018-09-16 08:19:33 +02:00
gingerBill 9f3e42e4ef Fix delete_key #262 2018-09-15 11:21:02 +01:00
gingerBill 71d987bd2e Fix runtime proc names; Change calling convention of context parameter 2018-09-15 10:46:46 +01:00
gingerBill 637899467c Merge branch 'master' of https://github.com/odin-lang/Odin 2018-09-15 10:14:40 +01:00
gingerBill 5bdb424c6b context.allocator = a; Remove __ from runtime procs; improve division for complex numbers 2018-09-15 10:14:24 +01:00
gingerBill c62cfddb9c Merge pull request #263 from hasenj/master
fix macos thread_count value
2018-09-14 17:48:34 +01:00
Hasen Judy 14a4c28f8f fix macos thread_count value 2018-09-15 01:46:50 +09:00
gingerBill f1e1814ff9 Syntactic sugar for anonymous enum within a bit set 2018-09-11 12:10:32 +01:00
gingerBill b468cf141b Fix are_types_identical for bit_set 2018-09-11 11:14:46 +01:00
gingerBill 787ea1feba Fix polymorphic constant parameters for procedures 2018-09-11 11:09:42 +01:00
gingerBill 91477e9e69 Allow for optional ok for return 2018-09-11 10:55:30 +01:00
gingerBill cfd0dfd2bf Remove assertion from constant parameter 2018-09-10 21:56:16 +01:00
gingerBill 46b1868185 Constant polymorphic names 2018-09-10 14:21:19 +01:00
gingerBill 4c4de1d6c4 Fix cloning of auto_cast Ast 2018-09-10 08:50:20 +01:00
gingerBill c8b30de771 Update compiler flags for build.bat 2018-09-09 15:06:04 +01:00
gingerBill 4f3837f0e6 Procedure inlining on call site 2018-09-09 14:46:28 +01:00
gingerBill 76848e8807 Disallow inline for recursive procedures 2018-09-09 13:58:23 +01:00
gingerBill 12902821d6 Make diverging procedure types different from ones without a return type 2018-09-09 13:48:33 +01:00
gingerBill f5549f6bde Make panic a diverging procedure 2018-09-08 12:17:16 +01:00
gingerBill 3825eab989 Diverging procedures proc() -> ! (no return) 2018-09-08 12:16:03 +01:00
gingerBill 3cd6ae311d Parametric polymorphic union type 2018-09-08 12:02:25 +01:00
gingerBill 26cfc0257d Fix array_ordered_remove typo 2018-09-08 10:44:18 +01:00
Ginger Bill 1d31eabb6e Fix minor parsing issue for polymorphic identifiers 2018-09-04 13:47:24 +01:00
Joshua Mark Manton 8cd2797b2e Fixed core library bugs after recent changes. (#257)
* Fix `delete_map` calling `delete_dynamic_array` instead of `delete_slice for its hashes.

* Removed print statements from `__dynamic_map_rehash`
2018-09-02 22:18:32 +02:00
gingerBill 11f5236434 Add $T: typeid/[]$E; Deprecate T: type/[]$E
`type` as a keyword will soon be removed in favour of polymorphic names (identifiers) in procedures
2018-09-02 16:33:54 +01:00
gingerBill 220485a2d2 typeid as keyword (ready to implement polymorphic name parameters) 2018-09-02 15:56:36 +01:00
gingerBill eb274cf316 Remove test code 2018-08-30 19:33:16 +01:00
gingerBill aa542980ce Change memory layout of map to be 3 words smaller 2018-08-30 19:14:10 +01:00
gingerBill e0240c186f Rename buffer entity 2018-08-30 12:53:07 +01:00
gingerBill ae58502a21 Make free_all built-in 2018-08-30 12:21:16 +01:00
gingerBill 6a3697279c Place assertf and printf to package fmt 2018-08-30 12:10:16 +01:00
gingerBill c19ec5d65d Fix delete for dynamic array and map 2018-08-30 12:00:51 +01:00
gingerBill 15dca449c9 Add assertf and panicf 2018-08-30 11:46:57 +01:00
gingerBill dda985f49d Add extra nil check for assert and panic 2018-08-30 11:16:06 +01:00
gingerBill 12256beeb2 Prevent other parameters being the default value 2018-08-30 11:12:57 +01:00
gingerBill 0858ae2024 Add utf8_to_ucs2 for package win32 so that the wide procedures can used by default 2018-08-30 10:59:46 +01:00
gingerBill 6c18864291 Add default_assertion_failure_proc to the minimum dependency build 2018-08-29 21:15:11 +01:00
gingerBill ae57284912 Add Assertion_Failure_Proc to context 2018-08-29 21:10:13 +01:00
gingerBill 001837e6bb Temporary allocator for context 2018-08-29 19:55:55 +01:00
gingerBill 28523f17e2 Add default allocator to allocation related procedures e.g. alloc, free, delete, make 2018-08-28 20:14:56 +01:00
gingerBill ae2af8315e Allow for default parameters that are non-constant entities, but not any non-constant expression 2018-08-28 20:03:27 +01:00
gingerBill adbb3bb75f Add -lld flag for using "bin\lld-link.exe" on Windows 2018-08-28 19:28:34 +01:00
gingerBill 6181c4edb3 Update 2018-08-28 19:26:05 +01:00
gingerBill 830c194da5 Allow enums for array lengths 2018-08-26 18:23:17 +01:00
gingerBill 1830c1e57c Allow bitwise operation on enums 2018-08-26 18:05:59 +01:00
gingerBill e5735af6d6 Disable for in over cstring 2018-08-26 15:10:23 +01:00
gingerBill a6b0ae71b2 Remove assert 2018-08-26 10:56:33 +01:00
gingerBill 3365baee8f runtime.Typeid_Bit_Field layout to store more information into the typeid 2018-08-25 12:11:48 +01:00
gingerBill cc88dd0b71 Allow for variadic min max procs
Request #252
2018-08-25 11:12:52 +01:00
gingerBill f050bfe872 Fix comparisons with union 2018-08-25 10:39:19 +01:00
gingerBill ab71acc3a5 Disable abs for arrays #254 2018-08-25 10:27:44 +01:00
gingerBill 0a85d1af6b Improve error messages for using on fields 2018-08-24 22:28:00 +01:00
gingerBill 68adadb01a Allow using in structs on arrays with count <= 4 2018-08-24 22:12:30 +01:00
gingerBill d56f458d11 Fix file scope #assert 2018-08-24 17:42:13 +01:00
gingerBill a65eadee63 Fix for in enum iteration 2018-08-22 18:56:41 +01:00
gingerBill 16dfae62bc Allow casting to and from rawptr and cstring #249 2018-08-22 15:19:04 +01:00
gingerBill fe680a8b1f Fix default return values #250 2018-08-22 15:17:29 +01:00
gingerBill 54fe9f3eb1 Improve min dep for min/max/abs/clamp 2018-08-21 21:43:38 +01:00
gingerBill cbc6c2666b Improve proc group scoring algorithm 2018-08-21 14:11:18 +01:00
gingerBill a4d0ac1802 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-08-20 19:20:44 +01:00
gingerBill 0dc29a7208 Implement suggestions from #247 2018-08-20 19:20:28 +01:00
gingerBill a9321bc73f Update README.md 2018-08-20 10:27:48 +01:00
gingerBill e3f0ab7c3d Parallelize parser on *nix 2018-08-19 10:58:57 +01:00
gingerBill 5643ea1ba2 Fix typo 2018-08-19 10:56:23 +01:00
gingerBill 3b6523fbd9 Fix gbMutex for *nix 2018-08-19 10:34:31 +01:00
gingerBill ffc4f01470 All enums in array indices 2018-08-17 19:38:15 +01:00
gingerBill e326f41d16 Fix demo 2018-08-17 15:26:29 +01:00
gingerBill 1d0ac72e4a Disable non-comparison operations for enum (use bit_set for flags) 2018-08-17 15:24:44 +01:00
gingerBill b216e44870 Add underlying type for bit_set 2018-08-17 15:11:41 +01:00
gingerBill 7d39b26cf4 Minor refactor 2018-08-16 20:52:46 +01:00
gingerBill 884d5fed9f bit_set['A'..'Z'], bit_set[0..8] 2018-08-16 15:16:57 +01:00
gingerBill ec84188597 Fix typos in parser 2018-08-16 10:30:44 +01:00
gingerBill 85ac95f81b Constant evaluation for in expression for bit_sets 2018-08-16 00:07:26 +01:00
gingerBill 042550cf87 Fix default value bug 2018-08-15 19:36:32 +01:00
gingerBill b3ebff715a Fix defer ir bug 2018-08-15 15:44:41 +01:00
gingerBill 1ee60663bb Fix issue #244 with constant array comparisons 2018-08-14 19:43:36 +01:00
gingerBill 59da98d3f0 Improve type hinting for compound literals 2018-08-14 19:39:28 +01:00
gingerBill 2d41a42f61 Cleaning type hinting for assignments 2018-08-14 19:33:42 +01:00
gingerBill e1e4a916a5 Fix demo and improve type hinting 2018-08-14 19:29:31 +01:00
gingerBill 71f94bff76 Minor sanity features for bit_set 2018-08-14 19:22:48 +01:00
gingerBill c7d6467cfa Fix assigning issue for bit sets 2018-08-14 18:43:47 +01:00
gingerBill 79a3c0b36c Fix bit_set range 2018-08-14 18:35:14 +01:00
gingerBill 966249c10a bit_set constants 2018-08-14 18:32:34 +01:00
gingerBill acc010cba5 Add bit_set type 2018-08-14 17:07:56 +01:00
gingerBill 89f4e7a8db -no-crt flag for windows amd64 2018-08-13 01:22:14 +01:00
gingerBill 55f4eabecd Fix map addressing mode bug 2018-08-10 18:22:37 +01:00
gingerBill d0fc9aa069 Allow for '\"' 2018-08-10 17:48:29 +01:00
gingerBill 8be9b5082c Fix default make parameters for dynamic arrays 2018-08-09 18:15:49 +01:00
gingerBill 708907df31 auto_cast for named parameters 2018-08-09 17:59:18 +01:00
gingerBill 70586b1cf8 auto_cast prefix for procedure parameters 2018-08-09 17:58:11 +01:00
gingerBill 877a78d6ba Fix make error messages 2018-08-08 23:07:51 +01:00
gingerBill 3928614326 Merge pull request #241 from thebirk/fix-sh-main-not-found
Fixed 'sh: main: command not found' error on linux.
2018-08-08 22:41:42 +01:00
thebirk 5e5f5bfa8d Fixed 'sh: main: command not found' error on linux. 2018-08-08 17:48:17 +02:00
gingerBill 3a1a7b40f9 Add runtime messages for make for the len/cap parameters 2018-08-08 13:04:40 +01:00
gingerBill 835d7dcab2 make as a user-level procedure rather than a built-in procedure 2018-08-08 13:04:40 +01:00
gingerBill 28816dc491 Fix parenthesis warning on clang 2018-08-08 13:04:40 +01:00
gingerBill ccdc3438be Refactor handle_parameter_value code 2018-08-06 00:26:38 +01:00
gingerBill 60711dd355 Refactor default parameter values 2018-08-05 23:57:34 +01:00
gingerBill fad3947e26 Add *with_allocator procedures to mem 2018-08-05 23:40:19 +01:00
gingerBill d8e5b2d1a4 Fix cstring cast operation 2018-08-05 19:07:03 +01:00
gingerBill 2d26ad0226 Remove opengl package 2018-08-05 19:01:15 +01:00
gingerBill 45d3c6c0d3 Fix cstring to string conversion 2018-08-05 18:58:35 +01:00
gingerBill c6bffd7c35 Change build.bat to use release_mode=1 by default 2018-08-05 10:37:09 +01:00
gingerBill 462d81430c Fix map runtime issue regarding erasing a key 2018-08-05 10:31:20 +01:00
gingerBill d3cada5bd6 Change rules for how context and defer interact 2018-08-04 23:46:46 +01:00
gingerBill cdbf831a7a Replace context <- c {} with context = c;. context assignments are scope based 2018-08-04 23:14:55 +01:00
gingerBill 0718f14774 Reduce number of range and slice operators #239
Replace .. and ... with : and ..
2018-08-01 21:34:59 +01:00
gingerBill a6fe656f21 foreign import x {"foo.lib", "bar.lib"} 2018-07-29 20:56:09 +01:00
gingerBill dc5da7933a Add older demos 2018-07-29 11:36:24 +01:00
gingerBill 96fc9138d4 Do using Foo :: enum at the type_decl stage 2018-07-29 11:29:20 +01:00
gingerBill 6512a3e5f2 using Foo :: enum {A, B, C}; len(Foo) 2018-07-29 10:50:15 +01:00
gingerBill 49f2124df0 Support larger integer literals to work with the new BigInt system 2018-07-29 10:22:17 +01:00
gingerBill a11d6e696a expand_to_tuple for fixed arrays 2018-07-28 20:56:27 +01:00
gingerBill 1705ba8069 Fix typos 2018-07-28 19:44:00 +01:00
gingerBill 8d2c4a78a1 Merge pull request #238 from odin-lang/big-int
Big int
2018-07-28 18:39:15 +01:00
gingerBill 8504ff920b Correctly handle bitwise operations for negative BigInt 2018-07-28 18:36:45 +01:00
gingerBill e34a9e6185 Fix big_int_shr 2018-07-28 00:48:36 +01:00
gingerBill c3c7834246 BigInt support in the constant system 2018-07-28 00:41:31 +01:00
gingerBill 1ab40d8600 Merge pull request #237 from lunaticLipid/master
Add mat3_mul and generic transpose to math.odin
2018-07-17 08:25:21 +01:00
Lipid 92ce02dab0 Fix indent characters 2018-07-16 20:30:49 +02:00
Lipid 8abe9ef507 Add mat3_mul and generic transpose to math.odin 2018-07-16 20:27:29 +02:00
gingerBill d0e04bf569 Merge pull request #236 from hasenj/master
Fix build errors on osx
2018-07-14 19:39:11 +01:00
Hasen Judy b92599879a free -> delete in os_osx and os_linux 2018-07-13 11:25:46 -06:00
gingerBill 0e91298fd1 Rename free to delete for non pointer types 2018-07-08 11:03:56 +01:00
gingerBill e515220694 Improve array arithmetic inlining 2018-07-08 10:15:46 +01:00
gingerBill a55683d287 Remove allocator parameters in ir.cpp 2018-07-07 11:29:45 +01:00
gingerBill fa4e95105f Loop array arithmetic on large arrays 2018-07-07 11:13:20 +01:00
gingerBill 1e01085ef7 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-07-07 08:12:48 +01:00
gingerBill 04a1f869b5 Fix when statements within a foreign block 2018-07-07 08:11:31 +01:00
Morten Vassvik e04ba7530d Updated makefile to run demo package instead of demo.odin 2018-07-05 15:48:55 +02:00
Morten Vassvik ea055f1465 Surrounded explicit link paths (.a and .so) and the exe path for 'odin run' in quotes, so that it works in paths containing characters that must be escaped (like spaces) 2018-07-05 15:46:11 +02:00
Morten Vassvik 3b2c867817 Replaced CLOCK_PROCESS_CPUTIME_ID with CLOCK_MONOTONIC in calls to clock_gettime and clock_getres to make timings on calling external executables accurate instead of showing them taking negligible time on linux. 2018-07-04 21:04:48 +02:00
gingerBill 3de23eb0bf Merge pull request #233 from zangent/master
Make macOS builds work again
2018-07-02 08:57:22 +01:00
Zachary Pierson 5de3b07e2b Made os_osx.odin use the new-style runtime.args__ and added read_directory for macOS 2018-07-02 02:50:08 -05:00
gingerBill c0ca4d4635 Uncomment code 2018-07-01 18:31:31 +01:00
gingerBill efe4b71bae Fix build.sh 2018-07-01 17:17:31 +01:00
gingerBill bc37bd5429 Merge branch 'packages' of https://github.com/odin-lang/Odin into packages 2018-07-01 17:14:43 +01:00
gingerBill 5f20e04259 Fix on *nix 2018-07-01 17:14:22 +01:00
gingerBill 9bef5ec01a Fix anonymous procedures 2018-07-01 16:21:32 +01:00
gingerBill cdf873542b Add read_directory for linux 2018-06-21 08:39:52 +01:00
gingerBill 4742690dec Fix is_excluded_target_filename 2018-06-21 08:39:07 +01:00
gingerBill 3a16f1e854 Minor style change 2018-06-17 22:25:28 +01:00
gingerBill 877400dd12 Scope to use flags rathers than booleans 2018-06-17 22:22:30 +01:00
gingerBill a4e3201113 Minor cleanup for builtin scope/pkg 2018-06-17 22:07:27 +01:00
gingerBill a99cc2fd70 Clean up import lookup code 2018-06-17 21:50:40 +01:00
gingerBill 5fe4c33d0e Allow importation of core:builtin to get built-in entities 2018-06-17 21:46:37 +01:00
gingerBill 4d9d38cc28 Move TypeAndValue to Ast from Map 2018-06-17 16:35:22 +01:00
gingerBill 5b71ffd4f9 Rename clone_ast_node to clone_ast 2018-06-17 11:03:26 +01:00
gingerBill c2ca24a486 Big renaming: AstNode to Ast 2018-06-17 10:58:59 +01:00
gingerBill e5aff6fd6d Minimize AstNode size 2018-06-17 10:48:50 +01:00
gingerBill 3eb8aa8268 Modify CommentGroup parsing 2018-06-17 10:29:20 +01:00
gingerBill 6d1c32eb77 Add escape code for ESC \e 2018-06-15 23:13:26 +01:00
gingerBill ba776a3c9f Fix bitwise not for signed integers 2018-06-15 23:01:12 +01:00
gingerBill cd7e260f4e Fix cyclic polymorphic procedure usage and improve its error message 2018-06-15 22:49:06 +01:00
gingerBill ba67e474d3 Make source code compile with 32 bit (but not build 32 bit code) 2018-06-15 21:46:03 +01:00
gingerBill b92a8c513e Modify how build settings are handled 2018-06-15 21:38:22 +01:00
gingerBill 13572aeef0 Fix gb.h 2018-06-15 20:26:39 +01:00
gingerBill 5081ea1a0c Fix type aliasing comparison; Fix gb_utf8_decode 2018-06-15 19:59:35 +01:00
gingerBill e9e7ce2606 Allow .allocator for dynamic arrays; Add mem.Pool 2018-06-12 19:10:14 +01:00
gingerBill 915dcb0c28 Fix min dependency check 2018-06-11 22:57:40 +01:00
gingerBill 8236c6d4b7 Allow for base enum type with an enum declaration 2018-06-11 22:50:13 +01:00
gingerBill 555fe37ad8 Remove the need for a look ahead 2018-06-11 18:06:58 +01:00
gingerBill 881f667558 Change how context <- is parsed to remove the need for a look-ahead 2018-06-11 18:02:04 +01:00
gingerBill 0a99595efe Remove using in stuff 2018-06-11 17:34:57 +01:00
gingerBill 268491b224 Use global arena for AstNode allocations 2018-06-09 19:53:06 +01:00
gingerBill 49ea9ed722 Entity aliasing clean up 2018-06-09 10:08:17 +01:00
gingerBill d7108416c9 Remove dead code 2018-06-07 23:52:13 +01:00
gingerBill b136630856 Fix type info generation for empty structs 2018-06-07 23:24:37 +01:00
gingerBill fa6f31186a Merge pull request #228 from shuaDev/shwadev-packages
Fix some core lib errors on packages branch
2018-06-03 21:09:57 +01:00
gingerBill b027b1d60f Fix min type info for polymorphic procedures and named types 2018-06-03 21:09:08 +01:00
Joshua Mark Manton 7ed1d931cb fix quick_sort_proc calling quick_sort instead of recursively calling itself 2018-06-03 11:27:57 -07:00
Joshua Mark Manton 2570296b01 fix core opengl ODIN_OS reference and pointer math stuff 2018-06-03 11:25:46 -07:00
Joshua Mark Manton f0a4526250 Fix alloc.odin using old raw file 2018-06-03 11:22:42 -07:00
gingerBill c39332c7e7 Revert name mangling 2018-06-03 19:18:47 +01:00
gingerBill 3f4b6b22dc Change our IR name mangling rules 2018-06-03 17:55:13 +01:00
gingerBill e0549df03e Fix minor possible issue 2018-06-03 17:44:10 +01:00
gingerBill e46662a546 Rename os.default_allocator to os.heap_allocator 2018-06-03 16:40:58 +01:00
gingerBill 360a74e2fe Merge pull request #226 from BrettRToomey/packages
Dsymutil fixes
2018-06-03 16:35:43 +01:00
Brett R. Toomey 597c4591bc Merge branch 'packages' of github.com:odin-lang/Odin into packages 2018-06-03 17:13:11 +02:00
Brett R. Toomey 80833ed703 Dsymutil fixes for macOS 2018-06-03 17:12:30 +02:00
gingerBill 106302189c Use gbString for opt_flags 2018-06-03 16:09:24 +01:00
gingerBill 05c5f98e8e Add -debug-compile parameter for llc 2018-06-03 15:55:14 +01:00
gingerBill d556fa2cd8 Remove special shared scope for runtime stuff 2018-06-03 15:06:40 +01:00
gingerBill 9bd7f023b2 Split up init_preload into specific parts 2018-06-03 11:38:02 +01:00
gingerBill 398109ac84 Remove need for __llvm_core 2018-06-03 10:51:43 +01:00
gingerBill 12b870ba66 Use const & for Array<AstNode *> parameters 2018-06-03 10:30:31 +01:00
gingerBill 6202fb8373 Re-allow when statements at the file scope 2018-06-02 19:44:34 +01:00
gingerBill ced818ad54 Remove dead code from checker 2018-06-02 11:58:35 +01:00
gingerBill ccbb6df749 Handle multiple +build package tags with '!' (nots) 2018-05-28 21:25:08 +01:00
gingerBill 6eb505a677 Comment based build tags for packages (basic and temporary) 2018-05-28 20:59:06 +01:00
gingerBill 619783ca1b Remove clutter parameters and begin parallelizing the type checker 2018-05-28 18:46:39 +01:00
gingerBill 642aa0bc4b Refactor: use CheckerContext rather than Checker in type checking part 2018-05-28 15:57:53 +01:00
gingerBill 45b3067068 Remove tmp_allocator from Checker 2018-05-28 14:15:08 +01:00
gingerBill b7858a66b9 Parallelize per file rather than per package 2018-05-28 12:06:50 +01:00
gingerBill 4e203feaf4 Change import lookup 2018-05-27 23:46:08 +01:00
gingerBill a513b47780 Remove unused packages 2018-05-27 23:33:10 +01:00
gingerBill 547a2831c7 Clean up name mangling by using unique package names per project 2018-05-27 22:09:11 +01:00
gingerBill 5c52ffe24e Reorganize runtime package 2018-05-27 21:22:25 +01:00
gingerBill a5763d6fee Err on empty directory packages 2018-05-27 14:12:10 +01:00
gingerBill 95482c554d Fix build.bat 2018-05-27 13:53:46 +01:00
gingerBill 10758710d4 Fix demo.odin 2018-05-27 13:53:19 +01:00
gingerBill 86cf9383ea Fix delayed assert collection 2018-05-27 13:49:55 +01:00
gingerBill 307977d4cf Remove dead code 2018-05-27 13:30:18 +01:00
gingerBill 1beff539d7 Single file "main" file 2018-05-27 13:22:24 +01:00
gingerBill df578d6ec5 Allow for either .odin file or directory as the initial start 2018-05-27 11:40:27 +01:00
gingerBill 6aae381e83 Move ODIN_* platform constants to core:os 2018-05-27 11:03:46 +01:00
gingerBill 7ee9051a56 IR now builds with the new package system 2018-05-27 10:49:14 +01:00
gingerBill eb11edabe0 Add file scopes for the packages 2018-05-27 00:10:38 +01:00
gingerBill c067b90403 Add basic package support (no IR support yet) 2018-05-26 23:12:55 +01:00
gingerBill 5b6770f3d2 Parse directories to be packages 2018-05-21 20:47:52 +01:00
gingerBill 718b80ba39 Fix demo for removing default struct values 2018-05-20 17:39:49 +01:00
gingerBill 4d052d5119 Remove code relating to default struct values 2018-05-20 17:31:46 +01:00
gingerBill 7e4c643401 Disable default struct field values; Update README.md 2018-05-20 16:00:39 +01:00
gingerBill e920338f21 Remove old dependency 2018-05-20 08:58:48 +01:00
gingerBill af2048570c Merge pull request #222 from shuaDev/master
added compiler command for only parsing and typechecking
2018-05-17 10:33:23 +01:00
Joshua Mark Manton 1ee4f849cb now return 1 if there were errors 2018-05-17 02:08:04 -07:00
Joshua Mark Manton 703393fc63 whitespace 2018-05-16 23:08:01 -07:00
Joshua Mark Manton 81420ab246 removed unneeded block 2018-05-16 23:07:27 -07:00
Joshua Mark Manton c94d19718b added compiler command for only parsing and typechecking 2018-05-16 23:03:05 -07:00
gingerBill e25c72ecdd Fix #219 and #220 2018-05-14 17:05:52 +01:00
gingerBill 780b81a59f Allow for NO_DEFAULT_STRUCT_VALUES
(will decide later if I want them or not)
2018-05-13 21:09:49 +01:00
gingerBill 9f1dda701d Comment out test 2018-05-13 18:43:21 +01:00
gingerBill e597a8d72e Fix issues with exact integer bounds and remove dead code 2018-05-13 17:38:35 +01:00
gingerBill de9a4b5164 Disable pointer arithmetic 2018-05-13 16:10:02 +01:00
gingerBill 319aca3101 Merge pull request #218 from hasenj/osx-timing
Fix timing on macos
2018-05-13 11:45:05 +01:00
Hasen Judy 9dc2c01aaa Fix timing on macos 2018-05-13 19:33:08 +09:00
gingerBill 6164672421 Change FreeAll to Free_All 2018-05-13 10:14:32 +01:00
gingerBill 61906613b0 Add typeid to Type_Info struct 2018-05-13 10:09:21 +01:00
gingerBill 3b48fa8e7d Fix default initialized values for globals (#217) 2018-05-12 21:22:39 +01:00
gingerBill 324b7d65e7 Use __type_info_of internally 2018-05-12 20:17:12 +01:00
gingerBill 373a60b9ef type_info_of allows typeid; typeid_of allows ^Type_Info; Otherwise only allow type 2018-05-12 19:54:16 +01:00
gingerBill 2ef22e86e0 Make any use typeid rather than ^Type_Info 2018-05-12 18:40:49 +01:00
gingerBill 830f4f540f typeid 2018-05-12 17:39:04 +01:00
gingerBill 56ff5496bc Minimal Type Info Dependency handling 2018-05-12 16:53:44 +01:00
gingerBill 20fbece14c Change semantics for distinctness for pointers, arrays, dynamic arrays, and maps. 2018-05-12 10:47:32 +01:00
gingerBill 9fbfd86cde Add log to math.odin 2018-05-12 10:46:00 +01:00
gingerBill 7547bc66cf Complete remove all non required preload stuff from min dep and only use what is used. 2018-05-12 10:38:40 +01:00
gingerBill 18a9fa7355 Improve minimal dependency system 2018-05-12 10:27:55 +01:00
gingerBill b32af841c5 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-05-06 15:31:32 +01:00
gingerBill 66b4252931 Fix #210 2018-05-06 15:31:18 +01:00
gingerBill 2c95eaa418 Merge pull request #216 from lunaticLipid/master
Fix copy-and-forget in math.odin
2018-05-02 19:40:59 +01:00
Lipid 7382f52dc9 Fix copy-and-forget in math.odin 2018-05-02 18:04:50 +02:00
gingerBill 49dd299999 Merge pull request #204 from ThisDrunkDane/master
Added more function to windows.odin
2018-04-14 19:45:00 +01:00
Mikkel Hjortshoej e391b05513 Merge branch 'master' of github.com:ThisDrunkDane/Odin 2018-04-13 22:39:17 +02:00
Mikkel Hjortshoej 2de62910fc Added more function to windows.odin 2018-04-13 22:38:58 +02:00
gingerBill fc77b5b4ac Try to fix internal compiler error in #208 2018-04-10 21:03:51 +01:00
gingerBill a83d916fad Fix immutable context to any assignment #214 2018-04-10 20:51:44 +01:00
gingerBill e71a641379 Fix internal compiler error trigger for issue #212 2018-04-10 20:46:32 +01:00
gingerBill e2eca45188 Fix race condition caused by parallelized parser: #211 2018-04-10 20:35:05 +01:00
gingerBill 4d78540658 Fix #210 2018-04-10 20:20:33 +01:00
gingerBill b83c3f265b Fix #209 #assert bug 2018-04-10 20:18:16 +01:00
gingerBill 30f5a3bb93 Move cycle checking to much earlier on in the semantic stage 2018-03-23 20:48:30 +00:00
gingerBill 2e1e1e6034 Type caching 2018-03-23 16:35:41 +00:00
gingerBill 991479fbf9 Remove allocator parameter to types.cpp functions 2018-03-23 16:01:23 +00:00
gingerBill 5660f98cc3 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-03-23 15:23:31 +00:00
gingerBill 5bf0f9d630 Fix type cycle bug 2018-03-23 15:23:14 +00:00
Mikkel Hjortshoej 15b72119eb Added more function to windows.odin 2018-03-21 19:30:27 +01:00
gingerBill dc30e7a200 Merge pull request #201 from nakst/master
update essence bindings
2018-03-11 20:52:58 +00:00
Nakst db2293144a update essence bindings 2018-03-10 21:33:59 +00:00
gingerBill 5016f45429 Merge pull request #200 from nakst/master
fix compile warnings on linux
2018-03-10 20:50:46 +00:00
Nakst 9fa4aa40b7 fix compile warnings on linux 2018-03-10 20:29:43 +00:00
gingerBill 52f60c706a Merge pull request #199 from ThisDrunkDane/resource
Resource Compiler calling
2018-03-10 13:42:25 +00:00
gingerBill fff4ead96a Fix gb_alloc_str_len 2018-03-07 20:36:15 +00:00
Mikkel Hjortshoej 3574341b6b Missing .rc error message 2018-03-05 13:10:01 +01:00
Mikkel Hjortshoej cbabc80d92 Calls rc.exe if -resource specified 2018-03-05 11:46:50 +01:00
gingerBill f4cf88c2ca Move os_*.odin files to os/ 2018-03-04 11:38:49 +00:00
gingerBill 6db95b554f __args__: []cstring 2018-03-04 11:25:23 +00:00
gingerBill 105de7705a Add unselector_expr 2018-03-04 11:06:59 +00:00
gingerBill 584dffea14 Remove dead code; fix referencing of a type assertion in a selector expression 2018-03-04 09:25:02 +00:00
gingerBill 41b6d215bb Fix using determination order 2018-03-03 20:07:12 +00:00
gingerBill 9274f29ca9 deprecated attribute for procedure declarations 2018-03-03 11:16:48 +00:00
gingerBill 08c87e57f8 Remove cwd in odin run for Linux 2018-03-03 10:26:25 +00:00
gingerBill b21cdd5037 Merge branch 'master' of https://github.com/odin-lang/Odin 2018-03-03 10:25:13 +00:00
gingerBill 63ab8b2418 Make irGen.output_base use full path rather than relative 2018-03-03 10:23:27 +00:00
Morten Vassvik cb7a343caf Fixed '_alloc_command_line_arguments()' in os_linux.odin to use the new cstrings, and made 'odin run' use the full executable path. 2018-03-01 12:58:57 +01:00
gingerBill 40542e6e26 Fix comparison against nil for cstring 2018-02-28 12:01:26 +00:00
gingerBill 9da05dd4cb Update core library with cstring 2018-02-28 11:44:41 +00:00
gingerBill ae9da0abfb Merge branch 'master' of https://github.com/odin-lang/Odin 2018-02-28 11:20:31 +00:00
gingerBill d3ea334e7a cstring 2018-02-28 11:20:11 +00:00
gingerBill d76132a3fb Merge pull request #198 from ThisDrunkDane/terminate_thread
Added terminate_thread to thread.odin
2018-02-26 10:06:08 +00:00
gingerBill 223c473cf6 Demo fix 2018-02-25 20:57:34 +00:00
gingerBill fd57cfa1ae Fix build_settings.cpp 2018-02-25 20:52:06 +00:00
gingerBill f23bd2dc27 Revert demo 2018-02-25 20:46:32 +00:00
gingerBill 69062ba3ab More code tidying with Array 2018-02-25 20:24:38 +00:00
gingerBill e75563cb32 Minor code rearrangement 2018-02-25 19:42:12 +00:00
gingerBill d63885a495 array_make 2018-02-25 19:23:52 +00:00
gingerBill f28a34fa99 Use Array<irValue *> in ir.cpp 2018-02-25 15:31:00 +00:00
gingerBill a1e8de4e00 Fix ir_emit_slice_bounds_check 2018-02-25 15:11:20 +00:00
gingerBill d247ba4751 Hexadecimal floats for "perfect values" 0h42f60000 == 123; use bit_cast in compiler 2018-02-25 15:09:16 +00:00
gingerBill 27b7dc336a Change parsing for floats and disallow x.0 2018-02-25 14:36:41 +00:00
gingerBill 60a7c68aa6 Minor code reorganization 2018-02-25 14:23:45 +00:00
Mikkel Hjortshoej 78c103e62c Merge branch 'terminate_thread' of github.com:ThisDrunkDane/Odin into terminate_thread 2018-02-25 14:57:22 +01:00
Mikkel Hjortshoej ffec1c77f2 Added terminate_thread to thread.odin 2018-02-25 14:56:50 +01:00
gingerBill 5357181484 Multithreaded parser (windows only) 2018-02-25 13:45:44 +00:00
Mikkel Hjortshoej 33ddb3ad4d Added terminate_thread to thread.odin 2018-02-25 14:38:55 +01:00
gingerBill 1cd453db14 Remove unneeded disabled warnings for MSVC 2018-02-25 12:29:48 +00:00
gingerBill 3b5932699c Fix #assert in opengl.odin 2018-02-25 12:14:35 +00:00
gingerBill bada81159d Add #no_bounds_check to __dynamic_map_* procedures 2018-02-25 12:13:45 +00:00
gingerBill 652da98c70 Fix slice bounds checking 2018-02-25 12:10:19 +00:00
gingerBill e14e2c3b4d -out and generate executable in the current working directory 2018-02-25 11:49:44 +00:00
gingerBill f96a897821 Make switch in f { valid 2018-02-25 10:55:18 +00:00
gingerBill b74ae77745 Merge pull request #197 from bpunsky/context-bug-fix
Context bug fix
2018-02-25 00:13:02 +00:00
Brendan Punsky 564226be02 fixed issues with uninitialized contexts
also, `any_to_bytes` I think, and maybe some bindings in `core:sys/windows.odin`
2018-02-24 18:58:22 -05:00
Brendan Punsky f6c45fc68a Merge remote-tracking branch 'origin/master' 2018-02-24 14:29:30 -05:00
gingerBill 35ba5771a5 Replace compile_assert with #assert 2018-02-24 19:03:29 +00:00
gingerBill b2461f7192 Fix issue #195 2018-02-24 18:19:11 +00:00
Brendan Punsky 60a54f404b Auto stash before merge of "master" and "origin/master" 2018-02-22 20:11:17 -05:00
gingerBill 921f261377 Fix os.args on Windows #143 2018-02-22 21:34:09 +00:00
gingerBill d70a555c1c Fix issue #192 2018-02-22 21:24:38 +00:00
gingerBill 4c339360e9 auto_cast 2018-02-18 15:14:13 +00:00
gingerBill 731dad480d Fix issue regarding nullptr Type * 2018-02-18 14:37:58 +00:00
gingerBill a0f2357cb3 Minor fix to demo 2018-02-17 19:40:33 +00:00
gingerBill e86ac75e9c Merge branch 'master' of https://github.com/odin-lang/Odin 2018-02-17 19:24:15 +00:00
gingerBill f51de2e488 Disallow #complete switch ranges 2018-02-17 19:24:02 +00:00
gingerBill 5efefdcf16 Merge pull request #189 from ThisDrunkDane/master
Pretty readme header
2018-02-17 19:16:51 +00:00
gingerBill cabb2bb992 Commit 1000 🎉🎂 2018-02-17 19:15:58 +00:00
gingerBill d560f6c920 Fix compile time issue regarding switch ranges 2018-02-17 19:05:14 +00:00
gingerBill 21432ba96e Clean up range code for switch 2018-02-17 18:34:14 +00:00
gingerBill c341597657 Remove constant from switch for strings 2018-02-17 18:22:43 +00:00
Mikkel Hjortshøj 2a1420d4e7 Update README.md 2018-02-17 15:03:26 +01:00
Mikkel Hjortshoej 28d88f6af4 rounded logo 2018-02-17 15:03:16 +01:00
gingerBill c4d2d287fc #complete switch; Removal of dyncall 2018-02-17 11:54:08 +00:00
gingerBill 6a85546b76 Fix #187 2018-02-14 21:46:39 +00:00
gingerBill 2e92d0c821 Remove old procedures 2018-02-13 22:05:25 +00:00
gingerBill a499a3aa5e Merge pull request #184 from ThisDrunkDane/master
Added widechar versions of functions, plus cursor functions
2018-02-13 22:02:43 +00:00
gingerBill 23ab3c4713 Replace [...] with [?] 2018-02-13 21:59:49 +00:00
gingerBill da300aa9c3 Fix enum #export does not work with export #185 2018-02-13 18:01:42 +00:00
ThisDrunkDane e225158a6f Merge branch 'master' of https://github.com/odin-lang/Odin 2018-02-12 06:07:15 +01:00
gingerBill 2ce55783d2 Fix make 2018-02-11 23:47:46 +00:00
Mikkel Hjortshoej 4c06b44315 Merge branch 'master' of github.com:odin-lang/odin 2018-02-07 21:23:28 +01:00
Mikkel Hjortshoej 678b58e0b1 Added widechar versions of functions, plus cursor functions 2018-02-07 21:17:59 +01:00
200 changed files with 49992 additions and 21553 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
./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).
+108 -36
View File
@@ -1,4 +1,23 @@
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
<p align="center">
<img src="misc/logo-slim.png" alt="Odin logo" height="120">
<br/>
A fast, concise, readable, pragmatic and open sourced programming language.
<br/>
<br/>
<a href="https://github.com/odin-lang/odin/releases/latest">
<img src="https://img.shields.io/github/release/odin-lang/odin.svg">
</a>
<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>
<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>
# The Odin Programming Language
@@ -7,12 +26,13 @@ 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
import "core:fmt.odin"
package main
import "core:fmt"
main :: proc() {
program := "+ + * 😃 - /";
@@ -35,24 +55,80 @@ 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)
## Documentation
#### [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 2015 installed (C++11 support)
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
* MSVC 2010 installed (C++11 support)
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
* Requires MSVC's link.exe as the linker
* run `vcvarsall.bat` to setup the path
@@ -72,23 +148,19 @@ main :: proc() {
* This is still highly in development and the language's design is quite volatile.
* Syntax is not fixed.
## Roadmap
## 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)
Not in any particular order and not be implemented
* Compile Time Execution (CTE)
- More metaprogramming madness
- Compiler as a library
- AST inspection and modification
* CTE-based build system
* Replace LLVM backend with my own custom backend
* Improve SSA design to accommodate for lowering to a "bytecode"
* SSA optimizations
* Documentation Generator for "Entities"
* Multiple Architecture support
* Debug Information
- pdb format too
* Command Line Tooling
* Compiler Internals:
- Big numbers library
- Multithreading for performance increase
+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.
+5 -10
View File
@@ -5,27 +5,25 @@ set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=0
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR-
if %release_mode% EQU 0 ( rem Debug
set compiler_flags=%compiler_flags% -Od -MDd -Z7
rem -DDISPLAY_TIMING
) else ( rem Release
set compiler_flags=%compiler_flags% -O2 -MT -Z7
set compiler_flags=%compiler_flags% -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
)
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
-wd4201 -wd4204 -wd4244 ^
-wd4306 ^
-wd4201 -wd4204 ^
-wd4456 -wd4457 -wd4480 ^
-wd4505 -wd4512 -wd4550
-wd4512
set compiler_includes=
set libs= ^
kernel32.lib
rem "src\dyncall\lib\*.lib"
set linker_flags= -incremental:no -opt:ref -subsystem:console
@@ -41,13 +39,10 @@ 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.odin
rem && odin docs core/fmt.odin
&& odin run examples/demo/demo.odin
del *.obj > NUL 2> NUL
:end_of_build
+1 -1
View File
@@ -25,4 +25,4 @@ if [[ "$(uname)" == "Darwin" ]]; then
other_args="${other_args} -liconv"
fi
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo.odin
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
+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% ^
-1301
View File
File diff suppressed because it is too large Load Diff
-112
View File
@@ -1,112 +0,0 @@
#shared_global_scope
/*
@(link_name="__multi3")
__multi3 :: proc "c" (a, b: u128) -> u128 {
bits_in_dword_2 :: size_of(i64) * 4;
lower_mask :: u128(~u64(0) >> bits_in_dword_2);
when ODIN_ENDIAN == "big" {
TWords :: struct #raw_union {
all: u128,
using _: struct {lo, hi: u64},
};
} else {
TWords :: struct #raw_union {
all: u128,
using _: struct {hi, lo: u64},
};
}
r: TWords;
t: u64;
r.lo = u64(a & lower_mask) * u64(b & lower_mask);
t = r.lo >> bits_in_dword_2;
r.lo &= u64(lower_mask);
t += u64(a >> bits_in_dword_2) * u64(b & lower_mask);
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
r.hi = t >> bits_in_dword_2;
t = r.lo >> bits_in_dword_2;
r.lo &= u64(lower_mask);
t += u64(b >> bits_in_dword_2) * u64(a & lower_mask);
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
r.hi += t >> bits_in_dword_2;
r.hi += u64(a >> bits_in_dword_2) * u64(b >> bits_in_dword_2);
return r.all;
}
@(link_name="__umodti3")
__u128_mod :: proc "c" (a, b: u128) -> u128 {
r: u128;
__u128_quo_mod(a, b, &r);
return r;
}
@(link_name="__udivti3")
__u128_quo :: proc "c" (a, b: u128) -> u128 {
return __u128_quo_mod(a, b, nil);
}
@(link_name="__modti3")
__i128_mod :: proc "c" (a, b: i128) -> i128 {
r: i128;
__i128_quo_mod(a, b, &r);
return r;
}
@(link_name="__divti3")
__i128_quo :: proc "c" (a, b: i128) -> i128 {
return __i128_quo_mod(a, b, nil);
}
@(link_name="__divmodti4")
__i128_quo_mod :: proc "c" (a, b: i128, rem: ^i128) -> (quo: i128) {
s: i128;
s = b >> 127;
b = (b~s) - s;
s = a >> 127;
b = (a~s) - s;
uquo: u128;
urem := __u128_quo_mod(transmute(u128)a, transmute(u128)b, &uquo);
iquo := transmute(i128)uquo;
irem := transmute(i128)urem;
iquo = (iquo~s) - s;
irem = (irem~s) - s;
if rem != nil do rem^ = irem;
return iquo;
}
@(link_name="__udivmodti4")
__u128_quo_mod :: proc "c" (a, b: u128, rem: ^u128) -> (quo: u128) {
alo := u64(a);
blo := u64(b);
if b == 0 {
if rem != nil do rem^ = 0;
return u128(alo/blo);
}
r, d, x, q: u128 = a, b, 1, 0;
for r >= d && (d>>127)&1 == 0 {
x <<= 1;
d <<= 1;
}
for x != 0 {
if r >= d {
r -= d;
q |= x;
}
x >>= 1;
d >>= 1;
}
if rem != nil do rem^ = r;
return q;
}
*/
-102
View File
@@ -1,102 +0,0 @@
// TODO(bill): Use assembly instead here to implement atomics
// Inline vs external file?
when ODIN_OS == "windows" {
import win32 "core:sys/windows.odin"
}
_ :: compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
yield_thread :: proc() { win32.mm_pause(); }
mfence :: proc() { win32.read_write_barrier(); }
sfence :: proc() { win32.write_barrier(); }
lfence :: proc() { win32.read_barrier(); }
load_i32 :: proc(a: ^i32) -> i32 {
return a^;
}
store_i32 :: proc(a: ^i32, value: i32) {
a^ = value;
}
compare_exchange_i32 :: proc(a: ^i32, expected, desired: i32) -> i32 {
return win32.interlocked_compare_exchange(a, desired, expected);
}
exchanged_i32 :: proc(a: ^i32, desired: i32) -> i32 {
return win32.interlocked_exchange(a, desired);
}
fetch_add_i32 :: proc(a: ^i32, operand: i32) -> i32 {
return win32.interlocked_exchange_add(a, operand);
}
fetch_and_i32 :: proc(a: ^i32, operand: i32) -> i32 {
return win32.interlocked_and(a, operand);
}
fetch_or_i32 :: proc(a: ^i32, operand: i32) -> i32 {
return win32.interlocked_or(a, operand);
}
spin_lock_i32 :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange_i32(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter += 1;
yield_thread();
old_value = compare_exchange_i32(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock_i32 :: proc(a: ^i32) {
store_i32(a, 0);
mfence();
}
try_acquire_lock_i32 :: proc(a: ^i32) -> bool {
yield_thread();
old_value := compare_exchange_i32(a, 1, 0);
mfence();
return old_value == 0;
}
load_i64 :: proc(a: ^i64) -> i64 {
return a^;
}
store_i64 :: proc(a: ^i64, value: i64) {
a^ = value;
}
compare_exchange_i64 :: proc(a: ^i64, expected, desired: i64) -> i64 {
return win32.interlocked_compare_exchange64(a, desired, expected);
}
exchanged_i64 :: proc(a: ^i64, desired: i64) -> i64 {
return win32.interlocked_exchange64(a, desired);
}
fetch_add_i64 :: proc(a: ^i64, operand: i64) -> i64 {
return win32.interlocked_exchange_add64(a, operand);
}
fetch_and_i64 :: proc(a: ^i64, operand: i64) -> i64 {
return win32.interlocked_and64(a, operand);
}
fetch_or_i64 :: proc(a: ^i64, operand: i64) -> i64 {
return win32.interlocked_or64(a, operand);
}
spin_lock_i64 :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange_i64(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter += 1;
yield_thread();
old_value = compare_exchange_i64(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock_i64 :: proc(a: ^i64) {
store_i64(a, 0);
mfence();
}
try_acquire_lock_i64 :: proc(a: ^i64) -> bool {
yield_thread();
old_value := compare_exchange_i64(a, 1, 0);
mfence();
return old_value == 0;
}
-244
View File
@@ -1,244 +0,0 @@
U8_MIN :: u8(0);
U16_MIN :: u16(0);
U32_MIN :: u32(0);
U64_MIN :: u64(0);
U8_MAX :: ~u8(0);
U16_MAX :: ~u16(0);
U32_MAX :: ~u32(0);
U64_MAX :: ~u64(0);
I8_MIN :: i8( ~u8(0) >> 1);
I16_MIN :: i16( ~u16(0) >> 1);
I32_MIN :: i32( ~u32(0) >> 1);
I64_MIN :: i64( ~u64(0) >> 1);
I8_MAX :: -I8_MIN - 1;
I16_MAX :: -I16_MIN - 1;
I32_MAX :: -I32_MIN - 1;
I64_MAX :: -I64_MIN - 1;
foreign __llvm_core {
@(link_name="llvm.ctpop.i8") __llvm_ctpop8 :: proc(u8) -> u8 ---;
@(link_name="llvm.ctpop.i16") __llvm_ctpop16 :: proc(u16) -> u16 ---;
@(link_name="llvm.ctpop.i32") __llvm_ctpop32 :: proc(u32) -> u32 ---;
@(link_name="llvm.ctpop.i64") __llvm_ctpop64 :: proc(u64) -> u64 ---;
@(link_name="llvm.ctlz.i8") __llvm_ctlz8 :: proc(u8, bool) -> u8 ---;
@(link_name="llvm.ctlz.i16") __llvm_ctlz16 :: proc(u16, bool) -> u16 ---;
@(link_name="llvm.ctlz.i32") __llvm_ctlz32 :: proc(u32, bool) -> u32 ---;
@(link_name="llvm.ctlz.i64") __llvm_ctlz64 :: proc(u64, bool) -> u64 ---;
@(link_name="llvm.cttz.i8") __llvm_cttz8 :: proc(u8, bool) -> u8 ---;
@(link_name="llvm.cttz.i16") __llvm_cttz16 :: proc(u16, bool) -> u16 ---;
@(link_name="llvm.cttz.i32") __llvm_cttz32 :: proc(u32, bool) -> u32 ---;
@(link_name="llvm.cttz.i64") __llvm_cttz64 :: proc(u64, bool) -> u64 ---;
@(link_name="llvm.bitreverse.i8") __llvm_bitreverse8 :: proc(u8) -> u8 ---;
@(link_name="llvm.bitreverse.i16") __llvm_bitreverse16 :: proc(u16) -> u16 ---;
@(link_name="llvm.bitreverse.i32") __llvm_bitreverse32 :: proc(u32) -> u32 ---;
@(link_name="llvm.bitreverse.i64") __llvm_bitreverse64 :: proc(u64) -> u64 ---;
@(link_name="llvm.bswap.i16") byte_swap16 :: proc(u16) -> u16 ---;
@(link_name="llvm.bswap.i32") byte_swap32 :: proc(u32) -> u32 ---;
@(link_name="llvm.bswap.i64") byte_swap64 :: proc(u64) -> u64 ---;
}
byte_swap_uint :: proc(i: uint) -> uint {
when size_of(uint) == size_of(u32) {
return uint(byte_swap32(u32(i)));
} else {
return uint(byte_swap64(u64(i)));
}
}
byte_swap :: proc[byte_swap16, byte_swap32, byte_swap64, byte_swap_uint];
count_ones8 :: proc(i: u8) -> u8 { return __llvm_ctpop8(i); }
count_ones16 :: proc(i: u16) -> u16 { return __llvm_ctpop16(i); }
count_ones32 :: proc(i: u32) -> u32 { return __llvm_ctpop32(i); }
count_ones64 :: proc(i: u64) -> u64 { return __llvm_ctpop64(i); }
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
rotate_left8 :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
rotate_left16 :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
rotate_left32 :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
rotate_left64 :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
rotate_right8 :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
rotate_right16 :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
rotate_right32 :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
rotate_right64 :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
leading_zeros8 :: proc(i: u8) -> u8 { return __llvm_ctlz8(i, false); }
leading_zeros16 :: proc(i: u16) -> u16 { return __llvm_ctlz16(i, false); }
leading_zeros32 :: proc(i: u32) -> u32 { return __llvm_ctlz32(i, false); }
leading_zeros64 :: proc(i: u64) -> u64 { return __llvm_ctlz64(i, false); }
trailing_zeros8 :: proc(i: u8) -> u8 { return __llvm_cttz8(i, false); }
trailing_zeros16 :: proc(i: u16) -> u16 { return __llvm_cttz16(i, false); }
trailing_zeros32 :: proc(i: u32) -> u32 { return __llvm_cttz32(i, false); }
trailing_zeros64 :: proc(i: u64) -> u64 { return __llvm_cttz64(i, false); }
reverse_bits8 :: proc(i: u8) -> u8 { return __llvm_bitreverse8(i); }
reverse_bits16 :: proc(i: u16) -> u16 { return __llvm_bitreverse16(i); }
reverse_bits32 :: proc(i: u32) -> u32 { return __llvm_bitreverse32(i); }
reverse_bits64 :: proc(i: u64) -> u64 { return __llvm_bitreverse64(i); }
from_be_u8 :: proc(i: u8) -> u8 { return i; }
from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_le_u8 :: proc(i: u8) -> u8 { return i; }
from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_be_u8 :: proc(i: u8) -> u8 { return i; }
to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_le_u8 :: proc(i: u8) -> u8 { return i; }
to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core @(link_name="llvm.uadd.with.overflow.i8") op :: proc(u8, u8) -> (u8, bool) ---; return op(lhs, rhs); }
overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core @(link_name="llvm.sadd.with.overflow.i8") op :: proc(i8, i8) -> (i8, bool) ---; return op(lhs, rhs); }
overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core @(link_name="llvm.uadd.with.overflow.i16") op :: proc(u16, u16) -> (u16, bool) ---; return op(lhs, rhs); }
overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core @(link_name="llvm.sadd.with.overflow.i16") op :: proc(i16, i16) -> (i16, bool) ---; return op(lhs, rhs); }
overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core @(link_name="llvm.uadd.with.overflow.i32") op :: proc(u32, u32) -> (u32, bool) ---; return op(lhs, rhs); }
overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core @(link_name="llvm.sadd.with.overflow.i32") op :: proc(i32, i32) -> (i32, bool) ---; return op(lhs, rhs); }
overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core @(link_name="llvm.uadd.with.overflow.i64") op :: proc(u64, u64) -> (u64, bool) ---; return op(lhs, rhs); }
overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core @(link_name="llvm.sadd.with.overflow.i64") op :: proc(i64, i64) -> (i64, bool) ---; return op(lhs, rhs); }
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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,
];
overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core @(link_name="llvm.usub.with.overflow.i8") op :: proc(u8, u8) -> (u8, bool) ---; return op(lhs, rhs); }
overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core @(link_name="llvm.ssub.with.overflow.i8") op :: proc(i8, i8) -> (i8, bool) ---; return op(lhs, rhs); }
overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core @(link_name="llvm.usub.with.overflow.i16") op :: proc(u16, u16) -> (u16, bool) ---; return op(lhs, rhs); }
overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core @(link_name="llvm.ssub.with.overflow.i16") op :: proc(i16, i16) -> (i16, bool) ---; return op(lhs, rhs); }
overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core @(link_name="llvm.usub.with.overflow.i32") op :: proc(u32, u32) -> (u32, bool) ---; return op(lhs, rhs); }
overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core @(link_name="llvm.ssub.with.overflow.i32") op :: proc(i32, i32) -> (i32, bool) ---; return op(lhs, rhs); }
overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core @(link_name="llvm.usub.with.overflow.i64") op :: proc(u64, u64) -> (u64, bool) ---; return op(lhs, rhs); }
overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core @(link_name="llvm.ssub.with.overflow.i64") op :: proc(i64, i64) -> (i64, bool) ---; return op(lhs, rhs); }
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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,
];
overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core @(link_name="llvm.umul.with.overflow.i8") op :: proc(u8, u8) -> (u8, bool) ---; return op(lhs, rhs); }
overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core @(link_name="llvm.smul.with.overflow.i8") op :: proc(i8, i8) -> (i8, bool) ---; return op(lhs, rhs); }
overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core @(link_name="llvm.umul.with.overflow.i16") op :: proc(u16, u16) -> (u16, bool) ---; return op(lhs, rhs); }
overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core @(link_name="llvm.smul.with.overflow.i16") op :: proc(i16, i16) -> (i16, bool) ---; return op(lhs, rhs); }
overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core @(link_name="llvm.umul.with.overflow.i32") op :: proc(u32, u32) -> (u32, bool) ---; return op(lhs, rhs); }
overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core @(link_name="llvm.smul.with.overflow.i32") op :: proc(i32, i32) -> (i32, bool) ---; return op(lhs, rhs); }
overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core @(link_name="llvm.umul.with.overflow.i64") op :: proc(u64, u64) -> (u64, bool) ---; return op(lhs, rhs); }
overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core @(link_name="llvm.smul.with.overflow.i64") op :: proc(i64, i64) -> (i64, bool) ---; return op(lhs, rhs); }
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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; }
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
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_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,
]
+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 ---
-38
View File
@@ -1,38 +0,0 @@
CHAR_BIT :: 8;
c_bool :: bool;
c_char :: u8;
c_byte :: u8;
c_schar :: i8;
c_uchar :: u8;
c_short :: i16;
c_ushort :: u16;
c_int :: i32;
c_uint :: u32;
when ODIN_OS == "windows" || size_of(rawptr) == 4 {
c_long :: i32;
} else {
c_long :: i64;
}
when ODIN_OS == "windows" || size_of(rawptr) == 4 {
c_ulong :: u32;
} else {
c_ulong :: u64;
}
c_longlong :: i64;
c_ulonglong :: u64;
c_float :: f32;
c_double :: f64;
c_complex_float :: complex64;
c_complex_double :: complex128;
_ :: compile_assert(size_of(uintptr) == size_of(int));
c_size_t :: uint;
c_ssize_t :: int;
c_ptrdiff_t :: int;
c_uintptr_t :: uintptr;
c_intptr_t :: int;
+35
View File
@@ -0,0 +1,35 @@
package c
import b "core:builtin"
CHAR_BIT :: 8;
bool :: b.bool;
char :: b.u8;
byte :: b.byte;
schar :: b.i8;
uchar :: b.u8;
short :: b.i16;
ushort :: b.u16;
int :: b.i32;
uint :: b.u32;
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;
float :: b.f32;
double :: b.f64;
complex_float :: b.complex64;
complex_double :: b.complex128;
#assert(size_of(b.uintptr) == size_of(b.int));
size_t :: b.uint;
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;
}
-1144
View File
File diff suppressed because it is too large Load Diff
+1611
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -1,4 +1,6 @@
import "core:mem.odin"
package hash
import "core:mem"
adler32 :: proc(data: []byte) -> u32 {
ADLER_CONST :: 65521;
@@ -64,9 +66,9 @@ murmur32 :: proc(data: []byte) -> u32 {
h1: u32 = 0;
nblocks := len(data)/4;
p := &data[0];
p1 := p + 4*nblocks;
p1 := mem.ptr_offset(p, 4*nblocks);
for ; p < p1; p += 4 {
for ; p < p1; p = mem.ptr_offset(p, 4) {
k1 := (cast(^u32)p)^;
k1 *= c1_32;
@@ -78,7 +80,7 @@ murmur32 :: proc(data: []byte) -> u32 {
h1 = h1*5 + 0xe6546b64;
}
tail := data[nblocks*4 ..];
tail := data[nblocks*4:];
k1: u32;
switch len(tail)&3 {
case 3:
@@ -185,7 +187,7 @@ murmur64 :: proc(data: []byte) -> u64 {
}
// TODO(bill): Fix this
#no_bounds_check data8 := slice_to_bytes(data32[i..])[..3];
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3];
switch len {
case 3:
h2 ~= u32(data8[2]) << 16;
+127
View File
@@ -0,0 +1,127 @@
// 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_unsigned :: 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, "] ");
}
+94
View File
@@ -0,0 +1,94 @@
package log
import "core:fmt"
Level :: enum {
Debug,
Info,
Warning,
Error,
Fatal,
}
Option :: enum {
Level,
Date,
Time,
Short_File_Path,
Long_File_Path,
Line,
Procedure,
Terminal_Color
}
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,
}
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, 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);
}
-470
View File
@@ -1,470 +0,0 @@
TAU :: 6.28318530717958647692528676655900576;
PI :: 3.14159265358979323846264338327950288;
E :: 2.71828182845904523536;
SQRT_TWO :: 1.41421356237309504880168872420969808;
SQRT_THREE :: 1.73205080756887729352744634150587236;
SQRT_FIVE :: 2.23606797749978969640917366873127623;
LOG_TWO :: 0.693147180559945309417232121458176568;
LOG_TEN :: 2.30258509299404568401799145468436421;
EPSILON :: 1.19209290e-7;
τ :: 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: f32, w: f32 = 1};
@(default_calling_convention="c")
foreign __llvm_core {
@(link_name="llvm.sqrt.f32")
sqrt_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.sqrt.f64")
sqrt_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.sin.f32")
sin_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.sin.f64")
sin_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.cos.f32")
cos_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.cos.f64")
cos_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.pow.f32")
pow_f32 :: proc(x, power: f32) -> f32 ---;
@(link_name="llvm.pow.f64")
pow_f64 :: proc(x, power: f64) -> f64 ---;
@(link_name="llvm.fmuladd.f32")
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
@(link_name="llvm.fmuladd.f64")
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
}
tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); }
tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); }
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); }
sign_f32 :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
sign_f64 :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
copy_sign_f32 :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
iy := transmute(u32)y;
ix &= 0x7fff_ffff;
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 |= iy & 0x8000_0000_0000_0000;
return transmute(f64)ix;
}
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];
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];
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];
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];
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];
mod_f32 :: proc(x, y: f32) -> f32 {
result: f32;
y = abs(y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
}
return copy_sign(result, x);
}
mod_f64 :: proc(x, y: f64) -> f64 {
result: f64;
y = abs(y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
}
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[
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 {
res += a[i] * b[i];
}
return res;
}
cross2 :: proc(a, b: $T/[2]$E) -> E {
return a[0]*b[1] - a[1]*b[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);
}
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: type/[$N][N]$E) -> T {
m: T;
for i in 0..N do m[i][i] = E(1);
return m;
}
transpose :: proc(m: Mat4) -> Mat4 {
for j in 0..4 {
for i in 0..4 {
m[i][j], m[j][i] = m[j][i], m[i][j];
}
}
return m;
}
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
c: Mat4;
for j in 0..4 {
for i in 0..4 {
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}, pitch);
r := axis_angle(Vec3{0, 0, 1}, pitch);
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;
F32_GUARD :: 0;
F32_MANT_DIG :: 24;
F32_MAX :: 3.402823466e+38;
F32_MAX_10_EXP :: 38;
F32_MAX_EXP :: 128;
F32_MIN :: 1.175494351e-38;
F32_MIN_10_EXP :: -37;
F32_MIN_EXP :: -125;
F32_NORMALIZE :: 0;
F32_RADIX :: 2;
F32_ROUNDS :: 1;
F64_DIG :: 15; // # of decimal digits of precision
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
F64_MANT_DIG :: 53; // # of bits in mantissa
F64_MAX :: 1.7976931348623158e+308; // max value
F64_MAX_10_EXP :: 308; // max decimal exponent
F64_MAX_EXP :: 1024; // max binary exponent
F64_MIN :: 2.2250738585072014e-308; // min positive value
F64_MIN_10_EXP :: -307; // min decimal exponent
F64_MIN_EXP :: -1021; // min binary exponent
F64_RADIX :: 2; // exponent radix
F64_ROUNDS :: 1; // addition rounding: near
+260
View File
@@ -0,0 +1,260 @@
package bits
import "core:os"
U8_MIN :: 0;
U16_MIN :: 0;
U32_MIN :: 0;
U64_MIN :: 0;
U8_MAX :: 1 << 8 - 1;
U16_MAX :: 1 << 16 - 1;
U32_MAX :: 1 << 32 - 1;
U64_MAX :: 1 << 64 - 1;
I8_MIN :: - 1 << 7;
I16_MIN :: - 1 << 15;
I32_MIN :: - 1 << 31;
I64_MIN :: - 1 << 63;
I8_MAX :: 1 << 7 - 1;
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 ---
@(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 ---
@(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 ---
@(link_name="llvm.ctlz.i8") leading_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.ctlz.i16") leading_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.ctlz.i32") leading_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.ctlz.i64") leading_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
@(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
@(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
@(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
@(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
@(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 ---
@(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 ---
@(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 ---
@(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 ---
@(link_name="llvm.bswap.i16") byte_swap_u16 :: proc(u16) -> u16 ---
@(link_name="llvm.bswap.i32") byte_swap_u32 :: proc(u32) -> u32 ---
@(link_name="llvm.bswap.i64") byte_swap_u64 :: proc(u64) -> u64 ---
@(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 {
when size_of(uint) == size_of(u32) {
return uint(byte_swap_u32(u32(i)));
} else {
return uint(byte_swap_u64(u64(i)));
}
}
byte_swap_int :: proc(i: int) -> int {
when size_of(int) == size_of(i32) {
return int(byte_swap_i32(i32(i)));
} else {
return int(byte_swap_i64(i64(i)));
}
}
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); }
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
rotate_left8 :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
rotate_left16 :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
rotate_left32 :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
rotate_left64 :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
rotate_right8 :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
rotate_right16 :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
rotate_right32 :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
rotate_right64 :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
from_be_u8 :: proc(i: u8) -> u8 { return i; }
from_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_le_u8 :: proc(i: u8) -> u8 { return i; }
from_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le_uint :: proc(i: uint) -> uint { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_be_u8 :: proc(i: u8) -> u8 { return i; }
to_be_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be_uint :: proc(i: uint) -> uint { when os.ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_le_u8 :: proc(i: u8) -> u8 { return i; }
to_le_u16 :: proc(i: u16) -> u16 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u32 :: proc(i: u32) -> u32 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le_u64 :: proc(i: u64) -> u64 { when os.ENDIAN == "little" { return i; } else { return byte_swap(i); } }
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) ---
@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
}
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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) ---
@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
}
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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) ---
@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
}
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
return int(x), ok;
}
}
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; }
is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
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_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,
};
+289
View File
@@ -0,0 +1,289 @@
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[0] - b[2]*a[0]);
c[2] = +(a[0]*b[1] - b[0]*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[k][i] += a[j][i] * b[k][j];
}
}
}
return;
}
mul_matrix_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
where !intrinsics.type_is_array(E),
intrinsics.type_is_numeric(E),
I != K {
for k in 0..<K {
for j in 0..<J {
for i in 0..<I {
c[k][i] += a[j][i] * b[k][j];
}
}
}
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;
Matrix1x1 :: distinct [1][1]Float;
Matrix1x2 :: distinct [1][2]Float;
Matrix1x3 :: distinct [1][3]Float;
Matrix1x4 :: distinct [1][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;
Matrix1 :: Matrix1x1;
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;
}
+597
View File
@@ -0,0 +1,597 @@
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;
LN2 :: 0.693147180559945309417232121458176568;
LN10 :: 2.30258509299404568401799145468436421;
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'
RAD_PER_DEG :: TAU/360.0;
DEG_PER_RAD :: 360.0/TAU;
@(default_calling_convention="none")
foreign _ {
@(link_name="llvm.sqrt.f32")
sqrt_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.sqrt.f64")
sqrt_f64 :: proc(x: f64) -> f64 ---;
@(link_name="llvm.sin.f32")
sin_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.sin.f64")
sin_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.cos.f32")
cos_f32 :: proc(θ: f32) -> f32 ---;
@(link_name="llvm.cos.f64")
cos_f64 :: proc(θ: f64) -> f64 ---;
@(link_name="llvm.pow.f32")
pow_f32 :: proc(x, power: f32) -> f32 ---;
@(link_name="llvm.pow.f64")
pow_f64 :: proc(x, power: f64) -> f64 ---;
@(link_name="llvm.fmuladd.f32")
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---;
@(link_name="llvm.fmuladd.f64")
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---;
@(link_name="llvm.log.f32")
ln_f32 :: proc(x: f32) -> f32 ---;
@(link_name="llvm.log.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 ---;
}
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 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;
iy := transmute(u32)y;
ix &= 0x7fff_ffff;
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_ffff;
ix |= iy & 0x8000_0000_0000_0000;
return transmute(f64)ix;
}
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};
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 {
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};
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};
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 abs(x);
}
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;
}
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_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;
}
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;
}
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];
}
}
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 dst[:N];
}
F32_DIG :: 6;
F32_EPSILON :: 1.192092896e-07;
F32_GUARD :: 0;
F32_MANT_DIG :: 24;
F32_MAX :: 3.402823466e+38;
F32_MAX_10_EXP :: 38;
F32_MAX_EXP :: 128;
F32_MIN :: 1.175494351e-38;
F32_MIN_10_EXP :: -37;
F32_MIN_EXP :: -125;
F32_NORMALIZE :: 0;
F32_RADIX :: 2;
F32_ROUNDS :: 1;
F64_DIG :: 15; // # of decimal digits of precision
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
F64_MANT_DIG :: 53; // # of bits in mantissa
F64_MAX :: 1.7976931348623158e+308; // max value
F64_MAX_10_EXP :: 308; // max decimal exponent
F64_MAX_EXP :: 1024; // max binary exponent
F64_MIN :: 2.2250738585072014e-308; // min positive value
F64_MIN_10_EXP :: -307; // min decimal exponent
F64_MIN_EXP :: -1021; // min binary exponent
F64_RADIX :: 2; // exponent radix
F64_ROUNDS :: 1; // addition rounding: near
-60
View File
@@ -1,60 +0,0 @@
Rand :: struct {
state: u64,
inc: u64,
}
init :: proc(r: ^Rand, seed: u64 = 8675309) {
r.state = 0;
r.inc = (seed << 1) | 1;
_random(r);
r.state += seed;
_random(r);
}
_random :: proc(r: ^Rand) -> u32 {
old_state := r.state;
r.state = old_state * 6364136223846793005 + (r.inc|1);
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
rot := u32(old_state >> 59);
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
}
uint32 :: proc(r: ^Rand) -> u32 { return _random(r); }
uint64 :: proc(r: ^Rand) -> 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_max :: proc(r: ^Rand, n: i32) -> i32 {
if n <= 0 do panic("Invalid argument to int31_max");
if n&(n-1) == 0 {
return int31(r) & (n-1);
}
max := i32((1<<31) - 1 - (1<<31)&u32(n));
v := int31(r);
for v > max {
v = int31(r);
}
return v % n;
}
int63_max :: proc(r: ^Rand, n: i64) -> i64 {
if n <= 0 do panic("Invalid argument to int63_max");
if n&(n-1) == 0 {
return int63(r) & (n-1);
}
max := i64((1<<63) - 1 - (1<<63)&u64(n));
v := int63(r);
for v > max {
v = int63(r);
}
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)); }
+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
}
+152
View File
@@ -0,0 +1,152 @@
package rand
Rand :: struct {
state: u64,
inc: u64,
}
@(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);
r.state += seed;
_random(r);
}
_random :: proc(r: ^Rand) -> u32 {
old_state := r.state;
r.state = old_state * 6364136223846793005 + (r.inc|1);
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27);
rot := u32(old_state >> 59);
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31));
}
uint32 :: proc(r: ^Rand = global_rand_ptr) -> u32 { return _random(r); }
uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
a := u64(_random(r));
b := u64(_random(r));
return (a<<32) | b;
}
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
a := u128(_random(r));
b := u128(_random(r));
c := u128(_random(r));
d := u128(_random(r));
return (a<<96) | (b<<64) | (c<<32) | d;
}
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); }
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
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);
}
max := i32((1<<31) - 1 - (1<<31)&u32(n));
v := int31(r);
for v > max {
v = int31(r);
}
return v % n;
}
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);
}
max := i64((1<<63) - 1 - (1<<63)&u64(n));
v := int63(r);
for v > max {
v = int63(r);
}
return v % n;
}
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
if n <= 0 do panic("Invalid argument to int63_max");
if n&(n-1) == 0 {
return int127(r) & (n-1);
}
max := i128((1<<63) - 1 - (1<<63)&u128(n));
v := int127(r);
for v > max {
v = int127(r);
}
return v % n;
}
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];
}
}
-314
View File
@@ -1,314 +0,0 @@
import "core:raw.odin"
foreign __llvm_core {
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
@(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];
set :: proc "contextless" (data: rawptr, value: i32, len: int) -> rawptr {
return __mem_set(data, value, len);
}
zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
return __mem_zero(data, len);
}
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
return __mem_copy(dst, src, len);
}
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
return __mem_copy_non_overlapping(dst, src, len);
}
compare :: proc "contextless" (a, b: []byte) -> int {
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
}
slice_ptr :: 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 {
s := transmute(raw.Slice)slice;
s.len *= size_of(T);
return transmute([]byte)s;
}
ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
assert(len >= 0);
return transmute([]byte)raw.Slice{ptr, len*size_of(T)};
}
kilobytes :: inline proc "contextless" (x: int) -> int do return (x) * 1024;
megabytes :: inline proc "contextless" (x: int) -> int do return kilobytes(x) * 1024;
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 {
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
assert(is_power_of_two(align));
a := uintptr(align);
p := uintptr(ptr);
modulo := p & (a-1);
if modulo != 0 do p += a - modulo;
return rawptr(p);
}
AllocationHeader :: struct {size: int};
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
header.size = size;
ptr := cast(^uint)(header+1);
n := cast(^uint)data - ptr;
for i in 0..n {
(ptr+i)^ = ~uint(0);
}
}
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
if data == nil do return nil;
p := cast(^uint)data;
for (p-1)^ == ~uint(0) do p = (p-1);
return cast(^AllocationHeader)(p-1);
}
Fixed_Byte_Buffer :: distinct [dynamic]byte;
make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
s := transmute(raw.Slice)backing;
d: raw.Dynamic_Array;
d.data = s.data;
d.len = 0;
d.cap = s.len;
d.allocator = nil_allocator();
return transmute(Fixed_Byte_Buffer)d;
}
// Custom allocators
Arena :: struct {
backing: Allocator,
memory: Fixed_Byte_Buffer,
temp_count: int,
}
ArenaTempMemory :: 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) -> Context {
c := context;
c.allocator = a;
return c;
}
destroy_arena :: proc(using a: ^Arena) {
if backing.procedure != nil {
context <- context_from_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 ArenaTempMemory if you want to free a block
case FreeAll:
(^raw.Slice)(&arena.memory).len = 0;
case Resize:
return default_resize_align(old_memory, old_size, size, alignment);
}
return nil;
}
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
tmp: ArenaTempMemory;
tmp.arena = a;
tmp.original_count = len(a.memory);
a.temp_count += 1;
return tmp;
}
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
assert(len(arena.memory) >= original_count);
assert(arena.temp_count > 0);
(^raw.Dynamic_Array)(&arena.memory).len = original_count;
arena.temp_count -= 1;
}
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
prev_pow2 :: proc(n: i64) -> i64 {
if n <= 0 do return 0;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return n - (n >> 1);
}
WORD_SIZE :: size_of(int);
MAX_ALIGN :: 2*align_of(rawptr); // TODO(bill): Should these constants be builtin constants?
switch info in type_info.variant {
case Type_Info_Named:
return align_of_type_info(info.base);
case Type_Info_Integer:
return type_info.align;
case Type_Info_Rune:
return type_info.align;
case Type_Info_Float:
return type_info.align;
case Type_Info_String:
return WORD_SIZE;
case Type_Info_Boolean:
return 1;
case Type_Info_Any:
return WORD_SIZE;
case Type_Info_Pointer:
return WORD_SIZE;
case Type_Info_Procedure:
return WORD_SIZE;
case Type_Info_Array:
return align_of_type_info(info.elem);
case Type_Info_Dynamic_Array:
return WORD_SIZE;
case Type_Info_Slice:
return WORD_SIZE;
case Type_Info_Tuple:
return type_info.align;
case Type_Info_Struct:
return type_info.align;
case Type_Info_Union:
return type_info.align;
case Type_Info_Enum:
return align_of_type_info(info.base);
case Type_Info_Map:
return align_of_type_info(info.generated_struct);
}
return 0;
}
align_formula :: proc(size, align: int) -> int {
result := size + align-1;
return result - result%align;
}
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
WORD_SIZE :: size_of(int);
switch info in type_info.variant {
case Type_Info_Named:
return size_of_type_info(info.base);
case Type_Info_Integer:
return type_info.size;
case Type_Info_Rune:
return type_info.size;
case Type_Info_Float:
return type_info.size;
case Type_Info_String:
return 2*WORD_SIZE;
case Type_Info_Boolean:
return 1;
case Type_Info_Any:
return 2*WORD_SIZE;
case Type_Info_Pointer:
return WORD_SIZE;
case Type_Info_Procedure:
return WORD_SIZE;
case Type_Info_Array:
count := info.count;
if count == 0 do return 0;
size := size_of_type_info(info.elem);
align := align_of_type_info(info.elem);
alignment := align_formula(size, align);
return alignment*(count-1) + size;
case Type_Info_Dynamic_Array:
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
case Type_Info_Slice:
return 2*WORD_SIZE;
case Type_Info_Struct:
return type_info.size;
case Type_Info_Union:
return type_info.size;
case Type_Info_Enum:
return size_of_type_info(info.base);
case Type_Info_Map:
return size_of_type_info(info.generated_struct);
}
return 0;
}
+152
View File
@@ -0,0 +1,152 @@
package mem
import "core:runtime"
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
Allocator_Mode :: enum byte {
Alloc,
Free,
Free_All,
Resize,
}
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
Allocator :: struct {
procedure: Allocator_Proc,
data: rawptr,
}
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if size == 0 do return nil;
if allocator.procedure == nil do return nil;
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
}
free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
if ptr == nil do return;
if allocator.procedure == nil do return;
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, 0, loc);
}
free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, 0, loc);
}
}
resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
if allocator.procedure == nil {
return nil;
}
if new_size == 0 {
free(ptr, allocator, loc);
return nil;
} else if ptr == nil {
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, 0, loc);
}
return allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
}
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
free(raw_data(str), allocator, loc);
}
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
free((^byte)(str), allocator, loc);
}
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
free(raw_data(array), array.allocator, loc);
}
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
free(raw_data(array), allocator, loc);
}
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
raw := transmute(Raw_Map)m;
delete_slice(raw.hashes);
free(raw.entries.data, raw.entries.allocator, loc);
}
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 {
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil do ptr^ = T{};
return ptr;
}
new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #caller_location) -> ^T {
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
if ptr != nil do ptr^ = data;
return ptr;
}
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_slice_error_loc(loc, len);
data := alloc(size_of(E)*len, align_of(E), allocator, loc);
s := Raw_Slice{data, len};
return transmute(T)s;
}
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> T {
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc);
}
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
return make_dynamic_array_len_cap(T, len, len, allocator, loc);
}
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, auto_cast cap: int, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_dynamic_array_error_loc(loc, len, cap);
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
s := Raw_Dynamic_Array{data, len, cap, allocator};
return transmute(T)s;
}
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
runtime.make_map_expr_error_loc(loc, cap);
context.allocator = allocator;
m: T;
reserve_map(&m, cap);
return m;
}
make :: proc{
make_slice,
make_dynamic_array,
make_dynamic_array_len,
make_dynamic_array_len_cap,
make_map,
};
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
if old_memory == nil do return alloc(new_size, alignment, allocator, loc);
if new_size == 0 {
free(old_memory, allocator, loc);
return nil;
}
if new_size == old_size do return old_memory;
new_memory := alloc(new_size, alignment, allocator, loc);
if new_memory == nil do return nil;
copy(new_memory, old_memory, min(old_size, new_size));;
free(old_memory, allocator, loc);
return new_memory;
}
+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);
}
+230
View File
@@ -0,0 +1,230 @@
package mem
import "core:runtime"
foreign _ {
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
@(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};
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
if data == nil do return nil;
if len < 0 do return data;
foreign _ {
when size_of(rawptr) == 8 {
@(link_name="llvm.memset.p0i8.i64")
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
} else {
@(link_name="llvm.memset.p0i8.i32")
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
}
}
llvm_memset(data, byte(value), len, 1, false);
return data;
}
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);
}
}
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
return runtime.mem_copy(dst, src, len);
}
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
return runtime.mem_copy_non_overlapping(dst, src, len);
}
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 #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;
}
compare_ptrs :: inline proc "contextless" (a, b: rawptr, n: int) -> int {
return compare_byte_ptrs((^byte)(a), (^byte)(b), n);
}
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 :: inline proc "contextless" (a, b: $P/^$T) -> int {
return (int(uintptr(a)) - int(uintptr(b)))/size_of(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 :: 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 :: inline proc(backing: $T/[]$E) -> [dynamic]E {
s := transmute(Raw_Slice)backing;
d := Raw_Dynamic_Array{
data = s.data,
len = 0,
cap = s.len,
allocator = nil_allocator(),
};
return transmute([dynamic]E)d;
}
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 :: 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};
}
kilobytes :: inline proc "contextless" (x: int) -> int do return (x) * 1024;
megabytes :: inline proc "contextless" (x: int) -> int do return kilobytes(x) * 1024;
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 :: inline proc(x: uintptr) -> bool {
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
assert(is_power_of_two(align));
a := uintptr(align);
p := uintptr(ptr);
modulo := p & (a-1);
if modulo != 0 do p += a - modulo;
return rawptr(p);
}
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align));
a := uintptr(align);
p := uintptr(ptr);
modulo := p & (a-1);
if modulo != 0 do p += a - modulo;
return uintptr(p);
}
align_forward_int :: inline proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
}
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;
make_fixed_byte_buffer :: proc(backing: []byte) -> Fixed_Byte_Buffer {
s := transmute(Raw_Slice)backing;
d: Raw_Dynamic_Array;
d.data = s.data;
d.len = 0;
d.cap = s.len;
d.allocator = nil_allocator();
return transmute(Fixed_Byte_Buffer)d;
}
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);
}
+57
View File
@@ -0,0 +1,57 @@
package mem
Raw_Any :: struct {
data: rawptr,
id: typeid,
}
Raw_String :: struct {
data: ^byte,
len: int,
}
Raw_Cstring :: struct {
data: ^byte,
}
Raw_Slice :: struct {
data: rawptr,
len: int,
}
Raw_Dynamic_Array :: struct {
data: rawptr,
len: int,
cap: int,
allocator: Allocator,
}
Raw_Map :: struct {
hashes: []int,
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 (transmute(Raw_String)s).data;
}
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
return cast(^E)(transmute(Raw_Slice)a).data;
}
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(transmute(Raw_Dynamic_Array)a).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};
}
-179
View File
@@ -1,179 +0,0 @@
when ODIN_OS == "windows" {
foreign import lib "system:opengl32.lib"
import win32 "core:sys/windows.odin"
import "core:sys/wgl.odin"
} else when ODIN_OS == "linux" {
foreign import lib "system:gl"
}
export "core:opengl_constants.odin"
_ := compile_assert(ODIN_OS != "osx");
@(default_calling_convention="c", link_prefix="gl")
foreign lib {
Clear :: proc(mask: u32) ---;
ClearColor :: proc(r, g, b, a: f32) ---;
Begin :: proc(mode: i32) ---;
End :: proc() ---;
Finish :: proc() ---;
BlendFunc :: proc(sfactor, dfactor: i32) ---;
Enable :: proc(cap: i32) ---;
Disable :: proc(cap: i32) ---;
GenTextures :: proc(count: i32, result: ^u32) ---;
DeleteTextures :: proc(count: i32, result: ^u32) ---;
TexParameteri :: proc(target, pname, param: i32) ---;
TexParameterf :: proc(target: i32, pname: i32, param: f32) ---;
BindTexture :: proc(target: i32, texture: u32) ---;
LoadIdentity :: proc() ---;
Viewport :: proc(x, y, width, height: i32) ---;
Ortho :: proc(left, right, bottom, top, near, far: f64) ---;
Color3f :: proc(r, g, b: f32) ---;
Vertex3f :: proc(x, y, z: f32) ---;
GetError :: proc() -> i32 ---;
GetString :: proc(name: i32) -> ^u8 ---;
GetIntegerv :: proc(name: i32, v: ^i32) ---;
TexCoord2f :: proc(x, y: f32) ---;
TexImage2D :: proc(target, level, internal_format: i32,
width, height, border: i32,
format, type_: i32, pixels: rawptr) ---;
}
_string_data :: inline proc(s: string) -> ^u8 do return &s[0];
_libgl := win32.load_library_a(_string_data("opengl32.dll\x00"));
get_gl_proc_address :: proc(name: string) -> rawptr {
if name[len(name)-1] == 0 {
name = name[..len(name)-1];
}
// NOTE(bill): null terminated
assert((&name[0] + len(name))^ == 0);
res := wgl.get_gl_proc_address(&name[0]);
if res == nil {
res = win32.get_proc_address(_libgl, &name[0]);
}
return rawptr(res);
}
// Procedures
GenBuffers: proc "c" (count: i32, buffers: ^u32);
GenVertexArrays: proc "c" (count: i32, buffers: ^u32);
GenSamplers: proc "c" (count: i32, buffers: ^u32);
DeleteBuffers: proc "c" (count: i32, buffers: ^u32);
BindBuffer: proc "c" (target: i32, buffer: u32);
BindVertexArray: proc "c" (buffer: u32);
DeleteVertexArrays: proc "c" (count: i32, arrays: ^u32);
BindSampler: proc "c" (position: i32, sampler: u32);
BufferData: proc "c" (target: i32, size: int, data: rawptr, usage: i32);
BufferSubData: proc "c" (target: i32, offset, size: int, data: rawptr);
DrawArrays: proc "c" (mode, first: i32, count: u32);
DrawElements: proc "c" (mode: i32, count: u32, type_: i32, indices: rawptr);
MapBuffer: proc "c" (target, access: i32) -> rawptr;
UnmapBuffer: proc "c" (target: i32);
VertexAttribPointer: proc "c" (index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr);
EnableVertexAttribArray: proc "c" (index: u32);
CreateShader: proc "c" (shader_type: i32) -> u32;
ShaderSource: proc "c" (shader: u32, count: u32, str: ^^u8, length: ^i32);
CompileShader: proc "c" (shader: u32);
CreateProgram: proc "c" () -> u32;
AttachShader: proc "c" (program, shader: u32);
DetachShader: proc "c" (program, shader: u32);
DeleteShader: proc "c" (shader: u32);
LinkProgram: proc "c" (program: u32);
UseProgram: proc "c" (program: u32);
DeleteProgram: proc "c" (program: u32);
GetShaderiv: proc "c" (shader: u32, pname: i32, params: ^i32);
GetProgramiv: proc "c" (program: u32, pname: i32, params: ^i32);
GetShaderInfoLog: proc "c" (shader: u32, max_length: u32, length: ^u32, info_long: ^u8);
GetProgramInfoLog: proc "c" (program: u32, max_length: u32, length: ^u32, info_long: ^u8);
ActiveTexture: proc "c" (texture: i32);
GenerateMipmap: proc "c" (target: i32);
SamplerParameteri: proc "c" (sampler: u32, pname: i32, param: i32);
SamplerParameterf: proc "c" (sampler: u32, pname: i32, param: f32);
SamplerParameteriv: proc "c" (sampler: u32, pname: i32, params: ^i32);
SamplerParameterfv: proc "c" (sampler: u32, pname: i32, params: ^f32);
SamplerParameterIiv: proc "c" (sampler: u32, pname: i32, params: ^i32);
SamplerParameterIuiv: proc "c" (sampler: u32, pname: i32, params: ^u32);
Uniform1i: proc "c" (loc: i32, v0: i32);
Uniform2i: proc "c" (loc: i32, v0, v1: i32);
Uniform3i: proc "c" (loc: i32, v0, v1, v2: i32);
Uniform4i: proc "c" (loc: i32, v0, v1, v2, v3: i32);
Uniform1f: proc "c" (loc: i32, v0: f32);
Uniform2f: proc "c" (loc: i32, v0, v1: f32);
Uniform3f: proc "c" (loc: i32, v0, v1, v2: f32);
Uniform4f: proc "c" (loc: i32, v0, v1, v2, v3: f32);
UniformMatrix4fv: proc "c" (loc: i32, count: u32, transpose: i32, value: ^f32);
GetUniformLocation: proc "c" (program: u32, name: ^u8) -> i32;
init :: proc() {
set_proc_address :: proc(p: rawptr, name: string) {
x := cast(^rawptr)p;
x^ = get_gl_proc_address(name);
}
set_proc_address(&GenBuffers, "glGenBuffers\x00");
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
set_proc_address(&GenSamplers, "glGenSamplers\x00");
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
set_proc_address(&BindBuffer, "glBindBuffer\x00");
set_proc_address(&BindSampler, "glBindSampler\x00");
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
set_proc_address(&DeleteVertexArrays, "glDeleteVertexArrays\x00");
set_proc_address(&BufferData, "glBufferData\x00");
set_proc_address(&BufferSubData, "glBufferSubData\x00");
set_proc_address(&DrawArrays, "glDrawArrays\x00");
set_proc_address(&DrawElements, "glDrawElements\x00");
set_proc_address(&MapBuffer, "glMapBuffer\x00");
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
set_proc_address(&CreateShader, "glCreateShader\x00");
set_proc_address(&ShaderSource, "glShaderSource\x00");
set_proc_address(&CompileShader, "glCompileShader\x00");
set_proc_address(&CreateProgram, "glCreateProgram\x00");
set_proc_address(&AttachShader, "glAttachShader\x00");
set_proc_address(&DetachShader, "glDetachShader\x00");
set_proc_address(&DeleteShader, "glDeleteShader\x00");
set_proc_address(&LinkProgram, "glLinkProgram\x00");
set_proc_address(&UseProgram, "glUseProgram\x00");
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
set_proc_address(&Uniform1i, "glUniform1i\x00");
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
}
File diff suppressed because it is too large Load Diff
-67
View File
@@ -1,67 +0,0 @@
when ODIN_OS == "windows" do export "core:os_windows.odin";
when ODIN_OS == "osx" do export "core:os_x.odin";
when ODIN_OS == "linux" do export "core:os_linux.odin";
when ODIN_OS == "essence" do export "core:os_essence.odin";
import "mem.odin";
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, cast([]byte)str);
}
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
return write(fd, []byte{b});
}
read_entire_file :: proc(name: string) -> (data: []byte, success: bool) {
fd, err := open(name, O_RDONLY, 0);
if err != 0 {
return nil, false;
}
defer close(fd);
length: i64;
if length, err = file_size(fd); err != 0 {
return nil, false;
}
if length <= 0 {
return nil, true;
}
data = make([]byte, int(length));
if data == nil {
return nil, false;
}
bytes_read, read_err := read(fd, data);
if read_err != 0 {
free(data);
return nil, false;
}
return data[0..bytes_read], true;
}
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
flags: int = O_WRONLY|O_CREATE;
if truncate {
flags |= O_TRUNC;
}
fd, err := open(name, flags, 0);
if err != 0 {
return false;
}
defer close(fd);
_, write_err := write(fd, data);
return write_err == 0;
}
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
return write(fd, mem.slice_ptr(cast(^byte)data, len));
}
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
return read(fd, mem.slice_ptr(cast(^byte)data, len));
}
+154
View File
@@ -0,0 +1,154 @@
package os
import "core:mem"
import "core:strconv"
import "core:unicode/utf8"
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, cast([]byte)str);
}
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
return write(fd, []byte{b});
}
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
if r < utf8.RUNE_SELF {
return write_byte(fd, byte(r));
}
b, n := utf8.encode_rune(r);
return write(fd, b[:n]);
}
write_encoded_rune :: proc(fd: Handle, r: rune) {
write_byte(fd, '\'');
switch r {
case '\a': write_string(fd, "\\a");
case '\b': write_string(fd, "\\b");
case '\e': write_string(fd, "\\e");
case '\f': write_string(fd, "\\f");
case '\n': write_string(fd, "\\n");
case '\r': write_string(fd, "\\r");
case '\t': write_string(fd, "\\t");
case '\v': write_string(fd, "\\v");
case:
if r < 32 {
write_string(fd, "\\x");
b: [2]byte;
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil);
switch len(s) {
case 0: write_string(fd, "00");
case 1: write_rune(fd, '0');
case 2: write_string(fd, s);
}
} else {
write_rune(fd, r);
}
}
write_byte(fd, '\'');
}
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 {
return nil, false;
}
defer close(fd);
length: i64;
if length, err = file_size(fd); err != 0 {
return nil, false;
}
if length <= 0 {
return nil, true;
}
data = make([]byte, int(length));
if data == nil {
return nil, false;
}
bytes_read, read_err := read(fd, data);
if read_err != 0 {
delete(data);
return nil, false;
}
return data[0:bytes_read], true;
}
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
flags: int = O_WRONLY|O_CREATE;
if truncate {
flags |= O_TRUNC;
}
fd, err := open(name, flags, 0);
if err != 0 {
return false;
}
defer close(fd);
_, write_err := write(fd, data);
return write_err == 0;
}
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
s := transmute([]byte)mem.Raw_Slice{data, len};
return write(fd, s);
}
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
s := transmute([]byte)mem.Raw_Slice{data, len};
return read(fd, s);
}
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 {
switch mode {
case .Alloc:
return heap_alloc(size);
case .Free:
heap_free(old_memory);
return nil;
case .Free_All:
// NOTE(bill): Does nothing
case .Resize:
if old_memory == nil {
return heap_alloc(size);
}
ptr := heap_resize(old_memory, size);
assert(ptr != nil);
return ptr;
}
return nil;
}
heap_allocator :: proc() -> mem.Allocator {
return mem.Allocator{
procedure = heap_allocator_proc,
data = nil,
};
}
+4
View File
@@ -0,0 +1,4 @@
package os;
ARCH :: "x86";
ENDIAN :: "little";
+4
View File
@@ -0,0 +1,4 @@
package os;
ARCH :: "amd64";
ENDIAN :: "little";
+65 -63
View File
@@ -1,13 +1,18 @@
package os
foreign import dl "system:dl"
foreign import libc "system:c"
import "core:strings.odin"
import "core:mem.odin"
import "core:runtime"
import "core:strings"
OS :: "darwin";
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct int;
INVALID_HANDLE :: ~Handle(0);
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
@@ -69,44 +74,44 @@ Stat :: struct {
blocks: i64, // Number of blocks allocated for the file
block_size: i32, // Optimal blocksize for I/O
flags: u32, // User-defined flags for the file
gen_num: u32, // File generation number ...?
gen_num: u32, // File generation number ..?
_spare: i32, // RESERVED
_reserve1,
_reserve2: i64, // RESERVED
};
// 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;
@@ -122,47 +127,41 @@ X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
foreign libc {
@(link_name="open") _unix_open :: proc(path: ^byte, 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 ---;
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---;
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
@(link_name="stat") _unix_stat :: proc(path: ^byte, stat: ^Stat) -> int ---;
@(link_name="access") _unix_access :: proc(path: ^byte, mask: int) -> int ---;
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> 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(^byte) -> ^byte ---;
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
@(link_name="exit") _unix_exit :: proc(status: int) ---;
}
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: ^byte, flags: int) -> rawptr ---;
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: ^byte) -> rawptr ---;
@(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() -> ^byte ---;
@(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_c_string(path);
defer free(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);
}
@@ -216,17 +215,21 @@ 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_c_string(path);
defer free(cstr);
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_c_string(path);
defer free(cstr);
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
return _unix_access(cstr, mask) == 0;
}
@@ -242,35 +245,34 @@ heap_free :: inline proc(ptr: rawptr) {
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
defer free(path_str);
path_str := strings.clone_to_cstring(name);
defer delete(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
return "", false;
}
return strings.to_odin_string(cstr), true;
return string(cstr), true;
}
exit :: inline proc(code: int) {
exit :: inline proc(code: int) -> ! {
_unix_exit(code);
}
current_thread_id :: proc() -> int {
current_thread_id :: proc "contextless" () -> int {
// return int(_unix_gettid());
return 0;
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.new_c_string(filename);
defer free(cstr);
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_c_string(symbol);
defer free(cstr);
cstr := strings.clone_to_cstring(symbol);
defer delete(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
}
@@ -279,14 +281,14 @@ dlclose :: inline proc(handle: rawptr) -> bool {
return _unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(_unix_dlerror());
return string(_unix_dlerror());
}
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, __argc__);
for i in 0..__argc__ {
args[i] = strings.to_odin_string((__argv__+i)^);
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
res[i] = string(arg);
}
return args;
return res;
}
File diff suppressed because it is too large Load Diff
+400
View File
@@ -0,0 +1,400 @@
package os
foreign import dl "system:dl"
foreign import libc "system:c"
import "core:runtime"
import "core:strings"
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;
O_RDWR :: 0x00002;
O_CREATE :: 0x00040;
O_EXCL :: 0x00080;
O_NOCTTY :: 0x00100;
O_TRUNC :: 0x00200;
O_NONBLOCK :: 0x00800;
O_APPEND :: 0x00400;
O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
SEEK_DATA :: 3;
SEEK_HOLE :: 4;
SEEK_MAX :: SEEK_HOLE;
// NOTE(zangent): These are OS specific!
// Do not mix these up!
RTLD_LAZY :: 0x001;
RTLD_NOW :: 0x002;
RTLD_BINDING_MASK :: 0x3;
RTLD_GLOBAL :: 0x100;
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments();
_File_Time :: struct {
seconds: i64,
nanoseconds: i64,
}
Stat :: struct {
device_id: u64, // ID of device containing file
serial: u64, // File serial number
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
_padding: i32, // 32 bits of padding
rdev: u64, // Device ID, if device
size: i64, // Size of the file, in bytes
block_size: i64, // Optimal bllocksize for I/O
blocks: i64, // Number of 512-byte blocks allocated
last_access: _File_Time, // Time of last access
modified: _File_Time, // Time of last modification
status_change: _File_Time, // Time of last status change
_reserve1,
_reserve2,
_reserve3: i64,
};
// File type
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 :: 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 :: 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 :: 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 :: 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;
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
F_OK :: 0; // Test for file existance
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="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
@(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="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 ---;
}
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 INVALID_HANDLE, Errno(get_last_error());
}
return handle, ERROR_NONE;
}
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) {
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) {
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));
if res == -1 {
return -1, Errno(get_last_error());
}
return res, ERROR_NONE;
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
s, err := fstat(fd);
if err != ERROR_NONE {
return -1, err;
}
return s.size, ERROR_NONE;
}
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0;
stdout: Handle = 1;
stderr: Handle = 2;
/* TODO(zangent): Implement these!
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;
}
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;
result := _unix_stat(cstr, &s);
if result == -1 {
return s, Errno(get_last_error());
}
return s, ERROR_NONE;
}
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);
result := _unix_access(cstr, mask);
if result == -1 {
return false, Errno(get_last_error());
}
return true, ERROR_NONE;
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size >= 0);
return _unix_calloc(1, size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return _unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
_unix_free(ptr);
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.clone_to_cstring(name);
defer delete(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
return "", false;
}
return string(cstr), true;
}
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 syscall(SYS_GETTID);
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
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.clone_to_cstring(symbol);
defer delete(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
}
dlclose :: inline proc(handle: rawptr) -> bool {
assert(handle != nil);
return _unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return string(_unix_dlerror());
}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__));
for arg, i in runtime.args__ {
res[i] = string(arg);
}
return res;
}
@@ -1,5 +1,9 @@
import win32 "core:sys/windows.odin"
import "core:mem.odin"
// +build windows
package os
import "core:sys/win32"
OS :: "windows";
Handle :: distinct uintptr;
File_Time :: distinct u64;
@@ -28,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;
@@ -56,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;
@@ -94,19 +103,19 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errn
case:
create_mode = win32.OPEN_EXISTING;
}
buf: [300]byte;
copy(buf[..], cast([]byte)path);
handle := Handle(win32.create_file_a(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
wide_path := win32.utf8_to_wstring(path);
handle := Handle(win32.create_file_w(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
if handle != INVALID_HANDLE do return handle, ERROR_NONE;
err := Errno(win32.get_last_error());
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;
}
@@ -202,30 +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;
buf: [1024]byte;
assert(len(buf) > len(name));
copy(buf[..], cast([]byte)name);
if win32.get_file_attributes_ex_a(&buf[0], win32.GetFileExInfoStandard, &data) {
last_write_time = data.last_write_time;
wide_path := win32.utf8_to_wstring(name);
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;
}
@@ -248,67 +254,72 @@ heap_free :: proc(ptr: rawptr) {
}
exit :: proc(code: int) {
exit :: proc(code: int) -> ! {
win32.exit_process(u32(code));
}
current_thread_id :: proc() -> int {
current_thread_id :: proc "contextless" () -> int {
return int(win32.get_current_thread_id());
}
_alloc_command_line_arguments :: proc() -> []string {
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
wstr_len := 0;
for (wstr+wstr_len)^ != 0 do wstr_len += 1;
len := 2*wstr_len-1;
buf := make([]byte, len+1);
str := mem.slice_ptr(wstr, wstr_len+1);
i, j := 0, 0;
for str[j] != 0 {
switch {
case str[j] < 0x80:
if i+1 > len do return "";
buf[i] = byte(str[j]); i += 1;
j += 1;
case str[j] < 0x800:
if i+2 > len do return "";
buf[i] = byte(0xc0 + (str[j]>>6)); i += 1;
buf[i] = byte(0x80 + (str[j]&0x3f)); i += 1;
j += 1;
case 0xd800 <= str[j] && str[j] < 0xdc00:
if i+4 > len do return "";
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
buf[i] = byte(0xf0 + (c >> 18)); i += 1;
buf[i] = byte(0x80 + ((c >> 12) & 0x3f)); i += 1;
buf[i] = byte(0x80 + ((c >> 6) & 0x3f)); i += 1;
buf[i] = byte(0x80 + ((c ) & 0x3f)); i += 1;
j += 2;
case 0xdc00 <= str[j] && str[j] < 0xe000:
return "";
case:
if i+3 > len do return "";
buf[i] = 0xe0 + byte (str[j] >> 12); i += 1;
buf[i] = 0x80 + byte((str[j] >> 6) & 0x3f); i += 1;
buf[i] = 0x80 + byte((str[j] ) & 0x3f); i += 1;
j += 1;
}
}
return string(buf[..i]);
}
arg_count: i32;
arg_list_ptr := win32.command_line_to_argv_w(win32.get_command_line_w(), &arg_count);
arg_list := make([]string, int(arg_count));
for _, i in arg_list do arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
for _, i in arg_list {
wc_str := (^win32.Wstring)(uintptr(arg_list_ptr) + size_of(win32.Wstring)*uintptr(i))^;
olen := win32.wide_char_to_multi_byte(win32.CP_UTF8, 0, wc_str, -1,
nil, 0, nil, nil);
buf := make([]byte, int(olen));
n := win32.wide_char_to_multi_byte(win32.CP_UTF8, 0, wc_str, -1,
cstring(&buf[0]), olen, nil, nil);
if n > 0 {
n -= 1;
}
arg_list[i] = string(buf[:n]);
}
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);
}
-113
View File
@@ -1,113 +0,0 @@
foreign import api "system:api"
Handle :: distinct int;
Errno :: distinct int;
O_RDONLY :: 1;
O_WRONLY :: 2;
O_CREATE :: 4;
O_TRUNC :: 4;
OS_Node_Type :: enum i32 {
File = 0,
Directory = 1,
}
OS_Node_Information :: struct {
handle: Handle,
id: [16]byte,
ntype: OS_Node_Type,
size: i64,
position: i64,
}
foreign api {
@(link_name="OSHelloWorld") os_hello_world :: proc() ---;
@(link_name="OSPrintDirect") os_print_direct :: proc(string: ^byte, length: int) ---;
@(link_name="OSHeapAllocate") os_heap_allocate :: proc(bytes: int, zero: bool) -> rawptr ---;
@(link_name="OSHeapFree") os_heap_free :: proc(address: rawptr) ---;
@(link_name="OSOpenNode") os_open_node :: proc(path: ^byte, path_length: int, flags: u64, information: ^OS_Node_Information) -> Errno ---;
@(link_name="OSResizeFile") os_resize_file :: proc(handle: Handle, new_size: u64) -> Errno ---;
@(link_name="OSCloseHandle") os_close_handle :: proc(handle: Handle) ---;
@(link_name="OSWriteFileSync") os_write_file_sync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---;
@(link_name="OSReadFileSync") os_read_file_sync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---;
@(link_name="OSInitialiseAPI") os_initialise_api :: proc() -> int ---;
@(link_name="OSTerminateProcess") os_terminate_process :: proc(handle: Handle) ---;
@(link_name="realloc") os_heap_reallocate :: proc(address: rawptr, size: int) -> rawptr ---;
}
stdin := Handle(-1); // Not implemented
stdout := Handle(0);
stderr := Handle(0);
current_thread_id :: proc() -> int {
// Not implemented
return -1;
}
heap_alloc :: proc(size: int) -> rawptr {
return os_heap_allocate(size, true);
}
heap_free :: proc(address: rawptr) {
os_heap_free(address);
}
heap_resize :: proc(address: rawptr, new_size: int) -> rawptr {
return os_heap_reallocate(address, new_size);
}
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
information := new(OS_Node_Information);
error := os_open_node(&path[0], len(path), u64(mode), information);
if error < -1 do return 0, 1;
information.position = 0;
if mode&O_TRUNC==O_TRUNC {
error := os_resize_file(information.handle, 0);
if error < -1 do return 0, 1;
}
return Handle(uintptr(information)), 0;
}
close :: proc(fd: Handle) {
information := (^OS_Node_Information)(uintptr(fd));
os_close_handle(information.handle);
free(information);
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
// Not (properly) implemented
information := cast(^OS_Node_Information)uintptr(fd);
return information.size,0;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if fd == 0 {
os_print_direct(&data[0], len(data));
return len(data), 0;
} else if fd == 1 {
assert(false);
return 0, 1;
}
information := (^OS_Node_Information)(uintptr(fd));
count := os_write_file_sync(information.handle, information.position, i64(len(data)), &data[0]);
if count < 0 do return 0, 1;
information.position += count;
return int(count), 0;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if (fd == 0 || fd == 1) {
assert(false);
return 0, 1;
}
information := (^OS_Node_Information)(uintptr(fd));
count := os_read_file_sync(information.handle, information.position, i64(len(data)), &data[0]);
if count < 0 do return 0, 1;
information.position += count;
return int(count), 0;
}
os_terminate_this_process :: proc() {
os_terminate_process(0x1001);
}
-278
View File
@@ -1,278 +0,0 @@
foreign import dl "system:dl"
foreign import libc "system:c"
import "core:strings.odin"
import "core:mem.odin"
Handle :: distinct i32;
File_Time :: distinct u64;
Errno :: distinct i32;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
O_CREATE :: 0x00040;
O_EXCL :: 0x00080;
O_NOCTTY :: 0x00100;
O_TRUNC :: 0x00200;
O_NONBLOCK :: 0x00800;
O_APPEND :: 0x00400;
O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
SEEK_DATA :: 3;
SEEK_HOLE :: 4;
SEEK_MAX :: SEEK_HOLE;
// NOTE(zangent): These are OS specific!
// Do not mix these up!
RTLD_LAZY :: 0x001;
RTLD_NOW :: 0x002;
RTLD_BINDING_MASK :: 0x3;
RTLD_GLOBAL :: 0x100;
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments();
_File_Time :: struct {
seconds: i64,
nanoseconds: i32,
reserved: i32,
}
// 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
mode: u32, // Mode of the file
uid: u32, // User ID of the file's owner
gid: u32, // Group ID of the file's group
_padding: i32, // 32 bits of padding
rdev: u64, // Device ID, if device
size: i64, // Size of the file, in bytes
block_size: i64, // Optimal bllocksize for I/O
blocks: i64, // Number of 512-byte blocks allocated
last_access: _File_Time, // Time of last access
modified: _File_Time, // Time of last modification
status_change: _File_Time, // Time of last status change
_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
// 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
// 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
// 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_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // 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;
S_ISDIR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
S_ISCHR :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
S_ISBLK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
S_ISFIFO :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
S_ISSOCK :: inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
F_OK :: 0; // Test for file existance
X_OK :: 1; // Test for execute permission
W_OK :: 2; // Test for write permission
R_OK :: 4; // Test for read permission
foreign libc {
@(link_name="open") _unix_open :: proc(path: ^byte, 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: ^byte, stat: ^Stat) -> i32 ---;
@(link_name="access") _unix_access :: proc(path: ^byte, mask: int) -> i32 ---;
@(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(^byte) -> ^byte ---;
@(link_name="exit") _unix_exit :: proc(status: int) ---;
}
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: ^byte, flags: int) -> rawptr ---;
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: ^byte) -> rawptr ---;
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
@(link_name="dlerror") _unix_dlerror :: proc() -> ^byte ---;
}
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_c_string(path);
handle := _unix_open(cstr, mode);
free(cstr);
if(handle == -1) {
return 0, 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);
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_read(fd, &data[0], len(data));
return sz, 0;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_write(fd, &data[0], len(data));
return sz, 0;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
res := _unix_seek(fd, offset, i32(whence));
return res, 0;
}
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;
}
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0;
stdout: Handle = 1;
stderr: Handle = 2;
/* TODO(zangent): Implement these!
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
stat :: inline proc(path: string) -> (Stat, int) {
cstr := strings.new_c_string(path);
defer free(cstr);
s: Stat;
ret_int := _unix_stat(cstr, &s);
return s, int(ret_int);
}
access :: inline proc(path: string, mask: int) -> bool {
cstr := strings.new_c_string(path);
defer free(cstr);
return _unix_access(cstr, mask) == 0;
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return _unix_calloc(1, size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return _unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
_unix_free(ptr);
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
defer free(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
return "", false;
}
return strings.to_odin_string(cstr), true;
}
exit :: proc(code: int) {
_unix_exit(code);
}
current_thread_id :: proc() -> int {
// return int(_unix_gettid());
return 0;
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.new_c_string(filename);
defer free(cstr);
handle := _unix_dlopen(cstr, flags);
return handle;
}
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
defer free(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
}
dlclose :: inline proc(handle: rawptr) -> bool {
assert(handle != nil);
return _unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(_unix_dlerror());
}
_alloc_command_line_arguments :: proc() -> []string {
args := make([]string, __argc__);
for i in 0..__argc__ {
args[i] = strings.to_odin_string((__argv__+i)^);
}
return args;
}
-45
View File
@@ -1,45 +0,0 @@
Any :: struct {
data: rawptr,
type_info: ^Type_Info,
}
String :: struct {
data: ^byte,
len: int,
}
Slice :: struct {
data: rawptr,
len: int,
}
Dynamic_Array :: struct {
data: rawptr,
len: int,
cap: int,
allocator: Allocator,
}
Map :: struct {
hashes: [dynamic]int,
entries: Dynamic_Array,
}
make_any :: inline proc(data: rawptr, type_info: ^Type_Info) -> any {
return transmute(any)Any{data, type_info};
}
string_data :: inline proc(s: $T/string) -> ^byte {
return (^String)(&s).data;
}
slice_data :: inline proc(a: $T/[]$E) -> ^E {
return cast(^E)(^Slice)(&a).data;
}
dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
return cast(^E)(^Dynamic_Array)(&a).data;
}
data :: proc[string_data, slice_data, dynamic_array_data];
+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;
}
+490
View File
@@ -0,0 +1,490 @@
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;
if (a == nil && b != nil) ||
(a != nil && b == nil) {
return false;
}
switch {
case a.size != b.size, a.align != b.align:
return false;
}
switch x in a.variant {
case rt.Type_Info_Named:
y, ok := b.variant.(rt.Type_Info_Named);
if !ok do return false;
return x.base == y.base;
case rt.Type_Info_Integer:
y, ok := b.variant.(rt.Type_Info_Integer);
if !ok do return false;
return x.signed == y.signed;
case rt.Type_Info_Rune:
_, ok := b.variant.(rt.Type_Info_Rune);
return ok;
case rt.Type_Info_Float:
_, ok := b.variant.(rt.Type_Info_Float);
return ok;
case rt.Type_Info_Complex:
_, ok := b.variant.(rt.Type_Info_Complex);
return ok;
case rt.Type_Info_String:
_, ok := b.variant.(rt.Type_Info_String);
return ok;
case rt.Type_Info_Boolean:
_, ok := b.variant.(rt.Type_Info_Boolean);
return ok;
case rt.Type_Info_Any:
_, ok := b.variant.(rt.Type_Info_Any);
return ok;
case rt.Type_Info_Pointer:
y, ok := b.variant.(rt.Type_Info_Pointer);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case rt.Type_Info_Procedure:
y, ok := b.variant.(rt.Type_Info_Procedure);
if !ok do return false;
switch {
case x.variadic != y.variadic,
x.convention != y.convention:
return false;
}
return are_types_identical(x.params, y.params) && are_types_identical(x.results, y.results);
case rt.Type_Info_Array:
y, ok := b.variant.(rt.Type_Info_Array);
if !ok do return false;
if x.count != y.count do return false;
return are_types_identical(x.elem, y.elem);
case rt.Type_Info_Dynamic_Array:
y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case rt.Type_Info_Slice:
y, ok := b.variant.(rt.Type_Info_Slice);
if !ok do return false;
return are_types_identical(x.elem, y.elem);
case rt.Type_Info_Tuple:
y, ok := b.variant.(rt.Type_Info_Tuple);
if !ok do return false;
if len(x.types) != len(y.types) do return false;
for _, i in x.types {
xt, yt := x.types[i], y.types[i];
if !are_types_identical(xt, yt) {
return false;
}
}
return true;
case rt.Type_Info_Struct:
y, ok := b.variant.(rt.Type_Info_Struct);
if !ok do return false;
switch {
case len(x.types) != len(y.types),
x.is_packed != y.is_packed,
x.is_raw_union != y.is_raw_union,
x.custom_align != y.custom_align:
return false;
}
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;
case rt.Type_Info_Union:
y, ok := b.variant.(rt.Type_Info_Union);
if !ok do return false;
if len(x.variants) != len(y.variants) do return false;
for _, i in x.variants {
xv, yv := x.variants[i], y.variants[i];
if !are_types_identical(xv, yv) do return false;
}
return true;
case rt.Type_Info_Enum:
// NOTE(bill): Should be handled above
return false;
case rt.Type_Info_Map:
y, ok := b.variant.(rt.Type_Info_Map);
if !ok do return false;
return are_types_identical(x.key, y.key) && are_types_identical(x.value, y.value);
case rt.Type_Info_Bit_Field:
y, ok := b.variant.(rt.Type_Info_Bit_Field);
if !ok do return false;
if len(x.names) != len(y.names) do return false;
for _, i in x.names {
xb, yb := x.bits[i], y.bits[i];
xo, yo := x.offsets[i], y.offsets[i];
xn, yn := x.names[i], y.names[i];
if xb != yb do return false;
if xo != yo do return false;
if xn != yn do return false;
}
return true;
case rt.Type_Info_Bit_Set:
y, ok := b.variant.(rt.Type_Info_Bit_Set);
if !ok do return false;
return x.elem == y.elem && x.lower == y.lower && x.upper == y.upper;
case rt.Type_Info_Opaque:
y, ok := b.variant.(rt.Type_Info_Opaque);
if !ok do return false;
return x.elem == y.elem;
}
return false;
}
is_signed :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
switch i in rt.type_info_base(info).variant {
case rt.Type_Info_Integer: return i.signed;
case rt.Type_Info_Float: return true;
}
return false;
}
is_integer :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Integer);
return ok;
}
is_rune :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Rune);
return ok;
}
is_float :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Float);
return ok;
}
is_complex :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Complex);
return ok;
}
is_any :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Any);
return ok;
}
is_string :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_String);
return ok;
}
is_boolean :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Boolean);
return ok;
}
is_pointer :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Pointer);
return ok;
}
is_procedure :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Procedure);
return ok;
}
is_array :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Array);
return ok;
}
is_dynamic_array :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Dynamic_Array);
return ok;
}
is_dynamic_map :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Map);
return ok;
}
is_slice :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Slice);
return ok;
}
is_tuple :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Tuple);
return ok;
}
is_struct :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
return ok && !s.is_raw_union;
}
is_raw_union :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
s, ok := rt.type_info_base(info).variant.(rt.Type_Info_Struct);
return ok && s.is_raw_union;
}
is_union :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Union);
return ok;
}
is_enum :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Enum);
return ok;
}
is_opaque :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
_, 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, ')');
}
}
}
File diff suppressed because it is too large Load Diff
+759
View File
@@ -0,0 +1,759 @@
package runtime
import "core:os"
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;
}
mem_copy_non_overlapping :: 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, 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;
}
i -= 1; a[i] = digits[u % b];
os.write(fd, a[i:]);
}
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);
for u >= b {
i -= 1; a[i] = digits[u % b];
u /= b;
}
i -= 1; a[i] = digits[u % b];
if neg {
i -= 1; a[i] = '-';
}
os.write(fd, a[i:]);
}
print_caller_location :: proc(fd: os.Handle, using loc: Source_Code_Location) {
os.write_string(fd, file_path);
os.write_byte(fd, '(');
print_u64(fd, u64(line));
os.write_byte(fd, ':');
print_u64(fd, u64(column));
os.write_byte(fd, ')');
}
print_typeid :: proc(fd: os.Handle, id: typeid) {
ti := type_info_of(id);
print_type(fd, ti);
}
print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
if ti == nil {
os.write_string(fd, "nil");
return;
}
switch info in ti.variant {
case Type_Info_Named:
os.write_string(fd, info.name);
case Type_Info_Integer:
switch ti.id {
case int: os.write_string(fd, "int");
case uint: os.write_string(fd, "uint");
case uintptr: os.write_string(fd, "uintptr");
case:
os.write_byte(fd, info.signed ? 'i' : 'u');
print_u64(fd, u64(8*ti.size));
}
case Type_Info_Rune:
os.write_string(fd, "rune");
case Type_Info_Float:
os.write_byte(fd, 'f');
print_u64(fd, u64(8*ti.size));
case Type_Info_Complex:
os.write_string(fd, "complex");
print_u64(fd, u64(8*ti.size));
case Type_Info_String:
os.write_string(fd, "string");
case Type_Info_Boolean:
switch ti.id {
case bool: os.write_string(fd, "bool");
case:
os.write_byte(fd, 'b');
print_u64(fd, u64(8*ti.size));
}
case Type_Info_Any:
os.write_string(fd, "any");
case Type_Info_Type_Id:
os.write_string(fd, "typeid");
case Type_Info_Pointer:
if info.elem == nil {
os.write_string(fd, "rawptr");
} else {
os.write_string(fd, "^");
print_type(fd, info.elem);
}
case Type_Info_Procedure:
os.write_string(fd, "proc");
if info.params == nil {
os.write_string(fd, "()");
} else {
t := info.params.variant.(Type_Info_Tuple);
os.write_string(fd, "(");
for t, i in t.types {
if i > 0 do os.write_string(fd, ", ");
print_type(fd, t);
}
os.write_string(fd, ")");
}
if info.results != nil {
os.write_string(fd, " -> ");
print_type(fd, info.results);
}
case Type_Info_Tuple:
count := len(info.names);
if count != 1 do os.write_string(fd, "(");
for name, i in info.names {
if i > 0 do os.write_string(fd, ", ");
t := info.types[i];
if len(name) > 0 {
os.write_string(fd, name);
os.write_string(fd, ": ");
}
print_type(fd, t);
}
if count != 1 do os.write_string(fd, ")");
case Type_Info_Array:
os.write_string(fd, "[");
print_u64(fd, u64(info.count));
os.write_string(fd, "]");
print_type(fd, info.elem);
case Type_Info_Dynamic_Array:
os.write_string(fd, "[dynamic]");
print_type(fd, info.elem);
case Type_Info_Slice:
os.write_string(fd, "[]");
print_type(fd, info.elem);
case Type_Info_Map:
os.write_string(fd, "map[");
print_type(fd, info.key);
os.write_byte(fd, ']');
print_type(fd, info.value);
case Type_Info_Struct:
os.write_string(fd, "struct ");
if info.is_packed do os.write_string(fd, "#packed ");
if info.is_raw_union do os.write_string(fd, "#raw_union ");
if info.custom_align {
os.write_string(fd, "#align ");
print_u64(fd, u64(ti.align));
os.write_byte(fd, ' ');
}
os.write_byte(fd, '{');
for name, i in info.names {
if i > 0 do os.write_string(fd, ", ");
os.write_string(fd, name);
os.write_string(fd, ": ");
print_type(fd, info.types[i]);
}
os.write_byte(fd, '}');
case Type_Info_Union:
os.write_string(fd, "union {");
for variant, i in info.variants {
if i > 0 do os.write_string(fd, ", ");
print_type(fd, variant);
}
os.write_string(fd, "}");
case Type_Info_Enum:
os.write_string(fd, "enum ");
print_type(fd, info.base);
os.write_string(fd, " {");
for name, i in info.names {
if i > 0 do os.write_string(fd, ", ");
os.write_string(fd, name);
}
os.write_string(fd, "}");
case Type_Info_Bit_Field:
os.write_string(fd, "bit_field ");
if ti.align != 1 {
os.write_string(fd, "#align ");
print_u64(fd, u64(ti.align));
os.write_byte(fd, ' ');
}
os.write_string(fd, " {");
for name, i in info.names {
if i > 0 do os.write_string(fd, ", ");
os.write_string(fd, name);
os.write_string(fd, ": ");
print_u64(fd, u64(info.bits[i]));
}
os.write_string(fd, "}");
case Type_Info_Bit_Set:
os.write_string(fd, "bit_set[");
switch elem in type_info_base(info.elem).variant {
case Type_Info_Enum:
print_type(fd, info.elem);
case Type_Info_Rune:
os.write_encoded_rune(fd, rune(info.lower));
os.write_string(fd, "..");
os.write_encoded_rune(fd, rune(info.upper));
case:
print_i64(fd, info.lower);
os.write_string(fd, "..");
print_i64(fd, info.upper);
}
if info.underlying != nil {
os.write_string(fd, "; ");
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;
case len(a) == 0: return true;
case &a[0] == &b[0]: return true;
}
return string_cmp(a, b) == 0;
}
string_cmp :: proc "contextless" (a, b: string) -> int {
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); }
string_lt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) < 0; }
string_gt :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) > 0; }
string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) <= 0; }
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
cstring_len :: proc "contextless" (s: cstring) -> int {
p0 := uintptr((^byte)(s));
p := p0;
for p != 0 && (^byte)(p)^ != 0 {
p += 1;
}
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)Raw_String{ptr, n};
}
complex64_eq :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
complex64_ne :: inline proc "contextless" (a, b: complex64) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
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;
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_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, "", 0});
os.write_string(fd, " Invalid slice indices: ");
print_i64(fd, i64(lo));
os.write_string(fd, ":");
print_i64(fd, i64(hi));
os.write_string(fd, ":");
print_i64(fd, i64(len));
os.write_byte(fd, '\n');
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;
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;
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) {
// 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_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) {
dynamic_array_expr_error(file_path, int(line), int(column), low, high, max);
}
make_slice_error_loc :: inline proc "contextless" (loc := #caller_location, len: int) {
if 0 <= len do return;
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;
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" (loc := #caller_location, cap: int) {
if 0 <= cap do return;
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);
}
@(default_calling_convention = "c")
foreign {
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
}
abs_f32 :: inline proc "contextless" (x: f32) -> f32 {
foreign {
@(link_name="llvm.fabs.f32") _abs :: proc "c" (x: f32) -> f32 ---
}
return _abs(x);
}
abs_f64 :: inline proc "contextless" (x: f64) -> f64 {
foreign {
@(link_name="llvm.fabs.f64") _abs :: proc "c" (x: f64) -> f64 ---
}
return _abs(x);
}
min_f32 :: proc(a, b: f32) -> f32 {
foreign {
@(link_name="llvm.minnum.f32") _min :: proc "c" (a, b: f32) -> f32 ---
}
return _min(a, b);
}
min_f64 :: proc(a, b: f64) -> f64 {
foreign {
@(link_name="llvm.minnum.f64") _min :: proc "c" (a, b: f64) -> f64 ---
}
return _min(a, b);
}
max_f32 :: proc(a, b: f32) -> f32 {
foreign {
@(link_name="llvm.maxnum.f32") _max :: proc "c" (a, b: f32) -> f32 ---
}
return _max(a, b);
}
max_f64 :: proc(a, b: f64) -> f64 {
foreign {
@(link_name="llvm.maxnum.f64") _max :: proc "c" (a, b: f64) -> f64 ---
}
return _max(a, b);
}
abs_complex64 :: inline proc "contextless" (x: complex64) -> f32 {
r, i := real(x), imag(x);
return _sqrt_f32(r*r + i*i);
}
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 "contextless" (n, m: complex64) -> complex64 {
e, f: f32;
if abs(real(m)) >= abs(imag(m)) {
ratio := imag(m) / real(m);
denom := real(m) + ratio*imag(m);
e = (real(n) + imag(n)*ratio) / denom;
f = (imag(n) - real(n)*ratio) / denom;
} else {
ratio := real(m) / imag(m);
denom := imag(m) + ratio*real(m);
e = (real(n)*ratio + imag(n)) / denom;
f = (imag(n)*ratio - real(n)) / denom;
}
return complex(e, f);
}
quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
e, f: f64;
if abs(real(m)) >= abs(imag(m)) {
ratio := imag(m) / real(m);
denom := real(m) + ratio*imag(m);
e = (real(n) + imag(n)*ratio) / denom;
f = (imag(n) - real(n)*ratio) / denom;
} else {
ratio := real(m) / imag(m);
denom := imag(m) + ratio*real(m);
e = (real(n)*ratio + imag(n)) / denom;
f = (imag(n)*ratio - real(n)) / denom;
}
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);
}
+50
View File
@@ -0,0 +1,50 @@
package runtime
foreign import kernel32 "system:Kernel32.lib"
@(link_name="memcpy")
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
foreign kernel32 {
RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) ---
}
RtlCopyMemory(dst, src, len);
return dst;
}
@(link_name="memmove")
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
foreign kernel32 {
RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) ---
}
RtlMoveMemory(dst, src, len);
return dst;
}
@(link_name="memset")
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
foreign kernel32 {
RtlFillMemory :: proc "c" (dst: rawptr, len: int, fill: byte) ---
}
RtlFillMemory(ptr, len, byte(val));
return ptr;
}
// @(link_name="memcmp")
// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 {
// if dst == nil || src == nil {
// return 0;
// }
// if dst == src {
// return 0;
// }
// d, s := uintptr(dst), uintptr(src);
// n := uintptr(len);
// for i := uintptr(0); i < n; i += 1 {
// x, y := (^byte)(d+i)^, (^byte)(s+i)^;
// if x != y {
// return x < y ? -1 : +1;
// }
// }
// return 0;
// }
+37 -24
View File
@@ -1,3 +1,8 @@
package sort
import "core:mem"
import "intrinsics"
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil);
count := len(array);
@@ -7,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 {
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;
@@ -22,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;
@@ -30,7 +35,7 @@ bubble_sort :: proc(array: $A/[]$T) {
for {
init_swap, prev_swap := -1, -1;
for j in init_j..last_j {
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;
@@ -65,11 +70,11 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
j -= 1;
}
quick_sort(a[0..i], f);
quick_sort(a[i..n], f);
quick_sort_proc(a[0:i], f);
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;
@@ -88,13 +93,13 @@ quick_sort :: proc(array: $A/[]$T) {
j -= 1;
}
quick_sort(a[0..i]);
quick_sort(a[i..n]);
quick_sort(a[0:i]);
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;
}
@@ -102,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 {
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;
@@ -122,16 +127,16 @@ 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+1 {
for j in 0..a {
for i in 0..M {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k..k+m], arr1[k+m..k+m+m], arr2[k..], f);
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:], f);
}
if N-b > m {
k := 2*a*m;
merge_slices(arr1[k..k+m], arr1[k+m..k+m+(N-b)&(m-1)], arr2[k..], f);
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:], f);
} else {
copy(arr2[b..N], arr1[b..N]);
copy(arr2[b:N], arr1[b:N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
@@ -142,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 {
for k in 0..<N1+N2 {
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
out[k] = arr1[i];
i += 1;
@@ -164,16 +169,16 @@ merge_sort :: proc(array: $A/[]$T) {
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M+1 {
for j in 0..a {
for i in 0..M {
for j in 0..<a {
k := 2*j*m;
merge_slices(arr1[k..k+m], arr1[k+m..k+m+m], arr2[k..]);
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:]);
}
if N-b > m {
k := 2*a*m;
merge_slices(arr1[k..k+m], arr1[k+m..k+m+(N-b)&(m-1)], arr2[k..]);
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:]);
} else {
copy(arr2[b..N], arr1[b..N]);
copy(arr2[b:N], arr1[b:N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
@@ -185,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; {
@@ -209,5 +222,5 @@ compare_f64s :: proc(a, b: f64) -> int {
return 0;
}
compare_strings :: proc(a, b: string) -> int {
return __string_cmp(a, b);
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
}
-503
View File
@@ -1,503 +0,0 @@
using import "core:decimal.odin"
Int_Flag :: enum {
Prefix = 1<<0,
Plus = 1<<1,
Space = 1<<2,
}
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
switch s {
case "1", "t", "T", "true", "TRUE", "True":
return true, true;
case "0", "f", "F", "false", "FALSE", "False":
return false, true;
}
return;
}
_digit_value :: proc(r: rune) -> int {
ri := int(r);
v: int = 16;
switch r {
case '0'...'9': v = ri-'0';
case 'a'...'z': v = ri-'a'+10;
case 'A'...'Z': v = ri-'A'+10;
}
return v;
}
parse_i64 :: proc(s: string) -> i64 {
neg := false;
if len(s) > 1 {
switch s[0] {
case '-':
neg = true;
s = s[1..];
case '+':
s = s[1..];
}
}
base: i64 = 10;
if len(s) > 2 && s[0] == '0' {
switch s[1] {
case 'b': base = 2; s = s[2..];
case 'o': base = 8; s = s[2..];
case 'd': base = 10; s = s[2..];
case 'z': base = 12; s = s[2..];
case 'x': base = 16; s = s[2..];
}
}
value: i64;
for r in s {
if r == '_' {
continue;
}
v := i64(_digit_value(r));
if v >= base {
break;
}
value *= base;
value += v;
}
if neg do return -value;
return value;
}
parse_u64 :: proc(s: string) -> u64 {
neg := false;
if len(s) > 1 && s[0] == '+' {
s = s[1..];
}
base := u64(10);
if len(s) > 2 && s[0] == '0' {
switch s[1] {
case 'b': base = 2; s = s[2..];
case 'o': base = 8; s = s[2..];
case 'd': base = 10; s = s[2..];
case 'z': base = 12; s = s[2..];
case 'x': base = 16; s = s[2..];
}
}
value: u64;
for r in s {
if r == '_' do continue;
v := u64(_digit_value(r));
if v >= base do break;
value *= base;
value += u64(v);
}
if neg do return -value;
return value;
}
parse_int :: proc(s: string) -> int {
return int(parse_i64(s));
}
parse_uint :: proc(s: string, base: int) -> uint {
return uint(parse_u64(s));
}
parse_f32 :: proc(s: string) -> f32 {
return f32(parse_f64(s));
}
parse_f64 :: proc(s: string) -> f64 {
if s == "" {
return 0;
}
i := 0;
sign: f64 = 1;
switch s[i] {
case '-': i += 1; sign = -1;
case '+': i += 1;
}
value: f64 = 0;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
v := _digit_value(r);
if v >= 10 do break;
value *= 10;
value += f64(v);
}
if i < len(s) && s[i] == '.' {
pow10: f64 = 10;
i += 1;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
v := _digit_value(r);
if v >= 10 do break;
value += f64(v)/pow10;
pow10 *= 10;
}
}
frac := false;
scale: f64 = 1;
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
i += 1;
if i < len(s) {
switch s[i] {
case '-': i += 1; frac = true;
case '+': i += 1;
}
exp: u32 = 0;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
d := u32(_digit_value(r));
if d >= 10 do break;
exp = exp * 10 + d;
}
if exp > 308 { exp = 308; }
for exp >= 50 { scale *= 1e50; exp -= 50; }
for exp >= 8 { scale *= 1e8; exp -= 8; }
for exp > 0 { scale *= 10; exp -= 1; }
}
}
if frac do return sign * (value/scale);
return sign * (value*scale);
}
append_bool :: proc(buf: []byte, b: bool) -> string {
n := 0;
if b do n = copy(buf, cast([]byte)"true");
else do n = copy(buf, cast([]byte)"false");
return string(buf[..n]);
}
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u64(u), base, false, 8*size_of(uint), digits, 0);
}
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, 0);
}
itoa :: proc(buf: []byte, i: int) -> string do return append_int(buf, i64(i), 10);
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
}
DecimalSlice :: struct {
digits: []byte,
count: int,
decimal_point: int,
neg: bool,
}
FloatInfo :: struct {
mantbits: uint,
expbits: uint,
bias: int,
}
_f16_info := FloatInfo{10, 5, -15};
_f32_info := FloatInfo{23, 8, -127};
_f64_info := FloatInfo{52, 11, -1023};
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;
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');
}
} 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':
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);
}
}
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 {
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(i));
case 16:
i := i16(u);
neg = i < 0;
u = u64(abs(i));
case 32:
i := i32(u);
neg = i < 0;
u = u64(abs(i));
case 64:
i := i64(u);
neg = i < 0;
u = u64(abs(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_Flag) -> 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 flags&Int_Flag.Prefix != 0 {
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 flags&Int_Flag.Plus != 0:
i-=1; a[i] = '+';
case flags&Int_Flag.Space != 0:
i-=1; a[i] = ' ';
}
out := a[i..];
copy(buf, out);
return string(buf[0..len(out)]);
}
@@ -1,5 +1,6 @@
// Multiple precision decimal numbers
// NOTE: This is only for floating point printing and nothing else
package strconv_decimal
Decimal :: struct {
digits: [384]byte, // big-endian digits
@@ -19,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
@@ -55,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);
@@ -129,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 {
@@ -140,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;
@@ -152,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;
@@ -160,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:
@@ -252,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)]);
}
+308
View File
@@ -0,0 +1,308 @@
package strconv
import "core:unicode/utf8"
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
switch s {
case "1", "t", "T", "true", "TRUE", "True":
return true, true;
case "0", "f", "F", "false", "FALSE", "False":
return false, true;
}
return;
}
_digit_value :: proc(r: rune) -> int {
ri := int(r);
v: int = 16;
switch r {
case '0'..'9': v = ri-'0';
case 'a'..'z': v = ri-'a'+10;
case 'A'..'Z': v = ri-'A'+10;
}
return v;
}
parse_i64 :: proc(str: string) -> i64 {
s := str;
neg := false;
if len(s) > 1 {
switch s[0] {
case '-':
neg = true;
s = s[1:];
case '+':
s = s[1:];
}
}
base: i64 = 10;
if len(s) > 2 && s[0] == '0' {
switch s[1] {
case 'b': base = 2; s = s[2:];
case 'o': base = 8; s = s[2:];
case 'd': base = 10; s = s[2:];
case 'z': base = 12; s = s[2:];
case 'x': base = 16; s = s[2:];
}
}
value: i64;
for r in s {
if r == '_' {
continue;
}
v := i64(_digit_value(r));
if v >= base {
break;
}
value *= base;
value += v;
}
if neg do return -value;
return value;
}
parse_u64 :: proc(str: string) -> u64 {
s := str;
neg := false;
if len(s) > 1 && s[0] == '+' {
s = s[1:];
}
base := u64(10);
if len(s) > 2 && s[0] == '0' {
switch s[1] {
case 'b': base = 2; s = s[2:];
case 'o': base = 8; s = s[2:];
case 'd': base = 10; s = s[2:];
case 'z': base = 12; s = s[2:];
case 'x': base = 16; s = s[2:];
}
}
value: u64;
for r in s {
if r == '_' do continue;
v := u64(_digit_value(r));
if v >= base do break;
value *= base;
value += u64(v);
}
if neg do return -value;
return value;
}
parse_int :: proc(s: string) -> int {
return int(parse_i64(s));
}
parse_uint :: proc(s: string, base: int) -> uint {
return uint(parse_u64(s));
}
parse_f32 :: proc(s: string) -> f32 {
return f32(parse_f64(s));
}
parse_f64 :: proc(s: string) -> f64 {
if s == "" {
return 0;
}
i := 0;
sign: f64 = 1;
switch s[i] {
case '-': i += 1; sign = -1;
case '+': i += 1;
}
value: f64 = 0;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
v := _digit_value(r);
if v >= 10 do break;
value *= 10;
value += f64(v);
}
if i < len(s) && s[i] == '.' {
pow10: f64 = 10;
i += 1;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
v := _digit_value(r);
if v >= 10 do break;
value += f64(v)/pow10;
pow10 *= 10;
}
}
frac := false;
scale: f64 = 1;
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
i += 1;
if i < len(s) {
switch s[i] {
case '-': i += 1; frac = true;
case '+': i += 1;
}
exp: u32 = 0;
for ; i < len(s); i += 1 {
r := rune(s[i]);
if r == '_' do continue;
d := u32(_digit_value(r));
if d >= 10 do break;
exp = exp * 10 + d;
}
if exp > 308 { exp = 308; }
for exp >= 50 { scale *= 1e50; exp -= 50; }
for exp >= 8 { scale *= 1e8; exp -= 8; }
for exp > 0 { scale *= 10; exp -= 1; }
}
}
if frac do return sign * (value/scale);
return sign * (value*scale);
}
append_bool :: proc(buf: []byte, b: bool) -> string {
n := 0;
if b do n = copy(buf, cast([]byte)"true");
else do n = copy(buf, cast([]byte)"false");
return string(buf[:n]);
}
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u64(u), base, false, 8*size_of(uint), digits, nil);
}
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 {
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 "";
}
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]);
}
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 "";
}
i := 0;
write_byte(buf, &i, '\'');
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:
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 {
write_rune(buf, &i, r);
}
}
write_byte(buf, &i, '\'');
return string(buf[:i]);
}
-29
View File
@@ -1,29 +0,0 @@
import "core:mem.odin"
new_string :: proc(s: string) -> string {
c := make([]byte, len(s)+1);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return string(c[..len(s)]);
}
new_c_string :: proc(s: string) -> ^byte {
c := make([]byte, len(s)+1);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return &c[0];
}
to_odin_string :: proc(str: ^byte) -> string {
if str == nil do return "";
end := str;
for end^ != 0 do end+=1;
return string(mem.slice_ptr(str, end-str));
}
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r do return offset;
}
return -1;
}
+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);
}
+845
View File
@@ -0,0 +1,845 @@
package strings
import "core:mem"
import "core:unicode/utf8"
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)]);
}
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]);
}
@(deprecated="Please use a standard cast for cstring to string")
to_odin_string :: proc(str: cstring) -> string {
return string(str);
}
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:];
}
}
-2
View File
@@ -1,2 +0,0 @@
when ODIN_OS == "windows" do export "core:sync_windows.odin";
when ODIN_OS == "linux" do export "core:sync_linux.odin";
+211
View File
@@ -0,0 +1,211 @@
package sync
import "intrinsics"
Ordering :: enum {
Relaxed, // Monotonic
Release,
Acquire,
Acquire_Release,
Sequentially_Consistent,
}
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> 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;
}
return .Relaxed;
}
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: panic("unknown order");
}
}
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: panic("unknown order");
}
}
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");
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
atomic_compare_exchange :: 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_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:
switch success {
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:
switch success {
case .Sequentially_Consistent: return intrinsics.atomic_cxchg(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 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 {
#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);
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
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);
}
panic("unknown order");
return T{};
}
@@ -1,5 +1,9 @@
import "core:atomics.odin"
import "core:os.odin"
package sync
/*
import "core:atomics"
import "core:os"
Semaphore :: struct {
// _handle: win32.Handle,
@@ -91,3 +95,4 @@ mutex_unlock :: proc(m: ^Mutex) {
}
}
*/
+111
View File
@@ -0,0 +1,111 @@
package sync
import "core:sys/win32"
foreign {
@(link_name="llvm.x86.sse2.pause")
yield_processor :: proc() ---
}
Semaphore :: struct {
_handle: win32.Handle,
}
Mutex :: struct {
_critical_section: win32.Critical_Section,
}
Condition :: struct {
event: win32.Handle,
}
Ticket_Mutex :: struct {
ticket: u64,
serving: u64,
}
current_thread_id :: proc() -> i32 {
return i32(win32.get_current_thread_id());
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.create_semaphore_w(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.close_handle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.release_semaphore(s._handle, i32(count), nil);
}
semaphore_release :: inline proc(s: ^Semaphore) {
semaphore_post(s, 1);
}
semaphore_wait :: proc(s: ^Semaphore) {
result := win32.wait_for_single_object(s._handle, win32.INFINITE);
assert(result != win32.WAIT_FAILED);
}
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
win32.initialize_critical_section_and_spin_count(&m._critical_section, u32(spin_count));
}
mutex_destroy :: proc(m: ^Mutex) {
win32.delete_critical_section(&m._critical_section);
}
mutex_lock :: proc(m: ^Mutex) {
win32.enter_critical_section(&m._critical_section);
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
return bool(win32.try_enter_critical_section(&m._critical_section));
}
mutex_unlock :: proc(m: ^Mutex) {
win32.leave_critical_section(&m._critical_section);
}
condition_init :: proc(using c: ^Condition) {
event = win32.create_event_w(nil, false, false, nil);
assert(event != nil);
}
condition_signal :: proc(using c: ^Condition) {
ok := win32.set_event(event);
assert(bool(ok));
}
condition_wait_for :: proc(using c: ^Condition) {
result := win32.wait_for_single_object(event, win32.INFINITE);
assert(result != win32.WAIT_FAILED);
}
condition_destroy :: proc(using c: ^Condition) {
if event != nil {
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);
}
-124
View File
@@ -1,124 +0,0 @@
when ODIN_OS == "windows" {
import win32 "core:sys/windows.odin";
}
import "core:atomics.odin"
Semaphore :: struct {
_handle: win32.Handle,
}
/*
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
*/
Mutex :: struct {
_critical_section: win32.Critical_Section,
}
current_thread_id :: proc() -> i32 {
return i32(win32.get_current_thread_id());
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.create_semaphore_a(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.close_handle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.release_semaphore(s._handle, i32(count), nil);
}
semaphore_release :: inline proc(s: ^Semaphore) {
semaphore_post(s, 1);
}
semaphore_wait :: proc(s: ^Semaphore) {
win32.wait_for_single_object(s._handle, win32.INFINITE);
}
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
win32.initialize_critical_section_and_spin_count(&m._critical_section, u32(spin_count));
}
mutex_destroy :: proc(m: ^Mutex) {
win32.delete_critical_section(&m._critical_section);
}
mutex_lock :: proc(m: ^Mutex) {
win32.enter_critical_section(&m._critical_section);
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
return bool(win32.try_enter_critical_section(&m._critical_section));
}
mutex_unlock :: proc(m: ^Mutex) {
win32.leave_critical_section(&m._critical_section);
}
/*
mutex_init :: proc(m: ^Mutex) {
atomics.store(&m._counter, 0);
atomics.store(&m._owner, current_thread_id());
semaphore_init(&m._semaphore);
m._recursion = 0;
}
mutex_destroy :: proc(m: ^Mutex) {
semaphore_destroy(&m._semaphore);
}
mutex_lock :: proc(m: ^Mutex) {
thread_id := current_thread_id();
if atomics.fetch_add(&m._counter, 1) > 0 {
if thread_id != atomics.load(&m._owner) {
semaphore_wait(&m._semaphore);
}
}
atomics.store(&m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomics.load(&m._owner) == thread_id {
atomics.fetch_add(&m._counter, 1);
} else {
expected: i32 = 0;
if atomics.load(&m._counter) != 0 {
return false;
}
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
return false;
}
atomics.store(&m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomics.load(&m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomics.store(&m._owner, thread_id);
}
if atomics.fetch_add(&m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(&m._semaphore);
}
}
}
*/
-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);
@@ -1,8 +1,7 @@
when ODIN_OS == "windows" {
foreign import "system:opengl32.lib"
using import "core:sys/windows.odin"
}
// +build windows
package win32
foreign import "system:opengl32.lib"
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
CONTEXT_MINOR_VERSION_ARB :: 0x2092;
@@ -55,7 +54,7 @@ Glyph_Metrics_Float :: struct {
Create_Context_Attribs_ARB_Type :: #type proc "c" (hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
Choose_Pixel_Format_ARB_Type :: #type proc "c" (hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool;
Swap_Interval_EXT_Type :: #type proc "c" (interval: i32) -> bool;
Get_Extensions_String_ARB_Type :: #type proc "c" (Hdc) -> ^byte;
Get_Extensions_String_ARB_Type :: #type proc "c" (Hdc) -> cstring;
// Procedures
create_context_attribs_arb: Create_Context_Attribs_ARB_Type;
@@ -72,7 +71,7 @@ foreign opengl32 {
make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool ---;
@(link_name="wglGetProcAddress")
get_gl_proc_address :: proc(c_str: ^byte) -> rawptr ---;
get_gl_proc_address :: proc(c_str: cstring) -> rawptr ---;
@(link_name="wglDeleteContext")
delete_context :: proc(hglrc: Hglrc) -> Bool ---;
+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 ---;
}
File diff suppressed because it is too large Load Diff
@@ -1,8 +1,7 @@
_ :: compile_assert(ODIN_OS == "windows");
package thread
when ODIN_OS == "windows" {
import win32 "core:sys/windows.odin"
}
import "core:runtime"
import "core:sys/win32"
Thread_Proc :: #type proc(^Thread) -> int;
@@ -17,7 +16,7 @@ Thread :: struct {
data: rawptr,
user_index: int,
init_context: Context,
init_context: runtime.Context,
use_init_context: bool,
}
@@ -30,13 +29,9 @@ create :: proc(procedure: Thread_Proc) -> ^Thread {
if t.use_init_context {
c = t.init_context;
}
context = c;
exit := 0;
context <- c {
exit = t.procedure(t);
}
return i32(exit);
return i32(t.procedure(t));
}
@@ -74,3 +69,7 @@ destroy :: proc(thread: ^Thread) {
join(thread);
free(thread);
}
terminate :: proc(using thread : ^Thread, exit_code : u32) {
win32.terminate_thread(win32_thread, exit_code);
}

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