Compare commits

..

367 Commits

Author SHA1 Message Date
Ginger Bill c454ede184 v0.6.1a 2017-08-01 17:30:26 +01:00
Ginger Bill d854c5003c Fix minor errors for *nix 2017-08-01 17:28:49 +01:00
Ginger Bill 66d8776b83 v0.6.1 2017-08-01 15:18:37 +01:00
Ginger Bill ba6ecf35cf Disable threading on *nix for the time being 2017-08-01 15:09:43 +01:00
Ginger Bill 10cc9cf661 Add mutexes to string buffer allocator uses 2017-08-01 14:24:40 +01:00
Ginger Bill 2db971eedd Use pthread mutex 2017-08-01 13:49:12 +01:00
Ginger Bill 1775e80b41 HACK: Ignore Mutex check 2017-07-31 23:18:21 +01:00
Ginger Bill e4a93619db Update gb.h 2017-07-31 12:17:53 +01:00
Ginger Bill 4d14b3bcb4 Update remove_temp_files 2017-07-31 12:15:20 +01:00
Ginger Bill 9f4f5f9346 Add -keep-temp-files option 2017-07-31 12:06:04 +01:00
Ginger Bill 0fae31fb54 Extra type safety; Fix typos 2017-07-31 11:36:00 +01:00
Ginger Bill 8987a6630c v0.6.0 2017-07-30 22:26:22 +01:00
Ginger Bill 10ff8e0426 Fix ir for TypeInfo.Map 2017-07-30 20:17:25 +01:00
Ginger Bill a0ae02168a Update add_type_info_type to ignore polymorphic types 2017-07-30 20:13:23 +01:00
Ginger Bill a3c1ac2030 Speed up llvm ir printing; Use CRITICAL_SECTION for Mutex on windows 2017-07-30 19:47:37 +01:00
Ginger Bill 629b248f53 Parallelization of the Parser
~66% reduction (unoptimized build)
~30% reduction (optimized build)
2017-07-30 19:01:02 +01:00
Ginger Bill 62a72f0163 transmute(type)x; Minor code clean up 2017-07-30 14:52:42 +01:00
Ginger Bill 655931f0ea Minor Simplification of threading demo 2017-07-29 15:18:36 +01:00
Ginger Bill ca36fabfc0 Remove dead code for the "fixed" map idea 2017-07-29 14:43:42 +01:00
Ginger Bill 7bd62481ad Fix nil assignment to unions 2017-07-29 14:23:34 +01:00
Ginger Bill fbd27d7c45 Fix map internal type generation 2017-07-29 13:56:45 +01:00
Ginger Bill 3546391311 Merge branch 'master' of https://github.com/gingerBill/Odin 2017-07-29 13:01:28 +01:00
Ginger Bill 24c812115e Remove empty union check on array types; Fix overflowing error printing 2017-07-29 13:01:17 +01:00
gingerBill 28be0ad69b Fix IR print bug for empty structs; 2017-07-28 11:35:01 +01:00
gingerBill f0980c0a98 Fix import name exportation bug; Fix procedure type printing 2017-07-24 07:57:09 +01:00
Ginger Bill 1df4aa90ce Fix struct parameter bugs 2017-07-21 15:25:58 +01:00
Ginger Bill 6b3cf051f8 Fix math.odin, again 2017-07-21 12:39:05 +01:00
Ginger Bill 4ecd6e592b Fix missing semicolons in math.odin 2017-07-21 10:37:49 +01:00
Ginger Bill dbddec33c8 Internal changes; thread.odin for windows only 2017-07-20 23:57:56 +01:00
Ginger Bill 401a5955a4 Fix minor check on vector types 2017-07-20 19:55:54 +01:00
Ginger Bill 9a3b4167bb Fix polymorphic element types usage; Empty union as opaque type 2017-07-20 19:40:51 +01:00
Ginger Bill 13bc6eeea4 Make fields et al an Array rather than a raw pointer 2017-07-20 15:32:34 +01:00
Ginger Bill 2da18b6d33 Change internals from Record to Struct 2017-07-20 15:23:13 +01:00
Ginger Bill 6d37ed12d2 Update internals of a Union and Tuple 2017-07-20 15:17:04 +01:00
Ginger Bill eab23cd5b7 Fix parsing bug with procedure types in return values 2017-07-19 22:34:50 +01:00
Ginger Bill d233706a2d Fix minor parsing bug with procedure return types 2017-07-19 22:17:57 +01:00
Ginger Bill f1ab17ed4e type_info_of; enum_value_to_string and string_to_enum_value 2017-07-19 14:01:56 +01:00
Ginger Bill 6113164211 Change union layout to store type info rather than an integer; ternary expression for types with constant condition 2017-07-19 12:15:21 +01:00
Ginger Bill 4db462a703 Fix copy 2017-07-18 20:39:53 +01:00
Ginger Bill a22c6d6c0c Fix parsing error for compound literals 2017-07-18 19:57:30 +01:00
Ginger Bill 59fb7b020a Merge raw_union into struct as a memory layout tag #raw_union 2017-07-18 19:24:45 +01:00
Ginger Bill 65f079ebc4 Remove atomic, ++, and -- 2017-07-18 18:58:41 +01:00
Ginger Bill d16aa79492 General specialization for polymorphic parameters 2017-07-18 18:05:41 +01:00
Ginger Bill 5af0acc4af Disallow default struct values for any; new_clone 2017-07-18 16:02:01 +01:00
Ginger Bill a459364de3 Ignore missing default values for struct literals at the end 2017-07-18 15:32:34 +01:00
Ginger Bill 277ef1a68f Allow undefined --- as a struct field default value. 2017-07-18 15:09:24 +01:00
Ginger Bill 193c7c82c8 Default struct field values 2017-07-18 14:56:07 +01:00
Ginger Bill f7d8ba408c Fix some preload bugs. 2017-07-18 11:42:16 +01:00
Ginger Bill 9a8759efef Polymorphic type specialization for procedures 2017-07-17 15:08:36 +01:00
Ginger Bill 054948e701 Basic procedure type parameter specialization 2017-07-16 15:00:16 +01:00
Ginger Bill 1c5ddd65b4 Rudimentary support for parametric polymorphic types 2017-07-13 22:35:00 +01:00
Ginger Bill b8697fb4ed Change precedence order for types e.g. ^T(x) == ^(T(x)) 2017-07-13 16:20:07 +01:00
Ginger Bill 03570275c1 Fix issue #78 and have a better error message. 2017-07-13 11:35:01 +01:00
Ginger Bill b5587f1937 Fix aliasing of overloaded procedures from other scopes 2017-07-11 20:54:38 +01:00
Ginger Bill c4c6975f1b cast(Type)expr; Fix overloaded procedure determination on assignment 2017-07-11 14:40:27 +01:00
Ginger Bill 0be0fb2a57 Nested when statements within records 2017-07-10 23:47:22 +01:00
Ginger Bill 115e6e7f9e Update demo for both subtyping and union based Entity 2017-07-10 23:28:53 +01:00
Ginger Bill 3868a9a0f0 Clean up _preload.odin types 2017-07-10 23:15:41 +01:00
Ginger Bill ba5050ac7c Compiler Internal Changes: TypeRecord_Union -> Type_Union 2017-07-10 22:59:23 +01:00
Ginger Bill d936ca1ea0 Compiler internal change: TypeRecord_Enum -> Type_Enum 2017-07-10 22:42:58 +01:00
Ginger Bill fd8c4d58bb union type allow for any types and removes common fields 2017-07-10 22:32:21 +01:00
Ginger Bill ce4b7b8b7d Nested record declarations 2017-07-10 20:39:42 +01:00
Ginger Bill 069a47220e Make record semicolon syntax more consistent 2017-07-10 14:52:58 +01:00
Ginger Bill 66e4aaffc5 Use semicolons as field delimiters in records 2017-07-10 13:49:50 +01:00
Ginger Bill 81336b58cb "Fix" printing of embedded any to prevent recursion 2017-07-10 10:37:51 +01:00
Ginger Bill b201670f7a Fix _preload.odin; Add for in without parameters; Change sync.Mutex for windows 2017-07-08 23:13:57 +01:00
Ginger Bill 4b051a0d3b .. half closed range; ... open range; ... variadic syntax 2017-07-07 23:42:43 +01:00
Ginger Bill 45353465a6 Add sort.odin 2017-07-07 22:26:55 +01:00
Ginger Bill c63cb98019 Fix else do 2017-07-07 17:50:45 +01:00
Ginger Bill 773cf5ca08 Add -show-timings; Clean up polymorphic procedure code a bit 2017-07-07 15:26:49 +01:00
Ginger Bill 2db03cb4a5 Fix aprint* bug; NULL -> nullptr; Better error messages for overloaded functions 2017-07-06 22:43:55 +01:00
Ginger Bill eed873c6ec Add free for maps (a previous oversight) 2017-07-05 13:51:25 +01:00
Ginger Bill 3d2d461867 Replace many built-in procedures with user-level procedures 2017-07-04 23:52:00 +01:00
Ginger Bill 36392d658e Fix demo.odin 2017-07-04 22:43:38 +01:00
Ginger Bill 82696179e8 Merge branch 'master' of https://github.com/gingerBill/Odin 2017-07-04 22:42:41 +01:00
Ginger Bill 188bc28f6a Allow for overloading of polymorphic procedures 2017-07-04 22:42:25 +01:00
Ginger Bill 240da5c8e0 Allow aliasing of aliases 2017-07-04 16:06:08 +01:00
Ginger Bill 689a0c0b49 *_of as keyords; Allow constant aliasing for user/built-in procedures, import names, and library names 2017-07-04 11:23:48 +01:00
Ginger Bill bc16b290ba Disable polymorphic overloading in the global scope
TODO: Figure out why it does not work in the global scope
2017-07-02 22:08:39 +01:00
Ginger Bill 96d32680fe Allow overloading of polymorphic procedures 2017-07-02 10:45:22 +01:00
Ginger Bill d782b3d21d Fix do on for loops 2017-07-01 11:53:01 +01:00
Ginger Bill ed089b44b9 do keyword for inline statements instead of blocks 2017-07-01 11:38:44 +01:00
Ginger Bill 33f4af2e19 Fix demo 2017-06-29 21:01:07 +01:00
Ginger Bill 69f7382eec Implicit parametric polymorphic procedures 2017-06-29 20:56:18 +01:00
Ginger Bill 7e3293fc20 Fix odin version printing 2017-06-29 16:08:30 +01:00
Ginger Bill e4a8283327 Remove Type
What was I thinking?!
2017-06-29 15:48:07 +01:00
Ginger Bill 001baf4419 Add Type -- Runtime type for comparing types (similar to TypeInfo but simpler) 2017-06-29 15:13:41 +01:00
Ginger Bill d167290b28 Make AstNodeIdent a struct wrapping its Token 2017-06-29 12:11:50 +01:00
Ginger Bill f4879d4723 Update procedure names and extend demo.odin 2017-06-29 11:25:05 +01:00
Ginger Bill fd81c06c35 Remove var and const keywords; Fix default parameter syntax 2017-06-28 23:55:40 +01:00
Ginger Bill 94afcec757 :: style procedure declarations; remove old parsing code 2017-06-28 23:47:06 +01:00
Ginger Bill 4f28e9e1fb Remove type prefix declarations 2017-06-28 23:23:10 +01:00
Ginger Bill 0622509807 Disable var and const declarations 2017-06-28 23:17:20 +01:00
Ginger Bill 9ca2246bac Basic allowance for := and :: 2017-06-28 22:38:04 +01:00
Ginger Bill 647e2cafd7 Fix expand_to_tuple 2017-06-27 22:47:19 +01:00
Ginger Bill 5df854fcef Fixed demo 2017-06-27 15:58:53 +01:00
Ginger Bill 260089431e Write demo for v0.5.0 2017-06-26 21:34:54 +01:00
Ginger Bill d0d8da8c08 Revert demo 2017-06-26 19:42:32 +01:00
Ginger Bill d1365b3466 Fix poly-procs for variadic calls 2017-06-26 19:24:04 +01:00
Ginger Bill c949ca2a5c Allow for named arguments for polymorphic procedures 2017-06-26 18:20:24 +01:00
Ginger Bill d974b29f67 Reduce excessive node cloning on para-poly checking and fix scope bug 2017-06-26 14:39:51 +01:00
Ginger Bill cc7316bb35 Fix IR printing for para-poly procedures 2017-06-26 14:16:16 +01:00
Ginger Bill a0d8dcd974 Remove let 2017-06-26 13:59:15 +01:00
Ginger Bill c642e326ce Undef value --- (for setting a value to be uninitialized/undefined) 2017-06-26 11:57:26 +01:00
Ginger Bill 362a118782 Remove "overloading" bug of para-poly-procs 2017-06-25 23:41:46 +01:00
Ginger Bill 3ab481df17 new as a user-level procedure 2017-06-25 22:31:30 +01:00
Ginger Bill 4e7150b470 Allow nested para-poly procedures 2017-06-25 22:29:23 +01:00
Ginger Bill 1ced92be47 Rudimentary para-poly procedures 2017-06-25 22:15:30 +01:00
Ginger Bill 15dbea6899 Generic procedures generate types on use 2017-06-25 19:41:07 +01:00
Ginger Bill c4081393c1 Fix typo for some built-in procedures 2017-06-25 17:36:10 +01:00
Ginger Bill 1d81b73df9 Basic command line flags: e.g. -opt=0 2017-06-24 22:58:50 +01:00
Ginger Bill 18f885efab expand_to_tuple 2017-06-24 20:39:37 +01:00
Ginger Bill bba088bee7 Use UTF-8 command line on windows 2017-06-24 11:42:49 +01:00
Ginger Bill 6cbb6bef0b Wrap hashing functions 2017-06-22 16:14:02 +01:00
Ginger Bill 8744c60563 Clean up code for return statements, slightly 2017-06-22 13:47:50 +01:00
Ginger Bill 8197c02dcf Default result values for procedure types; Named result values in return statements 2017-06-22 01:14:45 +01:00
Ginger Bill 9faf0020cc Amend Checker API 2017-06-21 21:46:27 +01:00
Ginger Bill 53075e2570 Update old demos 2017-06-21 21:20:26 +01:00
Ginger Bill 264ca00db7 Merge branch 'master' of https://github.com/gingerBill/Odin 2017-06-21 17:49:05 +01:00
Ginger Bill 6b65ef6d88 Fix compilation bug on Linux 2017-06-21 17:48:50 +01:00
Ginger Bill 5957d7f7be Implicit Parameter Passing based context system (replacing Thread Local Storage (TLS) approach) 2017-06-20 12:38:05 +01:00
Ginger Bill 35c102137f Compiler compiles for x86 (doesn't work properly) 2017-06-19 18:49:11 +01:00
Ginger Bill 5427d14416 Code will compile as 32 bit but will causes errors in the linker on Windows 2017-06-19 15:55:09 +01:00
Ginger Bill 178236d1ff Barebones layout for the documentation declarations 2017-06-18 23:41:13 +01:00
Ginger Bill 736c880ba9 Add docs.cpp 2017-06-18 23:18:15 +01:00
Ginger Bill 126f7aa892 Begin work on documentation generation 2017-06-18 23:16:57 +01:00
Ginger Bill 2957f007e3 Fix #location for anonymous procedures 2017-06-18 17:35:27 +01:00
Ginger Bill 04501c93fe Implement assert and panic in user side code
Removes 2 more built-in procedures!
2017-06-18 17:25:28 +01:00
Ginger Bill 4236519b84 #location(..) and #call_location 2017-06-18 14:36:06 +01:00
Ginger Bill e4944b4f2e Fix error reporting for foreign blocks 2017-06-17 20:03:52 +01:00
Ginger Bill 2deb2f8eeb Declaration grouping uses () rather than {}; Fix some problem with compilation on *nix 2017-06-17 12:01:53 +01:00
Ginger Bill 3fa398ec43 Add extra check for bodiless procedures 2017-06-15 21:36:29 +01:00
Ginger Bill 1851674b50 Code use API rather than raw CheckerInfo; begin work on generic procedures 2017-06-15 18:11:58 +01:00
Ginger Bill c5ef5279d4 Add foreign variables 2017-06-15 14:42:08 +01:00
Ginger Bill d3c24d159f Merge size_of and size_of_val et al. 2017-06-15 12:25:53 +01:00
Ginger Bill 23f9f9064e Add CheckerInfo API functions 2017-06-15 12:14:56 +01:00
Ginger Bill a134307dcd Fix issue #72 - 128-bit literal corruption 2017-06-14 14:58:48 +01:00
Ginger Bill c3b510c2d9 C-style c_varargs (Not heavily tested) 2017-06-13 21:00:42 +01:00
Ginger Bill e7fc24e48c Fix compilation error for Invalid EntityKind 2017-06-13 18:04:22 +01:00
Ginger Bill 6a88dc322a Declaration grouping uses braces rather than parentheses 2017-06-13 15:04:23 +01:00
Ginger Bill 6b464e3558 Update README.md 2017-06-12 21:41:14 +01:00
Ginger Bill 76b0c7b765 "Revert" to older demo 2017-06-12 21:27:53 +01:00
Ginger Bill 91857e8f16 Remove redundant paths in parsing 2017-06-12 21:25:47 +01:00
Ginger Bill ccda456c0a foreign blocks for procedures 2017-06-12 21:21:18 +01:00
Ginger Bill 83bad13e9e Update default field value syntax; Use more declaration groupings 2017-06-12 18:38:27 +01:00
Ginger Bill e6a206a430 Check for empty generic declaration list 2017-06-12 16:58:25 +01:00
Ginger Bill f52a1e4ded Fix IR bug for TypeSpec 2017-06-12 16:47:07 +01:00
Ginger Bill a8e458339b foreign_library allow for Pascal-style grouping 2017-06-12 16:26:51 +01:00
Ginger Bill 6b5e9aec8e Pascal style declaration grouping with () 2017-06-12 15:42:21 +01:00
Ginger Bill 2ab0d97573 import and import_load as keywords; Fix procedure literal call trick 2017-06-12 14:19:12 +01:00
Ginger Bill 0c05fc1432 Prefix type and let to replace immutable 2017-06-12 12:56:47 +01:00
Ginger Bill 33eeb58521 Prefix proc syntax 2017-06-12 12:34:55 +01:00
Ginger Bill 8fafdb185c Remove := with var and :: with const 2017-06-12 11:48:12 +01:00
Ginger Bill c2c935ba81 Fix trailing default argument checking 2017-06-11 20:52:54 +01:00
Ginger Bill 2d73c8868b Make default arguments for records invalid syntax 2017-06-11 19:01:36 +01:00
gingerBill b95bb1286b Merge pull request #70 from ThisDrunkDane/master
Add some WM_*, some WS_* and map_virtual_key
2017-06-11 18:54:30 +01:00
Mikkel Hjortshoej 4237c8ec30 Merge branch 'master' of github.com:gingerBill/Odin 2017-06-11 19:53:44 +02:00
Ginger Bill 49b4b39055 Minor change for overloaded procedures 2017-06-11 18:53:20 +01:00
Mikkel Hjortshoej bf15fea135 Merge branch 'master' of github.com:gingerBill/Odin 2017-06-11 19:47:57 +02:00
Mikkel Hjortshoej 47c03e376d Merge branch 'master' of github.com:gingerBill/Odin 2017-06-11 19:47:05 +02:00
Ginger Bill 1cabfac36c Update README.md 2017-06-11 18:46:59 +01:00
Mikkel Hjortshoej 8e32276283 Added a bunch of VM_* and map_virtual_key 2017-06-11 19:46:55 +02:00
Ginger Bill 366b306df0 Default parameters for procedures 2017-06-11 18:38:30 +01:00
Ginger Bill 4bf1f798f5 Allow for ignoring named procedural call arguments with _ 2017-06-11 17:41:55 +01:00
Ginger Bill b2fdb69b4d Named procedure calls 2017-06-11 12:01:40 +01:00
Ginger Bill af2736daec Fix bit field bug 2017-06-08 16:29:05 +01:00
Ginger Bill 5cad7d44a6 Use templated Map for extra type safety 2017-06-08 13:26:48 +01:00
Ginger Bill 2b96be0ae8 Remove unnecessary typedef usage 2017-06-08 13:08:39 +01:00
Ginger Bill 2a89d8021c Use templated Array with bounds checking 2017-06-08 12:54:52 +01:00
Ginger Bill 13deb4706c Update String to use overloading 2017-06-08 12:37:07 +01:00
Ginger Bill 9b61adb97d Build as C++ 2017-06-08 12:03:40 +01:00
Ginger Bill 333924cce1 v0.3 Release 2017-06-08 11:35:22 +01:00
Ginger Bill 574b82c0c7 v0.3.0 2017-06-07 22:09:16 +01:00
Ginger Bill f60c772c11 Make rune a basic type and not an alias; Remove byte 2017-06-06 23:54:33 +01:00
Ginger Bill 107740ca5e Fix issue #69 for fmt.printf padding 2017-06-06 10:02:53 +01:00
gingerBill 88b990eb63 Merge pull request #53 from ghost/master
Fix link time error about missing -fPIC flag
2017-06-06 09:47:40 +01:00
Ginger Bill d2e7d730ac Fix key generation for constant strings in IR 2017-06-05 23:06:15 +01:00
Ginger Bill 817e4b663e Add murmurhash3.c 2017-06-05 22:52:56 +01:00
Ginger Bill 214bb73454 Merge branch 'master' of https://github.com/gingerBill/Odin 2017-06-05 18:01:57 +01:00
Ginger Bill eba2c74bff Allow 128 bit map keys 2017-06-05 18:01:41 +01:00
gingerBill 7c5e6c808b Merge pull request #68 from ThisDrunkDane/master
Added extra sys/windows.odin stuff
2017-06-05 15:18:04 +01:00
Ginger Bill ebe5beaafd Allow using on bit fields 2017-06-04 11:53:33 +01:00
Ginger Bill 029a6095d9 Fix enum printing bug 2017-06-04 00:20:16 +01:00
Ginger Bill 2c0e59ae06 bit_field; Lexical sugar operators ≠ ≤ ≥
Example below:
// See: https://en.wikipedia.org/wiki/Bit_field
BoxProps :: bit_field {
	opaque        : 1,
	fill_colour   : 3,
	_             : 4,
	show_border   : 1,
	border_colour : 3,
	border_style  : 2,
	_             : 2,
	width         : 4,
	height        : 4,
	_             : 8,
}
2017-06-03 22:27:23 +01:00
Ginger Bill 9d1a4c304a Remove Quat from math.odin 2017-06-01 15:12:54 +01:00
Ginger Bill 13b8a1e348 Remove quaternion128 and quaternion256 as core types 2017-06-01 14:52:33 +01:00
Ginger Bill 0d4945dc87 Implement u128/i128 features; Add bits.odin 2017-06-01 14:23:46 +01:00
Mikkel Hjortshoej e0b9c4a275 Added extra sys/windows.odin stuff
- Added PM_NOREMOVE
	- Added PM_NOYIELD
	- Added get_message_a
	- Added post_message_a
2017-06-01 00:05:33 +02:00
Ginger Bill fec6df65b3 Use 128-bit integers for ExactValue integers 2017-05-30 15:23:01 +01:00
Ginger Bill 78494e84d5 Remove some asserts in timings.c 2017-05-29 19:41:13 +01:00
Ginger Bill 60d7c833c0 Fix unary expression type check 2017-05-28 21:56:40 +01:00
Ginger Bill 98dbbf11f3 Fix procedure overloading distinguishing 2017-05-28 18:51:42 +01:00
Ginger Bill f4924e39d4 Fix printing of struct literals with custom alignment 2017-05-28 16:11:19 +01:00
Ginger Bill 826e05c96e Convert windows.odin to the new naming convention 2017-05-28 16:08:29 +01:00
Ginger Bill d3f63e5903 Change label syntax for for and match from #label name to name: 2017-05-28 15:01:39 +01:00
Ginger Bill 80c034ec7c Change naming convention from Ada_Like to RustLike
Naming Conventions:
In general, PascalCase for types and snake_case for values

Import Name:        snake_case (but prefer single word)
Types:              PascalCase
Union Variants:     PascalCase
Enum Values:        PascalCase
Procedures:         snake_case
Local Variables:    snake_case
Constant Variables: SCREAMING_SNAKE_CASE
2017-05-28 14:47:11 +01:00
Ginger Bill b41f09b730 Experimental try for ABI for return values on windows
It's all done by reverse engineering it. I may be wrong...
2017-05-28 14:11:00 +01:00
Ginger Bill 06185e1769 Try a different ABI type for return values on Windows 2017-05-28 01:07:52 +01:00
Ginger Bill f8fa7fe380 Fix bug with too many field values in a structure literal. 2017-05-27 20:57:48 +01:00
Ginger Bill 45dbe8d354 default: to case:; no_alias to #no_alias 2017-05-27 11:47:21 +01:00
Ginger Bill ddb99dd638 Fix interval loop constant bug; Fix ir edge checking; Fix vector arithmetic with scalars 2017-05-22 23:29:09 +01:00
Ginger Bill 41aa4e606b Optional main for DLL; access struct elements by "index" 2017-05-17 21:23:52 +01:00
Ginger Bill e025a828ca Fix issue #66 2017-05-14 10:32:48 +01:00
Ginger Bill 807e17207a Merge branch 'master' of https://github.com/gingerBill/Odin 2017-05-13 16:09:04 +01:00
Ginger Bill 3e18f5f057 Fix Ternary Operator IR bug 2017-05-13 16:08:50 +01:00
gingerBill 9637cc5690 Add #ordered to the "raw" types in raw.odin 2017-05-12 16:04:05 +01:00
Ginger Bill ded99a2cab Fix issue with os.file_size on *nix 2017-05-12 10:29:55 +01:00
Ginger Bill 45eecc0905 Reimplement #ordered again 2017-05-12 10:27:14 +01:00
Ginger Bill 87f1a62ca4 Fix alignment for normal structures to match LLVM 2017-05-10 22:51:35 +01:00
Ginger Bill c6d531df95 Add %% operator (divisor modulo) 2017-05-09 16:21:31 +01:00
Ginger Bill 8677c81da7 Fix ir bug; allow formatting options for arrays & et al. 2017-05-09 12:05:17 +01:00
Ginger Bill 5595daf5a3 Revert demo.odin 2017-05-09 10:01:50 +01:00
Ginger Bill 64b5afd820 Fix issue #63 for block comments not terminating at an EOF 2017-05-09 10:01:10 +01:00
Ginger Bill 7692061eef Add XOR for booleans 2017-05-07 20:52:20 +01:00
Ginger Bill f7f2272c50 Fix fmt_float precision 2017-05-07 11:42:27 +01:00
Ginger Bill 03fbdc3f75 Fix IR printing bug with global unicode identifiers 2017-05-06 23:02:47 +01:00
Ginger Bill ea6a4859ed Merge branch 'master' of https://github.com/gingerBill/Odin 2017-05-06 20:56:18 +01:00
Ginger Bill 615fa82d1f Fix using issue #62 2017-05-06 20:55:09 +01:00
gingerBill b60b310121 Merge pull request #61 from ThisDrunkDane/master
Fix constant casing and add several win32 functions, structure and constants
2017-05-05 21:00:58 +01:00
Mikkel Hjortshoej c7f7e562a0 Add following win32 functions
- ShowCursor
	- GetFileAttributesA
	- FindFirstFileA
	- FindNextFileA
	- FindClose

Add following win32 constants
	- MAX_PATH
	- INVALID_FILE_ATTRIBUTES

Add following win32 structure
	- Find_Data
2017-05-05 20:32:48 +02:00
Mikkel Hjortshoej a317237404 Fix casing on FILE_ATTRIBUTE_DIRECTORY 2017-05-05 20:22:18 +02:00
Ginger Bill 51ea59d76a Fix calculation of vector type sizes 2017-05-04 23:18:54 +01:00
Ginger Bill 789b297f32 Add hidden __tag for union variables. 2017-05-04 20:34:50 +01:00
Ginger Bill 3b25f924cb Remove debug bug 2017-05-03 11:01:17 +01:00
Ginger Bill cc6282a6e3 Fix alignment and size bug of enums; Remove #ordered and make the default #ordered. 2017-05-02 21:16:09 +01:00
Ginger Bill 206a3e093c Remove check on array/slice/dynamic element size 2017-05-02 20:17:53 +01:00
Ginger Bill 19bde275a3 Add files in core 2017-05-01 15:30:16 +01:00
Ginger Bill 634ee450f4 v0.2.1 2017-05-01 15:28:26 +01:00
Ginger Bill 750d7256fc Unary expression for vector (fix) 2017-05-01 15:27:21 +01:00
Ginger Bill fae5df2ed8 Fix IR vector arith conv bug 2017-05-01 15:05:56 +01:00
Ginger Bill 01d9161772 Fix value conversion with enum value on for in. 2017-05-01 10:10:07 +01:00
Ginger Bill aceabb2f2f for in iteration of Enum Type (request from issue #58) 2017-05-01 10:02:25 +01:00
Ginger Bill 04f5fff7fa Improve vector math; Make bprint* return string 2017-05-01 00:38:26 +01:00
Ginger Bill dc5587eae2 Fix statement parsing of unary: & and ^ 2017-04-30 17:20:37 +01:00
Ginger Bill 7057034b75 v0.2.0 2017-04-30 16:28:13 +01:00
Ginger Bill 1430ca30a3 Fix subtype polymorphism implicit conversion 2017-04-30 16:22:24 +01:00
Ginger Bill e63393e394 Add type assertion for any 2017-04-30 15:29:46 +01:00
Ginger Bill 784f3ecf7e Syntax change: cast(T)x => T(x); union_cast(T)x => x.(T); transmute(T)x => transmute(T, x); y:=^x => y:=&x;
Sorry for all the code breaking in this commit :(
2017-04-30 15:09:36 +01:00
Ginger Bill 54ea70df98 Fix issues #50 and #55 2017-04-29 20:06:29 +01:00
Constantine Tarasenkov d05ec5e484 Fix link time error about missing -fPIC flag 2017-04-28 18:08:11 +03:00
Ginger Bill c7575164cc Revert to previous demo 2017-04-28 11:03:19 +01:00
Ginger Bill 99125dc743 Fix issue #51; begin work on atomic types 2017-04-28 11:01:46 +01:00
Ginger Bill b78e970698 Fix issue #48 dependency issue 2017-04-26 23:51:13 +01:00
Ginger Bill 5b8be25938 fmt.String_Buffer, Fix issue #44, Tweak overloading rules 2017-04-26 19:43:17 +01:00
Ginger Bill 29efdc5fc1 Fix initialization of global any types 2017-04-25 15:02:35 +01:00
Ginger Bill a80872b60d Fix checking if a procedure terminates for for loops. 2017-04-25 09:46:30 +01:00
Ginger Bill 822bb51b55 Swap memory layout of any 2017-04-23 18:03:29 +01:00
Ginger Bill c2fa79012e Fix find_using_index_expr 2017-04-23 11:04:22 +01:00
Ginger Bill 3fd37c6dc5 Internal change: IntervalExpr is now a BinaryExpr 2017-04-22 10:10:49 +01:00
Ginger Bill 0ea815db49 Fix constant bounds checking for slicing 2017-04-22 09:40:32 +01:00
Ginger Bill 91ed51ff5c Continue work on custom SSA; Fix double declaration in when statements 2017-04-21 17:56:29 +01:00
Ginger Bill 4d0afc55c3 Making slicing a little more robust 2017-04-21 10:03:27 +01:00
Ginger Bill 9a1566d665 Interval expressions for match statements 2017-04-21 00:13:20 +01:00
Ginger Bill a713e33007 Change interval syntax: .. open range, ..< half-closed range 2017-04-20 23:22:45 +01:00
Ginger Bill c5411a25a9 Change Union representation for LLVM IR; fix dynamic array size 2017-04-19 18:58:23 +01:00
Ginger Bill 95692fda52 Fix bug with union literal checking crashing the compiler 2017-04-18 21:20:41 +01:00
Ginger Bill 813a028ed0 Fix procedure calls from non-regular addressing modes 2017-04-17 22:17:16 +01:00
Ginger Bill 0c22081e5f Fix error printing for basic directives 2017-04-17 19:58:43 +01:00
Ginger Bill 6d9fadf351 Make the ABI changes only affect windows
TODO: decide upon rules for *nix systems
2017-04-17 12:01:04 +01:00
Ginger Bill a213061f33 Change tag checking order 2017-04-16 23:08:48 +01:00
Ginger Bill d1a0a46141 Fix issue #37 for procedure literal scopes 2017-04-16 22:48:29 +01:00
Ginger Bill 187b186112 Add #require_results for procedures 2017-04-16 22:30:48 +01:00
Ginger Bill 5041a35b95 Fix ir printing of constant slices 2017-04-16 22:07:26 +01:00
Ginger Bill 92d4fcedee Update ir type aggregate rules for transmute 2017-04-16 16:44:45 +01:00
Ginger Bill c69df7cd3a Exit program if there were syntax errors 2017-04-16 16:38:05 +01:00
Ginger Bill 67d8f48553 Calling convention, change from bitcast to transmute 2017-04-16 16:28:39 +01:00
Ginger Bill b4a339f2e3 Call convention, pass by pointer: pointers are 16 byte aligned 2017-04-16 10:54:05 +01:00
Ginger Bill 0d7bf58b60 Revert to the old demo 2017-04-16 10:40:24 +01:00
Ginger Bill abb9930725 IR emit C ABI compatible types for calling conventions (Only for x86/amd64 like processors at the moment) 2017-04-16 10:38:42 +01:00
Ginger Bill 169310a9f6 Fix non-ascii function parameters in LLVM IR 2017-04-15 23:14:14 +01:00
Ginger Bill 23a0a6de4b Add parse_int; Fix union bugs with size, alignment, and recursive definition checking 2017-04-14 21:47:59 +01:00
Ginger Bill 0d2dbee84e Fix addressing mode rules for match in statements 2017-04-13 22:42:36 +01:00
Ginger Bill d8d22e34dd Fix fmt for type; remove dead stuff 2017-04-13 19:29:17 +01:00
Ginger Bill 627ee002e8 Fix: map key not getting transferred on rehash 2017-04-11 23:11:05 +01:00
Ginger Bill 8e73d1ce1f Fix map bug which removed N values from the beginning 2017-04-11 22:43:33 +01:00
Ginger Bill b53d16d1d5 Remove debug text 2017-04-11 21:24:10 +01:00
Ginger Bill f5819eafa9 Fix map assignment bug due to growth 2017-04-11 21:13:21 +01:00
Ginger Bill 5916e71d4f Fix slicing bug on dynamic arrays 2017-04-11 16:00:49 +01:00
Ginger Bill 913b9b6447 Remove odin.exe 2017-04-10 22:30:38 +01:00
Ginger Bill 8e55bb2a6c Fix append crash when pointer is passed 2017-04-10 21:09:04 +01:00
root 98d493504b Fix segfault with heap allocation 2017-04-10 20:48:56 +01:00
Ginger Bill 3a3202fbc6 Change code to match original MSVC 2017-04-10 13:27:09 +01:00
Ginger Bill aaf355e750 Basic Linux Build! 2017-04-09 22:33:32 +01:00
gingerBill 0683d2b4f4 Merge pull request #33 from zangent/master
Base of *nix port
2017-04-09 22:01:22 +01:00
Ginger Bill d7fdd3d7b8 Add raw.odin
Forgot to do this in the previous commit, whoops :P
2017-04-09 11:45:41 +01:00
Ginger Bill 83ebb24015 Move to Raw_* types to raw.odin; Add size and align members to Type_Info 2017-04-07 14:05:28 +01:00
Ginger Bill 70f9cacdce Fix cast to any of untyped constants 2017-04-07 09:55:19 +01:00
Zachary Pierson 6b33b254e9 Merged from upstream, fixed 'args' name colission 2017-04-06 18:14:42 -05:00
Zachary Pierson c0019cc305 Merge https://github.com/gingerBill/Odin 2017-04-06 17:50:23 -05:00
Ginger Bill c067a1f0ec Fix ir bugs: global variable names, untyped to any assignment 2017-04-06 11:12:11 +01:00
Zachary Pierson 63345cd0d8 Bridged a bugfix from os_windows to other os's. 2017-04-04 18:51:36 -05:00
Zachary Pierson e41d6261c2 Merge https://github.com/gingerBill/Odin 2017-04-04 18:46:05 -05:00
Ginger Bill 3e80411d37 Fix issue #31; Removed down_cast 2017-04-04 21:54:55 +01:00
Zachary Pierson f952c7c747 Merge https://github.com/gingerBill/Odin 2017-04-03 00:08:00 -05:00
Zachary Pierson 642256f9ba I accidentally left debug stuff (like abs paths) in! Whoops! 2017-04-02 18:46:31 -05:00
Zachary Pierson c9c82da1f3 It's terrible, but I added _some_ form of launch args support for Linux/macOS 2017-04-02 18:42:58 -05:00
Ginger Bill 382a5ca6a2 Update and regression test old demos 2017-04-02 22:03:52 +01:00
Ginger Bill 96e8bb5b6f Add website to README.md 2017-04-02 20:20:14 +01:00
Ginger Bill 22afac2b90 Update README.md with latest demo 2017-04-02 20:10:56 +01:00
Ginger Bill 01da0d1377 Fix make for dynamic arrays 2017-04-02 18:28:45 +01:00
Ginger Bill 8ce58573df len, cap, make; remove .count, .capacity, new_slice 2017-04-02 18:16:45 +01:00
Zachary Pierson ce0d874efd Merge https://github.com/gingerBill/Odin 2017-04-02 03:29:51 -05:00
Ginger Bill 2c8b99337b Fix conj 2017-04-01 22:55:33 +01:00
Ginger Bill 5008e2c88b Add Quaternions: quaternion128, quaternion256 2017-04-01 22:41:23 +01:00
Ginger Bill 90fc9abeae Fix constant conversion for complex numbers from integers 2017-04-01 12:12:08 +01:00
Ginger Bill dc303cde21 Complex numbers: complex64 complex128 2017-04-01 12:07:41 +01:00
Zachary Pierson 24b33374b7 Reverted the main proc changed, after a chat with Bill about better solutions. 2017-03-31 05:31:45 -05:00
Zachary Pierson 3315dc7f25 Literally just a commit to revert a previous one. 2017-03-31 05:30:09 -05:00
Zachary Pierson 77b3295de5 Added checking for params and return values in main 2017-03-30 01:21:05 -05:00
Zachary Pierson 1349aa6f2c Merge https://github.com/gingerBill/Odin, cleaned up a bit, fixed the object file version message on macOS 2017-03-30 00:26:46 -05:00
Ginger Bill a75ccb6fbc v0.1.3 2017-03-27 20:32:36 +01:00
Zachary Pierson 7a28827602 Forgot to include stdio.h since Win32 won't resolve it otherwise. 2017-03-21 19:30:54 -05:00
Zachary Pierson c61015b1fe Updated shell.bat for Visual Studio 2017 2017-03-21 19:17:41 -05:00
Zac Pierson e935f8e2ff Fixed os_linux and os_x read_entire_file function not null-terminating data. 2017-03-21 16:00:11 -05:00
Zac Pierson 690c682847 Remember kids, always test your code. There was a variable name colission in dlsym D: 2017-03-21 14:57:09 -05:00
Zac Pierson f541dd40db Fixed some memory leaks and made os_* use strings.odin 2017-03-21 14:54:29 -05:00
Zac Pierson c7bb861d3c Merge https://github.com/gingerBill/Odin
"Fixed" a proc overload bug. Still needs a *real* fix.
2017-03-21 14:16:42 -05:00
Ginger Bill 188b290dd5 Update version number 2017-03-19 21:03:56 +00:00
Ginger Bill c6ff961088 Add base 12 in strconv.odin 2017-03-19 21:03:29 +00:00
Ginger Bill c26990c22d Multiple type cases for match in 2017-03-19 20:55:39 +00:00
Ginger Bill c34d839f9f Add named branches for match statements 2017-03-19 17:36:08 +00:00
Ginger Bill 5562364a98 Add branch labels for loops; using list 2017-03-19 16:59:11 +00:00
Ginger Bill 32150e401e Update gb.h 2017-03-17 12:30:59 +00:00
Ginger Bill aaec8bf423 windows.odin TYPE_NAME to Type_Name; More SSA work and SSA printing for debugging 2017-03-12 16:42:51 +00:00
Ginger Bill 0fcbda951a Finally fix signed integer conversion and printing 2017-03-10 10:34:25 +00:00
Ginger Bill e2734a2dc6 Begin work on the custom backend 2017-03-05 21:22:33 +00:00
Ginger Bill 5adfbec847 Refactoring of code: remove make prefix on many procedures 2017-03-05 15:03:01 +00:00
Ginger Bill 4ef4605d6d Move files to misc 2017-03-03 11:20:22 +00:00
Ginger Bill 2aa402f462 Cleanup root directory 2017-03-03 11:19:12 +00:00
Ginger Bill 00f6bee454 Update README.md 2017-03-03 11:15:34 +00:00
Zac Pierson d890731716 Merge https://github.com/gingerBill/Odin 2017-03-02 15:41:19 -06:00
Zachary Pierson 231ea8b026 Merge https://github.com/gingerBill/Odin 2017-02-27 23:25:47 -06:00
Zachary Pierson 5bbdb3a3a3 Merge https://github.com/gingerBill/Odin 2017-02-25 02:07:58 -06:00
Zachary Pierson 27aa07307b Merge https://github.com/gingerBill/Odin 2017-02-24 15:53:56 -06:00
Zac Pierson 20b9f1ff59 Added getenv to the *nix stdlib. 2017-02-23 15:29:41 -06:00
Zac Pierson 561c583b3f Merge https://github.com/gingerBill/Odin 2017-02-22 10:57:30 -06:00
Zac Pierson 8d5896ab7e Merge https://github.com/gingerBill/Odin 2017-02-20 10:14:52 -06:00
Zac Pierson 802b1a70f8 Fixed an error in function naming in os_linux 2017-02-15 11:20:11 -06:00
Zac Pierson aaa4dd5c36 Merge https://github.com/gingerBill/odin 2017-02-15 10:21:38 -06:00
Zachary Pierson 9d19ee7e4c Changed standard libraries for MacOS and Linux to be closer to os_windows. 2017-02-12 18:25:58 -06:00
Zachary Pierson 8df3175f10 Updated Linux standard library to convert c strs 2017-02-12 17:22:27 -06:00
Zachary Pierson ebb10e5597 One of the warning flags was misspelled. Oops! 2017-02-12 16:09:21 -06:00
Zachary Pierson 047f883078 Updated warning removal list, and made system_exec_command_line_app in main.c return the exit code. 2017-02-12 16:08:09 -06:00
Zachary Pierson 320c22e08a Merge https://github.com/gingerBill/Odin 2017-02-12 16:04:13 -06:00
Zachary Pierson a9398bf30f Tested MacOS. If a commit doesn't follow in 15 minutes, Linux works too! 2017-02-12 00:21:25 -06:00
Zachary Pierson 7829421085 Fixed Windows (updated gb.h) | Need to test on MacOS and Linux now! 2017-02-11 23:52:56 -06:00
Zachary Pierson c50aabd916 Merging from gingerBill's master 2017-02-11 23:35:07 -06:00
Zachary Pierson 3f3122bccc Temporary fix for an Odin bug. 2017-02-11 18:54:54 -06:00
Zachary Pierson fc1a006de1 Added support for reading files on MacOS and Linux 2017-02-11 17:24:47 -06:00
Zachary Pierson 754b368140 Added dynamic library loading to Linux and MacOS's standard libraries. 2017-02-11 15:09:53 -06:00
Zachary Pierson a49e888ce6 Merge https://github.com/gingerBill/Odin 2017-02-11 13:48:16 -06:00
Zac Pierson 99c663d9f3 Questioning whether MacOS libraries should be .dylib or .so 2017-02-11 01:10:03 -06:00
Zachary Pierson afac95e092 Oh, I left math.odin open when I merged gingerBill's changes. Oops. Updated to his version. 2017-02-11 00:33:12 -06:00
Zachary Pierson 05486f9fa3 I'm not sure what I changed here, to be honest. I've ctrl-z'd everything, but git's still complaining. 2017-02-11 00:30:04 -06:00
Zachary Pierson cad46ae51c Merge https://github.com/gingerBill/Odin 2017-02-10 23:41:23 -06:00
Zachary Pierson 3424b2badd Added ability to use -framework on MacOS 2017-02-10 23:33:30 -06:00
Zachary Pierson 3445a28c4a Code quality upkeep. Fixed a broken thread finding assembly instruction in gb.h 2017-02-09 01:40:45 -06:00
Zac Pierson 7f6b83d50c Fixed gb.h - the file handle for /proc/cpuinfo is needed to read chars. 2017-02-08 11:59:54 -06:00
Zac Pierson 72d4bfb32a Merge https://github.com/gingerBill/Odin 2017-02-08 11:50:33 -06:00
Zachary Pierson 37f7630a9e Updated README.md to reflect Linux's dependancy on clang for now. 2017-02-07 23:33:36 -06:00
Zachary Pierson 73c5c5d5d3 Linker on MacOS and GNU/Linux now includes foreign_system_libraries. Fixed foreign_system_library not respecting 'when' condition. 2017-02-07 23:21:52 -06:00
Zac Pierson 584869730a Linux can build now! Woo! 2017-02-07 15:07:20 -06:00
Zachary Pierson 90ab448bca Modified the test program to see where the compiler inserted the code. 2017-02-07 12:26:15 -06:00
Zachary Pierson 8becbdc1b2 Added a very basic Linux standard library shamelessly stolen from the MacOS one.
Made Linux (almost) work. The generated binaries segfault, but it's so close I can almost taste it.
2017-02-07 00:28:21 -06:00
Zachary Pierson eeeb90c441 MacOS is able to run Hello World! 2017-02-06 21:47:58 -06:00
Zac Pierson 6efd400c98 Updated build script in an attempt to track down a segfault. It's not helping, though. 2017-02-06 15:45:51 -06:00
Zac Pierson 5cfa4ba580 Added Linux functions throughout the code, but it segfaults. 2017-02-06 12:26:41 -06:00
95 changed files with 46050 additions and 31354 deletions
+9 -4
View File
@@ -251,8 +251,13 @@ paket-files/
# Project Specific
# - Windows
*.sln
!misc/llvm-bim/lli.exe
!misc/llvm-bim/opt.exe
builds
bin
builds/
bin/
*.exe
# - Linux/MacOS
odin
odin.dSYM
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016 Ginger Bill. All rights reserved.
Copyright (c) 2016-2017 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
+23 -8
View File
@@ -1,4 +1,4 @@
<img src="logo-slim.png" alt="Odin logo" height="74">
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
# The Odin Programming Language
@@ -8,7 +8,8 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* built for modern systems
* joy of programming
* metaprogramming
* designed for good programmers
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
## Demonstrations:
* First Talk & Demo
@@ -18,15 +19,29 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* [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)
* [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)
## Requirements to build and run
* Windows
* x86-64
* MSVC 2015 installed (C99 support)
* Requires MSVC's link.exe as the linker
- run `vcvarsall.bat` to setup the path
- 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`
* Requires MSVC's link.exe as the linker
* run `vcvarsall.bat` to setup the path
- MacOS
* x86-64
* LLVM explicitly installed (`brew install llvm`)
* XCode installed (for the linker)
- GNU/Linux
* x86-64
* Build tools (ld)
* LLVM installed
* Clang installed (temporary - this is Calling the linker for now)
## Warnings
+12 -16
View File
@@ -5,7 +5,7 @@ set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=0
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
if %release_mode% EQU 0 ( rem Debug
set compiler_flags=%compiler_flags% -Od -MDd -Z7
@@ -17,10 +17,13 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
-wd4201 -wd4204 -wd4244 ^
-wd4306 ^
-wd4456 -wd4457 -wd4480 ^
-wd4505 -wd4512 -wd4550
-wd4201 ^
-wd4512
rem -wd4100 -wd4101 -wd4127 -wd4189 ^
rem -wd4201 -wd4204 -wd4244 ^
rem -wd4306 ^
rem -wd4456 -wd4457 -wd4480 ^
rem -wd4505 -wd4512 -wd4550
set compiler_includes=
set libs= ^
@@ -38,22 +41,15 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
set linker_settings=%libs% %linker_flags%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.c" ^
cl %compiler_settings% "src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run code/demo.odin
rem && odin build code/Jaze/src/main.odin
rem && odin build_dll code/example.odin ^
rem odin run code/demo.odin
&& odin run code/demo.odin -opt=0
rem && odin docs core/fmt.odin
rem pushd src\asm
rem nasm hellope.asm -fwin64 -o hellope.obj ^
rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^
rem && hellope.exe
rem popd
del *.obj > NUL 2> NUL
:end_of_build
Executable
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
release_mode=0
warnings_to_disable="-std=c++11 -g -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings"
libraries="-pthread -ldl -lm -lstdc++"
other_args=""
compiler="clang"
if [ "$release_mode" -eq "0" ]; then
other_args="${other_args} -g -fno-inline-functions"
fi
if [[ "$(uname)" == "Darwin" ]]; then
# Set compiler to clang on MacOS
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
compiler="clang"
other_args="${other_args} -liconv"
fi
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
./odin run code/demo.odin
+568 -134
View File
@@ -1,167 +1,601 @@
#import "fmt.odin";
#import "atomic.odin";
#import "hash.odin";
#import "math.odin";
#import "mem.odin";
#import "opengl.odin";
#import "os.odin";
#import "strconv.odin";
#import "sync.odin";
main :: proc() {
// buf: [64]byte;
// // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64);
// x := 624.123;
// s := strconv.format_float(buf[..], x, 'f', 6, 64);
// fmt.println(s);
// fmt.printf("%3d\n", 102);
import (
"fmt.odin";
"strconv.odin";
"mem.odin";
"thread.odin" when ODIN_OS == "windows";
win32 "sys/windows.odin" when ODIN_OS == "windows";
s := new_slice(int, 0, 10);
append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2);
fmt.println(s);
when false {
/*
Version 0.1.1
Added:
* Dynamic Arrays `[dynamic]Type`
* Dynamic Maps `map[Key]Value`
* Dynamic array and map literals
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
* Allow `_` in numbers
* Variadic `append`
* fmt.sprint*
* Entities prefixes with an underscore do not get exported on imports
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
* enum types have an implict `names` field, a []string of all the names in that enum
* immutable variables are "completely immutable" - rules need a full explanation
* `slice_to_bytes` - convert any slice to a slice of bytes
* `union_cast` allows for optional ok check
* Record type field `names` (struct/raw_union/enum)
* ?: ternary operator
* Unions with variants and common fields
* New built-in procedures
- `delete` to delete map entries `delete(m, key)`
- `clear` to clear dynamic maps and arrays `clear(map_or_array)`
- `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
* Unexported entities and fields using an underscore prefix
Removed:
* Maybe/option types
* Remove `type` keyword and other "reserved" keywords
* `compile_assert` and `assert`return the value of the condition for semantic reasons
Changed:
* thread_local -> #thread_local
* #include -> #load
* Files only get checked if they are actually used
* match x in y {} // For type match statements
* Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch
* Core library additions to Windows specific stuff
Fixes:
* Many fmt.* fixes
* Overloading bug due to comparison of named types
* Overloading bug due to `#import .` collision
* disallow a `cast` from pointers of unions
* Minor bugs in generated IR code for slices
To come very Soon:
* Linux and OS X builds (unofficial ones do exist already)
"atomics.odin";
"bits.odin";
"hash.odin";
"math.odin";
"opengl.odin";
"os.odin";
"raw.odin";
"sort.odin";
"strings.odin";
"sync.odin";
"types.odin";
"utf8.odin";
"utf16.odin";
*/
{
)
general_stuff :: proc() {
{ // `do` for inline statmes rather than block
foo :: proc() do fmt.println("Foo!");
if false do foo();
for false do foo();
when false do foo();
if false do foo();
else do foo();
}
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
{ // Removal of `++` and `--` (again)
x: int;
x += 1;
x -= 1;
}
{ // Casting syntaxes
i := i32(137);
ptr := &i;
fp1 := (^f32)(ptr);
// ^f32(ptr) == ^(f32(ptr))
fp2 := cast(^f32)ptr;
f1 := (^f32)(ptr)^;
f2 := (cast(^f32)ptr)^;
// Questions: Should there be two ways to do it?
}
/*
* Remove *_val_of built-in procedures
* size_of, align_of, offset_of
* type_of, type_info_of
*/
{ // `expand_to_tuple` built-in procedure
Foo :: struct {
x: int;
b: bool;
}
fmt.println(Fruit.names);
f := Foo{137, true};
x, b := expand_to_tuple(f);
fmt.println(f);
fmt.println(x, b);
fmt.println(expand_to_tuple(f));
}
{
m: map[f32]int;
reserve(m, 16);
defer free(m);
// .. half-closed range
// ... open range
m[1.0] = 1278;
m[2.0] = 7643;
m[3.0] = 564;
_, ok := m[3.0];
c := m[3.0];
assert(ok && c == 564);
fmt.print("map[");
i := 0;
for val, key in m {
if i > 0 {
fmt.print(", ");
}
fmt.printf("%v=%v", key, val);
i += 1;
}
fmt.println("]");
for in 0..2 {} // 0, 1
for in 0...2 {} // 0, 1, 2
}
}
nested_struct_declarations :: proc() {
{
m := map[string]u32{
"a" = 56,
"b" = 13453,
"c" = 7654,
FooInteger :: int;
Foo :: struct {
i: FooInteger;
};
defer free(m);
c := m["c"];
_, ok := m["c"];
assert(ok && c == 7654);
fmt.println(m);
f := Foo{FooInteger(137)};
}
{
x: [dynamic]f64;
reserve(x, 16);
defer free(x);
append(x, 2_000_000.500_000, 3, 5, 7);
Foo :: struct {
Integer :: int;
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
i: Integer;
}
fmt.println();
f := Foo{Foo.Integer(137)};
}
}
default_struct_values :: proc() {
{
Vector3 :: struct {
x: f32;
y: f32;
z: f32;
}
v: Vector3;
fmt.println(v);
}
{
// Default values must be constants
Vector3 :: struct {
x: f32 = 1;
y: f32 = 4;
z: f32 = 9;
}
v: Vector3;
fmt.println(v);
v = Vector3{};
fmt.println(v);
// Uses the same semantics as a default values in a procedure
v = Vector3{137};
fmt.println(v);
v = Vector3{z = 137};
fmt.println(v);
}
{
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x);
Vector3 :: struct {
x := 1.0;
y := 4.0;
z := 9.0;
}
stack_default: Vector3;
stack_literal := Vector3{};
heap_one := new(Vector3); defer free(heap_one);
heap_two := new_clone(Vector3{}); defer free(heap_two);
fmt.println("stack_default - ", stack_default);
fmt.println("stack_literal - ", stack_literal);
fmt.println("heap_one - ", heap_one^);
fmt.println("heap_two - ", heap_two^);
N :: 4;
stack_array: [N]Vector3;
heap_array := new([N]Vector3); defer free(heap_array);
heap_slice := make([]Vector3, N); defer free(heap_slice);
fmt.println("stack_array[1] - ", stack_array[1]);
fmt.println("heap_array[1] - ", heap_array[1]);
fmt.println("heap_slice[1] - ", heap_slice[1]);
}
}
union_type :: proc() {
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
val: union{int, bool};
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
val = nil;
match v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
{
// There is a duality between `any` and `union`
// An `any` has a pointer to the data and allows for any type (open)
// A `union` has as binary blob to store the data and allows only certain types (closed)
// The following code is with `any` but has the same syntax
val: any;
val = 137;
if i, ok := val.(int); ok {
fmt.println(i);
}
val = true;
fmt.println(val);
val = nil;
match v in val {
case int: fmt.println("int", v);
case bool: fmt.println("bool", v);
case: fmt.println("nil");
}
}
Vector3 :: struct {
x, y, z: f32;
};
Quaternion :: struct {
x, y, z: f32;
w: f32 = 1;
};
// More realistic examples
{
// NOTE(bill): For the above basic examples, you may not have any
// particular use for it. However, my main use for them is not for these
// simple cases. My main use is for hierarchical types. Many prefer
// subtyping, embedding the base data into the derived types. Below is
// an example of this for a basic game Entity.
Entity :: struct {
id: u64;
name: string;
position: Vector3;
orientation: Quaternion;
derived: any;
}
Frog :: struct {
using entity: Entity;
jump_height: f32;
}
Monster :: struct {
using entity: Entity;
is_robot: bool;
is_zombie: bool;
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t^;
return t;
}
entity := new_entity(Monster);
match e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
}
{
// NOTE(bill): A union can be used to achieve something similar. Instead
// of embedding the base data into the derived types, the derived data
// in embedded into the base type. Below is the same example of the
// basic game Entity but using an union.
Entity :: struct {
id: u64;
name: string;
position: Vector3;
orientation: Quaternion;
derived: union {Frog, Monster};
}
Frog :: struct {
using entity: ^Entity;
jump_height: f32;
}
Monster :: struct {
using entity: ^Entity;
is_robot: bool;
is_zombie: bool;
}
// See `parametric_polymorphism` procedure for details
new_entity :: proc(T: type) -> ^Entity {
t := new(Entity);
t.derived = T{entity = t};
return t;
}
entity := new_entity(Monster);
match e in entity.derived {
case Frog:
fmt.println("Ribbit");
case Monster:
if e.is_robot do fmt.println("Robotic");
if e.is_zombie do fmt.println("Grrrr!");
}
// NOTE(bill): As you can see, the usage code has not changed, only its
// memory layout. Both approaches have their own advantages but they can
// be used together to achieve different results. The subtyping approach
// can allow for a greater control of the memory layout and memory
// allocation, e.g. storing the derivatives together. However, this is
// also its disadvantage. You must either preallocate arrays for each
// derivative separation (which can be easily missed) or preallocate a
// bunch of "raw" memory; determining the maximum size of the derived
// types would require the aid of metaprogramming. Unions solve this
// particular problem as the data is stored with the base data.
// Therefore, it is possible to preallocate, e.g. [100]Entity.
// It should be noted that the union approach can have the same memory
// layout as the any and with the same type restrictions by using a
// pointer type for the derivatives.
/*
Entity :: struct {
...
derived: union{^Frog, ^Monster};
}
Frog :: struct {
using entity: Entity;
...
}
Monster :: struct {
using entity: Entity;
...
}
new_entity :: proc(T: type) -> ^Entity {
t := new(T);
t.derived = t;
return t;
}
*/
}
}
parametric_polymorphism :: proc() {
print_value :: proc(value: $T) {
fmt.printf("print_value: %T %v\n", value, value);
}
v1: int = 1;
v2: f32 = 2.1;
v3: f64 = 3.14;
v4: string = "message";
print_value(v1);
print_value(v2);
print_value(v3);
print_value(v4);
fmt.println();
add :: proc(p, q: $T) -> T {
x: T = p + q;
return x;
}
a := add(3, 4);
fmt.printf("a: %T = %v\n", a, a);
b := add(3.2, 4.3);
fmt.printf("b: %T = %v\n", b, b);
// This is how `new` is implemented
alloc_type :: proc(T: type) -> ^T {
t := cast(^T)alloc(size_of(T), align_of(T));
t^ = T{}; // Use default initialization value
return t;
}
copy_slice :: proc(dst, src: []$T) -> int {
n := min(len(dst), len(src));
if n > 0 {
mem.copy(&dst[0], &src[0], n*size_of(T));
}
return n;
}
double_params :: proc(a: $A, b: $B) -> A {
return a + A(b);
}
fmt.println(double_params(12, 1.345));
{ // Polymorphic Types and Type Specialization
Table :: struct(Key, Value: type) {
Slot :: struct {
occupied: bool;
hash: u32;
key: Key;
value: Value;
}
SIZE_MIN :: 32;
count: int;
allocator: Allocator;
slots: []Slot;
}
// Only allow types that are specializations of a (polymorphic) slice
make_slice :: proc(T: type/[]$E, len: int) -> T {
return make(T, len);
}
// Only allow types that are specializations of `Table`
allocate :: proc(table: ^$T/Table, capacity: int) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
push_context c {
table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN));
}
}
expand :: proc(table: ^$T/Table) {
c := context;
if table.allocator.procedure != nil do c.allocator = table.allocator;
push_context c {
old_slots := table.slots;
cap := max(2*cap(table.slots), T.SIZE_MIN);
allocate(table, cap);
for s in old_slots do if s.occupied {
put(table, s.key, s.value);
}
free(old_slots);
}
}
// Polymorphic determination of a polymorphic struct
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
index := find_index(table, key, hash);
if index < 0 {
if f64(table.count) >= 0.75*f64(cap(table.slots)) {
expand(table);
}
assert(table.count <= cap(table.slots));
hash := get_hash(key);
index = int(hash % u32(cap(table.slots)));
for table.slots[index].occupied {
if index += 1; index >= cap(table.slots) {
index = 0;
}
}
table.count += 1;
}
slot := &table.slots[index];
slot.occupied = true;
slot.hash = hash;
slot.key = key;
slot.value = value;
}
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
hash := get_hash(key);
index := find_index(table, key, hash);
if index < 0 {
return Value{}, false;
}
return table.slots[index].value, true;
}
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
if cap(table.slots) <= 0 do return -1;
index := int(hash % u32(cap(table.slots)));
for table.slots[index].occupied {
if table.slots[index].hash == hash {
if table.slots[index].key == key {
return index;
}
}
if index += 1; index >= cap(table.slots) {
index = 0;
}
}
return -1;
}
get_hash :: proc(s: string) -> u32 { // fnv32a
h: u32 = 0x811c9dc5;
for i in 0..len(s) {
h = (h ~ u32(s[i])) * 0x01000193;
}
return h;
}
table: Table(string, int);
for i in 0..36 do put(&table, "Hellope", i);
for i in 0..42 do put(&table, "World!", i);
found, _ := find(&table, "Hellope");
fmt.printf("`found` is %v\n", found);
found, _ = find(&table, "World!");
fmt.printf("`found` is %v\n", found);
// I would not personally design a hash table like this in production
// but this is a nice basic example
// A better approach would either use a `u64` or equivalent for the key
// and let the user specify the hashing function or make the user store
// the hashing procedure with the table
}
}
prefix_table := [...]string{
"White",
"Red",
"Green",
"Blue",
"Octarine",
"Black",
};
threading_example :: proc() {
when ODIN_OS == "windows" {
unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
array[index] = array[len(array)-1];
pop(array);
}
ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
__bounds_check_error_loc(loc, index, len(array));
copy(array[index..], array[index+1..]);
pop(array);
}
worker_proc :: proc(t: ^thread.Thread) -> int {
for iteration in 1...5 {
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
// win32.sleep(1);
}
return 0;
}
threads := make([]^thread.Thread, 0, len(prefix_table));
defer free(threads);
for i in 0..len(prefix_table) {
if t := thread.create(worker_proc); t != nil {
t.init_context = context;
t.use_init_context = true;
t.user_index = len(threads);
append(&threads, t);
thread.start(t);
}
}
for len(threads) > 0 {
for i := 0; i < len(threads); {
if t := threads[i]; thread.is_done(t) {
fmt.printf("Thread %d is done\n", t.user_index);
thread.destroy(t);
ordered_remove(&threads, i);
} else {
i += 1;
}
}
}
}
}
main :: proc() {
when true {
fmt.println("\n# general_stuff"); general_stuff();
fmt.println("\n# nested_struct_declarations"); nested_struct_declarations();
fmt.println("\n# default_struct_values"); default_struct_values();
fmt.println("\n# union_type"); union_type();
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
fmt.println("\n# threading_example"); threading_example();
}
}
+430
View File
@@ -0,0 +1,430 @@
import (
"fmt.odin";
"atomics.odin";
"bits.odin";
"decimal.odin";
"hash.odin";
"math.odin";
"mem.odin";
"opengl.odin";
"os.odin";
"raw.odin";
"strconv.odin";
"strings.odin";
"sync.odin";
"sort.odin";
"types.odin";
"utf8.odin";
"utf16.odin";
/*
*/
)
general_stuff :: proc() {
// Complex numbers
a := 3 + 4i;
b: complex64 = 3 + 4i;
c: complex128 = 3 + 4i;
d := complex(2, 3);
e := a / conj(a);
fmt.println("(3+4i)/(3-4i) =", e);
fmt.println(real(e), "+", imag(e), "i");
// C-style variadic procedures
foreign __llvm_core {
// The variadic part allows for extra type checking too which C does not provide
c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
}
str := "%d\n\x00";
// c_printf(&str[0], i32(789456123));
Foo :: struct {
x: int;
y: f32;
z: string;
}
foo := Foo{123, 0.513, "A string"};
x, y, z := expand_to_tuple(foo);
fmt.println(x, y, z);
compile_assert(type_of(x) == int);
compile_assert(type_of(y) == f32);
compile_assert(type_of(z) == string);
// By default, all variables are zeroed
// This can be overridden with the "uninitialized value"
// This is similar to `nil` but applied to everything
undef_int: int = ---;
// Context system is now implemented using Implicit Parameter Passing (IPP)
// The previous implementation was Thread Local Storage (TLS)
// IPP has the advantage that it works on systems without TLS and that you can
// link the context to the stack frame and thus look at previous contexts
//
// It does mean that a pointer is implicitly passed procedures with the default
// Odin calling convention (#cc_odin)
// This can be overridden with something like #cc_contextless or #cc_c if performance
// is worried about
}
foreign_blocks :: proc() {
// See sys/windows.odin
}
default_arguments :: proc() {
hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
fmt.println("\nTesting default arguments:");
hello(1, 2);
hello(1);
hello();
}
named_arguments :: proc() {
Colour :: enum {
Red,
Orange,
Yellow,
Green,
Blue,
Octarine,
};
using Colour;
make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
fmt.println();
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
}
make_character("Frank", "¡Ay, caramba!", Blue, Green);
// As the procedures have more and more parameters, it is very easy
// to get many of the arguments in the wrong order especialy if the
// types are the same
make_character("¡Ay, caramba!", "Frank", Green, Blue);
// Named arguments help to disambiguate this problem
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
least_favourite_colour = Green, favourite_colour = Blue);
// The named arguments can be specifed in any order.
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green, name = "Dennis");
// NOTE: You cannot mix named arguments with normal values
/*
make_character("Dennis",
favourite_colour = Octarine, catch_phrase = "U wot m8!",
least_favourite_colour = Green);
*/
// Named arguments can also aid with default arguments
numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
g_str := g ? "true" : "false";
fmt.printf("How many?! %s: %v\n", s, g_str);
}
numerous_things("First");
numerous_things(s = "Second", g = true);
// Default values can be placed anywhere, not just at the end like in other languages
weird :: proc(pre: string, mid: int = 0, post: string) {
fmt.println(pre, mid, post);
}
weird("How many things", 42, "huh?");
weird(pre = "Prefix", post = "Pat");
}
default_return_values :: proc() {
foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
match x {
case 0: return;
case 1: return "Goodbye";
case 2: return "Goodbye", "cruel world...";
case 3: return second = "cruel world...", first = "Goodbye";
}
return second = "my old friend.";
}
fmt.printf("%s %s\n", foo(0));
fmt.printf("%s %s\n", foo(1));
fmt.printf("%s %s\n", foo(2));
fmt.printf("%s %s\n", foo(3));
fmt.printf("%s %s\n", foo(4));
fmt.println();
// A more "real" example
Error :: enum {
None,
WhyTheNumberThree,
TenIsTooBig,
};
Entity :: struct {
name: string;
id: u32;
}
some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
match {
case input == 3: return err = Error.WhyTheNumberThree;
case input >= 10: return err = Error.TenIsTooBig;
}
e := new(Entity);
e.id = u32(input);
return result = e;
}
}
call_location :: proc() {
amazing :: proc(n: int, using loc := #caller_location) {
fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
fully_pathed_filename, line, column);
fmt.printf("Normal -> %d\n", n);
fmt.printf("Amazing -> %d\n", n+1);
fmt.println();
}
loc := #location(main);
fmt.println("`main` is located at", loc);
fmt.println("This line is located at", #location());
fmt.println();
amazing(3);
amazing(4, #location(call_location));
// See _preload.odin for the implementations of `assert` and `panic`
}
explicit_parametric_polymorphic_procedures :: proc() {
// This is how `new` is actually implemented, see _preload.odin
alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
int_ptr := alloc_type(int);
defer free(int_ptr);
int_ptr^ = 137;
fmt.println(int_ptr, int_ptr^);
// Named arguments work too!
another_ptr := alloc_type(T = f32);
defer free(another_ptr);
add :: proc(T: type, args: ...T) -> T {
res: T;
for arg in args do res += arg;
return res;
}
fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
swap :: proc(T: type, a, b: ^T) {
tmp := a^;
a^ = b^;
b^ = tmp;
}
a, b: int = 3, 4;
fmt.println("Pre-swap:", a, b);
swap(int, &a, &b);
fmt.println("Post-swap:", a, b);
a, b = b, a; // Or use this syntax for this silly example case
Vector2 :: struct {x, y: f32;};
{
// A more complicated example using subtyping
// Something like this could be used in a game
Entity :: struct {
using position: Vector2;
flags: u64;
id: u64;
derived: any;
}
Rock :: struct {
using entity: Entity;
heavy: bool;
}
Door :: struct {
using entity: Entity;
open: bool;
}
Monster :: struct {
using entity: Entity;
is_robot: bool;
is_zombie: bool;
}
new_entity :: proc(T: type, x, y: f32) -> ^T {
result := new(T);
result.derived = result^;
result.x = x;
result.y = y;
return result;
}
entities: [dynamic]^Entity;
rock := new_entity(Rock, 3, 5);
// Named arguments work too!
door := new_entity(T = Door, x = 3, y = 6);
// And named arguments can be any order
monster := new_entity(
y = 1,
x = 2,
T = Monster,
);
append(&entities, rock, door, monster);
fmt.println("Subtyping");
for entity in entities {
match e in entity.derived {
case Rock: fmt.println("Rock", e.x, e.y);
case Door: fmt.println("Door", e.x, e.y);
case Monster: fmt.println("Monster", e.x, e.y);
}
}
}
{
Entity :: struct {
using position: Vector2;
flags: u64;
id: u64;
variant: union { Rock, Door, Monster };
}
Rock :: struct {
using entity: ^Entity;
heavy: bool;
}
Door :: struct {
using entity: ^Entity;
open: bool;
}
Monster :: struct {
using entity: ^Entity;
is_robot: bool;
is_zombie: bool;
}
new_entity :: proc(T: type, x, y: f32) -> ^T {
result := new(Entity);
result.variant = T{entity = result};
result.x = x;
result.y = y;
return cast(^T)&result.variant;
}
entities: [dynamic]^Entity;
rock := new_entity(Rock, 3, 5);
// Named arguments work too!
door := new_entity(T = Door, x = 3, y = 6);
// And named arguments can be any order
monster := new_entity(
y = 1,
x = 2,
T = Monster,
);
append(&entities, rock, door, monster);
fmt.println("Union");
for entity in entities {
match e in entity.variant {
case Rock: fmt.println("Rock", e.x, e.y);
case Door: fmt.println("Door", e.x, e.y);
case Monster: fmt.println("Monster", e.x, e.y);
}
}
}
}
implicit_polymorphic_assignment :: proc() {
yep :: proc(p: proc(x: int)) {
p(123);
}
frank :: proc(x: $T) do fmt.println("frank ->", x);
tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
yep(frank);
// yep(tim);
}
main :: proc() {
/*
foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
foo :: proc(x: type) do fmt.println("#3", type_info(x));
f :: foo;
f(y = 3785.1546, x = 123);
f(x = int, y = 897.513);
f(x = f32);
general_stuff();
foreign_blocks();
default_arguments();
named_arguments();
default_return_values();
call_location();
explicit_parametric_polymorphic_procedures();
implicit_polymorphic_assignment();
// Command line argument(s)!
// -opt=0,1,2,3
*/
/*
program := "+ + * - /";
accumulator := 0;
for token in program {
match token {
case '+': accumulator += 1;
case '-': accumulator -= 1;
case '*': accumulator *= 2;
case '/': accumulator /= 2;
case: // Ignore everything else
}
}
fmt.printf("The program \"%s\" calculates the value %d\n",
program, accumulator);
*/
}
+97 -90
View File
@@ -1,91 +1,91 @@
#import "win32.odin" when ODIN_OS == "windows";
#import "fmt.odin";
#import "math.odin";
#import "os.odin";
#import gl "opengl.odin";
import win32 "sys/windows.odin" when ODIN_OS == "windows";
import wgl "sys/wgl.odin" when ODIN_OS == "windows";
import "fmt.odin";
import "math.odin";
import "os.odin";
import gl "opengl.odin";
TWO_HEARTS :: '💕';
const TWO_HEARTS = '💕';
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
time_now :: proc() -> f64 {
var win32_perf_count_freq = win32.get_query_performance_frequency();
proc time_now() -> f64 {
assert(win32_perf_count_freq != 0);
counter: i64;
win32.QueryPerformanceCounter(^counter);
result := counter as f64 / win32_perf_count_freq as f64;
return result;
var counter: i64;
win32.query_performance_counter(&counter);
return f64(counter) / f64(win32_perf_count_freq);
}
win32_print_last_error :: proc() {
err_code := win32.GetLastError() as int;
proc win32_print_last_error() {
var err_code = win32.get_last_error();
if err_code != 0 {
fmt.println("GetLastError: %", err_code);
fmt.println("get_last_error: ", err_code);
}
}
// Yuk!
to_c_string :: proc(s: string) -> []u8 {
c_str := new_slice(u8, s.count+1);
copy(c_str, s as []byte);
c_str[s.count] = 0;
proc to_c_string(s: string) -> []u8 {
var c_str = make([]u8, len(s)+1);
copy(c_str, []u8(s));
c_str[len(s)] = 0;
return c_str;
}
Window :: struct {
width, height: int;
wc: win32.WNDCLASSEXA;
dc: win32.HDC;
hwnd: win32.HWND;
opengl_context, rc: win32.HGLRC;
c_title: []u8;
type Window struct {
width, height: int,
wc: win32.WndClassExA,
dc: win32.Hdc,
hwnd: win32.Hwnd,
opengl_context, rc: wgl.Hglrc,
c_title: []u8,
}
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
proc make_window(title: string, msg, height: int, window_proc: win32.WndProc) -> (Window, bool) {
using win32;
w: Window;
var w: Window;
w.width, w.height = msg, height;
class_name := "Win32-Odin-Window\x00";
c_class_name := class_name.data;
if title[title.count-1] != 0 {
var class_name = "Win32-Odin-Window\x00";
var c_class_name = &class_name[0];
if title[len(title)-1] != 0 {
w.c_title = to_c_string(title);
} else {
w.c_title = title as []u8;
w.c_title = []u8(title);
}
instance := GetModuleHandleA(nil);
var instance = get_module_handle_a(nil);
w.wc = WNDCLASSEXA{
size = size_of(WNDCLASSEXA) as u32,
w.wc = WndClassExA{
size = size_of(WndClassExA),
style = CS_VREDRAW | CS_HREDRAW,
instance = instance as HINSTANCE,
instance = Hinstance(instance),
class_name = c_class_name,
wnd_proc = window_proc,
};
if RegisterClassExA(^w.wc) == 0 {
if register_class_ex_a(&w.wc) == 0 {
win32_print_last_error();
return w, false;
}
w.hwnd = CreateWindowExA(0,
c_class_name, w.c_title.data,
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
w.width as i32, w.height as i32,
nil, nil, instance, nil);
w.hwnd = create_window_ex_a(0,
c_class_name, &w.c_title[0],
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
i32(w.width), i32(w.height),
nil, nil, instance, nil);
if w.hwnd == nil {
win32_print_last_error();
return w, false;
}
w.dc = GetDC(w.hwnd);
w.dc = get_dc(w.hwnd);
{
pfd := PIXELFORMATDESCRIPTOR{
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
var pfd = PixelFormatDescriptor{
size = size_of(PixelFormatDescriptor),
version = 1,
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
pixel_type = PFD_TYPE_RGBA,
@@ -96,89 +96,91 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
layer_type = PFD_MAIN_PLANE,
};
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
w.opengl_context = wglCreateContext(w.dc);
wglMakeCurrent(w.dc, w.opengl_context);
set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
w.opengl_context = wgl.create_context(w.dc);
wgl.make_current(w.dc, w.opengl_context);
attribs := [8]i32{
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
var attribs = [8]i32{
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0, // NOTE(bill): tells the proc that this is the end of attribs
};
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
wglMakeCurrent(w.dc, w.rc);
SwapBuffers(w.dc);
var wgl_str = "wglCreateContextAttribsARB\x00";
var wglCreateContextAttribsARB = wgl.CreateContextAttribsARBType(wgl.get_proc_address(&wgl_str[0]));
w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
wgl.make_current(w.dc, w.rc);
swap_buffers(w.dc);
}
return w, true;
}
destroy_window :: proc(w: ^Window) {
free(w.c_title.data);
proc destroy_window(w: ^Window) {
free(w.c_title);
}
display_window :: proc(w: ^Window) {
win32.SwapBuffers(w.dc);
proc display_window(w: ^Window) {
win32.swap_buffers(w.dc);
}
run :: proc() {
using win32;
proc run() {
using math;
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
using win32;
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
os.exit(0);
return 0;
}
return DefWindowProcA(hwnd, msg, wparam, lparam);
return def_window_proc_a(hwnd, msg, wparam, lparam);
}
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc);
var window, window_success = make_window("Odin Language Demo", 854, 480, win32.WndProc(win32_proc));
if !window_success {
return;
}
defer destroy_window(^window);
defer destroy_window(&window);
gl.init();
using win32;
prev_time := time_now();
running := true;
var prev_time = time_now();
var running = true;
pos := Vec2{100, 100};
var pos = Vec2{100, 100};
for running {
curr_time := time_now();
dt := (curr_time - prev_time) as f32;
var curr_time = time_now();
var dt = f32(curr_time - prev_time);
prev_time = curr_time;
msg: MSG;
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
var msg: Msg;
for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
if msg.message == WM_QUIT {
running = false;
}
TranslateMessage(^msg);
DispatchMessageA(^msg);
translate_message(&msg);
dispatch_message_a(&msg);
}
if is_key_down(Key_Code.ESCAPE) {
if is_key_down(KeyCode.Escape) {
running = false;
}
{
SPEED :: 500;
v: Vec2;
const SPEED = 500;
var v: Vec2;
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
if is_key_down(Key_Code.UP) { v[1] += 1; }
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
if is_key_down(KeyCode.Right) { v[0] += 1; }
if is_key_down(KeyCode.Left) { v[0] -= 1; }
if is_key_down(KeyCode.Up) { v[1] += 1; }
if is_key_down(KeyCode.Down) { v[1] -= 1; }
v = vec2_norm0(v);
v = norm(v);
pos += v * Vec2{SPEED * dt};
}
@@ -188,10 +190,10 @@ run :: proc() {
gl.Clear(gl.COLOR_BUFFER_BIT);
gl.LoadIdentity();
gl.Ortho(0, window.width as f64,
0, window.height as f64, 0, 1);
gl.Ortho(0, f64(window.width),
0, f64(window.height), 0, 1);
draw_rect :: proc(x, y, w, h: f32) {
proc draw_rect(x, y, w, h: f32) {
gl.Begin(gl.TRIANGLES);
defer gl.End();
@@ -206,10 +208,15 @@ run :: proc() {
draw_rect(pos.x, pos.y, 50, 50);
display_window(^window);
ms_to_sleep := (16 - 1000*dt) as i32;
display_window(&window);
var ms_to_sleep = i32(16 - 1000*dt);
if ms_to_sleep > 0 {
win32.Sleep(ms_to_sleep);
win32.sleep(ms_to_sleep);
}
}
}
proc main() {
run();
}
+62 -58
View File
@@ -1,12 +1,12 @@
#import "fmt.odin";
import "fmt.odin";
#foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
SOCKET :: #type uint;
INVALID_SOCKET :: ~(cast(SOCKET)0);
type SOCKET uint;
const INVALID_SOCKET = ~SOCKET(0);
AF :: enum i32 {
type AF enum i32 {
UNSPEC = 0, // unspecified
UNIX = 1, // local to host (pipes, portals)
INET = 2, // internetwork: UDP, TCP, etc.
@@ -37,19 +37,22 @@ AF :: enum i32 {
MAX = 26,
};
SOCK_STREAM :: 1;
SOCKET_ERROR :: -1;
IPPROTO_TCP :: 6;
AI_PASSIVE :: 0x0020;
SOMAXCONN :: 128;
const (
SOCK_STREAM = 1;
SOCKET_ERROR = -1;
IPPROTO_TCP = 6;
AI_PASSIVE = 0x0020;
SOMAXCONN = 128;
)
const (
SD_RECEIVE = 0;
SD_SEND = 1;
SD_BOTH = 2;
)
SD_RECEIVE :: 0;
SD_SEND :: 1;
SD_BOTH :: 2;
WSADESCRIPTION_LEN :: 256;
WSASYS_STATUS_LEN :: 128;
WSADATA :: struct #ordered {
const WSADESCRIPTION_LEN = 256;
const WSASYS_STATUS_LEN = 128;
type WSADATA struct #ordered {
version: i16,
high_version: i16,
@@ -57,12 +60,12 @@ WSADATA :: struct #ordered {
// NOTE(bill): This is x64 ordering
max_sockets: u16,
max_udp_dg: u16,
vendor_info: ^byte,
description: [WSADESCRIPTION_LEN+1]byte,
system_status: [WSASYS_STATUS_LEN+1]byte,
vendor_info: ^u8,
description: [WSADESCRIPTION_LEN+1]u8,
system_status: [WSASYS_STATUS_LEN+1]u8,
}
addrinfo :: struct #ordered {
type addrinfo struct #ordered {
flags: i32,
family: i32,
socktype: i32,
@@ -73,52 +76,53 @@ addrinfo :: struct #ordered {
next: ^addrinfo,
}
sockaddr :: struct #ordered {
type sockaddr struct #ordered {
family: u16,
data: [14]byte,
data: [14]u8,
}
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign ws2;
WSACleanup :: proc() -> i32 #foreign ws2;
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign ws2;
freeaddrinfo :: proc(ai: ^addrinfo) #foreign ws2;
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign ws2;
closesocket :: proc(s: SOCKET) -> i32 #foreign ws2;
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign ws2;
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign ws2;
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign ws2;
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign ws2;
WSAGetLastError :: proc() -> i32 #foreign ws2;
to_c_string :: proc(s: string) -> ^byte {
c_str := new_slice(byte, s.count+1);
assert(c_str.data != nil);
copy(c_str, cast([]byte)s);
c_str[s.count] = 0;
return c_str.data;
foreign ws2 {
proc WSAStartup (version_requested: i16, data: ^WSADATA) -> i32;
proc WSACleanup () -> i32;
proc getaddrinfo (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32;
proc freeaddrinfo (ai: ^addrinfo);
proc socket (af, type_, protocol: i32) -> SOCKET;
proc closesocket (s: SOCKET) -> i32;
proc bind (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32;
proc listen (s: SOCKET, back_log: i32) -> i32;
proc accept (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET;
proc recv (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
proc send (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
proc shutdown (s: SOCKET, how: i32) -> i32;
proc WSAGetLastError() -> i32;
}
proc to_c_string(s: string) -> ^u8 {
var c_str = make([]u8, len(s)+1);
copy(c_str, []u8(s));
c_str[len(s)] = 0;
return &c_str[0];
}
run :: proc() {
wsa: WSADATA;
res: ^addrinfo = nil;
hints: addrinfo;
s, client: SOCKET;
proc run() {
var (
wsa: WSADATA;
res: ^addrinfo = nil;
hints: addrinfo;
s, client: SOCKET;
)
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
if WSAStartup(2 | (2 << 8), &wsa) != 0 {
fmt.println("WSAStartup failed: ", WSAGetLastError());
return;
}
defer WSACleanup();
hints.family = cast(i32)AF.INET;
hints.family = i32(AF.INET);
hints.socktype = SOCK_STREAM;
hints.protocol = IPPROTO_TCP;
hints.flags = AI_PASSIVE;
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 {
fmt.println("getaddrinfo failed: ", WSAGetLastError());
return;
}
@@ -131,7 +135,7 @@ run :: proc() {
}
defer closesocket(s);
bind(s, res.addr, cast(i32)res.addrlen);
bind(s, res.addr, i32(res.addrlen));
listen(s, SOMAXCONN);
client = accept(s, nil, 0);
@@ -141,7 +145,7 @@ run :: proc() {
}
defer closesocket(client);
html :=
var html =
`HTTP/1.1 200 OK
Connection: close
Content-type: text/html
@@ -156,12 +160,12 @@ Content-type: text/html
</html>
`;
buf: [1024]byte;
var buf: [1024]u8;
for {
bytes := recv(client, ^buf[0], cast(i32)buf.count, 0);
var bytes = recv(client, &buf[0], i32(len(buf)), 0);
if bytes > 0 {
// fmt.println(buf[:bytes] as string)
bytes_sent := send(client, html.data, cast(i32)(html.count-1), 0);
// fmt.println(string(buf[0..<bytes]))
var bytes_sent = send(client, &html[0], i32(len(html)-1), 0);
if bytes_sent == SOCKET_ERROR {
fmt.println("send failed: ", WSAGetLastError());
return;
+8 -8
View File
@@ -136,7 +136,7 @@ bounds_checking :: proc() {
{
base: [10]int;
s := base[2:6];
s := base[2..6];
a, b := -1, 6;
#no_bounds_check {
@@ -164,7 +164,7 @@ type_introspection :: proc() {
info = type_info_of_val(x); // by value
// See: runtime.odin
match type i in info {
match i in info {
case Type_Info.Integer:
fmt.println("integer!");
case Type_Info.Float:
@@ -174,7 +174,7 @@ type_introspection :: proc() {
}
// Unsafe cast
integer_info := cast(^Type_Info.Integer)info;
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
}
{
@@ -263,12 +263,12 @@ crazy_introspection :: proc() {
}
fruit_ti := type_info(Fruit);
name := (cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info := cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
fmt.printf("% :: enum % {\n", name, info.base);
for i := 0; i < info.values.count; i += 1 {
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i]);
fmt.printf("%s :: enum %T {\n", name, info.base);
for i := 0; i < len(info.values); i++ {
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
}
fmt.printf("}\n");
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -1,14 +1,14 @@
#import "fmt.odin"
#import "utf8.odin"
#import "hash.odin"
#import "mem.odin"
#import "fmt.odin";
#import "utf8.odin";
#import "hash.odin";
#import "mem.odin";
main :: proc() {
{ // New Standard Library stuff
s := "Hello"
s := "Hello";
fmt.println(s,
utf8.valid_string(s),
hash.murmur64(s.data, s.count))
hash.murmur64(cast([]byte)s));
// utf8.odin
// hash.odin
@@ -19,15 +19,15 @@ main :: proc() {
}
{
arena: mem.Arena
mem.init_arena_from_context(^arena, mem.megabytes(16)) // Uses default allocator
defer mem.free_arena(^arena)
arena: mem.Arena;
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
defer mem.free_arena(^arena);
push_allocator mem.arena_allocator(^arena) {
x := new(int)
x^ = 1337
x := new(int);
x^ = 1337;
fmt.println(x^)
fmt.println(x^);
}
/*
@@ -48,14 +48,14 @@ main :: proc() {
// You can also "push" a context
c := current_context() // Create copy of the allocator
c.allocator = mem.arena_allocator(^arena)
c := context; // Create copy of the allocator
c.allocator = mem.arena_allocator(^arena);
push_context c {
x := new(int)
x^ = 365
x := new(int);
x^ = 365;
fmt.println(x^)
fmt.println(x^);
}
}
+15 -13
View File
@@ -42,12 +42,12 @@ syntax :: proc() {
};
Thing2 :: struct {x: f32, y: int, z: ^[]int};
// Slice interals are now just a `ptr+count`
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int));
// Slice interals are now just a `ptr+len+cap`
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
// Helper type - Help the reader understand what it is quicker
My_Int :: type int;
My_Proc :: type proc(int) -> f32;
My_Int :: #type int;
My_Proc :: #type proc(int) -> f32;
// All declarations with : are either variable or constant
@@ -59,6 +59,7 @@ syntax :: proc() {
c_proc :: proc() { /* code here */ };
/*
x += 1;
x -= 1;
// ++ and -- have been removed
@@ -67,7 +68,7 @@ syntax :: proc() {
// Question: Should they be added again?
// They were removed as they are redundant and statements, not expressions
// like in C/C++
*/
// You can now build files as a `.dll`
// `odin build_dll demo.odin`
@@ -85,7 +86,7 @@ syntax :: proc() {
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
thread_local my_tls: Prefix_Type;
#thread_local my_tls: Prefix_Type;
prefixes :: proc() {
using var: Prefix_Type;
@@ -98,7 +99,7 @@ prefixes :: proc() {
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
// int_ptr = nil; // Not valid
int_ptr^ = 123; // Is valid
// int_ptr^ = 123; // Not valid
}
@@ -154,6 +155,7 @@ foreign_procedures :: proc() {
}
special_expressions :: proc() {
/*
// Block expression
x := {
a: f32 = 123;
@@ -168,7 +170,7 @@ special_expressions :: proc() {
// TODO: Type cohesion is not yet finished
give 123;
}; // semicolon is required as it's an expression
*/
// This is allows for inline blocks of code and will be a useful feature to have when
// macros will be implemented into the language
@@ -191,17 +193,17 @@ loops :: proc() {
break;
}
for i in 0..<123 { // 123 exclusive
for i in 0..123 { // 123 exclusive
}
for i in 0...122 { // 122 inclusive
for i in 0..123-1 { // 122 inclusive
}
for val, idx in 12..<16 {
for val, idx in 12..16 {
fmt.println(val, idx);
}
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19};
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
for p in primes {
fmt.println(p);
@@ -224,7 +226,7 @@ loops :: proc() {
when false {
for i, size := 0; i < name.count; i += size {
r: rune;
r, size = utf8.decode_rune(name[i:]);
r, size = utf8.decode_rune(name[i..]);
fmt.printf("%r\n", r);
}
}
+233 -224
View File
@@ -1,35 +1,42 @@
#import win32 "sys/windows.odin";
#import "fmt.odin";
#import "os.odin";
#import "mem.odin";
import (
win32 "sys/windows.odin";
"fmt.odin";
"os.odin";
"mem.odin";
)
CANVAS_WIDTH :: 128;
CANVAS_HEIGHT :: 128;
CANVAS_SCALE :: 3;
FRAME_TIME :: 1.0/30.0;
WINDOW_TITLE :: "Punity\x00";
const (
CANVAS_WIDTH = 128;
CANVAS_HEIGHT = 128;
CANVAS_SCALE = 3;
FRAME_TIME = 1.0/30.0;
WINDOW_TITLE = "Punity\x00";
)
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
const _ = compile_assert(CANVAS_WIDTH % 16 == 0);
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
const (
WINDOW_WIDTH = CANVAS_WIDTH * CANVAS_SCALE;
WINDOW_HEIGHT = CANVAS_HEIGHT * CANVAS_SCALE;
)
const (
STACK_CAPACITY = 1<<20;
STORAGE_CAPACITY = 1<<20;
STACK_CAPACITY :: 1<<20;
STORAGE_CAPACITY :: 1<<20;
DRAW_LIST_RESERVE = 128;
DRAW_LIST_RESERVE :: 128;
MAX_KEYS = 256;
)
MAX_KEYS :: 256;
Core :: struct {
type Core struct {
stack: ^Bank,
storage: ^Bank,
running: bool,
key_modifiers: u32,
key_states: [MAX_KEYS]byte,
key_deltas: [MAX_KEYS]byte,
key_states: [MAX_KEYS]u8,
key_deltas: [MAX_KEYS]u8,
perf_frame,
perf_frame_inner,
@@ -45,52 +52,52 @@ Core :: struct {
draw_list: ^Draw_List,
}
Perf_Span :: struct {
type Perf_Span struct {
stamp: f64,
delta: f32,
}
Bank :: struct {
memory: []byte,
type Bank struct {
memory: []u8,
cursor: int,
}
Bank_State :: struct {
type Bank_State struct {
state: Bank,
bank: ^Bank,
}
Color :: raw_union {
using channels: struct{a, b, g, r: byte},
type Color raw_union {
using channels: struct{a, b, g, r: u8},
rgba: u32,
}
Palette :: struct {
type Palette struct {
colors: [256]Color,
colors_count: byte,
colors_count: u8,
}
Rect :: raw_union {
type Rect raw_union {
using minmax: struct {min_x, min_y, max_x, max_y: int},
using pos: struct {left, top, right, bottom: int},
e: [4]int,
}
Bitmap :: struct {
pixels: []byte,
type Bitmap struct {
pixels: []u8,
width: int,
height: int,
}
Font :: struct {
type Font struct {
using bitmap: Bitmap,
char_width: int,
char_height: int,
}
Canvas :: struct {
type Canvas struct {
using bitmap: ^Bitmap,
palette: Palette,
translate_x: int,
@@ -99,89 +106,92 @@ Canvas :: struct {
font: ^Font,
}
DrawFlag :: enum {
type DrawFlag enum {
NONE = 0,
FLIP_H = 1<<0,
FLIP_V = 1<<1,
MASK = 1<<2,
}
Draw_Item :: struct {}
Draw_List :: struct {
type Draw_Item struct {}
type Draw_List struct {
items: []Draw_Item,
}
Key :: enum {
MOD_SHIFT = 0x0001,
MOD_CONTROL = 0x0002,
MOD_ALT = 0x0004,
MOD_SUPER = 0x0008,
type Key enum {
ModShift = 0x0001,
ModControl = 0x0002,
ModAlt = 0x0004,
ModSuper = 0x0008,
UNKNOWN =-1,
INVALID =-2,
LBUTTON = 1,
RBUTTON = 2,
CANCEL = 3,
MBUTTON = 4,
Unknown =-1,
Invalid =-2,
BACK = 8,
TAB = 9,
CLEAR = 12,
RETURN = 13,
SHIFT = 16,
CONTROL = 17,
MENU = 18,
PAUSE = 19,
CAPITAL = 20,
KANA = 0x15,
HANGEUL = 0x15,
HANGUL = 0x15,
JUNJA = 0x17,
FINAL = 0x18,
HANJA = 0x19,
KANJI = 0x19,
ESCAPE = 0x1B,
CONVERT = 0x1C,
NONCONVERT = 0x1D,
ACCEPT = 0x1E,
MODECHANGE = 0x1F,
SPACE = 32,
PRIOR = 33,
NEXT = 34,
END = 35,
HOME = 36,
LEFT = 37,
UP = 38,
RIGHT = 39,
DOWN = 40,
SELECT = 41,
PRINT = 42,
EXEC = 43,
SNAPSHOT = 44,
INSERT = 45,
DELETE = 46,
HELP = 47,
LWIN = 0x5B,
RWIN = 0x5C,
APPS = 0x5D,
SLEEP = 0x5F,
NUMPAD0 = 0x60,
NUMPAD1 = 0x61,
NUMPAD2 = 0x62,
NUMPAD3 = 0x63,
NUMPAD4 = 0x64,
NUMPAD5 = 0x65,
NUMPAD6 = 0x66,
NUMPAD7 = 0x67,
NUMPAD8 = 0x68,
NUMPAD9 = 0x69,
MULTIPLY = 0x6A,
ADD = 0x6B,
SEPARATOR = 0x6C,
SUBTRACT = 0x6D,
DECIMAL = 0x6E,
DIVIDE = 0x6F,
Lbutton = 1,
Rbutton = 2,
Cancel = 3,
Mbutton = 4,
Back = 8,
Tab = 9,
Clear = 12,
Return = 13,
Shift = 16,
Control = 17,
Menu = 18,
Pause = 19,
Capital = 20,
Kana = 0x15,
Hangeul = 0x15,
Hangul = 0x15,
Junja = 0x17,
Final = 0x18,
Hanja = 0x19,
Kanji = 0x19,
Escape = 0x1B,
Convert = 0x1C,
NonConvert = 0x1D,
Accept = 0x1E,
ModeChange = 0x1F,
Space = 32,
Prior = 33,
Next = 34,
End = 35,
Home = 36,
Left = 37,
Up = 38,
Right = 39,
Down = 40,
Select = 41,
Print = 42,
Exec = 43,
Snapshot = 44,
Insert = 45,
Delete = 46,
Help = 47,
Lwin = 0x5B,
Rwin = 0x5C,
Apps = 0x5D,
Sleep = 0x5F,
Numpad0 = 0x60,
Numpad1 = 0x61,
Numpad2 = 0x62,
Numpad3 = 0x63,
Numpad4 = 0x64,
Numpad5 = 0x65,
Numpad6 = 0x66,
Numpad7 = 0x67,
Numpad8 = 0x68,
Numpad9 = 0x69,
Multiply = 0x6A,
Add = 0x6B,
Separator = 0x6C,
Subtract = 0x6D,
Decimal = 0x6E,
Divide = 0x6F,
F1 = 0x70,
F2 = 0x71,
F3 = 0x72,
@@ -206,32 +216,33 @@ Key :: enum {
F22 = 0x85,
F23 = 0x86,
F24 = 0x87,
NUMLOCK = 0x90,
SCROLL = 0x91,
LSHIFT = 0xA0,
RSHIFT = 0xA1,
LCONTROL = 0xA2,
RCONTROL = 0xA3,
LMENU = 0xA4,
RMENU = 0xA5,
Numlock = 0x90,
Scroll = 0x91,
Lshift = 0xA0,
Rshift = 0xA1,
Lcontrol = 0xA2,
Rcontrol = 0xA3,
Lmenu = 0xA4,
Rmenu = 0xA5,
APOSTROPHE = 39, /* ' */
COMMA = 44, /* , */
MINUS = 45, /* - */
PERIOD = 46, /* . */
SLASH = 47, /* / */
NUM0 = 48,
NUM1 = 49,
NUM2 = 50,
NUM3 = 51,
NUM4 = 52,
NUM5 = 53,
NUM6 = 54,
NUM7 = 55,
NUM8 = 56,
NUM9 = 57,
SEMICOLON = 59, /* ; */
EQUAL = 61, /* = */
Apostrophe = 39, /* ' */
Comma = 44, /* , */
Minus = 45, /* - */
Period = 46, /* . */
Slash = 47, /* / */
Num0 = 48,
Num1 = 49,
Num2 = 50,
Num3 = 51,
Num4 = 52,
Num5 = 53,
Num6 = 54,
Num7 = 55,
Num8 = 56,
Num9 = 57,
Semicolon = 59, /* ; */
Equal = 61, /* = */
A = 65,
B = 66,
C = 67,
@@ -258,56 +269,55 @@ Key :: enum {
X = 88,
Y = 89,
Z = 90,
LEFT_BRACKET = 91, /* [ */
BACKSLASH = 92, /* \ */
RIGHT_BRACKET = 93, /* ] */
GRAVE_ACCENT = 96, /* ` */
LeftBracket = 91, /* [ */
Backslash = 92, /* \ */
RightBracket = 93, /* ] */
GraveAccent = 96, /* ` */
};
key_down :: proc(k: Key) -> bool {
proc key_down(k: Key) -> bool {
return _core.key_states[k] != 0;
}
key_pressed :: proc(k: Key) -> bool {
proc key_pressed(k: Key) -> bool {
return (_core.key_deltas[k] != 0) && key_down(k);
}
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
time_now :: proc() -> f64 {
let win32_perf_count_freq = win32.get_query_performance_frequency();
proc time_now() -> f64 {
assert(win32_perf_count_freq != 0);
counter: i64;
win32.QueryPerformanceCounter(^counter);
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
return result;
var counter: i64;
win32.query_performance_counter(&counter);
return f64(counter) / f64(win32_perf_count_freq);
}
_core: Core;
var _core: Core;
run :: proc(user_init, user_step: proc(c: ^Core)) {
proc run(user_init, user_step: proc(c: ^Core)) {
using win32;
_core.running = true;
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
win32_app_key_mods :: proc() -> u32 {
mods: u32 = 0;
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
proc win32_app_key_mods() -> u32 {
var mods: u32 = 0;
if is_key_down(Key_Code.SHIFT) {
mods |= cast(u32)Key.MOD_SHIFT;
if is_key_down(KeyCode.Shift) {
mods |= u32(Key.ModShift);
}
if is_key_down(Key_Code.CONTROL) {
mods |= cast(u32)Key.MOD_CONTROL;
if is_key_down(KeyCode.Control) {
mods |= u32(Key.ModControl);
}
if is_key_down(Key_Code.MENU) {
mods |= cast(u32)Key.MOD_ALT;
if is_key_down(KeyCode.Menu) {
mods |= u32(Key.ModAlt);
}
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
mods |= cast(u32)Key.MOD_SUPER;
if is_key_down(KeyCode.Lwin) || is_key_down(KeyCode.Rwin) {
mods |= u32(Key.ModSuper);
}
return mods;
@@ -331,61 +341,62 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
return 0;
case WM_CLOSE:
PostQuitMessage(0);
post_quit_message(0);
_core.running = false;
return 0;
}
return DefWindowProcA(hwnd, msg, wparam, lparam);
return def_window_proc_a(hwnd, msg, wparam, lparam);
}
window_class := WNDCLASSEXA{
class_name = (cast(string)"Punity\x00").data, // C-style string
size = size_of(WNDCLASSEXA),
var class_name = "Punity\x00";
var window_class = WndClassExA{
class_name = &class_name[0],
size = size_of(WndClassExA),
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
instance = cast(HINSTANCE)GetModuleHandleA(nil),
instance = Hinstance(get_module_handle_a(nil)),
wnd_proc = win32_proc,
// wnd_proc = DefWindowProcA,
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
background = Hbrush(get_stock_object(BLACK_BRUSH)),
};
if RegisterClassExA(^window_class) == 0 {
fmt.fprintln(os.stderr, "RegisterClassExA failed");
if register_class_ex_a(&window_class) == 0 {
fmt.fprintln(os.stderr, "register_class_ex_a failed");
return;
}
screen_width := GetSystemMetrics(SM_CXSCREEN);
screen_height := GetSystemMetrics(SM_CYSCREEN);
var screen_width = get_system_metrics(SM_CXSCREEN);
var screen_height = get_system_metrics(SM_CYSCREEN);
rc: RECT;
var rc: Rect;
rc.left = (screen_width - WINDOW_WIDTH) / 2;
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
rc.right = rc.left + WINDOW_WIDTH;
rc.bottom = rc.top + WINDOW_HEIGHT;
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
assert(AdjustWindowRect(^rc, style, 0) != 0);
var style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
assert(adjust_window_rect(&rc, style, 0) != 0);
wt := WINDOW_TITLE;
var wt = WINDOW_TITLE;
win32_window := CreateWindowExA(0,
window_class.class_name,
wt.data,
style,
rc.left, rc.top,
rc.right-rc.left, rc.bottom-rc.top,
nil, nil, window_class.instance,
nil);
var win32_window = create_window_ex_a(0,
window_class.class_name,
&wt[0],
style,
rc.left, rc.top,
rc.right-rc.left, rc.bottom-rc.top,
nil, nil, window_class.instance,
nil);
if win32_window == nil {
fmt.fprintln(os.stderr, "CreateWindowExA failed");
fmt.fprintln(os.stderr, "create_window_ex_a failed");
return;
}
window_bmi: BITMAPINFO;
window_bmi.size = size_of(BITMAPINFOHEADER);
var window_bmi: BitmapInfo;
window_bmi.size = size_of(BitmapInfoHeader);
window_bmi.width = CANVAS_WIDTH;
window_bmi.height = CANVAS_HEIGHT;
window_bmi.planes = 1;
@@ -393,27 +404,27 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
window_bmi.compression = BI_RGB;
user_init(^_core);
user_init(&_core);
ShowWindow(win32_window, SW_SHOW);
show_window(win32_window, SW_SHOW);
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
var window_buffer = make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
defer free(window_buffer);
for i := 0; i < window_buffer.count; i += 1 {
for _, i in window_buffer {
window_buffer[i] = 0xff00ff;
}
var (
dt: f64;
prev_time = time_now();
curr_time = time_now();
total_time : f64 = 0;
offset_x = 0;
offset_y = 0;
)
dt: f64;
prev_time := time_now();
curr_time := time_now();
total_time : f64 = 0;
offset_x := 0;
offset_y := 0;
message: MSG;
var message: Msg;
for _core.running {
curr_time = time_now();
dt = curr_time - prev_time;
@@ -424,64 +435,62 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
offset_y += 2;
{
data: [128]byte;
buf: fmt.Buffer;
buf.data = data[:];
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
win32.SetWindowTextA(win32_window, ^buf[0]);
var buf: [128]u8;
var s = fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
win32.set_window_text_a(win32_window, &s[0]);
}
for y := 0; y < CANVAS_HEIGHT; y += 1 {
for x := 0; x < CANVAS_WIDTH; x += 1 {
g := (x % 32) * 8;
b := (y % 32) * 8;
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
for var y = 0; y < CANVAS_HEIGHT; y++ {
for var x = 0; x < CANVAS_WIDTH; x++ {
var g = (x % 32) * 8;
var b = (y % 32) * 8;
window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
}
}
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
if message.message == WM_QUIT {
_core.running = false;
}
TranslateMessage(^message);
DispatchMessageA(^message);
translate_message(&message);
dispatch_message_a(&message);
}
user_step(^_core);
user_step(&_core);
dc := GetDC(win32_window);
StretchDIBits(dc,
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
window_buffer.data,
^window_bmi,
DIB_RGB_COLORS,
SRCCOPY);
ReleaseDC(win32_window, dc);
var dc = get_dc(win32_window);
stretch_dibits(dc,
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
&window_buffer[0],
&window_bmi,
DIB_RGB_COLORS,
SRCCOPY);
release_dc(win32_window, dc);
{
delta := time_now() - prev_time;
ms := cast(i32)((FRAME_TIME - delta) * 1000);
var delta = time_now() - prev_time;
var ms = i32((FRAME_TIME - delta) * 1000);
if ms > 0 {
win32.Sleep(ms);
win32.sleep(ms);
}
}
_core.frame += 1;
_core.frame++;
}
}
main :: proc() {
user_init :: proc(c: ^Core) {
proc main() {
proc user_init(c: ^Core) {
}
user_step :: proc(c: ^Core) {
proc user_step(c: ^Core) {
}
-5
View File
@@ -1,5 +0,0 @@
#import "fmt.odin" as fmt
thing :: proc() {
fmt.println("Sub Hello!")
}
-35
View File
@@ -1,35 +0,0 @@
/*#import "fmt.odin"
thing :: proc() {
fmt.println("Hello1!")
}*/
#import "fmt.odin";
main :: proc() {
fmt.println("hello, world!");
}
/*#import "fmt.odin" as .
thing :: proc() {
println("Hello3!")
}
*/
/*#import "fmt.odin" as _
thing :: proc() {
// println("Hello4!")
}
*/
/*
#include "fmt.odin"
thing :: proc() {
println("Hello5!")
}*/
+773 -406
View File
File diff suppressed because it is too large Load Diff
+210 -142
View File
@@ -1,158 +1,226 @@
#shared_global_scope;
// import "fmt.odin";
// proc __u128_mod(a, b: u128) -> u128 #link_name "__umodti3" {
// var _, r := __u128_quo_mod(a, b)
// return r
// }
// proc __u128_quo(a, b: u128) -> u128 #link_name "__udivti3" {
// var n, _ := __u128_quo_mod(a, b)
// return n
// }
// proc __i128_mod(a, b: i128) -> i128 #link_name "__modti3" {
// var _, r := __i128_quo_mod(a, b)
// return r
// }
// proc __i128_quo(a, b: i128) -> i128 #link_name "__divti3" {
// var n, _ := __i128_quo_mod(a, b)
// return n
// }
// proc __i128_quo_mod(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
// var s := b >> 127
// b = (b ~ s) - s
// s = a >> 127
// a = (a ~ s) - s
// var n, r := __u128_quo_mod(a as u128, b as u128)
// return (n as i128 ~ s) - s, (r as i128 ~ s) - s
// }
__multi3 :: proc(a, b: u128) -> u128 #cc_c #link_name "__multi3" {
bits_in_dword_2 :: size_of(i64) * 4;
lower_mask :: u128(~u64(0) >> bits_in_dword_2);
// proc __u128_quo_mod(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
// proc clz(x: u64) -> u64 {
// proc clz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
// return clz_u64(x, false)
// }
// proc ctz(x: u64) -> u64 {
// proc ctz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
// return ctz_u64(x, false)
// }
TWords :: struct #raw_union {
all: u128;
using _: struct {
when ODIN_ENDIAN == "big" {
lo, hi: u64;
} else {
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;
}
__u128_mod :: proc(a, b: u128) -> u128 #cc_c #link_name "__umodti3" {
r: u128;
__u128_quo_mod(a, b, &r);
return r;
}
__u128_quo :: proc(a, b: u128) -> u128 #cc_c #link_name "__udivti3" {
return __u128_quo_mod(a, b, nil);
}
__i128_mod :: proc(a, b: i128) -> i128 #cc_c #link_name "__modti3" {
r: i128;
__i128_quo_mod(a, b, &r);
return r;
}
__i128_quo :: proc(a, b: i128) -> i128 #cc_c #link_name "__divti3" {
return __i128_quo_mod(a, b, nil);
}
__i128_quo_mod :: proc(a, b: i128, rem: ^i128) -> (quo: i128) #cc_c #link_name "__divmodti4" {
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;
}
// u128_lo_hi :: raw_union {
// all: u128
// using _lohi: struct {lo, hi: u64;}
// }
__u128_quo_mod :: proc(a, b: u128, rem: ^u128) -> (quo: u128) #cc_c #link_name "__udivmodti4" {
alo, ahi := u64(a), u64(a>>64);
blo, bhi := u64(b), u64(b>>64);
if b == 0 {
if rem != nil do rem^ = 0;
return u128(alo/blo);
}
// n, d, q, r: u128_lo_hi
// sr: u64
r, d, x, q: u128 = a, b, 1, 0;
// n.all = a
// d.all = b
for r >= d && (d>>127)&1 == 0 {
x <<= 1;
d <<= 1;
}
// if n.hi == 0 {
// if d.hi == 0 {
// return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
// }
// return 0, n.lo as u128
// }
// if d.lo == 0 {
// if d.hi == 0 {
// return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
// }
// if n.lo == 0 {
// r.hi = n.hi % d.hi
// r.lo = 0
// return (n.hi / d.hi) as u128, r.all
// }
// if (d.hi & (d.hi-1)) == 0 {
// r.lo = n.lo
// r.hi = n.hi & (d.hi-1)
// return (n.hi >> ctz(d.hi)) as u128, r.all
// }
for x != 0 {
if r >= d {
r -= d;
q |= x;
}
x >>= 1;
d >>= 1;
}
// sr = clz(d.hi) - clz(n.hi)
// if sr > 64 - 2 {
// return 0, n.all
// }
// sr++
// q.lo = 0
// q.hi = n.lo << (64-sr)
// r.hi = n.hi >> sr
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
// } else {
// if d.hi == 0 {
// if (d.lo & (d.lo - 1)) == 0 {
// var rem := (n.lo % (d.lo - 1)) as u128
// if d.lo == 1 {
// return n.all, rem
// }
// sr = ctz(d.lo)
// q.hi = n.hi >> sr
// q.lo = (n.hi << (64-sr)) | (n.lo >> sr)
// return q.all, rem
// }
if rem != nil do rem^ = r;
return q;
}
// sr = 1 + 64 + clz(d.lo) - clz(n.hi)
/*
__f16_to_f32 :: proc(f: f16) -> f32 #cc_c #no_inline #link_name "__gnu_h2f_ieee" {
when true {
// Source: https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
FP32 :: struct #raw_union {u: u32, f: f32};
// q.all = n.all << (128-sr)
// r.all = n.all >> sr
// if sr == 64 {
// q.lo = 0
// q.hi = n.lo
// r.hi = 0
// r.lo = n.hi
// } else if sr < 64 {
// q.lo = 0
// q.hi = n.lo << (64-sr)
// r.hi = n.hi >> sr
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
// } else {
// q.lo = n.lo << (128-sr)
// q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
// r.hi = 0
// r.lo = n.hi >> (sr-64)
// }
// } else {
// sr = clz(d.hi) - clz(n.hi)
// if sr > 64-1 {
// return 0, n.all
// }
// sr++
// q.lo = 0
// q.hi = n.lo << (64-sr)
// r.all = n.all >> sr
// if sr < 64 {
// r.hi = n.hi >> sr
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
// } else {
// r.hi = 0
// r.lo = n.hi
// }
// }
// }
magic, was_infnan: FP32;
magic.u = (254-15) << 23;
was_infnan.u = (127+16) << 23;
// carry: u64
// for ; sr > 0; sr-- {
// r.hi = (r.hi << 1) | (r.lo >> (64-1))
// r.lo = (r.lo << 1) | (r.hi >> (64-1))
// q.hi = (q.hi << 1) | (q.lo >> (64-1))
// q.lo = (q.lo << 1) | carry
hu := transmute(u16, f);
// carry = 0
// if r.all >= d.all {
// r.all -= d.all
// carry = 1
// }
// }
o := FP32{};
// q.all = (q.all << 1) | (carry as u128)
// return q.all, r.all
// }
o.u = u32(hu & 0x7fff) << 13);
o.f *= magic.f;
if o.f >= was_infnan.f {
o.u |= 255 << 23;
}
o.u |= u32(hu & 0x8000) << 16;
return o.f;
} else {
return 0;
}
}
__f32_to_f16 :: proc(f_: f32) -> f16 #cc_c #no_inline #link_name "__gnu_f2h_ieee" {
when false {
// Source: https://gist.github.com/rygorous/2156668
FP16 :: struct #raw_union {u: u16, f: f16};
FP32 :: struct #raw_union {u: u32, f: f32};
f32infty, f16infty, magic: FP32;
f32infty.u = 255<<23;
f16infty.u = 31<<23;
magic.u = 15<<23;
sign_mask :: u32(0x80000000);
round_mask :: ~u32(0x0fff);
f := transmute(FP32, f_);
o: FP16;
sign := f.u & sign_mask;
f.u ~= sign;
// NOTE all the integer compares in this function can be safely
// compiled into signed compares since all operands are below
// 0x80000000. Important if you want fast straight SSE2 code
// (since there's no unsigned PCMPGTD).
if f.u >= f32infty.u { // Inf or NaN (all exponent bits set)
o.u = f.u > f32infty.u ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
} else { // (De)normalized number or zero
f.u &= round_mask;
f.f *= magic.f;
f.u -= round_mask;
if f.u > f16infty.u {
f.u = f16infty.u; // Clamp to signed infinity if overflowed
}
o.u = u16(f.u >> 13); // Take the bits!
}
o.u |= u16(sign >> 16);
return o.f;
} else {
f := transmute(u32, f_);
h: u16;
hs, he, hf: u16;
fs := (f >> 31) & 1;
fe := (f >> 23) & 0b1111_1111;
ff := (f >> 0) & 0b0111_1111_1111_1111_1111_1111;
add_one := false;
if (fe == 0) {
he = 0;
} else if (fe == 255) {
he = 31;
hf = ff != 0 ? 0x200 : 0;
} else {
ne := fe - 127 + 15;
if ne >= 31 {
he = 31;
} else if ne <= 0 {
if (14-ne) <= 24 {
mant := ff | 0x800000;
hf = u16(mant >> (14-ne));
if (mant >> (13-ne)) & 1 != 0 {
add_one = true;
}
}
} else {
he = u16(ne);
hf = u16(ff >> 13);
if ff&0x1000 != 0 {
add_one = true;
}
}
}
hs = u16(hs);
h |= (he&0b0001_1111)<<10;
h |= (hf&0b0011_1111_1111);
if add_one {
h++;
}
h |= (hs&1) << 15;
return transmute(f16, h);
}
}
__f64_to_f16 :: proc(f: f64) -> f16 #cc_c #no_inline #link_name "__truncdfhf2" {
return __f32_to_f16(f32(f));
}
__f16_to_f64 :: proc(f: f16) -> f64 #cc_c #no_inline {
return f64(__f16_to_f32(f));
}
*/
+16 -16
View File
@@ -1,14 +1,14 @@
// TODO(bill): Use assembly instead here to implement atomics
// Inline vs external file?
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
import win32 "sys/windows.odin" when ODIN_OS == "windows";
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
yield_thread :: proc() { win32.mm_pause(); }
mfence :: proc() { win32.ReadWriteBarrier(); }
sfence :: proc() { win32.WriteBarrier(); }
lfence :: proc() { win32.ReadBarrier(); }
mfence :: proc() { win32.read_write_barrier(); }
sfence :: proc() { win32.write_barrier(); }
lfence :: proc() { win32.read_barrier(); }
load :: proc(a: ^i32) -> i32 {
@@ -18,26 +18,26 @@ store :: proc(a: ^i32, value: i32) {
a^ = value;
}
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
return win32.InterlockedCompareExchange(a, desired, expected);
return win32.interlocked_compare_exchange(a, desired, expected);
}
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
return win32.InterlockedExchange(a, desired);
return win32.interlocked_exchange(a, desired);
}
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedExchangeAdd(a, operand);
return win32.interlocked_exchange_add(a, operand);
}
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedAnd(a, operand);
return win32.interlocked_and(a, operand);
}
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedOr(a, operand);
return win32.interlocked_or(a, operand);
}
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter++;
counter += 1;
yield_thread();
old_value = compare_exchange(a, 1, 0);
mfence();
@@ -63,25 +63,25 @@ store :: proc(a: ^i64, value: i64) {
a^ = value;
}
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
return win32.InterlockedCompareExchange64(a, desired, expected);
return win32.interlocked_compare_exchange64(a, desired, expected);
}
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
return win32.InterlockedExchange64(a, desired);
return win32.interlocked_exchange64(a, desired);
}
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedExchangeAdd64(a, operand);
return win32.interlocked_exchange_add64(a, operand);
}
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedAnd64(a, operand);
return win32.interlocked_and64(a, operand);
}
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedOr64(a, operand);
return win32.interlocked_or64(a, operand);
}
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter++;
counter += 1;
yield_thread();
old_value = compare_exchange(a, 1, 0);
mfence();
+285
View File
@@ -0,0 +1,285 @@
U8_MIN :: u8(0);
U16_MIN :: u16(0);
U32_MIN :: u32(0);
U64_MIN :: u64(0);
U128_MIN :: u128(0);
I8_MIN :: i8(-0x80);
I16_MIN :: i16(-0x8000);
I32_MIN :: i32(-0x8000_0000);
I64_MIN :: i64(-0x8000_0000_0000_0000);
I128_MIN :: i128(-0x8000_0000_0000_0000_0000_0000_0000_0000);
U8_MAX :: ~u8(0);
U16_MAX :: ~u16(0);
U32_MAX :: ~u32(0);
U64_MAX :: ~u64(0);
U128_MAX :: ~u128(0);
I8_MAX :: i8(0x7f);
I16_MAX :: i16(0x7fff);
I32_MAX :: i32(0x7fff_ffff);
I64_MAX :: i64(0x7fff_ffff_ffff_ffff);
I128_MAX :: i128(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff);
count_ones :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_ctpop :: proc(u8) -> u8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctpop :: proc(i8) -> i8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctpop :: proc(u16) -> u16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctpop :: proc(i16) -> i16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctpop :: proc(u32) -> u32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctpop :: proc(i32) -> i32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctpop :: proc(u64) -> u64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctpop :: proc(i64) -> i64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
count_ones :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctpop :: proc(u128) -> u128 #link_name "llvm.ctpop.i128" ---;return __llvm_ctpop(i); }
count_ones :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctpop :: proc(i128) -> i128 #link_name "llvm.ctpop.i128" ---;return __llvm_ctpop(i); }
count_ones :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(count_ones(u32(i))); } else { return uint(count_ones(u64(i))); } }
count_ones :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(count_ones(i32(i))); } else { return int(count_ones(i64(i))); } }
count_zeros :: proc(i: u8) -> u8 { return 8 - count_ones(i); }
count_zeros :: proc(i: i8) -> i8 { return 8 - count_ones(i); }
count_zeros :: proc(i: u16) -> u16 { return 16 - count_ones(i); }
count_zeros :: proc(i: i16) -> i16 { return 16 - count_ones(i); }
count_zeros :: proc(i: u32) -> u32 { return 32 - count_ones(i); }
count_zeros :: proc(i: i32) -> i32 { return 32 - count_ones(i); }
count_zeros :: proc(i: u64) -> u64 { return 64 - count_ones(i); }
count_zeros :: proc(i: i64) -> i64 { return 64 - count_ones(i); }
count_zeros :: proc(i: u128) -> u128 { return 128 - count_ones(i); }
count_zeros :: proc(i: i128) -> i128 { return 128 - count_ones(i); }
count_zeros :: proc(i: uint) -> uint { return 8*size_of(uint) - count_ones(i); }
count_zeros :: proc(i: int) -> int { return 8*size_of(int) - count_ones(i); }
rotate_left :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
rotate_left :: proc(i: i8, s: uint) -> i8 { return (i << s)|(i >> (8*size_of(i8) - s)); }
rotate_left :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
rotate_left :: proc(i: i16, s: uint) -> i16 { return (i << s)|(i >> (8*size_of(i16) - s)); }
rotate_left :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
rotate_left :: proc(i: i32, s: uint) -> i32 { return (i << s)|(i >> (8*size_of(i32) - s)); }
rotate_left :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
rotate_left :: proc(i: i64, s: uint) -> i64 { return (i << s)|(i >> (8*size_of(i64) - s)); }
rotate_left :: proc(i: u128, s: uint) -> u128 { return (i << s)|(i >> (8*size_of(u128) - s)); }
rotate_left :: proc(i: i128, s: uint) -> i128 { return (i << s)|(i >> (8*size_of(i128) - s)); }
rotate_left :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_left(u32(i), s)); } else { return uint(rotate_left(u64(i), s)); } }
rotate_left :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_left(i32(i), s)); } else { return int(rotate_left(i64(i), s)); } }
rotate_right :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
rotate_right :: proc(i: i8, s: uint) -> i8 { return (i >> s)|(i << (8*size_of(i8) - s)); }
rotate_right :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
rotate_right :: proc(i: i16, s: uint) -> i16 { return (i >> s)|(i << (8*size_of(i16) - s)); }
rotate_right :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
rotate_right :: proc(i: i32, s: uint) -> i32 { return (i >> s)|(i << (8*size_of(i32) - s)); }
rotate_right :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
rotate_right :: proc(i: i64, s: uint) -> i64 { return (i >> s)|(i << (8*size_of(i64) - s)); }
rotate_right :: proc(i: u128, s: uint) -> u128 { return (i >> s)|(i << (8*size_of(u128) - s)); }
rotate_right :: proc(i: i128, s: uint) -> i128 { return (i >> s)|(i << (8*size_of(i128) - s)); }
rotate_right :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_right(u32(i), s)); } else { return uint(rotate_right(u64(i), s)); } }
rotate_right :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_right(i32(i), s)); } else { return int(rotate_right(i64(i), s)); } }
leading_zeros :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_ctlz :: proc(u8, bool) -> u8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctlz :: proc(i8, bool) -> i8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctlz :: proc(u16, bool) -> u16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctlz :: proc(i16, bool) -> i16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctlz :: proc(u32, bool) -> u32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctlz :: proc(i32, bool) -> i32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctlz :: proc(u64, bool) -> u64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctlz :: proc(i64, bool) -> i64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctlz :: proc(u128, bool) -> u128 #link_name "llvm.ctlz.i128" ---;return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctlz :: proc(i128, bool) -> i128 #link_name "llvm.ctlz.i128" ---;return __llvm_ctlz(i, false); }
leading_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(leading_zeros(u32(i))); } else { return uint(leading_zeros(u64(i))); } }
leading_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(leading_zeros(i32(i))); } else { return int(leading_zeros(i64(i))); } }
trailing_zeros :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_cttz :: proc(u8, bool) -> u8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_cttz :: proc(i8, bool) -> i8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_cttz :: proc(u16, bool) -> u16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_cttz :: proc(i16, bool) -> i16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_cttz :: proc(u32, bool) -> u32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_cttz :: proc(i32, bool) -> i32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_cttz :: proc(u64, bool) -> u64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_cttz :: proc(i64, bool) -> i64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_cttz :: proc(u128, bool) -> u128 #link_name "llvm.cttz.i128" ---;return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_cttz :: proc(i128, bool) -> i128 #link_name "llvm.cttz.i128" ---;return __llvm_cttz(i, false); }
trailing_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(trailing_zeros(u32(i))); } else { return uint(trailing_zeros(u64(i))); } }
trailing_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(trailing_zeros(i32(i))); } else { return int(trailing_zeros(i64(i))); } }
reverse_bits :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_bitreverse :: proc(u8) -> u8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_bitreverse :: proc(i8) -> i8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_bitreverse :: proc(u16) -> u16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_bitreverse :: proc(i16) -> i16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_bitreverse :: proc(u32) -> u32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_bitreverse :: proc(i32) -> i32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_bitreverse :: proc(u64) -> u64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_bitreverse :: proc(i64) -> i64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
reverse_bits :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_bitreverse :: proc(u128) -> u128 #link_name "llvm.bitreverse.i128" ---;return __llvm_bitreverse(i); }
reverse_bits :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_bitreverse :: proc(i128) -> i128 #link_name "llvm.bitreverse.i128" ---;return __llvm_bitreverse(i); }
reverse_bits :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(reverse_bits(u32(i))); } else { return uint(reverse_bits(u64(i))); } }
reverse_bits :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(reverse_bits(i32(i))); } else { return int(reverse_bits(i64(i))); } }
foreign __llvm_core {
byte_swap :: proc(u16) -> u16 #link_name "llvm.bswap.i16" ---;
byte_swap :: proc(i16) -> i16 #link_name "llvm.bswap.i16" ---;
byte_swap :: proc(u32) -> u32 #link_name "llvm.bswap.i32" ---;
byte_swap :: proc(i32) -> i32 #link_name "llvm.bswap.i32" ---;
byte_swap :: proc(u64) -> u64 #link_name "llvm.bswap.i64" ---;
byte_swap :: proc(i64) -> i64 #link_name "llvm.bswap.i64" ---;
byte_swap :: proc(u128) -> u128 #link_name "llvm.bswap.i128" ---;
byte_swap :: proc(i128) -> i128 #link_name "llvm.bswap.i128" ---;
}
byte_swap :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(byte_swap(u32(i))); } else { return uint(byte_swap(u64(i))); } }
byte_swap :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(byte_swap(i32(i))); } else { return int(byte_swap(i64(i))); } }
from_be :: proc(i: u8) -> u8 { return i; }
from_be :: proc(i: i8) -> i8 { return i; }
from_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: u8) -> u8 { return i; }
from_le :: proc(i: i8) -> i8 { return i; }
from_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
from_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: u8) -> u8 { return i; }
to_be :: proc(i: i8) -> i8 { return i; }
to_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: u8) -> u8 { return i; }
to_le :: proc(i: i8) -> i8 { return i; }
to_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
to_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
overflowing_add :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.uadd.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.sadd.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.uadd.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.sadd.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.uadd.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.sadd.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.uadd.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.sadd.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.uadd.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.sadd.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_add :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_add(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_add(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_add :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_add(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_add(i64(lhs), i64(rhs));
return int(x), ok;
}
}
overflowing_sub :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.usub.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.ssub.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.usub.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.ssub.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.usub.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.ssub.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.usub.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.ssub.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.usub.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.ssub.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_sub :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_sub(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_sub(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_sub :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_sub(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_sub(i64(lhs), i64(rhs));
return int(x), ok;
}
}
overflowing_mul :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.umul.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.smul.with.overflow.i8" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.umul.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.smul.with.overflow.i16" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.umul.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.smul.with.overflow.i32" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.umul.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.smul.with.overflow.i64" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.umul.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.smul.with.overflow.i128" ---; return op(lhs, rhs); }
overflowing_mul :: proc(lhs, rhs: uint) -> (uint, bool) {
when size_of(uint) == size_of(u32) {
x, ok := overflowing_mul(u32(lhs), u32(rhs));
return uint(x), ok;
} else {
x, ok := overflowing_mul(u64(lhs), u64(rhs));
return uint(x), ok;
}
}
overflowing_mul :: proc(lhs, rhs: int) -> (int, bool) {
when size_of(int) == size_of(i32) {
x, ok := overflowing_mul(i32(lhs), i32(rhs));
return int(x), ok;
} else {
x, ok := overflowing_mul(i64(lhs), i64(rhs));
return int(x), ok;
}
}
is_power_of_two :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: u128) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: i128) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
is_power_of_two :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
+41
View File
@@ -0,0 +1,41 @@
CHAR_BIT :: 8;
c_bool :: bool;
c_char :: u8;
c_schar :: i8;
c_uchar :: i8;
c_short :: i16;
c_ushort :: i16;
c_int :: i32;
c_uint :: u32;
c_long :: ODIN_OS == "windows" ?
i32 :
(size_of(int) == 4) ?
i32 :
i64;
c_ulong :: ODIN_OS == "windows" ?
u32 :
(size_of(int) == 4) ?
u32 :
u64;
c_longlong :: i64;
c_ulonglong :: u64;
c_float :: f32;
c_double :: f64;
c_complex_float :: complex64;
c_complex_double :: complex128;
c_size_t :: uint;
c_ssize_t :: int;
c_ptrdiff_t :: int;
c_uintptr_t :: uint;
c_intptr_t :: int;
+56 -58
View File
@@ -3,54 +3,52 @@
// NOTE: This is only for floating point printing and nothing else
Decimal :: struct {
digits: [384]byte, // big-endian digits
count: int,
decimal_point: int,
neg, trunc: bool,
digits: [384]u8; // big-endian digits
count: int;
decimal_point: int;
neg, trunc: bool;
}
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
digit_zero :: proc(buf: []byte) -> int {
for _, i in buf {
buf[i] = '0';
}
return buf.count;
decimal_to_string :: proc(buf: []u8, a: ^Decimal) -> string {
digit_zero :: proc(buf: []u8) -> int {
for _, i in buf do buf[i] = '0';
return len(buf);
}
n := 10 + a.count + abs(a.decimal_point);
// TODO(bill): make this work with a buffer that's not big enough
assert(buf.count >= n);
buf = buf[..n];
assert(len(buf) >= n);
buf = buf[0..n];
if a.count == 0 {
buf[0] = '0';
return cast(string)buf[0..1];
return string(buf[0..1]);
}
w := 0;
if a.decimal_point <= 0 {
buf[w] = '0'; w++;
buf[w] = '.'; w++;
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]);
} else if a.decimal_point < a.count {
w += copy(buf[w..], a.digits[0..a.decimal_point]);
buf[w] = '.'; w++;
buf[w] = '.'; w+=1;
w += copy(buf[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]);
}
return cast(string)buf[0..w];
return string(buf[0..w]);
}
// trim trailing zeros
trim :: proc(a: ^Decimal) {
for a.count > 0 && a.digits[a.count-1] == '0' {
a.count--;
a.count -= 1;
}
if a.count == 0 {
a.decimal_point = 0;
@@ -59,34 +57,33 @@ trim :: proc(a: ^Decimal) {
assign :: proc(a: ^Decimal, i: u64) {
buf: [32]byte;
buf: [32]u8;
n := 0;
for i > 0 {
j := i/10;
i -= 10*j;
buf[n] = cast(byte)('0'+i);
n++;
buf[n] = u8('0'+i);
n+=1;
i = j;
}
a.count = 0;
for n--; n >= 0; n-- {
for n -= 1; n >= 0; n -= 1 {
a.digits[a.count] = buf[n];
a.count++;
a.count+=1;
}
a.decimal_point = a.count;
trim(a);
}
uint_size :: 8*size_of(uint);
max_shift :: uint_size-4;
shift_right :: proc(a: ^Decimal, k: uint) {
r := 0; // read index
w := 0; // write index
n: uint;
for ; n>>k == 0; r++ {
for ; n>>k == 0; r+=1 {
if r >= a.count {
if n == 0 {
// Just in case
@@ -95,32 +92,32 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
for n>>k == 0 {
n = n * 10;
r++;
r+=1;
}
break;
}
c := cast(uint)a.digits[r];
c := uint(a.digits[r]);
n = n*10 + c - '0';
}
a.decimal_point -= r-1;
mask: uint = (1<<k) - 1;
for ; r < a.count; r++ {
c := cast(uint)a.digits[r];
for ; r < a.count; r+=1 {
c := uint(a.digits[r]);
dig := n>>k;
n &= mask;
a.digits[w] = cast(byte)('0' + dig);
w++;
a.digits[w] = u8('0' + dig);
w+=1;
n = n*10 + c - '0';
}
for n > 0 {
dig := n>>k;
n &= mask;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + dig);
w++;
if w < len(a.digits) {
a.digits[w] = u8('0' + dig);
w+=1;
} else if dig > 0 {
a.trunc = true;
}
@@ -133,19 +130,19 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
shift_left :: proc(a: ^Decimal, k: uint) {
delta := cast(int)(k/4);
delta := int(k/4);
r := a.count; // read index
w := a.count+delta; // write index
n: uint;
for r--; r >= 0; r-- {
n += (cast(uint)a.digits[r] - '0') << k;
for r -= 1; r >= 0; r -= 1 {
n += (uint(a.digits[r]) - '0') << k;
quo := n/10;
rem := n - 10*quo;
w--;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + rem);
w -= 1;
if w < len(a.digits) {
a.digits[w] = u8('0' + rem);
} else if rem != 0 {
a.trunc = true;
}
@@ -155,9 +152,9 @@ shift_left :: proc(a: ^Decimal, k: uint) {
for n > 0 {
quo := n/10;
rem := n - 10*quo;
w--;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + rem);
w -= 1;
if 0 <= w && w < len(a.digits) {
a.digits[w] = u8('0' + rem);
} else if rem != 0 {
a.trunc = true;
}
@@ -165,12 +162,15 @@ shift_left :: proc(a: ^Decimal, k: uint) {
}
a.count += delta;
a.count = min(a.count, a.digits.count);
a.count = min(a.count, len(a.digits));
a.decimal_point += delta;
trim(a);
}
shift :: proc(a: ^Decimal, k: int) {
uint_size :: 8*size_of(uint);
max_shift :: uint_size-4;
match {
case a.count == 0:
// no need to update
@@ -179,7 +179,7 @@ shift :: proc(a: ^Decimal, k: int) {
shift_left(a, max_shift);
k -= max_shift;
}
shift_left(a, cast(uint)k);
shift_left(a, uint(k));
case k < 0:
@@ -187,16 +187,14 @@ shift :: proc(a: ^Decimal, k: int) {
shift_right(a, max_shift);
k += max_shift;
}
shift_right(a, cast(uint)-k);
shift_right(a, uint(-k));
}
}
can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
if nd < 0 || nd >= a.count { return false ; }
if a.digits[nd] == '5' && nd+1 == a.count {
if a.trunc {
return true;
}
if a.trunc do return true;
return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
}
@@ -215,9 +213,9 @@ round :: proc(a: ^Decimal, nd: int) {
round_up :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return; }
for i := nd-1; i >= 0; i-- {
for i := nd-1; i >= 0; i -= 1 {
if c := a.digits[i]; c < '9' {
a.digits[i]++;
a.digits[i]+=1;
a.count = i+1;
return;
}
@@ -226,7 +224,7 @@ round_up :: proc(a: ^Decimal, nd: int) {
// Number is just 9s
a.digits[0] = '1';
a.count = 1;
a.decimal_point++;
a.decimal_point+=1;
}
round_down :: proc(a: ^Decimal, nd: int) {
@@ -241,17 +239,17 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
if a.decimal_point > 20 {
return 0xffff_ffff_ffff_ffff;
}
i: int;
i: int = 0;
n: u64 = 0;
m := min(a.decimal_point, a.count);
for i = 0; i < m; i++ {
n = n*10 + cast(u64)(a.digits[i]-'0');
for ; i < m; i += 1 {
n = n*10 + u64(a.digits[i]-'0');
}
for ; i < a.decimal_point; i++ {
for ; i < a.decimal_point; i += 1 {
n *= 10;
}
if can_round_up(a, a.decimal_point) {
n++;
n+=1;
}
return n;
}
+655 -505
View File
File diff suppressed because it is too large Load Diff
+52 -52
View File
@@ -1,57 +1,59 @@
crc32 :: proc(data: []byte) -> u32 {
result := ~cast(u32)0;
import "mem.odin";
crc32 :: proc(data: []u8) -> u32 {
result := ~u32(0);
for b in data {
result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff];
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
}
return ~result;
}
crc64 :: proc(data: []byte) -> u64 {
result := ~cast(u64)0;
crc64 :: proc(data: []u8) -> u64 {
result := ~u64(0);
for b in data {
result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff];
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
}
return ~result;
}
fnv32 :: proc(data: []byte) -> u32 {
fnv32 :: proc(data: []u8) -> u32 {
h: u32 = 0x811c9dc5;
for b in data {
h = (h * 0x01000193) ~ cast(u32)b;
h = (h * 0x01000193) ~ u32(b);
}
return h;
}
fnv64 :: proc(data: []byte) -> u64 {
fnv64 :: proc(data: []u8) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h * 0x100000001b3) ~ cast(u64)b;
h = (h * 0x100000001b3) ~ u64(b);
}
return h;
}
fnv32a :: proc(data: []byte) -> u32 {
fnv32a :: proc(data: []u8) -> u32 {
h: u32 = 0x811c9dc5;
for b in data {
h = (h ~ cast(u32)b) * 0x01000193;
h = (h ~ u32(b)) * 0x01000193;
}
return h;
}
fnv64a :: proc(data: []byte) -> u64 {
fnv64a :: proc(data: []u8) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ cast(u64)b) * 0x100000001b3;
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
murmur32 :: proc(data: []byte) -> u32 {
murmur32 :: proc(data: []u8) -> u32 {
c1_32: u32 : 0xcc9e2d51;
c2_32: u32 : 0x1b873593;
h1: u32 = 0;
nblocks := data.count/4;
p := data.data;
nblocks := len(data)/4;
p := &data[0];
p1 := p + 4*nblocks;
for ; p < p1; p += 4 {
@@ -67,24 +69,23 @@ murmur32 :: proc(data: []byte) -> u32 {
}
tail := data[nblocks*4 ..];
k1: u32;
match tail.count&3 {
match len(tail)&3 {
case 3:
k1 ~= cast(u32)tail[2] << 16;
k1 ~= u32(tail[2]) << 16;
fallthrough;
case 2:
k1 ~= cast(u32)tail[2] << 8;
k1 ~= u32(tail[2]) << 8;
fallthrough;
case 1:
k1 ~= cast(u32)tail[0];
k1 ~= u32(tail[0]);
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17) ;
k1 *= c2_32;
h1 ~= k1;
}
h1 ~= cast(u32)data.count;
h1 ~= u32(len(data));
h1 ~= h1 >> 16;
h1 *= 0x85ebca6b;
@@ -95,15 +96,15 @@ murmur32 :: proc(data: []byte) -> u32 {
return h1;
}
murmur64 :: proc(data: []byte) -> u64 {
murmur64 :: proc(data: []u8) -> u64 {
SEED :: 0x9747b28c;
when false && size_of(int) == 8 {
when size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995;
r :: 47;
h: u64 = SEED ~ (cast(u64)data.count * m);
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64));
h: u64 = SEED ~ (u64(len(data)) * m);
data64 := mem.slice_ptr(cast(^u64)&data[0], len(data)/size_of(u64));
for _, i in data64 {
k := data64[i];
@@ -116,15 +117,15 @@ murmur64 :: proc(data: []byte) -> u64 {
h *= m;
}
match data.count&7 {
case 7: h ~= cast(u64)data[6] << 48; fallthrough;
case 6: h ~= cast(u64)data[5] << 40; fallthrough;
case 5: h ~= cast(u64)data[4] << 32; fallthrough;
case 4: h ~= cast(u64)data[3] << 24; fallthrough;
case 3: h ~= cast(u64)data[2] << 16; fallthrough;
case 2: h ~= cast(u64)data[1] << 8; fallthrough;
match len(data)&7 {
case 7: h ~= u64(data[6]) << 48; fallthrough;
case 6: h ~= u64(data[5]) << 40; fallthrough;
case 5: h ~= u64(data[4]) << 32; fallthrough;
case 4: h ~= u64(data[3]) << 24; fallthrough;
case 3: h ~= u64(data[2]) << 16; fallthrough;
case 2: h ~= u64(data[1]) << 8; fallthrough;
case 1:
h ~= cast(u64)data[0];
h ~= u64(data[0]);
h *= m;
}
@@ -137,16 +138,15 @@ murmur64 :: proc(data: []byte) -> u64 {
m :: 0x5bd1e995;
r :: 24;
h1: u32 = cast(u32)SEED ~ cast(u32)data.count;
h2: u32 = SEED >> 32;
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32));
len := data.count;
h1 := u32(SEED) ~ u32(len(data));
h2 := u32(SEED) >> 32;
data32 := mem.slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
len := len(data);
i := 0;
for len >= 8 {
k1, k2: u32;
k1 = data32[i]; i++;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
@@ -154,7 +154,7 @@ murmur64 :: proc(data: []byte) -> u64 {
h1 ~= k1;
len -= 4;
k2 = data32[i]; i++;
k2 = data32[i]; i += 1;
k2 *= m;
k2 ~= k2>>r;
k2 *= m;
@@ -165,7 +165,7 @@ murmur64 :: proc(data: []byte) -> u64 {
if len >= 4 {
k1: u32;
k1 = data32[i]; i++;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
@@ -174,16 +174,17 @@ murmur64 :: proc(data: []byte) -> u64 {
len -= 4;
}
data8 := slice_to_bytes(data32[i..])[..3];
// TODO(bill): Fix this
#no_bounds_check data8 := slice_to_bytes(data32[i..])[..3];
match len {
case 3:
h2 ~= cast(u32)data8[2] << 16;
h2 ~= u32(data8[2]) << 16;
fallthrough;
case 2:
h2 ~= cast(u32)data8[1] << 8;
h2 ~= u32(data8[1]) << 8;
fallthrough;
case 1:
h2 ~= cast(u32)data8[0];
h2 ~= u32(data8[0]);
h2 *= m;
}
@@ -196,13 +197,12 @@ murmur64 :: proc(data: []byte) -> u64 {
h2 ~= h1>>19;
h2 *= m;
h := cast(u64)(h1)<<32 | cast(u64)(h2);
return h;
return u64(h1)<<32 | u64(h2);
}
}
immutable _crc32_table := [256]u32{
_crc32_table := [256]u32{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@@ -268,7 +268,7 @@ immutable _crc32_table := [256]u32{
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
};
immutable _crc64_table := [256]u64{
_crc64_table := [256]u64{
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
+93 -86
View File
@@ -20,69 +20,86 @@ Vec2 :: [vector 2]f32;
Vec3 :: [vector 3]f32;
Vec4 :: [vector 4]f32;
Mat2 :: [2]Vec2;
Mat3 :: [3]Vec3;
Mat4 :: [4]Vec4;
// Column major
Mat2 :: [2][2]f32;
Mat3 :: [3][3]f32;
Mat4 :: [4][4]f32;
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
Complex :: complex64;
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
foreign __llvm_core {
sqrt :: proc(x: f32) -> f32 #link_name "llvm.sqrt.f32" ---;
sqrt :: proc(x: f64) -> f64 #link_name "llvm.sqrt.f64" ---;
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
sin :: proc(θ: f32) -> f32 #link_name "llvm.sin.f32" ---;
sin :: proc(θ: f64) -> f64 #link_name "llvm.sin.f64" ---;
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
cos :: proc(θ: f32) -> f32 #link_name "llvm.cos.f32" ---;
cos :: proc(θ: f64) -> f64 #link_name "llvm.cos.f64" ---;
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
pow :: proc(x, power: f32) -> f32 #link_name "llvm.pow.f32" ---;
pow :: proc(x, power: f64) -> f64 #link_name "llvm.pow.f64" ---;
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
fmuladd :: proc(a, b, c: f32) -> f32 #link_name "llvm.fmuladd.f32" ---;
fmuladd :: proc(a, b, c: f64) -> f64 #link_name "llvm.fmuladd.f64" ---;
}
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
bit_reverse :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64";
tan :: proc(θ: f32) -> f32 #inline do return sin(θ)/cos(θ);
tan :: proc(θ: f64) -> f64 #inline do return sin(θ)/cos(θ);
lerp :: proc(a, b, t: f32) -> (x: f32) do return a*(1-t) + b*t;
lerp :: proc(a, b, t: f64) -> (x: f64) do return a*(1-t) + b*t;
unlerp :: proc(a, b, x: f32) -> (t: f32) do return (x-a)/(b-a);
unlerp :: proc(a, b, x: f64) -> (t: f64) do return (x-a)/(b-a);
sign :: proc(x: f32) -> f32 { if x >= 0 do return +1; return -1; }
sign :: proc(x: f64) -> f64 { if x >= 0 do return +1; return -1; }
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
copy_sign :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
iy := transmute(u32)y;
ix &= 0x7fffffff;
ix |= iy & 0x80000000;
ix &= 0x7fff_ffff;
ix |= iy & 0x8000_0000;
return transmute(f32)ix;
}
round :: proc(x: f32) -> f32 {
if x >= 0 {
return floor(x + 0.5);
}
return ceil(x - 0.5);
}
floor :: proc(x: f32) -> f32 {
if x >= 0 {
return cast(f32)cast(int)x;
}
return cast(f32)cast(int)(x-0.5);
}
ceil :: proc(x: f32) -> f32 {
if x < 0 {
return cast(f32)cast(int)x;
}
return cast(f32)cast(int)(x+1);
copy_sign :: 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;
}
remainder32 :: proc(x, y: f32) -> f32 {
return x - round(x/y) * y;
}
round :: proc(x: f32) -> f32 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
round :: proc(x: f64) -> f64 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
fmod32 :: proc(x, y: f32) -> f32 {
floor :: proc(x: f32) -> f32 { if x >= 0 do return f32(i64(x)); return f32(i64(x-0.5)); } // TODO: Get accurate versions
floor :: proc(x: f64) -> f64 { if x >= 0 do return f64(i64(x)); return f64(i64(x-0.5)); } // TODO: Get accurate versions
ceil :: proc(x: f32) -> f32 { if x < 0 do return f32(i64(x)); return f32(i64(x+1)); }// TODO: Get accurate versions
ceil :: proc(x: f64) -> f64 { if x < 0 do return f64(i64(x)); return f64(i64(x+1)); }// TODO: Get accurate versions
remainder :: proc(x, y: f32) -> f32 do return x - round(x/y) * y;
remainder :: proc(x, y: f64) -> f64 do return x - round(x/y) * y;
mod :: proc(x, y: f32) -> f32 {
result: f32;
y = abs(y);
result := remainder32(abs(x), y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
}
return copy_sign(result, x);
}
mod :: proc(x, y: f64) -> f64 {
result: f64;
y = abs(y);
result = remainder(abs(x), y);
if sign(result) < 0 {
result += y;
}
@@ -90,53 +107,46 @@ fmod32 :: proc(x, y: f32) -> f32 {
}
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
to_radians :: proc(degrees: f32) -> f32 do return degrees * TAU / 360;
to_degrees :: proc(radians: f32) -> f32 do return radians * 360 / TAU;
dot :: proc(a, b: $T/[vector 2]$E) -> E { c := a*b; return c.x + c.y; }
dot :: proc(a, b: $T/[vector 3]$E) -> E { c := a*b; return c.x + c.y + c.z; }
dot :: proc(a, b: $T/[vector 4]$E) -> E { c := a*b; return c.x + c.y + c.z + c.w; }
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
cross :: proc(x, y: Vec3) -> Vec3 {
cross :: proc(x, y: $T/[vector 3]$E) -> T {
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
return a - b;
return T(a - b);
}
mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: $T/[vector 2]$E) -> E do return sqrt(dot(v, v));
mag :: proc(v: $T/[vector 3]$E) -> E do return sqrt(dot(v, v));
mag :: proc(v: $T/[vector 4]$E) -> E do return sqrt(dot(v, v));
norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{mag(v)}; }
norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{mag(v)}; }
norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{mag(v)}; }
norm :: proc(v: $T/[vector 2]$E) -> T do return v / mag(v);
norm :: proc(v: $T/[vector 3]$E) -> T do return v / mag(v);
norm :: proc(v: $T/[vector 4]$E) -> T do return v / mag(v);
norm0 :: proc(v: Vec2) -> Vec2 {
norm0 :: proc(v: $T/[vector 2]$E) -> T {
m := mag(v);
if m == 0 {
return Vec2{0};
}
return v / Vec2{m};
if m == 0 do return 0;
return v/m;
}
norm0 :: proc(v: Vec3) -> Vec3 {
norm0 :: proc(v: $T/[vector 3]$E) -> T {
m := mag(v);
if m == 0 {
return Vec3{0};
}
return v / Vec3{m};
if m == 0 do return 0;
return v/m;
}
norm0 :: proc(v: Vec4) -> Vec4 {
norm0 :: proc(v: $T/[vector 4]$E) -> T {
m := mag(v);
if m == 0 {
return Vec4{0};
}
return v / Vec4{m};
if m == 0 do return 0;
return v/m;
}
@@ -204,6 +214,7 @@ inverse :: proc(m: Mat4) -> Mat4 {
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);
@@ -264,7 +275,7 @@ mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
s := sin(angle_radians);
a := norm(v);
t := a * Vec3{1-c};
t := a * (1-c);
rot := mat4_identity();
@@ -306,18 +317,18 @@ look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
s := norm(cross(f, up));
u := cross(s, f);
m: Mat4;
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
m[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
return m;
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);
@@ -341,7 +352,6 @@ ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
F32_DIG :: 6;
F32_EPSILON :: 1.192092896e-07;
F32_GUARD :: 0;
@@ -367,6 +377,3 @@ 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
+115 -127
View File
@@ -1,97 +1,90 @@
#import "fmt.odin";
#import "os.odin";
swap :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
return data;
import (
"fmt.odin";
"os.odin";
"raw.odin";
)
foreign __llvm_core {
swap :: proc(b: u16) -> u16 #link_name "llvm.bswap.i16" ---;
swap :: proc(b: u32) -> u32 #link_name "llvm.bswap.i32" ---;
swap :: proc(b: u64) -> u64 #link_name "llvm.bswap.i64" ---;
}
zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
return set(data, 0, len);
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless {
return __mem_set(data, value, len);
}
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
// NOTE(bill): This _must_ be implemented like C's memmove
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
llvm_memmove_64bit(dst, src, len, 1, false);
return dst;
zero :: proc(data: rawptr, len: int) -> rawptr #cc_contextless {
return __mem_zero(data, len);
}
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
// NOTE(bill): This _must_ be implemented like C's memcpy
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
llvm_memcpy_64bit(dst, src, len, 1, false);
return dst;
copy :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
return __mem_copy(dst, src, len);
}
compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" {
n := min(a.count, b.count);
for i in 0..n {
match {
case a[i] < b[i]:
return -1;
case a[i] > b[i]:
return +1;
}
}
return 0;
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
return __mem_copy_non_overlapping(dst, src, len);
}
compare :: proc(a, b: []u8) -> int #cc_contextless {
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
}
slice_ptr :: proc(ptr: ^$T, len: int) -> []T #cc_contextless {
assert(len >= 0);
slice := raw.Slice{data = ptr, len = len, cap = len};
return (cast(^[]T)&slice)^;
}
slice_ptr :: proc(ptr: ^$T, len, cap: int) -> []T #cc_contextless {
assert(0 <= len && len <= cap);
slice := raw.Slice{data = ptr, len = len, cap = cap};
return (cast(^[]T)&slice)^;
}
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024; }
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024; }
gigabytes :: proc(x: int) -> int #inline { return megabytes(x) * 1024; }
terabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
slice_to_bytes :: proc(slice: []$T) -> []u8 #cc_contextless {
s := cast(^raw.Slice)&slice;
s.len *= size_of(T);
s.cap *= size_of(T);
return (cast(^[]u8)s)^;
}
kilobytes :: proc(x: int) -> int #inline #cc_contextless { return (x) * 1024; }
megabytes :: proc(x: int) -> int #inline #cc_contextless { return kilobytes(x) * 1024; }
gigabytes :: proc(x: int) -> int #inline #cc_contextless { return megabytes(x) * 1024; }
terabytes :: proc(x: int) -> int #inline #cc_contextless { return gigabytes(x) * 1024; }
is_power_of_two :: proc(x: int) -> bool {
if x <= 0 {
return false;
}
if x <= 0 do return false;
return (x & (x-1)) == 0;
}
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
assert(is_power_of_two(align));
a := cast(uint)align;
p := cast(uint)ptr;
a := uint(align);
p := uint(ptr);
modulo := p & (a-1);
if modulo != 0 {
p += a - modulo;
}
return cast(rawptr)p;
if modulo != 0 do p += a - modulo;
return rawptr(p);
}
Allocation_Header :: struct {
size: int,
AllocationHeader :: struct {
size: int;
}
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
header.size = size;
ptr := cast(^int)(header+1);
ptr := cast(^uint)(header+1);
n := cast(^uint)data - ptr;
for i := 0; cast(rawptr)ptr < data; i++ {
(ptr+i)^ = -1;
for i in 0..n {
(ptr+i)^ = ~uint(0);
}
}
allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
if data == nil {
return nil;
}
p := cast(^int)data;
for (p-1)^ == -1 {
p = (p-1);
}
return cast(^Allocation_Header)p-1;
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);
}
@@ -99,23 +92,24 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
// Custom allocators
Arena :: struct {
backing: Allocator,
offset: int,
memory: []byte,
temp_count: int,
backing: Allocator;
offset: int;
memory: []u8;
temp_count: int;
}
Arena_Temp_Memory :: struct {
arena: ^Arena,
original_count: int,
ArenaTempMemory :: struct {
arena: ^Arena;
original_count: int;
}
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
init_arena_from_memory :: proc(using a: ^Arena, data: []u8) {
backing = Allocator{};
memory = data[..0];
temp_count = 0;
@@ -123,11 +117,11 @@ init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
init_arena_from_context :: proc(using a: ^Arena, size: int) {
backing = context.allocator;
memory = new_slice(byte, size);
memory = make([]u8, size);
temp_count = 0;
}
free_arena :: proc(using a: ^Arena) {
destroy_arena :: proc(using a: ^Arena) {
if backing.procedure != nil {
push_allocator backing {
free(memory);
@@ -144,54 +138,54 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
};
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocator_Mode;
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocator.Mode;
arena := cast(^Arena)allocator_data;
match mode {
case ALLOC:
case Alloc:
total_size := size + alignment;
if arena.offset + total_size > arena.memory.count {
if arena.offset + total_size > len(arena.memory) {
fmt.fprintln(os.stderr, "Arena out of memory");
return nil;
}
#no_bounds_check end := ^arena.memory[arena.offset];
#no_bounds_check end := &arena.memory[arena.offset];
ptr := align_forward(end, alignment);
arena.offset += total_size;
return zero(ptr, size);
case FREE:
case Free:
// NOTE(bill): Free all at once
// Use Arena_Temp_Memory if you want to free a block
// Use ArenaTempMemory if you want to free a block
case FREE_ALL:
case FreeAll:
arena.offset = 0;
case RESIZE:
case Resize:
return default_resize_align(old_memory, old_size, size, alignment);
}
return nil;
}
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory;
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
tmp: ArenaTempMemory;
tmp.arena = a;
tmp.original_count = a.memory.count;
a.temp_count++;
tmp.original_count = len(a.memory);
a.temp_count += 1;
return tmp;
}
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(arena.memory.count >= original_count);
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
assert(len(arena.memory) >= original_count);
assert(arena.temp_count > 0);
arena.memory.count = original_count;
arena.temp_count--;
arena.memory = arena.memory[..original_count];
arena.temp_count -= 1;
}
@@ -200,11 +194,9 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
align_of_type_info :: proc(type_info: ^TypeInfo) -> int {
prev_pow2 :: proc(n: i64) -> i64 {
if n <= 0 {
return 0;
}
if n <= 0 do return 0;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
@@ -216,14 +208,16 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
WORD_SIZE :: size_of(int);
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
using Type_Info;
match info in type_info {
using TypeInfo;
match info in type_info.variant {
case Named:
return align_of_type_info(info.base);
case Integer:
return info.size;
return type_info.align;
case Rune:
return type_info.align;
case Float:
return info.size;
return type_info.align;
case String:
return WORD_SIZE;
case Boolean:
@@ -236,23 +230,21 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
return WORD_SIZE;
case Array:
return align_of_type_info(info.elem);
case Dynamic_Array:
case DynamicArray:
return WORD_SIZE;
case Slice:
return WORD_SIZE;
case Vector:
size := size_of_type_info(info.elem);
count := cast(int)max(prev_pow2(cast(i64)info.count), 1);
size := size_of_type_info(info.elem);
count := int(max(prev_pow2(i64(info.count)), 1));
total := size * count;
return clamp(total, 1, MAX_ALIGN);
case Tuple:
return info.align;
return type_info.align;
case Struct:
return info.align;
return type_info.align;
case Union:
return info.align;
case Raw_Union:
return info.align;
return type_info.align;
case Enum:
return align_of_type_info(info.base);
case Map:
@@ -267,16 +259,18 @@ align_formula :: proc(size, align: int) -> int {
return result - result%align;
}
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
size_of_type_info :: proc(type_info: ^TypeInfo) -> int {
WORD_SIZE :: size_of(int);
using Type_Info;
match info in type_info {
using TypeInfo;
match info in type_info.variant {
case Named:
return size_of_type_info(info.base);
case Integer:
return info.size;
return type_info.size;
case Rune:
return type_info.size;
case Float:
return info.size;
return type_info.size;
case String:
return 2*WORD_SIZE;
case Boolean:
@@ -289,32 +283,26 @@ size_of_type_info :: proc(type_info: ^Type_Info) -> int {
return WORD_SIZE;
case Array:
count := info.count;
if count == 0 {
return 0;
}
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 Dynamic_Array:
case DynamicArray:
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
case Slice:
return 2*WORD_SIZE;
case Vector:
count := info.count;
if count == 0 {
return 0;
}
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 Struct:
return info.size;
return type_info.size;
case Union:
return info.size;
case Raw_Union:
return info.size;
return type_info.size;
case Enum:
return size_of_type_info(info.base);
case Map:
+149 -127
View File
@@ -1,156 +1,178 @@
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "sys/wgl.odin" when ODIN_OS == "windows";
#load "opengl_constants.odin";
foreign_system_library (
lib "opengl32.lib" when ODIN_OS == "windows";
lib "gl" when ODIN_OS == "linux";
)
import (
win32 "sys/windows.odin" when ODIN_OS == "windows";
"sys/wgl.odin" when ODIN_OS == "windows";
)
import_load "opengl_constants.odin";
Clear :: proc(mask: u32) #foreign lib "glClear";
ClearColor :: proc(r, g, b, a: f32) #foreign lib "glClearColor";
Begin :: proc(mode: i32) #foreign lib "glBegin";
End :: proc() #foreign lib "glEnd";
Finish :: proc() #foreign lib "glFinish";
BlendFunc :: proc(sfactor, dfactor: i32) #foreign lib "glBlendFunc";
Enable :: proc(cap: i32) #foreign lib "glEnable";
Disable :: proc(cap: i32) #foreign lib "glDisable";
GenTextures :: proc(count: i32, result: ^u32) #foreign lib "glGenTextures";
DeleteTextures:: proc(count: i32, result: ^u32) #foreign lib "glDeleteTextures";
TexParameteri :: proc(target, pname, param: i32) #foreign lib "glTexParameteri";
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign lib "glTexParameterf";
BindTexture :: proc(target: i32, texture: u32) #foreign lib "glBindTexture";
LoadIdentity :: proc() #foreign lib "glLoadIdentity";
Viewport :: proc(x, y, width, height: i32) #foreign lib "glViewport";
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho";
Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f";
Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f";
GetError :: proc() -> i32 #foreign lib "glGetError";
GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString";
GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv";
TexCoord2f :: proc(x, y: f32) #foreign lib "glTexCoord2f";
TexImage2D :: proc(target, level, internal_format,
width, height, border,
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
_ := compile_assert(ODIN_OS != "osx");
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; }
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00"));
GetProcAddress :: proc(name: string) -> proc() #cc_c {
assert(name[name.count-1] == 0);
res := wgl.GetProcAddress(name.data);
if res == nil {
res = win32.GetProcAddress(_libgl, name.data);
}
return res;
foreign lib {
Clear :: proc(mask: u32) #link_name "glClear" ---;
ClearColor :: proc(r, g, b, a: f32) #link_name "glClearColor" ---;
Begin :: proc(mode: i32) #link_name "glBegin" ---;
End :: proc() #link_name "glEnd" ---;
Finish :: proc() #link_name "glFinish" ---;
BlendFunc :: proc(sfactor, dfactor: i32) #link_name "glBlendFunc" ---;
Enable :: proc(cap: i32) #link_name "glEnable" ---;
Disable :: proc(cap: i32) #link_name "glDisable" ---;
GenTextures :: proc(count: i32, result: ^u32) #link_name "glGenTextures" ---;
DeleteTextures :: proc(count: i32, result: ^u32) #link_name "glDeleteTextures"---;
TexParameteri :: proc(target, pname, param: i32) #link_name "glTexParameteri" ---;
TexParameterf :: proc(target: i32, pname: i32, param: f32) #link_name "glTexParameterf" ---;
BindTexture :: proc(target: i32, texture: u32) #link_name "glBindTexture" ---;
LoadIdentity :: proc() #link_name "glLoadIdentity" ---;
Viewport :: proc(x, y, width, height: i32) #link_name "glViewport" ---;
Ortho :: proc(left, right, bottom, top, near, far: f64) #link_name "glOrtho" ---;
Color3f :: proc(r, g, b: f32) #link_name "glColor3f" ---;
Vertex3f :: proc(x, y, z: f32) #link_name "glVertex3f" ---;
GetError :: proc() -> i32 #link_name "glGetError" ---;
GetString :: proc(name: i32) -> ^u8 #link_name "glGetString" ---;
GetIntegerv :: proc(name: i32, v: ^i32) #link_name "glGetIntegerv" ---;
TexCoord2f :: proc(x, y: f32) #link_name "glTexCoord2f" ---;
TexImage2D :: proc(target, level, internal_format,
width, height, border,
format, type_: i32, pixels: rawptr) #link_name "glTexImage2D" ---;
}
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
BindVertexArray: proc(buffer: u32) #cc_c;
BindSampler: proc(position: i32, sampler: u32) #cc_c;
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
UnmapBuffer: proc(target: i32) #cc_c;
_libgl := win32.load_library_a(_string_data("opengl32.dll\x00"));
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
EnableVertexAttribArray: proc(index: u32) #cc_c;
get_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_proc_address(&name[0]);
if res == nil {
res = win32.get_proc_address(_libgl, &name[0]);
}
return rawptr(res);
}
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c;
CompileShader: proc(shader: u32) #cc_c;
CreateProgram: proc() -> u32 #cc_c;
AttachShader: proc(program, shader: u32) #cc_c;
DetachShader: proc(program, shader: u32) #cc_c;
DeleteShader: proc(shader: u32) #cc_c;
LinkProgram: proc(program: u32) #cc_c;
UseProgram: proc(program: u32) #cc_c;
DeleteProgram: proc(program: u32) #cc_c;
// Procedures
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
BindVertexArray: proc(buffer: u32) #cc_c;
DeleteVertexArrays: proc(count: i32, arrays: ^u32) #cc_c;
BindSampler: proc(position: i32, sampler: u32) #cc_c;
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
UnmapBuffer: proc(target: i32) #cc_c;
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
EnableVertexAttribArray: proc(index: u32) #cc_c;
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
ShaderSource: proc(shader: u32, count: u32, str: ^^u8, length: ^i32) #cc_c;
CompileShader: proc(shader: u32) #cc_c;
CreateProgram: proc() -> u32 #cc_c;
AttachShader: proc(program, shader: u32) #cc_c;
DetachShader: proc(program, shader: u32) #cc_c;
DeleteShader: proc(shader: u32) #cc_c;
LinkProgram: proc(program: u32) #cc_c;
UseProgram: proc(program: u32) #cc_c;
DeleteProgram: proc(program: u32) #cc_c;
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
ActiveTexture: proc(texture: i32) #cc_c;
GenerateMipmap: proc(target: i32) #cc_c;
ActiveTexture: proc(texture: i32) #cc_c;
GenerateMipmap: proc(target: i32) #cc_c;
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
GetUniformLocation: proc(program: u32, name: ^u8) -> i32 #cc_c;
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
init :: proc() {
set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); }
set_proc_address :: proc(p: rawptr, name: string) #inline {
x := cast(^rawptr)p;
x^ = get_proc_address(name);
}
set_proc_address(^GenBuffers, "glGenBuffers\x00");
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00");
set_proc_address(^GenSamplers, "glGenSamplers\x00");
set_proc_address(^BindBuffer, "glBindBuffer\x00");
set_proc_address(^BindSampler, "glBindSampler\x00");
set_proc_address(^BindVertexArray, "glBindVertexArray\x00");
set_proc_address(^BufferData, "glBufferData\x00");
set_proc_address(^BufferSubData, "glBufferSubData\x00");
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(&DrawArrays, "glDrawArrays\x00");
set_proc_address(&DrawElements, "glDrawElements\x00");
set_proc_address(^MapBuffer, "glMapBuffer\x00");
set_proc_address(^UnmapBuffer, "glUnmapBuffer\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(&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(&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(&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(&ActiveTexture, "glActiveTexture\x00");
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
set_proc_address(^Uniform1i, "glUniform1i\x00");
set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(&Uniform1i, "glUniform1i\x00");
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(^GetUniformLocation, "glGetUniformLocation\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");
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");
}
-2
View File
@@ -1,4 +1,3 @@
FALSE :: 0;
TRUE :: 1;
@@ -1382,4 +1381,3 @@ DEBUG_LOGGED_MESSAGES_ARB :: 0x9145;
DEBUG_SEVERITY_HIGH_ARB :: 0x9146;
DEBUG_SEVERITY_MEDIUM_ARB :: 0x9147;
DEBUG_SEVERITY_LOW_ARB :: 0x9148;
+48 -2
View File
@@ -1,3 +1,49 @@
#load "os_windows.odin" when ODIN_OS == "windows";
#load "os_x.odin" when ODIN_OS == "osx";
import_load (
"os_windows.odin" when ODIN_OS == "windows";
"os_x.odin" when ODIN_OS == "osx";
"os_linux.odin" when ODIN_OS == "linux";
)
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, cast([]u8)str);
}
read_entire_file :: proc(name: string) -> (data: []u8, 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([]u8, 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: []u8) -> (sucess: bool) {
fd, err := open(name, O_WRONLY, 0);
if err != 0 {
return false;
}
defer close(fd);
bytes_written, write_err := write(fd, data);
return write_err != 0;
}
+273
View File
@@ -0,0 +1,273 @@
foreign_system_library (
dl "dl";
libc "c";
)
import "strings.odin";
Handle :: i32;
FileTime :: u64;
Errno :: i32;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
O_CREAT :: 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();
_FileTime :: struct #ordered {
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 #ordered {
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: _FileTime; // Time of last access
modified: _FileTime; // Time of last modification
status_change: _FileTime; // 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 :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFLNK; }
S_ISREG :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFREG; }
S_ISDIR :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFDIR; }
S_ISCHR :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFCHR; }
S_ISBLK :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFBLK; }
S_ISFIFO :: proc(m: u32) -> bool #inline {return (m & S_IFMT) == S_IFIFO; }
S_ISSOCK :: proc(m: u32) -> bool #inline {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 {
_unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
_unix_close :: proc(fd: Handle) -> i32 #link_name "close" ---;
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "read" ---;
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "write" ---;
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #link_name "lseek64" ---;
_unix_gettid :: proc() -> u64 #link_name "gettid" ---;
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #link_name "stat" ---;
_unix_access :: proc(path: ^u8, mask: int) -> i32 #link_name "access" ---;
_unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
_unix_free :: proc(ptr: rawptr) #link_name "free" ---;
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
_unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
_unix_exit :: proc(status: int) #link_name "exit" ---;
}
foreign dl {
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
_unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
_unix_dlerror :: proc() -> ^u8 #link_name "dlerror" ---;
}
// 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: []u8) -> (int, Errno) {
sz := _unix_read(fd, &data[0], len(data));
return sz, 0;
}
write :: proc(fd: Handle, data: []u8) -> (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) -> FileTime {}
last_write_time_by_name :: proc(name: string) -> FileTime {}
*/
stat :: proc(path: string) -> (Stat, int) #inline {
s: Stat;
cstr := strings.new_c_string(path);
defer free(cstr);
ret_int := _unix_stat(cstr, &s);
return s, int(ret_int);
}
access :: proc(path: string, mask: int) -> bool #inline {
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_malloc(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);
cstr: ^u8 = _unix_getenv(path_str);
free(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 :: proc(filename: string, flags: int) -> rawptr #inline {
cstr := strings.new_c_string(filename);
handle := _unix_dlopen(cstr, flags);
free(cstr);
return handle;
}
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
proc_handle := _unix_dlsym(handle, cstr);
free(cstr);
return proc_handle;
}
dlclose :: proc(handle: rawptr) -> bool #inline {
assert(handle != nil);
return _unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(_unix_dlerror());
}
_alloc_command_line_arguments :: proc() -> []string {
// TODO(bill):
return nil;
}
+198 -167
View File
@@ -1,14 +1,14 @@
#import win32 "sys/windows.odin";
#import "fmt.odin";
import win32 "sys/windows.odin";
import "mem.odin";
Handle :: int;
FileTime :: u64;
Handle :: int;
File_Time :: u64;
Errno :: int;
INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
@@ -22,135 +22,168 @@ O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
ERROR_NONE: Errno : 0;
ERROR_FILE_NOT_FOUND: Errno : 2;
ERROR_PATH_NOT_FOUND: Errno : 3;
ERROR_ACCESS_DENIED: Errno : 5;
ERROR_NO_MORE_FILES: Errno : 18;
ERROR_HANDLE_EOF: Errno : 38;
ERROR_NETNAME_DELETED: Errno : 64;
ERROR_FILE_EXISTS: Errno : 80;
ERROR_BROKEN_PIPE: Errno : 109;
ERROR_BUFFER_OVERFLOW: Errno : 111;
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
ERROR_MOD_NOT_FOUND: Errno : 126;
ERROR_PROC_NOT_FOUND: Errno : 127;
ERROR_DIR_NOT_EMPTY: Errno : 145;
ERROR_ALREADY_EXISTS: Errno : 183;
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
ERROR_MORE_DATA: Errno : 234;
ERROR_OPERATION_ABORTED: Errno : 995;
ERROR_IO_PENDING: Errno : 997;
ERROR_NOT_FOUND: Errno : 1168;
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
WSAEACCES: Errno : 10013;
WSAECONNRESET: Errno : 10054;
Errno :: int;
ERROR_NONE: Errno : 0;
ERROR_FILE_NOT_FOUND: Errno : 2;
ERROR_PATH_NOT_FOUND: Errno : 3;
ERROR_ACCESS_DENIED: Errno : 5;
ERROR_NO_MORE_FILES: Errno : 18;
ERROR_HANDLE_EOF: Errno : 38;
ERROR_NETNAME_DELETED: Errno : 64;
ERROR_FILE_EXISTS: Errno : 80;
ERROR_BROKEN_PIPE: Errno : 109;
ERROR_BUFFER_OVERFLOW: Errno : 111;
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
ERROR_MOD_NOT_FOUND: Errno : 126;
ERROR_PROC_NOT_FOUND: Errno : 127;
ERROR_DIR_NOT_EMPTY: Errno : 145;
ERROR_ALREADY_EXISTS: Errno : 183;
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
ERROR_MORE_DATA: Errno : 234;
ERROR_OPERATION_ABORTED: Errno : 995;
ERROR_IO_PENDING: Errno : 997;
ERROR_NOT_FOUND: Errno : 1168;
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
WSAEACCES: Errno : 10013;
WSAECONNRESET: Errno : 10054;
// Windows reserves errors >= 1<<29 for application use
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments();
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
using win32;
if path.count == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
}
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;
access: u32;
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: access = FILE_GENERIC_READ;
case O_WRONLY: access = FILE_GENERIC_WRITE;
case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
case O_RDONLY: access = win32.FILE_GENERIC_READ;
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
}
if mode&O_CREAT != 0 {
access |= FILE_GENERIC_WRITE;
access |= win32.FILE_GENERIC_WRITE;
}
if mode&O_APPEND != 0 {
access &~= FILE_GENERIC_WRITE;
access |= FILE_APPEND_DATA;
access &~= win32.FILE_GENERIC_WRITE;
access |= win32.FILE_APPEND_DATA;
}
share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE);
sa: ^SECURITY_ATTRIBUTES = nil;
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
sa: ^win32.SecurityAttributes = nil;
sa_inherit := win32.SecurityAttributes{length = size_of(win32.SecurityAttributes), inherit_handle = 1};
if mode&O_CLOEXEC == 0 {
sa = ^sa_inherit;
sa = &sa_inherit;
}
create_mode: u32;
match {
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
create_mode = CREATE_NEW;
create_mode = win32.CREATE_NEW;
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
create_mode = CREATE_ALWAYS;
create_mode = win32.CREATE_ALWAYS;
case mode&O_CREAT == O_CREAT:
create_mode = OPEN_ALWAYS;
create_mode = win32.OPEN_ALWAYS;
case mode&O_TRUNC == O_TRUNC:
create_mode = TRUNCATE_EXISTING;
default:
create_mode = OPEN_EXISTING;
create_mode = win32.TRUNCATE_EXISTING;
case:
create_mode = win32.OPEN_EXISTING;
}
buf: [300]byte;
copy(buf[..], cast([]byte)path);
buf: [300]u8;
copy(buf[..], cast([]u8)path);
handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil);
if handle != INVALID_HANDLE {
return handle, ERROR_NONE;
}
err := GetLastError();
return INVALID_HANDLE, cast(Errno)err;
handle := Handle(win32.create_file_a(&buf[0], 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.CloseHandle(cast(win32.HANDLE)fd);
win32.close_handle(win32.Handle(fd));
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_written: i32;
e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, cast(Errno)err;
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
if len(data) == 0 do return 0, ERROR_NONE;
single_write_length: i32;
total_write: i64;
length := i64(len(data));
for total_write < length {
remaining := length - total_write;
MAX :: 1<<31-1;
to_write: i32 = min(i32(remaining), MAX);
e := win32.write_file(win32.Handle(fd), &data[total_write], to_write, &single_write_length, nil);
if single_write_length <= 0 || e == win32.FALSE {
err := Errno(win32.get_last_error());
return int(total_write), err;
}
total_write += i64(single_write_length);
}
return cast(int)bytes_written, ERROR_NONE;
return int(total_write), ERROR_NONE;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read: i32;
e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, cast(Errno)err;
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
if len(data) == 0 do return 0, ERROR_NONE;
single_read_length: i32;
total_read: i64;
length := i64(len(data));
for total_read < length {
remaining := length - total_read;
MAX :: 1<<32-1;
to_read: u32 = min(u32(remaining), MAX);
e := win32.read_file(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
if single_read_length <= 0 || e == win32.FALSE {
err := Errno(win32.get_last_error());
return int(total_read), err;
}
total_read += i64(single_read_length);
}
return cast(int)bytes_read, ERROR_NONE;
return int(total_read), ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
using win32;
w: u32;
match whence {
case 0: w = FILE_BEGIN;
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
case 0: w = win32.FILE_BEGIN;
case 1: w = win32.FILE_CURRENT;
case 2: w = win32.FILE_END;
}
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(HANDLE)fd);
if ft == FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
hi := i32(offset>>32);
lo := i32(offset);
ft := win32.get_file_type(win32.Handle(fd));
if ft == win32.FILE_TYPE_PIPE do return 0, ERROR_FILE_IS_PIPE;
dw_ptr := win32.set_file_pointer(win32.Handle(fd), lo, &hi, w);
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.get_last_error());
return 0, err;
}
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, cast(Errno)err;
}
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
length: i64;
err: Errno;
if win32.get_file_size_ex(win32.Handle(fd), &length) == 0 {
err = Errno(win32.get_last_error());
}
return length, err;
}
// NOTE(bill): Uses startup to initialize it
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
@@ -159,9 +192,9 @@ stderr := get_std_handle(win32.STD_ERROR_HANDLE);
get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(cast(i32)h);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd;
fd := win32.get_std_handle(i32(h));
win32.set_handle_information(fd, win32.HANDLE_FLAG_INHERIT, 0);
return Handle(fd);
}
@@ -169,115 +202,113 @@ get_std_handle :: proc(h: int) -> Handle {
last_write_time :: proc(fd: Handle) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
last_write_time :: proc(fd: Handle) -> FileTime {
file_info: win32.ByHandleFileInformation;
win32.get_file_information_by_handle(win32.Handle(fd), &file_info);
lo := FileTime(file_info.last_write_time.lo);
hi := FileTime(file_info.last_write_time.hi);
return lo | hi << 32;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.FILETIME;
data: win32.FILE_ATTRIBUTE_DATA;
buf: [1024]byte;
last_write_time_by_name :: proc(name: string) -> FileTime {
last_write_time: win32.Filetime;
data: win32.FileAttributeData;
buf: [1024]u8;
assert(buf.count > name.count);
assert(len(buf) > len(name));
copy(buf[..], cast([]byte)name);
copy(buf[..], cast([]u8)name);
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
if win32.get_file_attributes_ex_a(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
last_write_time = data.last_write_time;
}
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
l := FileTime(last_write_time.lo);
h := FileTime(last_write_time.hi);
return l | h << 32;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[..], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
return nil, false;
}
defer close(fd);
length: i64;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
if !file_size_ok {
return nil, false;
}
data := new_slice(u8, length);
if data.data == nil {
return nil, false;
}
single_read_length: i32;
total_read: i64;
for total_read < length {
remaining := length - total_read;
to_read: u32;
MAX :: 1<<32-1;
if remaining <= MAX {
to_read = cast(u32)remaining;
} else {
to_read = MAX;
}
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data);
return nil, false;
}
total_read += cast(i64)single_read_length;
}
return data, true;
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
return win32.heap_alloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
heap_free(ptr);
return nil;
}
if ptr == nil {
return heap_alloc(new_size);
}
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
if ptr == nil do return heap_alloc(new_size);
return win32.heap_realloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
if ptr == nil {
return;
}
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
if ptr == nil do return;
win32.heap_free(win32.get_process_heap(), 0, ptr);
}
exit :: proc(code: int) {
win32.ExitProcess(cast(u32)code);
win32.exit_process(u32(code));
}
current_thread_id :: proc() -> int {
return cast(int)win32.GetCurrentThreadId();
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([]u8, len+1);
str := mem.slice_ptr(wstr, wstr_len+1);
i, j := 0, 0;
for str[j] != 0 {
match {
case str[j] < 0x80:
if i+1 > len do return "";
buf[i] = u8(str[j]); i += 1;
j += 1;
case str[j] < 0x800:
if i+2 > len do return "";
buf[i] = u8(0xc0 + (str[j]>>6)); i += 1;
buf[i] = u8(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] = u8(0xf0 + (c >> 18)); i += 1;
buf[i] = u8(0x80 + ((c >> 12) & 0x3f)); i += 1;
buf[i] = u8(0x80 + ((c >> 6) & 0x3f)); i += 1;
buf[i] = u8(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 + u8 (str[j] >> 12); i += 1;
buf[i] = 0x80 + u8((str[j] >> 6) & 0x3f); i += 1;
buf[i] = 0x80 + u8((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)^);
return arg_list;
}
+221 -168
View File
@@ -1,10 +1,13 @@
#import "fmt.odin";
foreign_system_library (
dl "dl";
libc "c";
)
Handle :: i32;
File_Time :: u64;
Errno :: int;
import "strings.odin";
// INVALID_HANDLE: Handle : -1;
Handle :: i32;
FileTime :: u64;
Errno :: int;
O_RDONLY :: 0x00000;
@@ -20,203 +23,235 @@ O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
// ERROR_NONE: Errno : 0;
// ERROR_FILE_NOT_FOUND: Errno : 2;
// ERROR_PATH_NOT_FOUND: Errno : 3;
// ERROR_ACCESS_DENIED: Errno : 5;
// ERROR_NO_MORE_FILES: Errno : 18;
// ERROR_HANDLE_EOF: Errno : 38;
// ERROR_NETNAME_DELETED: Errno : 64;
// ERROR_FILE_EXISTS: Errno : 80;
// ERROR_BROKEN_PIPE: Errno : 109;
// ERROR_BUFFER_OVERFLOW: Errno : 111;
// ERROR_INSUFFICIENT_BUFFER: Errno : 122;
// ERROR_MOD_NOT_FOUND: Errno : 126;
// ERROR_PROC_NOT_FOUND: Errno : 127;
// ERROR_DIR_NOT_EMPTY: Errno : 145;
// ERROR_ALREADY_EXISTS: Errno : 183;
// ERROR_ENVVAR_NOT_FOUND: Errno : 203;
// ERROR_MORE_DATA: Errno : 234;
// ERROR_OPERATION_ABORTED: Errno : 995;
// ERROR_IO_PENDING: Errno : 997;
// ERROR_NOT_FOUND: Errno : 1168;
// ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
// WSAEACCES: Errno : 10013;
// WSAECONNRESET: Errno : 10054;
// Windows reserves errors >= 1<<29 for application use
// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
#foreign_system_library libc "c";
unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open";
unix_close :: proc(handle: Handle) #foreign libc "close";
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read";
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write";
unix_gettid :: proc() -> u64 #foreign libc "gettid";
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
unix_free :: proc(ptr: rawptr) #foreign libc "free";
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
unix_exit :: proc(status: int) #foreign libc "exit";
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 :: 0x1;
RTLD_NOW :: 0x2;
RTLD_LOCAL :: 0x4;
RTLD_GLOBAL :: 0x8;
RTLD_NODELETE :: 0x80;
RTLD_NOLOAD :: 0x10;
RTLD_FIRST :: 0x100;
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
return unix_open(path.data, mode, perm), 0;
args: [dynamic]string;
_FileTime :: struct #ordered {
seconds: i64;
nanoseconds: i64;
}
Stat :: struct #ordered {
device_id: i32; // ID of device containing file
mode: u16; // Mode of the file
nlink: u16; // Number of hard links
serial: u64; // File serial number
uid: u32; // User ID of the file's owner
gid: u32; // Group ID of the file's group
rdev: i32; // Device ID, if device
last_access: FileTime; // Time of last access
modified: FileTime; // Time of last modification
status_change: FileTime; // Time of last status change
created: FileTime; // Time of creation
size: i64; // Size of the file, in bytes
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 ...?
_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
// 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 :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFLNK;
S_ISREG :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFREG;
S_ISDIR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFDIR;
S_ISCHR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFCHR;
S_ISBLK :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFBLK;
S_ISFIFO :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFIFO;
S_ISSOCK :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFSOCK;
R_OK :: 4; // Test for read permission
W_OK :: 2; // Test for write permission
X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
foreign libc {
unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
unix_close :: proc(handle: Handle) #link_name "close" ---;
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "read" ---;
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "write" ---;
unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int #link_name "lseek" ---;
unix_gettid :: proc() -> u64 #link_name "gettid" ---;
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #link_name "stat" ---;
unix_access :: proc(path: ^u8, mask: int) -> int #link_name "access" ---;
unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
unix_free :: proc(ptr: rawptr) #link_name "free" ---;
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
unix_exit :: proc(status: int) #link_name "exit" ---;
}
foreign dl {
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
unix_dlerror :: proc() -> ^u8 #link_name "dlerror" ---;
}
// 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);
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_write(fd, data.data, data.count), 0;
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1);
bytes_written := unix_write(fd, &data[0], len(data));
if(bytes_written == -1) {
return 0, 1;
}
return bytes_written, 0;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_read(fd, data.data, data.count), 0;
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1);
bytes_read := unix_read(fd, &data[0], len(data));
if(bytes_read == -1) {
return 0, 1;
}
return bytes_read, 0;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
/*
using win32;
w: u32;
match whence {
case 0: w = FILE_BEGIN;
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
}
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(HANDLE)fd);
if ft == FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, cast(Errno)err;
}
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
assert(fd != -1);
*/
return 0, 0;
final_offset := i64(unix_lseek(fd, offset, whence));
if(final_offset == -1) {
return 0, 1;
}
return final_offset, 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 i64(size), err;
}
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
/*
get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(cast(i32)h);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd;
}
last_write_time :: proc(fd: Handle) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
return lo | hi << 32;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.FILETIME;
data: win32.FILE_ATTRIBUTE_DATA;
buf: [1024]byte;
assert(buf.count > name.count);
copy(buf[:], cast([]byte)name);
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
last_write_time = data.last_write_time;
}
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
return l | h << 32;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[:], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
return nil, false;
}
defer close(fd);
length: i64;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
if !file_size_ok {
return nil, false;
}
data := new_slice(u8, length);
if data.data == nil {
return nil, false;
}
single_read_length: i32;
total_read: i64;
for total_read < length {
remaining := length - total_read;
to_read: u32;
MAX :: 1<<32-1;
if remaining <= MAX {
to_read = cast(u32)remaining;
} else {
to_read = MAX;
}
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data);
return nil, false;
}
total_read += cast(i64)single_read_length;
}
return data, true;
}
/* TODO(zangent): Implement these!
last_write_time :: proc(fd: Handle) -> FileTime {}
last_write_time_by_name :: proc(name: string) -> FileTime {}
*/
heap_alloc :: proc(size: int) -> rawptr {
stat :: proc(path: string) -> (Stat, bool) #inline {
s: Stat;
cstr := strings.new_c_string(path);
defer free(cstr);
ret_int := unix_stat(cstr, &s);
return s, ret_int==0;
}
access :: proc(path: string, mask: int) -> bool #inline {
cstr := strings.new_c_string(path);
defer free(cstr);
return unix_access(cstr, mask) == 0;
}
heap_alloc :: proc(size: int) -> rawptr #inline {
assert(size > 0);
return unix_malloc(size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
return unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
heap_free :: proc(ptr: rawptr) #inline {
unix_free(ptr);
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
cstr: ^u8 = unix_getenv(path_str);
free(path_str);
if(cstr == nil) {
return "", false;
}
return strings.to_odin_string(cstr), true;
}
exit :: proc(code: int) {
exit :: proc(code: int) #inline {
unix_exit(code);
}
@@ -226,5 +261,23 @@ current_thread_id :: proc() -> int {
return 0;
}
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
cstr := strings.new_c_string(filename);
handle := unix_dlopen(cstr, flags);
free(cstr);
return handle;
}
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
proc_handle := unix_dlsym(handle, cstr);
free(cstr);
return proc_handle;
}
dlclose :: proc(handle: rawptr) -> bool #inline {
assert(handle != nil);
return unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(unix_dlerror());
}
+28
View File
@@ -0,0 +1,28 @@
Any :: struct #ordered {
data: rawptr;
type_info: ^TypeInfo;
};
String :: struct #ordered {
data: ^u8;
len: int;
};
Slice :: struct #ordered {
data: rawptr;
len: int;
cap: int;
};
DynamicArray :: struct #ordered {
data: rawptr;
len: int;
cap: int;
allocator: Allocator;
};
DynamicMap :: struct #ordered {
hashes: [dynamic]int;
entries: DynamicArray;
};
+213
View File
@@ -0,0 +1,213 @@
bubble_sort :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil);
count := len(array);
init_j, last_j := 0, count-1;
for {
init_swap, prev_swap := -1, -1;
for j in init_j..last_j {
if f(array[j], array[j+1]) > 0 {
array[j], array[j+1] = array[j+1], array[j];
prev_swap = j;
if init_swap == -1 do init_swap = j;
}
}
if prev_swap == -1 do return;
init_j = max(init_swap-1, 0);
last_j = prev_swap;
}
}
bubble_sort :: proc(array: $A/[]$T) {
count := len(array);
init_j, last_j := 0, count-1;
for {
init_swap, prev_swap := -1, -1;
for j in init_j..last_j {
if array[j] > array[j+1] {
array[j], array[j+1] = array[j+1], array[j];
prev_swap = j;
if init_swap == -1 do init_swap = j;
}
}
if prev_swap == -1 do return;
init_j = max(init_swap-1, 0);
last_j = prev_swap;
}
}
quick_sort :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil);
a := array;
n := len(a);
if n < 2 do return;
p := a[n/2];
i, j := 0, n-1;
loop: for {
for f(a[i], p) < 0 do i += 1;
for f(p, a[j]) < 0 do j -= 1;
if i >= j do break loop;
a[i], a[j] = a[j], a[i];
i += 1;
j -= 1;
}
quick_sort(a[0..i], f);
quick_sort(a[i..n], f);
}
quick_sort :: proc(array: $A/[]$T) {
a := array;
n := len(a);
if n < 2 do return;
p := a[n/2];
i, j := 0, n-1;
loop: for {
for a[i] < p do i += 1;
for p < a[j] do j -= 1;
if i >= j do break loop;
a[i], a[j] = a[j], a[i];
i += 1;
j -= 1;
}
quick_sort(a[0..i]);
quick_sort(a[i..n]);
}
_log2 :: proc(n: int) -> int {
res := 0;
for ; n != 0; n >>= 1 do res += 1;
return res;
}
merge_sort :: 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 {
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
out[k] = arr1[i];
i += 1;
} else {
out[k] = arr2[j];
j += 1;
}
}
}
assert(f != nil);
arr1 := array;
N := len(arr1);
arr2 := make([]T, N);
defer free(arr2);
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M+1 {
for j in 0..a {
k := 2*j*m;
merge_slices(arr1[k..k+m], arr1[k+m..k+m+m], arr2[k..], f);
}
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);
} else {
copy(arr2[b..N], arr1[b..N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
a >>= 1;
b = a << uint(i) << 2;
}
if M & 1 == 0 do copy(arr2, arr1);
}
merge_sort :: proc(array: $A/[]$T) {
merge_slices :: proc(arr1, arr2, out: A) {
N1, N2 := len(arr1), len(arr2);
i, j := 0, 0;
for k in 0..N1+N2 {
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
out[k] = arr1[i];
i += 1;
} else {
out[k] = arr2[j];
j += 1;
}
}
}
arr1 := array;
N := len(arr1);
arr2 := make([]T, N);
defer free(arr2);
a, b, m, M := N/2, N, 1, _log2(N);
for i in 0..M+1 {
for j in 0..a {
k := 2*j*m;
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..]);
} else {
copy(arr2[b..N], arr1[b..N]);
}
arr1, arr2 = arr2, arr1;
m <<= 1;
a >>= 1;
b = a << uint(i) << 2;
}
if M & 1 == 0 do copy(arr2, arr1);
}
compare_ints :: proc(a, b: int) -> int {
match delta := a - b; {
case delta < 0: return -1;
case delta > 0: return +1;
}
return 0;
}
compare_f32s :: proc(a, b: f32) -> int {
match delta := a - b; {
case delta < 0: return -1;
case delta > 0: return +1;
}
return 0;
}
compare_f64s :: proc(a, b: f64) -> int {
match delta := a - b; {
case delta < 0: return -1;
case delta > 0: return +1;
}
return 0;
}
compare_strings :: proc(a, b: string) -> int {
return __string_cmp(a, b);
}
+291 -137
View File
@@ -1,10 +1,9 @@
#import . "decimal.odin";
#import "math.odin";
import . "decimal.odin";
Int_Flag :: enum {
PREFIX = 1<<0,
PLUS = 1<<1,
SPACE = 1<<2,
IntFlag :: enum {
Prefix = 1<<0,
Plus = 1<<1,
Space = 1<<2,
}
@@ -18,63 +17,227 @@ parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
return false, false;
}
append_bool :: proc(buf: []byte, b: bool) -> string {
s := b ? "true" : "false";
append(buf, ..cast([]byte)s);
return cast(string)buf;
_digit_value :: proc(r: rune) -> int {
ri := int(r);
v: int = 16;
match r {
case '0'..'9': v = ri-'0';
case 'a'..'z': v = ri-'a'+10;
case 'A'..'Z': v = ri-'A'+10;
}
return v;
}
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, digits, 0);
}
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, cast(u64)i, base, i < 0, digits, 0);
}
itoa :: proc(buf: []byte, i: int) -> string {
return append_int(buf, cast(i64)i, 10);
parse_i128 :: proc(s: string) -> i128 {
neg := false;
if len(s) > 1 {
match s[0] {
case '-':
neg = true;
s = s[1..];
case '+':
s = s[1..];
}
}
base: i128 = 10;
if len(s) > 2 && s[0] == '0' {
match 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: i128;
for r in s {
if r == '_' {
continue;
}
v := i128(_digit_value(r));
if v >= base {
break;
}
value *= base;
value += v;
}
if neg do return -value;
return value;
}
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);
parse_u128 :: proc(s: string) -> u128 {
neg := false;
if len(s) > 1 && s[0] == '+' {
s = s[1..];
}
base := u128(10);
if len(s) > 2 && s[0] == '0' {
match 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: u128;
for r in s {
if r == '_' do continue;
v := u128(_digit_value(r));
if v >= base do break;
value *= base;
value += u128(v);
}
if neg do return -value;
return value;
}
parse_int :: proc(s: string) -> int {
return int(parse_i128(s));
}
parse_uint :: proc(s: string, base: int) -> uint {
return uint(parse_u128(s));
}
parse_f64 :: proc(s: string) -> f64 {
i := 0;
sign: f64 = 1;
match 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 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 s[i] == 'e' || s[i] == 'E' {
i += 1;
match 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: []u8, b: bool) -> string {
if b {
append(&buf, "true");
} else {
append(&buf, "false");
}
return string(buf);
}
append_uint :: proc(buf: []u8, u: u64, base: int) -> string {
return append_bits(buf, u128(u), base, false, 8*size_of(uint), digits, 0);
}
append_int :: proc(buf: []u8, i: i64, base: int) -> string {
return append_bits(buf, u128(i), base, true, 8*size_of(int), digits, 0);
}
itoa :: proc(buf: []u8, i: int) -> string { return append_int(buf, i64(i), 10); }
append_float :: proc(buf: []u8, f: f64, fmt: u8, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
}
Decimal_Slice :: struct {
digits: []byte,
count: int,
decimal_point: int,
neg: bool,
DecimalSlice :: struct {
digits: []u8;
count: int;
decimal_point: int;
neg: bool;
}
Float_Info :: struct {
mantbits: uint,
expbits: uint,
bias: int,
FloatInfo :: struct {
mantbits: uint;
expbits: uint;
bias: int;
}
f32_info := Float_Info{23, 8, -127};
f64_info := Float_Info{52, 11, -1023};
_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 {
generic_ftoa :: proc(buf: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 {
bits: u64;
flt: ^Float_Info;
flt: ^FloatInfo;
match bit_size {
case 32:
bits = cast(u64)transmute(u32)cast(f32)val;
flt = ^f32_info;
bits = u64(transmute(u32)f32(val));
flt = &_f32_info;
case 64:
bits = transmute(u64)val;
flt = ^f64_info;
default:
flt = &_f64_info;
case:
panic("strconv: invalid bit_size");
}
neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := cast(int)(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (cast(u64)1 << flt.mantbits - 1);
neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (u64(1) << flt.mantbits - 1);
match exp {
case 1<<flt.expbits - 1:
@@ -86,27 +249,27 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
} else {
s = "+Inf";
}
append(buf, ..cast([]byte)s);
append(&buf, ...cast([]u8)s);
return buf;
case 0: // denormalized
exp++;
exp += 1;
default:
mant |= cast(u64)1 << flt.mantbits;
case:
mant |= u64(1) << flt.mantbits;
}
exp += flt.bias;
d_: Decimal;
d := ^d_;
d := &d_;
assign(d, mant);
shift(d, exp - cast(int)flt.mantbits);
digs: Decimal_Slice;
shift(d, exp - int(flt.mantbits));
digs: DecimalSlice;
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};
digs = DecimalSlice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
match fmt {
case 'e', 'E': prec = digs.count-1;
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
@@ -123,60 +286,43 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
round(d, prec);
}
digs = Decimal_Slice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
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: Decimal_Slice, prec: int, fmt: byte) -> []byte {
format_digits :: proc(buf: []u8, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: u8) -> []u8 {
match fmt {
case 'f', 'F':
add_bytes :: proc(dst: ^[]byte, w: ^int, bytes: ..byte) {
for b in bytes {
if dst.capacity <= w^ {
break;
}
dst.count++;
dst[w^] = b;
w^++;
}
}
dst := buf[..];
w := 0;
if neg {
add_bytes(^dst, ^w, '-');
} else {
add_bytes(^dst, ^w, '+');
}
append(&buf, neg ? '-' : '+');
// integer, padded with zeros when needed
if digs.decimal_point > 0 {
m := min(digs.count, digs.decimal_point);
add_bytes(^dst, ^w, ..digs.digits[..m]);
for ; m < digs.decimal_point; m++ {
add_bytes(^dst, ^w, '0');
append(&buf, ...digs.digits[..m]);
for ; m < digs.decimal_point; m += 1 {
append(&buf, '0');
}
} else {
add_bytes(^dst, ^w, '0');
append(&buf, '0');
}
// fractional part
if prec > 0 {
add_bytes(^dst, ^w, '.');
append(&buf, '.');
for i in 0..prec {
c: byte = '0';
c: u8 = '0';
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
c = digs.digits[j];
}
add_bytes(^dst, ^w, c);
append(&buf, c);
}
}
return buf[..w];
return buf;
case 'e', 'E':
panic("strconv: e/E float printing is not yet supported");
@@ -187,14 +333,12 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
return buf; // TODO
}
c: [2]byte;
c[0] = '%';
c[1] = fmt;
append(buf, ..c[..]);
c := [2]u8{'%', fmt};
append(&buf, ...c[..]);
return buf;
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
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;
@@ -207,14 +351,14 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
332*(dp-nd) >= 100*(exp-mantbits)
*/
minexp := flt.bias+1;
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - cast(int)flt.mantbits) {
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
// Number is already its shortest
return;
}
upper_: Decimal; upper: = ^upper_;
upper_: Decimal; upper := &upper_;
assign(upper, 2*mant - 1);
shift(upper, exp - cast(int)flt.mantbits - 1);
shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64;
explo: int;
@@ -225,19 +369,19 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower: = ^lower_;
lower_: Decimal; lower := &lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - cast(int)flt.mantbits - 1);
shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0;
for i in 0..d.count {
l: byte = '0'; // lower digit
l: u8 = '0'; // lower digit
if i < lower.count {
l = lower.digits[i];
}
m := d.digits[i]; // middle digit
u: byte = '0'; // upper digit
u: u8 = '0'; // upper digit
if i < upper.count {
u = upper.digits[i];
}
@@ -262,75 +406,85 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
}
MAX_BASE :: 32;
immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, flags: Int_Flag) -> string {
is_pow2 :: proc(x: i64) -> bool {
if (x <= 0) {
return false;
is_integer_negative :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
neg := false;
if is_signed {
match bit_size {
case 8:
i := i8(u);
neg = i < 0;
if neg { i = -i; }
u = u128(i);
case 16:
i := i16(u);
neg = i < 0;
if neg { i = -i; }
u = u128(i);
case 32:
i := i32(u);
neg = i < 0;
if neg { i = -i; }
u = u128(i);
case 64:
i := i64(u);
neg = i < 0;
if neg { i = -i; }
u = u128(i);
case 128:
i := i128(u);
neg = i < 0;
if neg { i = -i; }
u = u128(i);
case:
panic("is_integer_negative: Unknown integer size");
}
return x&(x-1) == 0;
}
return u, neg;
}
append_bits :: proc(buf: []u8, u: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: IntFlag) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits");
}
a: [65]byte;
i := a.count;
if neg {
u = -u;
neg: bool;
a: [129]u8;
i := len(a);
u, neg = is_integer_negative(u, is_signed, bit_size);
b := u128(base);
for u >= b {
i-=1; a[i] = digits[uint(u % b)];
u /= b;
}
i-=1; a[i] = digits[uint(u % b)];
if is_pow2(cast(i64)base) {
b := cast(u64)base;
m := cast(uint)b - 1;
for u >= b {
i--;
a[i] = digits[cast(uint)u & m];
u >>= b;
}
i--;
a[i] = digits[cast(uint)u];
} else {
b := cast(u64)base;
for u >= b {
i--;
q := u / b;
a[i] = digits[cast(uint)(u-q*b)];
u = q;
}
i--;
a[i] = digits[cast(uint)u];
}
if flags&Int_Flag.PREFIX != 0 {
if flags&IntFlag.Prefix != 0 {
ok := true;
match base {
case 2: i--; a[i] = 'b';
case 8: i--; a[i] = 'o';
case 10: i--; a[i] = 'd';
case 16: i--; a[i] = 'x';
default: ok = false;
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--;
a[i] = '0';
i-=1; a[i] = '0';
}
}
if neg {
i--; a[i] = '-';
} else if flags&Int_Flag.PLUS != 0 {
i--; a[i] = '+';
} else if flags&Int_Flag.SPACE != 0 {
i--; a[i] = ' ';
i-=1; a[i] = '-';
} else if flags&IntFlag.Plus != 0 {
i-=1; a[i] = '+';
} else if flags&IntFlag.Space != 0 {
i-=1; a[i] = ' ';
}
append(buf, ..a[i..]);
return cast(string)buf;
append(&buf, ...a[i..]);
return string(buf);
}
+19 -12
View File
@@ -1,15 +1,22 @@
new_c_string :: proc(s: string) -> ^byte {
c := new_slice(byte, s.count+1);
copy(c, cast([]byte)s);
c[s.count] = 0;
return c.data;
import "mem.odin";
new_string :: proc(s: string) -> string {
c := make([]u8, len(s)+1);
copy(c, cast([]u8)s);
c[len(s)] = 0;
return string(c[..len(s)]);
}
to_odin_string :: proc(c: ^byte) -> string {
s: string;
s.data = c;
for (c+s.count)^ != 0 {
s.count++;
}
return s;
new_c_string :: proc(s: string) -> ^u8 {
c := make([]u8, len(s)+1);
copy(c, cast([]u8)s);
c[len(s)] = 0;
return &c[0];
}
to_odin_string :: proc(c: ^u8) -> string {
if c == nil do return "";
len := 0;
for (c+len)^ != 0 do len+=1;
return string(mem.slice_ptr(c, len));
}
+4 -91
View File
@@ -1,91 +1,4 @@
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "atomic.odin";
Semaphore :: struct {
_handle: win32.HANDLE,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return cast(i32)win32.GetCurrentThreadId();
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.CloseHandle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
}
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
semaphore_wait :: proc(s: ^Semaphore) {
win32.WaitForSingleObject(s._handle, win32.INFINITE);
}
mutex_init :: proc(m: ^Mutex) {
atomic.store(^m._counter, 0);
atomic.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 atomic.fetch_add(^m._counter, 1) > 0 {
if thread_id != atomic.load(^m._owner) {
semaphore_wait(^m._semaphore);
}
}
atomic.store(^m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomic.load(^m._owner) == thread_id {
atomic.fetch_add(^m._counter, 1);
} else {
expected: i32 = 0;
if atomic.load(^m._counter) != 0 {
return false;
}
if atomic.compare_exchange(^m._counter, expected, 1) == 0 {
return false;
}
atomic.store(^m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomic.load(^m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomic.store(^m._owner, thread_id);
}
if atomic.fetch_add(^m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(^m._semaphore);
}
}
}
import_load (
"sync_windows.odin" when ODIN_OS == "windows";
"sync_linux.odin" when ODIN_OS == "linux";
)
+95
View File
@@ -0,0 +1,95 @@
import (
"atomics.odin";
"os.odin";
)
Semaphore :: struct {
// _handle: win32.Handle;
}
Mutex :: struct {
_semaphore: Semaphore;
_counter: i32;
_owner: i32;
_recursion: i32;
}
current_thread_id :: proc() -> i32 {
return i32(os.current_thread_id());
}
semaphore_init :: proc(s: ^Semaphore) {
// s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
// win32.CloseHandle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
// win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
}
semaphore_release :: proc(s: ^Semaphore) #inline {
semaphore_post(s, 1);
}
semaphore_wait :: proc(s: ^Semaphore) {
// win32.WaitForSingleObject(s._handle, win32.INFINITE);
}
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 += 1;
}
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 += 1;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomics.load(&m._owner));
m._recursion -= 1;
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);
}
}
}
+122
View File
@@ -0,0 +1,122 @@
import (
win32 "sys/windows.odin" when ODIN_OS == "windows";
"atomics.odin";
)
Semaphore :: struct {
_handle: win32.Handle;
}
/*
Mutex :: struct {
_semaphore: Semaphore;
_counter: i32;
_owner: i32;
_recursion: i32;
}
*/
Mutex :: struct {
_critical_section: win32.CriticalSection;
}
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 :: proc(s: ^Semaphore) #inline { 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 win32.try_enter_critical_section(&m._critical_section) != 0;
}
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);
}
}
}
*/
+75 -64
View File
@@ -1,72 +1,83 @@
#foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
#import . "windows.odin";
foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
import . "windows.odin";
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
CONTEXT_MINOR_VERSION_ARB :: 0x2092;
CONTEXT_FLAGS_ARB :: 0x2094;
CONTEXT_PROFILE_MASK_ARB :: 0x9126;
CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001;
HGLRC :: HANDLE;
COLORREF :: u32;
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
CONTEXT_MINOR_VERSION_ARB :: 0x2092;
CONTEXT_FLAGS_ARB :: 0x2094;
CONTEXT_PROFILE_MASK_ARB :: 0x9126;
CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001;
CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002;
LAYERPLANEDESCRIPTOR :: struct #ordered {
size: u16,
version: u16,
flags: u32,
pixel_type: byte,
color_bits: byte,
red_bits: byte,
red_shift: byte,
green_bits: byte,
green_shift: byte,
blue_bits: byte,
blue_shift: byte,
alpha_bits: byte,
alpha_shift: byte,
accum_bits: byte,
accum_red_bits: byte,
accum_green_bits: byte,
accum_blue_bits: byte,
accum_alpha_bits: byte,
depth_bits: byte,
stencil_bits: byte,
aux_buffers: byte,
layer_type: byte,
reserved: byte,
transparent: COLORREF,
Hglrc :: Handle;
ColorRef :: u32;
LayerPlaneDescriptor :: struct {
size: u16;
version: u16;
flags: u32;
pixel_type: u8;
color_bits: u8;
red_bits: u8;
red_shift: u8;
green_bits: u8;
green_shift: u8;
blue_bits: u8;
blue_shift: u8;
alpha_bits: u8;
alpha_shift: u8;
accum_bits: u8;
accum_red_bits: u8;
accum_green_bits: u8;
accum_blue_bits: u8;
accum_alpha_bits: u8;
depth_bits: u8;
stencil_bits: u8;
aux_buffers: u8;
layer_type: u8;
reserved: u8;
transparent: ColorRef;
}
POINTFLOAT :: struct #ordered {
x, y: f32,
PointFloat :: struct {x, y: f32};
Glyph_MetricsFloat :: struct {
black_box_x: f32;
black_box_y: f32;
glyph_origin: PointFloat;
cell_inc_x: f32;
cell_inc_y: f32;
}
GLYPHMETRICSFLOAT :: struct #ordered {
black_box_x: f32,
black_box_y: f32,
glyph_origin: POINTFLOAT,
cell_inc_x: f32,
cell_inc_y: f32,
CreateContextAttribsARBType :: proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
ChoosePixelFormatARBType :: proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
SwapIntervalEXTType :: proc(interval: i32) -> bool #cc_c;
GetExtensionsStringARBType :: proc(Hdc) -> ^u8 #cc_c;
// Procedures
create_context_attribs_arb: CreateContextAttribsARBType;
choose_pixel_format_arb: ChoosePixelFormatARBType;
swap_interval_ext: SwapIntervalEXTType;
get_extensions_string_arb: GetExtensionsStringARBType;
foreign opengl32 {
create_context :: proc(hdc: Hdc) -> Hglrc #link_name "wglCreateContext" ---;
make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #link_name "wglMakeCurrent" ---;
get_proc_address :: proc(c_str: ^u8) -> rawptr #link_name "wglGetProcAddress" ---;
delete_context :: proc(hglrc: Hglrc) -> Bool #link_name "wglDeleteContext" ---;
copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool #link_name "wglCopyContext" ---;
create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #link_name "wglCreateLayerContext" ---;
describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^LayerPlaneDescriptor) -> Bool #link_name "wglDescribeLayerPlane" ---;
get_current_context :: proc() -> Hglrc #link_name "wglGetCurrentContext" ---;
get_current_dc :: proc() -> Hdc #link_name "wglGetCurrentDC" ---;
get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #link_name "wglGetLayerPaletteEntries" ---;
realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #link_name "wglRealizeLayerPalette" ---;
set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #link_name "wglSetLayerPaletteEntries" ---;
share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #link_name "wglShareLists" ---;
swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool #link_name "wglSwapLayerBuffers" ---;
use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #link_name "wglUseFontBitmaps" ---;
use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_MetricsFloat) -> Bool #link_name "wglUseFontOutlines" ---;
}
CreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
ChoosePixelFormatARBType :: #type proc(hdc: HDC, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> BOOL #cc_c;
CreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32 "wglCreateContext";
MakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32 "wglMakeCurrent";
GetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32 "wglGetProcAddress";
DeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32 "wglDeleteContext";
CopyContext :: proc(src, dst: HGLRC, mask: u32) -> BOOL #foreign opengl32 "wglCopyContext";
CreateLayerContext :: proc(hdc: HDC, layer_plane: i32) -> HGLRC #foreign opengl32 "wglCreateLayerContext";
DescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: i32, bytes: u32, pd: ^LAYERPLANEDESCRIPTOR) -> BOOL #foreign opengl32 "wglDescribeLayerPlane";
GetCurrentContext :: proc() -> HGLRC #foreign opengl32 "wglGetCurrentContext";
GetCurrentDC :: proc() -> HDC #foreign opengl32 "wglGetCurrentDC";
GetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries";
RealizeLayerPalette :: proc(hdc: HDC, layer_plane: i32, realize: BOOL) -> BOOL #foreign opengl32 "wglRealizeLayerPalette";
SetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries";
ShareLists :: proc(hglrc1, hglrc2: HGLRC) -> BOOL #foreign opengl32 "wglShareLists";
SwapLayerBuffers :: proc(hdc: HDC, planes: u32) -> BOOL #foreign opengl32 "wglSwapLayerBuffers";
UseFontBitmaps :: proc(hdc: HDC, first, count, list_base: u32) -> BOOL #foreign opengl32 "wglUseFontBitmaps";
UseFontOutlines :: proc(hdc: HDC, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^GLYPHMETRICSFLOAT) -> BOOL #foreign opengl32 "wglUseFontOutlines";
+545 -441
View File
File diff suppressed because it is too large Load Diff
+76
View File
@@ -0,0 +1,76 @@
_ :: compile_assert(ODIN_OS == "windows");
import win32 "sys/windows.odin";
Thread :: struct {
using specific: OsSpecific;
procedure: Proc;
data: any;
user_index: int;
init_context: Context;
use_init_context: bool;
Proc :: #type proc(^Thread) -> int;
OsSpecific :: struct {
win32_thread: win32.Handle;
win32_thread_id: u32;
}
}
create :: proc(procedure: Thread.Proc) -> ^Thread {
win32_thread_id: u32;
__windows_thread_entry_proc :: proc(data: rawptr) -> i32 #cc_c {
if data == nil do return 0;
t := cast(^Thread)data;
c := context;
if t.use_init_context {
c = t.init_context;
}
exit := 0;
push_context c {
exit = t.procedure(t);
}
return cast(i32)exit;
}
win32_thread_proc := cast(rawptr)__windows_thread_entry_proc;
thread := new(Thread);
win32_thread := win32.create_thread(nil, 0, win32_thread_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
if win32_thread == nil {
free(thread);
return nil;
}
thread.procedure = procedure;
thread.win32_thread = win32_thread;
thread.win32_thread_id = win32_thread_id;
return thread;
}
start :: proc(using thread: ^Thread) {
win32.resume_thread(win32_thread);
}
is_done :: proc(using thread: ^Thread) -> bool {
res := win32.wait_for_single_object(win32_thread, 0);
return res != win32.WAIT_TIMEOUT;
}
join :: proc(using thread: ^Thread) {
win32.wait_for_single_object(win32_thread, win32.INFINITE);
win32.close_handle(win32_thread);
win32_thread = win32.INVALID_HANDLE;
}
destroy :: proc(thread: ^Thread) {
join(thread);
free(thread);
}
+83 -126
View File
@@ -1,146 +1,103 @@
is_signed :: proc(info: ^Type_Info) -> bool {
if is_integer(info) {
i := union_cast(^Type_Info.Integer)info;
return i.signed;
}
if is_float(info) {
return true;
is_signed :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
match i in type_info_base(info).variant {
case TypeInfo.Integer: return i.signed;
case TypeInfo.Float: return true;
}
return false;
}
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Integer: return true;
}
return false;
is_integer :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Integer);
return ok;
}
is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Float: return true;
}
return false;
is_rune :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Rune);
return ok;
}
is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Any: return true;
}
return false;
is_float :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Float);
return ok;
}
is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.String: return true;
}
return false;
is_complex :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Complex);
return ok;
}
is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Boolean: return true;
}
return false;
is_any :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Any);
return ok;
}
is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Pointer: return true;
}
return false;
is_string :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.String);
return ok;
}
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Procedure: return true;
}
return false;
is_boolean :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Boolean);
return ok;
}
is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Array: return true;
}
return false;
is_pointer :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Pointer);
return ok;
}
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Dynamic_Array: return true;
}
return false;
is_procedure :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Procedure);
return ok;
}
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Map: return i.count == 0;
}
return false;
is_array :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Array);
return ok;
}
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Slice: return true;
}
return false;
is_dynamic_array :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.DynamicArray);
return ok;
}
is_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Vector: return true;
}
return false;
is_dynamic_map :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Map);
return ok;
}
is_tuple :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Tuple: return true;
}
return false;
is_slice :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Slice);
return ok;
}
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Struct: return true;
}
return false;
is_vector :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Vector);
return ok;
}
is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Union: return true;
}
return false;
is_tuple :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Tuple);
return ok;
}
is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Raw_Union: return true;
}
return false;
is_struct :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
s, ok := type_info_base(info).variant.(TypeInfo.Struct);
return ok && !s.is_raw_union;
}
is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Enum: return true;
}
return false;
is_raw_union :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
s, ok := type_info_base(info).variant.(TypeInfo.Struct);
return ok && s.is_raw_union;
}
is_union :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Union);
return ok;
}
is_enum :: proc(info: ^TypeInfo) -> bool {
if info == nil do return false;
_, ok := type_info_base(info).variant.(TypeInfo.Enum);
return ok;
}
+54
View File
@@ -0,0 +1,54 @@
REPLACEMENT_CHAR :: '\uFFFD';
MAX_RUNE :: '\U0010FFFF';
_surr1 :: 0xd800;
_surr2 :: 0xdc00;
_surr3 :: 0xe000;
_surr_self :: 0x10000;
is_surrogate :: proc(r: rune) -> bool {
return _surr1 <= r && r < _surr3;
}
decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self;
}
return REPLACEMENT_CHAR;
}
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
if r < _surr_self || r > MAX_RUNE {
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
}
r -= _surr_self;
return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff;
}
encode :: proc(d: []u16, s: []rune) {
n := len(s);
for r in s do if r >= _surr_self do n += 1;
max_n := min(len(d), n);
n = 0;
for r in s {
match r {
case 0.._surr1, _surr3.._surr_self:
d[n] = u16(r);
n += 1;
case _surr_self..MAX_RUNE:
r1, r2 := encode_surrogate_pair(r);
d[n] = u16(r1);
d[n+1] = u16(r2);
n += 2;
case:
d[n] = u16(REPLACEMENT_CHAR);
n += 1;
}
}
}
+46 -50
View File
@@ -1,7 +1,7 @@
RUNE_ERROR :: '\ufffd';
RUNE_SELF :: 0x80;
RUNE_BOM :: 0xfeff;
RUNE_EOF :: ~cast(rune)0;
RUNE_EOF :: ~rune(0);
MAX_RUNE :: '\U0010ffff';
UTF_MAX :: 4;
@@ -24,13 +24,13 @@ RUNE1_MAX :: 1<<7 - 1;
RUNE2_MAX :: 1<<11 - 1;
RUNE3_MAX :: 1<<16 - 1;
// The default lowest and highest continuation byte.
// The default lowest and highest continuation byte.
LOCB :: 0b1000_0000;
HICB :: 0b1011_1111;
Accept_Range :: struct { lo, hi: u8 }
AcceptRange :: struct { lo, hi: u8 }
immutable accept_ranges := [5]Accept_Range{
accept_ranges := [5]AcceptRange{
{0x80, 0xbf},
{0xa0, 0xbf},
{0x80, 0x9f},
@@ -38,7 +38,7 @@ immutable accept_ranges := [5]Accept_Range{
{0x80, 0x8f},
};
immutable accept_sizes := [256]byte{
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
@@ -58,17 +58,17 @@ immutable accept_sizes := [256]byte{
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
};
encode_rune :: proc(r: rune) -> ([4]byte, int) {
buf: [4]byte;
i := cast(u32)r;
mask: byte : 0x3f;
encode_rune :: proc(r: rune) -> ([4]u8, int) {
buf: [4]u8;
i := u32(r);
mask: u8 : 0x3f;
if i <= 1<<7-1 {
buf[0] = cast(byte)r;
buf[0] = u8(r);
return buf, 1;
}
if i <= 1<<11-1 {
buf[0] = 0xc0 | cast(byte)(r>>6);
buf[1] = 0x80 | cast(byte)r & mask;
buf[0] = 0xc0 | u8(r>>6);
buf[1] = 0x80 | u8(r) & mask;
return buf, 2;
}
@@ -79,34 +79,34 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) {
}
if i <= 1<<16-1 {
buf[0] = 0xe0 | cast(byte)(r>>12);
buf[1] = 0x80 | cast(byte)(r>>6) & mask;
buf[2] = 0x80 | cast(byte)r & mask;
buf[0] = 0xe0 | u8(r>>12);
buf[1] = 0x80 | u8(r>>6) & mask;
buf[2] = 0x80 | u8(r) & mask;
return buf, 3;
}
buf[0] = 0xf0 | cast(byte)(r>>18);
buf[1] = 0x80 | cast(byte)(r>>12) & mask;
buf[2] = 0x80 | cast(byte)(r>>6) & mask;
buf[3] = 0x80 | cast(byte)r & mask;
buf[0] = 0xf0 | u8(r>>18);
buf[1] = 0x80 | u8(r>>12) & mask;
buf[2] = 0x80 | u8(r>>6) & mask;
buf[3] = 0x80 | u8(r) & mask;
return buf, 4;
}
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]byte)s); }
decode_rune :: proc(s: []byte) -> (rune, int) {
n := s.count;
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]u8)s); }
decode_rune :: proc(s: []u8) -> (rune, int) {
n := len(s);
if n < 1 {
return RUNE_ERROR, 0;
}
s0 := s[0];
x := accept_sizes[s0];
if x >= 0xF0 {
mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1;
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 < cast(int)sz {
if n < int(sz) {
return RUNE_ERROR, 1;
}
b1 := s[1];
@@ -114,36 +114,36 @@ decode_rune :: proc(s: []byte) -> (rune, int) {
return RUNE_ERROR, 1;
}
if sz == 2 {
return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 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 cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 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 cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4;
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]byte)s); }
decode_last_rune :: proc(s: []byte) -> (rune, int) {
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]u8)s); }
decode_last_rune :: proc(s: []u8) -> (rune, int) {
r: rune;
size: int;
start, end, limit: int;
end = s.count;
end = len(s);
if end == 0 {
return RUNE_ERROR, 0;
}
start = end-1;
r = cast(rune)s[start];
r = rune(s[start]);
if r < RUNE_SELF {
return r, 1;
}
@@ -151,12 +151,8 @@ decode_last_rune :: proc(s: []byte) -> (rune, int) {
limit = max(end - UTF_MAX, 0);
start--;
for start >= limit {
if rune_start(s[start]) {
break;
}
start--;
for start-=1; start >= limit; start-=1 {
if rune_start(s[start]) do break;
}
start = max(start, 0);
@@ -183,18 +179,18 @@ valid_rune :: proc(r: rune) -> bool {
}
valid_string :: proc(s: string) -> bool {
n := s.count;
n := len(s);
for i := 0; i < n; {
si := s[i];
if si < RUNE_SELF { // ascii
i++;
i += 1;
continue;
}
x := accept_sizes[si];
if x == 0xf1 {
return false;
}
size := cast(int)(x & 7);
size := int(x & 7);
if i+size > n {
return false;
}
@@ -215,28 +211,28 @@ valid_string :: proc(s: string) -> bool {
return true;
}
rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; }
rune_start :: proc(b: u8) -> bool #inline { return b&0xc0 != 0x80; }
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]byte)s); }
rune_count :: proc(s: []byte) -> int {
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]u8)s); }
rune_count :: proc(s: []u8) -> int {
count := 0;
n := s.count;
n := len(s);
for i := 0; i < n; {
defer count++;
defer count += 1;
si := s[i];
if si < RUNE_SELF { // ascii
i++;
i += 1;
continue;
}
x := accept_sizes[si];
if x == 0xf1 {
i++;
i += 1;
continue;
}
size := cast(int)(x & 7);
size := int(x & 7);
if i+size > n {
i++;
i += 1;
continue;
}
ar := accept_ranges[x>>4];
+11
View File
@@ -0,0 +1,11 @@
@echo off
setlocal EnableDelayedExpansion
set file_input=%1
set name=%1
FOR %%f IN (name) do (
FOR %%g in (!%%f!) do set "%%f=%%~ng"
)
call clang -O2 -c %file_input% -o %name%.o ^
&& call ar %name%.o -rcs %name%.lib
View File

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

View File
+4 -2
View File
@@ -1,8 +1,10 @@
@echo off
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
set _NO_DEBUG_HEAP=1
set path=w:\Odin\misc;%path%
View File
-4
View File
@@ -1,4 +0,0 @@
@echo off
rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h
+102 -105
View File
@@ -1,104 +1,7 @@
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
#define Array(Type_) struct { \
gbAllocator allocator; \
Type_ * e; \
isize count; \
isize capacity; \
}
typedef Array(void) ArrayVoid;
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
void **e = cast(void **)&((x_)->e); \
GB_ASSERT((x_) != NULL); \
(x_)->allocator = (allocator_); \
(x_)->count = 0; \
(x_)->capacity = (init_capacity_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
} while (0)
#define array_init_count(x_, allocator_, init_count_) do { \
void **e = cast(void **)&((x_)->e); \
GB_ASSERT((x_) != NULL); \
(x_)->allocator = (allocator_); \
(x_)->count = (init_count_); \
(x_)->capacity = (init_count_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
} while (0)
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
#define array_grow(x_, min_capacity_) do { \
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
if (new_capacity < (min_capacity_)) { \
new_capacity = (min_capacity_); \
} \
array_set_capacity(x_, new_capacity); \
} while (0)
#define array_add(x_, item_) do { \
if ((x_)->capacity < (x_)->count+1) { \
array_grow(x_, 0); \
} \
(x_)->e[(x_)->count++] = item_; \
} while (0)
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
#define array_clear(x_) do { (x_)->count = 0; } while (0)
#define array_resize(x_, new_count_) do { \
if ((x_)->capacity < (new_count_)) { \
array_grow((x_), (new_count_)); \
} \
(x_)->count = (new_count_); \
} while (0)
#define array_reserve(x_, new_capacity_) do { \
if ((x_)->capacity < (new_capacity_)) { \
array_set_capacity((x_), (new_capacity_)); \
} \
} while (0)
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
ArrayVoid *x = cast(ArrayVoid *)ptr;
GB_ASSERT(ptr != NULL);
GB_ASSERT(element_size > 0);
if (capacity == x->capacity) {
return;
}
if (capacity < x->count) {
if (x->capacity < capacity) {
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
if (new_capacity < capacity) {
new_capacity = capacity;
}
array__set_capacity(ptr, new_capacity, element_size);
}
x->count = capacity;
}
{
// TODO(bill): Resize rather than copy and delete
void *new_data = gb_alloc(x->allocator, element_size*capacity);
gb_memmove(new_data, x->e, element_size*x->count);
gb_free(x->allocator, x->e);
x->capacity = capacity;
x->e = new_data;
}
}
#if 0
#if 1
template <typename T>
struct Array {
gbAllocator allocator;
@@ -107,12 +10,16 @@ struct Array {
isize capacity;
T &operator[](isize index) {
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
#if !defined(NO_ARRAY_BOUNDS_CHECK)
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
#endif
return data[index];
}
T const &operator[](isize index) const {
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
#if !defined(NO_ARRAY_BOUNDS_CHECK)
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
#endif
return data[index];
}
};
@@ -128,7 +35,6 @@ template <typename T> void array_reserve (Array<T> *array, isize capacit
template <typename T> void array_resize (Array<T> *array, isize count);
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
template <typename T>
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
array->allocator = a;
@@ -158,7 +64,7 @@ Array<T> array_make(T *data, isize count, isize capacity) {
template <typename T>
void array_free(Array<T> *array) {
if (array->allocator.proc != NULL) {
if (array->allocator.proc != nullptr) {
gb_free(array->allocator, array->data);
}
array->count = 0;
@@ -220,7 +126,7 @@ void array_set_capacity(Array<T> *array, isize capacity) {
array_resize(array, capacity);
}
T *new_data = NULL;
T *new_data = nullptr;
if (capacity > 0) {
new_data = gb_alloc_array(array->allocator, T, capacity);
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
@@ -230,6 +136,97 @@ void array_set_capacity(Array<T> *array, isize capacity) {
array->capacity = capacity;
}
#endif
#if 0
#define Array(Type_) struct { \
gbAllocator allocator; \
Type_ * e; \
isize count; \
isize capacity; \
}
typedef Array(void) ArrayVoid;
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
void **e = cast(void **)&((x_)->e); \
GB_ASSERT((x_) != nullptr); \
(x_)->allocator = (allocator_); \
(x_)->count = 0; \
(x_)->capacity = (init_capacity_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
} while (0)
#define array_init_count(x_, allocator_, init_count_) do { \
void **e = cast(void **)&((x_)->e); \
GB_ASSERT((x_) != nullptr); \
(x_)->allocator = (allocator_); \
(x_)->count = (init_count_); \
(x_)->capacity = (init_count_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
} while (0)
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
#define array_grow(x_, min_capacity_) do { \
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
if (new_capacity < (min_capacity_)) { \
new_capacity = (min_capacity_); \
} \
array_set_capacity(x_, new_capacity); \
} while (0)
#define array_add(x_, item_) do { \
if ((x_)->capacity < (x_)->count+1) { \
array_grow(x_, 0); \
} \
(x_)->e[(x_)->count++] = item_; \
} while (0)
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
#define array_clear(x_) do { (x_)->count = 0; } while (0)
#define array_resize(x_, new_count_) do { \
if ((x_)->capacity < (new_count_)) { \
array_grow((x_), (new_count_)); \
} \
(x_)->count = (new_count_); \
} while (0)
#define array_reserve(x_, new_capacity_) do { \
if ((x_)->capacity < (new_capacity_)) { \
array_set_capacity((x_), (new_capacity_)); \
} \
} while (0)
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
ArrayVoid *x = cast(ArrayVoid *)ptr;
GB_ASSERT(ptr != nullptr);
GB_ASSERT(element_size > 0);
if (capacity == x->capacity) {
return;
}
if (capacity < x->count) {
if (x->capacity < capacity) {
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
if (new_capacity < capacity) {
new_capacity = capacity;
}
array__set_capacity(ptr, new_capacity, element_size);
}
x->count = capacity;
}
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
x->capacity = capacity;
}
#endif
-238
View File
@@ -1,238 +0,0 @@
// This stores the information for the specify architecture of this build
typedef struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
// In bytes
i64 word_size; // Size of a pointer, must be >= 4
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
String llc_flags;
String link_flags;
bool is_dll;
} BuildContext;
gb_global BuildContext build_context = {0};
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
// is_file
// is_abs_path
// has_subdir
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
#if defined(GB_SYSTEM_WINDOWS)
String odin_root_dir(void) {
String path = global_module_path;
Array(wchar_t) path_buf;
isize len, i;
gbTempArenaMemory tmp;
wchar_t *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
if (len == 0) {
return make_string(NULL, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetModuleFileNameW(NULL, text, len);
path = string16_to_string(heap_allocator(), make_string16(text, len));
for (i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
array_free(&path_buf);
return path;
}
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
String odin_root_dir(void) {
String path = global_module_path;
Array(char) path_buf;
isize len, i;
gbTempArenaMemory tmp;
wchar_t *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
int sz = path_buf.count;
int res = _NSGetExecutablePath(&path_buf.e[0], &sz);
if(res == 0) {
len = sz;
break;
} else {
array_resize(&path_buf, sz + 1);
}
}
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf.e[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
// array_free(&path_buf);
return path;
}
#else
#error Implement system
#endif
#if defined(GB_SYSTEM_WINDOWS)
String path_to_fullpath(gbAllocator a, String s) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
String16 string16 = string_to_string16(string_buffer_allocator, s);
String result = {0};
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
if (len != 0) {
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetFullPathNameW(string16.text, len, text, NULL);
text[len] = 0;
result = string16_to_string(a, make_string16(text, len));
}
gb_temp_arena_memory_end(tmp);
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char* p = realpath(s.text, 0);
// GB_ASSERT(p && "file does not exist");
if(!p) return make_string_c("");
return make_string(p, strlen(p));
}
#else
#error Implement system
#endif
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
String res = {0};
isize str_len = base_dir.len+path.len;
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
isize i = 0;
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
gb_memmove(str+i, path.text, path.len);
str[str_len] = '\0';
res = path_to_fullpath(a, make_string(str, str_len));
gb_free(heap_allocator(), str);
return res;
}
String get_fullpath_core(gbAllocator a, String path) {
String module_dir = odin_root_dir();
String res = {0};
char core[] = "core/";
isize core_len = gb_size_of(core)-1;
isize str_len = module_dir.len + core_len + path.len;
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
gb_memmove(str, module_dir.text, module_dir.len);
gb_memmove(str+module_dir.len, core, core_len);
gb_memmove(str+module_dir.len+core_len, path.text, path.len);
str[str_len] = '\0';
res = path_to_fullpath(a, make_string(str, str_len));
gb_free(heap_allocator(), str);
return res;
}
void init_build_context(void) {
BuildContext *bc = &build_context;
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = str_lit("0.1.1");
bc->ODIN_ROOT = odin_root_dir();
#if defined(GB_SYSTEM_WINDOWS)
bc->ODIN_OS = str_lit("windows");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#elif defined(GB_SYSTEM_OSX)
bc->ODIN_OS = str_lit("osx");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#else
#error Implement system
#endif
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
bc->word_size = 8;
bc->max_align = 16;
bc->llc_flags = str_lit("-march=x86-64 ");
bc->link_flags = str_lit("/machine:x64 ");
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
bc->word_size = 4;
bc->max_align = 8;
bc->llc_flags = str_lit("-march=x86 ");
bc->link_flags = str_lit("/machine:x86 ");
}
}
+383
View File
@@ -0,0 +1,383 @@
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
// In bytes
i64 word_size; // Size of a pointer, must be >= 4
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
String opt_flags;
String llc_flags;
String link_flags;
bool is_dll;
bool generate_docs;
i32 optimization_level;
bool show_timings;
bool keep_temp_files;
gbAffinity affinity;
isize thread_count;
};
gb_global BuildContext build_context = {0};
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
// is_file
// is_abs_path
// has_subdir
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
#if defined(GB_SYSTEM_WINDOWS)
String odin_root_dir(void) {
String path = global_module_path;
Array<wchar_t> path_buf;
isize len, i;
gbTempArenaMemory tmp;
wchar_t *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count);
if (len == 0) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
len += 1; // NOTE(bill): It needs an extra 1 for some reason
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetModuleFileNameW(nullptr, text, cast(int)len);
path = string16_to_string(heap_allocator(), make_string16(text, len));
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
array_free(&path_buf);
return path;
}
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
String odin_root_dir(void) {
String path = global_module_path;
Array<char> path_buf;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
u32 sz = path_buf.count;
int res = _NSGetExecutablePath(&path_buf[0], &sz);
if(res == 0) {
len = sz;
break;
} else {
array_resize(&path_buf, sz + 1);
}
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
// array_free(&path_buf);
return path;
}
#else
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
String odin_root_dir(void) {
String path = global_module_path;
Array<char> path_buf;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
defer (array_free(&path_buf));
len = 0;
for (;;) {
// This is not a 100% reliable system, but for the purposes
// of this compiler, it should be _good enough_.
// That said, there's no solid 100% method on Linux to get the program's
// path without checking this link. Sorry.
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
if(len == 0) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
defer (gb_temp_arena_memory_end(tmp));
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
return path;
}
#endif
#if defined(GB_SYSTEM_WINDOWS)
String path_to_fullpath(gbAllocator a, String s) {
String result = {};
gb_mutex_lock(&string_buffer_mutex);
defer (gb_mutex_unlock(&string_buffer_mutex));
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
String16 string16 = string_to_string16(string_buffer_allocator, s);
DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
if (len != 0) {
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetFullPathNameW(&string16[0], len, text, nullptr);
text[len] = 0;
result = string16_to_string(a, make_string16(text, len));
}
gb_temp_arena_memory_end(tmp);
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char *p;
gb_mutex_lock(&string_buffer_mutex);
p = realpath(cast(char *)s.text, 0);
gb_mutex_unlock(&string_buffer_mutex);
if(p == nullptr) return make_string_c("");
return make_string_c(p);
}
#else
#error Implement system
#endif
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
String res = {0};
isize str_len = base_dir.len+path.len;
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
isize i = 0;
gb_memmove(str+i, &base_dir[0], base_dir.len); i += base_dir.len;
gb_memmove(str+i, &path[0], path.len);
str[str_len] = '\0';
res = path_to_fullpath(a, make_string(str, str_len));
gb_free(heap_allocator(), str);
return res;
}
String get_fullpath_core(gbAllocator a, String path) {
String module_dir = odin_root_dir();
String res = {0};
char core[] = "core/";
isize core_len = gb_size_of(core)-1;
isize str_len = module_dir.len + core_len + path.len;
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
gb_memmove(str, &module_dir[0], module_dir.len);
gb_memmove(str+module_dir.len, core, core_len);
gb_memmove(str+module_dir.len+core_len, &path[0], path.len);
str[str_len] = '\0';
res = path_to_fullpath(a, make_string(str, str_len));
gb_free(heap_allocator(), str);
return res;
}
String const ODIN_VERSION = str_lit("0.6.1a");
void init_build_context(void) {
BuildContext *bc = &build_context;
gb_affinity_init(&bc->affinity);
if (bc->thread_count == 0) {
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
}
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
#if defined(GB_SYSTEM_WINDOWS)
bc->ODIN_OS = str_lit("windows");
#elif defined(GB_SYSTEM_OSX)
bc->ODIN_OS = str_lit("osx");
#else
bc->ODIN_OS = str_lit("linux");
#endif
#if defined(GB_ARCH_64_BIT)
bc->ODIN_ARCH = str_lit("amd64");
#else
bc->ODIN_ARCH = str_lit("x86");
#endif
{
u16 x = 1;
bool big = !(*cast(u8 *)&x);
bc->ODIN_ENDIAN = big ? str_lit("big") : str_lit("little");
}
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
// here, so I just #defined the linker flags to keep things concise.
#if defined(GB_SYSTEM_WINDOWS)
#define LINK_FLAG_X64 "/machine:x64"
#define LINK_FLAG_X86 "/machine:x86"
#elif defined(GB_SYSTEM_OSX)
// NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
// an architecture option. All compilation done on MacOS must be x64.
GB_ASSERT(bc->ODIN_ARCH == "amd64");
#define LINK_FLAG_X64 ""
#define LINK_FLAG_X86 ""
#else
// Linux, but also BSDs and the like.
// NOTE(zangent): When clang is swapped out with ld as the linker,
// the commented flags here should be used. Until then, we'll have
// to use alternative build flags made for clang.
/*
#define LINK_FLAG_X64 "-m elf_x86_64"
#define LINK_FLAG_X86 "-m elf_i386"
*/
#define LINK_FLAG_X64 "-arch x86-64"
#define LINK_FLAG_X86 "-arch x86"
#endif
if (bc->ODIN_ARCH == "amd64") {
bc->word_size = 8;
bc->max_align = 16;
bc->llc_flags = str_lit("-march=x86-64 ");
bc->link_flags = str_lit(LINK_FLAG_X64 " ");
} else if (bc->ODIN_ARCH == "x86") {
bc->word_size = 4;
bc->max_align = 8;
bc->llc_flags = str_lit("-march=x86 ");
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
} else {
gb_printf_err("This current architecture is not supported");
gb_exit(1);
}
isize opt_max = 1023;
char *opt_flags_string = gb_alloc_array(heap_allocator(), char, opt_max+1);
isize opt_len = 0;
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
if (bc->optimization_level != 0) {
opt_len = gb_snprintf(opt_flags_string, opt_max, "-O%d", bc->optimization_level);
} else {
opt_len = gb_snprintf(opt_flags_string, opt_max, "");
}
if (opt_len > 0) {
opt_len--;
}
bc->opt_flags = make_string(cast(u8 *)opt_flags_string, opt_len);
#undef LINK_FLAG_X64
#undef LINK_FLAG_X86
}
-527
View File
@@ -1,527 +0,0 @@
bool check_is_terminating(AstNode *node);
void check_stmt (Checker *c, AstNode *node, u32 flags);
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
// NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message?
error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
LIT(context_name));
operand->mode = Addressing_Invalid;
gb_string_free(expr_str);
}
if (e->type == NULL) {
e->type = t_invalid;
}
return NULL;
}
if (e->type == NULL) {
// NOTE(bill): Use the type of the operand
Type *t = operand->type;
if (is_type_untyped(t)) {
if (t == t_invalid || is_type_untyped_nil(t)) {
error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
e->type = t_invalid;
return NULL;
}
t = default_type(t);
}
GB_ASSERT(is_type_typed(t));
e->type = t;
}
check_assignment(c, operand, e->type, context_name);
if (operand->mode == Addressing_Invalid) {
return NULL;
}
return e->type;
}
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
if ((lhs == NULL || lhs_count == 0) && inits.count == 0) {
return;
}
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
ArrayOperand operands = {0};
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
check_unpack_arguments(c, lhs_count, &operands, inits, true);
isize rhs_count = operands.count;
for_array(i, operands) {
if (operands.e[i].mode == Addressing_Invalid) {
rhs_count--;
}
}
isize max = gb_min(lhs_count, rhs_count);
for (isize i = 0; i < max; i++) {
check_init_variable(c, lhs[i], &operands.e[i], context_name);
}
if (rhs_count > 0 && lhs_count != rhs_count) {
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
}
gb_temp_arena_memory_end(tmp);
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (e->type == NULL) {
e->type = t_invalid;
}
return;
}
if (operand->mode != Addressing_Constant) {
// TODO(bill): better error
gbString str = expr_to_string(operand->expr);
error_node(operand->expr, "`%s` is not a constant", str);
gb_string_free(str);
if (e->type == NULL) {
e->type = t_invalid;
}
return;
}
if (!is_type_constant_type(operand->type)) {
gbString type_str = type_to_string(operand->type);
error_node(operand->expr, "Invalid constant type: `%s`", type_str);
gb_string_free(type_str);
if (e->type == NULL) {
e->type = t_invalid;
}
return;
}
if (e->type == NULL) { // NOTE(bill): type inference
e->type = operand->type;
}
check_assignment(c, operand, e->type, str_lit("constant declaration"));
if (operand->mode == Addressing_Invalid) {
return;
}
e->Constant.value = operand->value;
}
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
GB_ASSERT(e->type == NULL);
Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
named->Named.type_name = e;
if (def != NULL && def->kind == Type_Named) {
def->Named.base = named;
}
e->type = named;
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
Type *bt = check_type_extra(c, type_expr, named);
named->Named.base = base_type(bt);
if (named->Named.base == t_invalid) {
// gb_printf("check_type_decl: %s\n", type_to_string(named));
}
}
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Constant);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
if (type_expr) {
Type *t = check_type(c, type_expr);
if (!is_type_constant_type(t)) {
gbString str = type_to_string(t);
error_node(type_expr, "Invalid constant type `%s`", str);
gb_string_free(str);
e->type = t_invalid;
return;
}
e->type = t;
}
Operand operand = {0};
if (init != NULL) {
check_expr_or_type(c, &operand, init);
}
if (operand.mode == Addressing_Type) {
e->kind = Entity_TypeName;
DeclInfo *d = c->context.decl;
d->type_expr = d->init_expr;
check_type_decl(c, e, d->type_expr, named_type);
return;
}
check_init_constant(c, e, &operand);
if (operand.mode == Addressing_Invalid ||
base_type(operand.type) == t_invalid) {
error(e->token, "Invalid declaration type");
}
}
bool are_signatures_similar_enough(Type *a_, Type *b_) {
GB_ASSERT(a_->kind == Type_Proc);
GB_ASSERT(b_->kind == Type_Proc);
TypeProc *a = &a_->Proc;
TypeProc *b = &b_->Proc;
if (a->param_count != b->param_count) {
return false;
}
if (a->result_count != b->result_count) {
return false;
}
for (isize i = 0; i < a->param_count; i++) {
Type *x = base_type(a->params->Tuple.variables[i]->type);
Type *y = base_type(b->params->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (!are_types_identical(x, y)) {
return false;
}
}
for (isize i = 0; i < a->result_count; i++) {
Type *x = base_type(a->results->Tuple.variables[i]->type);
Type *y = base_type(b->results->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (!are_types_identical(x, y)) {
return false;
}
}
return true;
}
void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == NULL);
if (d->proc_lit->kind != AstNode_ProcLit) {
// TOOD(bill): Better error message
error_node(d->proc_lit, "Expected a procedure to check");
return;
}
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
e->type = proc_type;
ast_node(pd, ProcLit, d->proc_lit);
check_open_scope(c, pd->type);
check_procedure_type(c, proc_type, pd->type);
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
bool is_export = (pd->tags & ProcTag_export) != 0;
bool is_inline = (pd->tags & ProcTag_inline) != 0;
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
if ((d->scope->is_file || d->scope->is_global) &&
str_eq(e->token.string, str_lit("main"))) {
if (proc_type != NULL) {
TypeProc *pt = &proc_type->Proc;
if (pt->param_count != 0 ||
pt->result_count != 0) {
gbString str = type_to_string(proc_type);
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
gb_string_free(str);
}
}
}
if (is_inline && is_no_inline) {
error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
}
if (is_foreign && is_link_name) {
error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
} else if (is_foreign && is_export) {
error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
}
if (pd->body != NULL) {
if (is_foreign) {
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
}
// TODO(bill): Is this the best option? What about passing to external shit?!
// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
// error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
// proc_type->Proc.calling_convention = ProcCC_Odin;
// }
d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
}
if (is_foreign) {
MapEntity *fp = &c->info.foreigns;
String name = e->token.string;
if (pd->foreign_name.len > 0) {
name = pd->foreign_name;
}
AstNode *foreign_library = d->proc_lit->ProcLit.foreign_library;
if (foreign_library == NULL) {
error(e->token, "#foreign procedures must declare which library they are from");
} else if (foreign_library->kind != AstNode_Ident) {
error_node(foreign_library, "#foreign library names must be an identifier");
} else {
String name = foreign_library->Ident.string;
Entity *found = scope_lookup_entity(c->context.scope, name);
if (found == NULL) {
if (str_eq(name, str_lit("_"))) {
error_node(foreign_library, "`_` cannot be used as a value type");
} else {
error_node(foreign_library, "Undeclared name: %.*s", LIT(name));
}
} else if (found->kind != Entity_LibraryName) {
error_node(foreign_library, "`_` cannot be used as a library name");
} else {
// TODO(bill): Extra stuff to do with library names?
e->Procedure.foreign_library = found;
add_entity_use(c, foreign_library, found);
}
}
e->Procedure.is_foreign = true;
e->Procedure.foreign_name = name;
HashKey key = hash_string(name);
Entity **found = map_entity_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) {
error_node(d->proc_lit,
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_entity_set(fp, key, e);
}
} else {
String name = e->token.string;
if (is_link_name) {
name = pd->link_name;
}
if (is_link_name || is_export) {
MapEntity *fp = &c->info.foreigns;
e->Procedure.link_name = name;
HashKey key = hash_string(name);
Entity **found = map_entity_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
// TODO(bill): Better error message?
error_node(d->proc_lit,
"Non unique linking name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
} else {
map_entity_set(fp, key, e);
}
}
}
check_close_scope(c);
}
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Variable);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
if (type_expr != NULL) {
e->type = check_type_extra(c, type_expr, NULL);
}
if (init_expr == NULL) {
if (type_expr == NULL) {
e->type = t_invalid;
}
return;
}
if (entities == NULL || entity_count == 1) {
GB_ASSERT(entities == NULL || entities[0] == e);
Operand operand = {0};
check_expr(c, &operand, init_expr);
check_init_variable(c, e, &operand, str_lit("variable declaration"));
}
if (type_expr != NULL) {
for (isize i = 0; i < entity_count; i++) {
entities[i]->type = e->type;
}
}
AstNodeArray inits;
array_init_reserve(&inits, c->allocator, 1);
array_add(&inits, init_expr);
check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
}
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
if (e->type != NULL) {
return;
}
if (d == NULL) {
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
if (found) {
d = *found;
} else {
// TODO(bill): Err here?
e->type = t_invalid;
set_base_type(named_type, t_invalid);
return;
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
}
}
CheckerContext prev = c->context;
c->context.scope = d->scope;
c->context.decl = d;
switch (e->kind) {
case Entity_Variable:
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
break;
case Entity_Constant:
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
break;
case Entity_TypeName:
check_type_decl(c, e, d->type_expr, named_type);
break;
case Entity_Procedure:
check_proc_lit(c, e, d);
break;
}
c->context = prev;
}
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
GB_ASSERT(body->kind == AstNode_BlockStmt);
String proc_name = {0};
if (token.kind == Token_Ident) {
proc_name = token.string;
} else {
// TODO(bill): Better name
proc_name = str_lit("(anonymous-procedure)");
}
CheckerContext old_context = c->context;
c->context.scope = decl->scope;
c->context.decl = decl;
c->context.proc_name = proc_name;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e->kind == Entity_Variable);
if (!(e->flags & EntityFlag_Anonymous)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
break;
}
}
}
} else {
error(e->token, "`using` can only be applied to variables of type struct or raw_union");
break;
}
}
}
push_procedure(c, type);
{
ast_node(bs, BlockStmt, body);
check_stmt_list(c, bs->stmts, 0);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) {
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
} else {
error(bs->close, "Missing return statement at the end of the procedure");
}
}
}
}
pop_procedure(c);
check_scope_usage(c, c->context.scope);
c->context = old_context;
}
+768
View File
@@ -0,0 +1,768 @@
bool check_is_terminating(AstNode *node);
void check_stmt (Checker *c, AstNode *node, u32 flags);
// NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message?
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error(operand->expr,
"Cannot assign built-in procedure `%s` in %.*s",
expr_str,
LIT(context_name));
operand->mode = Addressing_Invalid;
gb_string_free(expr_str);
}
if (operand->mode == Addressing_Overload) {
if (e->type == nullptr) {
error(operand->expr, "Cannot determine type from overloaded procedure `%.*s`", LIT(operand->overload_entities[0]->token.string));
} else {
check_assignment(c, operand, e->type, str_lit("variable assignment"));
if (operand->mode != Addressing_Type) {
return operand->type;
}
}
}
if (e->type == nullptr) {
e->type = t_invalid;
}
return nullptr;
}
if (e->type == nullptr) {
// NOTE(bill): Use the type of the operand
Type *t = operand->type;
if (is_type_untyped(t)) {
if (t == t_invalid || is_type_untyped_nil(t)) {
error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name));
e->type = t_invalid;
return nullptr;
}
if (t == t_invalid || is_type_untyped_undef(t)) {
error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
e->type = t_invalid;
return nullptr;
}
t = default_type(t);
}
if (is_type_polymorphic(t)) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
e->type = t_invalid;
return nullptr;
} else if (is_type_empty_union(t)) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
e->type = t_invalid;
return nullptr;
}
if (is_type_bit_field_value(t)) {
t = default_bit_field_value_type(t);
}
GB_ASSERT(is_type_typed(t));
e->type = t;
}
e->parent_proc_decl = c->context.curr_proc_decl;
check_assignment(c, operand, e->type, context_name);
if (operand->mode == Addressing_Invalid) {
return nullptr;
}
return e->type;
}
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNode *> inits, String context_name) {
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
return;
}
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
defer (gb_temp_arena_memory_end(tmp));
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
Array<Operand> operands = {};
array_init(&operands, c->tmp_allocator, 2*lhs_count);
check_unpack_arguments(c, lhs_count, &operands, inits, true);
isize rhs_count = operands.count;
for_array(i, operands) {
if (operands[i].mode == Addressing_Invalid) {
rhs_count--;
}
}
isize max = gb_min(lhs_count, rhs_count);
for (isize i = 0; i < max; i++) {
check_init_variable(c, lhs[i], &operands[i], context_name);
}
if (rhs_count > 0 && lhs_count != rhs_count) {
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
}
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (e->type == nullptr) {
e->type = t_invalid;
}
return;
}
if (operand->mode != Addressing_Constant) {
// TODO(bill): better error
gbString str = expr_to_string(operand->expr);
error(operand->expr, "`%s` is not a constant", str);
gb_string_free(str);
if (e->type == nullptr) {
e->type = t_invalid;
}
return;
}
if (!is_type_constant_type(operand->type)) {
gbString type_str = type_to_string(operand->type);
error(operand->expr, "Invalid constant type: `%s`", type_str);
gb_string_free(type_str);
if (e->type == nullptr) {
e->type = t_invalid;
}
return;
}
if (e->type == nullptr) { // NOTE(bill): type inference
e->type = operand->type;
}
check_assignment(c, operand, e->type, str_lit("constant declaration"));
if (operand->mode == Addressing_Invalid) {
return;
}
e->parent_proc_decl = c->context.curr_proc_decl;
e->Constant.value = operand->value;
}
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
String name = e->token.string;
Type *named = make_type_named(c->allocator, name, nullptr, e);
named->Named.type_name = e;
if (def != nullptr && def->kind == Type_Named) {
def->Named.base = named;
}
e->type = named;
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
Type *bt = check_type(c, type_expr, named);
named->Named.base = base_type(bt);
if (named->Named.base == t_invalid) {
// gb_printf("check_type_decl: %s\n", type_to_string(named));
}
}
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Constant);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
if (type_expr) {
Type *t = check_type(c, type_expr);
if (!is_type_constant_type(t)) {
gbString str = type_to_string(t);
error(type_expr, "Invalid constant type `%s`", str);
gb_string_free(str);
e->type = t_invalid;
return;
}
e->type = t;
}
Operand operand = {};
if (init != nullptr) {
Entity *entity = nullptr;
if (init->kind == AstNode_Ident) {
entity = check_ident(c, &operand, init, nullptr, e->type, true);
} else if (init->kind == AstNode_SelectorExpr) {
entity = check_selector(c, &operand, init, e->type);
} else {
check_expr_or_type(c, &operand, init, e->type);
}
switch (operand.mode) {
case Addressing_Type: {
e->kind = Entity_TypeName;
DeclInfo *d = c->context.decl;
if (d->type_expr != nullptr) {
error(e->token, "A type declaration cannot have an type parameter");
}
d->type_expr = d->init_expr;
check_type_decl(c, e, d->type_expr, named_type);
return;
} break;
// NOTE(bill): Check to see if the expression it to be aliases
#if 1
case Addressing_Builtin:
if (e->type != nullptr) {
error(type_expr, "A constant alias of a built-in procedure may not have a type initializer");
}
e->kind = Entity_Builtin;
e->Builtin.id = operand.builtin_id;
e->type = t_invalid;
return;
case Addressing_Overload:
e->kind = Entity_Alias;
e->Alias.base = operand.overload_entities[0];
e->type = t_invalid;
return;
#endif
}
#if 1
if (entity != nullptr) {
switch (entity->kind) {
case Entity_Alias:
e->kind = Entity_Alias;
e->type = entity->type;
e->Alias.base = entity->Alias.base;
return;
case Entity_Procedure:
e->kind = Entity_Alias;
e->type = entity->type;
e->Alias.base = entity;
return;
case Entity_ImportName:
e->kind = Entity_ImportName;
e->type = entity->type;
e->ImportName.path = entity->ImportName.path;
e->ImportName.name = entity->ImportName.path;
e->ImportName.scope = entity->ImportName.scope;
e->ImportName.used = false;
return;
case Entity_LibraryName:
e->kind = Entity_LibraryName;
e->type = entity->type;
e->LibraryName.path = entity->LibraryName.path;
e->LibraryName.name = entity->LibraryName.path;
e->LibraryName.used = false;
return;
}
}
#endif
}
if (init != nullptr) {
check_expr_or_type(c, &operand, init, e->type);
}
check_init_constant(c, e, &operand);
if (operand.mode == Addressing_Invalid ||
base_type(operand.type) == t_invalid) {
gbString str = expr_to_string(init);
error(e->token, "Invalid declaration type `%s`", str);
gb_string_free(str);
}
}
bool are_signatures_similar_enough(Type *a_, Type *b_) {
GB_ASSERT(a_->kind == Type_Proc);
GB_ASSERT(b_->kind == Type_Proc);
TypeProc *a = &a_->Proc;
TypeProc *b = &b_->Proc;
if (a->param_count != b->param_count) {
return false;
}
if (a->result_count != b->result_count) {
return false;
}
for (isize i = 0; i < a->param_count; i++) {
Type *x = core_type(a->params->Tuple.variables[i]->type);
Type *y = core_type(b->params->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (is_type_integer(x) && is_type_integer(y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(heap_allocator(), x);
i64 sy = type_size_of(heap_allocator(), y);
if (sx == sy) continue;
}
if (!are_types_identical(x, y)) return false;
}
for (isize i = 0; i < a->result_count; i++) {
Type *x = base_type(a->results->Tuple.variables[i]->type);
Type *y = base_type(b->results->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (is_type_integer(x) && is_type_integer(y)) {
GB_ASSERT(x->kind == Type_Basic);
GB_ASSERT(y->kind == Type_Basic);
i64 sx = type_size_of(heap_allocator(), x);
i64 sy = type_size_of(heap_allocator(), y);
if (sx == sy) continue;
}
if (!are_types_identical(x, y)) {
return false;
}
}
return true;
}
void init_entity_foreign_library(Checker *c, Entity *e) {
AstNode *ident = nullptr;
Entity **foreign_library = nullptr;
switch (e->kind) {
case Entity_Procedure:
ident = e->Procedure.foreign_library_ident;
foreign_library = &e->Procedure.foreign_library;
break;
case Entity_Variable:
ident = e->Variable.foreign_library_ident;
foreign_library = &e->Variable.foreign_library;
break;
default:
return;
}
if (ident == nullptr) {
error(e->token, "foreign entiies must declare which library they are from");
} else if (ident->kind != AstNode_Ident) {
error(ident, "foreign library names must be an identifier");
} else {
String name = ident->Ident.token.string;
Entity *found = scope_lookup_entity(c->context.scope, name);
if (found == nullptr) {
if (is_blank_ident(name)) {
error(ident, "`_` cannot be used as a value type");
} else {
error(ident, "Undeclared name: %.*s", LIT(name));
}
} else if (found->kind != Entity_LibraryName) {
error(ident, "`%.*s` cannot be used as a library name", LIT(name));
} else {
// TODO(bill): Extra stuff to do with library names?
*foreign_library = found;
add_entity_use(c, ident, found);
}
}
}
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == nullptr);
if (d->proc_lit->kind != AstNode_ProcLit) {
// TOOD(bill): Better error message
error(d->proc_lit, "Expected a procedure to check");
return;
}
Type *proc_type = e->type;
if (d->gen_proc_type != nullptr) {
proc_type = d->gen_proc_type;
} else {
proc_type = make_type_proc(c->allocator, e->scope, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
}
e->type = proc_type;
ast_node(pl, ProcLit, d->proc_lit);
check_open_scope(c, pl->type);
defer (check_close_scope(c));
auto prev_context = c->context;
c->context.allow_polymorphic_types = true;
check_procedure_type(c, proc_type, pl->type);
c->context = prev_context;
TypeProc *pt = &proc_type->Proc;
bool is_foreign = (pl->tags & ProcTag_foreign) != 0;
bool is_link_name = (pl->tags & ProcTag_link_name) != 0;
bool is_export = (pl->tags & ProcTag_export) != 0;
bool is_inline = (pl->tags & ProcTag_inline) != 0;
bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0;
bool is_require_results = (pl->tags & ProcTag_require_results) != 0;
if (d->scope->is_file && e->token.string == "main") {
if (pt->param_count != 0 ||
pt->result_count != 0) {
gbString str = type_to_string(proc_type);
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
gb_string_free(str);
}
if (pt->calling_convention != ProcCC_Odin &&
pt->calling_convention != ProcCC_Contextless) {
error(e->token, "Procedure `main` cannot have a custom calling convention");
}
pt->calling_convention = ProcCC_Contextless;
}
if (is_inline && is_no_inline) {
error(pl->type, "You cannot apply both `inline` and `no_inline` to a procedure");
}
if (is_foreign && is_export) {
error(pl->type, "A foreign procedure cannot have an `export` tag");
}
if (pt->is_polymorphic) {
if (pl->body == nullptr) {
error(e->token, "Polymorphic procedures must have a body");
}
if (is_foreign) {
error(e->token, "A foreign procedure cannot be a polymorphic");
return;
}
}
if (pl->body != nullptr) {
if (is_foreign) {
error(pl->body, "A foreign procedure cannot have a body");
}
if (proc_type->Proc.c_vararg) {
error(pl->body, "A procedure with a `#c_vararg` field cannot have a body and must be foreign");
}
d->scope = c->context.scope;
GB_ASSERT(pl->body->kind == AstNode_BlockStmt);
if (!pt->is_polymorphic) {
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags);
}
} else if (!is_foreign) {
error(e->token, "Only a foreign procedure cannot have a body");
}
if (pt->result_count == 0 && is_require_results) {
error(pl->type, "`#require_results` is not needed on a procedure with no results");
} else {
pt->require_results = is_require_results;
}
if (is_foreign) {
String name = e->token.string;
if (pl->link_name.len > 0) {
name = pl->link_name;
}
e->Procedure.is_foreign = true;
e->Procedure.link_name = name;
init_entity_foreign_library(c, e);
auto *fp = &c->info.foreigns;
HashKey key = hash_string(name);
Entity **found = map_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
if (is_type_proc(this_type) && is_type_proc(other_type)) {
if (!are_signatures_similar_enough(this_type, other_type)) {
error(d->proc_lit,
"Redeclaration of foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else if (!are_types_identical(this_type, other_type)) {
error(d->proc_lit,
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_set(fp, key, e);
}
} else {
String name = e->token.string;
if (is_link_name) {
name = pl->link_name;
}
if (is_link_name || is_export) {
auto *fp = &c->info.foreigns;
e->Procedure.link_name = name;
HashKey key = hash_string(name);
Entity **found = map_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
// TODO(bill): Better error message?
error(d->proc_lit,
"Non unique linking name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
} else {
map_set(fp, key, e);
}
}
}
}
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Variable);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
String context_name = str_lit("variable declaration");
if (type_expr != nullptr) {
e->type = check_type(c, type_expr);
}
if (e->type != nullptr) {
if (is_type_polymorphic(base_type(e->type))) {
gbString str = type_to_string(e->type);
defer (gb_string_free(str));
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
e->type = t_invalid;
} else if (is_type_empty_union(e->type)) {
gbString str = type_to_string(e->type);
defer (gb_string_free(str));
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
e->type = t_invalid;
}
}
if (e->Variable.is_foreign) {
if (init_expr != nullptr) {
error(e->token, "A foreign variable declaration cannot have a default value");
}
init_entity_foreign_library(c, e);
String name = e->token.string;
auto *fp = &c->info.foreigns;
HashKey key = hash_string(name);
Entity **found = map_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
if (!are_types_identical(this_type, other_type)) {
error(e->token,
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_set(fp, key, e);
}
}
if (init_expr == nullptr) {
if (type_expr == nullptr) {
e->type = t_invalid;
}
return;
}
if (entities == nullptr || entity_count == 1) {
GB_ASSERT(entities == nullptr || entities[0] == e);
Operand operand = {};
check_expr(c, &operand, init_expr);
check_init_variable(c, e, &operand, context_name);
}
if (type_expr != nullptr) {
for (isize i = 0; i < entity_count; i++) {
entities[i]->type = e->type;
}
}
Array<AstNode *> inits;
array_init(&inits, c->allocator, 1);
array_add(&inits, init_expr);
check_init_variables(c, entities, entity_count, inits, context_name);
}
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
if (e->type != nullptr) {
return;
}
if (d == nullptr) {
d = decl_info_of_entity(&c->info, e);
if (d == nullptr) {
// TODO(bill): Err here?
e->type = t_invalid;
set_base_type(named_type, t_invalid);
return;
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
}
}
CheckerContext prev = c->context;
c->context.scope = d->scope;
c->context.decl = d;
e->parent_proc_decl = c->context.curr_proc_decl;
switch (e->kind) {
case Entity_Variable:
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
break;
case Entity_Constant:
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
break;
case Entity_TypeName:
check_type_decl(c, e, d->type_expr, named_type);
break;
case Entity_Procedure:
check_proc_decl(c, e, d);
break;
}
c->context = prev;
}
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
if (body == nullptr) {
return;
}
GB_ASSERT(body->kind == AstNode_BlockStmt);
String proc_name = {};
if (token.kind == Token_Ident) {
proc_name = token.string;
} else {
// TODO(bill): Better name
proc_name = str_lit("(anonymous-procedure)");
}
CheckerContext old_context = c->context;
defer (c->context = old_context);
c->context.scope = decl->scope;
c->context.decl = decl;
c->context.proc_name = proc_name;
c->context.curr_proc_decl = decl;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for_array(i, params->variables) {
Entity *e = params->variables[i];
if (e->kind != Entity_Variable) {
continue;
}
if (!(e->flags & EntityFlag_Using)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
if (scope == nullptr) {
scope = scope_of_node(&c->info, t->Struct.node);
}
GB_ASSERT(scope != nullptr);
for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != nullptr) {
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
break;
}
}
}
} else {
error(e->token, "`using` can only be applied to variables of type struct");
break;
}
}
}
push_procedure(c, type);
{
ast_node(bs, BlockStmt, body);
check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) {
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
} else {
error(bs->close, "Missing return statement at the end of the procedure");
}
}
}
}
pop_procedure(c);
check_scope_usage(c, c->context.scope);
if (decl->parent != nullptr) {
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
for_array(i, decl->deps.entries) {
HashKey key = decl->deps.entries[i].key;
Entity *e = cast(Entity *)key.ptr;
map_set(&decl->parent->deps, key, true);
}
}
}
-5638
View File
File diff suppressed because it is too large Load Diff
+8618
View File
File diff suppressed because it is too large Load Diff
-1447
View File
File diff suppressed because it is too large Load Diff
+1833
View File
File diff suppressed because it is too large Load Diff
-1943
View File
File diff suppressed because it is too large Load Diff
+2434
View File
File diff suppressed because it is too large Load Diff
-151
View File
@@ -1,151 +0,0 @@
#define GB_NO_DEFER
#define GB_IMPLEMENTATION
#include "gb/gb.h"
gbAllocator heap_allocator(void) {
return gb_heap_allocator();
}
#include "unicode.c"
#include "string.c"
#include "array.c"
gb_global String global_module_path = {0};
gb_global bool global_module_path_set = false;
gb_global gbScratchMemory scratch_memory = {0};
void init_scratch_memory(isize size) {
void *memory = gb_alloc(heap_allocator(), size);
gb_scratch_memory_init(&scratch_memory, memory, size);
}
gbAllocator scratch_allocator(void) {
return gb_scratch_allocator(&scratch_memory);
}
i64 next_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
n++;
return n;
}
i64 prev_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return n - (n >> 1);
}
i16 f32_to_f16(f32 value) {
union { u32 i; f32 f; } v;
i32 i, s, e, m;
v.f = value;
i = (i32)v.i;
s = (i >> 16) & 0x00008000;
e = ((i >> 23) & 0x000000ff) - (127 - 15);
m = i & 0x007fffff;
if (e <= 0) {
if (e < -10) return cast(i16)s;
m = (m | 0x00800000) >> (1 - e);
if (m & 0x00001000)
m += 0x00002000;
return cast(i16)(s | (m >> 13));
} else if (e == 0xff - (127 - 15)) {
if (m == 0) {
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
} else {
/* NOTE(bill): NAN */
m >>= 13;
return cast(i16)(s | 0x7c00 | m | (m == 0));
}
} else {
if (m & 0x00001000) {
m += 0x00002000;
if (m & 0x00800000) {
m = 0;
e += 1;
}
}
if (e > 30) {
float volatile f = 1e12f;
int j;
for (j = 0; j < 10; j++) {
f *= f; /* NOTE(bill): Cause overflow */
}
return cast(i16)(s | 0x7c00);
}
return cast(i16)(s | (e << 10) | (m >> 13));
}
}
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
// Doubly Linked Lists
#define DLIST_SET(curr_element, next_element) do { \
(curr_element)->next = (next_element); \
(curr_element)->next->prev = (curr_element); \
(curr_element) = (curr_element)->next; \
} while (0)
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
if ((root_element) == NULL) { \
(root_element) = (curr_element) = (next_element); \
} else { \
DLIST_SET(curr_element, next_element); \
} \
} while (0)
////////////////////////////////////////////////////////////////
//
// Generic Data Structures
//
////////////////////////////////////////////////////////////////
typedef Array(i32) Array_i32;
typedef Array(isize) Array_isize;
#define MAP_TYPE String
#define MAP_PROC map_string_
#define MAP_NAME MapString
#include "map.c"
#define MAP_TYPE bool
#define MAP_PROC map_bool_
#define MAP_NAME MapBool
#include "map.c"
#define MAP_TYPE isize
#define MAP_PROC map_isize_
#define MAP_NAME MapIsize
#include "map.c"
+463
View File
@@ -0,0 +1,463 @@
#if defined(GB_SYSTEM_UNIX)
// Required for intrinsics on GCC
#include <xmmintrin.h>
#endif
#define GB_IMPLEMENTATION
#include "gb/gb.h"
#include <wchar.h>
#include <stdio.h>
#include <math.h>
GB_ALLOCATOR_PROC(heap_allocator_proc);
gbAllocator heap_allocator(void) {
gbAllocator a;
a.proc = heap_allocator_proc;
a.data = NULL;
return a;
}
GB_ALLOCATOR_PROC(heap_allocator_proc) {
void *ptr = NULL;
gb_unused(allocator_data);
gb_unused(old_size);
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
case gbAllocation_Alloc:
ptr = _aligned_malloc(size, alignment);
if (flags & gbAllocatorFlag_ClearToZero)
gb_zero_size(ptr, size);
break;
case gbAllocation_Free:
_aligned_free(old_memory);
break;
case gbAllocation_Resize:
ptr = _aligned_realloc(old_memory, size, alignment);
break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
ptr = aligned_alloc(alignment, size);
// ptr = malloc(size+alignment);
if (flags & gbAllocatorFlag_ClearToZero) {
gb_zero_size(ptr, size);
}
} break;
case gbAllocation_Free: {
free(old_memory);
} break;
case gbAllocation_Resize: {
// ptr = realloc(old_memory, size);
ptr = gb_default_resize_align(heap_allocator(), old_memory, old_size, size, alignment);
} break;
#else
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
posix_memalign(&ptr, alignment, size);
if (flags & gbAllocatorFlag_ClearToZero) {
gb_zero_size(ptr, size);
}
} break;
case gbAllocation_Free: {
free(old_memory);
} break;
case gbAllocation_Resize: {
ptr = gb_default_resize_align(heap_allocator(), old_memory, old_size, size, alignment);
} break;
#endif
case gbAllocation_FreeAll:
break;
}
return ptr;
}
#include "unicode.cpp"
#include "string.cpp"
#include "array.cpp"
#include "integer128.cpp"
#include "murmurhash3.cpp"
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
u128 fnv128a(void const *data, isize len) {
u128 o = u128_lo_hi(0x13bull, 0x1000000ull);
u128 h = u128_lo_hi(0x62b821756295c58dull, 0x6c62272e07bb0142ull);
u8 const *bytes = cast(u8 const *)data;
for (isize i = 0; i < len; i++) {
h.lo ^= bytes[i];
h = h * o;
}
return h;
}
#include "map.cpp"
gb_global String global_module_path = {0};
gb_global bool global_module_path_set = false;
gb_global gbScratchMemory scratch_memory = {0};
void init_scratch_memory(isize size) {
void *memory = gb_alloc(heap_allocator(), size);
gb_scratch_memory_init(&scratch_memory, memory, size);
}
gbAllocator scratch_allocator(void) {
return gb_scratch_allocator(&scratch_memory);
}
struct Pool {
isize memblock_size;
isize out_of_band_size;
isize alignment;
Array<u8 *> unused_memblock;
Array<u8 *> used_memblock;
Array<u8 *> out_of_band_allocations;
u8 * current_memblock;
u8 * current_pos;
isize bytes_left;
gbAllocator block_allocator;
};
enum {
POOL_BUCKET_SIZE_DEFAULT = 65536,
POOL_OUT_OF_BAND_SIZE_DEFAULT = 6554,
};
void pool_init(Pool *pool,
isize memblock_size = POOL_BUCKET_SIZE_DEFAULT,
isize out_of_band_size = POOL_OUT_OF_BAND_SIZE_DEFAULT,
isize alignment = 8,
gbAllocator block_allocator = heap_allocator(),
gbAllocator array_allocator = heap_allocator()) {
pool->memblock_size = memblock_size;
pool->out_of_band_size = out_of_band_size;
pool->alignment = alignment;
pool->block_allocator = block_allocator;
array_init(&pool->unused_memblock, array_allocator);
array_init(&pool->used_memblock, array_allocator);
array_init(&pool->out_of_band_allocations, array_allocator);
}
void pool_free_all(Pool *p) {
if (p->current_memblock != nullptr) {
array_add(&p->unused_memblock, p->current_memblock);
p->current_memblock = nullptr;
}
for_array(i, p->used_memblock) {
array_add(&p->unused_memblock, p->used_memblock[i]);
}
array_clear(&p->unused_memblock);
for_array(i, p->out_of_band_allocations) {
gb_free(p->block_allocator, p->out_of_band_allocations[i]);
}
array_clear(&p->out_of_band_allocations);
}
void pool_destroy(Pool *p) {
pool_free_all(p);
for_array(i, p->unused_memblock) {
gb_free(p->block_allocator, p->unused_memblock[i]);
}
}
void pool_cycle_new_block(Pool *p) {
GB_ASSERT_MSG(p->block_allocator.proc != nullptr,
"You must call pool_init on a Pool before using it!");
if (p->current_memblock != nullptr) {
array_add(&p->used_memblock, p->current_memblock);
}
u8 *new_block = nullptr;
if (p->unused_memblock.count > 0) {
new_block = array_pop(&p->unused_memblock);
} else {
GB_ASSERT(p->block_allocator.proc != nullptr);
new_block = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, p->alignment);
}
p->bytes_left = p->memblock_size;
p->current_memblock = new_block;
p->current_memblock = new_block;
}
void *pool_get(Pool *p,
isize size, isize alignment = 0) {
if (alignment <= 0) alignment = p->alignment;
isize extra = alignment - (size & alignment);
size += extra;
if (size >= p->out_of_band_size) {
GB_ASSERT(p->block_allocator.proc != nullptr);
u8 *memory = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, alignment);
if (memory != nullptr) {
array_add(&p->out_of_band_allocations, memory);
}
return memory;
}
if (p->bytes_left < size) {
pool_cycle_new_block(p);
if (p->current_memblock != nullptr) {
return nullptr;
}
}
u8 *res = p->current_pos;
p->current_pos += size;
p->bytes_left -= size;
return res;
}
gbAllocator pool_allocator(Pool *pool);
GB_ALLOCATOR_PROC(pool_allocator_procedure) {
Pool *p = cast(Pool *)allocator_data;
void *ptr = nullptr;
switch (type) {
case gbAllocation_Alloc:
return pool_get(p, size, alignment);
case gbAllocation_Free:
// Does nothing
break;
case gbAllocation_FreeAll:
pool_free_all(p);
break;
case gbAllocation_Resize:
return gb_default_resize_align(pool_allocator(p), old_memory, old_size, size, alignment);
}
return ptr;
}
gbAllocator pool_allocator(Pool *pool) {
gbAllocator allocator;
allocator.proc = pool_allocator_procedure;
allocator.data = pool;
return allocator;
}
i32 next_pow2(i32 n) {
if (n <= 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;
return n;
}
i64 next_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
n++;
return n;
}
i32 prev_pow2(i32 n) {
if (n <= 0) {
return 0;
}
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
return n - (n >> 1);
}
i64 prev_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return n - (n >> 1);
}
i16 f32_to_f16(f32 value) {
union { u32 i; f32 f; } v;
i32 i, s, e, m;
v.f = value;
i = (i32)v.i;
s = (i >> 16) & 0x00008000;
e = ((i >> 23) & 0x000000ff) - (127 - 15);
m = i & 0x007fffff;
if (e <= 0) {
if (e < -10) return cast(i16)s;
m = (m | 0x00800000) >> (1 - e);
if (m & 0x00001000)
m += 0x00002000;
return cast(i16)(s | (m >> 13));
} else if (e == 0xff - (127 - 15)) {
if (m == 0) {
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
} else {
/* NOTE(bill): NAN */
m >>= 13;
return cast(i16)(s | 0x7c00 | m | (m == 0));
}
} else {
if (m & 0x00001000) {
m += 0x00002000;
if (m & 0x00800000) {
m = 0;
e += 1;
}
}
if (e > 30) {
float volatile f = 1e12f;
int j;
for (j = 0; j < 10; j++) {
f *= f; /* NOTE(bill): Cause overflow */
}
return cast(i16)(s | 0x7c00);
}
return cast(i16)(s | (e << 10) | (m >> 13));
}
}
f64 gb_sqrt(f64 x) {
return sqrt(x);
}
// Doubly Linked Lists
#define DLIST_SET(curr_element, next_element) do { \
(curr_element)->next = (next_element); \
(curr_element)->next->prev = (curr_element); \
(curr_element) = (curr_element)->next; \
} while (0)
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
if ((root_element) == nullptr) { \
(root_element) = (curr_element) = (next_element); \
} else { \
DLIST_SET(curr_element, next_element); \
} \
} while (0)
#if defined(GB_SYSTEM_WINDOWS)
wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
u32 i, j;
u32 len = cast(u32)string16_len(cmd_line);
i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i);
u32 argc = 0;
argv[argc] = _argv;
bool in_quote = false;
bool in_text = false;
bool in_space = true;
i = 0;
j = 0;
for (;;) {
wchar_t a = cmd_line[i];
if (a == 0) {
break;
}
if (in_quote) {
if (a == '\"') {
in_quote = false;
} else {
_argv[j++] = a;
}
} else {
switch (a) {
case '\"':
in_quote = true;
in_text = true;
if (in_space) argv[argc++] = _argv+j;
in_space = false;
break;
case ' ':
case '\t':
case '\n':
case '\r':
if (in_text) _argv[j++] = '\0';
in_text = false;
in_space = true;
break;
default:
in_text = true;
if (in_space) argv[argc++] = _argv+j;
_argv[j++] = a;
in_space = false;
break;
}
}
i++;
}
_argv[j] = '\0';
argv[argc] = nullptr;
if (_argc) *_argc = argc;
return argv;
}
#endif
+130
View File
@@ -0,0 +1,130 @@
// Generates Documentation
gbString expr_to_string(AstNode *expression);
String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
isize len = 0;
for_array(i, g.list) {
String comment = g.list[i].string;
len += comment.len;
len += 1; // for \n
}
if (len == 0) {
return make_string(nullptr, 0);
}
u8 *text = gb_alloc_array(a, u8, len+1);
len = 0;
for_array(i, g.list) {
String comment = g.list[i].string;
if (comment[1] == '/') {
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
comment.text += 2;
comment.len -= 4;
}
comment = string_trim_whitespace(comment);
gb_memmove(text+len, comment.text, comment.len);
len += comment.len;
text[len++] = '\n';
}
return make_string(text, len);
}
#if 0
void print_type_spec(AstNode *spec) {
ast_node(ts, TypeSpec, spec);
GB_ASSERT(ts->name->kind == AstNode_Ident);
String name = ts->name->Ident.string;
if (name.len == 0) {
return;
}
if (name[0] == '_') {
return;
}
gb_printf("type %.*s\n", LIT(name));
}
void print_proc_decl(AstNodeProcDecl *pd) {
GB_ASSERT(pd->name->kind == AstNode_Ident);
String name = pd->name->Ident.string;
if (name.len == 0) {
return;
}
if (name[0] == '_') {
return;
}
String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
defer (gb_free(heap_allocator(), docs.text));
if (docs.len > 0) {
gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
} else {
return;
}
ast_node(proc_type, ProcType, pd->type);
gbString params = expr_to_string(proc_type->params);
defer (gb_string_free(params));
gb_printf("proc %.*s(%s)", LIT(name), params);
if (proc_type->results != nullptr) {
ast_node(fl, FieldList, proc_type->results);
isize count = fl->list.count;
if (count > 0) {
gbString results = expr_to_string(proc_type->results);
defer (gb_string_free(results));
gb_printf(" -> ");
if (count != 1) {
gb_printf("(");
}
gb_printf("%s", results);
if (count != 1) {
gb_printf(")");
}
}
}
gb_printf("\n\n");
}
#endif
void print_declaration(AstNode *decl) {
switch (decl->kind) {
case_ast_node(gd, GenDecl, decl);
for_array(spec_index, gd->specs) {
AstNode *spec = gd->specs[spec_index];
switch(gd->token.kind) {
case Token_import:
case Token_import_load:
break;
case Token_foreign_library:
case Token_foreign_system_library:
break;
}
}
case_end;
// case_ast_node(pd, ProcDecl, decl);
// print_proc_decl(pd);
// case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
// TODO(bill)
case_end;
}
}
void generate_documentation(Parser *parser) {
for_array(file_index, parser->files) {
AstFile *file = &parser->files[file_index];
Tokenizer *tokenizer = &file->tokenizer;
String fullpath = tokenizer->fullpath;
gb_printf("%.*s\n", LIT(fullpath));
for_array(decl_index, file->decls) {
AstNode *decl = file->decls[decl_index];
print_declaration(decl);
}
}
}
+99 -43
View File
@@ -1,6 +1,7 @@
typedef struct Scope Scope;
typedef struct Checker Checker;
typedef struct Type Type;
struct Scope;
struct Checker;
struct Type;
struct DeclInfo;
// typedef enum BuiltinProcId BuiltinProcId;
@@ -11,16 +12,18 @@ typedef struct Type Type;
ENTITY_KIND(TypeName) \
ENTITY_KIND(Procedure) \
ENTITY_KIND(Builtin) \
ENTITY_KIND(Alias) \
ENTITY_KIND(ImportName) \
ENTITY_KIND(LibraryName) \
ENTITY_KIND(Nil) \
ENTITY_KIND(Count)
ENTITY_KIND(Label)
typedef enum EntityKind {
enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
ENTITY_KINDS
#undef ENTITY_KIND
} EntityKind;
Entity_Count,
};
String const entity_strings[] = {
#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
@@ -28,35 +31,48 @@ String const entity_strings[] = {
#undef ENTITY_KIND
};
typedef enum EntityFlag {
EntityFlag_Visited = 1<<0,
EntityFlag_Used = 1<<1,
EntityFlag_Anonymous = 1<<2,
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
EntityFlag_Ellipsis = 1<<6,
EntityFlag_NoAlias = 1<<7,
EntityFlag_TypeField = 1<<8,
} EntityFlag;
enum EntityFlag {
EntityFlag_Visited = 1<<0,
EntityFlag_Used = 1<<1,
EntityFlag_Using = 1<<2,
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
EntityFlag_Ellipsis = 1<<6,
EntityFlag_NoAlias = 1<<7,
EntityFlag_TypeField = 1<<8,
EntityFlag_Value = 1<<9,
EntityFlag_Sret = 1<<10,
EntityFlag_BitFieldValue = 1<<11,
EntityFlag_CVarArg = 1<<20,
};
// Zero value means the overloading process is not yet done
typedef enum OverloadKind {
enum OverloadKind {
Overload_Unknown,
Overload_No,
Overload_Yes,
} OverloadKind;
};
enum EntityAliasKind {
EntityAlias_Invalid,
EntityAlias_Type,
EntityAlias_Entity,
};
// An Entity is a named "thing" in the language
typedef struct Entity Entity;
struct Entity {
EntityKind kind;
u64 id;
u32 flags;
Token token;
Scope * scope;
Type * type;
AstNode * identifier; // Can be NULL
AstNode * identifier; // Can be nullptr
DeclInfo * parent_proc_decl; // nullptr if in file/global scope
// TODO(bill): Cleanup how `using` works for entities
Entity * using_parent;
@@ -67,23 +83,37 @@ struct Entity {
ExactValue value;
} Constant;
struct {
i32 field_index;
i32 field_src_index;
bool is_immutable;
bool is_thread_local;
i32 field_index;
i32 field_src_index;
ExactValue default_value;
bool default_is_nil;
bool default_is_undef;
bool default_is_location;
bool is_immutable;
bool is_thread_local;
bool is_foreign;
Entity * foreign_library;
AstNode * foreign_library_ident;
String link_name;
} Variable;
i32 TypeName;
struct {
bool is_foreign;
String foreign_name;
Entity * foreign_library;
bool is_type_alias;
Type *type_parameter_specialization;
} TypeName;
struct {
OverloadKind overload_kind;
String link_name;
u64 tags;
OverloadKind overload_kind;
bool is_foreign;
Entity * foreign_library;
AstNode * foreign_library_ident;
} Procedure;
struct {
i32 id;
} Builtin;
struct {
Entity *base;
} Alias;
struct {
String path;
String name;
@@ -96,10 +126,14 @@ struct Entity {
bool used;
} LibraryName;
i32 Nil;
struct {
String name;
AstNode *node;
} Label;
};
};
gb_global Entity *e_context = NULL;
gb_global Entity *e_context = nullptr;
bool is_entity_kind_exported(EntityKind kind) {
switch (kind) {
@@ -114,7 +148,7 @@ bool is_entity_kind_exported(EntityKind kind) {
bool is_entity_exported(Entity *e) {
// TODO(bill): Determine the actual exportation rules for imports of entities
GB_ASSERT(e != NULL);
GB_ASSERT(e != nullptr);
if (!is_entity_kind_exported(e->kind)) {
return false;
}
@@ -123,9 +157,10 @@ bool is_entity_exported(Entity *e) {
if (name.len == 0) {
return false;
}
return name.text[0] != '_';
return name[0] != '_';
}
gb_global u64 global_entity_id = 0;
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
Entity *entity = gb_alloc_item(a, Entity);
@@ -133,6 +168,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
entity->scope = scope;
entity->token = token;
entity->type = type;
entity->id = ++global_entity_id;
return entity;
}
@@ -143,11 +179,12 @@ Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *typ
}
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
GB_ASSERT(parent != NULL);
GB_ASSERT(parent != nullptr);
token.pos = parent->token.pos;
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent;
entity->flags |= EntityFlag_Anonymous;
entity->parent_proc_decl = parent->parent_proc_decl;
entity->flags |= EntityFlag_Using;
return entity;
}
@@ -163,20 +200,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
entity->flags |= EntityFlag_Used;
if (anonymous) entity->flags |= EntityFlag_Anonymous;
if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Param;
return entity;
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Field;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
return entity;
}
@@ -201,6 +238,12 @@ Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type
return entity;
}
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type, Entity *base) {
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
entity->Alias.base = base;
return entity;
}
Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
String path, String name, Scope *import_scope) {
Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
@@ -218,14 +261,27 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
return entity;
}
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
Entity *entity = alloc_entity(a, Entity_Nil, nullptr, token, type);
return entity;
}
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL, false);
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
AstNode *node) {
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
entity->Label.node = node;
return entity;
}
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, scope, token, nullptr, false);
}
-495
View File
@@ -1,495 +0,0 @@
#include <math.h>
// TODO(bill): Big numbers
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
typedef struct AstNode AstNode;
typedef enum ExactValueKind {
ExactValue_Invalid,
ExactValue_Bool,
ExactValue_String,
ExactValue_Integer,
ExactValue_Float,
ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough?
ExactValue_Count,
} ExactValueKind;
typedef struct ExactValue {
ExactValueKind kind;
union {
bool value_bool;
String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
AstNode *value_compound;
};
} ExactValue;
HashKey hash_exact_value(ExactValue v) {
return hashing_proc(&v, gb_size_of(ExactValue));
}
ExactValue make_exact_value_compound(AstNode *node) {
ExactValue result = {ExactValue_Compound};
result.value_compound = node;
return result;
}
ExactValue make_exact_value_bool(bool b) {
ExactValue result = {ExactValue_Bool};
result.value_bool = (b != 0);
return result;
}
ExactValue make_exact_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_String};
result.value_string = string;
return result;
}
ExactValue make_exact_value_integer(i64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i;
return result;
}
ExactValue make_exact_value_float(f64 f) {
ExactValue result = {ExactValue_Float};
result.value_float = f;
return result;
}
ExactValue make_exact_value_pointer(i64 ptr) {
ExactValue result = {ExactValue_Pointer};
result.value_pointer = ptr;
return result;
}
ExactValue make_exact_value_integer_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
i32 base = 10;
bool has_prefix = false;
if (string.len > 2 && string.text[0] == '0') {
switch (string.text[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
i64 result = 0;
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
i64 v = 0;
if (gb_char_is_digit(r)) {
v = r - '0';
} else if (gb_char_is_hex_digit(r)) {
v = gb_hex_digit_to_int(r);
} else {
break;
}
result *= base;
result += v;
}
return make_exact_value_integer(result);
}
ExactValue make_exact_value_float_from_string(String string) {
isize i = 0;
u8 *str = string.text;
isize len = string.len;
f64 sign = 1.0;
if (str[i] == '-') {
sign = -1.0;
i++;
} else if (*str == '+') {
i++;
}
f64 value = 0.0;
for (; i < len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
if (!gb_char_is_digit(r)) {
break;
}
i64 v = r - '0';
value *= 10.0;
value += v;
}
if (str[i] == '.') {
f64 pow10 = 10.0;
i++;
for (; i < string.len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
if (!gb_char_is_digit(r)) {
break;
}
value += (r-'0')/pow10;
pow10 *= 10.0;
}
}
f64 frac = 0;
f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) {
i++;
if (str[i] == '-') {
frac = 1;
i++;
} else if (str[i] == '+') {
i++;
}
u32 exp;
for (exp = 0; gb_char_is_digit(str[i]); i++) {
exp = exp * 10 + (str[i]-'0');
}
if (exp > 308) exp = 308;
while (exp >= 50) { scale *= 1e50; exp -= 50; }
while (exp >= 8) { scale *= 1e8; exp -= 8; }
while (exp > 0) { scale *= 10.0; exp -= 1; }
}
f64 result = sign * (frac ? (value / scale) : (value * scale));
return make_exact_value_float(result);
}
ExactValue make_exact_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return make_exact_value_string(token.string);
case Token_Integer: return make_exact_value_integer_from_string(token.string);
case Token_Float: return make_exact_value_float_from_string(token.string);
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r);
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
return make_exact_value_integer(r);
}
default:
GB_PANIC("Invalid token for basic literal");
break;
}
ExactValue result = {ExactValue_Invalid};
return result;
}
ExactValue exact_value_to_integer(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return v;
case ExactValue_Float: {
i64 i = cast(i64)v.value_float;
f64 f = cast(f64)i;
if (f == v.value_float) {
return make_exact_value_integer(i);
}
} break;
case ExactValue_Pointer:
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_float(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return make_exact_value_float(cast(i64)v.value_integer);
case ExactValue_Float:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
switch (op) {
case Token_Add: {
switch (v.kind) {
case ExactValue_Invalid:
case ExactValue_Integer:
case ExactValue_Float:
return v;
}
} break;
case Token_Sub: {
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer: {
ExactValue i = v;
i.value_integer = -i.value_integer;
return i;
}
case ExactValue_Float: {
ExactValue i = v;
i.value_float = -i.value_float;
return i;
}
}
} break;
case Token_Xor: {
i64 i = 0;
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer:
i = ~v.value_integer;
break;
default:
goto failure;
}
// NOTE(bill): unsigned integers will be negative and will need to be
// limited to the types precision
// IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored
if (0 < precision && precision < 64) {
i &= ~((~0ll)<<precision);
}
return make_exact_value_integer(i);
} break;
case Token_Not: {
switch (v.kind) {
case ExactValue_Invalid: return v;
case ExactValue_Bool:
return make_exact_value_bool(!v.value_bool);
}
} break;
}
failure:
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
ExactValue error_value = {0};
return error_value;
}
// NOTE(bill): Make sure things are evaluated in correct order
i32 exact_value_order(ExactValue v) {
switch (v.kind) {
case ExactValue_Invalid:
return 0;
case ExactValue_Bool:
case ExactValue_String:
return 1;
case ExactValue_Integer:
return 2;
case ExactValue_Float:
return 3;
case ExactValue_Pointer:
return 4;
default:
GB_PANIC("How'd you get here? Invalid Value.kind");
return -1;
}
}
void match_exact_values(ExactValue *x, ExactValue *y) {
if (exact_value_order(*y) < exact_value_order(*x)) {
match_exact_values(y, x);
return;
}
switch (x->kind) {
case ExactValue_Invalid:
*y = *x;
return;
case ExactValue_Bool:
case ExactValue_String:
return;
case ExactValue_Integer:
switch (y->kind) {
case ExactValue_Integer:
return;
case ExactValue_Float:
// TODO(bill): Is this good enough?
*x = make_exact_value_float(cast(f64)x->value_integer);
return;
}
break;
case ExactValue_Float:
if (y->kind == ExactValue_Float)
return;
break;
}
compiler_error("How'd you get here? Invalid ExactValueKind");
}
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return x;
case ExactValue_Bool:
switch (op) {
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool);
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool);
default: goto error;
}
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
i64 c = 0;
switch (op) {
case Token_Add: c = a + b; break;
case Token_Sub: c = a - b; break;
case Token_Mul: c = a * b; break;
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
case Token_Mod: c = a % b; break;
case Token_And: c = a & b; break;
case Token_Or: c = a | b; break;
case Token_Xor: c = a ^ b; break;
case Token_AndNot: c = a&(~b); break;
case Token_Shl: c = a << b; break;
case Token_Shr: c = a >> b; break;
default: goto error;
}
return make_exact_value_integer(c);
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op) {
case Token_Add: return make_exact_value_float(a + b);
case Token_Sub: return make_exact_value_float(a - b);
case Token_Mul: return make_exact_value_float(a * b);
case Token_Quo: return make_exact_value_float(a / b);
default: goto error;
}
} break;
}
error:
; // MSVC accepts this??? apparently you cannot declare variables immediately after labels...
ExactValue error_value = {0};
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
return error_value;
}
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
i32 cmp_f64(f64 a, f64 b) {
return (a > b) - (a < b);
}
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return false;
case ExactValue_Bool:
switch (op) {
case Token_CmpEq: return x.value_bool == y.value_bool;
case Token_NotEq: return x.value_bool != y.value_bool;
}
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
switch (op) {
case Token_CmpEq: return a == b;
case Token_NotEq: return a != b;
case Token_Lt: return a < b;
case Token_LtEq: return a <= b;
case Token_Gt: return a > b;
case Token_GtEq: return a >= b;
}
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
case Token_Lt: return cmp_f64(a, b) < 0;
case Token_LtEq: return cmp_f64(a, b) <= 0;
case Token_Gt: return cmp_f64(a, b) > 0;
case Token_GtEq: return cmp_f64(a, b) >= 0;
}
} break;
case ExactValue_String: {
String a = x.value_string;
String b = y.value_string;
isize len = gb_min(a.len, b.len);
// TODO(bill): gb_memcompare is used because the strings are UTF-8
switch (op) {
case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0;
case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0;
case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0;
case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0;
}
} break;
}
GB_PANIC("Invalid comparison");
return false;
}
+673
View File
@@ -0,0 +1,673 @@
#include <math.h>
// TODO(bill): Big numbers
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
struct AstNode;
struct HashKey;
struct Type;
bool are_types_identical(Type *x, Type *y);
struct Complex128 {
f64 real, imag;
};
enum ExactValueKind {
ExactValue_Invalid,
ExactValue_Bool,
ExactValue_String,
ExactValue_Integer,
ExactValue_Float,
ExactValue_Complex,
ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough?
ExactValue_Type,
ExactValue_Count,
};
struct ExactValue {
ExactValueKind kind;
union {
bool value_bool;
String value_string;
i128 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
Complex128 value_complex;
AstNode * value_compound;
Type * value_type;
};
};
gb_global ExactValue const empty_exact_value = {};
HashKey hash_exact_value(ExactValue v) {
return hashing_proc(&v, gb_size_of(ExactValue));
}
ExactValue exact_value_compound(AstNode *node) {
ExactValue result = {ExactValue_Compound};
result.value_compound = node;
return result;
}
ExactValue exact_value_bool(bool b) {
ExactValue result = {ExactValue_Bool};
result.value_bool = (b != 0);
return result;
}
ExactValue exact_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_String};
result.value_string = string;
return result;
}
ExactValue exact_value_i64(i64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i128_from_i64(i);
return result;
}
ExactValue exact_value_i128(i128 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i;
return result;
}
ExactValue exact_value_u128(u128 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = u128_to_i128(i);
return result;
}
ExactValue exact_value_float(f64 f) {
ExactValue result = {ExactValue_Float};
result.value_float = f;
return result;
}
ExactValue exact_value_complex(f64 real, f64 imag) {
ExactValue result = {ExactValue_Complex};
result.value_complex.real = real;
result.value_complex.imag = imag;
return result;
}
ExactValue exact_value_pointer(i64 ptr) {
ExactValue result = {ExactValue_Pointer};
result.value_pointer = ptr;
return result;
}
ExactValue exact_value_type(Type *type) {
ExactValue result = {ExactValue_Type};
result.value_type = type;
return result;
}
ExactValue exact_value_integer_from_string(String string) {
return exact_value_u128(u128_from_string(string));
}
f64 float_from_string(String string) {
isize i = 0;
u8 *str = string.text;
isize len = string.len;
f64 sign = 1.0;
if (str[i] == '-') {
sign = -1.0;
i++;
} else if (*str == '+') {
i++;
}
#if 0
if (len-i > 2 &&
str[i] == '0' &&
str[i+1] == 'h') {
i += 2;
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
u64 base = 16;
u64 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result *= base;
result += v;
}
return *cast(f64 *)&result;
}
#endif
f64 value = 0.0;
for (; i < len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
i64 v = digit_value(r);
if (v >= 10) {
break;
}
value *= 10.0;
value += v;
}
if (str[i] == '.') {
f64 pow10 = 10.0;
i++;
for (; i < string.len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
i64 v = digit_value(r);
if (v >= 10) {
break;
}
value += v/pow10;
pow10 *= 10.0;
}
}
bool frac = false;
f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) {
i++;
if (str[i] == '-') {
frac = true;
i++;
} else if (str[i] == '+') {
i++;
}
u32 exp = 0;
for (; i < len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
u32 d = cast(u32)digit_value(r);
if (d >= 10) {
break;
}
exp = exp * 10 + d;
}
if (exp > 308) exp = 308;
while (exp >= 50) { scale *= 1e50; exp -= 50; }
while (exp >= 8) { scale *= 1e8; exp -= 8; }
while (exp > 0) { scale *= 10.0; exp -= 1; }
}
return sign * (frac ? (value / scale) : (value * scale));
}
ExactValue exact_value_float_from_string(String string) {
return exact_value_float(float_from_string(string));
}
ExactValue exact_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return exact_value_string(token.string);
case Token_Integer: return exact_value_integer_from_string(token.string);
case Token_Float: return exact_value_float_from_string(token.string);
case Token_Imag: {
String str = token.string;
Rune last_rune = cast(Rune)str[str.len-1];
str.len--; // Ignore the `i|j|k`
f64 imag = float_from_string(str);
if (last_rune == 'i') {
return exact_value_complex(0, imag);
}
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r);
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
return exact_value_i64(r);
}
default:
GB_PANIC("Invalid token for basic literal");
break;
}
ExactValue result = {ExactValue_Invalid};
return result;
}
ExactValue exact_value_to_integer(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return v;
case ExactValue_Float: {
i128 i = i128_from_f64(v.value_float);
f64 f = i128_to_f64(i);
if (f == v.value_float) {
return exact_value_i128(i);
}
} break;
case ExactValue_Pointer:
return exact_value_i64(cast(i64)cast(intptr)v.value_pointer);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_float(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_float(i128_to_f64(v.value_integer));
case ExactValue_Float:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_complex(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(i128_to_f64(v.value_integer), 0);
case ExactValue_Float:
return exact_value_complex(v.value_float, 0);
case ExactValue_Complex:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_real(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return v;
case ExactValue_Complex:
return exact_value_float(v.value_complex.real);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return exact_value_i64(0);
case ExactValue_Complex:
return exact_value_float(v.value_complex.imag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(0, exact_value_to_float(v).value_float);
case ExactValue_Float:
return exact_value_complex(0, v.value_float);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
switch (op) {
case Token_Add: {
switch (v.kind) {
case ExactValue_Invalid:
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
return v;
}
} break;
case Token_Sub: {
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer: {
ExactValue i = v;
i.value_integer = -i.value_integer;
return i;
}
case ExactValue_Float: {
ExactValue i = v;
i.value_float = -i.value_float;
return i;
}
case ExactValue_Complex: {
f64 real = v.value_complex.real;
f64 imag = v.value_complex.imag;
return exact_value_complex(-real, -imag);
}
}
} break;
case Token_Xor: {
i128 i = I128_ZERO;
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer:
i = ~v.value_integer;
break;
default:
goto failure;
}
// NOTE(bill): unsigned integers will be negative and will need to be
// limited to the types precision
// IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored
if (0 < precision && precision < 128) {
i = i & ~(I128_NEG_ONE << precision);
}
return exact_value_i128(i);
} break;
case Token_Not: {
switch (v.kind) {
case ExactValue_Invalid: return v;
case ExactValue_Bool:
return exact_value_bool(!v.value_bool);
}
} break;
}
failure:
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
ExactValue error_value = {};
return error_value;
}
// NOTE(bill): Make sure things are evaluated in correct order
i32 exact_value_order(ExactValue v) {
switch (v.kind) {
case ExactValue_Invalid:
return 0;
case ExactValue_Bool:
case ExactValue_String:
return 1;
case ExactValue_Integer:
return 2;
case ExactValue_Float:
return 3;
case ExactValue_Complex:
return 4;
case ExactValue_Pointer:
return 5;
default:
GB_PANIC("How'd you get here? Invalid Value.kind");
return -1;
}
}
void match_exact_values(ExactValue *x, ExactValue *y) {
if (exact_value_order(*y) < exact_value_order(*x)) {
match_exact_values(y, x);
return;
}
switch (x->kind) {
case ExactValue_Invalid:
*y = *x;
return;
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Complex:
return;
case ExactValue_Integer:
switch (y->kind) {
case ExactValue_Integer:
return;
case ExactValue_Float:
// TODO(bill): Is this good enough?
*x = exact_value_float(i128_to_f64(x->value_integer));
return;
case ExactValue_Complex:
*x = exact_value_complex(i128_to_f64(x->value_integer), 0);
return;
}
break;
case ExactValue_Float:
switch (y->kind) {
case ExactValue_Float:
return;
case ExactValue_Complex:
*x = exact_value_to_complex(*x);
return;
}
break;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
}
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return x;
case ExactValue_Bool:
switch (op) {
case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
default: goto error;
}
break;
case ExactValue_Integer: {
i128 a = x.value_integer;
i128 b = y.value_integer;
i128 c = I128_ZERO;
switch (op) {
case Token_Add: c = a + b; break;
case Token_Sub: c = a - b; break;
case Token_Mul: c = a * b; break;
case Token_Quo: return exact_value_float(fmod(i128_to_f64(a), i128_to_f64(b)));
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
case Token_Mod: c = a % b; break;
case Token_ModMod: c = ((a % b) + b) % b; break;
case Token_And: c = a & b; break;
case Token_Or: c = a | b; break;
case Token_Xor: c = a ^ b; break;
case Token_AndNot: c = i128_and_not(a, b); break;
case Token_Shl: c = a << cast(u32)i128_to_u64(b); break;
case Token_Shr: c = a >> cast(u32)i128_to_u64(b); break;
default: goto error;
}
return exact_value_i128(c);
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op) {
case Token_Add: return exact_value_float(a + b);
case Token_Sub: return exact_value_float(a - b);
case Token_Mul: return exact_value_float(a * b);
case Token_Quo: return exact_value_float(a / b);
default: goto error;
}
} break;
case ExactValue_Complex: {
y = exact_value_to_complex(y);
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
f64 real = 0;
f64 imag = 0;
switch (op) {
case Token_Add:
real = a + c;
imag = b + d;
break;
case Token_Sub:
real = a - c;
imag = b - d;
break;
case Token_Mul:
real = (a*c - b*d);
imag = (b*c + a*d);
break;
case Token_Quo: {
f64 s = c*c + d*d;
real = (a*c + b*d)/s;
imag = (b*c - a*d)/s;
} break;
default: goto error;
}
return exact_value_complex(real, imag);
} break;
case ExactValue_String: {
if (op != Token_Add) goto error;
// NOTE(bill): How do you minimize this over allocation?
String sx = x.value_string;
String sy = y.value_string;
isize len = sx.len+sy.len;
u8 *data = gb_alloc_array(heap_allocator(), u8, len);
gb_memmove(data, sx.text, sx.len);
gb_memmove(data+sx.len, sy.text, sy.len);
return exact_value_string(make_string(data, len));
} break;
}
error:; // NOTE(bill): MSVC accepts this??? apparently you cannot declare variables immediately after labels...
return empty_exact_value;
}
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
i32 cmp_f64(f64 a, f64 b) {
return (a > b) - (a < b);
}
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return false;
case ExactValue_Bool:
switch (op) {
case Token_CmpEq: return x.value_bool == y.value_bool;
case Token_NotEq: return x.value_bool != y.value_bool;
}
break;
case ExactValue_Integer: {
i128 a = x.value_integer;
i128 b = y.value_integer;
switch (op) {
case Token_CmpEq: return a == b;
case Token_NotEq: return a != b;
case Token_Lt: return a < b;
case Token_LtEq: return a <= b;
case Token_Gt: return a > b;
case Token_GtEq: return a >= b;
}
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
case Token_Lt: return cmp_f64(a, b) < 0;
case Token_LtEq: return cmp_f64(a, b) <= 0;
case Token_Gt: return cmp_f64(a, b) > 0;
case Token_GtEq: return cmp_f64(a, b) >= 0;
}
} break;
case ExactValue_Complex: {
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
}
} break;
case ExactValue_String: {
String a = x.value_string;
String b = y.value_string;
// TODO(bill): gb_memcompare is used because the strings are UTF-8
switch (op) {
case Token_CmpEq: return a == b;
case Token_NotEq: return a != b;
case Token_Lt: return a < b;
case Token_LtEq: return a <= b;
case Token_Gt: return a > b;
case Token_GtEq: return a >= b;
}
} break;
case ExactValue_Type:
switch (op) {
case Token_CmpEq: return are_types_identical(x.value_type, y.value_type);
case Token_NotEq: return !are_types_identical(x.value_type, y.value_type);
}
break;
}
GB_PANIC("Invalid comparison");
return false;
}
+544 -224
View File
File diff suppressed because it is too large Load Diff
+726
View File
@@ -0,0 +1,726 @@
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
#define MSVC_AMD64_INTRINSICS
#include <intrin.h>
#pragma intrinsic(_mul128)
#endif
#define BIT128_U64_HIGHBIT 0x8000000000000000ull
#define BIT128_U64_BITS62 0x7fffffffffffffffull
#define BIT128_U64_ALLBITS 0xffffffffffffffffull
typedef struct u128 { u64 lo; u64 hi; } u128;
typedef struct i128 { u64 lo; i64 hi; } i128;
static u128 const U128_ZERO = {0, 0};
static u128 const U128_ONE = {1, 0};
static i128 const I128_ZERO = {0, 0};
static i128 const I128_ONE = {1, 0};
static u128 const U128_NEG_ONE = {BIT128_U64_ALLBITS, BIT128_U64_ALLBITS};
static i128 const I128_NEG_ONE = {BIT128_U64_ALLBITS, cast(i64)BIT128_U64_ALLBITS};
u128 u128_lo_hi (u64 lo, u64 hi);
u128 u128_from_u32 (u32 u);
u128 u128_from_u64 (u64 u);
u128 u128_from_i64 (i64 u);
u128 u128_from_f32 (f32 f);
u128 u128_from_f64 (f64 f);
u128 u128_from_string(String string);
i128 i128_lo_hi (u64 lo, i64 hi);
i128 i128_from_u32 (u32 u);
i128 i128_from_u64 (u64 u);
i128 i128_from_i64 (i64 u);
i128 i128_from_f32 (f32 f);
i128 i128_from_f64 (f64 f);
i128 i128_from_string(String string);
u64 u128_to_u64(u128 a);
i64 u128_to_i64(u128 a);
f64 u128_to_f64(u128 a);
i128 u128_to_i128(u128 a);
u64 i128_to_u64(i128 a);
i64 i128_to_i64(i128 a);
f64 i128_to_f64(i128 a);
u128 i128_to_u128(i128 a);
String u128_to_string(u128 a, char *buf, isize len);
String i128_to_string(i128 a, char *buf, isize len);
i32 u128_cmp (u128 a, u128 b);
bool u128_eq (u128 a, u128 b);
bool u128_ne (u128 a, u128 b);
bool u128_lt (u128 a, u128 b);
bool u128_gt (u128 a, u128 b);
bool u128_le (u128 a, u128 b);
bool u128_ge (u128 a, u128 b);
u128 u128_add (u128 a, u128 b);
u128 u128_not (u128 a);
u128 u128_neg (u128 a);
u128 u128_sub (u128 a, u128 b);
u128 u128_and (u128 a, u128 b);
u128 u128_or (u128 a, u128 b);
u128 u128_xor (u128 a, u128 b);
u128 u128_and_not(u128 a, u128 b);
u128 u128_shl (u128 a, u32 n);
u128 u128_shr (u128 a, u32 n);
u128 u128_mul (u128 a, u128 b);
void u128_divide (u128 num, u128 den, u128 *quo, u128 *rem);
u128 u128_quo (u128 a, u128 b);
u128 u128_mod (u128 a, u128 b);
i128 i128_abs (i128 a);
i32 i128_cmp (i128 a, i128 b);
bool i128_eq (i128 a, i128 b);
bool i128_ne (i128 a, i128 b);
bool i128_lt (i128 a, i128 b);
bool i128_gt (i128 a, i128 b);
bool i128_le (i128 a, i128 b);
bool i128_ge (i128 a, i128 b);
i128 i128_add (i128 a, i128 b);
i128 i128_not (i128 a);
i128 i128_neg (i128 a);
i128 i128_sub (i128 a, i128 b);
i128 i128_and (i128 a, i128 b);
i128 i128_or (i128 a, i128 b);
i128 i128_xor (i128 a, i128 b);
i128 i128_and_not(i128 a, i128 b);
i128 i128_shl (i128 a, u32 n);
i128 i128_shr (i128 a, u32 n);
i128 i128_mul (i128 a, i128 b);
void i128_divide (i128 num, i128 den, i128 *quo, i128 *rem);
i128 i128_quo (i128 a, i128 b);
i128 i128_mod (i128 a, i128 b);
bool operator==(u128 a, u128 b) { return u128_eq(a, b); }
bool operator!=(u128 a, u128 b) { return u128_ne(a, b); }
bool operator< (u128 a, u128 b) { return u128_lt(a, b); }
bool operator> (u128 a, u128 b) { return u128_gt(a, b); }
bool operator<=(u128 a, u128 b) { return u128_le(a, b); }
bool operator>=(u128 a, u128 b) { return u128_ge(a, b); }
u128 operator+(u128 a, u128 b) { return u128_add(a, b); }
u128 operator-(u128 a, u128 b) { return u128_sub(a, b); }
u128 operator*(u128 a, u128 b) { return u128_mul(a, b); }
u128 operator/(u128 a, u128 b) { return u128_quo(a, b); }
u128 operator%(u128 a, u128 b) { return u128_mod(a, b); }
u128 operator&(u128 a, u128 b) { return u128_and(a, b); }
u128 operator|(u128 a, u128 b) { return u128_or (a, b); }
u128 operator^(u128 a, u128 b) { return u128_xor(a, b); }
u128 operator~(u128 a) { return u128_not(a); }
u128 operator+(u128 a) { return a; }
u128 operator-(u128 a) { return u128_neg(a); }
u128 operator<<(u128 a, u32 b) { return u128_shl(a, b); }
u128 operator>>(u128 a, u32 b) { return u128_shr(a, b); }
bool operator==(i128 a, i128 b) { return i128_eq(a, b); }
bool operator!=(i128 a, i128 b) { return i128_ne(a, b); }
bool operator< (i128 a, i128 b) { return i128_lt(a, b); }
bool operator> (i128 a, i128 b) { return i128_gt(a, b); }
bool operator<=(i128 a, i128 b) { return i128_le(a, b); }
bool operator>=(i128 a, i128 b) { return i128_ge(a, b); }
i128 operator+(i128 a, i128 b) { return i128_add(a, b); }
i128 operator-(i128 a, i128 b) { return i128_sub(a, b); }
i128 operator*(i128 a, i128 b) { return i128_mul(a, b); }
i128 operator/(i128 a, i128 b) { return i128_quo(a, b); }
i128 operator%(i128 a, i128 b) { return i128_mod(a, b); }
i128 operator&(i128 a, i128 b) { return i128_and(a, b); }
i128 operator|(i128 a, i128 b) { return i128_or (a, b); }
i128 operator^(i128 a, i128 b) { return i128_xor(a, b); }
i128 operator~(i128 a) { return i128_not(a); }
i128 operator+(i128 a) { return a; }
i128 operator-(i128 a) { return i128_neg(a); }
i128 operator<<(i128 a, u32 b) { return i128_shl(a, b); }
i128 operator>>(i128 a, u32 b) { return i128_shr(a, b); }
////////////////////////////////////////////////////////////////
u64 bit128__digit_value(Rune r) {
if ('0' <= r && r <= '9') {
return r - '0';
} else if ('a' <= r && r <= 'f') {
return r - 'a' + 10;
} else if ('A' <= r && r <= 'F') {
return r - 'A' + 10;
}
return 16; // NOTE(bill): Larger than highest possible
}
u128 u128_lo_hi(u64 lo, u64 hi) {
u128 r = {};
r.lo = lo;
r.hi = hi;
return r;
}
u128 u128_from_u32(u32 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_u64(u64 u) { return u128_lo_hi(cast(u64)u, 0); }
u128 u128_from_i64(i64 u) { return u128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
u128 u128_from_f32(f32 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_f64(f64 f) { return u128_lo_hi(cast(u64)f, 0); }
u128 u128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
u128 base_ = u128_from_u64(base);
u128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = u128_mul(result, base_);
result = u128_add(result, u128_from_u64(v));
}
return result;
}
i128 i128_lo_hi(u64 lo, i64 hi) {
i128 i;
i.lo = lo;
i.hi = hi;
return i;
}
i128 i128_from_u32(u32 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_u64(u64 u) { return i128_lo_hi(cast(u64)u, 0); }
i128 i128_from_i64(i64 u) { return i128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
i128 i128_from_f32(f32 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_f64(f64 f) { return i128_lo_hi(cast(u64)f, 0); }
i128 i128_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
u64 base = 10;
bool has_prefix = false;
if (string.len > 2 && string[0] == '0') {
switch (string[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
i128 base_ = i128_from_u64(base);
i128 result = {0};
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
u64 v = bit128__digit_value(r);
if (v >= base) {
break;
}
result = i128_mul(result, base_);
result = i128_add(result, i128_from_u64(v));
}
return result;
}
u64 u128_to_u64(u128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 u128_to_i64(u128 a) {
return a.lo;
}
f64 u128_to_f64(u128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = cast(i64)a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
i128 u128_to_i128(u128 a) {
return *cast(i128 *)&a;
}
u64 i128_to_u64(i128 a) {
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
}
i64 i128_to_i64(i128 a) {
return cast(i64)a.lo;
}
f64 i128_to_f64(i128 a) {
if (a.hi >= 0) {
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
}
i64 h = a.hi;
u64 l = a.lo;
h = ~h;
l = ~l;
l += 1;
if (l == 0) {
h += 1;
}
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
}
u128 i128_to_u128(i128 a) {
return *cast(u128 *)&a;
}
String u128_to_string(u128 v, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memcopy(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
String i128_to_string(i128 a, char *out_buf, isize out_buf_len) {
char buf[200] = {0};
isize i = gb_size_of(buf);
bool negative = false;
if (i128_lt(a, I128_ZERO)) {
negative = true;
a = i128_neg(a);
}
u128 v = *cast(u128 *)&a;
u128 b = u128_from_u64(10);;
while (u128_ge(v, b)) {
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
v = u128_quo(v, b);
}
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
if (negative) {
buf[--i] = '-';
}
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
gb_memcopy(out_buf, &buf[i], len);
return make_string(cast(u8 *)out_buf, len);
}
////////////////////////////////////////////////////////////////
i32 u128_cmp(u128 a, u128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool u128_ne(u128 a, u128 b) { return !u128_eq(a, b); }
bool u128_lt(u128 a, u128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool u128_gt(u128 a, u128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool u128_le(u128 a, u128 b) { return !u128_gt(a, b); }
bool u128_ge(u128 a, u128 b) { return !u128_lt(a, b); }
u128 u128_add(u128 a, u128 b) {
u128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
u128 u128_not(u128 a) { return u128_lo_hi(~a.lo, ~a.hi); }
u128 u128_neg(u128 a) {
return u128_add(u128_not(a), u128_from_u64(1));
}
u128 u128_sub(u128 a, u128 b) {
return u128_add(a, u128_neg(b));
}
u128 u128_and(u128 a, u128 b) { return u128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
u128 u128_or (u128 a, u128 b) { return u128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
u128 u128_xor(u128 a, u128 b) { return u128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
u128 u128_and_not(u128 a, u128 b) { return u128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
u128 u128_shl(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
u128 u128_shr(u128 a, u32 n) {
if (n >= 128) {
return u128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
u128 u128_mul(u128 a, u128 b) {
if (a.lo == 0 && a.hi == 0) {
return u128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return u128_from_u64(0);
}
if (u128_eq(a, U128_ONE)) {
return b;
}
if (u128_eq(b, U128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _umul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
u128 res = {0};
u128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = u128_add(res, u128_shl(a, i));
}
t = u128_shr(t, 1);
}
return res;
}
bool u128_hibit(u128 *d) { return (d->hi & BIT128_U64_HIGHBIT) != 0; }
void u128_divide(u128 num, u128 den, u128 *quo, u128 *rem) {
if (u128_eq(den, U128_ZERO)) {
if (quo) *quo = u128_from_u64(num.lo/den.lo);
if (rem) *rem = U128_ZERO;
} else {
u128 n = num;
u128 d = den;
u128 x = U128_ONE;
u128 r = U128_ZERO;
while (u128_ge(n, d) && !u128_hibit(&d)) {
x = u128_shl(x, 1);
d = u128_shl(d, 1);
}
while (u128_ne(x, U128_ZERO)) {
if (u128_ge(n, d)) {
n = u128_sub(n, d);
r = u128_or(r, x);
}
x = u128_shr(x, 1);
d = u128_shr(d, 1);
}
if (quo) *quo = r;
if (rem) *rem = n;
}
}
u128 u128_quo(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo/b.lo);
}
u128 res = {0};
u128_divide(a, b, &res, nullptr);
return res;
}
u128 u128_mod(u128 a, u128 b) {
if (a.hi == 0 && b.hi == 0) {
return u128_from_u64(a.lo%b.lo);
}
u128 res = {0};
u128_divide(a, b, nullptr, &res);
return res;
}
////////////////////////////////////////////////////////////////
i128 i128_abs(i128 a) {
if ((a.hi&BIT128_U64_HIGHBIT) != 0) {
return i128_neg(a);
}
return a;
}
i32 i128_cmp(i128 a, i128 b) {
if (a.hi == b.hi && b.lo == b.lo) {
return 0;
}
if (a.hi == b.hi) {
return a.lo < b.lo ? -1 : +1;
}
return a.hi < b.hi ? -1 : +1;
}
bool i128_eq(i128 a, i128 b) { return a.hi == b.hi && a.lo == b.lo; }
bool i128_ne(i128 a, i128 b) { return !i128_eq(a, b); }
bool i128_lt(i128 a, i128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
bool i128_gt(i128 a, i128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
bool i128_le(i128 a, i128 b) { return a.hi == b.hi ? a.lo <= b.lo : a.hi <= b.hi; }
bool i128_ge(i128 a, i128 b) { return a.hi == b.hi ? a.lo >= b.lo : a.hi >= b.hi; }
i128 i128_add(i128 a, i128 b) {
i128 old_a = a;
a.lo += b.lo;
a.hi += b.hi;
if (a.lo < old_a.lo) {
a.hi += 1;
}
return a;
}
i128 i128_not(i128 a) { return i128_lo_hi(~a.lo, ~a.hi); }
i128 i128_neg(i128 a) {
return i128_add(i128_not(a), i128_from_u64(1));
}
i128 i128_sub(i128 a, i128 b) {
return i128_add(a, i128_neg(b));
}
i128 i128_and(i128 a, i128 b) { return i128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
i128 i128_or (i128 a, i128 b) { return i128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
i128 i128_xor(i128 a, i128 b) { return i128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
i128 i128_and_not(i128 a, i128 b) { return i128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
i128 i128_shl(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.hi = __shiftleft128(a.lo, a.hi, n);
a.lo = a.lo << n;
return a;
#else
if (n >= 64) {
n -= 64;
a.hi = a.lo;
a.lo = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS >> n);
a.hi <<= n;
a.hi |= (a.lo&mask) >> (64 - n);
a.lo <<= n;
}
return a;
#endif
}
i128 i128_shr(i128 a, u32 n) {
if (n >= 128) {
return i128_lo_hi(0, 0);
}
#if 0 && defined(MSVC_AMD64_INTRINSICS)
a.lo = __shiftright128(a.lo, a.hi, n);
a.hi = a.hi >> n;
return a;
#else
if (n >= 64) {
n -= 64;
a.lo = a.hi;
a.hi = 0;
}
if (n != 0) {
u64 mask = ~(BIT128_U64_ALLBITS << n);
a.lo >>= n;
a.lo |= (a.hi&mask) << (64 - n);
a.hi >>= n;
}
return a;
#endif
}
i128 i128_mul(i128 a, i128 b) {
if (a.lo == 0 && a.hi == 0) {
return i128_from_u64(0);
} else if (b.lo == 0 && b.hi == 0) {
return i128_from_u64(0);
}
if (i128_eq(a, I128_ONE)) {
return b;
}
if (i128_eq(b, I128_ONE)) {
return a;
}
#if defined(MSVC_AMD64_INTRINSICS)
if (a.hi == 0 && b.hi == 0) {
a.lo = _mul128(a.lo, b.lo, &a.hi);
return a;
}
#endif
i128 res = {0};
i128 t = b;
for (u32 i = 0; i < 128; i++) {
if ((t.lo&1) != 0) {
res = i128_add(res, i128_shl(a, i));
}
t = i128_shr(t, 1);
}
return res;
}
void i128_divide(i128 a, i128 b, i128 *quo, i128 *rem) {
// TODO(bill): Which one is correct?!
#if 1
i128 s = i128_shr(b, 127);
b = i128_sub(i128_xor(b, s), s);
s = i128_shr(a, 127);
b = i128_sub(i128_xor(a, s), s);
u128 n, r = {0};
u128_divide(*cast(u128 *)&a, *cast(u128 *)&b, &n, &r);
i128 ni = *cast(i128 *)&n;
i128 ri = *cast(i128 *)&r;
if (quo) *quo = i128_sub(i128_xor(ni, s), s);
if (rem) *rem = i128_sub(i128_xor(ri, s), s);
#else
if (i128_eq(b, I128_ZERO)) {
if (quo) *quo = i128_from_u64(a.lo/b.lo);
if (rem) *rem = I128_ZERO;
} else {
i128 n = a;
i128 d = b;
i128 x = I128_ONE;
i128 r = I128_ZERO;
while (i128_ge(n, d) && ((i128_shr(d, 128-1).lo&1) == 0)) {
x = i128_shl(x, 1);
d = i128_shl(d, 1);
}
while (i128_ne(x, I128_ZERO)) {
if (i128_ge(n, d)) {
n = i128_sub(n, d);
r = i128_or(r, x);
}
x = i128_shr(x, 1);
d = i128_shr(d, 1);
}
if (quo) *quo = r;
if (rem) *rem = n;
}
#endif
}
i128 i128_quo(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, &res, nullptr);
return res;
}
i128 i128_mod(i128 a, i128 b) {
i128 res = {0};
i128_divide(a, b, nullptr, &res);
return res;
}
-6866
View File
File diff suppressed because it is too large Load Diff
+8346
View File
File diff suppressed because it is too large Load Diff
+57 -54
View File
@@ -1,6 +1,6 @@
// Optimizations for the IR code
void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
void ir_opt_add_operands(Array<irValue *> *ops, irInstr *i) {
switch (i->kind) {
case irInstr_Comment:
break;
@@ -39,7 +39,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
array_add(ops, i->If.cond);
break;
case irInstr_Return:
if (i->Return.value != NULL) {
if (i->Return.value != nullptr) {
array_add(ops, i->Return.value);
}
break;
@@ -48,7 +48,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
break;
case irInstr_Phi:
for_array(j, i->Phi.edges) {
array_add(ops, i->Phi.edges.e[j]);
array_add(ops, i->Phi.edges[j]);
}
break;
case irInstr_Unreachable:
@@ -80,6 +80,8 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
// break;
case irInstr_StartupRuntime:
break;
#if 0
case irInstr_BoundsCheck:
array_add(ops, i->BoundsCheck.index);
array_add(ops, i->BoundsCheck.len);
@@ -88,6 +90,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
array_add(ops, i->SliceBoundsCheck.low);
array_add(ops, i->SliceBoundsCheck.high);
break;
#endif
}
}
@@ -97,24 +100,24 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
void ir_opt_block_replace_pred(irBlock *b, irBlock *from, irBlock *to) {
for_array(i, b->preds) {
irBlock *pred = b->preds.e[i];
irBlock *pred = b->preds[i];
if (pred == from) {
b->preds.e[i] = to;
b->preds[i] = to;
}
}
}
void ir_opt_block_replace_succ(irBlock *b, irBlock *from, irBlock *to) {
for_array(i, b->succs) {
irBlock *succ = b->succs.e[i];
irBlock *succ = b->succs[i];
if (succ == from) {
b->succs.e[i] = to;
b->succs[i] = to;
}
}
}
bool ir_opt_block_has_phi(irBlock *b) {
return b->instrs.e[0]->Instr.kind == irInstr_Phi;
return b->instrs[0]->Instr.kind == irInstr_Phi;
}
@@ -126,10 +129,10 @@ bool ir_opt_block_has_phi(irBlock *b) {
irValueArray ir_get_block_phi_nodes(irBlock *b) {
irValueArray phis = {0};
Array<irValue *> ir_get_block_phi_nodes(irBlock *b) {
Array<irValue *> phis = {0};
for_array(i, b->instrs) {
irInstr *instr = &b->instrs.e[i]->Instr;
irInstr *instr = &b->instrs[i]->Instr;
if (instr->kind != irInstr_Phi) {
phis = b->instrs;
phis.count = i;
@@ -140,22 +143,22 @@ irValueArray ir_get_block_phi_nodes(irBlock *b) {
}
void ir_remove_pred(irBlock *b, irBlock *p) {
irValueArray phis = ir_get_block_phi_nodes(b);
Array<irValue *> phis = ir_get_block_phi_nodes(b);
isize i = 0;
for_array(j, b->preds) {
irBlock *pred = b->preds.e[j];
irBlock *pred = b->preds[j];
if (pred != p) {
b->preds.e[i] = b->preds.e[j];
b->preds[i] = b->preds[j];
for_array(k, phis) {
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
phi->edges.e[i] = phi->edges.e[j];
irInstrPhi *phi = &phis[k]->Instr.Phi;
phi->edges[i] = phi->edges[j];
}
i++;
}
}
b->preds.count = i;
for_array(k, phis) {
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
irInstrPhi *phi = &phis[k]->Instr.Phi;
phi->edges.count = i;
}
@@ -164,13 +167,13 @@ void ir_remove_pred(irBlock *b, irBlock *p) {
void ir_remove_dead_blocks(irProcedure *proc) {
isize j = 0;
for_array(i, proc->blocks) {
irBlock *b = proc->blocks.e[i];
if (b == NULL) {
irBlock *b = proc->blocks[i];
if (b == nullptr) {
continue;
}
// NOTE(bill): Swap order
b->index = j;
proc->blocks.e[j++] = b;
b->index = cast(i32)j;
proc->blocks[j++] = b;
}
proc->blocks.count = j;
}
@@ -180,7 +183,7 @@ void ir_mark_reachable(irBlock *b) {
isize const BLACK = -1;
b->index = BLACK;
for_array(i, b->succs) {
irBlock *succ = b->succs.e[i];
irBlock *succ = b->succs[i];
if (succ->index == WHITE) {
ir_mark_reachable(succ);
}
@@ -191,23 +194,23 @@ void ir_remove_unreachable_blocks(irProcedure *proc) {
isize const WHITE = 0;
isize const BLACK = -1;
for_array(i, proc->blocks) {
proc->blocks.e[i]->index = WHITE;
proc->blocks[i]->index = WHITE;
}
ir_mark_reachable(proc->blocks.e[0]);
ir_mark_reachable(proc->blocks[0]);
for_array(i, proc->blocks) {
irBlock *b = proc->blocks.e[i];
irBlock *b = proc->blocks[i];
if (b->index == WHITE) {
for_array(j, b->succs) {
irBlock *c = b->succs.e[j];
irBlock *c = b->succs[j];
if (c->index == BLACK) {
ir_remove_pred(c, b);
}
}
// NOTE(bill): Mark as empty but don't actually free it
// As it's been allocated with an arena
proc->blocks.e[i] = NULL;
proc->blocks[i] = nullptr;
}
}
ir_remove_dead_blocks(proc);
@@ -217,7 +220,7 @@ bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
if (a->succs.count != 1) {
return false;
}
irBlock *b = a->succs.e[0];
irBlock *b = a->succs[0];
if (b->preds.count != 1) {
return false;
}
@@ -228,21 +231,21 @@ bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
array_pop(&a->instrs); // Remove branch at end
for_array(i, b->instrs) {
array_add(&a->instrs, b->instrs.e[i]);
ir_set_instr_parent(b->instrs.e[i], a);
array_add(&a->instrs, b->instrs[i]);
ir_set_instr_parent(b->instrs[i], a);
}
array_clear(&a->succs);
for_array(i, b->succs) {
array_add(&a->succs, b->succs.e[i]);
array_add(&a->succs, b->succs[i]);
}
// Fix preds links
for_array(i, b->succs) {
ir_opt_block_replace_pred(b->succs.e[i], b, a);
ir_opt_block_replace_pred(b->succs[i], b, a);
}
proc->blocks.e[b->index] = NULL;
proc->blocks[b->index] = nullptr;
return true;
}
@@ -254,8 +257,8 @@ void ir_opt_blocks(irProcedure *proc) {
while (changed) {
changed = false;
for_array(i, proc->blocks) {
irBlock *b = proc->blocks.e[i];
if (b == NULL) {
irBlock *b = proc->blocks[i];
if (b == nullptr) {
continue;
}
GB_ASSERT_MSG(b->index == i, "%d, %td", b->index, i);
@@ -273,21 +276,21 @@ void ir_opt_blocks(irProcedure *proc) {
void ir_opt_build_referrers(irProcedure *proc) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
irValueArray ops = {0}; // NOTE(bill): Act as a buffer
array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
Array<irValue *> ops = {0}; // NOTE(bill): Act as a buffer
array_init(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
for_array(i, proc->blocks) {
irBlock *b = proc->blocks.e[i];
irBlock *b = proc->blocks[i];
for_array(j, b->instrs) {
irValue *instr = b->instrs.e[j];
irValue *instr = b->instrs[j];
array_clear(&ops);
ir_opt_add_operands(&ops, &instr->Instr);
for_array(k, ops) {
irValue *op = ops.e[k];
if (op == NULL) {
irValue *op = ops[k];
if (op == nullptr) {
continue;
}
irValueArray *refs = ir_value_referrers(op);
if (refs != NULL) {
Array<irValue *> *refs = ir_value_referrers(op);
if (refs != nullptr) {
array_add(refs, instr);
}
}
@@ -322,10 +325,10 @@ i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorde
preorder[i] = p;
p->dom.pre = i++;
lt->sdom[p->index] = p;
ir_lt_link(lt, NULL, p);
ir_lt_link(lt, nullptr, p);
for_array(index, p->succs) {
irBlock *q = p->succs.e[index];
if (lt->sdom[q->index] == NULL) {
irBlock *q = p->succs[index];
if (lt->sdom[q->index] == nullptr) {
lt->parent[q->index] = p;
i = ir_lt_depth_first_search(lt, q, i, preorder);
}
@@ -336,7 +339,7 @@ i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorde
irBlock *ir_lt_eval(irLTState *lt, irBlock *v) {
irBlock *u = v;
for (;
lt->ancestor[v->index] != NULL;
lt->ancestor[v->index] != nullptr;
v = lt->ancestor[v->index]) {
if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
u = v;
@@ -354,7 +357,7 @@ irDomPrePost ir_opt_number_dom_tree(irBlock *v, i32 pre, i32 post) {
v->dom.pre = pre++;
for_array(i, v->dom.children) {
result = ir_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
result = ir_opt_number_dom_tree(v->dom.children[i], result.pre, result.post);
}
v->dom.post = post++;
@@ -370,7 +373,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
isize n = proc->blocks.count;
i32 n = cast(i32)proc->blocks.count;
irBlock **buf = gb_alloc_array(proc->module->tmp_allocator, irBlock *, 5*n);
irLTState lt = {0};
@@ -381,7 +384,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
irBlock **preorder = &buf[3*n];
irBlock **buckets = &buf[4*n];
irBlock *root = proc->blocks.e[0];
irBlock *root = proc->blocks[0];
// Step 1 - number vertices
i32 pre_num = ir_lt_depth_first_search(&lt, root, 0, preorder);
@@ -403,7 +406,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
// Step 2 - Compute all sdoms
lt.sdom[w->index] = lt.parent[w->index];
for_array(pred_index, w->preds) {
irBlock *v = w->preds.e[pred_index];
irBlock *v = w->preds[pred_index];
irBlock *u = ir_lt_eval(&lt, v);
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
lt.sdom[w->index] = lt.sdom[u->index];
@@ -429,7 +432,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
for (isize i = 1; i < n; i++) {
irBlock *w = preorder[i];
if (w == root) {
w->dom.idom = NULL;
w->dom.idom = nullptr;
} else {
// Weird tree relationships here!
@@ -438,7 +441,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
}
// Calculate children relation as inverse of idom
if (w->dom.idom->dom.children.e == NULL) {
if (w->dom.idom->dom.children.data == nullptr) {
// TODO(bill): Is this good enough for memory allocations?
array_init(&w->dom.idom->dom.children, heap_allocator());
}
@@ -461,7 +464,7 @@ void ir_opt_tree(irGen *s) {
s->opt_called = true;
for_array(member_index, s->module.procs) {
irProcedure *proc = s->module.procs.e[member_index];
irProcedure *proc = s->module.procs[member_index];
if (proc->blocks.count == 0) { // Prototype/external procedure
continue;
}
-1589
View File
File diff suppressed because it is too large Load Diff
+1926
View File
File diff suppressed because it is too large Load Diff
-339
View File
@@ -1,339 +0,0 @@
#if defined(__cplusplus)
extern "C" {
#endif
#include "common.c"
#include "timings.c"
#include "build_settings.c"
#include "tokenizer.c"
#include "parser.c"
// #include "printer.c"
#include "checker.c"
// #include "ssa.c"
#include "ir.c"
#include "ir_opt.c"
#include "ir_print.c"
// #include "vm.c"
#if defined(GB_SYSTEM_WINDOWS)
// NOTE(bill): `name` is used in debugging and profiling modes
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
PROCESS_INFORMATION pi = {0};
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
gbTempArenaMemory tmp;
String16 cmd;
i32 exit_code = 0;
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_SHOW;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
if (CreateProcessW(NULL, cmd.text,
NULL, NULL, true, 0, NULL, NULL,
&start_info, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
// NOTE(bill): failed to create process
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
exit_code = -1;
}
gb_temp_arena_memory_end(tmp);
return exit_code;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
String cmd;
i32 exit_code = 0;
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
exit_code = system(&cmd_line[0]);
// pid_t pid = fork();
// int status = 0;
// if(pid == 0) {
// // in child, pid == 0.
// int ret = execvp(cmd.text, (char* const*) cmd.text);
// if(ret == -1) {
// gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
// // we're in the child, so returning won't do us any good -- just quit.
// exit(-1);
// }
// // unreachable
// abort();
// } else {
// // wait for child to finish, then we can continue cleanup
// int s = 0;
// waitpid(pid, &s, 0);
// status = WEXITSTATUS(s);
// }
// exit_code = status;
}
#endif
void print_usage_line(i32 indent, char *fmt, ...) {
while (indent --> 0) {
gb_printf_err("\t");
}
va_list va;
va_start(va, fmt);
gb_printf_err_va(fmt, va);
va_end(va);
gb_printf_err("\n");
}
void usage(char *argv0) {
print_usage_line(0, "%s is a tool for managing Odin source code", argv0);
print_usage_line(0, "Usage:");
print_usage_line(1, "%s command [arguments]", argv0);
print_usage_line(0, "Commands:");
print_usage_line(1, "build compile .odin file as executable");
print_usage_line(1, "build_dll compile .odin file as dll");
print_usage_line(1, "run compile and run .odin file");
print_usage_line(1, "version print version");
}
int main(int argc, char **argv) {
if (argc < 2) {
usage(argv[0]);
return 1;
}
Timings timings = {0};
timings_init(&timings, str_lit("Total Time"), 128);
// defer (timings_destroy(&timings));
init_string_buffer_memory();
init_scratch_memory(gb_megabytes(10));
init_global_error_collector();
#if 1
init_build_context();
init_universal_scope();
char *init_filename = NULL;
bool run_output = false;
String arg1 = make_string_c(argv[1]);
if (str_eq(arg1, str_lit("run"))) {
if (argc != 3) {
usage(argv[0]);
return 1;
}
init_filename = argv[2];
run_output = true;
} else if (str_eq(arg1, str_lit("build_dll"))) {
if (argc != 3) {
usage(argv[0]);
return 1;
}
init_filename = argv[2];
build_context.is_dll = true;
} else if (str_eq(arg1, str_lit("build"))) {
if (argc != 3) {
usage(argv[0]);
return 1;
}
init_filename = argv[2];
} else if (str_eq(arg1, str_lit("version"))) {
gb_printf("%s version %.*s", argv[0], LIT(build_context.ODIN_VERSION));
return 0;
} else {
usage(argv[0]);
return 1;
}
// TODO(bill): prevent compiling without a linker
timings_start_section(&timings, str_lit("parse files"));
Parser parser = {0};
if (!init_parser(&parser)) {
return 1;
}
// defer (destroy_parser(&parser));
if (parse_files(&parser, init_filename) != ParseFile_None) {
return 1;
}
#if 1
timings_start_section(&timings, str_lit("type check"));
Checker checker = {0};
init_checker(&checker, &parser, &build_context);
// defer (destroy_checker(&checker));
check_parsed_files(&checker);
#endif
#if 0
if (global_error_collector.count != 0) {
return 1;
}
if (checker.parser->total_token_count < 2) {
return 1;
}
if (!ssa_generate(&checker.info)) {
return 1;
}
#else
irGen ir_gen = {0};
if (!ir_gen_init(&ir_gen, &checker)) {
return 1;
}
// defer (ssa_gen_destroy(&ir_gen));
timings_start_section(&timings, str_lit("llvm ir gen"));
ir_gen_tree(&ir_gen);
timings_start_section(&timings, str_lit("llvm ir opt tree"));
ir_opt_tree(&ir_gen);
timings_start_section(&timings, str_lit("llvm ir print"));
print_llvm_ir(&ir_gen);
// prof_print_all();
#if 1
timings_start_section(&timings, str_lit("llvm-opt"));
char const *output_name = ir_gen.output_file.filename;
isize base_name_len = gb_path_extension(output_name)-1 - output_name;
String output = make_string(cast(u8 *)output_name, base_name_len);
i32 optimization_level = 0;
optimization_level = gb_clamp(optimization_level, 0, 3);
i32 exit_code = 0;
// For more passes arguments: http://llvm.org/docs/Passes.html
exit_code = system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
// "-dse "
// "-dce "
// "-S "
"",
LIT(build_context.ODIN_ROOT),
output_name, LIT(output));
if (exit_code != 0) {
return exit_code;
}
#if defined(GB_SYSTEM_WINDOWS)
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = system_exec_command_line_app("llvm-llc", false,
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(build_context.ODIN_ROOT),
LIT(output),
optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("msvc-link"));
gbString lib_str = gb_string_make(heap_allocator(), "");
// defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths.e[i];
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" \"%.*s\"", LIT(lib));
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
char *output_ext = "exe";
char *link_settings = "";
if (build_context.is_dll) {
output_ext = "dll";
link_settings = "/DLL";
} else {
link_settings = "/ENTRY:mainCRTStartup";
}
exit_code = system_exec_command_line_app("msvc-link", true,
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
"/defaultlib:libcmt "
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
" %.*s "
" %s "
"",
LIT(output), LIT(output), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
if (exit_code != 0) {
return exit_code;
}
// timings_print_all(&timings);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
}
#else
#error Implement build stuff for this platform
#endif
#endif
#endif
#endif
return 0;
}
#if defined(__cplusplus)
}
#endif
+794
View File
@@ -0,0 +1,794 @@
#define USE_CUSTOM_BACKEND 0
#define USE_THREADED_PARSER 1
// #define NO_ARRAY_BOUNDS_CHECK
#include "common.cpp"
#include "timings.cpp"
#include "build_settings.cpp"
#include "tokenizer.cpp"
#include "parser.cpp"
#include "docs.cpp"
#include "checker.cpp"
#include "ssa.cpp"
#include "ir.cpp"
#include "ir_opt.cpp"
#include "ir_print.cpp"
#if defined(GB_SYSTEM_WINDOWS)
// NOTE(bill): `name` is used in debugging and profiling modes
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
PROCESS_INFORMATION pi = {0};
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
gbTempArenaMemory tmp;
String16 cmd;
i32 exit_code = 0;
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_SHOW;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
// gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
if (CreateProcessW(nullptr, cmd.text,
nullptr, nullptr, true, 0, nullptr, nullptr,
&start_info, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
// NOTE(bill): failed to create process
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
exit_code = -1;
}
gb_temp_arena_memory_end(tmp);
return exit_code;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
String cmd;
i32 exit_code = 0;
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
exit_code = system(&cmd_line[0]);
// pid_t pid = fork();
// int status = 0;
// if(pid == 0) {
// // in child, pid == 0.
// int ret = execvp(cmd.text, (char* const*) cmd.text);
// if(ret == -1) {
// gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
// // we're in the child, so returning won't do us any good -- just quit.
// exit(-1);
// }
// // unreachable
// abort();
// } else {
// // wait for child to finish, then we can continue cleanup
// int s = 0;
// waitpid(pid, &s, 0);
// status = WEXITSTATUS(s);
// }
// exit_code = status;
return exit_code;
}
#endif
Array<String> setup_args(int argc, char **argv) {
Array<String> args = {};
gbAllocator a = heap_allocator();
int i;
#if defined(GB_SYSTEM_WINDOWS)
int wargc = 0;
wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
array_init(&args, a, wargc);
for (i = 0; i < wargc; i++) {
wchar_t *warg = wargv[i];
isize wlen = string16_len(warg);
String16 wstr = make_string16(warg, wlen);
String arg = string16_to_string(a, wstr);
if (arg.len > 0) {
array_add(&args, arg);
}
}
#else
array_init(&args, a, argc);
for (i = 0; i < argc; i++) {
String arg = make_string_c(argv[i]);
if (arg.len > 0) {
array_add(&args, arg);
}
}
#endif
return args;
}
void print_usage_line(i32 indent, char *fmt, ...) {
while (indent --> 0) {
gb_printf_err("\t");
}
va_list va;
va_start(va, fmt);
gb_printf_err_va(fmt, va);
va_end(va);
gb_printf_err("\n");
}
void usage(String argv0) {
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
print_usage_line(0, "Commands:");
print_usage_line(1, "build compile .odin file as executable");
print_usage_line(1, "build_dll compile .odin file as dll");
print_usage_line(1, "run compile and run .odin file");
print_usage_line(1, "docs generate documentation for a .odin file");
print_usage_line(1, "version print version");
}
enum BuildFlagKind {
BuildFlag_Invalid,
BuildFlag_OptimizationLevel,
BuildFlag_ShowTimings,
BuildFlag_ThreadCount,
BuildFlag_KeepTempFiles,
BuildFlag_COUNT,
};
enum BuildFlagParamKind {
BuildFlagParam_None,
BuildFlagParam_Boolean,
BuildFlagParam_Integer,
BuildFlagParam_Float,
BuildFlagParam_String,
BuildFlagParam_COUNT,
};
struct BuildFlag {
BuildFlagKind kind;
String name;
BuildFlagParamKind param_kind;
};
void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
BuildFlag flag = {kind, name, param_kind};
array_add(build_flags, flag);
}
bool parse_build_flags(Array<String> args) {
Array<BuildFlag> build_flags = {};
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
Array<String> flag_args = args;
flag_args.data += 3;
flag_args.count -= 3;
bool set_flags[BuildFlag_COUNT] = {};
bool bad_flags = false;
for_array(i, flag_args) {
String flag = flag_args[i];
if (flag[0] != '-') {
gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
continue;
}
String name = substring(flag, 1, flag.len);
isize end = 0;
for (; end < name.len; end++) {
if (name[end] == '=') break;
}
name = substring(name, 0, end);
String param = {};
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
bool found = false;
for_array(build_flag_index, build_flags) {
BuildFlag bf = build_flags[build_flag_index];
if (bf.name == name) {
found = true;
if (set_flags[bf.kind]) {
gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
bad_flags = true;
} else {
ExactValue value = {};
bool ok = false;
if (bf.param_kind == BuildFlagParam_None) {
if (param.len == 0) {
ok = true;
} else {
gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
bad_flags = true;
}
} else if (param.len == 0) {
gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
bad_flags = true;
} else {
ok = true;
switch (bf.param_kind) {
default: ok = false; break;
case BuildFlagParam_Boolean: {
if (param == "t") {
value = exact_value_bool(true);
} else if (param == "T") {
value = exact_value_bool(true);
} else if (param == "true") {
value = exact_value_bool(true);
} else if (param == "TRUE") {
value = exact_value_bool(true);
} else if (param == "1") {
value = exact_value_bool(true);
} else if (param == "f") {
value = exact_value_bool(false);
} else if (param == "F") {
value = exact_value_bool(false);
} else if (param == "false") {
value = exact_value_bool(false);
} else if (param == "FALSE") {
value = exact_value_bool(false);
} else if (param == "0") {
value = exact_value_bool(false);
} else {
gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
}
} break;
case BuildFlagParam_Integer:
value = exact_value_integer_from_string(param);
break;
case BuildFlagParam_Float:
value = exact_value_float_from_string(param);
break;
case BuildFlagParam_String:
value = exact_value_string(param);
break;
}
}
if (ok) {
switch (bf.param_kind) {
case BuildFlagParam_None:
if (value.kind != ExactValue_Invalid) {
gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Boolean:
if (value.kind != ExactValue_Bool) {
gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Integer:
if (value.kind != ExactValue_Integer) {
gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Float:
if (value.kind != ExactValue_Float) {
gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_String:
if (value.kind != ExactValue_String) {
gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
}
if (ok) switch (bf.kind) {
case BuildFlag_OptimizationLevel:
GB_ASSERT(value.kind == ExactValue_Integer);
build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
break;
case BuildFlag_ShowTimings:
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.show_timings = true;
break;
case BuildFlag_ThreadCount: {
GB_ASSERT(value.kind == ExactValue_Integer);
isize count = cast(isize)i128_to_i64(value.value_integer);
if (count <= 0) {
gb_printf_err("%.*s expected a positive non-zero number, got %.*s", LIT(name), LIT(param));
build_context.thread_count = 0;
} else {
build_context.thread_count = count;
}
} break;
case BuildFlag_KeepTempFiles:
GB_ASSERT(value.kind == ExactValue_Invalid);
build_context.keep_temp_files = true;
break;
}
}
set_flags[bf.kind] = ok;
}
break;
}
}
if (!found) {
gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
bad_flags = true;
}
}
return !bad_flags;
}
void show_timings(Checker *c, Timings *t) {
Parser *p = c->parser;
isize lines = p->total_line_count;
isize tokens = p->total_token_count;
isize files = p->files.count;
{
timings_print_all(t);
gb_printf("\n");
gb_printf("Total Lines - %td\n", lines);
gb_printf("Total Tokens - %td\n", tokens);
gb_printf("Total Files - %td\n", files);
gb_printf("\n");
}
{
TimeStamp ts = t->sections[0];
GB_ASSERT(ts.label == "parse files");
f64 parse_time = time_stamp_as_second(ts, t->freq);
gb_printf("Parse pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
gb_printf("\n");
}
{
f64 total_time = t->total_time_seconds;
gb_printf("Total pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
gb_printf("\n");
}
}
void remove_temp_files(String output_base) {
if (build_context.keep_temp_files) return;
Array<u8> data = {};
array_init_count(&data, heap_allocator(), output_base.len + 10);
defer (array_free(&data));
isize n = output_base.len;
gb_memcopy(data.data, output_base.text, n);
#define EXT_REMOVE(s) do { \
gb_memcopy(data.data+n, s, gb_size_of(s)); \
gb_file_remove(cast(char *)data.data); \
} while (0)
EXT_REMOVE(".ll");
EXT_REMOVE(".bc");
#if defined(GB_SYSTEM_WINDOWS)
EXT_REMOVE(".obj");
#else
EXT_REMOVE(".o");
#endif
#undef EXT_REMOVE
}
int main(int arg_count, char **arg_ptr) {
if (arg_count < 2) {
usage(make_string_c(arg_ptr[0]));
return 1;
}
Timings timings = {0};
timings_init(&timings, str_lit("Total Time"), 128);
defer (timings_destroy(&timings));
init_string_buffer_memory();
init_scratch_memory(gb_megabytes(10));
init_global_error_collector();
Array<String> args = setup_args(arg_count, arg_ptr);
#if 1
String init_filename = {};
bool run_output = false;
if (args[1] == "run") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
init_filename = args[2];
run_output = true;
} else if (args[1] == "build_dll") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
init_filename = args[2];
build_context.is_dll = true;
} else if (args[1] == "build") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
init_filename = args[2];
} else if (args[1] == "docs") {
if (args.count < 3) {
usage(args[0]);
return 1;
}
init_filename = args[2];
build_context.generate_docs = true;
#if 1
print_usage_line(0, "Documentation generation is not yet supported");
return 1;
#endif
} else if (args[1] == "version") {
gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
return 0;
} else {
usage(args[0]);
return 1;
}
if (!parse_build_flags(args)) {
return 1;
}
init_build_context();
if (build_context.word_size == 4) {
print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
return 1;
}
init_universal_scope();
// TODO(bill): prevent compiling without a linker
timings_start_section(&timings, str_lit("parse files"));
Parser parser = {0};
if (!init_parser(&parser)) {
return 1;
}
defer (destroy_parser(&parser));
if (parse_files(&parser, init_filename) != ParseFile_None) {
return 1;
}
if (build_context.generate_docs) {
generate_documentation(&parser);
return 0;
}
#if 1
timings_start_section(&timings, str_lit("type check"));
Checker checker = {0};
init_checker(&checker, &parser);
defer (destroy_checker(&checker));
check_parsed_files(&checker);
#endif
#if defined(USE_CUSTOM_BACKEND) && USE_CUSTOM_BACKEND
if (global_error_collector.count != 0) {
return 1;
}
if (checker.parser->total_token_count < 2) {
return 1;
}
if (!ssa_generate(&parser, &checker.info)) {
return 1;
}
#else
irGen ir_gen = {0};
if (!ir_gen_init(&ir_gen, &checker)) {
return 1;
}
defer (ir_gen_destroy(&ir_gen));
timings_start_section(&timings, str_lit("llvm ir gen"));
ir_gen_tree(&ir_gen);
timings_start_section(&timings, str_lit("llvm ir opt tree"));
ir_opt_tree(&ir_gen);
timings_start_section(&timings, str_lit("llvm ir print"));
print_llvm_ir(&ir_gen);
// prof_print_all();
#if 1
timings_start_section(&timings, str_lit("llvm-opt"));
String output_name = ir_gen.output_name;
String output_base = ir_gen.output_base;
int base_name_len = cast(int)output_base.len;
build_context.optimization_level = gb_clamp(build_context.optimization_level, 0, 3);
i32 exit_code = 0;
#if defined(GB_SYSTEM_WINDOWS)
// For more passes arguments: http://llvm.org/docs/Passes.html
exit_code = system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc %.*s "
"-mem2reg "
"-memcpyopt "
"-die "
"",
LIT(build_context.ODIN_ROOT),
LIT(output_base), LIT(output_base),
LIT(build_context.opt_flags));
if (exit_code != 0) {
return exit_code;
}
#else
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
exit_code = system_exec_command_line_app("llvm-opt", false,
"opt \"%.*s\".ll -o \"%.*s\".bc %.*s "
"-mem2reg "
"-memcpyopt "
"-die "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `macosx_version_min` param passed to `llc`
"-mtriple=x86_64-apple-macosx10.8 "
#endif
"",
LIT(output_base), LIT(output_base),
LIT(build_context.opt_flags));
if (exit_code != 0) {
return exit_code;
}
#endif
#if defined(GB_SYSTEM_WINDOWS)
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = system_exec_command_line_app("llvm-llc", false,
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(build_context.ODIN_ROOT),
LIT(output_base),
build_context.optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("msvc-link"));
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths[i];
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" \"%.*s\"", LIT(lib));
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
char *output_ext = "exe";
char *link_settings = "";
if (build_context.is_dll) {
output_ext = "dll";
link_settings = "/DLL";
} else {
link_settings = "/ENTRY:mainCRTStartup";
}
exit_code = system_exec_command_line_app("msvc-link", true,
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
"/defaultlib:libcmt "
// "/nodefaultlib "
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
" %.*s "
" %s "
"",
LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
if (exit_code != 0) {
return exit_code;
}
if (build_context.show_timings) {
show_timings(&checker, &timings);
}
remove_temp_files(output_base);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
}
#else
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = system_exec_command_line_app("llc", false,
"llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(output_base),
build_context.optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("ld-link"));
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths[i];
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
#if defined(GB_SYSTEM_OSX)
isize len;
if(lib.len > 2 && lib[0] == '-' && lib[1] == 'f') {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
} else {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
}
#else
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
#endif
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
// Unlike the Win32 linker code, the output_ext includes the dot, because
// typically executable files on *NIX systems don't have extensions.
char *output_ext = "";
char *link_settings = "";
char *linker;
if (build_context.is_dll) {
// Shared libraries are .dylib on MacOS and .so on Linux.
// TODO(zangent): Is that statement entirely truthful?
#if defined(GB_SYSTEM_OSX)
output_ext = ".dylib";
#else
output_ext = ".so";
#endif
link_settings = "-shared";
} else {
// TODO: Do I need anything here?
link_settings = "";
}
#if defined(GB_SYSTEM_OSX)
linker = "ld";
#else
// TODO(zangent): Figure out how to make ld work on Linux.
// It probably has to do with including the entire CRT, but
// that's quite a complicated issue to solve while remaining distro-agnostic.
// Clang can figure out linker flags for us, and that's good enough _for now_.
linker = "clang -Wno-unused-command-line-argument";
#endif
exit_code = system_exec_command_line_app("ld-link", true,
"%s \"%.*s\".o -o \"%.*s%s\" %s "
"-lc -lm "
" %.*s "
" %s "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `mtriple` param passed to `opt`
" -macosx_version_min 10.8.0 "
// This points the linker to where the entry point is
" -e _main "
#endif
, linker, LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
if (exit_code != 0) {
return exit_code;
}
if (build_context.show_timings) {
show_timings(&checker, &timings);
}
remove_temp_files(output_base);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
}
#endif
#endif
#endif
#endif
return 0;
}
-361
View File
@@ -1,361 +0,0 @@
/*
Example of usage:
#define MAP_TYPE String
#define MAP_PROC map_string_
#define MAP_NAME MapString
#include "map.c"
*/
// A `Map` is an unordered hash table which can allow for a key to point to multiple values
// with the use of the `multi_*` procedures.
// TODO(bill): I should probably allow the `multi_*` stuff to be #ifdefed out
#ifndef MAP_UTIL_STUFF
#define MAP_UTIL_STUFF
// NOTE(bill): This util stuff is the same for every `Map`
typedef struct MapFindResult {
isize hash_index;
isize entry_prev;
isize entry_index;
} MapFindResult;
typedef enum HashKeyKind {
HashKey_Default,
HashKey_String,
HashKey_Pointer,
} HashKeyKind;
typedef struct HashKey {
HashKeyKind kind;
u64 key;
union {
String string; // if String, s.len > 0
void * ptr;
};
} HashKey;
gb_inline HashKey hashing_proc(void const *data, isize len) {
HashKey h = {HashKey_Default};
h.kind = HashKey_Default;
// h.key = gb_murmur64(data, len);
h.key = gb_fnv64a(data, len);
return h;
}
gb_inline HashKey hash_string(String s) {
HashKey h = hashing_proc(s.text, s.len);
h.kind = HashKey_String;
h.string = s;
return h;
}
gb_inline HashKey hash_pointer(void *ptr) {
HashKey h = {HashKey_Default};
h.key = cast(u64)cast(uintptr)ptr;
h.ptr = ptr;
h.kind = HashKey_Default;
return h;
}
bool hash_key_equal(HashKey a, HashKey b) {
if (a.key == b.key) {
// NOTE(bill): If two string's hashes collide, compare the strings themselves
if (a.kind == HashKey_String) {
if (b.kind == HashKey_String) {
return str_eq(a.string, b.string);
}
return false;
}
return true;
}
return false;
}
#endif
#define _J2_IND(a, b) a##b
#define _J2(a, b) _J2_IND(a, b)
/*
MAP_TYPE - Entry type
MAP_PROC - Function prefix (e.g. entity_map_)
MAP_NAME - Name of Map (e.g. EntityMap)
*/
#define MAP_ENTRY _J2(MAP_NAME,Entry)
typedef struct MAP_ENTRY {
HashKey key;
isize next;
MAP_TYPE value;
} MAP_ENTRY;
typedef struct MAP_NAME {
Array(isize) hashes;
Array(MAP_ENTRY) entries;
} MAP_NAME;
void _J2(MAP_PROC,init) (MAP_NAME *h, gbAllocator a);
void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity);
void _J2(MAP_PROC,destroy) (MAP_NAME *h);
MAP_TYPE *_J2(MAP_PROC,get) (MAP_NAME *h, HashKey key);
void _J2(MAP_PROC,set) (MAP_NAME *h, HashKey key, MAP_TYPE value);
void _J2(MAP_PROC,remove) (MAP_NAME *h, HashKey key);
void _J2(MAP_PROC,clear) (MAP_NAME *h);
void _J2(MAP_PROC,grow) (MAP_NAME *h);
void _J2(MAP_PROC,rehash) (MAP_NAME *h, isize new_count);
// Mutlivalued map procedure
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key);
MAP_ENTRY *_J2(MAP_PROC,multi_find_next) (MAP_NAME *h, MAP_ENTRY *e);
isize _J2(MAP_PROC,multi_count) (MAP_NAME *h, HashKey key);
void _J2(MAP_PROC,multi_get_all) (MAP_NAME *h, HashKey key, MAP_TYPE *items);
void _J2(MAP_PROC,multi_insert) (MAP_NAME *h, HashKey key, MAP_TYPE value);
void _J2(MAP_PROC,multi_remove) (MAP_NAME *h, HashKey key, MAP_ENTRY *e);
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key);
gb_inline void _J2(MAP_PROC,init)(MAP_NAME *h, gbAllocator a) {
array_init(&h->hashes, a);
array_init(&h->entries, a);
}
gb_inline void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity) {
array_init_reserve(&h->hashes, a, capacity);
array_init_reserve(&h->entries, a, capacity);
}
gb_inline void _J2(MAP_PROC,destroy)(MAP_NAME *h) {
array_free(&h->entries);
array_free(&h->hashes);
}
gb_internal isize _J2(MAP_PROC,_add_entry)(MAP_NAME *h, HashKey key) {
MAP_ENTRY e = {0};
e.key = key;
e.next = -1;
array_add(&h->entries, e);
return h->entries.count-1;
}
gb_internal MapFindResult _J2(MAP_PROC,_find)(MAP_NAME *h, HashKey key) {
MapFindResult fr = {-1, -1, -1};
if (h->hashes.count > 0) {
fr.hash_index = key.key % h->hashes.count;
fr.entry_index = h->hashes.e[fr.hash_index];
while (fr.entry_index >= 0) {
if (hash_key_equal(h->entries.e[fr.entry_index].key, key)) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = h->entries.e[fr.entry_index].next;
}
}
return fr;
}
gb_internal MapFindResult _J2(MAP_PROC,_find_from_entry)(MAP_NAME *h, MAP_ENTRY *e) {
MapFindResult fr = {-1, -1, -1};
if (h->hashes.count > 0) {
fr.hash_index = e->key.key % h->hashes.count;
fr.entry_index = h->hashes.e[fr.hash_index];
while (fr.entry_index >= 0) {
if (&h->entries.e[fr.entry_index] == e) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = h->entries.e[fr.entry_index].next;
}
}
return fr;
}
gb_internal b32 _J2(MAP_PROC,_full)(MAP_NAME *h) {
return 0.75f * h->hashes.count <= h->entries.count;
}
gb_inline void _J2(MAP_PROC,grow)(MAP_NAME *h) {
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
_J2(MAP_PROC,rehash)(h, new_count);
}
void _J2(MAP_PROC,rehash)(MAP_NAME *h, isize new_count) {
isize i, j;
MAP_NAME nh = {0};
_J2(MAP_PROC,init)(&nh, h->hashes.allocator);
array_resize(&nh.hashes, new_count);
array_reserve(&nh.entries, h->entries.count);
for (i = 0; i < new_count; i++) {
nh.hashes.e[i] = -1;
}
for (i = 0; i < h->entries.count; i++) {
MAP_ENTRY *e = &h->entries.e[i];
MapFindResult fr;
if (nh.hashes.count == 0) {
_J2(MAP_PROC,grow)(&nh);
}
fr = _J2(MAP_PROC,_find)(&nh, e->key);
j = _J2(MAP_PROC,_add_entry)(&nh, e->key);
if (fr.entry_prev < 0) {
nh.hashes.e[fr.hash_index] = j;
} else {
nh.entries.e[fr.entry_prev].next = j;
}
nh.entries.e[j].next = fr.entry_index;
nh.entries.e[j].value = e->value;
if (_J2(MAP_PROC,_full)(&nh)) {
_J2(MAP_PROC,grow)(&nh);
}
}
_J2(MAP_PROC,destroy)(h);
*h = nh;
}
gb_inline MAP_TYPE *_J2(MAP_PROC,get)(MAP_NAME *h, HashKey key) {
isize index = _J2(MAP_PROC,_find)(h, key).entry_index;
if (index >= 0) {
return &h->entries.e[index].value;
}
return NULL;
}
void _J2(MAP_PROC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
isize index;
MapFindResult fr;
if (h->hashes.count == 0)
_J2(MAP_PROC,grow)(h);
fr = _J2(MAP_PROC,_find)(h, key);
if (fr.entry_index >= 0) {
index = fr.entry_index;
} else {
index = _J2(MAP_PROC,_add_entry)(h, key);
if (fr.entry_prev >= 0) {
h->entries.e[fr.entry_prev].next = index;
} else {
h->hashes.e[fr.hash_index] = index;
}
}
h->entries.e[index].value = value;
if (_J2(MAP_PROC,_full)(h)) {
_J2(MAP_PROC,grow)(h);
}
}
void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
MapFindResult last;
if (fr.entry_prev < 0) {
h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
} else {
h->entries.e[fr.entry_prev].next = h->entries.e[fr.entry_index].next;
}
if (fr.entry_index == h->entries.count-1) {
array_pop(&h->entries);
return;
}
h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
if (last.entry_prev >= 0) {
h->entries.e[last.entry_prev].next = fr.entry_index;
} else {
h->hashes.e[last.hash_index] = fr.entry_index;
}
}
void _J2(MAP_PROC,remove)(MAP_NAME *h, HashKey key) {
MapFindResult fr = _J2(MAP_PROC,_find)(h, key);
if (fr.entry_index >= 0) {
_J2(MAP_PROC,_erase)(h, fr);
}
}
gb_inline void _J2(MAP_PROC,clear)(MAP_NAME *h) {
array_clear(&h->hashes);
array_clear(&h->entries);
}
#if 1
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key) {
isize i = _J2(MAP_PROC,_find)(h, key).entry_index;
if (i < 0) {
return NULL;
}
return &h->entries.e[i];
}
MAP_ENTRY *_J2(MAP_PROC,multi_find_next)(MAP_NAME *h, MAP_ENTRY *e) {
isize i = e->next;
while (i >= 0) {
if (hash_key_equal(h->entries.e[i].key, e->key)) {
return &h->entries.e[i];
}
i = h->entries.e[i].next;
}
return NULL;
}
isize _J2(MAP_PROC,multi_count)(MAP_NAME *h, HashKey key) {
isize count = 0;
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
while (e != NULL) {
count++;
e = _J2(MAP_PROC,multi_find_next)(h, e);
}
return count;
}
void _J2(MAP_PROC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
isize i = 0;
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
while (e != NULL) {
items[i++] = e->value;
e = _J2(MAP_PROC,multi_find_next)(h, e);
}
}
void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
MapFindResult fr;
isize i;
if (h->hashes.count == 0) {
_J2(MAP_PROC,grow)(h);
}
// Make
fr = _J2(MAP_PROC,_find)(h, key);
i = _J2(MAP_PROC,_add_entry)(h, key);
if (fr.entry_prev < 0) {
h->hashes.e[fr.hash_index] = i;
} else {
h->entries.e[fr.entry_prev].next = i;
}
h->entries.e[i].next = fr.entry_index;
h->entries.e[i].value = value;
// Grow if needed
if (_J2(MAP_PROC,_full)(h)) {
_J2(MAP_PROC,grow)(h);
}
}
void _J2(MAP_PROC,multi_remove)(MAP_NAME *h, HashKey key, MAP_ENTRY *e) {
MapFindResult fr = _J2(MAP_PROC,_find_from_entry)(h, e);
if (fr.entry_index >= 0) {
_J2(MAP_PROC,_erase)(h, fr);
}
}
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key) {
while (_J2(MAP_PROC,get)(h, key) != NULL) {
_J2(MAP_PROC,remove)(h, key);
}
}
#endif
#undef _J2
#undef MAP_TYPE
#undef MAP_PROC
#undef MAP_NAME
#undef MAP_ENTRY
+381
View File
@@ -0,0 +1,381 @@
// A `Map` is an unordered hash table which can allow for a key to point to multiple values
// with the use of the `multi_*` procedures.
// TODO(bill): I should probably allow the `multi_map_*` stuff to be #ifdefed out
#ifndef MAP_UTIL_STUFF
#define MAP_UTIL_STUFF
// NOTE(bill): This util stuff is the same for every `Map`
struct MapFindResult {
isize hash_index;
isize entry_prev;
isize entry_index;
};
enum HashKeyKind {
HashKey_Default,
HashKey_String,
HashKey_Ptr,
HashKey_PtrAndId,
};
struct PtrAndId {
void *ptr;
u32 id;
};
struct HashKey {
HashKeyKind kind;
// u128 key;
u64 key;
union {
String string; // if String, s.len > 0
void * ptr;
PtrAndId ptr_and_id;
};
};
gb_inline HashKey hashing_proc(void const *data, isize len) {
HashKey h = {HashKey_Default};
h.kind = HashKey_Default;
// h.key = u128_from_u64(gb_fnv64a(data, len));
h.key = gb_fnv64a(data, len);
return h;
}
gb_inline HashKey hash_string(String s) {
HashKey h = hashing_proc(s.text, s.len);
h.kind = HashKey_String;
h.string = s;
return h;
}
gb_inline HashKey hash_pointer(void *ptr) {
HashKey h = {HashKey_Ptr};
h.key = cast(u64)cast(uintptr)ptr;
h.ptr = ptr;
return h;
}
gb_inline HashKey hash_ptr_and_id(void *ptr, u32 id) {
HashKey h = {HashKey_PtrAndId};
h.key = cast(u64)cast(uintptr)ptr;
h.ptr_and_id.ptr = ptr;
h.ptr_and_id.id = id;
return h;
}
bool hash_key_equal(HashKey a, HashKey b) {
if (a.key == b.key) {
// NOTE(bill): If two string's hashes collide, compare the strings themselves
if (a.kind == HashKey_String) {
if (b.kind == HashKey_String) {
return a.string == b.string;
}
return false;
} else if (a.kind == HashKey_PtrAndId) {
if (b.kind == HashKey_PtrAndId) {
return a.ptr_and_id.id == b.ptr_and_id.id;
}
return false;
}
return true;
}
return false;
}
bool operator==(HashKey a, HashKey b) { return hash_key_equal(a, b); }
bool operator!=(HashKey a, HashKey b) { return !hash_key_equal(a, b); }
#endif
template <typename T>
struct MapEntry {
HashKey key;
isize next;
T value;
};
template <typename T>
struct Map {
Array<isize> hashes;
Array<MapEntry<T> > entries;
};
template <typename T> void map_init (Map<T> *h, gbAllocator a);
template <typename T> void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity);
template <typename T> void map_destroy (Map<T> *h);
template <typename T> T * map_get (Map<T> *h, HashKey key);
template <typename T> void map_set (Map<T> *h, HashKey key, T const &value);
template <typename T> void map_remove (Map<T> *h, HashKey key);
template <typename T> void map_clear (Map<T> *h);
template <typename T> void map_grow (Map<T> *h);
template <typename T> void map_rehash (Map<T> *h, isize new_count);
// Mutlivalued map procedure
template <typename T> MapEntry<T> * multi_map_find_first(Map<T> *h, HashKey key);
template <typename T> MapEntry<T> * multi_map_find_next (Map<T> *h, MapEntry<T> *e);
template <typename T> isize multi_map_count (Map<T> *h, HashKey key);
template <typename T> void multi_map_get_all (Map<T> *h, HashKey key, T *items);
template <typename T> void multi_map_insert (Map<T> *h, HashKey key, T const &value);
template <typename T> void multi_map_remove (Map<T> *h, HashKey key, MapEntry<T> *e);
template <typename T> void multi_map_remove_all(Map<T> *h, HashKey key);
template <typename T>
gb_inline void map_init(Map<T> *h, gbAllocator a) {
array_init(&h->hashes, a);
array_init(&h->entries, a);
}
template <typename T>
gb_inline void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity) {
array_init(&h->hashes, a, capacity);
array_init(&h->entries, a, capacity);
}
template <typename T>
gb_inline void map_destroy(Map<T> *h) {
array_free(&h->entries);
array_free(&h->hashes);
}
template <typename T>
gb_internal isize map__add_entry(Map<T> *h, HashKey key) {
MapEntry<T> e = {};
e.key = key;
e.next = -1;
array_add(&h->entries, e);
return h->entries.count-1;
}
template <typename T>
gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
MapFindResult fr = {-1, -1, -1};
if (h->hashes.count > 0) {
// fr.hash_index = u128_to_i64(key.key % u128_from_i64(h->hashes.count));
fr.hash_index = key.key % h->hashes.count;
fr.entry_index = h->hashes[fr.hash_index];
while (fr.entry_index >= 0) {
if (hash_key_equal(h->entries[fr.entry_index].key, key)) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = h->entries[fr.entry_index].next;
}
}
return fr;
}
template <typename T>
gb_internal MapFindResult map__find_from_entry(Map<T> *h, MapEntry<T> *e) {
MapFindResult fr = {-1, -1, -1};
if (h->hashes.count > 0) {
fr.hash_index = e->key.key % h->hashes.count;
fr.entry_index = h->hashes[fr.hash_index];
while (fr.entry_index >= 0) {
if (&h->entries[fr.entry_index] == e) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = h->entries[fr.entry_index].next;
}
}
return fr;
}
template <typename T>
gb_internal b32 map__full(Map<T> *h) {
return 0.75f * h->hashes.count <= h->entries.count;
}
template <typename T>
gb_inline void map_grow(Map<T> *h) {
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
map_rehash(h, new_count);
}
template <typename T>
void map_rehash(Map<T> *h, isize new_count) {
isize i, j;
Map<T> nh = {};
map_init(&nh, h->hashes.allocator);
array_resize(&nh.hashes, new_count);
array_reserve(&nh.entries, h->entries.count);
for (i = 0; i < new_count; i++) {
nh.hashes[i] = -1;
}
for (i = 0; i < h->entries.count; i++) {
MapEntry<T> *e = &h->entries[i];
MapFindResult fr;
if (nh.hashes.count == 0) {
map_grow(&nh);
}
fr = map__find(&nh, e->key);
j = map__add_entry(&nh, e->key);
if (fr.entry_prev < 0) {
nh.hashes[fr.hash_index] = j;
} else {
nh.entries[fr.entry_prev].next = j;
}
nh.entries[j].next = fr.entry_index;
nh.entries[j].value = e->value;
if (map__full(&nh)) {
map_grow(&nh);
}
}
map_destroy(h);
*h = nh;
}
template <typename T>
gb_inline T *map_get(Map<T> *h, HashKey key) {
isize index = map__find(h, key).entry_index;
if (index >= 0) {
return &h->entries[index].value;
}
return nullptr;
}
template <typename T>
void map_set(Map<T> *h, HashKey key, T const &value) {
isize index;
MapFindResult fr;
if (h->hashes.count == 0)
map_grow(h);
fr = map__find(h, key);
if (fr.entry_index >= 0) {
index = fr.entry_index;
} else {
index = map__add_entry(h, key);
if (fr.entry_prev >= 0) {
h->entries[fr.entry_prev].next = index;
} else {
h->hashes[fr.hash_index] = index;
}
}
h->entries[index].value = value;
if (map__full(h)) {
map_grow(h);
}
}
template <typename T>
void map__erase(Map<T> *h, MapFindResult fr) {
MapFindResult last;
if (fr.entry_prev < 0) {
h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
} else {
h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
}
if (fr.entry_index == h->entries.count-1) {
array_pop(&h->entries);
return;
}
h->entries[fr.entry_index] = h->entries[h->entries.count-1];
last = map__find(h, h->entries[fr.entry_index].key);
if (last.entry_prev >= 0) {
h->entries[last.entry_prev].next = fr.entry_index;
} else {
h->hashes[last.hash_index] = fr.entry_index;
}
}
template <typename T>
void map_remove(Map<T> *h, HashKey key) {
MapFindResult fr = map__find(h, key);
if (fr.entry_index >= 0) {
map__erase(h, fr);
}
}
template <typename T>
gb_inline void map_clear(Map<T> *h) {
array_clear(&h->hashes);
array_clear(&h->entries);
}
#if 1
template <typename T>
MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
isize i = map__find(h, key).entry_index;
if (i < 0) {
return nullptr;
}
return &h->entries[i];
}
template <typename T>
MapEntry<T> *multi_map_find_next(Map<T> *h, MapEntry<T> *e) {
isize i = e->next;
while (i >= 0) {
if (hash_key_equal(h->entries[i].key, e->key)) {
return &h->entries[i];
}
i = h->entries[i].next;
}
return nullptr;
}
template <typename T>
isize multi_map_count(Map<T> *h, HashKey key) {
isize count = 0;
MapEntry<T> *e = multi_map_find_first(h, key);
while (e != nullptr) {
count++;
e = multi_map_find_next(h, e);
}
return count;
}
template <typename T>
void multi_map_get_all(Map<T> *h, HashKey key, T *items) {
isize i = 0;
MapEntry<T> *e = multi_map_find_first(h, key);
while (e != nullptr) {
items[i++] = e->value;
e = multi_map_find_next(h, e);
}
}
template <typename T>
void multi_map_insert(Map<T> *h, HashKey key, T const &value) {
MapFindResult fr;
isize i;
if (h->hashes.count == 0) {
map_grow(h);
}
// Make
fr = map__find(h, key);
i = map__add_entry(h, key);
if (fr.entry_prev < 0) {
h->hashes[fr.hash_index] = i;
} else {
h->entries[fr.entry_prev].next = i;
}
h->entries[i].next = fr.entry_index;
h->entries[i].value = value;
// Grow if needed
if (map__full(h)) {
map_grow(h);
}
}
template <typename T>
void multi_map_remove(Map<T> *h, HashKey key, MapEntry<T> *e) {
MapFindResult fr = map__find_from_entry(h, e);
if (fr.entry_index >= 0) {
map__erase(h, fr);
}
}
template <typename T>
void multi_map_remove_all(Map<T> *h, HashKey key) {
while (map_get(h, key) != nullptr) {
map_remove(h, key);
}
}
#endif
+225
View File
@@ -0,0 +1,225 @@
//-----------------------------------------------------------------------------
// MurmurHash3 was written by Austin Appleby, and is placed in the public
// domain. The author hereby disclaims copyright to this source code.
// Note - The x86 and x64 versions do _not_ produce the same results, as the
// algorithms are optimized for their respective platforms. You can still
// compile and run any of them on any platform, but your performance with the
// non-native version will be less than optimal.
#if defined(_MSC_VER)
#define ROTL32(x,y) _rotl(x,y)
#define ROTL64(x,y) _rotl64(x,y)
#else
gb_inline u32 rotl32(u32 x, i8 r) {
return (x << r) | (x >> (32-r));
}
gb_inline u64 rotl64(u64 x, i8 r) {
return (x << r) | (x >> (64-r));
}
#define ROTL32(x,y) rotl32(x,y)
#define ROTL64(x,y) rotl64(x,y)
#endif
gb_inline u32 fmix32(u32 h) {
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
gb_inline u64 fmix64(u64 k) {
k ^= k >> 33;
k *= 0xff51afd7ed558ccdULL;
k ^= k >> 33;
k *= 0xc4ceb9fe1a85ec53ULL;
k ^= k >> 33;
return k;
}
gb_inline u32 mm3_getblock32(u32 const *p, isize i) {
return p[i];
}
gb_inline u64 mm3_getblock64(u64 const *p, isize i) {
return p[i];
}
void MurmurHash3_x64_128(void const *key, isize len, u32 seed, void *out) {
u8 const * data = cast(u8 const *)key;
isize nblocks = len / 16;
u64 h1 = seed;
u64 h2 = seed;
u64 const c1 = 0x87c37b91114253d5ULL;
u64 const c2 = 0x4cf5ad432745937fULL;
u64 const * blocks = cast(u64 const *)data;
for (isize i = 0; i < nblocks; i++) {
u64 k1 = mm3_getblock64(blocks, i*2 + 0);
u64 k2 = mm3_getblock64(blocks, i*2 + 1);
k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1;
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
}
u8 const * tail = cast(u8 const *)(data + nblocks*16);
u64 k1 = 0;
u64 k2 = 0;
switch(len & 15) {
case 15: k2 ^= ((u64)tail[14]) << 48;
case 14: k2 ^= ((u64)tail[13]) << 40;
case 13: k2 ^= ((u64)tail[12]) << 32;
case 12: k2 ^= ((u64)tail[11]) << 24;
case 11: k2 ^= ((u64)tail[10]) << 16;
case 10: k2 ^= ((u64)tail[ 9]) << 8;
case 9: k2 ^= ((u64)tail[ 8]) << 0;
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
case 8: k1 ^= ((u64)tail[ 7]) << 56;
case 7: k1 ^= ((u64)tail[ 6]) << 48;
case 6: k1 ^= ((u64)tail[ 5]) << 40;
case 5: k1 ^= ((u64)tail[ 4]) << 32;
case 4: k1 ^= ((u64)tail[ 3]) << 24;
case 3: k1 ^= ((u64)tail[ 2]) << 16;
case 2: k1 ^= ((u64)tail[ 1]) << 8;
case 1: k1 ^= ((u64)tail[ 0]) << 0;
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
}
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
((u64 *)out)[0] = h1;
((u64 *)out)[1] = h2;
}
void MurmurHash3_x86_128(void const *key, isize len, u32 seed, void *out) {
u8 const * data = cast(u8 * const)key;
isize nblocks = len / 16;
u32 h1 = seed;
u32 h2 = seed;
u32 h3 = seed;
u32 h4 = seed;
u32 const c1 = 0x239b961b;
u32 const c2 = 0xab0e9789;
u32 const c3 = 0x38b34ae5;
u32 const c4 = 0xa1e38b93;
//----------
// body
u32 const * blocks = cast(u32 const *)(data + nblocks*16);
for (isize i = -nblocks; i != 0; i++) {
u32 k1 = mm3_getblock32(blocks, i*4 + 0);
u32 k2 = mm3_getblock32(blocks, i*4 + 1);
u32 k3 = mm3_getblock32(blocks, i*4 + 2);
u32 k4 = mm3_getblock32(blocks, i*4 + 3);
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
}
//----------
// tail
u8 const * tail = cast(u8 const *)(data + nblocks*16);
u32 k1 = 0;
u32 k2 = 0;
u32 k3 = 0;
u32 k4 = 0;
switch(len & 15) {
case 15: k4 ^= tail[14] << 16;
case 14: k4 ^= tail[13] << 8;
case 13: k4 ^= tail[12] << 0;
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
case 12: k3 ^= tail[11] << 24;
case 11: k3 ^= tail[10] << 16;
case 10: k3 ^= tail[ 9] << 8;
case 9: k3 ^= tail[ 8] << 0;
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
case 8: k2 ^= tail[ 7] << 24;
case 7: k2 ^= tail[ 6] << 16;
case 6: k2 ^= tail[ 5] << 8;
case 5: k2 ^= tail[ 4] << 0;
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
case 4: k1 ^= tail[ 3] << 24;
case 3: k1 ^= tail[ 2] << 16;
case 2: k1 ^= tail[ 1] << 8;
case 1: k1 ^= tail[ 0] << 0;
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
};
//----------
// finalization
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
((u32 *)out)[0] = h1;
((u32 *)out)[1] = h2;
((u32 *)out)[2] = h3;
((u32 *)out)[3] = h4;
}
gb_inline u128 MurmurHash3_128(void const *key, isize len, u32 seed) {
u128 res;
#if defined(GB_ARCH_64_BIT)
MurmurHash3_x64_128(key, len, seed, &res);
#else
MurmurHash3_x86_128(key, len, seed, &res);
#endif
return res;
}
-1305
View File
File diff suppressed because it is too large Load Diff
-3858
View File
File diff suppressed because it is too large Load Diff
+5168
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -6,7 +6,7 @@ gb_inline void print_indent(isize indent) {
}
void print_ast(AstNode *node, isize indent) {
if (node == NULL)
if (node == nullptr)
return;
switch (node->kind) {
+2569
View File
File diff suppressed because it is too large Load Diff
+275
View File
@@ -0,0 +1,275 @@
#define SSA_OPS \
SSA_OP(Invalid)\
\
SSA_OP(Unknown)\
\
SSA_OP(Comment) /* Does nothing */\
\
SSA_OP(SP) /* Stack Pointer */\
SSA_OP(SB) /* Stack Base */\
\
SSA_OP(Local)\
SSA_OP(Global)\
SSA_OP(Proc)\
\
SSA_OP(Load)\
SSA_OP(Store)\
SSA_OP(Move)\
SSA_OP(LoadReg)\
SSA_OP(StoreReg)\
SSA_OP(Zero) /* Zero initialize */\
\
SSA_OP(ArrayIndex) /* Index for a fixed array */\
SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
SSA_OP(PtrOffset)\
SSA_OP(ValueIndex) /* Extract for a value from a register */\
\
SSA_OP(Phi)\
SSA_OP(Copy)\
\
/* TODO(bill): calling conventions */\
SSA_OP(CallOdin)\
SSA_OP(CallC)\
SSA_OP(CallStd)\
SSA_OP(CallFast)\
\
SSA_OP(BoundsCheck)\
SSA_OP(SliceBoundsCheck)\
\
/* Built in operations/procedures */\
SSA_OP(Bswap16)\
SSA_OP(Bswap32)\
SSA_OP(Bswap64)\
\
SSA_OP(Assume)\
SSA_OP(DebugTrap)\
SSA_OP(Trap)\
SSA_OP(ReadCycleCounter)\
\
\
SSA_OP(ConstBool)\
SSA_OP(ConstString)\
SSA_OP(ConstSlice)\
SSA_OP(ConstNil)\
SSA_OP(Const8)\
SSA_OP(Const16)\
SSA_OP(Const32)\
SSA_OP(Const64)\
SSA_OP(Const32F)\
SSA_OP(Const64F)\
\
/* These should be all the operations I could possibly need for the mean time */\
SSA_OP(Add8)\
SSA_OP(Add16)\
SSA_OP(Add32)\
SSA_OP(Add64)\
SSA_OP(AddPtr)\
SSA_OP(Add32F)\
SSA_OP(Add64F)\
SSA_OP(Sub8)\
SSA_OP(Sub16)\
SSA_OP(Sub32)\
SSA_OP(Sub64)\
SSA_OP(SubPtr)\
SSA_OP(Sub32F)\
SSA_OP(Sub64F)\
SSA_OP(Mul8)\
SSA_OP(Mul16)\
SSA_OP(Mul32)\
SSA_OP(Mul64)\
SSA_OP(Mul32F)\
SSA_OP(Mul64F)\
SSA_OP(Div8)\
SSA_OP(Div8U)\
SSA_OP(Div16)\
SSA_OP(Div16U)\
SSA_OP(Div32)\
SSA_OP(Div32U)\
SSA_OP(Div64)\
SSA_OP(Div64U)\
SSA_OP(Div32F)\
SSA_OP(Div64F)\
SSA_OP(Mod8)\
SSA_OP(Mod8U)\
SSA_OP(Mod16)\
SSA_OP(Mod16U)\
SSA_OP(Mod32)\
SSA_OP(Mod32U)\
SSA_OP(Mod64)\
SSA_OP(Mod64U)\
\
SSA_OP(And8)\
SSA_OP(And16)\
SSA_OP(And32)\
SSA_OP(And64)\
SSA_OP(Or8)\
SSA_OP(Or16)\
SSA_OP(Or32)\
SSA_OP(Or64)\
SSA_OP(Xor8)\
SSA_OP(Xor16)\
SSA_OP(Xor32)\
SSA_OP(Xor64)\
SSA_OP(AndNot8)\
SSA_OP(AndNot16)\
SSA_OP(AndNot32)\
SSA_OP(AndNot64)\
\
SSA_OP(Lsh8x8)\
SSA_OP(Lsh8x16)\
SSA_OP(Lsh8x32)\
SSA_OP(Lsh8x64)\
SSA_OP(Lsh16x8)\
SSA_OP(Lsh16x16)\
SSA_OP(Lsh16x32)\
SSA_OP(Lsh16x64)\
SSA_OP(Lsh32x8)\
SSA_OP(Lsh32x16)\
SSA_OP(Lsh32x32)\
SSA_OP(Lsh32x64)\
SSA_OP(Lsh64x8)\
SSA_OP(Lsh64x16)\
SSA_OP(Lsh64x32)\
SSA_OP(Lsh64x64)\
SSA_OP(Rsh8x8)\
SSA_OP(Rsh8x16)\
SSA_OP(Rsh8x32)\
SSA_OP(Rsh8x64)\
SSA_OP(Rsh16x8)\
SSA_OP(Rsh16x16)\
SSA_OP(Rsh16x32)\
SSA_OP(Rsh16x64)\
SSA_OP(Rsh32x8)\
SSA_OP(Rsh32x16)\
SSA_OP(Rsh32x32)\
SSA_OP(Rsh32x64)\
SSA_OP(Rsh64x8)\
SSA_OP(Rsh64x16)\
SSA_OP(Rsh64x32)\
SSA_OP(Rsh64x64)\
SSA_OP(Rsh8Ux8)\
SSA_OP(Rsh8Ux16)\
SSA_OP(Rsh8Ux32)\
SSA_OP(Rsh8Ux64)\
SSA_OP(Rsh16Ux8)\
SSA_OP(Rsh16Ux16)\
SSA_OP(Rsh16Ux32)\
SSA_OP(Rsh16Ux64)\
SSA_OP(Rsh32Ux8)\
SSA_OP(Rsh32Ux16)\
SSA_OP(Rsh32Ux32)\
SSA_OP(Rsh32Ux64)\
SSA_OP(Rsh64Ux8)\
SSA_OP(Rsh64Ux16)\
SSA_OP(Rsh64Ux32)\
SSA_OP(Rsh64Ux64)\
\
SSA_OP(Eq8)\
SSA_OP(Eq16)\
SSA_OP(Eq32)\
SSA_OP(Eq64)\
SSA_OP(EqPtr)\
SSA_OP(Eq32F)\
SSA_OP(Eq64F)\
SSA_OP(Ne8)\
SSA_OP(Ne16)\
SSA_OP(Ne32)\
SSA_OP(Ne64)\
SSA_OP(NePtr)\
SSA_OP(Ne32F)\
SSA_OP(Ne64F)\
SSA_OP(Lt8)\
SSA_OP(Lt16)\
SSA_OP(Lt32)\
SSA_OP(Lt64)\
SSA_OP(LtPtr)\
SSA_OP(Lt32F)\
SSA_OP(Lt64F)\
SSA_OP(Gt8)\
SSA_OP(Gt16)\
SSA_OP(Gt32)\
SSA_OP(Gt64)\
SSA_OP(GtPtr)\
SSA_OP(Gt32F)\
SSA_OP(Gt64F)\
SSA_OP(Le8)\
SSA_OP(Le16)\
SSA_OP(Le32)\
SSA_OP(Le64)\
SSA_OP(LePtr)\
SSA_OP(Le32F)\
SSA_OP(Le64F)\
SSA_OP(Ge8)\
SSA_OP(Ge16)\
SSA_OP(Ge32)\
SSA_OP(Ge64)\
SSA_OP(GePtr)\
SSA_OP(Ge32F)\
SSA_OP(Ge64F)\
\
SSA_OP(NotB)\
SSA_OP(EqB)\
SSA_OP(NeB)\
\
SSA_OP(Neg8)\
SSA_OP(Neg16)\
SSA_OP(Neg32)\
SSA_OP(Neg64)\
SSA_OP(Neg32F)\
SSA_OP(Neg64F)\
\
SSA_OP(Not8)\
SSA_OP(Not16)\
SSA_OP(Not32)\
SSA_OP(Not64)\
\
SSA_OP(SignExt8to16)\
SSA_OP(SignExt8to32)\
SSA_OP(SignExt8to64)\
SSA_OP(SignExt16to32)\
SSA_OP(SignExt16to64)\
SSA_OP(SignExt32to64)\
SSA_OP(ZeroExt8to16)\
SSA_OP(ZeroExt8to32)\
SSA_OP(ZeroExt8to64)\
SSA_OP(ZeroExt16to32)\
SSA_OP(ZeroExt16to64)\
SSA_OP(ZeroExt32to64)\
SSA_OP(Trunc16to8)\
SSA_OP(Trunc32to8)\
SSA_OP(Trunc32to16)\
SSA_OP(Trunc64to8)\
SSA_OP(Trunc64to16)\
SSA_OP(Trunc64to32)\
\
SSA_OP(Cvt32to32F)\
SSA_OP(Cvt32to64F)\
SSA_OP(Cvt64to32F)\
SSA_OP(Cvt64to64F)\
SSA_OP(Cvt32Fto32)\
SSA_OP(Cvt32Fto64)\
SSA_OP(Cvt64Fto32)\
SSA_OP(Cvt64Fto64)\
SSA_OP(Cvt32Fto64F)\
SSA_OP(Cvt64Fto32F)\
SSA_OP(Cvt32Uto32F)\
SSA_OP(Cvt32Uto64F)\
SSA_OP(Cvt32Fto32U)\
SSA_OP(Cvt64Fto32U)\
SSA_OP(Cvt64Uto32F)\
SSA_OP(Cvt64Uto64F)\
SSA_OP(Cvt32Fto64U)\
SSA_OP(Cvt64Fto64U)\
enum ssaOp {
#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
SSA_OPS
#undef SSA_OP
};
String const ssa_op_strings[] = {
#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
SSA_OPS
#undef SSA_OP
};
+135 -55
View File
@@ -1,29 +1,51 @@
gb_global gbArena string_buffer_arena = {0};
gb_global gbAllocator string_buffer_allocator = {0};
gb_global gbArena string_buffer_arena = {};
gb_global gbAllocator string_buffer_allocator = {};
gb_global gbMutex string_buffer_mutex = {};
void init_string_buffer_memory(void) {
// NOTE(bill): This should be enough memory for file systems
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
gb_mutex_init(&string_buffer_mutex);
}
// NOTE(bill): Used for UTF-8 strings
typedef struct String {
struct String {
u8 * text;
isize len;
} String;
u8 &operator[](isize i) {
GB_ASSERT(0 <= i && i < len);
return text[i];
}
u8 const &operator[](isize i) const {
GB_ASSERT(0 <= i && i < len);
return text[i];
}
};
// NOTE(bill): used for printf style arguments
#define LIT(x) ((int)(x).len), (x).text
#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1}
#define str_lit(c_str) (String){cast(u8 *)c_str, gb_size_of(c_str)-1}
#if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
#else
#define str_lit(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1}
#endif
// NOTE(bill): String16 is only used for Windows due to its file directories
typedef struct String16 {
struct String16 {
wchar_t *text;
isize len;
} String16;
wchar_t &operator[](isize i) {
GB_ASSERT(0 <= i && i < len);
return text[i];
}
wchar_t const &operator[](isize i) const {
GB_ASSERT(0 <= i && i < len);
return text[i];
}
};
gb_inline String make_string(u8 *text, isize len) {
@@ -44,11 +66,29 @@ gb_inline String16 make_string16(wchar_t *text, isize len) {
return s;
}
isize string16_len(wchar_t *s) {
if (s == nullptr) {
return 0;
}
wchar_t *p = s;
while (*p) {
p++;
}
return p - s;
}
gb_inline String make_string_c(char *text) {
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
}
String substring(String s, isize lo, isize hi) {
isize max = s.len;
GB_ASSERT(lo <= hi && hi <= max);
return make_string(s.text+lo, hi-lo);
}
@@ -56,8 +96,8 @@ gb_inline bool str_eq_ignore_case(String a, String b) {
if (a.len == b.len) {
isize i;
for (i = 0; i < a.len; i++) {
char x = cast(char)a.text[i];
char y = cast(char)b.text[i];
char x = cast(char)a[i];
char y = cast(char)b[i];
if (gb_char_to_lower(x) != gb_char_to_lower(y))
return false;
}
@@ -66,9 +106,8 @@ gb_inline bool str_eq_ignore_case(String a, String b) {
return false;
}
int string_compare(String x, String y) {
if (!(x.len == y.len &&
x.text == y.text)) {
int string_compare(String const &x, String const &y) {
if (x.len != y.len || x.text != y.text) {
isize n, fast, offset, curr_block;
isize *la, *lb;
isize pos;
@@ -88,16 +127,16 @@ int string_compare(String x, String y) {
for (; curr_block < fast; curr_block++) {
if (la[curr_block] ^ lb[curr_block]) {
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
if (x.text[pos] ^ y.text[pos]) {
return cast(int)x.text[pos] - cast(int)y.text[pos];
if (x[pos] ^ y[pos]) {
return cast(int)x[pos] - cast(int)y[pos];
}
}
}
}
for (; offset < n; offset++) {
if (x.text[offset] ^ y.text[offset]) {
return cast(int)x.text[offset] - cast(int)y.text[offset];
if (x[offset] ^ y[offset]) {
return cast(int)x[offset] - cast(int)y[offset];
}
}
}
@@ -110,12 +149,36 @@ GB_COMPARE_PROC(string_cmp_proc) {
return string_compare(x, y);
}
gb_inline bool str_eq(String a, String b) { return a.len == b.len ? gb_memcompare(a.text, b.text, a.len) == 0 : false; }
gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); }
gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; }
gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; }
gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; }
gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; }
gb_inline bool str_eq(String const &a, String const &b) {
if (a.len != b.len) return false;
for (isize i = 0; i < a.len; i++) {
if (a.text[i] != b.text[i]) {
return false;
}
}
return true;
}
gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); }
gb_inline bool str_lt(String const &a, String const &b) { return string_compare(a, b) < 0; }
gb_inline bool str_gt(String const &a, String const &b) { return string_compare(a, b) > 0; }
gb_inline bool str_le(String const &a, String const &b) { return string_compare(a, b) <= 0; }
gb_inline bool str_ge(String const &a, String const &b) { return string_compare(a, b) >= 0; }
gb_inline bool operator == (String const &a, String const &b) { return str_eq(a, b); }
gb_inline bool operator != (String const &a, String const &b) { return str_ne(a, b); }
gb_inline bool operator < (String const &a, String const &b) { return str_lt(a, b); }
gb_inline bool operator > (String const &a, String const &b) { return str_gt(a, b); }
gb_inline bool operator <= (String const &a, String const &b) { return str_le(a, b); }
gb_inline bool operator >= (String const &a, String const &b) { return str_ge(a, b); }
template <isize N> bool operator == (String const &a, char const (&b)[N]) { return str_eq(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator != (String const &a, char const (&b)[N]) { return str_ne(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator < (String const &a, char const (&b)[N]) { return str_lt(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator > (String const &a, char const (&b)[N]) { return str_gt(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
gb_inline bool str_has_prefix(String s, String prefix) {
isize i;
@@ -123,7 +186,7 @@ gb_inline bool str_has_prefix(String s, String prefix) {
return false;
}
for (i = 0; i < prefix.len; i++) {
if (s.text[i] != prefix.text[i]) {
if (s[i] != prefix[i]) {
return false;
}
}
@@ -133,11 +196,10 @@ gb_inline bool str_has_prefix(String s, String prefix) {
gb_inline isize string_extension_position(String str) {
isize dot_pos = -1;
isize i = str.len;
bool seen_dot = false;
while (i --> 0) {
if (str.text[i] == GB_PATH_SEPARATOR)
if (str[i] == GB_PATH_SEPARATOR)
break;
if (str.text[i] == '.') {
if (str[i] == '.') {
dot_pos = i;
break;
}
@@ -147,11 +209,11 @@ gb_inline isize string_extension_position(String str) {
}
String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str.text[str.len-1])) {
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
str.len--;
}
while (str.len > 0 && rune_is_whitespace(str.text[0])) {
while (str.len > 0 && rune_is_whitespace(str[0])) {
str.text++;
str.len--;
}
@@ -166,7 +228,7 @@ gb_inline bool string_has_extension(String str, String ext) {
}
isize len = str.len;
for (isize i = len-1; i >= 0; i--) {
if (str.text[i] == '.') {
if (str[i] == '.') {
break;
}
len--;
@@ -182,12 +244,28 @@ gb_inline bool string_has_extension(String str, String ext) {
bool string_contains_char(String s, u8 c) {
isize i;
for (i = 0; i < s.len; i++) {
if (s.text[i] == c)
if (s[i] == c)
return true;
}
return false;
}
String filename_from_path(String s) {
isize i = string_extension_position(s);
if (i > 0) {
isize j = 0;
s.len = i;
for (j = i-1; j >= 0; j--) {
if (s[j] == '/' ||
s[j] == '\\') {
break;
}
}
s.text += j+1;
s.len = i-j-1;
}
return make_string(nullptr, 0);
}
@@ -201,13 +279,13 @@ bool string_contains_char(String s, u8 c) {
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, multibyte_input, input_length, output, output_size);
}
int convert_widechar_to_multibyte(wchar_t *widechar_input, int input_length, char *output, int output_size) {
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, NULL, NULL);
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
}
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
#include <iconv.h>
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
iconv_close(conv);
@@ -215,7 +293,7 @@ bool string_contains_char(String s, u8 c) {
return (int) result;
}
int convert_widechar_to_multibyte(wchar_t* widechar_input, int input_length, char* output, int output_size) {
int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
iconv_close(conv);
@@ -235,45 +313,47 @@ String16 string_to_string16(gbAllocator a, String s) {
wchar_t *text;
if (s.len < 1) {
return make_string16(NULL, 0);
return make_string16(nullptr, 0);
}
len = convert_multibyte_to_widechar(cast(char *)s.text, s.len, NULL, 0);
len = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, nullptr, 0);
if (len == 0) {
return make_string16(NULL, 0);
return make_string16(nullptr, 0);
}
text = gb_alloc_array(a, wchar_t, len+1);
len1 = convert_multibyte_to_widechar(cast(char *)s.text, s.len, text, len);
len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len);
if (len1 == 0) {
gb_free(a, text);
return make_string16(NULL, 0);
return make_string16(nullptr, 0);
}
text[len] = 0;
return make_string16(text, len-1);
}
String string16_to_string(gbAllocator a, String16 s) {
int len, len1;
u8 *text;
if (s.len < 1) {
return make_string(NULL, 0);
return make_string(nullptr, 0);
}
len = convert_widechar_to_multibyte(s.text, s.len, NULL, 0);
len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0);
if (len == 0) {
return make_string(NULL, 0);
return make_string(nullptr, 0);
}
len += 1; // NOTE(bill): It needs an extra 1 for some reason
text = gb_alloc_array(a, u8, len+1);
len1 = convert_widechar_to_multibyte(s.text, s.len, cast(char *)text, len);
len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len);
if (len1 == 0) {
gb_free(a, text);
return make_string(NULL, 0);
return make_string(nullptr, 0);
}
text[len] = 0;
@@ -299,18 +379,18 @@ String string16_to_string(gbAllocator a, String16 s) {
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
u8 c;
if (s.text[0] == quote &&
if (s[0] == quote &&
(quote == '\'' || quote == '"')) {
return false;
} else if (s.text[0] >= 0x80) {
} else if (s[0] >= 0x80) {
Rune r = -1;
isize size = gb_utf8_decode(s.text, s.len, &r);
*rune = r;
*multiple_bytes = true;
*tail_string = make_string(s.text+size, s.len-size);
return true;
} else if (s.text[0] != '\\') {
*rune = s.text[0];
} else if (s[0] != '\\') {
*rune = s[0];
*tail_string = make_string(s.text+1, s.len-1);
return true;
}
@@ -318,7 +398,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
if (s.len <= 1) {
return false;
}
c = s.text[1];
c = s[1];
s = make_string(s.text+2, s.len-2);
switch (c) {
@@ -356,7 +436,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
return false;
}
for (i = 0; i < 2; i++) {
i32 d = gb_digit_to_int(s.text[i]);
i32 d = gb_digit_to_int(s[i]);
if (d < 0 || d > 7) {
return false;
}
@@ -384,7 +464,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
return false;
}
for (i = 0; i < count; i++) {
i32 d = gb_hex_digit_to_int(s.text[i]);
i32 d = gb_hex_digit_to_int(s[i]);
if (d < 0) {
return false;
}
@@ -417,8 +497,8 @@ i32 unquote_string(gbAllocator a, String *s_) {
if (n < 2) {
return 0;
}
quote = s.text[0];
if (quote != s.text[n-1]) {
quote = s[0];
if (quote != s[n-1]) {
return 0;
}
s.text += 1;
@@ -455,12 +535,12 @@ i32 unquote_string(gbAllocator a, String *s_) {
{
u8 rune_temp[4] = {0};
u8 rune_temp[4] = {};
isize buf_len = 3*s.len / 2;
u8 *buf = gb_alloc_array(a, u8, buf_len);
isize offset = 0;
while (s.len > 0) {
String tail_string = {0};
String tail_string = {};
Rune r = 0;
bool multiple_bytes = false;
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
+27 -22
View File
@@ -1,14 +1,15 @@
typedef struct TimeStamp {
struct TimeStamp {
u64 start;
u64 finish;
String label;
} TimeStamp;
};
typedef struct Timings {
struct Timings {
TimeStamp total;
Array(TimeStamp) sections;
Array<TimeStamp> sections;
u64 freq;
} Timings;
f64 total_time_seconds;
};
#if defined(GB_SYSTEM_WINDOWS)
@@ -45,13 +46,7 @@ u64 unix_time_stamp__freq(void) {
if (freq == 0) {
struct timespec ts;
clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
// that would be an absurd resolution (or lack thereof)
GB_ASSERT(ts.tv_sec == 0);
freq = cast(u64) ((1.0 / ts.tv_nsec) * 1000000000.0);
GB_ASSERT(freq != 0);
}
return freq;
@@ -89,7 +84,7 @@ TimeStamp make_time_stamp(String label) {
}
void timings_init(Timings *t, String label, isize buffer_size) {
array_init_reserve(&t->sections, heap_allocator(), buffer_size);
array_init(&t->sections, heap_allocator(), buffer_size);
t->total = make_time_stamp(label);
t->freq = time_stamp__freq();
}
@@ -100,7 +95,7 @@ void timings_destroy(Timings *t) {
void timings__stop_current_section(Timings *t) {
if (t->sections.count > 0) {
t->sections.e[t->sections.count-1].finish = time_stamp_time_now();
t->sections[t->sections.count-1].finish = time_stamp_time_now();
}
}
@@ -109,36 +104,46 @@ void timings_start_section(Timings *t, String label) {
array_add(&t->sections, make_time_stamp(label));
}
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
f64 time_stamp_as_second(TimeStamp ts, u64 freq) {
GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq;
return cast(f64)(ts.finish - ts.start) / cast(f64)freq;
}
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
return 1000.0*time_stamp_as_second(ts, freq);
}
void timings_print_all(Timings *t) {
char const SPACES[] = " ";
isize max_len, i;
isize max_len;
timings__stop_current_section(t);
t->total.finish = time_stamp_time_now();
max_len = t->total.label.len;
for_array(i, t->sections) {
TimeStamp ts = t->sections.e[i];
TimeStamp ts = t->sections[i];
max_len = gb_max(max_len, ts.label.len);
}
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
gb_printf("%.*s%.*s - %.3f ms\n",
t->total_time_seconds = time_stamp_as_second(t->total, t->freq);
f64 total_ms = time_stamp_as_ms(t->total, t->freq);
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
LIT(t->total.label),
cast(int)(max_len-t->total.label.len), SPACES,
time_stamp_as_ms(t->total, t->freq));
total_ms,
cast(f64)100.0);
for_array(i, t->sections) {
TimeStamp ts = t->sections.e[i];
gb_printf("%.*s%.*s - %.3f ms\n",
TimeStamp ts = t->sections[i];
f64 section_ms = time_stamp_as_ms(ts, t->freq);
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
LIT(ts.label),
cast(int)(max_len-ts.label.len), SPACES,
time_stamp_as_ms(ts, t->freq));
section_ms, 100*section_ms/total_ms);
}
}
+207 -143
View File
@@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
TOKEN_KIND(Token_Ident, "identifier"), \
TOKEN_KIND(Token_Integer, "integer"), \
TOKEN_KIND(Token_Float, "float"), \
TOKEN_KIND(Token_Imag, "imaginary"), \
TOKEN_KIND(Token_Rune, "rune"), \
TOKEN_KIND(Token_String, "string"), \
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
@@ -24,6 +25,7 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
TOKEN_KIND(Token_Mul, "*"), \
TOKEN_KIND(Token_Quo, "/"), \
TOKEN_KIND(Token_Mod, "%"), \
TOKEN_KIND(Token_ModMod, "%%"), \
TOKEN_KIND(Token_And, "&"), \
TOKEN_KIND(Token_Or, "|"), \
TOKEN_KIND(Token_Xor, "~"), \
@@ -35,24 +37,27 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
TOKEN_KIND(Token_CmpOr, "||"), \
\
TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
TOKEN_KIND(Token_AddEq, "+="), \
TOKEN_KIND(Token_SubEq, "-="), \
TOKEN_KIND(Token_MulEq, "*="), \
TOKEN_KIND(Token_QuoEq, "/="), \
TOKEN_KIND(Token_ModEq, "%="), \
TOKEN_KIND(Token_AndEq, "&="), \
TOKEN_KIND(Token_OrEq, "|="), \
TOKEN_KIND(Token_XorEq, "~="), \
TOKEN_KIND(Token_AndNotEq, "&~="), \
TOKEN_KIND(Token_ShlEq, "<<="), \
TOKEN_KIND(Token_ShrEq, ">>="), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
TOKEN_KIND(Token_ArrowRight, "->"), \
TOKEN_KIND(Token_ArrowLeft, "<-"), \
TOKEN_KIND(Token_Inc, "++"), \
TOKEN_KIND(Token_Dec, "--"), \
TOKEN_KIND(Token_AddEq, "+="), \
TOKEN_KIND(Token_SubEq, "-="), \
TOKEN_KIND(Token_MulEq, "*="), \
TOKEN_KIND(Token_QuoEq, "/="), \
TOKEN_KIND(Token_ModEq, "%="), \
TOKEN_KIND(Token_ModModEq, "%%="), \
TOKEN_KIND(Token_AndEq, "&="), \
TOKEN_KIND(Token_OrEq, "|="), \
TOKEN_KIND(Token_XorEq, "~="), \
TOKEN_KIND(Token_AndNotEq, "&~="), \
TOKEN_KIND(Token_ShlEq, "<<="), \
TOKEN_KIND(Token_ShrEq, ">>="), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
TOKEN_KIND(Token_ArrowRight, "->"), \
TOKEN_KIND(Token_ArrowLeft, "<-"), \
TOKEN_KIND(Token_DoubleArrowRight, "=>"), \
/* TOKEN_KIND(Token_Inc, "++"), */ \
/* TOKEN_KIND(Token_Dec, "--"), */ \
TOKEN_KIND(Token_Undef, "---"), \
\
TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_CmpEq, "=="), \
@@ -63,64 +68,73 @@ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
\
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
/* TOKEN_KIND(Token_HalfOpenRange, "..<"), */ \
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, "..."), \
TOKEN_KIND(Token_HalfClosed, ".."), \
TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
\
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_when, "when"), \
TOKEN_KIND(Token_if, "if"), \
TOKEN_KIND(Token_else, "else"), \
TOKEN_KIND(Token_for, "for"), \
TOKEN_KIND(Token_in, "in"), \
TOKEN_KIND(Token_match, "match"), \
TOKEN_KIND(Token_default, "default"), \
TOKEN_KIND(Token_case, "case"), \
TOKEN_KIND(Token_break, "break"), \
TOKEN_KIND(Token_continue, "continue"), \
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
TOKEN_KIND(Token_give, "give"), \
TOKEN_KIND(Token_proc, "proc"), \
TOKEN_KIND(Token_macro, "macro"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_raw_union, "raw_union"), \
TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_vector, "vector"), \
TOKEN_KIND(Token_static, "static"), \
TOKEN_KIND(Token_dynamic, "dynamic"), \
TOKEN_KIND(Token_map, "map"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_no_alias, "no_alias"), \
TOKEN_KIND(Token_immutable, "immutable"), \
TOKEN_KIND(Token_cast, "cast"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_import, "import"), \
TOKEN_KIND(Token_import_load, "import_load"), \
TOKEN_KIND(Token_foreign, "foreign"), \
TOKEN_KIND(Token_foreign_library, "foreign_library"), \
TOKEN_KIND(Token_foreign_system_library, "foreign_system_library"), \
TOKEN_KIND(Token_type, "type"), \
TOKEN_KIND(Token_when, "when"), \
TOKEN_KIND(Token_if, "if"), \
TOKEN_KIND(Token_else, "else"), \
TOKEN_KIND(Token_for, "for"), \
TOKEN_KIND(Token_match, "match"), \
TOKEN_KIND(Token_in, "in"), \
TOKEN_KIND(Token_do, "do"), \
TOKEN_KIND(Token_case, "case"), \
TOKEN_KIND(Token_break, "break"), \
TOKEN_KIND(Token_continue, "continue"), \
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
TOKEN_KIND(Token_proc, "proc"), \
TOKEN_KIND(Token_macro, "macro"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_bit_field, "bit_field"), \
TOKEN_KIND(Token_vector, "vector"), \
TOKEN_KIND(Token_map, "map"), \
TOKEN_KIND(Token_static, "static"), \
TOKEN_KIND(Token_dynamic, "dynamic"), \
TOKEN_KIND(Token_cast, "cast"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_size_of, "size_of"), \
TOKEN_KIND(Token_align_of, "align_of"), \
TOKEN_KIND(Token_offset_of, "offset_of"), \
TOKEN_KIND(Token_type_of, "type_of"), \
TOKEN_KIND(Token_type_info_of, "type_info_of"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_yield, "yield"), \
TOKEN_KIND(Token_await, "await"), \
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
TOKEN_KIND(Token_Count, "")
typedef enum TokenKind {
enum TokenKind {
#define TOKEN_KIND(e, s) e
TOKEN_KINDS
#undef TOKEN_KIND
} TokenKind;
};
String const token_strings[] = {
#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1}
@@ -129,11 +143,11 @@ String const token_strings[] = {
};
typedef struct TokenPos {
struct TokenPos {
String file;
isize line;
isize column;
} TokenPos;
};
i32 token_pos_cmp(TokenPos a, TokenPos b) {
if (a.line == b.line) {
@@ -151,11 +165,11 @@ bool token_pos_eq(TokenPos a, TokenPos b) {
return token_pos_cmp(a, b) == 0;
}
typedef struct Token {
struct Token {
TokenKind kind;
String string;
TokenPos pos;
} Token;
};
Token empty_token = {Token_Invalid};
Token blank_token = {Token_Ident, {cast(u8 *)"_", 1}};
@@ -166,12 +180,12 @@ Token make_token_ident(String s) {
}
typedef struct ErrorCollector {
struct ErrorCollector {
TokenPos prev;
i64 count;
i64 warning_count;
i64 count;
i64 warning_count;
gbMutex mutex;
} ErrorCollector;
};
gb_global ErrorCollector global_error_collector;
@@ -305,7 +319,7 @@ gb_inline bool token_is_shift(TokenKind t) {
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
typedef enum TokenizerInitError {
enum TokenizerInitError {
TokenizerInit_None,
TokenizerInit_Invalid,
@@ -314,18 +328,18 @@ typedef enum TokenizerInitError {
TokenizerInit_Empty,
TokenizerInit_Count,
} TokenizerInitError;
};
typedef struct TokenizerState {
struct TokenizerState {
Rune curr_rune; // current character
u8 * curr; // character pos
u8 * read_curr; // pos from start
u8 * line; // current line pos
isize line_count;
} TokenizerState;
};
typedef struct Tokenizer {
struct Tokenizer {
String fullpath;
u8 *start;
u8 *end;
@@ -337,12 +351,12 @@ typedef struct Tokenizer {
isize line_count;
isize error_count;
Array(String) allocated_strings;
} Tokenizer;
Array<String> allocated_strings;
};
TokenizerState save_tokenizer_state(Tokenizer *t) {
TokenizerState state = {0};
TokenizerState state = {};
state.curr_rune = t->curr_rune;
state.curr = t->curr;
state.read_curr = t->read_curr;
@@ -414,13 +428,13 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
TokenizerInitError err = TokenizerInit_None;
char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
memcpy(c_str, fullpath.text, fullpath.len);
gb_memcopy(c_str, fullpath.text, fullpath.len);
c_str[fullpath.len] = '\0';
// TODO(bill): Memory map rather than copy contents
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str);
gb_zero_item(t);
if (fc.data != NULL) {
if (fc.data != nullptr) {
t->start = cast(u8 *)fc.data;
t->line = t->read_curr = t->curr = t->start;
t->end = t->start + fc.size;
@@ -434,7 +448,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
array_init(&t->allocated_strings, heap_allocator());
} else {
gbFile f = {0};
gbFile f = {};
gbFileError file_err = gb_file_open(&f, c_str);
switch (file_err) {
@@ -455,11 +469,11 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
}
gb_inline void destroy_tokenizer(Tokenizer *t) {
if (t->start != NULL) {
if (t->start != nullptr) {
gb_free(heap_allocator(), t->start);
}
for_array(i, t->allocated_strings) {
gb_free(heap_allocator(), t->allocated_strings.e[i].text);
gb_free(heap_allocator(), t->allocated_strings[i].text);
}
array_free(&t->allocated_strings);
}
@@ -484,20 +498,14 @@ gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible
}
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
if (allow_underscore) {
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
advance_to_next_rune(t);
}
} else {
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
}
gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
advance_to_next_rune(t);
}
}
Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
Token token = {0};
Token token = {};
token.kind = Token_Integer;
token.string = make_string(t->curr, 1);
token.pos.file = t->fullpath;
@@ -506,7 +514,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
if (seen_decimal_point) {
token.kind = Token_Float;
scan_mantissa(t, 10, true);
scan_mantissa(t, 10);
goto exponent;
}
@@ -515,31 +523,44 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t);
scan_mantissa(t, 2, true);
scan_mantissa(t, 2);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'o') { // Octal
advance_to_next_rune(t);
scan_mantissa(t, 8, true);
scan_mantissa(t, 8);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'd') { // Decimal
advance_to_next_rune(t);
scan_mantissa(t, 10, true);
scan_mantissa(t, 10);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'z') { // Dozenal
advance_to_next_rune(t);
scan_mantissa(t, 12);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'x') { // Hexadecimal
advance_to_next_rune(t);
scan_mantissa(t, 16, true);
scan_mantissa(t, 16);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else {
} /* else if (t->curr_rune == 'h') { // Hexadecimal Float
token.kind = Token_Float;
advance_to_next_rune(t);
scan_mantissa(t, 16);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} */ else {
seen_decimal_point = false;
scan_mantissa(t, 10, true);
scan_mantissa(t, 10);
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true;
@@ -547,24 +568,24 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
}
}
token.string.len = t->curr - token.string.text;
return token;
goto end;
}
scan_mantissa(t, 10, true);
scan_mantissa(t, 10);
fraction:
if (t->curr_rune == '.') {
// HACK(bill): This may be inefficient
TokenizerState state = save_tokenizer_state(t);
advance_to_next_rune(t);
if (digit_value(t->curr_rune) >= 10) {
if (t->curr_rune == '.') {
// TODO(bill): Clean up this shit
restore_tokenizer_state(t, &state);
goto end;
}
token.kind = Token_Float;
scan_mantissa(t, 10, true);
scan_mantissa(t, 10);
}
exponent:
@@ -574,7 +595,12 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t);
}
scan_mantissa(t, 10, false);
scan_mantissa(t, 10);
}
if (t->curr_rune == 'i') {
token.kind = Token_Imag;
advance_to_next_rune(t);
}
end:
@@ -611,20 +637,22 @@ bool scan_escape(Tokenizer *t, Rune quote) {
advance_to_next_rune(t);
len = 8; base = 16; max = GB_RUNE_MAX;
} else {
if (t->curr_rune < 0)
if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated");
else
} else {
tokenizer_err(t, "Unknown escape sequence");
}
return false;
}
while (len --> 0) {
u32 d = cast(u32)digit_value(t->curr_rune);
if (d >= base) {
if (t->curr_rune < 0)
if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated");
else
} else {
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
}
return false;
}
@@ -734,7 +762,7 @@ bool tokenizer_find_line_end(Tokenizer *t) {
Token tokenizer_get_token(Tokenizer *t) {
tokenizer_skip_whitespace(t);
Token token = {0};
Token token = {};
token.string = make_string(t->curr, 1);
token.pos.file = t->fullpath;
token.pos.line = t->line_count;
@@ -752,7 +780,7 @@ Token tokenizer_get_token(Tokenizer *t) {
// NOTE(bill): All keywords are > 1
if (token.string.len > 1) {
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
if (str_eq(token.string, token_strings[k])) {
if (token.string == token_strings[k]) {
token.kind = cast(TokenKind)k;
break;
}
@@ -858,36 +886,69 @@ Token tokenizer_get_token(Tokenizer *t) {
token.kind = Token_Period; // Default
if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t);
token.kind = Token_Ellipsis;
// if (t->curr_rune == '<') {
// advance_to_next_rune(t);
// token.kind = Token_HalfOpenRange;
// }
token.kind = Token_HalfClosed;
if (t->curr_rune == '.') {
advance_to_next_rune(t);
token.kind = Token_Ellipsis;
}
}
break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case '\\': token.kind = Token_BackSlash; break;
case 0x2260: token.kind = Token_NotEq; break; // '≠'
case 0x2264: token.kind = Token_LtEq; break; // '≤'
case 0x2265: token.kind = Token_GtEq; break; // '≥'
case '%': token.kind = token_kind_dub_eq(t, '%', Token_Mod, Token_ModEq, Token_ModMod, Token_ModModEq); break;
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break;
case '=':
token.kind = Token_Eq;
if (t->curr_rune == '>') {
advance_to_next_rune(t);
token.kind = Token_DoubleArrowRight;
} else if (t->curr_rune == '=') {
advance_to_next_rune(t);
token.kind = Token_CmpEq;
}
break;
case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break;
case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break;
case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc); break;
case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Dec, '>', Token_ArrowRight); break;
// case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc); break;
case '+': token.kind = token_kind_variant2(t, Token_Add, Token_AddEq); break;
case '-':
token.kind = Token_Sub;
if (t->curr_rune == '=') {
advance_to_next_rune(t);
token.kind = Token_SubEq;
} else if (t->curr_rune == '-') {
advance_to_next_rune(t);
token.kind = Token_Invalid;
if (t->curr_rune == '-') {
advance_to_next_rune(t);
token.kind = Token_Undef;
}
} else if (t->curr_rune == '>') {
advance_to_next_rune(t);
token.kind = Token_ArrowRight;
}
break;
case '/': {
if (t->curr_rune == '/') {
while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
@@ -898,7 +959,9 @@ Token tokenizer_get_token(Tokenizer *t) {
isize comment_scope = 1;
advance_to_next_rune(t);
while (comment_scope > 0) {
if (t->curr_rune == '/') {
if (t->curr_rune == GB_RUNE_EOF) {
break;
} else if (t->curr_rune == '/') {
advance_to_next_rune(t);
if (t->curr_rune == '*') {
advance_to_next_rune(t);
@@ -922,6 +985,7 @@ Token tokenizer_get_token(Tokenizer *t) {
case '<':
if (t->curr_rune == '-') {
advance_to_next_rune(t);
token.kind = Token_ArrowLeft;
} else {
token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
@@ -946,8 +1010,8 @@ Token tokenizer_get_token(Tokenizer *t) {
case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break;
default:
if (curr_rune != GB_RUNE_BOM) {
u8 str[4] = {0};
if (curr_rune != GB_RUNE_BOM) {
u8 str[4] = {};
int len = cast(int)gb_utf8_encode_rune(str, curr_rune);
tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune);
}
-2170
View File
File diff suppressed because it is too large Load Diff
+2445
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,9 +1,10 @@
#pragma warning(push)
#pragma warning(disable: 4245)
extern "C" {
// #include "utf8proc/utf8proc.h"
#include "utf8proc/utf8proc.c"
}
#pragma warning(pop)
+8 -12
View File
@@ -383,7 +383,7 @@ UTF8PROC_DLLEXPORT int utf8proc_charwidth(utf8proc_int32_t c) {
}
UTF8PROC_DLLEXPORT utf8proc_category_t utf8proc_category(utf8proc_int32_t c) {
return utf8proc_get_property(c)->category;
return cast(utf8proc_category_t)utf8proc_get_property(c)->category;
}
UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) {
@@ -393,15 +393,15 @@ UTF8PROC_DLLEXPORT const char *utf8proc_category_string(utf8proc_int32_t c) {
#define utf8proc_decompose_lump(replacement_uc) \
return utf8proc_decompose_char((replacement_uc), dst, bufsize, \
options & ~UTF8PROC_LUMP, last_boundclass)
(utf8proc_option_t)(options & ~UTF8PROC_LUMP), last_boundclass)
UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_decompose_char(utf8proc_int32_t uc, utf8proc_int32_t *dst, utf8proc_ssize_t bufsize, utf8proc_option_t options, int *last_boundclass) {
const utf8proc_property_t *property;
utf8proc_propval_t category;
utf8proc_category_t category;
utf8proc_int32_t hangul_sindex;
if (uc < 0 || uc >= 0x110000) return UTF8PROC_ERROR_NOTASSIGNED;
property = unsafe_get_property(uc);
category = property->category;
category = cast(utf8proc_category_t)property->category;
hangul_sindex = uc - UTF8PROC_HANGUL_SBASE;
if (options & (UTF8PROC_COMPOSE|UTF8PROC_DECOMPOSE)) {
if (hangul_sindex >= 0 && hangul_sindex < UTF8PROC_HANGUL_SCOUNT) {
@@ -728,28 +728,24 @@ UTF8PROC_DLLEXPORT utf8proc_ssize_t utf8proc_map_custom(
UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFD(const utf8proc_uint8_t *str) {
utf8proc_uint8_t *retval;
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
UTF8PROC_DECOMPOSE);
utf8proc_map(str, 0, &retval, cast(utf8proc_option_t)(UTF8PROC_NULLTERM|UTF8PROC_STABLE|UTF8PROC_DECOMPOSE));
return retval;
}
UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFC(const utf8proc_uint8_t *str) {
utf8proc_uint8_t *retval;
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
UTF8PROC_COMPOSE);
utf8proc_map(str, 0, &retval, cast(utf8proc_option_t)(UTF8PROC_NULLTERM|UTF8PROC_STABLE|UTF8PROC_COMPOSE));
return retval;
}
UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKD(const utf8proc_uint8_t *str) {
utf8proc_uint8_t *retval;
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT);
utf8proc_map(str, 0, &retval, cast(utf8proc_option_t)(UTF8PROC_NULLTERM|UTF8PROC_STABLE|UTF8PROC_DECOMPOSE|UTF8PROC_COMPAT));
return retval;
}
UTF8PROC_DLLEXPORT utf8proc_uint8_t *utf8proc_NFKC(const utf8proc_uint8_t *str) {
utf8proc_uint8_t *retval;
utf8proc_map(str, 0, &retval, UTF8PROC_NULLTERM | UTF8PROC_STABLE |
UTF8PROC_COMPOSE | UTF8PROC_COMPAT);
utf8proc_map(str, 0, &retval, cast(utf8proc_option_t)(UTF8PROC_NULLTERM|UTF8PROC_STABLE|UTF8PROC_COMPOSE|UTF8PROC_COMPAT));
return retval;
}