198 Commits

Author SHA1 Message Date
CHatingPython 3b98f77c08 chore: flat out the file structure 2026-09-11 18:48:16 +02:00
CHatingPython 959d0a7773 feat(furc): implement SSA destruction 2026-09-11 18:12:54 +02:00
CHatingPython 70375af068 test(furc/ssa): implement tests for SSA 2026-09-10 21:49:25 +02:00
CHatingPython 46d0de759a feat(furc/ssa): implement SSA stage 2026-09-10 21:48:45 +02:00
CHatingPython 01964a495c feat(furc/ir): add equality comparison to ir classes 2026-09-10 21:47:47 +02:00
CHatingPython 21fb587e36 fix(furc/ir): use separate counter for variables 2026-09-09 14:11:45 +02:00
CHatingPython fe65a09af9 feat(furlang): add serialization 2026-09-06 15:51:51 +02:00
CHatingPython 712a0d7047 chore: change clang-tidy 2026-09-06 15:09:50 +02:00
CHatingPython 35d7fc4970 refactor(furlang): improve results 2026-09-06 15:09:34 +02:00
CHatingPython 9bf4da4fd9 feat(furvm): add string type and improve assigning
Closes: #61
2026-09-06 01:21:14 +02:00
CHatingPython 6c7d16631d feat(furvm): allow setting thing's value 2026-09-04 21:28:28 +02:00
CHatingPython 24a9530abc refactor(furvm): improve thing value access 2026-09-04 21:28:18 +02:00
CHatingPython 0354ec1e79 refactor(furvm): move thing type to types.hpp 2026-09-04 20:59:14 +02:00
CHatingPython ddb60ca93a fix(furdb): add slices to furdb 2026-09-04 18:17:55 +02:00
CHatingPython 3aaed55485 feat(furvm): introduce thing iterators
Refs: #38
2026-09-04 18:17:45 +02:00
CHatingPython cfefca49ee test(furvm): add array and slice tests
Refs: #38
2026-09-04 00:16:58 +02:00
CHatingPython 21e963168e feat(furas): add slice type and instruction 2026-09-03 23:56:19 +02:00
CHatingPython b067bbb4ea feat(furvm): introduce slice instruction 2026-09-03 23:55:57 +02:00
CHatingPython fe37bf8058 feat(furvm): introduce slice type
Refs: #63
2026-09-03 23:55:31 +02:00
CHatingPython 977b22d8f5 fix(furvm): add stack.hpp
I forgot to add it earlier
2026-09-03 22:42:59 +02:00
CHatingPython 30a06186db feat(furvm): introduce stack storage
Closes: #62
2026-09-01 22:33:18 +02:00
CHatingPython 5e425d7fe4 feat(furvm): implement constants
Closes: #13
2026-09-01 17:18:01 +02:00
CHatingPython 12a131c2b8 feat(furc/SSA): insert phi nodes 2026-08-18 17:50:34 +02:00
CHatingPython 5403c0e1a4 feat(furc): actually use SSA 2026-08-18 17:49:43 +02:00
CHatingPython a1dac8ae3f chore(clangd): remove optional access checking 2026-08-18 17:49:20 +02:00
CHatingPython a311795915 fix(furc/IR): fix terminate function 2026-08-18 17:48:51 +02:00
CHatingPython 090d8ad81e feat(furc/SSA): compute dominance frontiers 2026-08-18 13:55:39 +02:00
CHatingPython 5efcf1b9b0 feat(furc/SSA): compute dominance tree 2026-08-18 00:16:32 +02:00
CHatingPython 136663aeab feat(furc/IR): add global variables 2026-08-18 00:15:43 +02:00
CHatingPython 56ea4ba2ab feat(furdb): add stack info 2026-08-17 21:14:10 +02:00
CHatingPython dab7f73216 feat(furvm): expose the stack 2026-08-17 21:13:02 +02:00
CHatingPython 1d4e8bb1d0 feat(furdb): add isocline for better command line 2026-08-17 12:14:30 +02:00
CHatingPython ed84a65757 fix(furdb): improve printing 2026-08-17 11:39:35 +02:00
CHatingPython 26efd110f1 feat(furdb): add continue and next commands 2026-08-17 11:35:49 +02:00
CHatingPython dd96f95d77 fix(furvm): add JustHit executor flag 2026-08-17 11:34:37 +02:00
CHatingPython 70cbb003f2 feat(furdb): introduce info command 2026-08-17 11:21:22 +02:00
CHatingPython 665982049d fix(furdb): improve program execution 2026-08-17 11:20:44 +02:00
CHatingPython 195d154474 fix(furvm): improve executor flags 2026-08-17 11:20:14 +02:00
CHatingPython 26d935e22a feat(furdb): add function for printing instruction 2026-08-17 10:59:00 +02:00
CHatingPython a4966a057c feat(furdb): add break command 2026-08-16 20:01:52 +02:00
CHatingPython b3ce4fa0ed feat(furvm): add breakpoints 2026-08-16 19:59:46 +02:00
CHatingPython 29dd9abb3f fix(furvm): more fixing 2026-08-15 23:13:45 +02:00
CHatingPython ffe1208775 refactor(furvm): cleanup some bugs
Or we could just pretend that there were no bugs clueless
2026-08-15 22:41:32 +02:00
CHatingPython a382b92cf0 refactor(furvm): improve things
Refs: #62
2026-08-15 22:15:35 +02:00
CHatingPython c59f0252ed refactor(furvm): remove thing arena from context 2026-08-15 12:14:24 +02:00
CHatingPython 5c841a1428 feat(furvm): add header to thing data
Refs: #62
2026-08-15 12:10:38 +02:00
CHatingPython 6d176e58f3 refactor(furvm): remove thing allocator
Refs: #62
2026-08-15 12:00:18 +02:00
CHatingPython 6737ed86b0 fix(furvm, furas): fix function signature matching 2026-08-15 00:32:57 +02:00
CHatingPython 4ede6f3476 feat(disfuras): add global variables 2026-08-15 00:32:02 +02:00
CHatingPython c841427eff feat(furas): introduce global variables 2026-08-14 13:58:29 +02:00
CHatingPython a685ecf226 feat(furvm): introduce global variables
Closes: #59
2026-08-14 13:56:10 +02:00
CHatingPython 84b70077c1 refactor(furvm): remove thing handles 2026-08-13 22:08:59 +02:00
CHatingPython 21e92dcb4b refactor(furvm): remove executor handle 2026-08-13 14:10:43 +02:00
CHatingPython 713aec199f feat(furvm): add new frame callback to executor 2026-08-12 23:44:15 +02:00
CHatingPython 15218d7530 feat(furvm): add frames function to executor 2026-08-12 23:05:37 +02:00
CHatingPython d419b62225 chore(furdb): initialize furdb 2026-08-12 14:17:16 +02:00
CHatingPython c68763f785 chore(disfuras): implement basic disfuras 2026-08-11 16:18:40 +02:00
CHatingPython dcefe20d47 refactor(furas): use new instructions from furvm 2026-08-11 16:18:17 +02:00
CHatingPython 989fcb7199 refactor(furvm): use new instructions in executor 2026-08-11 16:17:24 +02:00
CHatingPython 4cffd27a33 refactor(furvm): use furlang views 2026-08-11 16:16:30 +02:00
CHatingPython f9b910ae13 feat(furlang): add view
Add a basic view (span from C++20).
2026-08-11 16:15:48 +02:00
CHatingPython 7a7a1ec64c feat(furvm): improve instruction abstraction
Add reading and writing to instruction abstraction.
2026-08-11 13:56:34 +02:00
CHatingPython 99dba99db2 refactor(furvm): change instruction definition
Improve instruction definition for instruction abstraction.
2026-08-11 13:36:47 +02:00
CHatingPython cc88d6b6a0 refactor(furc): move IR to middle-end 2026-08-10 19:57:56 +02:00
CHatingPython 0e916c314a feat(furc/IR): move IR from furlang to furc
First try baby, whoo!
2026-08-10 18:34:10 +02:00
CHatingPython b9d50c62af feat(furc/ast): add visitor pattern for AST 2026-08-10 16:44:06 +02:00
CHatingPython 4b9f58b34f feat(furc/parser): add function calls 2026-08-09 18:34:53 +02:00
CHatingPython a420ebf69f refactor(furc/parser): improve statement parsing 2026-08-09 18:34:25 +02:00
CHatingPython e1ffa8a11d refactor(furc/lexer): improve token peeking 2026-08-09 18:32:40 +02:00
CHatingPython 2126b03069 feat(furc/parser): add if expression 2026-08-09 17:26:21 +02:00
CHatingPython 47e8b1bf51 feat(furc/parser): add pre- and post- conditions 2026-08-09 17:17:47 +02:00
CHatingPython 7ebc27cab9 feat(furc/parser): add while statement 2026-08-08 00:07:38 +02:00
CHatingPython 1db13b4e10 feat(furc/parser): add return statement 2026-08-08 00:07:23 +02:00
CHatingPython 84c6795ff8 feat(furc/parser): add if statement 2026-08-08 00:04:04 +02:00
CHatingPython 212b1e2384 feat(furc/parser): add variable and group expressions 2026-08-08 00:03:19 +02:00
CHatingPython ecdf6944a7 feat(furc/lexer): add integers 2026-08-07 11:59:45 +02:00
CHatingPython 79b6b9742d feat(furc/parser): parse binary and unary ops 2026-08-07 11:24:52 +02:00
CHatingPython 2d40b9337c feat(furc/AST): add binary and unary operations 2026-08-07 11:23:16 +02:00
CHatingPython db485af7f6 feat(furc): implement a basic parser 2026-08-06 10:54:54 +02:00
CHatingPython 854343a5a3 refactor(furc/lexer): distinguish keywords 2026-08-06 10:52:30 +02:00
CHatingPython 3faafe371f feat(furc): implement lexer 2026-08-05 12:35:49 +02:00
CHatingPython 4604f5186e feat(furc): introduce token 2026-08-05 11:03:43 +02:00
CHatingPython 224468446d refactor: remove furc for later remake 2026-08-04 13:09:46 +02:00
CHatingPython 549af109b9 fix: fix this mess
I am stupid
I am stupid
I am stupid
I can't believe how f- ricking stupid I am
2026-07-19 22:04:50 +02:00
CHatingPython 6dce7016ce test(furvm): introduce an add things test 2026-07-19 21:53:37 +02:00
CHatingPython a1e43576cc this code is a mess and I am losing my mind
This commit does not work and I am so down I don't even bother
committing to conventional commits until I fix this mess.
2026-07-19 21:38:00 +02:00
CHatingPython 9f950e45e7 feat(furas): introduce basic generator
Closes: #56
2026-07-17 20:43:34 +02:00
CHatingPython dfaa3081cb fix: use thing::true_type and fix module serialization 2026-07-17 20:41:27 +02:00
CHatingPython 46e92deed5 feat(furvm): introduce missing push instructions
Closes: #58
2026-07-14 12:55:34 +02:00
CHatingPython 42e71080a2 feat(furvm): introduce set instruction
Closes: #57
2026-07-14 12:40:47 +02:00
CHatingPython 03448d865e feat(furas): introduce a basic lexer
Closes: #56
2026-07-14 00:38:27 +02:00
CHatingPython cb845ca625 Merge branch 'master' into furas 2026-07-13 20:34:06 +02:00
CHatingPython 798287ef47 refactor: remove ReturnValue instruction 2026-07-13 20:33:37 +02:00
CHatingPython 466d7f003b chore: initialize furas 2026-07-13 19:12:13 +02:00
CHatingPython ff3fce53f5 feat: add return type to function
Closes: #51
2026-07-13 17:28:22 +02:00
CHatingPython 9b280fab22 refactor(furvm): improve references
Closes: #53
2026-07-12 00:03:39 +02:00
CHatingPython e1b75ccc8e feat(furvm): introduce length of operator
Closes: #54
2026-07-11 23:17:59 +02:00
CHatingPython cd715ae779 feat(furvm): introduce pointer type
Closes: #35
2026-07-11 22:44:18 +02:00
CHatingPython 8d3859ce70 refactor(furvm): improve primitive types
Improve primitive types and binary operations.

Closes: #55
2026-07-11 02:28:23 +02:00
CHatingPython 3c0588e8db refactor(furvm): improve type system
Closes: #52
2026-07-11 00:45:09 +02:00
CHatingPython abbc1714c1 refactor(furvm): enforce argument types 2026-07-09 15:02:37 +02:00
CHatingPython b7da2f3d32 feat: add missing furlang/utility/hash.hpp
I forgot to stage it from untracked lol
2026-07-08 00:20:52 +02:00
CHatingPython d49b4f5bc7 refactor: improve functions
Closes: #49
2026-07-08 00:19:37 +02:00
CHatingPython 7de645d323 refactor: replace type_p with type_ref in thing
Damn, this was such a great call.

Closes: #46
2026-07-07 19:29:30 +02:00
CHatingPython 2519560451 refactor: remove name from function
Closes: #47
2026-07-06 22:06:53 +02:00
CHatingPython 7c74850aff feat: introduce array and get instruction 2026-07-06 10:53:48 +02:00
CHatingPython 81a439b2ad refactor: add size argument to array type
Closes: #43
2026-07-05 23:15:40 +02:00
CHatingPython b913fc0c8b refactor: rename list to array
Closes: #42
2026-07-05 22:41:53 +02:00
CHatingPython ee9eadeea8 feat: introduce sizeof instruction
Closes: #34
2026-07-05 22:33:15 +02:00
CHatingPython 642d57622a feat: introduce pointerof operation
Introduce pointerof operation and do some side-quests along the way.

Refs: #34
Closes: #17
2026-07-05 14:54:16 +02:00
CHatingPython 9690ac3617 feat: add core module to context
Closes: #45
2026-07-05 13:31:15 +02:00
CHatingPython f89f643870 feat: implement module loading
I couldn't care less to commit all the changes separately after fight
with this stupid codebase.
Also I am closing the issue since serializing contexts meant doing
something like jars which I don't want to do right now. I'll reopen the
issue though for sure clueless.

Closes: #23
2026-07-04 18:41:06 +02:00
CHatingPython 100c542fe1 refactor: serialize types in module
Refs: #23
2026-07-04 16:15:40 +02:00
CHatingPython cf284592fd fix: fix handle's copy constructors 2026-07-04 16:14:46 +02:00
CHatingPython f5ebc767ab fix: fix module's function serialization
Refs: #23
2026-07-04 13:20:08 +02:00
CHatingPython 484e16963f refactor: move thing allocator to a separate file
Closes: #44
2026-07-03 19:51:09 +02:00
CHatingPython 575054b75a feat: introduce import type
Closes: #39
2026-07-03 19:43:18 +02:00
CHatingPython a7ab214ce3 refactor: make context inherit module handle container
Refs: #39
2026-07-03 19:41:28 +02:00
CHatingPython e087c11008 feat: add types to module
Refs: #39
2026-07-03 19:41:27 +02:00
CHatingPython 2164ab0d97 refactor: move type to separate file 2026-07-03 19:41:10 +02:00
CHatingPython 11b05b8c18 fix: fix handle class
Fix handle class for headers without reference counting.
2026-07-03 18:42:06 +02:00
CHatingPython 6ec4710eaa feat: introduce reference instruction 2026-07-01 21:58:33 +02:00
CHatingPython 09252c3ec3 feat: introduce list type
Closes: #33
2026-07-01 21:39:59 +02:00
CHatingPython 3d461048cc feat: introduce reference thing type
Closes: #32
2026-07-01 16:56:57 +02:00
CHatingPython 482ab859ed refactor: rename thing_type to type
Rename thing_type to type and use shared pointer instead of value in
thing.
2026-07-01 16:25:46 +02:00
CHatingPython 42f020977e feat: introduce more integer thing types
Closes: #31
2026-06-29 20:43:00 +02:00
CHatingPython 6ab9254746 refactor: improve thing types
Change thing_t enumeration to thing_type structure.

Closes: #30
2026-06-29 20:36:42 +02:00
CHatingPython 8466902281 feat(IR): improve IR functions
Add access specifiers and parameter counts to furlang's IR functions.
2026-06-28 13:21:13 +02:00
CHatingPython 7b609f87c9 feat(furc): introduce function call expression and call instruction 2026-06-28 13:21:13 +02:00
CHatingPython 0bcbe1d7d1 feat(furc): add access specifiers to functions
Introduce public and private access specifiers to functions.

Closes: #26
2026-06-28 13:21:05 +02:00
CHatingPython 8702298cd2 refactor(furvm): improve functions
Define native functions inside the module instead of the function
itself (native functions hold only the key to the definition), introduce
private functions, add parameter count to function and fix some bugs.

Closes: #20
Closes: #25
2026-06-28 12:37:36 +02:00
CHatingPython 55932f6113 feat(furvm): add iterator functions to handle container 2026-06-28 12:36:31 +02:00
CHatingPython a3db4ef1e1 feat(furvm): add type function to thing 2026-06-28 12:35:46 +02:00
CHatingPython 079f6326a4 Merge pull request 'furc' (#24) from furc into master
Reviewed-on: KPGPMC/furlang#24
2026-06-24 16:29:25 +00:00
CHatingPython d8d81851d3 feat(furc): introduce native and import functions
Closes: #19
2026-06-24 18:26:01 +02:00
CHatingPython abeb5b390b feat(furc): introduce function declaration parameters
Closes: #16
2026-06-24 18:06:40 +02:00
CHatingPython a61f5c194b refactor(furc): improve function declarations
Refs: #16
2026-06-24 15:52:14 +02:00
CHatingPython d497e2de45 feat: introduce arrow tokens
Refs: #16
2026-06-24 15:50:37 +02:00
CHatingPython 1965ab26af Merge pull request 'Types for furc' (#22) from furc-types into master
Reviewed-on: KPGPMC/furlang#22
Closes: #15
2026-06-24 13:32:30 +00:00
CHatingPython 70b7b6a2f4 feat: introduce parse type function
Refs: #15
2026-06-24 15:30:45 +02:00
CHatingPython b5dcd6690a feat: introduce AST type class
Refs: #15
2026-06-24 15:30:28 +02:00
CHatingPython 0baf896c3a feat: introduce int32 keyword
Refs: #15
2026-06-24 15:27:29 +02:00
CHatingPython 318b3e8a65 Merge pull request 'Furvm backend for furc' (#18) from furvm-gen into master
Reviewed-on: KPGPMC/furlang#18
2026-06-24 12:58:16 +00:00
CHatingPython 7d2e748c27 feat(furc): introduce furvm backend to furc
Closes: #14
2026-06-24 14:56:49 +02:00
CHatingPython e485a7900b feat(furvm): introduce handle dispatch function 2026-06-24 14:53:50 +02:00
CHatingPython 68e1f8fda0 Merge branch 'great-refactor' into master
Closes #12
2026-06-24 04:54:30 +02:00
CHatingPython 8909a0cfff refactor(furc): bring back ADCE to post process
Refs: #12
2026-06-23 15:21:12 +02:00
CHatingPython dad2c180d6 refactor(furc): bring back SCCP to post process
Refs: #12
2026-06-23 15:15:01 +02:00
CHatingPython 4478a839b7 refactor(furc): improve the SSA optimization class
Improve and rename the SSA optimization class to post process.

Refs: #12
2026-06-23 14:54:28 +02:00
CHatingPython 9549a7b61a docs(furvm): document the thing
Refs: #12
2026-06-21 20:40:31 +02:00
CHatingPython 51483db283 docs(furvm): document the function
Refs: #12
2026-06-21 20:17:07 +02:00
CHatingPython 3759c0361f docs(furvm): document the handle 2026-06-21 20:08:41 +02:00
CHatingPython 1ead2f6592 docs(furvm): document the function
Refs: #12
2026-06-21 19:45:59 +02:00
CHatingPython b6d3387388 docs(furvm): document the executor
Refs: #12
2026-06-21 19:40:33 +02:00
CHatingPython 3a2fa32ce1 fix!: remove references to context::collect
I forgor to do that earlier lol

Refs: #12
2026-06-20 12:56:43 +02:00
CHatingPython f2294079a0 docs(furvm): document the context
Refs: #12
2026-06-20 12:42:42 +02:00
CHatingPython 39f873a984 docs(furvm): document detail folder
Refs: #12
2026-06-20 12:26:53 +02:00
CHatingPython d46aa45c1f feat(furvm): add on release callback to refcount handles
Refs: #12
2026-06-20 12:19:53 +02:00
CHatingPython 190aa8f985 refactor(furc): move arena allocator out of the parser
Refs: #12
2026-06-20 11:56:15 +02:00
CHatingPython f80abd68ad refactor(furvm): introduce a furvm.hpp header
Refs: #12
2026-06-20 11:32:01 +02:00
CHatingPython 99666495e7 refactor(furvm): improve serialization
Refs: #12
2026-06-19 15:14:25 +02:00
CHatingPython 6f023765b5 chore: clang-tidy 2026-06-19 15:13:56 +02:00
CHatingPython 65e5fdf324 refactor(furvm): move bad access exceptions to exceptions.hpp
Refs: #12
2026-06-19 14:26:09 +02:00
CHatingPython 5a1b7c5aa6 refactor(furvm): expose context thing allocator
Refs: #12
2026-06-19 14:15:05 +02:00
CHatingPython f14f3854e2 fix(furvm): fix thing allocator
Refs: #12
2026-06-19 11:55:30 +02:00
CHatingPython 6fbdb2299b refactor(furvm): refactor import function
Refs: #12
2026-06-19 11:36:20 +02:00
CHatingPython ec2c81fc56 refactor(furvm): improve the handle system
Absolute slop

Refs: #12
2026-06-18 23:58:37 +02:00
CHatingPython e9b38e95a2 refactor(furvm): integrate handle system into things
Sloppy af

Refs: #12
2026-06-17 20:19:53 +02:00
CHatingPython 00bcc0e6a5 refactor(furvm): use the handle system
Refs: #12
2026-06-17 13:59:54 +02:00
CHatingPython e8ccaa6390 feat(furvm): implement a handle
Refs: #12
2026-06-16 16:55:43 +02:00
CHatingPython c2b32c9604 refactor(furvm): rename handle suffixes to ids
Refs: #12
2026-06-16 16:21:49 +02:00
CHatingPython c9f714642c refactor(furvm): refactor thing
A pretty sloppy thing class refactor.

Refs: #12
2026-06-16 14:59:29 +02:00
CHatingPython 0dfa8c1c02 refactor(furvm): refactor executor
Refs: #12
2026-06-16 14:45:36 +02:00
CHatingPython 78acfb6ee2 refactor(furvm): refactor function
Refs: #12
2026-06-16 12:48:16 +02:00
CHatingPython 43dac1ec6d refactor(furvm): refactor mod
Refs: #12
2026-06-16 12:05:55 +02:00
CHatingPython b35a274e81 refactor(furlang/ir): rename module to mod
Refs: #12
2026-06-16 11:25:41 +02:00
CHatingPython 63ea593c1e Merge branch 'furvm' into ssa 2026-06-15 17:40:26 +02:00
CHatingPython 8dfc880c07 feat(furvm): introduce comparison operators 2026-06-15 00:06:50 +02:00
CHatingPython eec25e3aea feat(furvm): introduce jump and jump not zero instructions 2026-06-14 23:49:29 +02:00
CHatingPython e809eb82c7 fix(furvm): handle import function in function move operators 2026-06-14 23:48:43 +02:00
CHatingPython 9fccaefbfc Merge branch 'furvm' into ssa 2026-06-14 15:20:28 +02:00
CHatingPython 759ccad7a6 feat(furvm): add serializer 2026-06-14 15:19:12 +02:00
CHatingPython f6f716e8c9 feat(furvm): add variables 2026-06-14 14:22:45 +02:00
CHatingPython a262014348 feat(furc/ssa): implement de-ssa phase
Refs: #2
2026-06-14 12:52:14 +02:00
CHatingPython 986015bc09 feat(furc/ssa): implement copy propagation
Refs: #2
2026-06-14 11:59:39 +02:00
CHatingPython ac1e226b4b feat(furc/ssa): add dead code elimination
Refs: #2
2026-06-13 22:33:57 +02:00
CHatingPython d0f3637539 feat(furc/ssa): implement basic constant propagation
Refs: #2
2026-06-13 14:46:16 +02:00
CHatingPython f1f62fad8c refactor(furc/ssa): change to semi-pruned SSA
Refs: #2
2026-06-13 12:38:01 +02:00
CHatingPython 065a2af699 refactor(furc/ssa): fix typo
Refs: #2
2026-06-13 12:20:20 +02:00
CHatingPython 767970c69b feat(furc): introduce while loop statement
Refs: #2
2026-06-12 23:10:26 +02:00
CHatingPython 97c01bc96c refactor(furc/ssa): add exit instruction awareness
Refs: #2
2026-06-12 23:09:36 +02:00
CHatingPython abd975be23 refactor(furc/ssa): revert back to maximal SSA
Refs: #2
2026-06-12 23:05:33 +02:00
CHatingPython be386cf230 feat(furc/ssa): implement renaming
Refs: #2
2026-06-12 22:07:26 +02:00
CHatingPython a14252bd1d refactor(furlang/ir): improve instruction interface
Refs: #2
2026-06-12 14:45:26 +02:00
CHatingPython 6b92e19b23 refactor(furlang/ir): add version to register_operand
Refs: #2
2026-06-12 14:07:09 +02:00
CHatingPython 865691df26 feat(furc, ssa): implement phi instruction placement
Refs: #2
2026-06-12 13:45:35 +02:00
CHatingPython b4fda8d7d0 feat(furc): start working on ssa optimizations
Compute dominance frontiers.

Refs: #2
2026-06-12 12:21:52 +02:00
CHatingPython 3f6fcc56ff feat(furlang): introduce phi instruction
Refs: #2
2026-06-12 09:43:42 +02:00
118 changed files with 9847 additions and 5818 deletions
+9 -2
View File
@@ -11,11 +11,13 @@ Checks: >
-bugprone-assignment-in-if-condition,
-bugprone-easily-swappable-parameters,
-bugprone-multi-level-implicit-pointer-conversion,
-bugprone-unintended-char-ostream-output,
-readability-function-cognitive-complexity,
-readability-magic-numbers,
-readability-redundant-access-specifiers,
-readability-use-anyofallof,
-readability-named-parameter,
-readability-convert-member-functions-to-static,
-performance-enum-size,
-cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
@@ -28,7 +30,12 @@ Checks: >
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-non-private-member-variables-in-classes,
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-use-enum-class,
-bugprone-forward-declaration-namespace,
-bugprone-tagged-union-member-count,
-bugprone-unchecked-optional-access
WarningsAsErrors: "*"
@@ -95,4 +102,4 @@ CheckOptions:
- key: readability-identifier-naming.EnumConstantCase
value: CamelCase
- key: readability-identifier-naming.ScopedEnumConstantCase
value: CamelCase
value: CamelCase
+6 -5
View File
@@ -4,16 +4,16 @@ set -e
BUILD_DIR="build"
echo "Running clang-format..."
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' || true)
FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(cpp|hpp)$' | grep -v 'deps/' || true)
if [ -z "$FILES" ]; then
echo "No C/C++ files to check"
exit 0
fi
git diff -U0 --cached | \
echo "Running clang-format..."
git diff -U0 --cached -- $FILES | \
python3 <(curl -s https://raw.githubusercontent.com/llvm/llvm-project/refs/heads/main/clang/tools/clang-format/clang-format-diff.py) -p1 -i
echo "Running clang-tidy..."
@@ -21,7 +21,8 @@ echo "Running clang-tidy..."
for file in $FILES; do
clang-tidy \
"$file" \
--header-filter="^(?!.*deps/).*" \
-p "$BUILD_DIR"
done
echo "pre-commit checks passed"
echo "pre-commit checks passed"
+3
View File
@@ -0,0 +1,3 @@
[submodule "deps/isocline"]
path = deps/isocline
url = https://github.com/daanx/isocline
+11 -4
View File
@@ -11,9 +11,16 @@ find_package(Doxygen)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_subdirectory(furlang)
add_subdirectory(furvm)
add_subdirectory(furc)
add_subdirectory(src/furlang)
add_subdirectory(src/furvm)
add_subdirectory(src/furc)
add_subdirectory(src/furas)
add_subdirectory(src/disfuras)
add_subdirectory(src/furdb)
# Dependencies
set(IC_USE_CXX ON)
add_subdirectory(${CMAKE_SOURCE_DIR}/deps/isocline)
if(DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/docs)
@@ -24,4 +31,4 @@ if(DOXYGEN_FOUND)
doxygen_add_docs(doxygen_doc ${CMAKE_SOURCE_DIR}/furlang/ ${CMAKE_SOURCE_DIR}/furvm/ ${CMAKE_SOURCE_DIR}/furc/
ALL WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen")
endif()
endif()
+1
View File
@@ -0,0 +1 @@
Checks: '-*'
Vendored Submodule
+1
Submodule deps/isocline added at 8d6dc1ef95
+35
View File
@@ -0,0 +1,35 @@
type arr = array $s8 10
func println $arr = native println
public func main = #main
main:
array $arr
dup
store %0
lenof
store %1
push $s32 0
loop:
dup
load %1
ge
jnz #end
body:
dup
load %0
swap
push $s8 69
set
push $s32 1
add
jmp #loop
end:
drop
load %0
call $arr println
ret
+38
View File
@@ -0,0 +1,38 @@
type arr = array $s8 10
type arrSlice = slice $s8
func println $arrSlice = native println
public func main = #main
main:
array $arr
store %0
push $s32 2
store %1
push $s32 0
loop:
dup
load %1
ge
jnz #end
body:
dup
load %0
swap
push $s8 69
set
push $s32 1
add
jmp #loop
end:
drop
load %0
push $u32 0
load %1
slice
call $arrSlice println
ret
+25
View File
@@ -0,0 +1,25 @@
func println $u32 = native println
allocate uwu
public func main = #main
main:
push $u32 0
storeg %uwu
loop_header:
loadg %uwu
push $u32 10
ge
jnz #loop_end
loop_body:
loadg %uwu
call $u32 println
loadg %uwu
push $u32 1
add
storeg %uwu
jmp #loop_header
loop_end:
ret
+7
View File
@@ -0,0 +1,7 @@
func println $s32 = native println
public func main = #main
main:
push $s32 69
call $s32 println
ret
-16
View File
@@ -1,16 +0,0 @@
file(GLOB_RECURSE FURC_SRCS "src/**.cpp")
file(GLOB_RECURSE FURC_HDRS "include/**.hpp")
add_library(libfurc ${FURC_SRCS} ${FURC_HDRS})
target_include_directories(libfurc PUBLIC include/)
target_compile_definitions(libfurc PRIVATE LIBFURC)
set_target_properties(libfurc PROPERTIES PREFIX "")
target_link_libraries(libfurc PUBLIC furlang)
add_executable(furc src/main.cpp)
target_link_libraries(furc PRIVATE libfurc)
include(GoogleTest)
file(GLOB_RECURSE FURC_TESTS "test/**.cpp")
add_executable(furc_tests ${FURC_TESTS})
target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main)
gtest_discover_tests(furc_tests)
-140
View File
@@ -1,140 +0,0 @@
#ifndef FURC_AST_DECLARATION_HPP
#define FURC_AST_DECLARATION_HPP
#include "furc/ast/node.hpp"
#include "furc/ast/statement.hpp"
#include <string>
namespace furc {
namespace ast {
/**
* @brief Declaration node type.
*/
enum class declaration_node_t {
Func, /**< Function declaration. */
FuncDef, /**< Function definition. */
};
/**
* @brief Declaration AST node interface.
*/
class declaration_node : public statement_node, public abstract_node {
public:
/**
* @brief Construct a new declaration AST node.
*
* @param location Node location.
*/
declaration_node(struct location location)
: abstract_node(location) {}
public:
/**
* @brief Returns this node's category.
*
* @return node_t::Declaration.
*/
node_t category() const override { return node_t::Declaration; }
/**
* @brief Returns this node's statement type.
*
* @return statement_node_t::Declaration.
*/
statement_node_t statement_type() const final { return statement_node_t::Declaration; }
/**
* @brief Returns this node's declaration type.
*
* @return The declaration type.
*/
virtual declaration_node_t declaration_type() const = 0;
protected:
bool equal(const node& rhs) const override;
};
/**
* @brief Function declaration AST node.
*/
class function_declaration_node : public declaration_node {
public:
/**
* @brief Construct a new function declaration node object from name token.
*
* @param location Node location.
* @param name Name of the function.
*/
template <typename T>
function_declaration_node(struct location location, T&& name)
: declaration_node(location), p_name(std::forward<T>(name)) {}
public:
/**
* @brief Returns this node's declaration type.
*
* @return declaration_node_t::FunctionDeclaration.
*/
declaration_node_t declaration_type() const override { return declaration_node_t::Func; }
/**
* @brief Returns function's name.
*
* @return Name of the function.
*/
std::string name() const { return p_name; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
protected:
/**
* @brief Name of the function.
*/
std::string p_name;
};
/**
* @brief Function definition AST node.
*/
class function_definition_node final : public function_declaration_node {
public:
/**
* @brief Construct a new function definition node object from name and body.
*
* @param location Node location.
* @param name Name of the function.
* @param body Body of the function.
*/
template <typename T>
function_definition_node(struct location location, T&& name, body&& body)
: function_declaration_node(location, std::forward<T>(name)), m_body(std::move(body)) {}
public:
/**
* @brief Returns this node's declaration type.
*
* @return declaration_node_t::FunctionDefinition.
*/
declaration_node_t declaration_type() const override { return declaration_node_t::FuncDef; }
/**
* @brief Returns function's body.
*
* @return Body of the function.
*/
const body& body() const { return m_body; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
struct body m_body;
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_DECLARATION_HPP
-356
View File
@@ -1,356 +0,0 @@
#ifndef FURC_AST_EXPRESSION_HPP
#define FURC_AST_EXPRESSION_HPP
#include "furc/ast/node.hpp"
#include "furc/ast/statement.hpp"
namespace furc {
namespace ast {
/**
* @brief Expression node type.
*/
enum class expression_node_t {
Literal, /**< Literal */
VarRead, /**< Variable read expression */
Unaryop, /**< Unary operation expression */
Binop, /**< Binary operation expression */
VarAssign, /**< Variable assignment expression */
};
/**
* @brief Expression AST node.
*/
class expression_node : public statement_node, public abstract_node {
public:
/**
* @brief Construct a new expression AST node.
*
* @param location Node location.
*/
expression_node(struct location location)
: abstract_node(location) {}
public:
/**
* @brief Returns this node's category.
*
* @return node_t::Expression.
*/
node_t category() const override { return node_t::Expression; }
/**
* @brief Returns this node's statement type.
*
* @return statement_node_t::Expression.
*/
statement_node_t statement_type() const override { return statement_node_t::Expression; }
/**
* @brief Returns this node's expression type.
*
* @return The expression type.
*/
virtual expression_node_t expression_type() const = 0;
protected:
bool equal(const node& rhs) const override;
};
/**
* @brief Var read expression AST node.
*/
class var_read_expression_node final : public expression_node {
public:
/**
* @brief Construct a new var read expression node object from a name handle.
*
* @param location Node location.
* @param name Handle to the name.
*/
template <typename T>
var_read_expression_node(struct location location, T&& name)
: expression_node(location), m_name(std::forward<T>(name)) {}
/**
* @brief Returns the variable's name.
*
* @return Name of the variable.
*/
const std::string& get_name() const { return m_name; }
/**
* @brief Returns the variable's name.
*
* @return Name of the variable.
*/
std::string&& move_name() { return std::move(m_name); }
public:
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::VarRead.
*/
expression_node_t expression_type() const override { return expression_node_t::VarRead; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
std::string m_name;
};
/**
* @brief Unary operation node type.
*/
enum class unaryop_expression_node_t {
Positive, /**< Positive (unary plus) */
Negative, /**< Negative (unary minus) */
PrefixIncrement, /**< Prefix increment */
PostfixIncrement, /**< Postfix increment */
PrefixDecrement, /**< Prefix decrement */
PostfixDecrement, /**< Postfix decrement */
};
/**
* @brief Unary operation expression AST node.
*/
class unary_op_expression_node final : public expression_node {
public:
/**
* @brief Construct a new unaryop expression node object from type and expression node handle.
*
* @param location Node location.
* @param type Operation type.
* @param node Handle to the inner expression node.
*/
unary_op_expression_node(struct location location, unaryop_expression_node_t type, expression_node_p&& node)
: expression_node(location), m_type(type), m_node(std::move(node)) {}
/**
* @brief Sets this node's inner expression.
*
* @param node New node handle.
*/
void set_node(expression_node_p&& node) { m_node = std::move(node); }
/**
* @brief Returns the type of this node's operation.
*
* @return The operation type.
*/
unaryop_expression_node_t type() const { return m_type; }
/**
* @brief Returns this node's inner expression.
*
* @return The inner expression.
*/
const expression_node_p& get_node() const { return m_node; }
/**
* @brief Returns this node's inner expression.
*
* @return The inner expression.
*/
expression_node_p& get_node() { return m_node; }
/**
* @brief Moves this node's inner expression.
*
* @return The moved inner expression.
*/
expression_node_p&& move_node() { return std::move(m_node); }
public:
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::Unaryop.
*/
expression_node_t expression_type() const override { return expression_node_t::Unaryop; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
unaryop_expression_node_t m_type;
expression_node_p m_node; /**< The inner expression. */
};
/**
* @brief Binary operation expression node type.
*/
enum class binop_expression_node_t {
None = 0, /**< None */
Add, /**< Addition */
Sub, /**< Subtraction */
Mul, /**< Multiplication */
Div, /**< Division */
Mod, /**< Modulo */
Equal, /**< Equality */
NotEqual, /**< Inequality */
LessThan, /**< Less */
GreaterThan, /**< Greater */
LessEqual, /**< Less or equal */
GreaterEqual, /**< Greater or equal */
};
/**
* @brief Binary operation expression AST node.
*/
class binary_op_expression_node final : public expression_node {
public:
/**
* @brief Construct a new binary operation expression AST node.
*
* @param location Node location.
* @param type Binary operation type.
* @param lhs Left-hand-side expression.
* @param rhs Right-hand-side expression.
*/
binary_op_expression_node(struct location location,
binop_expression_node_t type,
expression_node_p&& lhs,
expression_node_p&& rhs)
: expression_node(location), m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {}
/**
* @brief Returns this node's binary operation type.
*
* @return The binary operation type.
*/
binop_expression_node_t type() const { return m_type; };
/**
* @brief Returns this node's left-hand-side expression.
*
* @return The left-hand-side expression.
*/
const expression_node_p& lhs() const { return m_lhs; };
/**
* @brief Returns this node's left-hand-side expression.
*
* @return The left-hand-side expression.
*/
expression_node_p& lhs() { return m_lhs; };
/**
* @brief Moves this node's left-hand-side expression.
*
* @return The moved left-hand-side expression.
*/
expression_node_p&& move_lhs() { return std::move(m_lhs); };
/**
* @brief Returns this node's right-hand-side expression.
*
* @return The right-hand-side expression.
*/
const expression_node_p& rhs() const { return m_rhs; };
/**
* @brief Returns this node's right-hand-side expression.
*
* @return The right-hand-side expression.
*/
expression_node_p& rhs() { return m_rhs; };
/**
* @brief Moves this node's right-hand-side expression.
*
* @return The moved right-hand-side expression.
*/
expression_node_p&& move_rhs() { return std::move(m_rhs); };
public:
expression_node_t expression_type() const override { return expression_node_t::Binop; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
binop_expression_node_t m_type;
expression_node_p m_lhs;
expression_node_p m_rhs;
};
/**
* @brief Variable assignment expression AST node.
*/
class var_assign_expression_node final : public expression_node {
public:
/**
* @brief Construct a new variable assignment expression AST node.
*
* @param location Node location.
* @param lhs Left-hand-side expression handle.
* @param rhs Right-hand-side expression handle.
*/
var_assign_expression_node(struct location location, expression_node_p&& lhs, expression_node_p&& rhs)
: expression_node(location),
m_compound(binop_expression_node_t::None),
m_lhs(std::move(lhs)),
m_rhs(std::move(rhs)) {}
/**
* @brief Construct a new compound variable assignment expression AST node.
*
* @param location Node location.
* @param compound Compound operation type.
* @param lhs Left-hand-side expression handle.
* @param rhs Right-hand-side expression handle.
*/
var_assign_expression_node(struct location location,
binop_expression_node_t compound,
expression_node_p&& lhs,
expression_node_p&& rhs)
: expression_node(location), m_compound(compound), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)) {}
/**
* @brief Returns this node's compound operation type.
*
* @return The compound operation type.
*/
binop_expression_node_t compound() const { return m_compound; }
/**
* @brief Returns this node's left-hand-side expression.
*
* @return The left-hand-side expression.
*/
const expression_node_p& lhs() const { return m_lhs; }
/**
* @brief Returns this node's right-hand-side expression.
*
* @return The right-hand-side expression.
*/
const expression_node_p& rhs() const { return m_rhs; }
public:
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::VarAssign.
*/
expression_node_t expression_type() const override { return expression_node_t::VarAssign; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
binop_expression_node_t m_compound;
expression_node_p m_lhs;
expression_node_p m_rhs;
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_EXPRESSION_HPP
-241
View File
@@ -1,241 +0,0 @@
#ifndef FURC_AST_FWD_HPP
#define FURC_AST_FWD_HPP
#include "furc/diag.hpp"
#include "furlang/result.hpp"
#include <cstdint>
#include <memory>
#include <vector>
namespace furc {
/**
* @brief Abstract Syntax Tree definitions.
*/
namespace ast {
/**
* @brief AST error.
*/
struct error {
location location; /**< Location of the error. */
/**
* @brief Compares two AST errors for equality.
*
* @param other Error to compare against.
* @return true if the errors are equal.
*/
bool operator==(const error& other) const { return location == other.location; }
/**
* @brief Compares two AST errors for inequality.
*
* @param other Error to compare against.
* @return true if the errors are not equal.
*/
bool operator!=(const error& other) const { return !this->operator==(other); }
/**
* @brief Prints an AST error to output stream.
*
* @param os Output stream.
* @param error AST error to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const error& error);
};
class node;
/**
* @brief Alias for a shared pointer to node.
*
* @tparam T AST node type.
*/
template <typename T = node>
using node_p = std::shared_ptr<T>;
/**
* @brief Alias for node result.
*
* @tparam T AST node type.
*/
template <typename T = node>
using node_r = furlang::result<node_p<T>, error>;
class expression_node;
using expression_node_p = node_p<expression_node>; /**< Alias for a shared pointer to expression_node. */
using expression_node_r = node_r<expression_node>; /**< Alias for expression_node result */
class declaration_node;
using declaration_node_p = node_p<declaration_node>; /**< Alias for a shared pointer to declaration_node. */
using declaration_node_r = node_r<declaration_node>; /**< Alias for declaration_node result */
class statement_node;
using statement_node_p = node_p<statement_node>; /**< Alias for a shared pointer to statement_node. */
using statement_node_r = node_r<statement_node>; /**< Alias for statement_node result */
class program_node;
using program_node_p = node_p<program_node>; /**< Alias for a shared pointer to program_node. */
using program_node_r = node_r<program_node>; /**< Alias for program_node result */
/**
* @brief Literal node type.
*/
enum class literal_node_t {
String, /**< String literal. */
Integer, /**< Integer literal. */
};
template <typename, literal_node_t>
class literal_node;
/**
* @brief String literal AST node.
*/
using string_literal_node = literal_node<std::string, literal_node_t::String>;
using string_literal_node_p = node_p<string_literal_node>; /**< Alias for a shared pointer to string_literal_node */
using string_literal_node_r = node_r<string_literal_node>; /**< Alias for string_literal_node result */
/**
* @brief Integer literal AST node.
*/
using integer_literal_node = literal_node<std::uint64_t, literal_node_t::Integer>;
using integer_literal_node_p = node_p<integer_literal_node>; /**< Alias for a shared pointer to integer_literal_node */
using integer_literal_node_r = node_r<integer_literal_node>; /**< Alias for integer_literal_node result */
class var_read_expression_node;
using var_read_expression_node_p =
node_p<var_read_expression_node>; /**< Alias for a shared pointer to var_read_expression_node. */
using var_read_expression_node_r = node_r<var_read_expression_node>; /**< Alias for var_read_expression_node result */
class unary_op_expression_node;
using unary_op_expression_node_p =
node_p<unary_op_expression_node>; /**< Alias for a shared pointer to unaryop_expression_node. */
using unary_op_expression_node_r = node_r<unary_op_expression_node>; /**< Alias for unaryop_expression_node result */
class binary_op_expression_node;
using binary_op_expression_node_p =
node_p<binary_op_expression_node>; /**< Alias for a shared pointer to binop_expression_node. */
using binary_op_expression_node_r = node_r<binary_op_expression_node>; /**< Alias for binop_expression_node result */
class var_assign_expression_node;
using var_assign_expression_node_p =
node_p<var_assign_expression_node>; /**< Alias for a shared pointer to var_assign_expression_node. */
using var_assign_expression_node_r =
node_r<var_assign_expression_node>; /**< Alias for var_assign_expression_node result */
/**
* @brief List of statements.
*/
struct body {
/**
* @brief Location of the opening curly.
*/
location begin;
/**
* @brief Location of the closing curly.
*/
location end;
/**
* @brief List of statements.
*/
std::vector<statement_node_r> statements;
/**
* @brief Compares two bodies for equality.
*
* @param rhs Body to compare against.
* @return true if the bodies are equal.
*/
bool operator==(const body& rhs) const {
return begin == rhs.begin && end == rhs.end && statements == rhs.statements;
}
/**
* @brief Compares two bodies for inequality.
*
* @param rhs Body to compare against.
* @return true if the bodies are not equal.
*/
bool operator!=(const body& rhs) const { return !this->operator==(rhs); }
/**
* @brief Prints a body to an output stream.
*
* @param os Output stream.
* @param body Body to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const body& body);
};
/**
* @brief Alias for body result.
* @see body
*/
using body_r = furlang::result<body, error>;
class function_declaration_node;
using function_declaration_node_p =
node_p<function_declaration_node>; /**< Alias for a shared pointer to function_declaration_node. */
using function_declaration_node_r =
node_r<function_declaration_node>; /**< Alias for function_declaration_node result */
class function_definition_node;
using function_definition_node_p =
node_p<function_definition_node>; /**< Alias for a shared pointer to function_definition_node. */
using function_definition_node_r = node_r<function_definition_node>; /**< Alias for function_definition_node result */
class return_statement_node;
using return_statement_node_p =
node_p<return_statement_node>; /**< Alias for a shared pointer to return_statement_node. */
using return_statement_node_r = node_r<return_statement_node>; /**< Alias for return_statement_node result */
class if_statement_node;
using if_statement_node_p = node_p<if_statement_node>; /**< Alias for a shared pointer to if_statement_node. */
using if_statement_node_r = node_r<if_statement_node>; /**< Alias for if_statement_node result */
class compound_statement_node;
using compound_statement_node_p =
node_p<compound_statement_node>; /**< Alias for a shared pointer to compound_statement_node. */
using compound_statement_node_r = node_r<compound_statement_node>; /**< Alias for compound_statement_node result */
} // namespace ast
} // namespace furc
#endif // FURC_AST_FWD_HPP
-102
View File
@@ -1,102 +0,0 @@
// NOLINTBEGIN(portability-template-virtual-member-function)
#ifndef FURC_AST_LITERAL_HPP
#define FURC_AST_LITERAL_HPP
#include "furc/ast/expression.hpp"
#include "furc/ast/node.hpp"
namespace furc {
namespace ast {
/**
* @brief Literal AST node.
*/
template <typename ValueType, literal_node_t LiteralType>
class literal_node : public expression_node {
public:
using value_type = std::remove_reference_t<ValueType>; /**< Value type. */
public:
/**
* @brief Construct a new literal AST node.
*
* @param location Node location.
*/
template <typename = std::enable_if_t<std::is_default_constructible_v<ValueType>>>
literal_node(struct location location)
: expression_node(location) {}
/**
* @brief Construct a new literal AST node.
*
* @param location Node location.
* @param value Node value to copy.
*/
literal_node(struct location location, const value_type& value)
: expression_node(location), p_value(value) {}
/**
* @brief Construct a new literal AST node.
*
* @param location Node location.
* @param value Node value to move.
*/
literal_node(struct location location, value_type&& value)
: expression_node(location), p_value(std::move(value)) {}
/**
* @brief Construct a new literal AST node.
*
* @param location Node location.
* @param args Arguments to call value constructor with.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<ValueType, Args...>>>
literal_node(struct location location, Args&&... args)
: expression_node(location), p_value(std::forward<Args>(args)...) {}
public:
/**
* @brief Returns this node's category.
*
* @return node_t::Literal.
*/
node_t category() const override { return node_t::Literal; }
/**
* @brief Returns this node's expression type.
*
* @return expression_node_t::Literal.
*/
expression_node_t expression_type() const override { return expression_node_t::Literal; }
/**
* @brief Returns this node's literal type.
*
* @return The literal type.
*/
literal_node_t literal_type() const { return LiteralType; }
/**
* @brief Returns this node's value.
*
* @return A string view result.
*/
const value_type& value() const { return p_value; }
public:
void accept(visitor& visitor) const override { visitor.visit(*this); }
std::ostream& print(std::ostream& os) const override { return os << p_value; }
protected:
bool equal(const node& rhsNode) const override {
const auto& rhs = dynamic_cast<const literal_node&>(rhsNode);
return literal_type() == rhs.literal_type() && p_value == rhs.p_value;
}
protected:
value_type p_value; /**< Node value. */
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_LITERAL_HPP
// NOLINTEND(portability-template-virtual-member-function)
-166
View File
@@ -1,166 +0,0 @@
#ifndef FURC_AST_NODE_HPP
#define FURC_AST_NODE_HPP
#include "furc/ast/fwd.hpp"
#include "furc/ast/visitor.hpp"
namespace furc {
namespace ast {
/**
* @brief Node category.
*/
enum class node_t {
Literal, /**< Literal. */
Expression, /**< Expression. */
Statement, /**< Statement. */
Declaration, /**< Declaration. */
Program, /**< Program. */
};
/**
* @brief Prints a node type (category) to an output stream.
*
* @param os Output stream.
* @param type Type to print.
* @return The output stream.
*/
static inline std::ostream& operator<<(std::ostream& os, node_t type) {
switch (type) {
case node_t::Literal: return os << "literal";
case node_t::Expression: return os << "expression";
case node_t::Statement: return os << "statement";
case node_t::Declaration: return os << "declaration";
case node_t::Program: return os << "program";
}
return os;
}
/**
* @brief AST node interface.
*/
class node {
public:
node() = default;
virtual ~node() = default;
/**
* @brief Move constructor.
*
* Constructs a node by transferring the state of another node.
*
* @param other Node to move from.
*/
node(node&& other) = default;
node(const node&) = delete;
/**
* @brief Move constructor.
*
* Constructs a node by transferring the state of another node.
*
* @param other Node to move from.
*/
node& operator=(node&& other) = default;
node& operator=(const node&) = delete;
public:
/**
* @brief Returns the category of this AST node.
* @see node_t
*
* @return The node category.
*/
virtual node_t category() const = 0;
/**
* @brief Returns the location of this AST node.
* @see locaiton
*
* @return The location.
*/
virtual location location() const = 0;
public:
/**
* @brief Compares two nodes for equality.
*
* Nodes are equal if they have the same category and
* their derived-class-specific contents are equal.
*
* @param rhs Node to compare against.
* @return true if the nodes are equal.
*/
bool operator==(const node& rhs) const { return category() == rhs.category() && equal(rhs); }
/**
* @brief Compares two nodes for inequality.
*
* @param rhs Node to compare against.
* @return true if the nodes are not equal.
*/
bool operator!=(const node& rhs) const { return !this->operator==(rhs); }
public:
/**
* @brief Accepts a visitor.
*
* Dispatches to the visitor overload corresponding to the concrete node type.
*
* @param visitor Visitor instance.
*/
virtual void accept(visitor& visitor) const = 0;
/**
* @brief Prints a node to an output stream.
*
* @param os Output stream.
* @return The output stream.
*/
virtual std::ostream& print(std::ostream& os) const = 0;
/**
* @brief Prints a node to an output stream.
*
* Equivalent to calling node.print(os).
*
* @param os Output stream.
* @param node Node to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const node& node) {
return node.print(os << node.location() << ": ");
}
protected:
/**
* @brief Compares two nodes for equality.
*
* @param rhs Node to compare against.
* @return true if nodes are equal.
*/
virtual bool equal(const node& rhs) const = 0;
};
/**
* @brief An abstract AST node.
* @see node
*
* Implements location().
*/
class abstract_node : public virtual node {
public:
abstract_node(struct location location)
: p_location(location) {}
public:
/**
* @brief Returns the location of this AST node.
* @see locaiton
*
* @return The location.
*/
struct location location() const override { return p_location; }
protected:
struct location p_location; /**< Node location. */
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_NODE_HPP
-52
View File
@@ -1,52 +0,0 @@
#ifndef FURC_AST_PROGRAM_HPP
#define FURC_AST_PROGRAM_HPP
#include "furc/ast/node.hpp"
#include <vector>
namespace furc {
namespace ast {
/**
* @brief Program AST node.
*/
class program_node final : public abstract_node {
public:
/**
* @brief Construct a new program AST node.
*
* @param location Node location.
*/
program_node(struct location location)
: abstract_node(location) {}
node_t category() const override { return node_t::Program; }
public:
/**
* @brief Adds a declaration to this program.
*
* @param declaration Declaration to add.
*/
void push(declaration_node_p&& declaration) { m_declarations.push_back(std::move(declaration)); }
/**
* @brief Returns a list of declarations of this program.
*
* @return The list of this program's declarations.
*/
const std::vector<declaration_node_p>& declarations() const { return m_declarations; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
std::vector<declaration_node_p> m_declarations;
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_PROGRAM_HPP
-197
View File
@@ -1,197 +0,0 @@
#ifndef FURC_AST_STATEMENT_HPP
#define FURC_AST_STATEMENT_HPP
#include "furc/ast/node.hpp"
#include <optional>
namespace furc {
namespace ast {
/**
* @brief Statement node type.
*/
enum class statement_node_t {
Expression, /**< Expression */
Declaration, /**< Declaration */
Return, /**< Return statement */
If, /**< If statement */
Compound, /**< Compound statement */
};
/**
* @brief Statement AST node.
*/
class statement_node : public virtual node {
public:
/**
* @brief Returns this node's category.
*
* @return node_t::Statement.
*/
node_t category() const override { return node_t::Statement; }
/**
* @brief Returns this node's statement type.
*
* @return The statement type.
*/
virtual statement_node_t statement_type() const = 0;
protected:
bool equal(const node& rhs) const override;
};
/**
* @brief Return statement AST node.
*/
class return_statement_node final : public statement_node, public abstract_node {
public:
using value_type = std::optional<expression_node_p>; /**< Value type. */
public:
/**
* @brief Construct a new return statement AST node.
*/
return_statement_node(struct location location)
: abstract_node(location) {}
/**
* @brief Construct a new return statement AST node.
*
* @param location Node location.
* @param value Return value handle.
*/
return_statement_node(struct location location, expression_node_p&& value)
: abstract_node(location), m_value(std::move(value)) {}
public:
/**
* @brief Returns this node's return value handle.
*
* @return The return value handle.
*/
value_type value() const { return m_value; }
public:
/**
* @brief Returns this node's statement type.
*
* @return statement_node_t::Return.
*/
statement_node_t statement_type() const override { return statement_node_t::Return; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
value_type m_value; /**< Return value handle. */
};
/**
* @brief If statement AST node.
*/
class if_statement_node final : public statement_node, public abstract_node {
public:
/**
* @brief Construct a new if statement AST node.
*
* @param location Node location.
* @param cond Condition expression handle.
* @param then Then statement handle.
*/
if_statement_node(struct location location, expression_node_p&& cond, statement_node_p&& then)
: abstract_node(location), m_cond(std::move(cond)), m_then(std::move(then)) {}
/**
* @brief Construct a new if statement AST node.
*
* @param location Node location.
* @param cond Condition expression handle.
* @param then Then statement handle.
* @param elze Else statement handle.
*/
if_statement_node(struct location location,
expression_node_p&& cond,
statement_node_p&& then,
statement_node_p&& elze)
: abstract_node(location), m_cond(std::move(cond)), m_then(std::move(then)), m_else(std::move(elze)) {}
public:
/**
* @brief Returns this node's condition expression handle.
*
* @return The condition expression handle.
*/
expression_node_p cond() const { return m_cond; }
/**
* @brief Returns this node's then statement handle.
*
* @return The then statement handle.
*/
const statement_node_p& then() const { return m_then; }
/**
* @brief Returns this node's else statement handle.
*
* @return The else statement handle.
*/
const std::optional<statement_node_p>& elze() const { return m_else; }
public:
/**
* @brief Returns this node's statement type.
*
* @return statement_node_t::If.
*/
statement_node_t statement_type() const override { return statement_node_t::If; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
expression_node_p m_cond; /**< The condition expression handle */
statement_node_p m_then; /**< The then statement handle */
std::optional<statement_node_p> m_else; /**< The else statement handle */
};
/**
* @brief Compound statement AST node.
*/
class compound_statement_node final : public statement_node, public abstract_node {
public:
/**
* @brief Construct a new compound statement AST node.
*
* @param location Node location.
* @param body Body handle.
*/
compound_statement_node(struct location location, body&& body)
: abstract_node(location), m_body(std::move(body)) {}
public:
/**
* @brief Returns this node's body handle.
*
* @return The body handle.
*/
const body& body() const { return m_body; }
public:
/**
* @brief Returns this node's statement type.
*
* @return statement_node_t::Compound.
*/
statement_node_t statement_type() const override { return statement_node_t::Compound; }
public:
void accept(visitor& visitor) const override;
std::ostream& print(std::ostream& os) const override;
protected:
bool equal(const node& rhs) const override;
private:
struct body m_body; /**< The body handle. */
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_STATEMENT_HPP
-136
View File
@@ -1,136 +0,0 @@
#ifndef FURC_AST_VISITOR_HPP
#define FURC_AST_VISITOR_HPP
#include "furc/ast/fwd.hpp"
namespace furc {
namespace ast {
/**
* @brief Visitor pattern class for AST nodes.
*/
class visitor {
public:
visitor() = default;
virtual ~visitor() = default;
/**
* @brief Move constructor.
*/
visitor(visitor&&) = default;
/**
* @brief Move constructor.
*/
visitor& operator=(visitor&&) = default;
/**
* @brief Copy constructor.
*/
visitor(const visitor&) = default;
/**
* @brief Copy constructor.
*/
visitor& operator=(const visitor&) = default;
public:
/**
* @brief Visit a string_literal_node.
* @see string_literal_node
*
* @param node Node.
*/
virtual void visit(const string_literal_node& node) {}
/**
* @brief Visit a integer_literal_node.
* @see integer_literal_node
*
* @param node Node.
*/
virtual void visit(const integer_literal_node& node) {}
/**
* @brief Visit a var_read_expression_node.
* @see var_read_expression_node
*
* @param node Node.
*/
virtual void visit(const var_read_expression_node& node) {}
/**
* @brief Visit a unaryop_expression_node.
* @see unaryop_expression_node
*
* @param node Node.
*/
virtual void visit(const unary_op_expression_node& node) {}
/**
* @brief Visit a binop_expression_node.
* @see binop_expression_node
*
* @param node Node.
*/
virtual void visit(const binary_op_expression_node& node) {}
/**
* @brief Visit a var_assign_expression_node.
* @see var_assign_expression_node
*
* @param node Node.
*/
virtual void visit(const var_assign_expression_node& node) {}
/**
* @brief Visit a function_declaration_node.
* @see function_declaration_node
*
* @param node Node.
*/
virtual void visit(const function_declaration_node& node) {}
/**
* @brief Visit a function_definition_node.
* @see function_definition_node
*
* @param node Node.
*/
virtual void visit(const function_definition_node& node) {}
/**
* @brief Visit a return_statement_node.
* @see return_statement_node
*
* @param node Node.
*/
virtual void visit(const return_statement_node& node) {}
/**
* @brief Visit a if_statement_node.
* @see if_statement_node
*
* @param node Node.
*/
virtual void visit(const if_statement_node& node) {}
/**
* @brief Visit a compound_statement_node.
* @see compound_statement_node
*
* @param node Node.
*/
virtual void visit(const compound_statement_node& node) {}
/**
* @brief Visit an AST error.
*
* @param error AST error.
*/
virtual void visit_error(const ast::error& error) {}
};
} // namespace ast
} // namespace furc
#endif // FURC_AST_VISITOR_HPP
-42
View File
@@ -1,42 +0,0 @@
#ifndef FURC_DIAG_HPP
#define FURC_DIAG_HPP
#include <ostream>
#include <string_view>
namespace furc {
/**
* @brief A location in file.
*/
struct location {
std::string_view filename; /**< File's name */
std::size_t line = 0; /**< Line */
std::size_t column = 0; /**< Column */
/**
* @brief Compare two locations for equality.
*
* @param rhs Location to compare against.
* @return true if the locations are equal.
*/
bool operator==(const location& rhs) const {
return filename == rhs.filename && line == rhs.line && column == rhs.column;
}
/**
* @brief Compare two locations for inequality.
*
* @param rhs Location to compare against.
* @return true if the locations are not equal.
*/
bool operator!=(const location& rhs) const { return !this->operator==(rhs); }
};
static inline std::ostream& operator<<(std::ostream& os, const location& location) {
return os << location.filename << ':' << location.line + 1 << ':' << location.column + 1;
}
} // namespace furc
#endif // FURC_DIAG_HPP
-58
View File
@@ -1,58 +0,0 @@
#ifndef FURC_FRONT_IR_GENERATOR_HPP
#define FURC_FRONT_IR_GENERATOR_HPP
#include "furc/ast/fwd.hpp"
#include "furc/ast/visitor.hpp"
#include "furlang/ir/module.hpp"
#include <unordered_map>
namespace furc {
namespace front {
using ir_register = std::uint32_t;
class ir_generator final : public ast::visitor {
public:
ir_generator() = default;
~ir_generator() override = default;
ir_generator(ir_generator&&) = default;
ir_generator& operator=(ir_generator&&) = default;
ir_generator(const ir_generator&) = delete;
ir_generator& operator=(const ir_generator&) = delete;
public:
furlang::ir::module&& move_module() { return std::move(m_module); }
public:
void visit(const ast::function_definition_node& funcDef) override;
void visit(const ast::return_statement_node& returnStmt) override;
void visit(const ast::if_statement_node& node) override;
void visit(const ast::compound_statement_node& node) override;
void visit(const ast::string_literal_node& node) override;
void visit(const ast::integer_literal_node& node) override;
void visit(const ast::var_read_expression_node& node) override;
void visit(const ast::unary_op_expression_node& node) override;
void visit(const ast::binary_op_expression_node& node) override;
void visit(const ast::var_assign_expression_node& node) override;
private:
template <typename T, typename... Args>
void push(Args&&... args) {
if (!m_currentBlock->emplace<T>(std::forward<Args>(args)...)) {
throw std::runtime_error("block exited too soon");
}
}
furlang::ir::block_index push_block();
private:
furlang::ir::module m_module;
std::unique_ptr<furlang::ir::function> m_currentFunction;
std::shared_ptr<furlang::ir::block> m_currentBlock;
ir_register m_registerCounter = 0;
std::unordered_map<std::string_view, ir_register> m_variables;
};
} // namespace front
} // namespace furc
#endif // FURC_FRONT_IR_GENERATOR_HPP
-70
View File
@@ -1,70 +0,0 @@
#ifndef FURC_FRONT_LEXER_HPP
#define FURC_FRONT_LEXER_HPP
#include "furc/front/token.hpp"
namespace furc {
namespace front {
/**
* @brief Lexer.
*
* Furlang's lazy tokenizer.
*/
class lexer {
public:
lexer() = default;
/**
* @brief Construct a new lexer.
*
* @param filename Filename for debugging.
* @param content Content.
*/
lexer(std::string_view filename, std::string_view content);
~lexer() = default;
/**
* @brief Move constructor.
*/
lexer(lexer&&) = default;
lexer(const lexer&) = delete;
/**
* @brief Move constructor.
*/
lexer& operator=(lexer&&) = default;
lexer& operator=(const lexer&) = delete;
public:
/**
* @brief Returns a handle to next token.
*
* @return The token handle.
*/
token_r next_token();
/**
* @brief Checks whether the cursor is at the EOF.
*
* @return true if the cursor is at the EOF.
*/
bool empty() const { return m_cursor >= m_content.size(); }
private:
void next();
char get(std::size_t offset = 0) const;
void skip_spaces();
location current_location();
private:
std::string_view m_filename;
std::string_view m_content;
std::size_t m_cursor = 0;
std::size_t m_row = 0;
std::size_t m_lineStart = 0;
};
} // namespace front
} // namespace furc
#endif // FURC_FRONT_LEXER_HPP
-83
View File
@@ -1,83 +0,0 @@
#ifndef FURC_FRONT_PARSER_HPP
#define FURC_FRONT_PARSER_HPP
#include "furc/ast/fwd.hpp"
#include "furc/front/lexer.hpp"
#include "furlang/arena.hpp"
#include <vector>
namespace furc {
namespace front {
/**
* @brief Parser.
*
* Furlang's parser.
*/
class parser final {
public:
/**
* @brief Construct a new parser from content.
*
* @param filename Filename for debugging.
* @param content Content.
*/
parser(std::string_view filename, std::string_view content);
/**
* @brief Construct a new parser from file.
*
* Constructs a lexer with content read from file passed through \p filename.
*
* @param filename Name of the file.
*/
parser(std::string_view filename);
~parser() = default;
/**
* @brief Move constructor.
*/
parser(parser&&) = default;
parser(const parser&) = delete;
/**
* @brief Move constructor.
*/
parser& operator=(parser&&) = default;
parser& operator=(const parser&) = delete;
public:
/**
* @brief Returns a parsed program.
*
* @return Handle to an AST node of the program.
*/
ast::program_node_r parse() &;
private:
ast::declaration_node_r parse_declaration();
ast::statement_node_r parse_statement();
ast::expression_node_r parse_expression(std::uint32_t precedence = 16);
ast::expression_node_r parse_expression_primary();
ast::expression_node_r parse_expression_unary(std::uint32_t precedence);
ast::expression_node_r parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence);
ast::body_r parse_body();
private:
token_r next_token();
const token_r& peek_token();
token_r eat_token(token_t type);
private:
std::string m_filename;
std::string m_content;
lexer m_lexer;
furlang::arena m_arena;
std::vector<token_r> m_peekBuffer;
};
} // namespace front
} // namespace furc
#endif // FURC_FRONT_PARSER_HPP
-380
View File
@@ -1,380 +0,0 @@
#ifndef FURC_FRONT_TOKEN_HPP
#define FURC_FRONT_TOKEN_HPP
#include "furc/diag.hpp"
#include "furlang/result.hpp"
#include <cassert>
#include <cstdint>
#include <ostream>
#include <string_view>
namespace furc {
namespace front {
/**
* @brief Token type.
*/
enum class token_t {
None, /**< None */
Identifier, /**< Identifier */
String, /**< String */
Keyword, /**< Keyword */
Integer, /**< Integer */
LParen, /**< `(` */
RParen, /**< `)` */
LBrace, /**< `{` */
RBrace, /**< `}` */
LBracket, /**< `[` */
RBracket, /**< `]` */
Semicolon, /**< `;` */
Colon, /**< `:` */
Comma, /**< `,` */
Dot, /**< `.` */
Plus, /**< `+` */
Minus, /**< `-` */
Star, /**< `*` */
Slash, /**< `/` */
Percent, /**< `%` */
DPlus, /**< `++` */
DMinus, /**< `--` */
Eq, /**< `=` */
PlusEq, /**< `+=` */
MinusEq, /**< `-=` */
StarEq, /**< `*=` */
SlashEq, /**< `/=` */
PercentEq, /**< `%=` */
DEq, /**< `==` */
NotEq, /**< `!=` */
LessThan, /**< `<` */
GreaterThan, /**< `>` */
LessEq, /**< `<=` */
GreaterEq, /**< `>=` */
};
static inline std::ostream& operator<<(std::ostream& os, token_t type) {
switch (type) {
case token_t::None: return os << "none";
case token_t::Identifier: return os << "identifier";
case token_t::String: return os << "string";
case token_t::Keyword: return os << "keyword";
case token_t::Integer: return os << "integer";
case token_t::LParen: return os << "'('";
case token_t::RParen: return os << "')'";
case token_t::LBrace: return os << "'{'";
case token_t::RBrace: return os << "'}'";
case token_t::LBracket: return os << "'['";
case token_t::RBracket: return os << "']'";
case token_t::Semicolon: return os << "';'";
case token_t::Colon: return os << "':'";
case token_t::Comma: return os << "','";
case token_t::Dot: return os << "'.'";
case token_t::Plus: return os << "'+'";
case token_t::Minus: return os << "'-'";
case token_t::Star: return os << "'*'";
case token_t::Slash: return os << "'/'";
case token_t::Percent: return os << "'%'";
case token_t::DPlus: return os << "++";
case token_t::DMinus: return os << "--";
case token_t::Eq: return os << "=";
case token_t::PlusEq: return os << "+=";
case token_t::MinusEq: return os << "-=";
case token_t::StarEq: return os << "*=";
case token_t::SlashEq: return os << "/=";
case token_t::PercentEq: return os << "%=";
case token_t::DEq: return os << "==";
case token_t::NotEq: return os << "!=";
case token_t::LessThan: return os << "<";
case token_t::GreaterThan: return os << ">";
case token_t::LessEq: return os << "<=";
case token_t::GreaterEq: return os << ">=";
}
return os;
}
static inline std::string operator+(const std::string& str, token_t type) {
switch (type) {
case token_t::None: return str + "none";
case token_t::Identifier: return str + "identifier";
case token_t::String: return str + "string";
case token_t::Keyword: return str + "keyword";
case token_t::Integer: return str + "integer";
case token_t::LParen: return str + "'('";
case token_t::RParen: return str + "')'";
case token_t::LBrace: return str + "'{'";
case token_t::RBrace: return str + "'}'";
case token_t::LBracket: return str + "'['";
case token_t::RBracket: return str + "']'";
case token_t::Semicolon: return str + "';'";
case token_t::Colon: return str + "':'";
case token_t::Comma: return str + "','";
case token_t::Dot: return str + "'.'";
case token_t::Plus: return str + "'+'";
case token_t::Minus: return str + "'-'";
case token_t::Star: return str + "'*'";
case token_t::Slash: return str + "'/'";
case token_t::Percent: return str + "'%'";
case token_t::DPlus: return str + "++";
case token_t::DMinus: return str + "--";
case token_t::Eq: return str + "=";
case token_t::PlusEq: return str + "+=";
case token_t::MinusEq: return str + "-=";
case token_t::StarEq: return str + "*=";
case token_t::SlashEq: return str + "/=";
case token_t::PercentEq: return str + "%=";
case token_t::DEq: return str + "==";
case token_t::NotEq: return str + "!=";
case token_t::LessThan: return str + "<";
case token_t::GreaterThan: return str + ">";
case token_t::LessEq: return str + "<=";
case token_t::GreaterEq: return str + ">=";
}
return str;
}
/**
* @brief Keyword token.
*/
enum class keyword_token {
None, /**< None */
Func, /**< `func` */
Return, /**< `return` */
If, /**< `if` */
Else, /**< `else` */
};
static inline std::ostream& operator<<(std::ostream& os, keyword_token keyword) {
switch (keyword) {
case keyword_token::None: return os << "none";
case keyword_token::Func: return os << "func";
case keyword_token::Return: return os << "return";
case keyword_token::If: return os << "if";
case keyword_token::Else: return os << "else";
}
return os;
}
static inline std::string operator+(const std::string& str, keyword_token keyword) {
switch (keyword) {
case keyword_token::None: return str + "none";
case keyword_token::Func: return str + "func";
case keyword_token::Return: return str + "return";
case keyword_token::If: return str + "if";
case keyword_token::Else: return str + "else";
}
return str;
}
using integer_token = std::uint64_t; /**< Integer token. */
/**
* @brief Token.
*/
struct token {
location location; /**< Token location. */
token_t type = token_t::None; /**< Token type. */
/**
* @brief Token's value.
*/
union value {
/**
* @brief Null value. For token_t::None, token_t::Plus, token_t::Minus, etc.
* @see token_t::None
*/
std::nullptr_t null;
/**
* @brief String value. For token_t::Identifier and token_t::String.
* @see token_t::Identifier
* @see token_t::String
*/
std::string_view string;
/**
* @brief Keyword value. For token_t::Keyword.
* @see token_t::Keyword.
*/
keyword_token keyword;
/**
* @brief Integer value. For token_t::Integer.
* @see token_t::Integer
*/
integer_token integer;
/**
* @brief Construct a new null value.
*
* @param value Null value.
*/
value(std::nullptr_t value = nullptr)
: null(value) {}
/**
* @brief Construct a new string value.
*
* @param value The string.
*/
value(std::string_view value)
: string(value) {}
/**
* @brief Construct a new keyword value.
*
* @param value The keyword.
*/
value(keyword_token value)
: keyword(value) {}
/**
* @brief Construct a new integer value.
*
* @param value The integer.
*/
value(integer_token value)
: integer(value) {}
} value; /**< Token value. */
token() = default;
/**
* @brief Construct a new null token.
*
* @param location Token's location.
* @param type Token's type.
*/
token(struct location location, token_t type)
: location(location), type(type) {}
/**
* @brief Construct a new string token.
*
* @param location Token's location.
* @param type Token's type.
* @param value String value.
*/
token(struct location location, token_t type, std::string_view value)
: location(location), type(type), value(value) {}
/**
* @brief Construct a new keyword token.
*
* @param location Token's location.
* @param keyword Keyword value.
*/
token(struct location location, keyword_token keyword)
: location(location), type(token_t::Keyword), value(keyword) {}
/**
* @brief Construct a new integer token.
*
* @param location Token's location.
* @param integer Integer value.
*/
token(struct location location, integer_token integer)
: location(location), type(token_t::Integer), value(integer) {}
/**
* @brief Returns pointer to this token's value.
*
* @return Pointer to the token's value.
*/
union value* operator->() { return &value; }
/**
* @brief Returns constant pointer to this token's value.
*
* @return Pointer to the token's value.
*/
const union value* operator->() const { return &value; }
/**
* @brief Compares two tokens for equality.
*
* @param rhs Token to compare against.
* @return true if the tokens are equal.
*/
bool operator==(const token& rhs) const {
if (type != rhs.type) return false;
switch (type) {
case token_t::Identifier:
case token_t::String: return value.string == rhs.value.string;
case token_t::Keyword: return value.keyword == rhs.value.keyword;
case token_t::Integer: return value.integer == rhs.value.integer;
default: return true;
}
}
};
static inline std::ostream& operator<<(std::ostream& os, const token& token) {
switch (token.type) {
case token_t::Identifier:
case token_t::String: return os << token.value.string;
case token_t::Keyword: return os << token.value.keyword;
case token_t::Integer: return os << token.value.integer;
default: return os << token.type;
}
}
/**
* @brief Token error type
*/
enum class token_error_t {
EndOfFile, /**< End of file */
UnexpectedEof, /**< Unexpected end of file */
UnexpectedCharacter, /**< Unexpected character */
UnexpectedToken, /**< Unexpected character */
IntegerOverflow, /**< Integer overflow */
};
/**
* @brief Token error
*
* For token_r alias.
*/
struct token_error {
location location; /**< Error location. */
token_error_t type; /**< Error type. */
std::string message; /**< Error message. */
/**
* @brief Compares two token errors for equality.
*
* @param rhs Error to compare against.
* @return true if the errors are equal.
*/
bool operator==(const token_error& rhs) const {
return location == rhs.location && type == rhs.type && message == rhs.message;
}
/**
* @brief Compares two token errors for inequality.
*
* @param rhs Error to compare against.
* @return true if the errors are not equal.
*/
bool operator!=(const token_error& rhs) const { return !this->operator==(rhs); }
/**
* @brief Prints a token error to an output stream.
*
* @param os Output stream.
* @param error Token error to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const token_error& error) {
return os << error.location << ": error: unknown";
}
};
using token_r = furlang::result<token, token_error>; /**< Alias to a token result. */
} // namespace front
} // namespace furc
#endif // FURC_FRONT_TOKEN_HPP
-215
View File
@@ -1,215 +0,0 @@
#include "furc/ast/declaration.hpp"
#include "furc/ast/expression.hpp"
#include "furc/ast/node.hpp"
#include "furc/ast/program.hpp"
#include "furc/ast/statement.hpp"
#include <ostream>
namespace furc::ast {
std::ostream& operator<<(std::ostream& os, const error& error) {
return os << error.location << ": ERROR: unknown";
}
bool expression_node::equal(const node& rhs) const {
return expression_type() == dynamic_cast<const expression_node&>(rhs).expression_type();
}
void var_read_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& var_read_expression_node::print(std::ostream& os) const {
return os << m_name;
}
bool var_read_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const var_read_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_name == rhs.m_name;
}
std::ostream& operator<<(std::ostream& os, unaryop_expression_node_t type) {
switch (type) {
case unaryop_expression_node_t::Positive: return os << "+";
case unaryop_expression_node_t::Negative: return os << "-";
case unaryop_expression_node_t::PrefixIncrement:
case unaryop_expression_node_t::PostfixIncrement: return os << "++";
case unaryop_expression_node_t::PrefixDecrement:
case unaryop_expression_node_t::PostfixDecrement: return os << "--";
}
return os;
}
void unary_op_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& unary_op_expression_node::print(std::ostream& os) const {
if (m_node == nullptr) return os;
switch (m_type) {
case unaryop_expression_node_t::Positive:
case unaryop_expression_node_t::Negative:
case unaryop_expression_node_t::PrefixIncrement:
case unaryop_expression_node_t::PrefixDecrement: return os << '(' << m_type << *m_node << ')';
case unaryop_expression_node_t::PostfixIncrement:
case unaryop_expression_node_t::PostfixDecrement: return os << '(' << *m_node << m_type << ')';
}
return os;
}
bool unary_op_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const unary_op_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_type == rhs.m_type && m_node == rhs.m_node;
}
std::ostream& operator<<(std::ostream& os, binop_expression_node_t type) {
switch (type) {
default:
case binop_expression_node_t::None: return os;
case binop_expression_node_t::Add: return os << '+';
case binop_expression_node_t::Sub: return os << '-';
case binop_expression_node_t::Mul: return os << '*';
case binop_expression_node_t::Div: return os << '/';
case binop_expression_node_t::Mod: return os << '%';
case binop_expression_node_t::Equal: return os << "==";
case binop_expression_node_t::NotEqual: return os << "!=";
case binop_expression_node_t::LessThan: return os << '<';
case binop_expression_node_t::GreaterThan: return os << '>';
case binop_expression_node_t::LessEqual: return os << "<=";
case binop_expression_node_t::GreaterEqual: return os << ">=";
}
}
void binary_op_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& binary_op_expression_node::print(std::ostream& os) const {
if (m_type == binop_expression_node_t::None) return os;
return os << '(' << *m_lhs << ' ' << m_type << ' ' << *m_rhs << ')';
}
bool binary_op_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const binary_op_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_type == rhs.m_type && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs;
}
void var_assign_expression_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& var_assign_expression_node::print(std::ostream& os) const {
return os << '(' << *m_lhs << ' ' << m_compound << "= " << *m_rhs << ')';
}
bool var_assign_expression_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const var_assign_expression_node&>(rhsNode);
return expression_node::equal(rhsNode) && m_compound == rhs.m_compound && m_lhs == rhs.m_lhs && m_rhs == rhs.m_rhs;
}
bool declaration_node::equal(const node& rhs) const {
return declaration_type() == dynamic_cast<const declaration_node&>(rhs).declaration_type();
}
void function_declaration_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& function_declaration_node::print(std::ostream& os) const {
return os << "function " << p_name << " declaration";
}
bool function_declaration_node::equal(const node& rhs) const {
return declaration_node::equal(rhs) && p_name == dynamic_cast<const function_declaration_node&>(rhs).p_name;
}
void function_definition_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& function_definition_node::print(std::ostream& os) const {
function_declaration_node::print(os);
os << ":\n";
for (const auto& entry : m_body.statements)
os << entry << '\n';
return os << m_body.end << ": " << p_name << " end";
}
bool function_definition_node::equal(const node& rhs) const {
return function_declaration_node::equal(rhs) && m_body == dynamic_cast<const function_definition_node&>(rhs).m_body;
}
bool statement_node::equal(const node& rhs) const {
return statement_type() == dynamic_cast<const statement_node&>(rhs).statement_type();
}
void return_statement_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& return_statement_node::print(std::ostream& os) const {
os << "return statement";
if (m_value.has_value()) return os << ' ' << *m_value.value();
return os;
}
bool return_statement_node::equal(const node& rhs) const {
return statement_node::equal(rhs) && m_value == dynamic_cast<const return_statement_node&>(rhs).m_value;
}
void if_statement_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& if_statement_node::print(std::ostream& os) const {
os << "if " << *m_cond << ", then:\n";
os << m_then;
if (m_else.has_value()) os << *m_else.value();
return os;
}
bool if_statement_node::equal(const node& rhsNode) const {
const auto& rhs = dynamic_cast<const if_statement_node&>(rhsNode);
return statement_node::equal(rhs) && m_cond == rhs.m_cond && m_then == rhs.m_then && m_else == rhs.m_else;
}
void compound_statement_node::accept(visitor& visitor) const {
visitor.visit(*this);
}
std::ostream& compound_statement_node::print(std::ostream& os) const {
return os << m_body;
}
bool compound_statement_node::equal(const node& rhs) const {
return statement_node::equal(rhs) && m_body == dynamic_cast<const compound_statement_node&>(rhs).m_body;
}
void program_node::accept(visitor& visitor) const {
for (const auto& decl : m_declarations) {
decl->accept(visitor);
}
}
std::ostream& program_node::print(std::ostream& os) const {
os << "program:";
for (const auto& handle : m_declarations) {
os << '\n' << handle;
}
return os;
}
bool program_node::equal(const node& rhs) const {
return m_declarations == dynamic_cast<const program_node&>(rhs).m_declarations;
}
std::ostream& operator<<(std::ostream& os, const body& body) {
os << "body:";
for (const auto& stmt : body.statements) {
os << '\n' << stmt;
}
return os;
}
} // namespace furc::ast
-151
View File
@@ -1,151 +0,0 @@
#include "furc/front/ir_generator.hpp"
#include "furc/ast/declaration.hpp" // IWYU pragma: keep
#include "furc/ast/expression.hpp" // IWYU pragma: keep
#include "furc/ast/literal.hpp" // IWYU pragma: keep
#include "furc/ast/statement.hpp" // IWYU pragma: keep
#include <cassert>
namespace furc::front {
namespace {
namespace ir = furlang::ir;
}
void ir_generator::visit(const ast::function_definition_node& funcDef) {
m_currentFunction = std::make_unique<furlang::ir::function>(std::string(funcDef.name()));
push_block();
for (const auto& stmt : funcDef.body().statements) {
stmt.value()->accept(*this);
}
m_currentBlock->emplace<ir::return_instruction>();
m_module.push(std::move(m_currentFunction));
}
void ir_generator::visit(const ast::return_statement_node& returnStmt) {
if (returnStmt.value().has_value()) {
returnStmt.value().value()->accept(*this);
push<ir::return_instruction>(ir::operand::new_reg(m_registerCounter - 1));
} else {
push<ir::return_instruction>();
}
}
void ir_generator::visit(const ast::if_statement_node& node) {
node.cond()->accept(*this);
ir_register cond = m_registerCounter - 1;
push<ir::branch_cond_instruction>(ir::operand::new_reg(cond),
m_currentFunction->blocks().size(),
m_currentFunction->blocks().size() + 1);
push_block(); // then block
node.then()->accept(*this);
if (node.elze().has_value()) {
m_currentBlock->emplace<ir::branch_instruction>(m_currentFunction->blocks().size() + 1);
push_block(); // else block
node.elze().value()->accept(*this);
}
m_currentBlock->emplace<ir::branch_instruction>(m_currentFunction->blocks().size());
push_block(); // merge block
}
void ir_generator::visit(const ast::compound_statement_node& node) {
for (const auto& stmt : node.body().statements) {
stmt.value()->accept(*this);
}
}
void ir_generator::visit(const ast::string_literal_node& node) {
push<furlang::ir::assign_instruction>(ir::operand::new_string(node.value()),
ir::operand::new_reg(m_registerCounter++));
}
void ir_generator::visit(const ast::integer_literal_node& node) {
push<furlang::ir::assign_instruction>(ir::operand::new_integer(node.value()),
ir::operand::new_reg(m_registerCounter++));
}
void ir_generator::visit(const ast::var_read_expression_node& node) {
if (auto it = m_variables.find(node.get_name()); it != m_variables.end()) {
push<furlang::ir::assign_instruction>(ir::operand::new_reg(it->second),
ir::operand::new_reg(m_registerCounter++));
} else {
throw std::runtime_error("unknown variable");
}
}
void ir_generator::visit(const ast::unary_op_expression_node& node) {
throw std::runtime_error("unimplemented");
}
static inline furlang::ir::binary_op_instruction_t binary_op_instruction_t(ast::binop_expression_node_t type) {
switch (type) {
case ast::binop_expression_node_t::Add: return furlang::ir::binary_op_instruction_t::Add;
case ast::binop_expression_node_t::Sub: return furlang::ir::binary_op_instruction_t::Sub;
case ast::binop_expression_node_t::Mul: return furlang::ir::binary_op_instruction_t::Mul;
case ast::binop_expression_node_t::Div: return furlang::ir::binary_op_instruction_t::Div;
case ast::binop_expression_node_t::Mod: return furlang::ir::binary_op_instruction_t::Mod;
case ast::binop_expression_node_t::Equal: return furlang::ir::binary_op_instruction_t::Eq;
case ast::binop_expression_node_t::NotEqual: return furlang::ir::binary_op_instruction_t::NotEq;
case ast::binop_expression_node_t::LessThan: return furlang::ir::binary_op_instruction_t::LessThan;
case ast::binop_expression_node_t::GreaterThan: return furlang::ir::binary_op_instruction_t::GreaterThan;
case ast::binop_expression_node_t::LessEqual: return furlang::ir::binary_op_instruction_t::LessEq;
case ast::binop_expression_node_t::GreaterEqual: return furlang::ir::binary_op_instruction_t::GreaterEq;
case ast::binop_expression_node_t::None:
default: throw std::runtime_error("unreachable");
}
}
void ir_generator::visit(const ast::binary_op_expression_node& node) {
node.lhs()->accept(*this);
ir_register lhs = m_registerCounter - 1;
node.rhs()->accept(*this);
ir_register rhs = m_registerCounter - 1;
ir_register dst = m_registerCounter++;
push<furlang::ir::binary_op_instruction>(binary_op_instruction_t(node.type()),
ir::operand::new_reg(lhs),
ir::operand::new_reg(rhs),
ir::operand::new_reg(dst));
}
void ir_generator::visit(const ast::var_assign_expression_node& node) {
node.rhs()->accept(*this);
ir_register rhs = m_registerCounter - 1;
assert(node.lhs()->expression_type() == ast::expression_node_t::VarRead);
auto lhs = std::dynamic_pointer_cast<ast::var_read_expression_node>(node.lhs());
ir_register reg = 0;
if (auto it = m_variables.find(lhs->get_name()); it != m_variables.end()) {
reg = it->second;
} else {
m_variables[lhs->get_name()] = reg = m_registerCounter++;
}
auto compound = node.compound();
if (compound != ast::binop_expression_node_t::None) {
push<ir::binary_op_instruction>(binary_op_instruction_t(compound),
ir::operand::new_reg(reg),
ir::operand::new_reg(rhs),
ir::operand::new_reg(reg));
} else {
push<ir::assign_instruction>(ir::operand::new_reg(rhs), ir::operand::new_reg(reg));
}
}
furlang::ir::block_index ir_generator::push_block() {
if (!m_currentFunction->blocks().empty() && !m_currentFunction->blocks().back()->has_exit()) {
throw std::runtime_error(
"block " + std::to_string(m_currentFunction->blocks().size() - 1) + " is lacking an exit");
}
ir::block_index index = m_currentFunction->blocks().size();
m_currentBlock = m_currentFunction->push();
return index;
}
} // namespace furc::front
-188
View File
@@ -1,188 +0,0 @@
#include "furc/front/lexer.hpp"
#include <cctype>
#include <limits>
#include <map>
#include <string>
#include <unordered_map>
namespace furc::front {
using namespace std::string_literals;
lexer::lexer(std::string_view filename, std::string_view content)
: m_filename(filename), m_content(content) {}
token_r lexer::next_token() {
skip_spaces();
while (m_cursor + 2 <= m_content.size() && m_content[m_cursor] == '/') {
if (m_content[m_cursor + 1] == '/') {
m_cursor += 2;
while (m_content[m_cursor] != '\n') {
next();
}
} else if (m_content[m_cursor + 1] == '*') {
m_cursor += 2;
while (m_cursor + 2 < m_content.size()) {
if (m_content[m_cursor + 1] == '*') {
next();
} else if (m_content[m_cursor + 0] != '*' || m_content[m_cursor + 1] != '/') {
next();
next();
} else {
break;
}
}
if (m_cursor + 2 >= m_content.size()) {
next();
return token_r(
token_error{ current_location(), token_error_t::UnexpectedEof, "before enclosing `*/`" });
}
m_cursor += 2;
} else {
break;
}
skip_spaces();
}
location location = current_location();
switch (get()) {
case '"': {
std::size_t begin = ++m_cursor;
while (m_cursor < m_content.size() && m_content[m_cursor] != '"')
++m_cursor;
if (m_cursor >= m_content.size()) {
return token_r(token_error{ current_location(), token_error_t::UnexpectedEof, "before enclosing '\"'" });
}
++m_cursor;
return { location, token_t::String, m_content.substr(begin, m_cursor - begin - 1) };
}
case std::char_traits<char>::eof(): return token_r(token_error{ current_location(), token_error_t::EndOfFile });
default: {
if (std::isdigit(get()) != 0) {
integer_token integer = 0;
integer_token max = std::numeric_limits<integer_token>::max();
integer_token upperBound = max / 10;
std::size_t start = m_cursor;
while (std::isdigit(get()) != 0) {
integer_token digit = get() - '0';
if (integer > upperBound || integer == upperBound && (integer - upperBound + digit) > (max % 10)) {
while (std::isdigit(get()) != 0)
++m_cursor;
return token_r(token_error{ location,
token_error_t::IntegerOverflow,
std::string(m_content.substr(start, m_cursor - start)) });
}
integer *= 10;
integer += digit;
++m_cursor;
}
return { location, integer };
}
if (std::isalnum(get()) != 0 || get() == '_') {
std::size_t start = m_cursor++;
while (std::isalnum(get()) != 0 || get() == '_')
next();
std::string_view value = m_content.substr(start, m_cursor - start);
static std::unordered_map<std::string_view, keyword_token> s_keywords = {
{ "func", keyword_token::Func },
{ "return", keyword_token::Return },
{ "if", keyword_token::If },
{ "else", keyword_token::Else },
};
if (auto it = s_keywords.find(value); it != s_keywords.end()) return { location, it->second };
return { location, token_t::Identifier, value };
}
struct compare {
bool operator()(const std::string_view& lhs, const std::string_view& rhs) const {
if (lhs.size() != rhs.size()) return lhs.size() > rhs.size();
return lhs < rhs;
}
};
static std::map<std::string_view, token_t, compare> s_tokens = {
{ "(", token_t::LParen },
{ ")", token_t::RParen },
{ "{", token_t::LBrace },
{ "}", token_t::RBrace },
{ "[", token_t::LBracket },
{ "]", token_t::RBracket },
{ ";", token_t::Semicolon },
{ ":", token_t::Colon },
{ ",", token_t::Comma },
{ ".", token_t::Dot },
{ "+", token_t::Plus },
{ "-", token_t::Minus },
{ "*", token_t::Star },
{ "/", token_t::Slash },
{ "%", token_t::Percent },
{ "++", token_t::DPlus },
{ "--", token_t::DMinus },
{ "=", token_t::Eq },
{ "+=", token_t::PlusEq },
{ "-=", token_t::MinusEq },
{ "*=", token_t::StarEq },
{ "/=", token_t::SlashEq },
{ "%=", token_t::PercentEq },
{ "==", token_t::DEq },
{ "!=", token_t::NotEq },
{ "<", token_t::LessThan },
{ ">", token_t::GreaterThan },
{ "<=", token_t::LessEq },
{ ">=", token_t::GreaterEq },
};
token_t type = token_t::None;
std::size_t length = 1;
while (m_cursor + length <= m_content.size()) {
auto it = s_tokens.find(m_content.substr(m_cursor, length));
if (it == s_tokens.end()) break;
type = it->second;
++length;
}
if (type != token_t::None) {
m_cursor += length - 1;
return { location, type };
}
return token_r(
token_error{ location, token_error_t::UnexpectedCharacter, std::string(m_content.substr(m_cursor, 1)) });
}
}
}
void lexer::next() {
if (m_cursor >= m_content.size()) return;
char ch = get();
++m_cursor;
if (ch == '\n') {
++m_row;
m_lineStart = m_cursor;
}
}
char lexer::get(std::size_t offset) const {
if (m_cursor + offset < m_content.size()) return m_content[m_cursor + offset];
return std::char_traits<char>::eof();
}
void lexer::skip_spaces() {
while (std::isspace(get()) != 0)
next();
}
location lexer::current_location() {
return { m_filename, m_row, m_cursor - m_lineStart };
}
} // namespace furc::front
-441
View File
@@ -1,441 +0,0 @@
#include "furc/front/parser.hpp"
#include "furc/ast/declaration.hpp" // IWYU pragma: keep
#include "furc/ast/expression.hpp" // IWYU pragma: keep
#include "furc/ast/literal.hpp" // IWYU pragma: keep
#include "furc/ast/program.hpp" // IWYU pragma: keep
#include "furc/ast/statement.hpp" // IWYU pragma: keep
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>
namespace furc::front {
using namespace std::string_literals;
parser::parser(std::string_view filename, std::string_view content)
: m_filename(filename), m_content(content), m_lexer(m_filename, m_content) {}
parser::parser(std::string_view filename)
: m_filename(filename) {
std::ifstream file(m_filename, std::ios_base::binary | std::ios_base::ate);
if (!file.is_open()) throw std::runtime_error("failed to open file "s.append(m_filename));
std::streampos size = file.tellg();
file.seekg(0);
m_content.resize(size);
file.read(m_content.data(), size);
m_lexer = { filename, m_content };
}
ast::program_node_r parser::parse() & {
auto program = m_arena.allocate_shared<ast::program_node>(location{ m_filename });
while (peek_token().has_value()) {
auto decl = parse_declaration();
if (decl.has_error()) {
program = nullptr;
} else if (program != nullptr) {
program->push(std::move(decl.value()));
}
}
return program != nullptr ? std::move(program) : ast::program_node_r(ast::error{ location{ m_filename } });
}
ast::declaration_node_r parser::parse_declaration() {
const auto& first = peek_token();
switch (first->type) {
case token_t::Keyword: {
auto first = next_token();
switch ((*first)->keyword) {
case keyword_token::Func: {
auto name = eat_token(token_t::Identifier);
if (name.has_error()) return ast::declaration_node_r(ast::error{ name.error().location });
auto tok = eat_token(token_t::LParen);
if (tok.has_error()) return ast::declaration_node_r(ast::error{ tok.error().location });
tok = eat_token(token_t::RParen);
if (tok.has_error()) return ast::declaration_node_r(ast::error{ tok.error().location });
const auto& peek = peek_token();
if (peek.has_error()) return ast::declaration_node_r(ast::error{ peek.error().location });
switch (peek->type) {
case token_t::LBrace: {
ast::body_r body = parse_body();
if (body.has_error()) return ast::declaration_node_r(ast::error{ body.error().location });
return m_arena.allocate_shared<ast::function_definition_node>(first->location,
name->value.string,
std::move(body.value()));
}
case token_t::Semicolon: {
m_peekBuffer.clear();
return m_arena.allocate_shared<ast::function_declaration_node>(first->location, name->value.string);
}
default: return ast::declaration_node_r(ast::error{ tok->location });
}
}
default: return ast::declaration_node_r(ast::error{ first->location });
}
}
case token_t::None:
case token_t::Identifier:
case token_t::Integer:
case token_t::LParen:
case token_t::RParen:
case token_t::LBrace:
case token_t::RBrace:
case token_t::LBracket:
case token_t::RBracket:
case token_t::Semicolon:
case token_t::Colon:
default: {
return ast::declaration_node_r(ast::error{ first->location });
}
}
}
ast::statement_node_r parser::parse_statement() {
const auto& tok = peek_token();
if (tok.has_error()) return ast::statement_node_r(ast::error{ tok.error().location });
auto location = tok->location;
switch (tok->type) {
case token_t::Keyword: {
switch (tok->value.keyword) {
case keyword_token::Return: {
auto tok = next_token();
if (peek_token()->type == token_t::Semicolon) {
next_token();
return m_arena.allocate_shared<ast::return_statement_node>(location);
}
auto value = parse_expression();
auto err = eat_token(token_t::Semicolon);
if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location });
return m_arena.allocate_shared<ast::return_statement_node>(location, std::move(value.value()));
}
case keyword_token::If: {
auto tok = next_token();
auto err = eat_token(token_t::LParen);
if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location });
auto cond = parse_expression();
err = eat_token(token_t::RParen);
if (err.has_error()) return ast::statement_node_r(ast::error{ err.error().location });
auto then = parse_statement();
if (then.has_error()) return ast::statement_node_r(ast::error{ then.error().location });
if (peek_token().has_value() && peek_token()->type == token_t::Keyword &&
peek_token()->value.keyword == keyword_token::Else) {
next_token();
auto elseBody = parse_statement();
if (elseBody.has_error()) return ast::statement_node_r(ast::error{ elseBody.error().location });
return m_arena.allocate_shared<ast::if_statement_node>(location,
std::move(cond.value()),
std::move(then.value()),
std::move(elseBody.value()));
}
return m_arena.allocate_shared<ast::if_statement_node>(location,
std::move(cond.value()),
std::move(then.value()));
}
case keyword_token::None:
case keyword_token::Func:
default: break;
}
}
case token_t::LBrace: {
auto body = parse_body();
if (body.has_error()) return ast::statement_node_r(ast::error{ body.error().location });
return m_arena.allocate_shared<ast::compound_statement_node>(location, std::move(body.value()));
}
default: break;
}
auto declaration = parse_declaration();
if (declaration.has_value()) return std::move(*declaration);
auto expression = parse_expression();
if (expression.has_value()) {
auto semi = eat_token(token_t::Semicolon);
if (semi.has_error()) return ast::statement_node_r(ast::error{ semi.error().location });
return std::move(*expression);
}
auto token = next_token();
return ast::statement_node_r(ast::error{ token->location });
}
ast::expression_node_r parser::parse_expression(std::uint32_t precedence) {
auto expr = parse_expression_unary(precedence);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
return parse_expression_rhs(std::move(expr.value()), precedence);
}
ast::expression_node_r parser::parse_expression_primary() {
const auto& tok = peek_token();
switch (tok->type) {
case token_t::Identifier: {
auto tok = next_token();
if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location });
return m_arena.allocate_shared<ast::var_read_expression_node>(tok->location, (*tok)->string);
}
case token_t::LParen: {
auto tok = next_token();
auto node = parse_expression();
auto err = eat_token(token_t::RParen);
if (err.has_error()) return ast::expression_node_r(ast::error{ err.error().location });
return node;
}
case token_t::String: {
auto tok = next_token();
if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location });
return m_arena.allocate_shared<ast::string_literal_node>(tok->location, (*tok)->string);
}
case token_t::Integer: {
auto tok = next_token();
if (tok.has_error()) return ast::expression_node_r(ast::error{ tok.error().location });
return m_arena.allocate_shared<ast::integer_literal_node>(tok->location, (*tok)->integer);
}
default: {
return ast::expression_node_r(ast::error{ tok->location });
}
}
}
struct unaryop_info {
ast::unaryop_expression_node_t type;
std::uint32_t precedence;
};
ast::expression_node_r parser::parse_expression_unary(std::uint32_t precedence) {
static std::unordered_map<token_t, unaryop_info> s_prefixes = {
{ token_t::Plus, unaryop_info{ ast::unaryop_expression_node_t::Positive, 3 } },
{ token_t::Minus, unaryop_info{ ast::unaryop_expression_node_t::Negative, 3 } },
{ token_t::DPlus, unaryop_info{ ast::unaryop_expression_node_t::PrefixIncrement, 2 } },
{ token_t::DMinus, unaryop_info{ ast::unaryop_expression_node_t::PrefixDecrement, 2 } },
};
std::shared_ptr<ast::unary_op_expression_node> result;
while (true) {
auto it = s_prefixes.find(peek_token()->type);
if (it == s_prefixes.end()) break;
auto current = it->second;
if (current.precedence >= precedence) break;
auto token = next_token();
ast::expression_node_p expression;
auto nextIt = s_prefixes.find(peek_token()->type);
if (nextIt != s_prefixes.end()) {
auto next = nextIt->second;
auto expr = parse_expression_unary(current.precedence + 1);
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
expression = std::move(std::move(expr.value()));
}
result = m_arena.allocate_shared<ast::unary_op_expression_node>(token->location,
current.type,
std::move(expression));
}
if (result == nullptr) return parse_expression_primary();
if (result->get_node() == nullptr) {
auto expr = parse_expression_primary();
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
result->set_node(std::move(std::move(expr.value())));
}
return result;
}
enum class associativity {
Left,
Right,
};
enum class rhsop_info_t {
Unaryop,
Binop,
Assignment,
};
struct rhsop_info {
rhsop_info_t type;
std::uint32_t precedence;
associativity associativity;
union {
ast::unaryop_expression_node_t unary;
ast::binop_expression_node_t binary;
ast::binop_expression_node_t assignment;
};
static rhsop_info create(ast::unaryop_expression_node_t type, std::uint32_t precedence) {
rhsop_info info{};
info.type = rhsop_info_t::Unaryop;
info.precedence = precedence;
info.associativity = associativity::Left;
info.unary = type;
return info;
}
static rhsop_info create(ast::binop_expression_node_t type,
std::uint32_t precedence,
enum associativity associativity) {
rhsop_info info{};
info.type = rhsop_info_t::Binop;
info.precedence = precedence;
info.associativity = associativity;
info.binary = type;
return info;
}
static rhsop_info create(ast::binop_expression_node_t compound = ast::binop_expression_node_t::None) {
rhsop_info info{};
info.type = rhsop_info_t::Assignment;
info.precedence = 14;
info.associativity = associativity::Right;
info.assignment = compound;
return info;
}
};
ast::expression_node_r parser::parse_expression_rhs(ast::expression_node_p&& init, std::uint32_t precedence) {
static std::unordered_map<token_t, rhsop_info> s_rhsops = {
{ token_t::Plus, rhsop_info::create(ast::binop_expression_node_t::Add, 5, associativity::Left) },
{ token_t::Minus, rhsop_info::create(ast::binop_expression_node_t::Sub, 5, associativity::Left) },
{ token_t::Star, rhsop_info::create(ast::binop_expression_node_t::Mul, 4, associativity::Left) },
{ token_t::Slash, rhsop_info::create(ast::binop_expression_node_t::Div, 4, associativity::Left) },
{ token_t::Percent, rhsop_info::create(ast::binop_expression_node_t::Mod, 5, associativity::Left) },
{ token_t::DPlus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixIncrement, 1) },
{ token_t::DMinus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixDecrement, 1) },
{ token_t::DMinus, rhsop_info::create(ast::unaryop_expression_node_t::PostfixDecrement, 1) },
{ token_t::Eq, rhsop_info::create() },
{ token_t::PlusEq, rhsop_info::create(ast::binop_expression_node_t::Add) },
{ token_t::MinusEq, rhsop_info::create(ast::binop_expression_node_t::Sub) },
{ token_t::StarEq, rhsop_info::create(ast::binop_expression_node_t::Mul) },
{ token_t::SlashEq, rhsop_info::create(ast::binop_expression_node_t::Div) },
{ token_t::PercentEq, rhsop_info::create(ast::binop_expression_node_t::Mod) },
{ token_t::DEq, rhsop_info::create(ast::binop_expression_node_t::Equal, 10, associativity::Left) },
{ token_t::NotEq, rhsop_info::create(ast::binop_expression_node_t::NotEqual, 10, associativity::Left) },
{ token_t::LessThan, rhsop_info::create(ast::binop_expression_node_t::LessThan, 9, associativity::Left) },
{ token_t::GreaterThan, rhsop_info::create(ast::binop_expression_node_t::GreaterThan, 9, associativity::Left) },
{ token_t::LessEq, rhsop_info::create(ast::binop_expression_node_t::LessEqual, 9, associativity::Left) },
{ token_t::GreaterEq, rhsop_info::create(ast::binop_expression_node_t::GreaterEqual, 9, associativity::Left) },
};
ast::expression_node_p lhs = std::move(init);
while (peek_token().has_value()) {
auto it = s_rhsops.find(peek_token()->type);
if (it == s_rhsops.end()) return lhs;
rhsop_info current = it->second;
if (current.precedence >= precedence) return lhs;
auto opToken = next_token();
ast::expression_node_p rhs;
if (current.type != rhsop_info_t::Unaryop) {
auto expr = parse_expression_unary(current.precedence + 1); // unary prefix is always right-associative
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
rhs = std::move(expr.value());
}
auto nextIt = s_rhsops.find(peek_token()->type);
if (nextIt != s_rhsops.end()) {
rhsop_info next = nextIt->second;
auto expr = std::move(parse_expression_rhs(std::move(rhs),
current.precedence + static_cast<std::uint32_t>(current.associativity == associativity::Right)));
if (expr.has_error()) {
return ast::expression_node_r(ast::error{ expr.error().location });
}
if (current.type != rhsop_info_t::Unaryop) {
rhs = std::move(expr.value());
} else {
lhs = std::move(expr.value());
}
}
switch (current.type) {
case rhsop_info_t::Unaryop:
lhs = m_arena.allocate_shared<ast::unary_op_expression_node>(opToken->location,
current.unary,
std::move(lhs));
break;
case rhsop_info_t::Binop:
lhs = m_arena.allocate_shared<ast::binary_op_expression_node>(opToken->location,
current.binary,
std::move(lhs),
std::move(rhs));
break;
case rhsop_info_t::Assignment:
lhs = m_arena.allocate_shared<ast::var_assign_expression_node>(opToken->location,
current.assignment,
std::move(lhs),
std::move(rhs));
break;
}
}
return lhs;
}
ast::body_r parser::parse_body() {
ast::body body;
auto begin = eat_token(token_t::LBrace);
if (begin.has_error()) return ast::body_r(ast::error{ begin.error().location });
body.begin = begin->location;
while (!peek_token().has_error() && peek_token()->type != token_t::None && peek_token()->type != token_t::RBrace) {
body.statements.push_back(parse_statement());
}
auto end = eat_token(token_t::RBrace);
if (end.has_error()) return ast::body_r(ast::error{ end.error().location });
body.end = end->location;
return body;
}
token_r parser::next_token() {
if (!m_peekBuffer.empty()) {
auto token = std::move(m_peekBuffer.back());
m_peekBuffer.pop_back();
return token;
}
return m_lexer.next_token();
}
const token_r& parser::peek_token() {
if (m_peekBuffer.empty()) {
auto token = m_lexer.next_token();
return m_peekBuffer.emplace_back(std::move(token));
}
return m_peekBuffer.front();
}
token_r parser::eat_token(token_t type) {
auto token = next_token();
if (token.has_error()) return token;
if (token->type != type) {
if (token->type == token_t::None)
return token_r(token_error{ token->location, token_error_t::UnexpectedToken, ", expected " + type });
return token_r(
token_error{ token->location, token_error_t::UnexpectedToken, ""s + token->type + ", expected " + type });
}
return token;
}
} // namespace furc::front
-57
View File
@@ -1,57 +0,0 @@
#ifndef LIBFURC
#include "furc/ast/program.hpp"
#include "furc/front/ir_generator.hpp"
#include "furc/front/parser.hpp"
#include <iostream>
int main(void) {
try {
std::string programStr = R"(
func main() {
x = 5;
x -= 3;
if (x < 3) {
y = x * 2;
w = y;
} else {
y = x - 3;
}
w = x - y;
z = x + y;
}
)";
furc::front::parser parser("<TEMP>", programStr);
furc::front::ir_generator generator;
auto programResult = parser.parse();
if (programResult.has_error()) {
std::cerr << programResult.error() << '\n';
return 1;
}
const auto& program = *programResult;
program->accept(generator);
auto module = std::move(generator.move_module());
std::cout << "Generated IR:\n";
for (const auto& function : module.functions()) {
std::cout << function->name() << ":\n";
furlang::ir::block_index blockIndex = 0;
for (const auto& block : function->blocks()) {
std::cout << " # block " << blockIndex++ << '\n';
for (const auto& instruction : block->instructions()) {
std::cout << " " << *instruction << '\n';
}
std::cout << " " << *block->exit() << '\n';
}
}
return 0;
} catch (...) {
std::cerr << "Caught an exception in main!\n";
return 1;
}
}
#endif // LIBFURC
-78
View File
@@ -1,78 +0,0 @@
// NOLINTBEGIN(readability-identifier-naming)
#include "furc/front/lexer.hpp"
#include "gtest/gtest.h"
#include <string>
namespace {
using namespace furc::front;
using namespace std::string_view_literals;
using namespace std::string_literals;
using lexer_case = std::pair<std::string, std::vector<token_r>>;
class LexerTestingFixture : public testing::TestWithParam<lexer_case> {};
TEST_P(LexerTestingFixture, LexerTest) {
auto [code, expected] = GetParam();
lexer lexer("<TEMP>", code);
auto it = expected.begin();
while (it != expected.end()) {
const auto& expected = *it++;
EXPECT_EQ(lexer.next_token(), expected);
}
auto eof = std::move(lexer.next_token());
ASSERT_TRUE(eof.has_error());
ASSERT_EQ(eof.error().type, token_error_t::EndOfFile);
}
furc::location loc(size_t col, size_t line) {
return furc::location{ "<TEMP>", line, col };
}
INSTANTIATE_TEST_SUITE_P(EmptyTests,
LexerTestingFixture,
testing::Values(lexer_case("", {}), lexer_case(" ", {}), lexer_case("\t", {}), lexer_case("\n", {})));
INSTANTIATE_TEST_SUITE_P(Comments,
LexerTestingFixture,
testing::Values(lexer_case("(/** skibidi **/func{//)\n}",
{ { loc(0, 0), token_t::LParen },
{ loc(16, 0), keyword_token::Func },
{ loc(20, 0), token_t::LBrace },
{ loc(0, 1), token_t::RBrace } })));
INSTANTIATE_TEST_SUITE_P(Integers,
LexerTestingFixture,
testing::Values(lexer_case("67 6\n7", { { loc(0, 0), 67 }, { loc(3, 0), 6 }, { loc(0, 1), 7 } }),
lexer_case("18446744073709551615\n18446744073709551616",
{ { loc(0, 0), 18446744073709551615ULL },
token_r(
token_error{ loc(0, 1), token_error_t::IntegerOverflow, std::string("18446744073709551616") }) })));
INSTANTIATE_TEST_SUITE_P(Tokens,
LexerTestingFixture,
testing::Values(lexer_case("()\n\t\t{\n}[\"shto-to\"]; :,.main return func",
{ { loc(0, 0), token_t::LParen },
{ loc(1, 0), token_t::RParen },
{ loc(2, 1), token_t::LBrace },
{ loc(0, 2), token_t::RBrace },
{ loc(1, 2), token_t::LBracket },
{ loc(2, 2), token_t::String, "shto-to"sv },
{ loc(10, 2), token_t::RBracket },
{ loc(11, 2), token_t::Semicolon },
{ loc(15, 2), token_t::Colon },
{ loc(16, 2), token_t::Comma },
{ loc(17, 2), token_t::Dot },
{ loc(18, 2), token_t::Identifier, "main"sv },
{ loc(23, 2), keyword_token::Return },
{ loc(30, 2), keyword_token::Func } })));
} // namespace
// NOLINTEND(readability-identifier-naming)
-274
View File
@@ -1,274 +0,0 @@
#include "furc/front/parser.hpp"
#include "furc/ast/declaration.hpp" // IWYU pragma: keep
#include "furc/ast/expression.hpp" // IWYU pragma: keep
#include "furc/ast/literal.hpp" // IWYU pragma: keep
#include "furc/ast/program.hpp" // IWYU pragma: keep
#include "furc/ast/statement.hpp" // IWYU pragma: keep
#include "gtest/gtest.h" // IWYU pragma: keep
namespace {
using namespace furc::front;
using namespace furc::ast;
using namespace std::string_view_literals;
// TEST(Parser, EmptyFunctions) {
// parser parser("<TEMP>", "func main() {}\nfunc foo();");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 2);
// {
// auto first = program->declarations()[0];
// EXPECT_TRUE(first.present());
// EXPECT_EQ(first->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = first;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 0);
// }
// {
// auto second = program->declarations()[1];
// EXPECT_TRUE(second.present());
// EXPECT_EQ(second->declaration_type(), declaration_node_t::Func);
// function_declaration_node_h funcDecl = second;
// EXPECT_EQ(funcDecl->name()->string, "foo");
// }
// }
// #define EXPECT_INTLIT(expr, integer) \
// do { \
// EXPECT_EQ((expr)->expression_type(), expression_node_t::Literal); \
// literal_node_h literal = (expr); \
// EXPECT_EQ(literal->literal_type(), literal_node_t::Integer); \
// integer_literal_node_h intLit = literal; \
// EXPECT_EQ(intLit->value(), integer_token((integer))); \
// } while (0)
// TEST(Parser, Literals) {
// parser parser("<TEMP>", R"(
// func test1() { return 67; }
// func test2() { return "uwu"; }
// )");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 2);
// {
// auto test1 = program->declarations()[0];
// EXPECT_TRUE(test1.present());
// EXPECT_EQ(test1->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = test1;
// EXPECT_EQ(funcDef->name()->string, "test1");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// EXPECT_INTLIT(ret->value(), 67);
// }
// {
// auto test2 = program->declarations()[1];
// EXPECT_TRUE(test2.present());
// EXPECT_EQ(test2->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDecl = test2;
// EXPECT_EQ(funcDecl->name()->string, "test2");
// }
// }
// #define EXPECT_VARREAD(expr, varname) \
// do { \
// EXPECT_EQ((expr)->expression_type(), expression_node_t::VarRead); \
// var_read_expression_node_h varRead = (expr); \
// EXPECT_EQ(varRead->get_name(), (varname)); \
// } while (0)
// // TODO: Use arena (I am too exhausted rn to do it)
// TEST(Parser, OperatorPrecedence_AddMul) {
// parser parser("<TEMP>", "func main() { return 1 + 2 * 3; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// auto retVal = ret->value();
// EXPECT_TRUE(retVal.present());
// EXPECT_EQ(retVal->expression_type(), expression_node_t::Binop);
// binop_expression_node_h add = retVal;
// EXPECT_EQ(add->type(), binop_expression_node_t::Add);
// EXPECT_INTLIT(add->lhs(), 1);
// EXPECT_EQ(add->rhs()->expression_type(), expression_node_t::Binop);
// binop_expression_node_h mul = add->rhs();
// EXPECT_EQ(mul->type(), binop_expression_node_t::Mul);
// EXPECT_INTLIT(mul->lhs(), 2);
// EXPECT_INTLIT(mul->rhs(), 3);
// }
// TEST(Parser, OperatorPrecedence_Complex) {
// parser parser("<TEMP>", "func main() { return 1 + 2 * 3 - 4 / 2; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// auto retVal = ret->value();
// EXPECT_TRUE(retVal.present());
// EXPECT_EQ(retVal->expression_type(), expression_node_t::Binop);
// binop_expression_node_h sub = retVal;
// EXPECT_EQ(sub->type(), binop_expression_node_t::Sub);
// EXPECT_EQ(sub->lhs()->expression_type(), expression_node_t::Binop);
// binop_expression_node_h add = sub->lhs();
// EXPECT_EQ(add->type(), binop_expression_node_t::Add);
// EXPECT_INTLIT(add->lhs(), 1);
// EXPECT_EQ(add->rhs()->expression_type(), expression_node_t::Binop);
// binop_expression_node_h mul = add->rhs();
// EXPECT_EQ(mul->type(), binop_expression_node_t::Mul);
// EXPECT_INTLIT(mul->lhs(), 2);
// EXPECT_INTLIT(mul->rhs(), 3);
// EXPECT_EQ(sub->rhs()->expression_type(), expression_node_t::Binop);
// binop_expression_node_h div = sub->rhs();
// EXPECT_EQ(div->type(), binop_expression_node_t::Div);
// EXPECT_INTLIT(div->lhs(), 4);
// EXPECT_INTLIT(div->rhs(), 2);
// }
// TEST(Parser, UnaryOperator_Simple) {
// parser parser("<TEMP>", "func main() { return -5; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// auto retVal = ret->value();
// EXPECT_TRUE(retVal.present());
// EXPECT_EQ(retVal->expression_type(), expression_node_t::Unaryop);
// unaryop_expression_node_h neg = retVal;
// EXPECT_EQ(neg->type(), unaryop_expression_node_t::Negative);
// EXPECT_INTLIT(neg->get_node(), 5);
// }
// TEST(Parser, UnaryOperator_PrePost) {
// parser parser("<TEMP>", "func main() { return --5++; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// auto retVal = ret->value();
// EXPECT_TRUE(retVal.present());
// EXPECT_EQ(retVal->expression_type(), expression_node_t::Unaryop);
// unaryop_expression_node_h inc = retVal;
// EXPECT_EQ(inc->type(), unaryop_expression_node_t::PostfixIncrement);
// EXPECT_EQ(inc->get_node()->expression_type(), expression_node_t::Unaryop);
// unaryop_expression_node_h dec = inc->get_node();
// EXPECT_INTLIT(dec->get_node(), 5);
// }
// TEST(Parser, Paren) {
// parser parser("<TEMP>", "func main() { return --(x++); }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// return_statement_node_h ret = funcDef->body()->statements[0];
// auto retVal = ret->value();
// EXPECT_TRUE(retVal.present());
// EXPECT_EQ(retVal->expression_type(), expression_node_t::Unaryop);
// unaryop_expression_node_h dec = retVal;
// EXPECT_EQ(dec->type(), unaryop_expression_node_t::PrefixDecrement);
// EXPECT_EQ(dec->get_node()->expression_type(), expression_node_t::Unaryop);
// unaryop_expression_node_h inc = dec->get_node();
// EXPECT_EQ(inc->type(), unaryop_expression_node_t::PostfixIncrement);
// EXPECT_VARREAD(inc->get_node(), "x"sv);
// }
// TEST(Parser, Assignment) {
// parser parser("<TEMP>", "func main() { x = 10; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// EXPECT_EQ(funcDef->body()->statements[0]->statement_type(), statement_node_t::Expression);
// expression_node_h expr = funcDef->body()->statements[0];
// EXPECT_EQ(expr->expression_type(), expression_node_t::VarAssign);
// var_assign_expression_node_h assign = expr;
// EXPECT_EQ(assign->compound(), binop_expression_node_t::None);
// EXPECT_VARREAD(assign->lhs(), "x"sv);
// expression_node_h rhs = assign->rhs();
// EXPECT_EQ(rhs->expression_type(), expression_node_t::Literal);
// EXPECT_INTLIT(rhs, 10);
// }
// TEST(Parser, CompoundAssignment) {
// parser parser("<TEMP>", "func main() { x += 10; }");
// auto program = parser.parse();
// EXPECT_TRUE(program.present());
// EXPECT_EQ(program->declarations().size(), 1);
// auto func = program->declarations()[0];
// EXPECT_TRUE(func.present());
// EXPECT_EQ(func->declaration_type(), declaration_node_t::FuncDef);
// function_definition_node_h funcDef = func;
// EXPECT_EQ(funcDef->name()->string, "main");
// EXPECT_EQ(funcDef->body()->statements.size(), 1);
// EXPECT_EQ(funcDef->body()->statements[0]->statement_type(), statement_node_t::Expression);
// expression_node_h expr = funcDef->body()->statements[0];
// EXPECT_EQ(expr->expression_type(), expression_node_t::VarAssign);
// var_assign_expression_node_h assign = expr;
// EXPECT_EQ(assign->compound(), binop_expression_node_t::Add);
// EXPECT_VARREAD(assign->lhs(), "x"sv);
// expression_node_h rhs = assign->rhs();
// EXPECT_EQ(rhs->expression_type(), expression_node_t::Literal);
// EXPECT_INTLIT(rhs, 10);
// }
} // namespace
-4
View File
@@ -1,4 +0,0 @@
file(GLOB_RECURSE FURLANG_SRCS "src/**.cpp")
file(GLOB_RECURSE FURLANG_HDRS "include/**.hpp")
add_library(furlang STATIC ${FURLANG_SRCS} ${FURLANG_HDRS})
target_include_directories(furlang PUBLIC include/)
-87
View File
@@ -1,87 +0,0 @@
#ifndef FURLANG_IR_BLOCK_HPP
#define FURLANG_IR_BLOCK_HPP
#include "furlang/ir/instruction.hpp"
#include <memory>
#include <type_traits>
#include <vector>
namespace furlang {
namespace ir {
/**
* @brief Basic block.
*
* A basic block of IR instructions. https://en.wikipedia.org/wiki/Basic_block
*/
class block {
public:
using value_type = std::unique_ptr<instruction>; /**< Value type */
public:
block() = default;
public:
/**
* @brief Emplaces a new instruction.
*
* Emplaces a new instruction, if exit instruction hasn't been emplaced in this block yet.
*
* @tparam T Type of the instruction to emplace.
* @param args Arguments to call the constructor with.
* @return true if the instruction has been emplaced successfully.
*/
template <typename T, typename... Args, typename = std::enable_if_t<std::is_base_of_v<instruction, T>>>
bool emplace(Args&&... args) {
if (has_exit()) return false;
auto instr = std::make_unique<T>(std::forward<Args>(args)...);
if (is_exit_instruction(instr->type())) {
m_exit = std::move(instr);
} else {
m_instructions.emplace_back(std::move(instr));
}
return true;
}
/**
* @brief Returns this block's instructions.
*
* @return The instructions.
*/
std::vector<value_type>& instructions() { return m_instructions; }
/**
* @brief Returns this block's instructions.
*
* @return The instructions.
*/
const std::vector<value_type>& instructions() const { return m_instructions; }
/**
* @brief Checks whether an exit instruction has been emplaced in this block yet.
*
* @return true if the exit instruction has been emplaced.
*/
bool has_exit() const { return m_exit != nullptr; }
/**
* @brief Returns this block's exit instruction.
*
* @return The exit instruction.
*/
value_type& exit() { return m_exit; }
/**
* @brief Returns this block's exit instruction.
*
* @return The exit instruction.
*/
const value_type& exit() const { return m_exit; }
private:
std::vector<value_type> m_instructions;
value_type m_exit;
};
} // namespace ir
} // namespace furlang
#endif // FURLANG_IR_BLOCK_HPP
-66
View File
@@ -1,66 +0,0 @@
#ifndef FURLANG_IR_FUNCTION_HPP
#define FURLANG_IR_FUNCTION_HPP
#include "furlang/ir/block.hpp"
#include <memory>
#include <vector>
namespace furlang {
namespace ir {
/**
* @brief IR function.
*
* Consists of a name and blocks.
* @see block
*/
class function {
public:
using value_type = std::shared_ptr<block>; /**< Value type. */
public:
/**
* @brief Construct a new IR function.
*
* @param name Name to copy.
*/
function(const std::string& name)
: m_name(name) {}
/**
* @brief Construct a new IR function.
*
* @param name Name to move.
*/
function(std::string&& name)
: m_name(std::move(name)) {}
public:
/**
* @brief Returns this function's name.
*
* @return The name.
*/
const std::string& name() const { return m_name; }
/**
* @brief Pushes and returns a new IR block.
*
* @return The new IR block.
*/
value_type push() { return m_blocks.emplace_back(std::make_shared<block>()); }
/**
* @brief Returns this function's IR blocks.
*
* @return The IR blocks.
*/
const std::vector<value_type>& blocks() const { return m_blocks; }
private:
std::string m_name;
std::vector<value_type> m_blocks;
};
} // namespace ir
} // namespace furlang
#endif // FURLANG_IR_FUNCTION_HPP
-464
View File
@@ -1,464 +0,0 @@
#ifndef FURLANG_IR_INSTRUCTION_HPP
#define FURLANG_IR_INSTRUCTION_HPP
#include "furlang/ir/operand.hpp"
#include <cstdint>
#include <optional>
#include <ostream>
namespace furlang {
namespace ir {
/**
* @brief IR instruction type.
*/
enum class instruction_t {
Alloca, /**< Unused */
Assign, /**< Assign */
BinaryOp, /**< Binary operation */
Call, /**< Call */
Branch, /**< Branch */
BranchCond, /**< Conditional branch */
Return, /**< Return */
};
/**
* @brief Checks if an instruction type exits.
*
* @param type Instruction type.
* @return true if the instruction type exits.
*/
static inline bool is_exit_instruction(instruction_t type) {
switch (type) {
case instruction_t::Branch:
case instruction_t::BranchCond:
case instruction_t::Return: return true;
default: return false;
}
}
/**
* @brief IR instruction
*/
class instruction {
public:
instruction() = default;
virtual ~instruction() = default;
/**
* @brief Move constructor
*/
instruction(instruction&&) = default;
/**
* @brief Move constructor
*/
instruction& operator=(instruction&&) = default;
instruction(const instruction&) = delete;
instruction& operator=(const instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return The type.
*/
virtual instruction_t type() const = 0;
public:
/**
* @brief Prints an instruction to an output stream.
*
* Equivalent to calling instruction.print(os).
*
* @param os Output stream.
* @param instruction Instruction to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const instruction& instruction) { return instruction.print(os); }
protected:
/**
* @brief Prints this instruction to an output stream.
*
* @param os Output stream.
* @return The output stream.
*/
virtual std::ostream& print(std::ostream& os) const = 0;
};
/**
* @brief Alloca instruction
*/
class alloca_instruction final : public instruction {
public:
alloca_instruction() {}
~alloca_instruction() override = default;
/**
* @brief Move constructor
*/
alloca_instruction(alloca_instruction&&) = default;
/**
* @brief Move constructor
*/
alloca_instruction& operator=(alloca_instruction&&) = default;
alloca_instruction(const alloca_instruction&) = delete;
alloca_instruction& operator=(const alloca_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::Alloca.
*/
instruction_t type() const override { return instruction_t::Alloca; }
protected:
std::ostream& print(std::ostream& os) const override { return os << "alloca"; }
};
/**
* @brief Assign instruction
*/
class assign_instruction final : public instruction {
public:
/**
* @brief Construct a new assign instruction.
*
* @param src Source operand.
* @param dst Destination operand.
*/
assign_instruction(operand&& src, operand&& dst)
: m_source(std::move(src)), m_destination(std::move(dst)) {}
~assign_instruction() override = default;
/**
* @brief Move constructor
*/
assign_instruction(assign_instruction&&) = default;
/**
* @brief Move constructor
*/
assign_instruction& operator=(assign_instruction&&) = default;
assign_instruction(const assign_instruction&) = delete;
assign_instruction& operator=(const assign_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::Assign.
*/
instruction_t type() const override { return instruction_t::Assign; }
/**
* @brief Returns this instruction's source.
*
* @return The source.
*/
const operand& source() const { return m_source; }
/**
* @brief Returns this instruction's destination.
*
* @return The destination.
*/
const operand& destination() const { return m_destination; }
private:
operand m_source;
operand m_destination;
protected:
std::ostream& print(std::ostream& os) const override {
return os << "assign " << m_source << ", " << m_destination;
}
};
/**
* @brief Binary operation instruction type
*/
enum class binary_op_instruction_t {
Add, /**< Addition */
Sub, /**< Subtraction */
Mul, /**< Multiplication */
Div, /**< Division */
Mod, /**< Modulo */
Eq, /**< Equal */
NotEq, /**< Not equal */
LessThan, /**< Less than */
GreaterThan, /**< Greater than */
LessEq, /**< Less or equal */
GreaterEq, /**< Greater or equal */
};
static inline std::ostream& operator<<(std::ostream& os, binary_op_instruction_t type) {
switch (type) {
case binary_op_instruction_t::Add: return os << '+';
case binary_op_instruction_t::Sub: return os << '-';
case binary_op_instruction_t::Mul: return os << '*';
case binary_op_instruction_t::Div: return os << '/';
case binary_op_instruction_t::Mod: return os << '%';
case binary_op_instruction_t::Eq: return os << "==";
case binary_op_instruction_t::NotEq: return os << "!=";
case binary_op_instruction_t::LessThan: return os << '<';
case binary_op_instruction_t::GreaterThan: return os << '>';
case binary_op_instruction_t::LessEq: return os << "<=";
case binary_op_instruction_t::GreaterEq: return os << ">=";
}
return os;
}
/**
* @brief Binary operation instruction
*/
class binary_op_instruction final : public instruction {
public:
/**
* @brief Construct a new binary operation instruction.
*
* @param type Operation type.
* @param lhs Left-hand-side operand.
* @param rhs Right-hand-side operand.
* @param dst Destination operand.
*/
binary_op_instruction(binary_op_instruction_t type, operand&& lhs, operand&& rhs, operand&& dst)
: m_type(type), m_lhs(std::move(lhs)), m_rhs(std::move(rhs)), m_dst(std::move(dst)) {}
~binary_op_instruction() override = default;
/**
* @brief Move constructor
*/
binary_op_instruction(binary_op_instruction&&) = default;
/**
* @brief Move constructor
*/
binary_op_instruction& operator=(binary_op_instruction&&) = default;
binary_op_instruction(const binary_op_instruction&) = delete;
binary_op_instruction& operator=(const binary_op_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::BinaryOp.
*/
instruction_t type() const override { return instruction_t::BinaryOp; }
/**
* @brief Returns this instruction's operation type.
*
* @return The operation type.
*/
binary_op_instruction_t op_type() const { return m_type; }
/**
* @brief Returns this instruction's left-hand-side operand.
*
* @return The operand.
*/
const operand& lhs() const { return m_lhs; }
/**
* @brief Returns this instruction's right-hand-side operand.
*
* @return The operand.
*/
const operand& rhs() const { return m_rhs; }
/**
* @brief Returns this instruction's destination operand.
*
* @return The operand.
*/
const operand& dst() const { return m_dst; }
private:
binary_op_instruction_t m_type;
operand m_lhs /**< Left-hand-side operand */;
operand m_rhs /**< Right-hand-side operand */;
operand m_dst /**< Destination operand */;
protected:
std::ostream& print(std::ostream& os) const override {
return os << "binop(" << m_type << ") " << m_lhs << ", " << m_rhs << ", " << m_dst;
}
};
using block_index = std::uint64_t; /**< IR block index alias */
/**
* @brief Branch instruction
*/
class branch_instruction final : public instruction {
public:
/**
* @brief Construct a new branch instruction.
*
* @param block Destination block index.
*/
branch_instruction(block_index block)
: m_block(block) {}
~branch_instruction() override = default;
/**
* @brief Move constructor.
*/
branch_instruction(branch_instruction&&) = default;
/**
* @brief Move constructor.
*/
branch_instruction& operator=(branch_instruction&&) = default;
branch_instruction(const branch_instruction&) = delete;
branch_instruction& operator=(const branch_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::Branch.
*/
instruction_t type() const override { return instruction_t::Branch; }
/**
* @brief Returns this instruction's destination block index.
*
* @return The destination block index.
*/
block_index block() const { return m_block; }
private:
block_index m_block; /**< Destination block index. */
protected:
std::ostream& print(std::ostream& os) const override { return os << "branch #" << m_block; }
};
/**
* @brief Conditional branch instruction
*/
class branch_cond_instruction final : public instruction {
public:
/**
* @brief Construct a new conditional branch instruction.
*
* @param condition Condition operand.
* @param ifBlock Destination block index.
* @param elseBlock Else block index.
*/
branch_cond_instruction(operand&& condition, block_index ifBlock, block_index elseBlock)
: m_condition(std::move(condition)), m_ifBlock(ifBlock), m_elseBlock(elseBlock) {}
~branch_cond_instruction() override = default;
/**
* @brief Move constructor.
*/
branch_cond_instruction(branch_cond_instruction&&) = default;
/**
* @brief Move constructor.
*/
branch_cond_instruction& operator=(branch_cond_instruction&&) = default;
branch_cond_instruction(const branch_cond_instruction&) = delete;
branch_cond_instruction& operator=(const branch_cond_instruction&) = delete;
public:
instruction_t type() const override { return instruction_t::BranchCond; }
/**
* @brief Returns this instruction's condition operand.
*
* @return The operand.
*/
const operand& condition() const { return m_condition; }
/**
* @brief Returns this instruction's destination block index.
*
* @return The destination block index.
*/
block_index if_block() const { return m_ifBlock; }
/**
* @brief Returns this instruction's else block index.
*
* @return The else block index.
*/
block_index else_block() const { return m_elseBlock; }
private:
operand m_condition /**< Condition operand. */;
block_index m_ifBlock /**< Destination block index. */;
block_index m_elseBlock /**< Else block index. */;
protected:
std::ostream& print(std::ostream& os) const override {
return os << "branch_cond " << m_condition << ", #" << m_ifBlock << ", #" << m_elseBlock;
}
};
/**
* @brief Return instruction
*/
class return_instruction final : public instruction {
public:
return_instruction() = default;
/**
* @brief Construct a new return instruction.
*
* @param value Return value operand.
*/
return_instruction(operand&& value)
: m_value(std::move(value)) {}
~return_instruction() override = default;
/**
* @brief Move constructor.
*/
return_instruction(return_instruction&&) = default;
/**
* @brief Move constructor.
*/
return_instruction& operator=(return_instruction&&) = default;
return_instruction(const return_instruction&) = delete;
return_instruction& operator=(const return_instruction&) = delete;
public:
/**
* @brief Returns this instruction's type.
*
* @return instruction_t::Return.
*/
instruction_t type() const override { return instruction_t::Return; }
/**
* @brief Returns this instruction's return value operand.
*
* @return The operand.
*/
const std::optional<operand>& value() const { return m_value; }
private:
std::optional<operand> m_value; /**< The return value operand. */
protected:
std::ostream& print(std::ostream& os) const override {
os << "return";
if (m_value.has_value()) os << ' ' << m_value.value();
return os;
}
};
} // namespace ir
} // namespace furlang
#endif // FURLANG_IR_INSTRUCTION_HPP
-52
View File
@@ -1,52 +0,0 @@
#ifndef FURLANG_IR_MODULE_HPP
#define FURLANG_IR_MODULE_HPP
#include "furlang/ir/function.hpp"
#include <memory>
#include <vector>
namespace furlang {
namespace ir {
/**
* @brief IR module
*/
class module {
public:
using value_type = std::unique_ptr<function>; /**< Value type. */
public:
module() = default;
public:
/**
* @brief Pushes and returns a new IR function.
*
* @param args Arguments to call the constructor with.
* @return The new IR function.
*/
template <typename... Args>
value_type& push(Args&&... args) {
return m_functions.emplace_back(std::forward<Args>(args)...);
}
/**
* @brief Returns this module's functions.
*
* @return The functions.
*/
std::vector<value_type>& functions() { return m_functions; }
/**
* @brief Returns this module's functions.
*
* @return The functions.
*/
const std::vector<value_type>& functions() const { return m_functions; }
private:
std::vector<value_type> m_functions;
};
} // namespace ir
} // namespace furlang
#endif // FURLANG_IR_MODULE_HPP
-231
View File
@@ -1,231 +0,0 @@
#ifndef FURLANG_IR_OPERAND_HPP
#define FURLANG_IR_OPERAND_HPP
#include <cstdint>
#include <ostream>
#include <string>
namespace furlang {
namespace ir {
/**
* @brief Operand type
*/
enum class operand_t {
None, /**< None */
Register, /**< Register */
Variable, /**< Variable */
Integer, /**< Integer */
String, /**< String */
};
/**
* @brief Register operand alias.
* @see operand_t::Register
*/
using register_operand = std::uint32_t;
/**
* @brief Variable operand alias.
* @see operand_t::Variable
*/
using variable_operand = std::string;
/**
* @brief Integer operand alias.
* @see operand_t::Integer
*/
using integer_operand = std::uint64_t;
/**
* @brief String operand alias.
* @see operand_t::String
*/
using string_operand = std::string;
/**
* @brief IR operand
*/
class operand {
public:
~operand() {
if (m_type == operand_t::String) {
m_value.string.~basic_string();
}
}
/**
* @brief Move constructor.
*/
operand(operand&& other) noexcept
: m_type(other.m_type) {
switch (m_type) {
case operand_t::None: break;
case operand_t::Register: {
m_value.reg = other.m_value.reg;
} break;
case operand_t::Variable: {
new (&m_value.variable) variable_operand(std::move(other.m_value.variable));
} break;
case operand_t::Integer: {
m_value.integer = other.m_value.integer;
} break;
case operand_t::String: {
new (&m_value.string) string_operand(std::move(other.m_value.string));
} break;
}
other.m_value.destroy(other.m_type);
}
/**
* @brief Move constructor.
*/
operand& operator=(operand&& other) noexcept {
if (this == &other) return *this;
m_type = other.m_type;
switch (m_type) {
case operand_t::None: break;
case operand_t::Register: {
m_value.reg = other.m_value.reg;
} break;
case operand_t::Variable: {
new (&m_value.variable) variable_operand(std::move(other.m_value.variable));
} break;
case operand_t::Integer: {
m_value.integer = other.m_value.integer;
} break;
case operand_t::String: {
new (&m_value.string) string_operand(std::move(other.m_value.string));
} break;
}
other.m_value.destroy(other.m_type);
return *this;
}
operand(const operand&) = delete;
operand& operator=(const operand&) = delete;
public:
/**
* @brief Construct a new register operand.
*
* @param value Value of the new register operand.
* @return The register operand.
*/
static operand new_reg(register_operand value) {
operand operand;
operand.m_type = operand_t::Register;
operand.m_value.reg = value;
return operand;
}
/**
* @brief Construct a new variable operand.
*
* @param value Value of the new variable operand.
* @return The variable operand.
*/
template <typename T>
static operand new_variable(T&& value) {
operand operand;
operand.m_type = operand_t::Variable;
new (&operand.m_value.variable) variable_operand(std::forward<T>(value));
return operand;
}
/**
* @brief Construct a new integer operand.
*
* @param value Value of the new integer operand.
* @return The integer operand.
*/
static operand new_integer(integer_operand value) {
operand operand;
operand.m_type = operand_t::Integer;
operand.m_value.integer = value;
return operand;
}
/**
* @brief Construct a new string operand.
*
* @param value Value of the new string operand.
* @return The string operand.
*/
template <typename T>
static operand new_string(T&& value) {
operand operand;
operand.m_type = operand_t::String;
new (&operand.m_value.string) string_operand(std::forward<T>(value));
return operand;
}
public:
/**
* @brief Returns this operand's type.
*
* @return The operand type.
*/
operand_t type() const { return m_type; }
/**
* @brief Returns this operand's register value.
*
* @return The register value.
*/
register_operand reg() const { return m_value.reg; }
/**
* @brief Returns this operand's integer value.
*
* @return The integer value.
*/
integer_operand integer() const { return m_value.integer; }
public:
/**
* @brief Prints an operand to an output stream.
*
* @param os Output stream.
* @param operand Operand to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const operand& operand) {
switch (operand.m_type) {
case operand_t::None: return os << "none";
case operand_t::Register: return os << '%' << operand.m_value.reg;
case operand_t::Variable: return os << operand.m_value.variable;
case operand_t::Integer: return os << operand.m_value.integer;
case operand_t::String: return os << '"' << operand.m_value.string << '"';
}
return os;
}
private:
operand() = default;
private:
operand_t m_type = operand_t::None;
union value {
std::nullptr_t null = nullptr;
register_operand reg;
variable_operand variable;
integer_operand integer;
string_operand string;
void destroy(operand_t type) {
switch (type) {
case operand_t::String: {
string.~basic_string();
} break;
default: break;
}
null = nullptr;
}
value() = default;
~value() {}
value(value&&) noexcept = delete;
value& operator=(value&&) noexcept = delete;
value(const value&) = delete;
value& operator=(const value&) = delete;
} m_value;
};
} // namespace ir
} // namespace furlang
#endif // FURLANG_IR_OPERAND_HPP
-112
View File
@@ -1,112 +0,0 @@
#ifndef FURVM_CONSTANT_HPP
#define FURVM_CONSTANT_HPP
#include "furvm/fwd.hpp"
#include <exception>
#include <string_view>
namespace furvm {
/**
* @brief Bad constant access exception.
*/
class bad_constant_access : public std::exception {
public:
bad_constant_access() = default;
~bad_constant_access() override = default;
/**
* @brief Move constructor.
*/
bad_constant_access(bad_constant_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_constant_access& operator=(bad_constant_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_constant_access(const bad_constant_access&) = default;
/**
* @brief Copy constructor.
*/
bad_constant_access& operator=(const bad_constant_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad constant access"; }
};
enum class constant_t : std::uint8_t {
String, /**< String constant. */
};
class constant {
public:
using string_type = std::string_view; /**< String constant type. */
public:
/**
* @brief Construct a new string constant.
*
* @param string String.
*/
constant(string_type string)
: m_type(constant_t::String), m_value(string) {}
~constant() = default;
/**
* @brief Move constructor.
*/
constant(constant&&) = default;
/**
* @brief Move constructor.
*/
constant& operator=(constant&&) = default;
constant(const constant&) = delete;
constant& operator=(const constant&) = delete;
public:
/**
* @brief Returns this constant's type.
* @see constant_t
*
* @return The constant type.
*/
constexpr constant_t type() const { return m_type; }
/**
* @brief Returns this constant's string value.
* @throws bad_constant_access if this constant's type is not constant_t::String.
*
* @return The string value.
*/
constexpr string_type string() const {
require_type(constant_t::String);
return m_value.string;
}
private:
void require_type(constant_t type) const {
if (m_type != type) throw bad_constant_access();
}
private:
constant_t m_type{};
union value {
string_type string;
value(string_type sv)
: string(sv) {}
} m_value;
};
} // namespace furvm
#endif // FURVM_CONSTANT_HPP
-113
View File
@@ -1,113 +0,0 @@
#ifndef FURVM_CONTEXT_HPP
#define FURVM_CONTEXT_HPP
#include "furlang/arena.hpp"
#include "furvm/fwd.hpp"
#include "furvm/module.hpp"
#include <queue>
#include <type_traits>
#include <vector>
namespace furvm {
class context {
public:
friend class executor;
friend class thing;
public:
context();
~context() = default;
/**
* @brief Move constructor.
*/
context(context&&) noexcept = default;
/**
* @brief Move constructor.
*/
context& operator=(context&&) noexcept = default;
context(const context&) = delete;
context& operator=(const context&) = delete;
public:
/**
* @brief Adds a module to this context.
*
* @param args Arguments to forward to module's constructor.
* @return An index to the emplaced module.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<mod, module_handle, Args...>>>
constexpr const auto& emplace(Args&&... args) {
module_handle id = static_cast<module_handle>(m_modules.size());
return m_modules.emplace_back(std::make_unique<mod>(id, std::forward<Args>(args)...));
}
/**
* @brief Erases a module from this context.
*
* @param index Index to the module.
* @return Old value.
*/
mod_p erase(module_handle index) {
if (index >= m_modules.size()) return nullptr;
return std::move(m_modules[index]);
}
/**
* @brief Returns a module of this context.
*
* @param index Position of the module.
* @return The module.
*/
constexpr mod_p& operator[](module_handle index) { return m_modules[index]; }
/**
* @brief Returns a module of this context.
*
* @param index Position of the module.
* @return The module.
*/
constexpr const mod_p& operator[](module_handle index) const { return m_modules[index]; }
/**
* @brief Returns a module of this context.
*
* @param index Position of the module.
* @return The module.
*/
constexpr mod_p& at(module_handle index) { return m_modules.at(index); }
/**
* @brief Returns a module of this context.
*
* @param index Position of the module.
* @return The module.
*/
constexpr const mod_p& at(module_handle index) const { return m_modules.at(index); }
/**
* @brief Returns how many does this context have modules.
*
* @return The module count.
*/
constexpr size_t size() const { return m_modules.size(); }
public:
/**
* @brief Removes unreferenced things from the thing list.
*/
void collect();
private:
std::vector<mod_p> m_modules;
std::vector<thing_p> m_things;
std::vector<executor_p> m_executors;
std::queue<thing_handle> m_deadThings;
std::vector<void*> m_deadThingData;
furlang::arena m_thingArena;
};
} // namespace furvm
#endif // FURVM_CONTEXT_HPP
-43
View File
@@ -1,43 +0,0 @@
#ifndef FURVM_EXCEPTIONS_HPP
#define FURVM_EXCEPTIONS_HPP
#include <exception>
namespace furvm {
class stack_underflow : public std::exception {
public:
stack_underflow() = default;
~stack_underflow() override = default;
/**
* @brief Move constructor.
*/
stack_underflow(stack_underflow&&) noexcept = default;
/**
* @brief Move constructor.
*/
stack_underflow& operator=(stack_underflow&&) noexcept = default;
/**
* @brief Copy constructor.
*/
stack_underflow(const stack_underflow&) = default;
/**
* @brief Copy constructor.
*/
stack_underflow& operator=(const stack_underflow&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "stack underflow"; }
};
} // namespace furvm
#endif // FURVM_EXCEPTIONS_HPP
-158
View File
@@ -1,158 +0,0 @@
#ifndef FURVM_EXECUTOR_HPP
#define FURVM_EXECUTOR_HPP
#include "furvm/fwd.hpp"
#include <stack>
namespace furvm {
enum class executor_flags : std::uint32_t {
Suspended = (1 << 0), /**< Execution suspended. */
Done = (1 << 1), /**< Execution is finished. */
};
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator&(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator~(executor_flags flags) {
return executor_flags(~static_cast<std::uint32_t>(flags));
}
class executor {
private:
/**
* @brief A private token for the private constructor.
*
* Also `egzekutor` in Polish translates to `executor` I think.
*/
struct egzekutor {
explicit egzekutor() = default;
};
public:
/**
* @brief Executor frame.
*
* Call frame.
*/
struct frame {
mod_p mod; /**< Shared pointer to a module with the bytecode. */
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
};
public:
/**
* @brief Private constructor.
*
* @param id
* @param context
*/
executor(egzekutor, executor_handle id, const context_p& context);
~executor();
/**
* @brief Move constructor.
*/
executor(executor&&) noexcept = default;
/**
* @brief Move constructor.
*/
executor& operator=(executor&&) noexcept = default;
executor(const executor&) = delete;
executor& operator=(const executor&) = delete;
public:
/**
* @brief Returns a new executor.
*
* @param context Furvm context.
* @return Shared pointer to the new executor.
*/
static executor_p create(const context_p& context);
public:
/**
* @brief Returns an id of this executor.
*
* @return The id.
*/
executor_handle id() const { return m_id; }
/**
* @brief Returns flags of this executor.
*
* @return The flags.
*/
executor_flags flags() const { return m_flags; }
public:
/**
* @brief Pushes a new frame.
*
* @param function Function.
*/
void push_frame(const function_p& function);
/**
* @brief Pops the top frame.
*
* @return The popped frame.
*/
frame pop_frame();
/**
* @brief Returns the top frame.
*
* @return The frame.
*/
frame frame() const;
public:
/**
* @brief Pushes a thing onto the stack.
*
* @param thing The thing to push.
*/
void push_thing(const thing_p& thing);
/**
* @brief Pushes a thing onto the stack.
*
* @param thing The thing to push.
*/
void push_thing(thing_p&& thing);
/**
* @brief Pops a thing from the stack.
*
* @return The popped thing.
*/
thing_p pop_thing();
/**
* @brief Returns the top thing on the stack.
*
* @return The thing.
*/
thing_p thing() const;
public:
/**
* @brief Executes next instruction.
*/
void step();
private:
executor_handle m_id;
executor_flags m_flags{}; // NOLINT(bugprone-invalid-enum-default-initialization)
context_p m_context;
std::stack<struct frame> m_frames;
std::stack<thing_p> m_stack;
};
} // namespace furvm
#endif // FURVM_EXECUTOR_HPP
-176
View File
@@ -1,176 +0,0 @@
#ifndef FURVM_FUNCTION_HPP
#define FURVM_FUNCTION_HPP
#include "furvm/fwd.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep
#include <functional>
#include <stdexcept>
namespace furvm {
enum class function_t : std::uint8_t {
Normal = 0, /**< A normal bytecode function. */
Native, /**< A native function implemented through furvm API. */
Import, /**< A function imported from another module. */
};
using native_function = std::function<void(executor&)>;
class function {
private:
/**
* @brief A private token for the private constructor.
*
* Also `funkcja` in Polish translates to `function` from what I heard.
*/
struct funkcja {
explicit funkcja() = default;
};
public:
/**
* @brief Private constructor.
*
* @param id
* @param position
* @param mod
*/
function(funkcja, function_handle id, std::size_t position, const mod_p& mod);
/**
* @brief Private constructor.
*
* @param id
* @param native
* @param mod
*/
function(funkcja, function_handle id, const native_function& native, const mod_p& mod);
~function() = default;
/**
* @brief Move constructor.
*/
function(function&&) noexcept;
/**
* @brief Move constructor.
*/
function& operator=(function&&) noexcept;
function(const function&) = delete;
function& operator=(const function&) = delete;
public:
/**
* @brief Returns a new function.
*
* @param mod Module.
* @param args Arguments to pass to the function constructor.
* @return The new function.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<function, funkcja, function_handle, Args..., const mod_p&>>>
static function_p create(const mod_p& mod, Args&&... args) {
function_handle id = mod->m_functions.size();
auto func = std::make_shared<function>(funkcja{}, id, std::forward<Args>(args)..., mod);
mod->m_functions.emplace(mod->m_functions.begin() + id, func);
return std::move(func);
}
public:
/**
* @brief Returns an id of this function.
*
* @return The id.
*/
constexpr function_handle id() const { return m_id; }
/**
* @brief Returns a type of this function.
*
* @return The type.
*/
constexpr function_t type() const { return m_type; }
/**
* @brief Returns a parent module of this function.
*
* @return A shared pointer to the module.
*/
const mod_p& mod() const { return m_module; }
public:
/**
* @brief Returns a value for normal function.
*
* @return The value.
*/
std::size_t position() const {
if (m_type != function_t::Normal) throw std::runtime_error("function type mismatch");
return m_value.position;
}
/**
* @brief Returns a value for native function.
*
* @return The value.
*/
const native_function& native() const {
if (m_type != function_t::Native) throw std::runtime_error("function type mismatch");
return m_value.native;
}
/**
* @brief Returns a module of imported function.
*
* @return A handle to the module.
*/
module_handle imported_module() const {
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
return m_value.imp.mod;
}
/**
* @brief Returns a module of imported function.
*
* @return A handle to the module.
*/
function_handle imported_function() const {
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
return m_value.imp.function;
}
private:
function_handle m_id;
function_t m_type;
mod_p m_module;
union value {
std::size_t position = 0;
native_function native;
struct {
module_handle mod;
function_handle function;
} imp;
value() = default;
value(std::size_t position)
: position(position) {}
value(const native_function& native)
: native(native) {}
value(module_handle mod, function_handle function)
: imp({ mod, function }) {}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} m_value;
};
} // namespace furvm
#endif // FURVM_FUNCTION_HPP
-99
View File
@@ -1,99 +0,0 @@
#ifndef FURVM_INSTRUCTION_HPP
#define FURVM_INSTRUCTION_HPP
#include "furvm/fwd.hpp"
namespace furvm {
enum class instruction_t : byte {
/**
* @brief No operation.
*/
NoOperation = 0,
/**
* @brief Pushes an integer from a byte onto the stack.
*
* Pushes an integer constructed from a next byte onto the stack.
*/
PushB2I,
/**
* @brief Pushes a constant onto the stack.
*
* Pushes a constant from the constant pool denoted by two next bytes in little-endian onto the stack.
*/
PushConstant,
/**
* @brief Pops top element from the stack.
*/
Drop,
/**
* @brief Duplicates top element on the stack.
*/
Duplicate,
/**
* @brief Clones top element on the stack.
*/
Clone,
/**
* @brief Adds two things together on the stack.
*/
Add,
/**
* @brief Subtracts two things together on the stack.
*/
Sub,
/**
* @brief Multiplies two things together on the stack.
*/
Mul,
/**
* @brief Divides two things together on the stack.
*/
Div,
/**
* @brief Modulos two things together on the stack.
*/
Mod,
/**
* @brief Calls a function.
*
* Calls a function denoted by next two bytes in little-endian from current frame's module.
*/
Call,
/**
* @brief Pops the current call frame.
*/
Return,
/**
* @brief Pops the current call frame and pushes the first element from the previous stack onto the new stack.
*/
ReturnValue,
};
struct instruction {
instruction_t type; /**< Instruction type. */
/**
* @brief Instruction value.
*/
union value {
constant_index constant; /**< Constant instruction argument. */
} value; /**< Instruction value. */
};
} // namespace furvm
#endif // FURVM_INSTRUCTION_HPP
-70
View File
@@ -1,70 +0,0 @@
#ifndef FURVM_MODULE_HPP
#define FURVM_MODULE_HPP
#include "furvm/fwd.hpp"
#include <vector>
namespace furvm {
class mod {
friend class function;
public:
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
public:
/**
* @brief Construct a new module.
*
* @param id Id of this module.
* @param args Arguments to forward to bytecode's constructor.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
mod(module_handle id, Args&&... args)
: m_id(id), m_bytecode(std::forward<Args>(args)...) {}
~mod() = default;
/**
* @brief Move constructor.
*/
mod(mod&&) = default;
/**
* @brief Move constructor.
*/
mod& operator=(mod&&) = default;
mod(const mod&) = delete;
mod& operator=(const mod&) = delete;
public:
/**
* @brief Returns an id of this module.
*
* @return The id.
*/
constexpr module_handle id() const { return m_id; }
/**
* @brief Returns a byte from bytecode of this module.
*
* @param offset An offset of the byte.
* @return The byte.
*/
constexpr byte byte(std::size_t offset) const { return m_bytecode.at(offset); }
public:
/**
* @brief Returns a function from this module.
*
* @param id Id of the function.
* @return The function.
*/
constexpr const function_p& function_at(function_handle id) const { return m_functions.at(id); }
private:
module_handle m_id;
bytecode_t m_bytecode;
std::vector<function_p> m_functions;
};
} // namespace furvm
#endif // FURVM_MODULE_HPP
-211
View File
@@ -1,211 +0,0 @@
#ifndef FURVM_THING_HPP
#define FURVM_THING_HPP
#include "furvm/context.hpp" // IWYU pragma: keep
#include "furvm/fwd.hpp"
namespace furvm {
enum class thing_t : std::uint8_t {
Int32,
};
/**
* @brief Returns data size of a thing.
*
* @param type Type of the thing.
* @return The data size of the thing.
*/
std::size_t thing_type_size(thing_t type);
class bad_thing_access : public std::exception {
public:
bad_thing_access() = default;
~bad_thing_access() override = default;
/**
* @brief Move constructor.
*/
bad_thing_access(bad_thing_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_thing_access& operator=(bad_thing_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_thing_access(const bad_thing_access&) = default;
/**
* @brief Copy constructor.
*/
bad_thing_access& operator=(const bad_thing_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad thing access"; }
};
class thing final {
friend class executor;
private:
/**
* @brief A private token for the private constructor.
*
* Also `rzecz` in Polish translates to `thing` I think.
*/
struct rzecz {
explicit rzecz() = default;
};
public:
using nref_t = std::size_t; /**< Type of reference count. */
static constexpr thing_handle GENERATION_SIZE = 12; /**< Bit size of generation part in thing_handle. */
public:
/**
* @brief Private constructor.
*
* @param id
* @param type
* @param context
*/
thing(rzecz, thing_handle id, thing_t type, const context_p& context);
~thing();
/**
* @brief Move constructor.
*/
thing(thing&&) noexcept;
/**
* @brief Move constructor.
*/
thing& operator=(thing&&) noexcept;
thing(const thing&) = delete;
thing& operator=(const thing&) = delete;
public:
/**
* @brief Adds two things together.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator+(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Subtracts two things together.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator-(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Multiplies two things together.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator*(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Divides two things together.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator/(const thing_p& lhs, const thing_p& rhs);
/**
* @brief Modulos two things together.
*
* @param lhs Left-hand-side thing.
* @param rhs Right-hand-side thing.
* @return Shared pointer to result thing.
*/
friend thing_p operator%(const thing_p& lhs, const thing_p& rhs);
public:
/**
* @brief Returns a new thing.
*
* @param context Furvm context.
* @param args Arguments to forward to the thing constructor.
* @return Shared pointer to the new thing.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<thing, rzecz, thing_handle, Args..., const context_p&>>>
static thing_p create(const context_p& context, Args&&... args) {
thing_handle id = context->m_things.size();
if (!context->m_deadThings.empty()) {
id = context->m_deadThings.front();
context->m_deadThings.pop();
id += 1 << GENERATION_SIZE;
}
thing_handle idx = id & ((1ULL << ((sizeof(id) * 8) - GENERATION_SIZE)) - 1);
auto th = std::make_shared<thing>(rzecz{}, id, std::forward<Args>(args)..., context);
context->m_things.emplace(context->m_things.begin() + idx, th);
return std::move(th);
}
/**
* @brief Returns a clone of the thing.
*
* @param thing Thing to clone.
* @return Shared pointer to a clone of the thing.
*/
static thing_p clone(const thing_p& thing);
public:
/**
* @brief Returns an int32 value from this thing.
*
* @return The value.
*/
std::int32_t& int32();
/**
* @brief Returns an int32 value from this thing.
*
* @return The value.
*/
const std::int32_t& int32() const;
public:
/**
* @brief Increments reference count of this thing by one.
*/
void add_reference() { ++m_refCount; }
/**
* @brief Decrements reference count of this thing by one.
*/
void remove_reference() { --m_refCount; }
/**
* @brief Returns reference count of this thing.
*
* @return The reference count.
*/
constexpr nref_t reference_count() const { return m_refCount; }
private:
thing_handle m_id;
thing_t m_type;
context_p m_context;
nref_t m_refCount = 0;
void* m_data = nullptr;
};
} // namespace furvm
#endif // FURVM_THING_HPP
-16
View File
@@ -1,16 +0,0 @@
#include "furvm/context.hpp"
#include "furvm/thing.hpp" // IWYU pragma: keep
namespace furvm {
context::context() {}
void context::collect() {
for (auto& ref : m_things) {
if (ref->reference_count() != 0) continue;
ref = nullptr;
}
}
} // namespace furvm
-141
View File
@@ -1,141 +0,0 @@
#include "furvm/executor.hpp"
#include "furvm/context.hpp" // IWYU pragma: keep
#include "furvm/exceptions.hpp"
#include "furvm/function.hpp" // IWYU pragma: keep
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <stdexcept>
namespace furvm {
executor::executor(egzekutor, executor_handle id, const context_p& context)
: m_id(id), m_context(context) {}
executor::~executor() {
m_context->m_executors[m_id] = nullptr;
}
executor_p executor::create(const context_p& context) {
auto ex = std::make_shared<executor>(egzekutor{}, context->m_executors.size(), context);
context->m_executors.push_back(ex);
return std::move(ex);
}
void executor::push_frame(const function_p& function) {
if (function->type() != function_t::Normal) return;
m_frames.emplace((struct executor::frame){ function->mod(), function->position(), m_stack.size() });
}
struct executor::frame executor::pop_frame() {
if (m_frames.empty()) throw stack_underflow();
struct executor::frame frame = m_frames.top();
m_frames.pop();
return frame;
}
struct executor::frame executor::frame() const {
return m_frames.top();
}
void executor::push_thing(const thing_p& thing) {
thing->add_reference();
m_stack.push(thing);
}
void executor::push_thing(thing_p&& thing) {
thing->add_reference();
m_stack.push(std::move(thing));
}
thing_p executor::pop_thing() {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
thing_p top = std::move(m_stack.top());
m_stack.pop();
top->remove_reference();
return top;
}
thing_p executor::thing() const {
if (m_frames.top().stackBase >= m_stack.size()) throw stack_underflow();
return m_stack.top();
}
void executor::step() {
if ((m_flags & executor_flags::Suspended) == executor_flags::Suspended) return;
struct frame& frame = m_frames.top();
instruction_t instr = static_cast<instruction_t>(frame.mod->byte(frame.position++));
switch (instr) {
case instruction_t::NoOperation: break;
case instruction_t::PushB2I: {
auto thing = thing::create(m_context, thing_t::Int32);
thing->int32() = frame.mod->byte(frame.position++);
push_thing(std::move(thing));
} break;
case instruction_t::Drop: {
pop_thing();
} break;
case instruction_t::Duplicate: {
push_thing(thing());
} break;
case instruction_t::Clone: {
push_thing(std::move(thing::clone(thing())));
} break;
case instruction_t::Add: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs + rhs);
} break;
case instruction_t::Sub: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs - rhs);
} break;
case instruction_t::Mul: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs * rhs);
} break;
case instruction_t::Div: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs / rhs);
} break;
case instruction_t::Mod: {
auto rhs = pop_thing();
auto lhs = pop_thing();
push_thing(lhs % rhs);
} break;
case instruction_t::Call: {
function_handle funcId = static_cast<std::uint16_t>(frame.mod->byte(frame.position)) |
(static_cast<std::uint16_t>(frame.mod->byte(frame.position + 1)) << 8);
frame.position += 2;
const function_p& function = frame.mod->function_at(funcId);
switch (function->type()) {
case function_t::Normal: push_frame(function); break;
case function_t::Native: function->native()(*this); break;
case function_t::Import: {
const mod_p& impMod = m_context->m_modules.at(function->imported_module());
push_frame(impMod->function_at(function->imported_function()));
} break;
}
} break;
case instruction_t::Return: {
pop_frame();
if (m_frames.empty()) m_flags = m_flags | executor_flags::Done;
} break;
case instruction_t::ReturnValue: {
auto value = pop_thing();
pop_frame();
push_thing(std::move(value));
} break;
case instruction_t::PushConstant: throw std::runtime_error("unimplemented");
default: throw std::runtime_error("unknown instruction");
}
}
} // namespace furvm
-43
View File
@@ -1,43 +0,0 @@
#include "furvm/function.hpp"
namespace furvm {
function::function(funkcja, function_handle id, std::size_t position, const mod_p& mod)
: m_id(id), m_type(function_t::Normal), m_module(mod), m_value(position) {}
function::function(funkcja, function_handle id, const native_function& native, const mod_p& mod)
: m_id(id), m_type(function_t::Native), m_module(mod), m_value(native) {}
function::function(function&& other) noexcept
: m_id(other.m_id), m_type(other.m_type), m_module(std::move(other.m_module)) {
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
} break;
case function_t::Native: {
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
}
other.m_value.position = 0;
}
function& function::operator=(function&& other) noexcept {
if (this == &other) return *this;
m_id = other.m_id;
m_type = other.m_type;
m_module = std::move(other.m_module);
switch (m_type) {
case function_t::Normal: {
m_value.position = other.m_value.position;
} break;
case function_t::Native: {
new (&m_value.native) native_function(std::move(other.m_value.native));
} break;
}
other.m_value.position = 0;
return *this;
}
} // namespace furvm
-52
View File
@@ -1,52 +0,0 @@
#ifndef LIBFURVM
#include "furvm/context.hpp"
#include "furvm/executor.hpp"
#include "furvm/function.hpp"
#include "furvm/instruction.hpp"
#include "furvm/thing.hpp"
#include <array>
#include <cstddef>
#include <iostream>
#include <memory>
static constexpr std::array<furvm::byte, 8> s_bytecode = {
furvm::byte(furvm::instruction_t::PushB2I),
67,
furvm::byte(furvm::instruction_t::Clone),
furvm::byte(furvm::instruction_t::Add),
furvm::byte(furvm::instruction_t::Call),
1,
0,
furvm::byte(furvm::instruction_t::Return),
};
void print(furvm::executor& exec) {
std::cout << exec.pop_thing()->int32() << '\n';
}
int main(void) {
auto context = std::make_shared<furvm::context>();
auto mainModule = context->emplace(s_bytecode.begin(), s_bytecode.end());
auto mainFunction = furvm::function::create(mainModule, 0);
auto printFunction = furvm::function::create(mainModule, print);
auto executor = furvm::executor::create(context);
executor->push_frame(mainFunction);
static constexpr std::size_t FPC = 3; // Frames per collection
std::size_t count = 0;
while ((executor->flags() & furvm::executor_flags::Done) != furvm::executor_flags::Done) {
executor->step();
if ((++count % FPC) == 0) context->collect();
}
return 0;
}
#endif // LUBFURVM
-3
View File
@@ -1,3 +0,0 @@
#include "furvm/module.hpp"
namespace furvm {}
-177
View File
@@ -1,177 +0,0 @@
// NOLINTBEGIN(cppcoreguidelines-no-malloc)
#include "furvm/thing.hpp"
#include "furvm/context.hpp" // IWYU pragma: keep
#include <cstdlib>
#include <cstring>
#include <stdexcept>
namespace furvm {
std::size_t thing_type_size(thing_t type) {
switch (type) {
case thing_t::Int32: return 4;
}
return 0;
}
thing::thing(rzecz, thing_handle id, thing_t type, const context_p& context)
: m_id(id), m_type(type), m_context(context) {
std::size_t size = thing_type_size(type);
std::byte* data = nullptr;
if (!m_context->m_deadThingData.empty()) {
thing_t itType{};
for (auto it = m_context->m_deadThingData.rbegin(); it != m_context->m_deadThingData.rend(); ++it) {
std::memcpy(&itType, static_cast<std::byte*>(*it) - sizeof(itType), sizeof(itType));
if (size == thing_type_size(itType)) {
data = static_cast<std::byte*>(*it);
m_context->m_deadThingData.erase(std::next(it).base());
break;
}
}
}
if (data == nullptr) data = m_context->m_thingArena.allocate<std::byte>(sizeof(type) + size);
if (data == nullptr) throw std::runtime_error("failed to allocate data for new thing");
std::memcpy(data, &type, sizeof(type));
m_data = data + sizeof(type);
switch (m_type) {
// Primitives are zero-initialized.
default:
case thing_t::Int32: std::memset(m_data, 0, size);
}
}
thing::~thing() {
switch (m_type) {
// Primitives are not destructed.
default:
case thing_t::Int32: break;
}
m_context->m_deadThingData.push_back(m_data);
}
thing::thing(thing&& other) noexcept
: m_id(other.m_id),
m_type(other.m_type),
m_context(std::move(other.m_context)),
m_refCount(other.m_refCount),
m_data(other.m_data) {
other.m_id = {};
other.m_type = {};
other.m_refCount = {};
other.m_data = nullptr;
}
thing& thing::operator=(thing&& other) noexcept {
if (this == &other) return *this;
m_id = other.m_id;
m_type = other.m_type;
m_context = std::move(other.m_context);
m_refCount = other.m_refCount;
m_data = other.m_data;
other.m_id = {};
other.m_type = {};
other.m_refCount = {};
other.m_data = nullptr;
return *this;
}
static constexpr std::uint16_t thing_type_pair(thing_t lhs, thing_t rhs) {
return (static_cast<std::uint16_t>(lhs) << 8) | static_cast<std::uint16_t>(rhs);
}
thing_p operator+(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = lhs->int32() + rhs->int32();
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator-(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = lhs->int32() - rhs->int32();
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator*(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = lhs->int32() * rhs->int32();
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator/(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = lhs->int32() / rhs->int32();
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p operator%(const thing_p& lhs, const thing_p& rhs) {
switch (thing_type_pair(lhs->m_type, rhs->m_type)) {
case thing_type_pair(thing_t::Int32, thing_t::Int32): {
auto res = thing::create(lhs->m_context, thing_t::Int32);
res->int32() = lhs->int32() % rhs->int32();
return res;
}
default: throw std::runtime_error("unexpected operator");
}
}
thing_p thing::clone(const thing_p& thing) {
thing_handle id = thing->m_context->m_things.size();
if (!thing->m_context->m_deadThings.empty()) {
id = thing->m_context->m_deadThings.front();
thing->m_context->m_deadThings.pop();
id += 1 << GENERATION_SIZE;
}
thing_handle idx = id & ((1ULL << ((sizeof(id) * 8) - GENERATION_SIZE)) - 1);
auto th = std::make_shared<class thing>(rzecz{}, id, thing->m_type, thing->m_context);
switch (thing->m_type) {
// Primitives
default: {
memcpy(th->m_data, thing->m_data, thing_type_size(thing->m_type));
}
}
thing->m_context->m_things.emplace(thing->m_context->m_things.begin() + idx, th);
return std::move(th);
}
std::int32_t& thing::int32() {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
const std::int32_t& thing::int32() const {
if (m_type != thing_t::Int32) throw bad_thing_access();
return *static_cast<std::int32_t*>(m_data);
}
} // namespace furvm
// NOLINTEND(cppcoreguidelines-no-malloc)
-3
View File
@@ -1,3 +0,0 @@
#include "gtest/gtest.h" // IWYU pragma: keep
namespace {}
+36
View File
@@ -0,0 +1,36 @@
#ifndef FURAS_GEN_HPP
#define FURAS_GEN_HPP
#include "furas/lexer.hpp"
#include "furvm/module.hpp"
#include <string>
namespace furas {
struct generator_error {
enum type {
Success = 0,
Eof = 1,
UnexpectedEof = -1,
UnexpectedToken = -2,
UnknownCharacter = -3,
UnknownType = -4,
} type = Success;
std::string message;
};
class generator {
public:
struct result {
generator_error error;
furvm::mod mod;
};
public:
static result generate(lexer lexer);
};
} // namespace furas
#endif // FURAS_GEN_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURAS_LEXER_HPP
#define FURAS_LEXER_HPP
#include "furas/token.hpp"
#include "furlang/result.hpp"
#include <cstddef>
#include <string_view>
namespace furas {
struct lexer_location {
std::string_view filename;
std::size_t row, col;
};
struct lexer_error {
enum type {
EndOfFile = 0,
UnknownCharacter,
} type;
lexer_location location;
std::string message;
};
using token_r = furlang::result<lexer_error, token>;
class lexer {
public:
lexer(std::string_view filename, std::string_view content)
: m_filename(filename), m_content(content) {}
token_r next_token();
private:
constexpr lexer_location location() const { return { m_filename, m_cursor - m_lineStart, m_column }; }
private:
std::string_view m_filename;
std::string_view m_content;
std::size_t m_cursor = 0;
std::size_t m_lineStart = 0;
std::size_t m_column = 0;
};
} // namespace furas
#endif // FURAS_LEXER_HPP
+98
View File
@@ -0,0 +1,98 @@
#ifndef FURAS_TOKEN_HPP
#define FURAS_TOKEN_HPP
#include <cstdint>
#include <string_view>
namespace furas {
struct token {
enum type {
Identifier = 0, /**< An identifier. */
Signed, /**< A signed integer. */
Unsigned, /**< An unsigned integer. */
// Markers
Monkey, /**< Constant marker (`@`). */
Dolar, /**< Type marker(`$`). */
Sha256, /**< Label marker(`#`). */
Percent, /**< Variable marker(`%`). The more the better. */
EqSign, /**< `=` */
Dot, /**< . */
Colon, /**< `:` */
// Keywords
Func, /**< `func` keyword for defining functions. */
Type, /**< `type` keyword for defining types. */
Native, /**< `native` keyword for native functions. :v: */
Import, /**< `import` keyword for importing functions and types. */
Public, /**< `public` access specifier. */
Private, /**< `private` access specifier. */
Allocate, /**< `allocate` keyword for global variables. */
// Instructions
Push,
Array,
Slice,
Get,
Set,
Drop,
Dup,
Swap,
Clone,
Ref,
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
Ptrof,
Sizeof,
Lenof,
Load,
Store,
LoadGlobal,
StoreGlobal,
Call,
Jmp,
Jnz,
Ret,
Count
} type = Count;
union value {
std::nullptr_t null = nullptr;
std::string_view string;
std::int64_t integer;
std::uint64_t uint;
} value;
token(enum type type)
: type(type) {}
token(enum type type, std::string_view string)
: type(type) {
value.string = string;
}
token(std::uint64_t num)
: type(Unsigned) {
value.uint = num;
}
token(std::int64_t num)
: type(Signed) {
value.integer = num;
}
};
} // namespace furas
#endif // FURAS_TOKEN_HPP
+16
View File
@@ -0,0 +1,16 @@
#ifndef FURC_BACK_FURVM_HPP
#define FURC_BACK_FURVM_HPP
#include "furc/middle/ir.hpp"
#include "furvm/module.hpp"
namespace furc {
class furvm_generator final {
public:
static furvm::mod generate(const ir_module& mod);
};
} // namespace furc
#endif // FURC_BACK_FURVM_HPP
+346
View File
@@ -0,0 +1,346 @@
#ifndef FURC_FRONT_AST_HPP
#define FURC_FRONT_AST_HPP
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
namespace furc {
struct comp_stmt_node;
struct if_stmt_node;
struct while_stmt_node;
struct return_stmt_node;
struct var_decl_node;
struct func_decl_node;
struct var_read_expr_node;
struct func_call_expr_node;
struct group_expr_node;
struct binary_op_expr_node;
struct unary_op_expr_node;
struct if_expr_node;
struct int_lit_node;
struct char_lit_node;
struct ast_visitor {
ast_visitor() = default;
virtual ~ast_visitor() = default;
ast_visitor(ast_visitor&&) noexcept = default;
ast_visitor& operator=(ast_visitor&&) noexcept = default;
ast_visitor(const ast_visitor&) = default;
ast_visitor& operator=(const ast_visitor&) = default;
virtual void visit_comp_stmt_node(const comp_stmt_node& node) {}
virtual void visit_if_stmt_node(const if_stmt_node& node) {}
virtual void visit_while_stmt_node(const while_stmt_node& node) {}
virtual void visit_return_stmt_node(const return_stmt_node& node) {}
virtual void visit_var_decl_node(const var_decl_node& node) {}
virtual void visit_func_decl_node(const func_decl_node& node) {}
virtual void visit_var_read_expr_node(const var_read_expr_node& node) {}
virtual void visit_func_call_expr_node(const func_call_expr_node& node) {}
virtual void visit_group_expr_node(const group_expr_node& node) {}
virtual void visit_binary_op_expr_node(const binary_op_expr_node& node) {}
virtual void visit_unary_op_expr_node(const unary_op_expr_node& node) {}
virtual void visit_if_expr_node(const if_expr_node& node) {}
virtual void visit_int_lit_node(const int_lit_node& node) {}
virtual void visit_char_lit_node(const char_lit_node& node) {}
};
struct ast_type {
enum type_e {
Void = 0,
S8,
U8,
S16,
U16,
S32,
U32,
S64,
U64,
} type = Void;
};
class ast_node {
public:
enum category_e {
Statement,
Declaration,
Expression,
Literal,
};
public:
ast_node() = default;
virtual ~ast_node() = default;
ast_node(ast_node&&) noexcept = default;
ast_node& operator=(ast_node&&) noexcept = default;
ast_node(const ast_node&) = delete;
ast_node& operator=(const ast_node&) = delete;
public:
virtual category_e category() const = 0;
virtual void accept(ast_visitor& visitor) const = 0;
};
using ast_node_cat = ast_node::category_e;
class stmt_node : public ast_node {
public:
enum stmt_type_e {
Declaration = 0,
Expression,
Compound,
If,
While,
Return,
};
public:
category_e category() const override { return ast_node_cat::Statement; }
virtual stmt_type_e stmt_type() const = 0;
};
struct comp_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return Compound; }
void accept(ast_visitor& visitor) const override { visitor.visit_comp_stmt_node(*this); }
std::vector<stmt_node*> stmts;
};
class expr_node;
struct if_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return If; }
void accept(ast_visitor& visitor) const override { visitor.visit_if_stmt_node(*this); }
expr_node* cond = nullptr;
stmt_node* thenBranch = nullptr;
stmt_node* elseBranch = nullptr;
};
struct while_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return While; }
void accept(ast_visitor& visitor) const override { visitor.visit_while_stmt_node(*this); }
expr_node* cond = nullptr;
stmt_node* body = nullptr;
};
struct return_stmt_node final : public stmt_node {
stmt_type_e stmt_type() const override { return Return; }
void accept(ast_visitor& visitor) const override { visitor.visit_return_stmt_node(*this); }
expr_node* value = nullptr;
};
class decl_node : public stmt_node {
public:
enum decl_type_e {
Variable,
Function
};
public:
category_e category() const override { return ast_node_cat::Declaration; }
stmt_type_e stmt_type() const override { return Declaration; }
virtual decl_type_e decl_type() const = 0;
};
struct var_decl_node final : public decl_node {
decl_type_e decl_type() const override { return Variable; }
void accept(ast_visitor& visitor) const override { visitor.visit_var_decl_node(*this); }
std::string name;
ast_type type;
expr_node* init = nullptr;
};
struct func_decl_node final : public decl_node {
decl_type_e decl_type() const override { return Function; }
void accept(ast_visitor& visitor) const override { visitor.visit_func_decl_node(*this); }
struct def_s {
comp_stmt_node body;
std::vector<expr_node*> preConds;
std::vector<expr_node*> postConds;
};
std::string name;
ast_type type;
std::vector<var_decl_node> params;
std::optional<def_s> def;
};
class expr_node : public stmt_node {
public:
enum expr_type_e {
Literal,
VarRead,
FunctionCall,
Group,
BinaryOp,
UnaryOp,
If,
};
public:
category_e category() const override { return ast_node_cat::Expression; }
stmt_type_e stmt_type() const override { return Expression; }
virtual expr_type_e expr_type() const = 0;
};
struct var_read_expr_node final : public expr_node {
expr_type_e expr_type() const override { return VarRead; }
void accept(ast_visitor& visitor) const override { visitor.visit_var_read_expr_node(*this); }
std::string name;
var_read_expr_node(std::string&& name)
: name(std::move(name)) {}
};
struct func_call_expr_node final : public expr_node {
expr_type_e expr_type() const override { return FunctionCall; }
void accept(ast_visitor& visitor) const override { visitor.visit_func_call_expr_node(*this); }
expr_node* lhs = nullptr;
std::vector<expr_node*> args;
};
struct group_expr_node final : public expr_node {
expr_type_e expr_type() const override { return Group; }
void accept(ast_visitor& visitor) const override { visitor.visit_group_expr_node(*this); }
expr_node* inner = nullptr;
};
struct binary_op_expr_node final : public expr_node {
enum binary_op_type {
Add = 0,
Sub,
Mul,
Div,
Mod,
Shl,
Shr,
BinAnd,
BinOr,
BinXor,
And,
Or,
Equals,
NotEquals,
LessThan,
LessEquals,
GreaterThan,
GreaterEquals,
};
expr_type_e expr_type() const override { return BinaryOp; }
void accept(ast_visitor& visitor) const override { visitor.visit_binary_op_expr_node(*this); }
expr_node* lhs = nullptr;
expr_node* rhs = nullptr;
binary_op_type type = Add;
};
struct unary_op_expr_node final : public expr_node {
enum unary_op_type {
Positive = 0,
Negative,
PreInc,
PreDec,
PostInc,
PostDec,
BinNot,
Not,
Sizeof,
Pointerof,
Lengthof,
};
expr_type_e expr_type() const override { return UnaryOp; }
void accept(ast_visitor& visitor) const override { visitor.visit_unary_op_expr_node(*this); }
expr_node* lhs = nullptr;
unary_op_type type = Positive;
};
struct if_expr_node final : public expr_node {
expr_type_e expr_type() const override { return If; }
void accept(ast_visitor& visitor) const override { visitor.visit_if_expr_node(*this); }
expr_node* cond = nullptr;
expr_node* thenExpr = nullptr;
expr_node* elseExpr = nullptr;
};
class lit_node : public expr_node {
public:
enum lit_type_e {
Integer,
Char,
};
public:
category_e category() const override { return ast_node_cat::Literal; }
expr_type_e expr_type() const override { return Literal; }
virtual lit_type_e lit_type() const = 0;
};
struct int_lit_node final : public lit_node {
int_lit_node(std::uint64_t value)
: value(value) {}
lit_type_e lit_type() const override { return Integer; }
void accept(ast_visitor& visitor) const override { visitor.visit_int_lit_node(*this); }
std::uint64_t value;
};
struct char_lit_node final : public lit_node {
char_lit_node(char value)
: value(value) {}
lit_type_e lit_type() const override { return Char; }
void accept(ast_visitor& visitor) const override { visitor.visit_char_lit_node(*this); }
char value;
};
struct ast {
std::vector<decl_node*> decls;
};
} // namespace furc
#endif // FURC_FRONT_AST_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURC_FRONT_LEXER_HPP
#define FURC_FRONT_LEXER_HPP
#include "furc/front/token.hpp"
#include <cstddef>
#include <deque>
#include <string_view>
namespace furc {
class lexer {
public:
lexer(std::string_view filepath, std::string_view content)
: m_filepath(filepath), m_content(content) {}
~lexer() = default;
lexer(lexer&&) noexcept = default;
lexer& operator=(lexer&&) noexcept = default;
lexer(const lexer&) = delete;
lexer& operator=(const lexer&) = delete;
public:
token next_token();
token peek_token(std::size_t offset = 0);
private:
token get_token();
void next();
constexpr char get(std::size_t offset = 0) const;
void skip_spaces();
constexpr token::location location() const;
private:
std::string_view m_filepath;
std::string_view m_content;
std::size_t m_cursor = 0;
std::size_t m_row = 0;
std::size_t m_lineStart = 0;
std::deque<token> m_peekToken;
};
} // namespace furc
#endif // FURC_FRONT_LEXER_HPP
+51
View File
@@ -0,0 +1,51 @@
#ifndef FURC_FRONT_PARSER_HPP
#define FURC_FRONT_PARSER_HPP
#include "furc/front/ast.hpp"
#include "furc/front/lexer.hpp"
#include "furlang/arena.hpp"
#include <stdexcept>
namespace furc {
class parser {
public:
parser(lexer&& lexer, furlang::arena& arena)
: m_lexer(std::move(lexer)), m_arena(&arena) {}
~parser() = default;
parser(parser&&) noexcept = default;
parser& operator=(parser&&) noexcept = default;
parser(const parser&) = delete;
parser& operator=(const parser&) = delete;
public:
ast parse();
private:
stmt_node* parse_stmt();
decl_node* parse_decl();
expr_node* parse_expr();
ast_type parse_type();
comp_stmt_node parse_comp();
expr_node* parse_expr_primary();
expr_node* parse_expr_unary();
expr_node* parse_expr_right(expr_node* lhs, std::uint32_t precedence = 15);
private:
template <typename... Types>
token eat_token(Types... types) {
auto token = m_lexer.next_token();
if (((token.type == types) || ...)) return token;
throw std::runtime_error("unexpected token");
}
private:
lexer m_lexer;
furlang::arena* m_arena;
};
} // namespace furc
#endif // FURC_FRONT_PARSER_HPP
+218
View File
@@ -0,0 +1,218 @@
#ifndef FURC_FRONT_TOKEN_HPP
#define FURC_FRONT_TOKEN_HPP
#include <cstddef>
#include <cstdint>
#include <ostream>
#include <string_view>
namespace furc {
struct token {
enum type {
Identifier = 0,
Integer,
String,
Char,
LParen, /**< `(` */
RParen, /**< `)` */
LBrace, /**< `{` */
RBrace, /**< `}` */
LBracket, /**< `[` */
RBracket, /**< `]` */
Semicolon, /**< `;` */
Colon, /**< `:` */
Comma, /**< `,` */
Dot, /**< `.` */
Plus, /**< `+` */
Minus, /**< `-` */
Star, /**< `*` */
Slash, /**< `/` */
Percent, /**< `%` */
DblLT, /**< `<<` */
DblGT, /**< `>>` */
Ampersand, /**< `&` */
Pipe, /**< `|` */
Hat, /**< `^` */
DblAmpersand, /**< `&&` */
DblPipe, /**< `||` */
DblPlus, /**< `++` */
DblMinus, /**< `--` */
Tilde, /**< `~` */
ExMark, /**< `!` */
CatEars, /**< `^^` */
Equals, /**< `=` */
PlusEquals, /**< `+=` */
MinusEquals, /**< `-=` */
StarEquals, /**< `*=` */
SlashEquals, /**< `/=` */
PercentEquals, /**< `%=` */
AmpersandEquals, /**< `&=` */
PipeEquals, /**< `|=` */
HatEquals, /**< `^=` */
DblEquals, /**< `==` */
ExEquals, /**< `!=` */
LessThan, /**< `<` */
LessEquals, /**< `<=` */
GreaterThan, /**< `>` */
GreaterEquals, /**< `>=` */
SlimArrow, /**< `->` */
// My brother just another me
FatArrow, /**< `=>` */
Monkey, /**< `@` */
Sha256, /**< `#` */
Func, /**< `func` */
Return, /**< `return` */
If, /**< `if` */
Else, /**< `else` */
While, /**< `while` */
Public, /**< `public` */
Private, /**< `private` */
Pre, /**< `pre` */
Post, /**< `post` */
Pointerof, /**< `pointerof` */
Sizeof, /**< `sizeof` */
Lengthof, /**< `lengthof` */
S8, /**< `s8` */
U8, /**< `u8` */
S16, /**< `s16` */
U16, /**< `u16` */
S32, /**< `s32` */
U32, /**< `u32` */
S64, /**< `s64` */
U64, /**< `u64` */
// Errors:
UnexpectedCharacter,
UnexpectedEOF,
InvalidInteger,
EndOfFile,
} type;
union value {
std::nullptr_t null = nullptr;
std::uint64_t integer;
std::string_view string;
char character;
} value;
struct location {
std::string_view filepath;
std::size_t row = 0;
std::size_t col = 0;
} loc;
token(location loc, enum type type)
: loc(loc), type(type) {}
token(location loc, std::uint64_t integer)
: loc(loc), type(Integer) {
value.integer = integer;
}
token(location loc, enum type type, std::string_view string)
: loc(loc), type(type) {
value.string = string;
}
token(location loc, enum type type, char character)
: loc(loc), type(type) {
value.character = character;
}
friend std::ostream& operator<<(std::ostream& os, const token& token) {
switch (token.type) {
case token::Identifier: return os << token.value.string;
case token::String: return os << '"' << token.value.string << '"';
case token::Char: return os << '\'' << token.value.character << '\'';
case token::Integer: return os << token.value.integer;
case token::LParen: return os << "(";
case token::RParen: return os << ")";
case token::LBrace: return os << "{";
case token::RBrace: return os << "}";
case token::LBracket: return os << "[";
case token::RBracket: return os << "]";
case token::Semicolon: return os << ";";
case token::Colon: return os << ":";
case token::Comma: return os << ",";
case token::Dot: return os << ".";
case token::Plus: return os << "+";
case token::Minus: return os << "-";
case token::Star: return os << "*";
case token::Slash: return os << "/";
case token::Percent: return os << "%";
case token::DblLT: return os << "<<";
case token::DblGT: return os << ">>";
case token::Ampersand: return os << "&";
case token::Pipe: return os << "|";
case token::Hat: return os << "^";
case token::DblAmpersand: return os << "&&";
case token::DblPipe: return os << "||";
case token::DblPlus: return os << "++";
case token::DblMinus: return os << "--";
case token::Tilde: return os << "~";
case token::ExMark: return os << "!";
case token::CatEars: return os << "^^";
case token::Equals: return os << "=";
case token::PlusEquals: return os << "+=";
case token::MinusEquals: return os << "-=";
case token::StarEquals: return os << "*=";
case token::SlashEquals: return os << "/=";
case token::PercentEquals: return os << "%=";
case token::AmpersandEquals: return os << "&=";
case token::PipeEquals: return os << "|=";
case token::HatEquals: return os << "^=";
case token::DblEquals: return os << "==";
case token::ExEquals: return os << "!=";
case token::LessThan: return os << "<";
case token::LessEquals: return os << "<=";
case token::GreaterThan: return os << ">";
case token::GreaterEquals: return os << ">=";
case token::SlimArrow: return os << "->";
case token::FatArrow: return os << "=>";
case token::Monkey: return os << "@";
case token::Sha256: return os << "#";
case token::Func: return os << "func";
case token::Return: return os << "return";
case token::If: return os << "if";
case token::Else: return os << "else";
case token::While: return os << "while";
case token::Public: return os << "public";
case token::Private: return os << "private";
case token::Pre: return os << "pre";
case token::Post: return os << "post";
case token::Pointerof: return os << "pointerof";
case token::Sizeof: return os << "sizeof";
case token::Lengthof: return os << "lengthof";
case token::S8: return os << "s8";
case token::U8: return os << "u8";
case token::S16: return os << "s16";
case token::U16: return os << "u16";
case token::S32: return os << "s32";
case token::U32: return os << "u32";
case token::S64: return os << "s64";
case token::U64: return os << "u64";
case token::UnexpectedCharacter: return os << "Unexpected character `" << token.value.character << "`";
case token::UnexpectedEOF: return os << "Unexpected End Of File";
case token::InvalidInteger: return os << "Invalid Integer";
case token::EndOfFile: return os << "End Of File";
}
return os;
}
};
using token_t = enum token::type;
} // namespace furc
#endif // FURC_FRONT_TOKEN_HPP
+455
View File
@@ -0,0 +1,455 @@
#ifndef FURC_MIDDLE_IR_HPP
#define FURC_MIDDLE_IR_HPP
#include "furc/front/ast.hpp"
#include "furlang/arena.hpp"
#include <algorithm>
#include <cstdint>
#include <initializer_list>
#include <optional>
#include <stack>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furc {
struct ir_operand {
enum type_e {
Integer = 0,
Register,
Variable,
Global,
Function,
Block,
BlockPair,
PhiPair,
} type;
union value_u {
std::uint64_t integer;
struct register_s {
std::uint64_t name : 54;
std::uint64_t ver : 10;
register_s() = default;
register_s(std::uint64_t id)
: name((id >> 10) & ((1ULL << 54) - 1)), ver((id >> 0) & ((1 << 10) - 1)) {}
register_s(std::uint64_t name, std::uint64_t ver)
: name(name), ver(ver) {}
operator std::uint64_t() const { return name << 10 | ver; }
} reg;
std::uint16_t variable;
std::uint16_t global;
std::uint64_t function;
std::uint64_t block;
struct block_pair_s {
std::uint64_t first;
std::uint64_t second;
} blockPair;
struct phi_pair_s {
register_s reg;
std::uint64_t block;
} phiPair;
value_u() = default;
value_u(std::uint64_t integer)
: integer(integer) {}
value_u(std::uint16_t variable)
: variable(variable) {}
value_u(register_s reg)
: reg(reg) {}
value_u(std::uint64_t first, std::uint64_t second)
: blockPair({ first, second }) {}
value_u(register_s reg, std::uint64_t block)
: phiPair({ reg, block }) {}
} value;
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_u, Args...>>>
ir_operand(type_e type, Args&&... args)
: type(type), value(std::forward<Args>(args)...) {}
static ir_operand reg(std::uint64_t name, std::uint64_t ver) {
return { Register, value_u::register_s{ name, ver } };
}
bool operator==(const ir_operand& rhs) const {
if (type != rhs.type) return false;
switch (type) {
case Integer: return value.integer == rhs.value.integer;
case Register: return value.reg.name == rhs.value.reg.name && value.reg.ver == rhs.value.reg.ver;
case Variable: return value.variable == rhs.value.variable;
case Global: return value.global == rhs.value.global;
case Function: return value.function == rhs.value.function;
case Block: return value.block == rhs.value.block;
case BlockPair:
return value.blockPair.first == rhs.value.blockPair.first &&
value.blockPair.second == rhs.value.blockPair.second;
case PhiPair:
return value.phiPair.block == rhs.value.phiPair.block &&
value.phiPair.reg.name == rhs.value.phiPair.reg.name &&
value.phiPair.reg.ver == rhs.value.phiPair.reg.ver;
}
throw std::runtime_error("unreachable");
}
};
struct ir_type {
enum type_e {
Void = 0,
S8,
U8,
S16,
U16,
S32,
U32,
S64,
U64,
} type = Void;
};
// TODO: Add data types to instructions (like mov QWORD ... in x86 assembly)
struct ir_instruction {
enum type_e {
Move = 0,
Call,
Branch,
BranchCond,
Return,
Phi,
Add,
Sub,
Mul,
Div,
Mod,
Shl,
Shr,
BinAnd,
BinOr,
BinXor,
And,
Or,
Eq,
NotEq,
LessThan,
LessEq,
GreaterThan,
GreaterEq,
Positive,
Negative,
Increment,
Decrement,
BinNot,
Not,
Sizeof,
Pointerof,
Lenof,
} type;
std::optional<ir_operand> destination;
std::vector<ir_operand> sources;
ir_instruction(type_e type,
std::optional<ir_operand> destination = {},
std::initializer_list<ir_operand> sources = {})
: type(type), destination(destination), sources(sources) {}
static constexpr bool is_terminating(type_e type) {
switch (type) {
case Branch:
case BranchCond:
case Return: return true;
default: return false;
}
}
bool operator==(const ir_instruction& rhs) const {
return type == rhs.type && destination == rhs.destination && sources == rhs.sources;
}
};
struct ir_basic_block {
std::vector<ir_instruction> instructions;
bool is_terminated() const {
return !instructions.empty() && ir_instruction::is_terminating(instructions.back().type);
}
};
struct ir_variable {
ir_variable() = default;
ir_variable(ir_type type)
: type(type) {}
virtual ~ir_variable() = default;
ir_variable(ir_variable&&) noexcept = default;
ir_variable& operator=(ir_variable&&) noexcept = default;
ir_variable(const ir_variable&) = default;
ir_variable& operator=(const ir_variable&) = default;
ir_type type;
virtual ir_operand operand() const = 0;
};
struct ir_module_variable : ir_variable {
ir_module_variable(ir_type type, std::uint16_t name)
: ir_variable(type), name(name) {}
std::uint16_t name;
ir_operand operand() const final { return { ir_operand::Global, name }; }
};
struct ir_function_variable : ir_variable {
ir_function_variable(ir_type type, std::uint64_t name)
: ir_variable(type), name(name) {}
std::uint64_t name;
ir_operand operand() const final { return { ir_operand::Variable, name }; }
};
struct ir_scope {
ir_scope() = default;
virtual ~ir_scope() = default;
ir_scope(ir_scope&&) noexcept = default;
ir_scope& operator=(ir_scope&&) noexcept = default;
ir_scope(const ir_scope&) = default;
ir_scope& operator=(const ir_scope&) = default;
ir_scope* previous = nullptr;
std::unordered_map<std::string, ir_variable*> variables;
const ir_variable* variable(const std::string& name) const {
if (auto it = variables.find(name); it != variables.end()) return it->second;
return (previous != nullptr) ? previous->variable(name) : nullptr;
}
virtual const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) = 0;
};
struct ir_function : ir_scope {
enum type_e {
Normal = 0,
Import,
Native,
} type = Normal;
enum access_e {
Public = 0,
Private,
} access = Public;
std::string name;
std::vector<ir_type> params;
ir_type retType;
std::vector<ir_basic_block> blocks;
std::uint64_t regCount = 0;
std::uint64_t varCount = 0;
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
return variables[name] = arena.allocate<ir_function_variable>(type, varCount++);
}
static ir_function from_name(std::string&& name) {
ir_function func;
func.name = std::move(name);
return func;
}
};
struct ir_module : ir_scope {
std::vector<ir_function*> functions;
furlang::arena arena;
std::uint16_t varCount = 0;
const ir_variable* allocate(furlang::arena& arena, const std::string& name, ir_type type) final {
return variables[name] = arena.allocate<ir_module_variable>(type, varCount);
}
ir_function* add_function(ir_function&& function) {
return functions.emplace_back(arena.allocate<ir_function>(std::move(function)));
}
};
struct ir_context {
ir_context(ir_function* function)
: function(function) {
if (function->blocks.empty()) new_last();
blockPtr = &function->blocks.front();
}
~ir_context() {
if (blockPtr == nullptr) return;
if (!blockPtr->is_terminated()) {
if (blockIdx + 1 == function->blocks.size()) {
add_instr(ir_instruction::Return);
} else {
add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, blockIdx + 1 });
}
}
}
ir_context(ir_context&& other) noexcept
: function(other.function), blockIdx(other.blockIdx), blockPtr(other.blockPtr) {
other.function = nullptr;
other.blockIdx = 0;
other.blockPtr = nullptr;
}
ir_context& operator=(ir_context&& other) noexcept {
if (this == &other) return *this;
function = other.function;
blockIdx = other.blockIdx;
blockPtr = other.blockPtr;
other.function = nullptr;
other.blockIdx = 0;
other.blockPtr = nullptr;
return *this;
}
ir_context(const ir_context&) = delete;
ir_context& operator=(const ir_context&) = delete;
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<ir_instruction, Args...>>>
ir_instruction& add_instr(Args&&... args) {
auto it = blockPtr->instructions.end();
if (!blockPtr->instructions.empty() && ir_instruction::is_terminating(blockPtr->instructions.back().type)) --it;
it = blockPtr->instructions.emplace(it, std::forward<Args>(args)...);
if (ir_instruction::is_terminating(it->type) && it + 1 != blockPtr->instructions.end())
blockPtr->instructions.pop_back();
return *it;
}
void terminate() { add_instr(ir_instruction::Return); }
void terminate(ir_operand value) {
ir_instruction instr = { ir_instruction::Return };
instr.sources.emplace_back(value);
add_instr(std::move(instr));
}
void terminate(std::uint64_t block) { add_instr(ir_instruction::Branch, ir_operand{ ir_operand::Block, block }); }
ir_instruction* terminate(ir_operand cond, std::uint64_t thenBranch, std::uint64_t elseBranch) {
return &add_instr(ir_instruction{ ir_instruction::BranchCond,
ir_operand{ ir_operand::BlockPair, thenBranch, elseBranch },
{ cond } });
}
ir_context& new_next() {
if (blockPtr->instructions.empty()) return *this;
auto it = function->blocks.begin() + static_cast<std::ptrdiff_t>(++blockIdx);
if (!blockPtr->is_terminated()) terminate(blockIdx);
blockPtr = &*function->blocks.emplace(it);
return *this;
}
ir_context& new_last() {
blockIdx = function->blocks.size();
blockPtr = &*function->blocks.emplace(function->blocks.end());
return *this;
}
ir_context& go(std::uint64_t block) {
blockIdx = std::min(block, function->blocks.size() - 1);
blockPtr = function->blocks.data() + static_cast<std::ptrdiff_t>(blockIdx);
return *this;
}
ir_context& go_next() { return go(blockIdx + 1); }
ir_context& go_last() { return go(std::min<std::uint64_t>(0, function->blocks.size() - 1)); }
ir_operand last_register() const { return { ir_operand::Register, function->regCount - 1 }; }
ir_operand next_register() const { return { ir_operand::Register, function->regCount++ }; }
static ir_operand block_op(std::uint64_t blockIdx) { return { ir_operand::Block, blockIdx }; }
ir_function* function = nullptr;
std::uint64_t blockIdx = 0;
ir_basic_block* blockPtr = nullptr;
};
class ir_generator final : public ast_visitor {
public:
ir_generator()
: m_initContext(m_module.add_function(ir_function::from_name("module$init"))) {}
void finalize() {
m_module.functions.front()->blocks.emplace_back().instructions.push_back(
ir_instruction{ ir_instruction::Return });
}
ir_module build() {
m_scope = nullptr;
m_context = {};
m_initContext.blockPtr = nullptr;
return std::move(m_module);
}
static ir_module generate(const ast_node& node) {
ir_generator gen;
node.accept(gen);
gen.finalize();
return gen.build();
}
static ir_module generate(const ast& tree) {
ir_generator gen;
for (const auto& node : tree.decls)
node->accept(gen);
gen.finalize();
return gen.build();
}
private:
void visit_comp_stmt_node(const comp_stmt_node& node) override;
void visit_if_stmt_node(const if_stmt_node& node) override;
void visit_while_stmt_node(const while_stmt_node& node) override;
void visit_return_stmt_node(const return_stmt_node& node) override;
void visit_var_decl_node(const var_decl_node& node) override;
void visit_func_decl_node(const func_decl_node& node) override;
void visit_var_read_expr_node(const var_read_expr_node& node) override;
void visit_func_call_expr_node(const func_call_expr_node& node) override;
void visit_group_expr_node(const group_expr_node& node) override;
void visit_binary_op_expr_node(const binary_op_expr_node& node) override;
void visit_unary_op_expr_node(const unary_op_expr_node& node) override;
void visit_if_expr_node(const if_expr_node& node) override;
void visit_int_lit_node(const int_lit_node& node) override;
void visit_char_lit_node(const char_lit_node& node) override;
private:
ir_context& context() { return m_context.top(); }
private:
ir_module m_module;
ir_scope* m_scope = &m_module;
std::stack<ir_context> m_context;
ir_context m_initContext;
};
} // namespace furc
#endif // FURC_MIDDLE_IR_HPP
+177
View File
@@ -0,0 +1,177 @@
/**
* SSA destruction (out-of-SSA phase) for register-based targets based on "Mechanizing Conventional SSA for a
* Verified Destruction with Coalescing" by Delphine Demange and Yon Fernandez de Retana
* (https://dl.acm.org/doi/pdf/10.1145/2892208.2892222 09/11/2026).
*/
#ifndef FURC_MIDDLE_REG_GEN_HPP
#define FURC_MIDDLE_REG_GEN_HPP
#include "furc/middle/ir.hpp"
#include "furc/middle/ssa.hpp"
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace furc {
class reg_gen {
public:
class disjoint_set {
public:
std::uint64_t find(std::uint64_t var) {
if (m_parents.find(var) == m_parents.end()) m_parents.emplace(var, var);
if (m_parents[var] == var) return var;
return find(m_parents[var]);
}
void unite(std::uint64_t var1, std::uint64_t var2) {
std::uint64_t rep1 = find(var1);
std::uint64_t rep2 = find(var2);
if (rep1 != rep2) m_parents[rep1] = rep2;
}
private:
std::unordered_map<std::uint64_t, std::uint64_t> m_parents;
};
public:
struct block_info {
std::unordered_set<std::uint64_t> defs;
std::unordered_set<std::uint64_t> uses;
std::unordered_set<std::uint64_t> liveIn;
std::unordered_set<std::uint64_t> liveOut;
};
public:
reg_gen(ir_function& func, ssa& ssa) {
std::vector<block_info> lifeBlocks;
live_analysis(lifeBlocks, func.blocks, ssa.cfgBlocks);
remove_interference(func.blocks, lifeBlocks);
merge(func.blocks);
}
public:
static void live_analysis(std::vector<block_info>& lifeBlocks,
const std::vector<ir_basic_block>& irBlocks,
const std::vector<ssa::cfg_block>& cfgBlocks) {
lifeBlocks.resize(irBlocks.size());
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
const auto& irBlock = irBlocks[i];
auto& block = lifeBlocks[i];
for (const auto& instr : irBlock.instructions) {
for (const auto& op : instr.sources) {
if (op.type != ir_operand::Register) continue;
if (block.defs.find(op.value.reg) != block.defs.end()) continue;
block.uses.insert(op.value.reg);
}
if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
block.defs.insert(instr.destination->value.reg);
}
}
bool changed = true;
while (changed) {
changed = false;
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
const auto& irBlock = irBlocks[i];
auto& block = lifeBlocks[i];
std::unordered_set<std::uint64_t> newSet;
for (auto succ : cfgBlocks[i].sucs) {
newSet.insert(lifeBlocks[succ].liveIn.begin(), lifeBlocks[succ].liveIn.end());
}
if (newSet != block.liveOut) {
block.liveOut = newSet;
changed = true;
}
newSet.clear();
newSet.insert(block.uses.begin(), block.uses.end());
for (const auto& var : block.liveOut) {
if (block.defs.find(var) != block.defs.end()) continue;
newSet.insert(var);
}
if (newSet != block.liveIn) {
block.liveIn = newSet;
changed = true;
}
}
}
}
static void remove_interference(std::vector<ir_basic_block>& irBlocks, const std::vector<block_info>& lifeBlocks) {
for (std::uint64_t i = 0; i < irBlocks.size(); ++i) {
auto& irBlock = irBlocks[i];
for (auto it = irBlock.instructions.begin(), end = irBlock.instructions.end();
it != end && it->type == ir_instruction::Phi;
++it) {
assert(it->destination.has_value() && it->destination->type == ir_operand::Register);
const auto& phiDst = it->destination->value.reg;
for (auto& op : it->sources) {
assert(op.type == ir_operand::PhiPair);
const auto& predBlock = lifeBlocks[op.value.phiPair.block];
auto& phiArg = op.value.phiPair.reg;
if (predBlock.liveOut.count(phiArg) == 0 || phiArg == phiDst) continue;
auto oldArg = phiArg;
phiArg.ver = 0; // TODO: Allocate temporary registers
auto& irBlock = irBlocks[op.value.phiPair.block];
assert(!irBlock.instructions.empty());
auto it = irBlock.instructions.end() - 1;
if (ir_instruction::is_terminating(it->type)) --it;
irBlock.instructions.emplace(it,
ir_instruction{ ir_instruction::Move,
ir_operand::reg(phiArg.name, phiArg.ver),
{ ir_operand::reg(oldArg.name, oldArg.ver) } });
}
}
}
}
static void merge(std::vector<ir_basic_block>& irBlocks) {
disjoint_set dj;
for (const auto& block : irBlocks) {
for (const auto& instr : block.instructions) {
if (instr.type != ir_instruction::Phi) break;
assert(instr.destination.has_value() && instr.destination->type == ir_operand::Register);
const auto& phiDst = instr.destination->value.reg;
dj.find(phiDst);
for (const auto& op : instr.sources) {
assert(op.type == ir_operand::PhiPair);
const auto& predBlock = op.value.phiPair.block;
const auto& phiArg = op.value.phiPair.reg;
dj.unite(phiDst, phiArg);
}
}
}
for (auto& block : irBlocks) {
auto it = block.instructions.begin();
while (it != block.instructions.end() && it->type == ir_instruction::Phi) {
it = block.instructions.erase(it);
}
for (; it != block.instructions.end(); ++it) {
for (auto& op : it->sources) {
if (op.type != ir_operand::Register) continue;
op.value.reg = dj.find(op.value.reg);
}
if (!it->destination.has_value() || it->destination->type != ir_operand::Register) continue;
it->destination->value.reg = dj.find(it->destination->value.reg);
}
}
}
};
} // namespace furc
#endif // FURC_MIDDLE_REG_GEN_HPP
+101
View File
@@ -0,0 +1,101 @@
#ifndef FURC_MIDDLE_SSA_HPP
#define FURC_MIDDLE_SSA_HPP
#include "furc/middle/ir.hpp"
#include <cassert>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace furc {
class ssa {
public:
struct cfg_block {
std::unordered_set<std::uint64_t> preds;
std::unordered_set<std::uint64_t> sucs;
};
struct ssa_block {
std::size_t order = 0;
std::uint64_t idom = -1;
std::unordered_set<std::uint64_t> children; // Children of the block in dominator tree
// Dominance Frontiers
std::unordered_set<std::uint64_t> df;
};
struct register_info {
std::unordered_set<std::uint64_t> sites; // Definition Sites
};
public:
ssa(ir_function& func) {
registers.resize(func.regCount);
compute_cfg(func.blocks, cfgBlocks);
collect_registers(func.blocks, registers, globals);
std::vector<std::uint64_t> order;
compute_rpo(cfgBlocks, ssaBlocks, order);
build_dtree(cfgBlocks, ssaBlocks, order);
compute_dfrontiers(cfgBlocks, ssaBlocks);
ssaification(func.blocks, cfgBlocks, ssaBlocks, registers, globals);
rename(func.blocks, func.regCount, cfgBlocks, ssaBlocks, order);
}
public:
static void compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks);
static void collect_registers(const std::vector<ir_basic_block>& irBlocks,
std::vector<register_info>& registers,
std::unordered_set<std::uint64_t>& globals);
static void build_dtree(const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::size_t>& order);
static void compute_dfrontiers(const std::vector<cfg_block>& cfgBlocks, std::vector<ssa_block>& ssaBlocks);
static void compute_rpo(std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
std::vector<std::size_t>& order);
static void ssaification(std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
const std::vector<register_info>& registers,
const std::unordered_set<std::uint64_t>& globals);
static void rename(std::vector<ir_basic_block>& irBlocks,
std::size_t regCount,
const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::uint64_t>& order);
private:
static void rename_rec(std::vector<std::uint64_t>& counters,
std::vector<std::stack<std::uint64_t>>& stacks,
std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
std::size_t blockIdx);
private:
static void rpo_dfs(std::unordered_set<std::size_t>& visited,
std::vector<std::size_t>& order,
std::size_t block,
const std::vector<cfg_block>& blocks);
static std::size_t intersect(std::vector<ssa_block>& m_blocks, std::size_t b1, std::size_t b2);
public:
std::vector<cfg_block> cfgBlocks;
std::vector<ssa_block> ssaBlocks;
std::vector<register_info> registers;
std::unordered_set<std::uint64_t> globals;
};
} // namespace furc
#endif // FURC_MIDDLE_SSA_HPP
@@ -106,6 +106,55 @@ private:
region* m_tail = nullptr;
};
template <typename T>
class arena_allocator {
template <typename>
friend class arena_allocator;
public:
using value_type = T;
public:
explicit arena_allocator(arena& arena) noexcept
: m_arena(&arena) {}
template <typename U>
arena_allocator(const arena_allocator<U>& other) noexcept
: m_arena(other.m_arena) {}
template <typename U>
arena_allocator& operator=(const arena_allocator<U>& other) noexcept {
if (this == &other) return *this;
m_arena = other.m_arena;
return *this;
}
template <typename U>
arena_allocator(arena_allocator<U>&& other) noexcept
: m_arena(std::move(other.m_arena)) {}
template <typename U>
arena_allocator& operator=(arena_allocator<U>&& other) noexcept {
if (this == &other) return *this;
m_arena = std::move(other.m_arena);
return *this;
}
public:
[[nodiscard]] T* allocate(std::size_t count = 1) { return m_arena->allocate<T>(count); }
void deallocate(T* ptr, std::size_t count) noexcept {}
public:
template <typename U>
bool operator==(const arena_allocator<U>& other) const noexcept {
return m_arena == other.m_arena;
}
template <typename U>
bool operator!=(const arena_allocator<U>& other) const noexcept {
return m_arena != other.m_arena;
}
private:
arena* m_arena;
};
} // namespace furlang
#endif // FURLANG_ARENA_HPP
#endif // FURLANG_ARENA_HPP
@@ -2,7 +2,9 @@
#define FURLANG_RESULT_HPP
#include <exception>
#include <optional>
#include <ostream>
#include <type_traits>
#include <utility>
namespace furlang {
@@ -43,6 +45,8 @@ public:
const char* what() const noexcept override { return "bad result access"; }
};
struct error_tag {};
/**
* @brief Result.
*
@@ -51,7 +55,7 @@ public:
* @tparam R Value type.
* @tparam E Error type.
*/
template <typename R, typename E>
template <typename E, typename R = void>
class result {
public:
using value_type = std::remove_reference_t<R>; /**< Value type. */
@@ -63,6 +67,10 @@ public:
using error_reference = error_type&; /**< Error reference type. */
using error_const_reference = const error_type&; /**< Error const reference type. */
public:
template <typename Other>
result(const result<E, Other>& error)
: result(error_tag{}, error.error()) {}
/**
* @brief Construct a new result.
*
@@ -82,7 +90,7 @@ public:
*
* @param args Variadic arguments to construct the value with.
*/
template <typename... Args>
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
result(Args&&... args) {
new (&m_value.result) value_type(std::forward<Args>(args)...);
}
@@ -92,7 +100,7 @@ public:
*
* @param error Error to copy.
*/
explicit result(const error_type& error)
result(error_tag tag, const error_type& error)
: m_error(true) {
new (&m_value.error) error_type(error);
}
@@ -102,7 +110,7 @@ public:
*
* @param error Error to move.
*/
explicit result(error_type&& error)
result(error_tag tag, error_type&& error)
: m_error(true) {
new (&m_value.error) error_type(std::move(error));
}
@@ -166,6 +174,16 @@ public:
}
return *this;
}
public:
template <typename ResultFwd, typename = std::enable_if_t<std::is_constructible_v<R, ResultFwd>>>
static result ok(ResultFwd&& value) {
return { std::forward<ResultFwd>(value) };
}
template <typename ErrorFwd, typename = std::enable_if_t<std::is_constructible_v<E, ErrorFwd>>>
static result error(ErrorFwd&& value) {
return { error_tag{}, std::forward<ErrorFwd>(value) };
}
public:
/**
* @brief Checks if this result contains a value.
@@ -364,6 +382,34 @@ private:
bool m_error = false;
};
template <typename E>
class result<E, void> {
public:
using value_type = std::remove_reference_t<E>;
using reference = value_type&;
using const_reference = const value_type&;
public:
result() = default;
result(const value_type& value)
: m_error(true), m_value(value) {}
result(value_type&& value)
: m_error(true), m_value(std::move(value)) {}
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<value_type, Args...>>>
result(Args&&... args)
: m_error(true), m_value(std::forward<Args>(args)...) {}
public:
bool has_value() const { return !m_error; }
bool has_error() const { return m_error; }
const value_type& error() const { return *m_value; }
private:
std::optional<value_type> m_value;
bool m_error = false;
};
} // namespace furlang
#endif // FURLANG_RESULT_HPP
#endif // FURLANG_RESULT_HPP
+58
View File
@@ -0,0 +1,58 @@
#ifndef FURLANG_SERIALIZATION_CODEC_HPP
#define FURLANG_SERIALIZATION_CODEC_HPP
#include "furlang/result.hpp"
#include "furlang/serialization/io.hpp"
#include <type_traits>
#include <utility>
namespace furlang {
namespace serialization {
template <typename Codec, typename T>
using codec_encode_result_t = decltype(std::declval<Codec>().encode(std::declval<writer&>(), std::declval<const T&>()));
template <typename Codec, typename T>
using codec_decode_result_t = decltype(std::declval<Codec>().decode(std::declval<reader&>()));
template <typename Codec, typename T, typename = void>
struct is_codec : std::false_type {};
template <typename Codec, typename T>
struct is_codec<Codec, T, std::void_t<codec_encode_result_t<Codec, T>, codec_decode_result_t<Codec, T>>>
: std::true_type {};
template <typename Codec, typename T>
constexpr bool is_codec_v = is_codec<Codec, T>::value;
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
result<error> encode(Codec& codec, writer& writer, const T& value) {
return codec.encode(writer, value);
}
template <typename Codec, typename T, typename = std::enable_if_t<is_codec_v<Codec, T>>>
result<error, T> decode(Codec& codec, reader& reader) {
return codec.decode(reader);
}
template <typename T, typename = void>
class codec;
template <typename T>
struct codec<T, std::enable_if_t<std::is_integral_v<T>>> {
result<error> encode(writer& writer, const T& value) { return writer.write_int(value); }
result<error, T> decode(reader& reader) { return reader.read_int(T{}); }
};
template <>
struct codec<std::string> {
result<error> encode(writer& writer, const std::string& value) { return writer.write_string(value); }
result<error, std::string> decode(reader& reader) { return reader.read_string(); }
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_CODEC_HPP
+33
View File
@@ -0,0 +1,33 @@
#ifndef FURLANG_SERIALIZATION_ERROR_HPP
#define FURLANG_SERIALIZATION_ERROR_HPP
#include <cstddef>
#include <string>
namespace furlang {
namespace serialization {
enum class error_code {
EndOfFile,
InvalidData,
InvalidTag,
InvalidVersion,
IntegerOverflow,
SizeLimit,
DuplicateId,
UnknownId,
TypeMismatch,
Unsupported,
};
struct error {
error_code code;
std::string message;
std::size_t offset = 0;
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_ERROR_HPP
+190
View File
@@ -0,0 +1,190 @@
#ifndef FURLANG_SERIALIZATION_IO_HPP
#define FURLANG_SERIALIZATION_IO_HPP
#include "furlang/result.hpp"
#include "furlang/serialization/error.hpp"
#include <cstdint>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>
namespace furlang {
namespace serialization {
class writer {
public:
writer() = default;
virtual ~writer() = default;
writer(writer&&) noexcept = default;
writer& operator=(writer&&) noexcept = default;
writer(const writer&) = default;
writer& operator=(const writer&) = default;
public:
virtual result<error> write_s8(std::int8_t value) = 0;
virtual result<error> write_u8(std::uint8_t value) = 0;
virtual result<error> write_s16(std::int16_t value) = 0;
virtual result<error> write_u16(std::uint16_t value) = 0;
virtual result<error> write_s32(std::int32_t value) = 0;
virtual result<error> write_u32(std::uint32_t value) = 0;
virtual result<error> write_s64(std::int64_t value) = 0;
virtual result<error> write_u64(std::uint64_t value) = 0;
result<error> write_int(std::int8_t value) { return write_s8(value); }
result<error> write_int(std::uint8_t value) { return write_u8(value); }
result<error> write_int(std::int16_t value) { return write_s16(value); }
result<error> write_int(std::uint16_t value) { return write_u16(value); }
result<error> write_int(std::int32_t value) { return write_s32(value); }
result<error> write_int(std::uint32_t value) { return write_u32(value); }
result<error> write_int(std::int64_t value) { return write_s64(value); }
result<error> write_int(std::uint64_t value) { return write_u64(value); }
virtual result<error> write_string(const char* string) = 0;
virtual result<error> write_string(std::string_view string) = 0;
virtual result<error> write_string(const std::string& string) = 0;
};
class reader {
public:
reader() = default;
virtual ~reader() = default;
reader(reader&&) noexcept = default;
reader& operator=(reader&&) noexcept = default;
reader(const reader&) = default;
reader& operator=(const reader&) = default;
public:
virtual result<error, std::int8_t> read_s8() = 0;
virtual result<error, std::uint8_t> read_u8() = 0;
virtual result<error, std::int16_t> read_s16() = 0;
virtual result<error, std::uint16_t> read_u16() = 0;
virtual result<error, std::int32_t> read_s32() = 0;
virtual result<error, std::uint32_t> read_u32() = 0;
virtual result<error, std::int64_t> read_s64() = 0;
virtual result<error, std::uint64_t> read_u64() = 0;
result<error, std::int8_t> read_int(std::int8_t) { return read_s8(); }
result<error, std::uint8_t> read_int(std::uint8_t) { return read_u8(); }
result<error, std::int16_t> read_int(std::int16_t) { return read_s16(); }
result<error, std::uint16_t> read_int(std::uint16_t) { return read_u16(); }
result<error, std::int32_t> read_int(std::int32_t) { return read_s32(); }
result<error, std::uint32_t> read_int(std::uint32_t) { return read_u32(); }
result<error, std::int64_t> read_int(std::int64_t) { return read_s64(); }
result<error, std::uint64_t> read_int(std::uint64_t) { return read_u64(); }
virtual result<error, std::string> read_string() = 0;
virtual std::size_t offset() const = 0;
};
enum class endianness {
Little = 0,
Big = 1,
};
class byte_writer : public writer {
public:
byte_writer(endianness endianness = endianness::Big)
: m_endianness(endianness) {}
public:
result<error> write_s8(std::int8_t value) override;
result<error> write_u8(std::uint8_t value) override;
result<error> write_s16(std::int16_t value) override;
result<error> write_u16(std::uint16_t value) override;
result<error> write_s32(std::int32_t value) override;
result<error> write_u32(std::uint32_t value) override;
result<error> write_s64(std::int64_t value) override;
result<error> write_u64(std::uint64_t value) override;
result<error> write_string(const char* string) override;
result<error> write_string(std::string_view string) override;
result<error> write_string(const std::string& string) override;
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error> write_integral_le(T value) {
return write_integral_le(value, std::make_index_sequence<sizeof(T)>{});
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
result<error> write_integral_le(T value, std::index_sequence<I...>) {
auto usig = static_cast<std::make_unsigned_t<T>>(value);
(m_bytes.push_back(usig >> (I * 8)), ...);
return {};
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
void write_integral_be(T value) {
write_integral_be(value, std::make_index_sequence<sizeof(T)>{});
}
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>, std::size_t... I>
void write_integral_be(T value, std::index_sequence<I...>) {
auto usig = static_cast<std::make_unsigned_t<T>>(value);
(m_bytes.push_back(usig >> ((sizeof(T) - 1 - I) * 8)), ...);
}
private:
endianness m_endianness;
std::vector<std::uint8_t> m_bytes;
};
class byte_reader : public reader {
public:
byte_reader(const std::uint8_t* bytes, std::size_t length, endianness endianness = endianness::Big)
: m_endianness(endianness), m_bytes(bytes), m_length(length) {}
public:
result<error, std::int8_t> read_s8() override;
result<error, std::uint8_t> read_u8() override;
result<error, std::int16_t> read_s16() override;
result<error, std::uint16_t> read_u16() override;
result<error, std::int32_t> read_s32() override;
result<error, std::uint32_t> read_u32() override;
result<error, std::int64_t> read_s64() override;
result<error, std::uint64_t> read_u64() override;
result<error, std::string> read_string() override;
std::size_t offset() const override;
private:
result<error, std::uint8_t> read_byte() {
if (m_offset >= m_length)
return result<error, std::uint8_t>::error(error{ error_code::EndOfFile, "", m_offset });
return { m_bytes[m_offset++] };
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error, T> read_integral_le() {
using U = std::make_unsigned_t<T>;
U usig = 0;
for (std::size_t i = 0; i < sizeof(T); ++i) {
auto res = read_byte();
if (res.has_error()) return res;
usig |= static_cast<U>(res.value()) << (i * 8);
}
return { static_cast<T>(usig) };
}
private:
template <typename T, typename = std::enable_if_t<std::is_integral_v<T>>>
result<error, T> read_integral_be() {
using U = std::make_unsigned_t<T>;
U usig = 0;
for (std::size_t i = 0; i < sizeof(T); ++i) {
auto res = read_byte();
if (res.has_error()) return res;
usig |= static_cast<U>(res.value()) << ((sizeof(T) - 1 - i) * 8);
}
return { static_cast<T>(usig) };
}
private:
endianness m_endianness;
const std::uint8_t* m_bytes;
std::size_t m_length;
std::size_t m_offset = 0;
};
} // namespace serialization
} // namespace furlang
#endif // FURLANG_SERIALIZATION_IO_HPP
+47
View File
@@ -0,0 +1,47 @@
#ifndef FURLANG_UTILITY_HASH_HPP
#define FURLANG_UTILITY_HASH_HPP
#include <cstddef>
#include <functional>
namespace furlang {
namespace utility {
// Source - https://stackoverflow.com/a/27952689
// Posted by Yakk - Adam Nevraumont, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 4.0
static inline std::size_t hash_combine(std::size_t lhs, std::size_t rhs) {
if constexpr (sizeof(std::size_t) >= 8) {
lhs ^= rhs + 0x517cc1b727220a95 + (lhs << 6) + (lhs >> 2);
} else {
lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
}
return lhs;
}
// Source - https://stackoverflow.com/a/20602159
// Posted by Casey, modified by community. See post 'Timeline' for change history
// Retrieved 2026-07-07, License - CC BY-SA 3.0
template <typename T, typename U, typename FirstHash = std::hash<T>, typename SecondHash = std::hash<U>>
struct pair_hash {
std::size_t operator()(const std::pair<T, U>& pair) const {
return hash_combine(FirstHash()(pair.first), SecondHash()(pair.second));
}
};
template <typename T, typename Hash = std::hash<T>>
struct vector_hash {
std::size_t operator()(const std::vector<T>& vec) const {
std::size_t seed = 0;
for (const auto& element : vec) {
hash_combine(seed, Hash()(element));
}
return seed;
}
};
} // namespace utility
} // namespace furlang
#endif // FURLANG_UTILITY_HASH_HPP
+38
View File
@@ -0,0 +1,38 @@
#ifndef FURLANG_VIEW_HPP
#define FURLANG_VIEW_HPP
#include <algorithm>
#include <cstddef>
#include <limits>
#include <stdexcept>
namespace furlang {
template <typename T>
class view {
public:
constexpr view() noexcept = default;
constexpr view(const T* data, std::size_t size) noexcept
: m_data(data), m_size(size) {}
public:
constexpr view subview(std::size_t offset, std::size_t count = std::numeric_limits<std::size_t>::max()) {
if (count > 0 && offset >= m_size) throw std::runtime_error("offset too large");
return { m_data + offset, std::min(m_size - offset, count) };
}
const T& operator[](std::size_t offset) const {
if (offset >= m_size) throw std::runtime_error("out of bounds");
return m_data[offset];
}
constexpr const T* data() const { return m_data; }
constexpr std::size_t size() const { return m_size; }
private:
const T* m_data = nullptr;
std::size_t m_size = 0;
};
} // namespace furlang
#endif // FURLANG_VIEW_HPP
+31
View File
@@ -0,0 +1,31 @@
#ifndef FURVM_CONSTANT_HPP
#define FURVM_CONSTANT_HPP
#include "furvm/fwd.hpp"
#include <cstdint>
#include <string_view>
namespace furvm {
// TODO: Array constants
struct constant {
enum type_e {
S32 = 0,
U32,
S64,
U64,
String,
} type = S32;
union {
std::int32_t s32;
std::uint32_t u32;
std::int64_t s64;
std::uint64_t u64;
std::string_view string;
};
};
} // namespace furvm
#endif // FURVM_CONSTANT_HPP
+90
View File
@@ -0,0 +1,90 @@
#ifndef FURVM_CONTEXT_HPP
#define FURVM_CONTEXT_HPP
#include "furvm/executor.hpp"
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/thing.hpp" // IWYU pragma: keep
#include <cstddef>
#include <utility>
#include <vector>
namespace furvm {
class context : public handle_container<mod_h> {
public:
friend class executor;
public:
/**
* @brief Constructs a context.
*/
context() {}
~context() = default;
/**
* @brief Move constructor.
*/
context(context&&) noexcept = default;
/**
* @brief Move constructor.
*/
context& operator=(context&&) noexcept = default;
context(const context&) = delete;
context& operator=(const context&) = delete;
public:
template <typename... Args>
auto& allocate_executor() {
executor executor(this);
return m_executors.emplace_back(std::move(executor));
}
/**
* @brief Returns an executor from the context.
*
* @param args Id of the executor.
* @return A handle to the executor.
*/
template <typename... Args>
auto& executor_at(Args&&... args) {
return m_executors.at(std::forward<Args>(args)...);
}
/**
* @brief Returns an executor from the context.
*
* @param args Id of the executor.
* @return A handle to the executor.
*/
template <typename... Args>
const auto& executor_at(Args&&... args) const {
return m_executors.at(std::forward<Args>(args)...);
}
const std::vector<executor>& executors() const { return m_executors; }
public:
thing_type_store& tt_store() { return m_thingTypeStore; }
public:
template <typename... Args>
thing<> allocate_thing(Args&&... args) {
thing<> thing = { std::forward<Args>(args)... };
m_heap.push_back(thing.raw());
return std::move(thing);
}
private:
handle_container<mod_h> m_modules;
std::vector<executor> m_executors;
class thing_type_store m_thingTypeStore;
// A list of things on the heap
std::vector<std::byte*> m_heap;
};
} // namespace furvm
#endif // FURVM_CONTEXT_HPP
+39
View File
@@ -0,0 +1,39 @@
#ifndef FURVM_DETAIL_HANDLE_HPP
#define FURVM_DETAIL_HANDLE_HPP
#include <functional>
#include <type_traits>
namespace furvm {
namespace detail {
/**
* @brief Default specialization for header_has_refcount type trait.
*/
template <typename Header, typename = void>
struct header_has_refcount : std::false_type {};
/**
* @brief Specialization for header_has_refcount type trait.
*/
template <typename Header>
struct header_has_refcount<Header,
std::void_t<decltype(std::declval<Header&>().acquire()),
decltype(std::declval<Header&>().release()),
decltype(std::declval<Header&>().reference_count())>> : std::true_type {};
/**
* @brief An alias for header_has_refcount's value.
*/
template <typename Header>
static constexpr auto header_has_refcount_v = header_has_refcount<Header>::value;
template <typename Handle, typename IdHash = std::hash<typename Handle::id_type>>
struct handle_hash {
std::size_t operator()(const Handle& handle) const { return IdHash{}(handle.id()); }
};
} // namespace detail
} // namespace furvm
#endif // FURVM_DETAIL_HANDLE_HPP
+176
View File
@@ -0,0 +1,176 @@
#ifndef FURVM_DETAIL_SERIALIZATION_HPP
#define FURVM_DETAIL_SERIALIZATION_HPP
#include <cstdint>
#include <ostream>
#include <string>
namespace furvm {
namespace detail {
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int8_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int16_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int32_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::int64_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint8_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint16_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint32_t value);
/**
* @brief Serializes an integer.
*
* @param os Output stream.
* @param value Integer.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, std::uint64_t value);
/**
* @brief Serializes a string.
*
* @param os Output stream.
* @param value String.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os, const std::string& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::int64_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint8_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint16_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint32_t& value);
/**
* @brief Deserializes an integer.
*
* @param is Input stream.
* @param value Integer.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::uint64_t& value);
/**
* @brief Deserializes a string.
*
* @param is Input stream.
* @param value String.
* @return The input stream.
*/
std::istream& load(std::istream& is, std::string& value);
} // namespace detail
} // namespace furvm
#endif // FURVM_DETAIL_SERIALIZATION_HPP
+112
View File
@@ -0,0 +1,112 @@
#ifndef FURVM_EXCEPTIONS_HPP
#define FURVM_EXCEPTIONS_HPP
#include <exception>
namespace furvm {
class bad_thing_access : public std::exception {
public:
bad_thing_access() = default;
~bad_thing_access() override = default;
/**
* @brief Move constructor.
*/
bad_thing_access(bad_thing_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_thing_access& operator=(bad_thing_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_thing_access(const bad_thing_access&) = default;
/**
* @brief Copy constructor.
*/
bad_thing_access& operator=(const bad_thing_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad thing access"; }
};
/**
* @brief Bad constant access exception.
*/
class bad_constant_access : public std::exception {
public:
bad_constant_access() = default;
~bad_constant_access() override = default;
/**
* @brief Move constructor.
*/
bad_constant_access(bad_constant_access&&) noexcept = default;
/**
* @brief Move constructor.
*/
bad_constant_access& operator=(bad_constant_access&&) noexcept = default;
/**
* @brief Copy constructor.
*/
bad_constant_access(const bad_constant_access&) = default;
/**
* @brief Copy constructor.
*/
bad_constant_access& operator=(const bad_constant_access&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "bad constant access"; }
};
class stack_underflow : public std::exception {
public:
stack_underflow() = default;
~stack_underflow() override = default;
/**
* @brief Move constructor.
*/
stack_underflow(stack_underflow&&) noexcept = default;
/**
* @brief Move constructor.
*/
stack_underflow& operator=(stack_underflow&&) noexcept = default;
/**
* @brief Copy constructor.
*/
stack_underflow(const stack_underflow&) = default;
/**
* @brief Copy constructor.
*/
stack_underflow& operator=(const stack_underflow&) = default;
public:
/**
* @brief Returns a C-style string describing the cause of the error.
*
* @return The cause of the error.
*/
const char* what() const noexcept override { return "stack underflow"; }
};
} // namespace furvm
#endif // FURVM_EXCEPTIONS_HPP
+208
View File
@@ -0,0 +1,208 @@
#ifndef FURVM_EXECUTOR_HPP
#define FURVM_EXECUTOR_HPP
#include "furvm/fwd.hpp"
#include "furvm/module.hpp" // IWYU pragma: keep
#include "furvm/stack.hpp"
#include "furvm/thing.hpp" // IWYU pragma: keep
#include <functional>
#include <stack>
#include <vector>
namespace furvm {
enum class executor_flags : std::uint32_t {
Suspended = (1 << 0), /**< Execution suspended. */
Done = (1 << 1), /**< Execution is finished. */
JustHit = (1 << 16), /**< Executor just hit a breakpoint. */
};
static inline executor_flags operator|(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) | static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator&(executor_flags lhs, executor_flags rhs) {
return executor_flags(static_cast<std::uint32_t>(lhs) & static_cast<std::uint32_t>(rhs));
}
static inline executor_flags operator~(executor_flags flags) {
return executor_flags(~static_cast<std::uint32_t>(flags));
}
class executor {
friend class context;
private:
executor(context* context)
: m_context(context) {}
public:
static constexpr executor_flags STATE_FLAGS = executor_flags::JustHit;
using new_frame_callback = std::function<void(executor&)>;
using stack_thing = thing<stack_allocator>;
public:
/**
* @brief Executor frame.
*
* Call frame.
*/
struct frame {
mod_h mod; /**< Handle to the frame's module. */
std::size_t position; /**< Cursor to a current instruction in the bytecode. */
std::size_t stackBase; /**< Snapshot of the stack size before this frame. */
thing_type* returnType; /**< Return type. */
std::vector<stack_thing> variables; /**< Frame variables. */
};
public:
~executor() = default;
/**
* @brief Move constructor.
*/
executor(executor&&) noexcept = default;
/**
* @brief Move constructor.
*/
executor& operator=(executor&&) noexcept = default;
/**
* @brief Copy constructor.
*/
executor(const executor&) = default;
/**
* @brief Copy constructor.
*/
executor& operator=(const executor&) = default;
public:
template <typename CallbackFwd>
void set_new_frame_callback(CallbackFwd&& callback) {
m_newFrameCb = std::forward<CallbackFwd>(callback);
}
public:
/**
* @brief Returns flags of this executor.
*
* @return The flags.
*/
executor_flags flags() const { return m_flags; }
bool done() const { return (m_flags & executor_flags::Done) == executor_flags::Done; }
bool suspended() const { return (m_flags & executor_flags::Suspended) == executor_flags::Suspended; }
void unsuspend() { m_flags = m_flags & ~executor_flags::Suspended; }
void clear_flags() {
m_flags = m_flags & STATE_FLAGS;
m_flags = m_frames.empty() ? executor_flags::Done : furvm::executor_flags{ 0 };
}
public:
/**
* @brief Pushes a new frame.
*
* @param mod Handle to the frame's module.
* @param function Frame's function.
*/
void push_frame(const mod_h& mod, function function);
/**
* @brief Pops the top frame.
*
* @return The popped frame.
*/
frame pop_frame();
/**
* @brief Returns the top frame.
*
* @return The frame.
*/
frame top_frame() const;
const std::stack<frame>& frames() const { return m_frames; }
public:
/**
* @brief Pushes a thing onto the stack.
*
* Registers a new thing and pushes its handle onto the stack.
*
* @param thing Thing.
* @return The pushed handle.
*/
stack_thing& push_thing(stack_thing&& thing);
stack_thing& push_thing(const stack_thing& thing);
/**
* @brief Pops a thing from the stack.
*
* @return A handle to the popped thing.
*/
stack_thing pop_thing();
/**
* @brief Returns the top thing on the stack.
*
* @return A handle to the top thing.
*/
stack_thing& top_thing();
const stack_thing& top_thing() const;
const std::vector<stack_thing>& stack() const { return m_stack; }
public:
/**
* @brief Stores a thing in a frame variable.
*
* @param variable Id of the variable in which the handle will be put.
* @param thing Thing handle.
*/
void store_thing(variable_t variable, const stack_thing& thing);
/**
* @brief Stores a thing in a frame variable.
*
* @param variable Id of the variable in which the handle will be put.
* @param thing Thing handle.
*/
void store_thing(variable_t variable, stack_thing&& thing);
/**
* @brief Returns a thing stored in a variable.
*
* @param variable Id of the variable from which the handle will be fetched.
* @return A handle stored in the variable.
*/
stack_thing& load_thing(variable_t variable);
const stack_thing& load_thing(variable_t variable) const;
public:
/**
* @brief Executes next instruction.
*/
void step();
private:
thing_type thing_type_impl(mod_h mod, mod_type type) const;
thing_type* mod_to_thing_type(const mod_h& mod, const mod_type& type) const;
private:
static bool compare_thing_types(const thing_type& lhs, const thing_type& rhs);
private:
executor_flags m_flags = executor_flags::Done;
context* m_context;
furvm::stack<std::byte> m_stackStorage;
std::stack<frame> m_frames;
std::vector<stack_thing> m_stack;
new_frame_callback m_newFrameCb = nullptr;
};
} // namespace furvm
#endif // FURVM_EXECUTOR_HPP
+201
View File
@@ -0,0 +1,201 @@
#ifndef FURVM_FUNCTION_HPP
#define FURVM_FUNCTION_HPP
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp" // IWYU pragma: keep
#include <cstdint>
#include <optional>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace furvm {
enum class function_t : std::uint8_t {
Normal = 0, /**< A normal bytecode function. */
Native, /**< A native function implemented through furvm API. */
Import, /**< A function imported from another module. */
};
/**
* @brief A native function.
*/
using native_function = std::string;
/**
* @brief A function import.
*/
struct import_function {
mod_id mod;
function_id function;
};
/**
* @brief Function signature.
*/
struct function_sig {
std::vector<mod_type_h> params;
std::optional<mod_type_h> returnType;
bool operator==(const function_sig& rhs) const { return params == rhs.params; }
bool operator!=(const function_sig& rhs) const { return !this->operator==(rhs); }
};
class function {
public:
/**
* @brief Constructs a normal function.
*
* @param signature Function's signature.
* @param position Offset in bytecode of the function.
*/
template <typename SigFwd, typename = std::enable_if_t<std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, bytecode_pos position)
: m_type(function_t::Normal), m_signature(std::forward<SigFwd>(signature)), m_value(position) {}
/**
* @brief Constructs a native function.
*
* @param signature Function's signature.
* @param native Native function tag.
*/
template <typename SigFwd,
typename Native,
typename = std::enable_if_t<std::is_constructible_v<native_function, Native> &&
std::is_constructible_v<function_sig, SigFwd>>>
function(SigFwd&& signature, Native&& native)
: m_type(function_t::Native),
m_signature(std::forward<SigFwd>(signature)),
m_value(std::forward<Native>(native)) {}
/**
* @brief Constructs an import function.
*
* @param mod Module's id.
* @param function Function's id.
*/
template <typename ModFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModFwd>>>
function(ModFwd&& mod, function_id function)
: m_type(function_t::Import), m_signature(), m_value(import_function{ std::forward<ModFwd>(mod), function }) {}
/**
* @brief Constructs an import function.
*
* @param mod Module.
* @param function Function.
*/
function(const mod_h& mod, const function_h& function);
/**
* @brief Destructs a function.
*/
~function();
/**
* @brief Move constructor.
*/
function(function&&) noexcept;
/**
* @brief Move constructor.
*/
function& operator=(function&&) noexcept;
/**
* @brief Copy constructor.
*/
function(const function&);
/**
* @brief Copy constructor.
*/
function& operator=(const function&);
public:
/**
* @brief Returns a type of this function.
*
* @return The type.
*/
constexpr function_t type() const { return m_type; }
/**
* @brief Returns this function's signature.
*
* @return The signature.
*/
function_sig signature() const { return m_signature; }
public:
/**
* @brief Returns normal function's value.
*
* @return The value.
*/
std::size_t position() const {
if (m_type != function_t::Normal) throw std::runtime_error("function type mismatch");
return m_value.position;
}
/**
* @brief Returns native function's value.
*
* @return The value.
*/
const native_function& native() const {
if (m_type != function_t::Native) throw std::runtime_error("function type mismatch");
return m_value.native;
}
/**
* @brief Returns import function's value.
*
* @return The value.
*/
const import_function& imp() const {
if (m_type != function_t::Import) throw std::runtime_error("function type mismatch");
return m_value.imp;
}
private:
function_t m_type;
function_sig m_signature;
union value {
std::size_t position = 0;
native_function native;
import_function imp;
value() = default;
value(std::size_t position)
: position(position) {}
template <typename Native, typename = std::enable_if_t<std::is_constructible_v<native_function, Native>>>
value(Native&& native)
: native(std::forward<Native>(native)) {}
value(const import_function& imp)
: imp(imp) {}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} m_value;
};
namespace detail {
struct function_sig_hash {
std::size_t operator()(const function_sig& signature) const;
};
} // namespace detail
} // namespace furvm
#endif // FURVM_FUNCTION_HPP
+12
View File
@@ -0,0 +1,12 @@
#ifndef FURVM_HPP
#define FURVM_HPP
#include "furvm/context.hpp" // IWYU pragma: export
#include "furvm/executor.hpp" // IWYU pragma: export
#include "furvm/function.hpp" // IWYU pragma: export
#include "furvm/fwd.hpp" // IWYU pragma: export
#include "furvm/handle.hpp" // IWYU pragma: export
#include "furvm/instruction.hpp" // IWYU pragma: export
#include "furvm/thing.hpp" // IWYU pragma: export
#endif // FURVM_HPP
@@ -4,6 +4,7 @@
#include <cstddef> // IWYU pragma: export
#include <cstdint> // IWYU pragma: export
#include <memory>
#include <string>
/**
* @brief Furlang's virtual machine.
@@ -17,6 +18,40 @@ namespace furvm {
*/
using byte = std::uint8_t;
/**
* @brief An offset into bytecode.
*/
using bytecode_pos = std::uint64_t;
/**
* @brief Handle header with reference count.
*/
template <typename Id>
class refcount_header;
/**
* @brief Generic handle header.
*/
template <typename Id>
class generic_header;
/**
* @brief Generic furvm object handle.
*
* @tparam Value Type of the handle's value.
* @tparam Header Type of the handle's header.
*/
template <typename Value, typename Header>
class handle;
/**
* @brief Container for the handles.
*
* @tparam Handle Type of the container's handle.
*/
template <typename Handle, typename = void>
class handle_container;
// constant.hpp
/**
@@ -40,11 +75,7 @@ class constant;
// instruction.hpp
/**
* @enum instruction_t
* @brief Furvm's instruction type.
*/
enum class instruction_t : byte;
struct instruction_argument;
/**
* @struct instruction
@@ -68,18 +99,24 @@ enum class function_t : std::uint8_t;
*/
class function;
/**
* @brief An alias to a function shared pointer.
*/
using function_p = std::shared_ptr<function>;
/**
* @brief Furvm function's index.
*/
using function_handle = std::uint16_t;
using function_id = std::uint16_t;
/**
* @brief A handle to a furvm function.
*/
using function_h = handle<function, refcount_header<function_id>>;
// module.hpp
struct mod_type;
using mod_type_id = std::uint32_t;
using mod_type_h = handle<mod_type, generic_header<mod_type_id>>;
/**
* @class mod
* @brief Module.
@@ -94,17 +131,16 @@ class mod;
using mod_p = std::shared_ptr<mod>;
/**
* @brief Furvm module's index.
* @brief An alias for a module's identifier.
*/
using module_handle = std::uint32_t;
// thing.hpp
using mod_id = std::string;
/**
* @enum thing_t
* @brief Thing type.
* @brief A handle to a furvm module.
*/
enum class thing_t : std::uint8_t;
using mod_h = handle<mod, refcount_header<mod_id>>;
// thing.hpp
/**
* @class bad_thing_access
@@ -112,26 +148,29 @@ enum class thing_t : std::uint8_t;
*/
class bad_thing_access;
using thing_type_id = std::uint32_t;
/**
* @class thing
* @brief Furvm thing.
*
* A stack element. Think of it like of a value in C++ or I guess a class in java.
*/
template <template <typename> typename Allocator = std::allocator>
class thing;
/**
* @brief An alias to a thing shared pointer.
*/
using thing_p = std::shared_ptr<thing>;
/**
* @brief Furvm thing's index.
*/
using thing_handle = std::uint32_t;
using thing_id = std::uint32_t;
// executor.hpp
/**
* @brief A variable index type.
*/
using variable_t = std::uint16_t;
/**
* @enum executor_flags
* @brief Flags of an executor.
@@ -146,15 +185,10 @@ enum class executor_flags : std::uint32_t;
*/
class executor;
/**
* @brief An alias to a executor shared pointer.
*/
using executor_p = std::shared_ptr<executor>;
/**
* @brief Furvm executor's index.
*/
using executor_handle = std::uint32_t;
using executor_id = std::uint32_t;
// context.hpp
@@ -181,4 +215,4 @@ class stack_underflow;
} // namespace furvm
#endif // FURVM_FWD_HPP
#endif // FURVM_FWD_HPP
+448
View File
@@ -0,0 +1,448 @@
#ifndef FURVM_HANDLE_HPP
#define FURVM_HANDLE_HPP
#include "furvm/detail/handle.hpp"
#include "furvm/fwd.hpp"
#include <atomic>
#include <functional>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furvm {
// TODO: Implement generational indexes
template <typename Id>
class refcount_header {
public:
using id_type = Id; /**< Id type. */
using refcount_type = std::uint32_t; /**< Reference count type. */
public:
/**
* @brief Constructs a reference counting header.
*
* @param id Identifier of the handle's value.
* @param refCount Handle's reference count.
* @param onRelease Callback function.
*/
template <typename IdFwd, typename Func>
refcount_header(IdFwd&& id, refcount_type refCount, Func&& onRelease)
: m_id(std::forward<IdFwd>(id)), m_refCount(refCount), m_onRelease(std::forward<Func>(onRelease)) {}
public:
/**
* @brief Returns the header's reference count.
*
* @return The reference count.
*/
refcount_type reference_count() const { return m_refCount; }
/**
* @brief Increments the header's reference count.
*/
void acquire() { ++m_refCount; }
/**
* @brief Decrements the header's reference count.
*
* If the reference count reaches 0, the onRelease callback passed in the constructor will be called.
*/
void release() {
--m_refCount;
if (m_refCount == 0) m_onRelease(m_id);
}
public:
/**
* @brief Returns the header's identifier.
*
* @return The identifier.
*/
id_type id() const { return m_id; }
private:
id_type m_id;
std::atomic<refcount_type> m_refCount;
std::function<void(const id_type&)> m_onRelease;
};
template <typename Id>
class generic_header {
public:
using id_type = Id; /**< Id type. */
public:
/**
* @brief Constructs a generic header.
*
* @param id Identifier of the handle.
*/
generic_header(id_type id)
: m_id(id) {}
public:
/**
* @brief Returns the header's identifier.
*/
id_type id() const { return m_id; }
private:
id_type m_id;
};
template <typename Value, typename Header = refcount_header<std::uint32_t>>
class handle {
public:
using value_type = Value; /** Value type. */
using reference = Value&; /** Reference type. */
using const_reference = const Value&; /** Constant reference type. */
using pointer = Value*; /** Pointer type. */
using const_pointer = const Value*; /** Constant pointer type. */
public:
using id_type = typename Header::id_type; /** Id type of the header. */
using header_type = Header;
public:
using pair_type = std::pair<Header, Value>; /** Type of a header-value pair. */
public:
handle() = default;
/**
* @brief Constructs a handle.
*
* @param value A pointer to the header-value pair.
*/
handle(pair_type* value)
: m_value(value) {
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
}
/**
* @brief Destructs a handle.
*/
~handle() {
if constexpr (detail::header_has_refcount_v<Header>) {
if (m_value != nullptr) m_value->first.release();
}
m_value = nullptr;
}
/**
* @brief Move constructor.
*/
handle(handle&& other) noexcept
: m_value(other.m_value) {
other.m_value = nullptr;
}
/**
* @brief Move constructor.
*/
handle& operator=(handle&& other) noexcept {
if (this == &other) return *this;
m_value = other.m_value;
other.m_value = nullptr;
return *this;
}
/**
* @brief Copy constructor.
*/
handle(const handle& other)
: m_value(other.m_value) {
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
}
/**
* @brief Copy constructor.
*/
handle& operator=(const handle& other) {
if (this == &other) return *this;
m_value = other.m_value;
if constexpr (detail::header_has_refcount_v<Header>) {
m_value->first.acquire();
}
return *this;
}
public:
/**
* @brief Returns an identifier of the handle's header.
*
* @return The header's identifier.
*/
id_type id() const { return m_value->first.id(); }
/**
* @brief Returns whether the handle is empty.
*
* @return true if the handle is empty.
*/
bool empty() const { return m_value == nullptr; }
/**
* @brief Returns the handle's header reference count.
*
* @return The reference count.
*/
template <typename U = Header, typename = std::enable_if_t<detail::header_has_refcount_v<U>>>
auto reference_count() const {
return m_value->first.reference_count();
}
/**
* @brief Returns a pointer to the handle's value.
*
* @return The value pointer.
*/
pointer operator->() { return &m_value->second; }
/**
* @brief Returns a pointer to the handle's value.
*
* @return The value pointer.
*/
const_pointer operator->() const { return &m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
reference operator*() { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
const_reference operator*() const { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
reference value() { return m_value->second; }
/**
* @brief Returns a reference to the handle's value.
*
* @return The value reference.
*/
const_reference value() const { return m_value->second; }
public:
/**
* @brief Invalidates the handle without releasing.
*/
void dispatch() { m_value = nullptr; }
public:
bool operator==(const handle& rhs) const { return m_value == rhs.m_value; }
bool operator!=(const handle& rhs) const { return !this->operator==(rhs); }
private:
pair_type* m_value = nullptr;
};
template <typename Handle>
class handle_container<Handle, std::enable_if_t<!std::is_integral_v<typename Handle::id_type>>> {
private:
using pair_type = typename Handle::pair_type; /**< Handle's pair type. */
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
using id_type = typename Handle::id_type; /**< Handle's header identifier type. */
public:
handle_container() = default;
~handle_container() = default;
handle_container(handle_container&&) noexcept = default;
handle_container& operator=(handle_container&&) noexcept = default;
handle_container(const handle_container&) = delete;
handle_container& operator=(const handle_container&) = delete;
public:
/**
* @brief Emplaces a new value.
*
* @param id Identifier of the emplaced value.
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename IdFwd,
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
value_type emplace(IdFwd&& id, Args&&... args) {
id_type idFwd = std::forward<IdFwd>(id);
if (auto it = m_pairs.find(idFwd); it != m_pairs.end()) delete it->second;
auto pair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(idFwd, 0, [&](const id_type& id) { erase(id); }),
std::forward_as_tuple(std::forward<Args>(args)...));
m_pairs.emplace(std::move(idFwd), pair);
return { pair };
}
/**
* @brief Returns a handle to the container's value.
*
* @param id Idenfifier of the value.
* @return The value.
*/
template <typename IdFwd>
value_type at(IdFwd&& id) {
return { m_pairs.at(std::forward<IdFwd>(id)) };
}
/**
* @brief Returns a handle to the container's value.
*
* @param id Idenfifier of the value.
* @return The value.
*/
template <typename IdFwd>
const_value at(IdFwd&& id) const {
return { m_pairs.at(std::forward<IdFwd>(id)) };
}
/**
* @brief Erases a value from the container.
*
* @param id Identifier of the value.
*/
template <typename IdFwd>
void erase(IdFwd&& id) {
auto it = m_pairs.find(std::forward<IdFwd>(id));
if (it == m_pairs.end()) return;
delete it->second;
m_pairs.erase(it);
}
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
template <typename IdFwd>
constexpr bool contains(IdFwd&& id) const {
return m_pairs.find(std::forward<IdFwd>(id)) != m_pairs.end();
}
private:
std::unordered_map<id_type, pair_type*> m_pairs;
};
template <typename Handle>
class handle_container<Handle, std::enable_if_t<std::is_integral_v<typename Handle::id_type>>> {
private:
using pair_type = typename Handle::pair_type; /**< Handle's pair type. */
public:
using value_type = std::remove_cv_t<std::remove_reference_t<Handle>>; /**< Handle type. */
using const_value = std::add_const_t<value_type>; /**< Constant handle type. */
using id_type = typename Handle::id_type; /**< Handle's header identifier type. */
public:
handle_container() = default;
~handle_container() = default;
handle_container(handle_container&&) noexcept = default;
handle_container& operator=(handle_container&&) noexcept = default;
handle_container(const handle_container&) = delete;
handle_container& operator=(const handle_container&) = delete;
public:
/**
* @brief Emplaces a new value.
*
* @param id Identifier of the emplaced value.
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename pair_type::second_type, Args...>>>
value_type emplace(id_type id, Args&&... args) {
if (id >= m_pairs.size()) {
m_pairs.resize(id + 1, nullptr);
} else if (m_pairs[id] != nullptr) {
delete m_pairs[id];
}
pair_type* newPair = nullptr;
if constexpr (detail::header_has_refcount_v<typename Handle::header_type>) {
newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id, 0, [&](const id_type& id) { erase(id); }),
std::forward_as_tuple(std::forward<Args>(args)...));
} else {
newPair = new pair_type(std::piecewise_construct,
std::forward_as_tuple(id),
std::forward_as_tuple(std::forward<Args>(args)...));
}
m_pairs[id] = newPair;
return { newPair };
}
/**
* @brief Emplaces a new value.
*
* Emplaces a new value with an automatically-assigned identifier.
*
* @param args Arguments passed to the Handle's value type constructor.
* @return A handle to the emplaced value.
*/
template <typename... Args,
typename = std::enable_if_t<std::is_constructible_v<typename Handle::pair_type::second_type, Args...>>>
value_type emplace_back(Args&&... args) {
return emplace(static_cast<id_type>(m_pairs.size()), std::forward<Args>(args)...);
}
/**
* @brief Returns a handle to a value.
*
* @param id Identifier of the value.
* @return The value.
*/
value_type at(id_type id) { return { m_pairs.at(id) }; }
/**
* @brief Returns a handle to a value.
*
* @param id Identifier of the value.
* @return The value.
*/
const_value at(id_type id) const { return { m_pairs.at(id) }; }
/**
* @brief Erases a value from the container.
*
* @param id Identifier of the value.
*/
void erase(id_type id) {
if (id >= m_pairs.size()) return;
delete m_pairs[id];
m_pairs[id] = nullptr;
}
/**
* @brief Checks whether a handle exists inside.
*
* @param id Identifier of the handle.
* @return true if the handle exists insdie of this container.
*/
constexpr bool contains(id_type id) const { return id < m_pairs.size() && m_pairs[id] != nullptr; }
public:
auto begin() { return m_pairs.begin(); }
auto begin() const { return m_pairs.begin(); }
auto cbegin() const { return m_pairs.cbegin(); }
auto end() { return m_pairs.end(); }
auto end() const { return m_pairs.end(); }
auto cend() const { return m_pairs.cend(); }
private:
std::vector<pair_type*> m_pairs;
};
} // namespace furvm
#endif // FURVM_HANDLE_HPP
+104
View File
@@ -0,0 +1,104 @@
#ifndef FURVM_INSTRUCTION_HPP
#define FURVM_INSTRUCTION_HPP
#include "furlang/view.hpp"
#include "furvm/fwd.hpp"
#include <cstddef>
#include <vector>
namespace furvm {
struct instruction_argument {
enum type_e {
None = 0,
S8,
U8,
S16,
U16,
S32,
U32,
Constant,
Type,
Variable,
GlobalVariable,
Function,
Offset,
Count,
} type;
union {
std::int8_t s8;
std::uint8_t u8;
std::int16_t s16;
std::uint16_t u16;
std::int32_t s32;
std::uint32_t u32;
};
static const std::size_t s_sizes[Count];
static const bool s_signedness[Count];
std::size_t size() const { return s_sizes[type]; }
bool is_signed() const { return s_signedness[type]; }
};
using instruction_argument_t = instruction_argument::type_e;
struct instruction {
enum type_e : byte {
NoOperation = 0,
PushS8,
PushU8,
PushS16,
PushU16,
PushS32,
PushU32,
PushConstant,
Array,
Slice,
Get,
Set,
Drop,
Duplicate,
Swap,
Clone,
Reference,
Add,
Sub,
Mul,
Div,
Mod,
Equals,
NotEquals,
LessThan,
GreaterThan,
LessEqual,
GreaterEqual,
Pointerof,
Sizeof,
Lengthof,
Load,
Store,
LoadGlobal,
StoreGlobal,
Call,
Jump,
JumpNotZero,
Return,
Count,
} type;
instruction_argument arg;
static const instruction_argument_t s_arguments[Count];
std::size_t read(furlang::view<std::uint8_t> in);
std::size_t write(std::vector<std::uint8_t>& out) const;
};
using instruction_t = instruction::type_e;
} // namespace furvm
#endif // FURVM_INSTRUCTION_HPP
+452
View File
@@ -0,0 +1,452 @@
#ifndef FURVM_MODULE_HPP
#define FURVM_MODULE_HPP
#include "furlang/utility/hash.hpp"
#include "furlang/view.hpp"
#include "furvm/constant.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include "furvm/handle.hpp"
#include "furvm/thing.hpp"
#include <functional>
#include <istream>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furvm {
struct mod_type {
struct array_value {
mod_type_id typeId;
std::size_t size;
};
struct slice_value {
mod_type_id typeId;
};
struct import_value {
mod_id modId;
mod_type_id typeId;
};
enum type {
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
Ptr,
Ref,
Array,
Slice,
Import,
Count,
} type;
union value {
std::nullptr_t null = nullptr;
mod_type_id typeRef;
array_value array;
slice_value slice;
import_value imprt;
value() = default;
value(mod_type_id id)
: typeRef(id) {}
value(mod_type_id id, std::size_t size)
: array({}) {
array.typeId = id;
array.size = size;
}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
value(ModIdFwd&& modId, mod_type_id typeId)
: imprt({}) {
imprt.modId = std::forward<ModIdFwd>(modId);
imprt.typeId = typeId;
}
~value() {}
value(value&& other) = delete;
value& operator=(value&& other) = delete;
value(const value& other) = delete;
value& operator=(const value& other) = delete;
} value;
mod_type(enum type type)
: type(type) {}
mod_type(enum type type, mod_type_id typeRef)
: type(type), value(typeRef) {}
mod_type(mod_type_id id, std::size_t size)
: type(Array), value(id, size) {}
template <typename ModIdFwd, typename = std::enable_if_t<std::is_constructible_v<mod_id, ModIdFwd>>>
mod_type(ModIdFwd&& modId, mod_type_id typeId)
: type(Import), value(std::forward<ModIdFwd>(modId), typeId) {}
~mod_type() {
switch (type) {
case Array: value.array.~array_value(); break;
case Slice: value.slice.~slice_value(); break;
case Import: value.imprt.~import_value(); break;
default: break;
}
}
mod_type(mod_type&& other) noexcept
: type(other.type) {
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
}
mod_type& operator=(mod_type&& other) noexcept {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(std::move(other.value.imprt)); break;
default: break;
}
other.type = Count;
return *this;
}
mod_type(const mod_type& other)
: type(other.type) {
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(other.value.imprt); break;
default: break;
}
}
mod_type& operator=(const mod_type& other) {
if (this == &other) return *this;
type = other.type;
switch (type) {
case Array: new (&value.array) array_value(other.value.array); break;
case Slice: new (&value.slice) slice_value(other.value.slice); break;
case Import: new (&value.imprt) import_value(other.value.imprt); break;
default: break;
}
return *this;
}
};
struct breakpoint {
std::function<void(executor&, void*)> callback;
void* data = nullptr;
};
class mod {
friend class function;
friend class serializer;
public:
using bytecode_t = std::vector<byte>; /**< An alias to a vector of bytes. */
static constexpr char MAGIC[4] = { 'F', 'u', 'r', 'M' }; /** Furvm module file magic. */
using native_function = std::function<void(executor&)>;
public:
/**
* @brief Constructs a module.
*
* @param name Name of the module.
* @param args Arguments forwarded to bytecode's constructor.
*/
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<bytecode_t, Args...>>>
mod(Args&&... args)
: m_bytecode(std::forward<Args>(args)...) {}
~mod() = default;
/**
* @brief Move constructor.
*/
mod(mod&&) = default;
/**
* @brief Move constructor.
*/
mod& operator=(mod&&) = default;
mod(const mod&) = delete;
mod& operator=(const mod&) = delete;
public:
/**
* @brief Returns a byte from bytecode of this module.
*
* @param offset An offset of the byte.
* @return The byte.
*/
byte byte_at(std::size_t offset) const { return m_bytecode.at(offset); }
/**
* @brief Returns the module's bytecode.
*
* @return A reference to the bytecode.
*/
constexpr bytecode_t& bytecode() { return m_bytecode; }
/**
* @brief Returns the module's bytecode.
*
* @return A constant reference to the bytecode.
*/
furlang::view<std::uint8_t> bytecode_view() const { return { m_bytecode.data(), m_bytecode.size() }; }
public:
/**
* @brief Emplaces a function in the module's function container.
*
* Emplaces the function in module's function container and name to function map and public functions map.
*
* @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function.
*/
template <typename... Args>
function_h emplace_function(Args&&... args) {
function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
return std::move(function);
}
/**
* @brief Emplaces a function in the module's function container.
*
* Emplaces the function in module's function container and name to function map.
*
* @param name Name of the function.
* @param args Arguments forwarded into the container's emplace_back function.
* @return A handle to the emplaced function.
*/
template <typename NameFwd,
typename... Args,
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd>>>
function_h emplace_function(NameFwd&& name, Args&&... args) {
function_h function;
if constexpr (std::is_constructible_v<class function, Args...>) {
function = std::move(m_functions.emplace_back(std::forward<Args>(args)...));
} else {
function = std::move(m_functions.emplace(std::forward<Args>(args)...));
}
auto pair = std::make_pair(std::forward<NameFwd>(name), function->signature());
m_functionMap[function.id()] = pair;
m_functionSigs[std::move(pair)] = function.id();
return std::move(function);
}
/**
* @brief Returns a function from the module.
*
* @param id Identifier of the function.
* @return A handle to the function.
*/
auto function_at(function_id id) { return m_functions.at(id); }
/**
* @brief Returns a function from the module.
*
* @param id Identifier of the function.
* @return A handle to the function.
*/
auto function_at(function_id id) const { return m_functions.at(id); }
/**
* @brief Returns a function from the module.
*
* @param name Name of the function.
* @return A handle to the function.
*/
template <typename NameFwd,
typename SigFwd,
typename = std::enable_if_t<std::is_constructible_v<std::string, NameFwd> &&
std::is_constructible_v<function_sig, SigFwd>>>
auto function_at(NameFwd&& name, SigFwd&& signature) {
return function_at(
m_functionSigs.at(std::make_pair<>(std::forward<NameFwd>(name), std::forward<SigFwd>(signature))));
}
/**
* @brief Erases a function from the module's function container.
*
* @param id Identifier of the function.
*/
void erase_function(function_id id) {
m_functions.erase(id);
if (auto it = m_functionMap.find(id); it != m_functionMap.end()) {
m_functionSigs.erase(it->second);
m_functionMap.erase(it);
}
}
const handle_container<function_h>& functions() const { return m_functions; }
const auto& function_map() const { return m_functionMap; }
public:
template <typename NameFwd, typename Func>
void set_native_function(NameFwd&& name, Func&& func) {
m_nativeFunctions.emplace(std::forward<NameFwd>(name), std::forward<Func>(func));
}
template <typename NameFwd>
native_function get_native_function(NameFwd&& name) const {
return m_nativeFunctions.at(std::forward<NameFwd>(name));
}
public:
/**
* @brief Emplaces a type in the context.
*
* @param args Arguments forwarded to the type constructor.
* @return The emplaced type.
*/
template <typename... Args>
auto emplace_type(Args&&... args) {
if constexpr (std::is_constructible_v<mod_type, Args...>) {
return m_types.emplace_back(std::forward<Args>(args)...);
} else {
return m_types.emplace(std::forward<Args>(args)...);
}
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Returns a type from the context.
*
* @param args type's id.
* @return A handle to the type.
*/
template <typename... Args>
auto type_at(Args&&... args) const {
return m_types.at(std::forward<Args>(args)...);
}
/**
* @brief Erases a type from the context.
*
* @param args type's id.
*/
template <typename... Args>
void erase_type(Args&&... args) {
m_types.erase(std::forward<Args>(args)...);
}
const handle_container<mod_type_h>& types() const { return m_types; }
public:
void set_global_variable_count(std::uint16_t count) {
m_globalVariables.resize(count);
m_globalVariables.shrink_to_fit();
}
std::uint16_t get_global_variable_count() const { return static_cast<std::uint16_t>(m_globalVariables.size()); }
void store_global_variable(std::uint16_t var, thing<>&& thing) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
m_globalVariables.emplace(m_globalVariables.cbegin() + var, std::move(thing));
}
void store_global_variable(std::uint16_t var, const thing<>& thing) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
m_globalVariables.emplace(m_globalVariables.cbegin() + var, thing);
}
thing<>& load_global_variable(std::uint16_t var) {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
return m_globalVariables[var];
}
const thing<>& load_global_variable(std::uint16_t var) const {
if (var >= m_globalVariables.size()) throw std::runtime_error("invalid slot");
return m_globalVariables[var];
}
public:
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<constant, Args...>>>
void emplace_constant(Args&&... args) {
m_constants.emplace_back(std::forward<Args>(args)...);
}
const constant& constant_at(constant_index index) const { return m_constants.at(index); }
public:
template <typename Fwd, typename = std::enable_if_t<std::is_constructible_v<breakpoint, Fwd>>>
void set_breakpoint(bytecode_pos pos, Fwd&& breakpoint) {
m_breakpoints[pos] = std::forward<Fwd>(breakpoint);
}
bool has_breakpoint(bytecode_pos pos) const { return m_breakpoints.find(pos) != m_breakpoints.end(); }
const breakpoint& breakpoint_at(bytecode_pos pos) const { return m_breakpoints.at(pos); }
public:
/**
* @brief Prints the module in a bytecode form to an output stream.
*
* @param os Output stream.
* @return The output stream.
*/
std::ostream& serialize(std::ostream& os) const;
/**
* @brief Loads a module in a bytecode form from an input stream.
*
* @param is Input stream.
* @return The loaded module.
*/
static mod load(std::istream& is);
private:
bytecode_t m_bytecode;
using pair_type = std::pair<std::string, function_sig>;
using pair_hash =
furlang::utility::pair_hash<std::string, function_sig, std::hash<std::string>, detail::function_sig_hash>;
std::unordered_map<pair_type, function_id, pair_hash> m_functionSigs;
std::unordered_map<function_id, pair_type> m_functionMap;
handle_container<function_h> m_functions;
handle_container<mod_type_h> m_types;
std::vector<thing<>> m_globalVariables;
std::vector<constant> m_constants;
std::unordered_map<std::string, native_function> m_nativeFunctions;
std::unordered_map<bytecode_pos, breakpoint> m_breakpoints;
};
} // namespace furvm
#endif // FURVM_MODULE_HPP
+56
View File
@@ -0,0 +1,56 @@
#ifndef FURVM_STACK_HPP
#define FURVM_STACK_HPP
#include <cstddef>
#include <new>
#include <stack>
namespace furvm {
template <typename T>
struct stack {
stack(std::size_t capacity = (1024ULL * 1024ULL) / sizeof(T))
: begin(new T[capacity]()), cursor(begin), capacity(capacity) {}
T* begin;
T* cursor;
std::size_t capacity;
std::stack<T*> frames;
void push_frame() { frames.push(cursor); }
void pop_frame() {
cursor = frames.top();
frames.pop();
}
};
template <typename T>
class stack_allocator {
public:
stack_allocator() = default;
stack_allocator(stack<T>& stack)
: m_ref(&stack) {}
template <typename U>
constexpr stack_allocator(const stack_allocator<U>& other) noexcept
: m_ref(other.m_ref) {}
public:
T* allocate(std::size_t n) {
if (m_ref == nullptr) throw std::bad_alloc();
if (m_ref->capacity - (m_ref->cursor - m_ref->begin) < n) throw std::bad_alloc();
T* ptr = m_ref->cursor;
m_ref->cursor += n;
return ptr;
}
void deallocate(T* ptr, std::size_t n) {}
private:
stack<T>* m_ref = nullptr;
};
} // namespace furvm
#endif // FURVM_STACK_HPP
File diff suppressed because it is too large Load Diff
+226
View File
@@ -0,0 +1,226 @@
#ifndef FURVM_TYPES_HPP
#define FURVM_TYPES_HPP
#include "furvm/fwd.hpp"
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace furvm {
using s8 = std::int8_t;
using s16 = std::int16_t;
using s32 = std::int32_t;
using s64 = std::int64_t;
using u8 = std::uint8_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using u64 = std::uint64_t;
struct thing_type {
struct array_value {
thing_type* type;
std::size_t size;
};
struct slice_value {
thing_type* type;
};
enum type { // NOLINT
S8 = 0,
S16,
S32,
S64,
U8,
U16,
U32,
U64,
String,
Ptr,
Ref,
Array,
Slice,
Count,
} type = Count;
union value {
std::nullptr_t null = nullptr;
thing_type* typeRef;
array_value array;
slice_value slice;
value() = default;
value(thing_type* type)
: typeRef(type) {}
value(thing_type* type, std::size_t size)
: array({}) {
array.type = type;
array.size = size;
}
} value;
static constexpr thing_type_id INVALID_ID = std::numeric_limits<thing_type_id>::max();
thing_type_id id = INVALID_ID;
bool operator==(const thing_type& other) const {
if (type != other.type) return false;
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64:
case String: return true;
case Ptr:
case Ref: return *value.typeRef == *other.value.typeRef;
case Array: return *value.array.type == *other.value.array.type && value.array.size == other.value.array.size;
case Slice: return *value.slice.type == *other.value.slice.type;
case Count: break;
}
return false;
}
bool operator!=(const thing_type& other) const { return !this->operator==(other); }
static bool is_primitive(enum type type) {
switch (type) {
case S8:
case S16:
case S32:
case S64:
case U8:
case U16:
case U32:
case U64: return true;
case String:
case Ptr:
case Ref:
case Array:
case Slice: return false;
case Count: break;
}
throw std::runtime_error("unreachable");
}
static std::size_t primitive_size(enum type type) {
switch (type) {
case thing_type::S8: return sizeof(s8);
case thing_type::S16: return sizeof(s16);
case thing_type::S32: return sizeof(s32);
case thing_type::S64: return sizeof(s64);
case thing_type::U8: return sizeof(u8);
case thing_type::U16: return sizeof(u16);
case thing_type::U32: return sizeof(u32);
case thing_type::U64: return sizeof(u64);
case thing_type::String:
case Ptr:
case Ref:
case Array:
case Slice: return 0;
case Count: break;
}
throw std::runtime_error("unreachable");
}
};
namespace detail {
template <typename T, typename = void>
struct overrides_thing_type_matching : std::false_type {};
template <typename T>
struct overrides_thing_type_matching<T, std::void_t<decltype(T::matches(std::declval<const thing_type&>()))>>
: std::is_same<decltype(T::matches(std::declval<const thing_type&>())), bool> {};
template <typename T>
struct thing_traits {
bool operator()(const thing_type& type) const {
if constexpr (overrides_thing_type_matching<T>::value) {
return T::matches(type);
} else {
return false;
}
}
};
template <>
struct thing_traits<s8> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S8; }
};
template <>
struct thing_traits<u8> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U8; }
};
template <>
struct thing_traits<s16> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S16; }
};
template <>
struct thing_traits<u16> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U16; }
};
template <>
struct thing_traits<s32> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S32; }
};
template <>
struct thing_traits<u32> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U32; }
};
template <>
struct thing_traits<s64> {
bool operator()(const thing_type& type) const { return type.type == thing_type::S64; }
};
template <>
struct thing_traits<u64> {
bool operator()(const thing_type& type) const { return type.type == thing_type::U64; }
};
template <typename Inner>
struct thing_traits<Inner*> {
bool operator()(const thing_type& type) const {
return (type.type == thing_type::Ptr || type.type == thing_type::Ref) &&
thing_traits<Inner>{}(*type.value.typeRef);
}
};
template <typename T, typename Thing, typename = void>
struct cassignable_to_thing : std::false_type {};
template <typename T, typename Thing>
struct cassignable_to_thing<T,
Thing,
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<const T&>()))>>
: std::true_type {};
template <typename T, typename Thing, typename = void>
struct massignable_to_thing : std::false_type {};
template <typename T, typename Thing>
struct massignable_to_thing<T,
Thing,
std::void_t<decltype(std::declval<thing_traits<T>>().assign_to(std::declval<Thing&>(), std::declval<T&&>()))>>
: std::true_type {};
} // namespace detail
} // namespace furvm
#endif // FURVM_TYPES_HPP
+4
View File
@@ -0,0 +1,4 @@
file(GLOB_RECURSE DISFURAS_SRCS "${CMAKE_SOURCE_DIR}/src/disfuras/**.cpp")
add_executable(disfuras ${DISFURAS_SRCS})
target_include_directories(disfuras PRIVATE ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(disfuras PUBLIC furlang libfurc libfurvm)
+250
View File
@@ -0,0 +1,250 @@
#include "furvm/instruction.hpp"
#include "furvm/module.hpp"
#include <cassert>
#include <exception>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <unordered_set>
using namespace std::string_literals;
void print_type(const furvm::mod_type& type, const furvm::mod& mod) {
switch (type.type) {
case furvm::mod_type::S8: std::cout << "$s8"; return;
case furvm::mod_type::S16: std::cout << "$s16"; return;
case furvm::mod_type::S32: std::cout << "$s32"; return;
case furvm::mod_type::S64: std::cout << "$s64"; return;
case furvm::mod_type::U8: std::cout << "$u8"; return;
case furvm::mod_type::U16: std::cout << "$u16"; return;
case furvm::mod_type::U32: std::cout << "$u32"; return;
case furvm::mod_type::U64: std::cout << "$u64"; return;
case furvm::mod_type::Ptr:
std::cout << "ptr ";
print_type(*mod.type_at(type.value.typeRef), mod);
return;
case furvm::mod_type::Ref:
std::cout << "ref ";
print_type(*mod.type_at(type.value.typeRef), mod);
return;
case furvm::mod_type::Array:
std::cout << "array ";
print_type(*mod.type_at(type.value.array.typeId), mod);
if (type.value.array.size == 0) {
std::cout << " dynamic";
} else {
std::cout << ' ' << type.value.array.size;
}
return;
case furvm::mod_type::Import: std::cout << "import " << type.value.imprt.modId << "::t" << type.value.imprt.typeId;
case furvm::mod_type::Count: break;
}
assert(false);
}
static const char* typeNames[furvm::instruction::Count] = {
// NoOperation:
"nop",
// PushS8:
"push $__t0",
// PushU8:
"push $__t1",
// PushS16:
"push $__t2",
// PushU16:
"push $__t3",
// PushS32:
"push $__t4",
// PushU32:
"push $__t5",
// PushConstant:
"push",
// Array:
"array",
// Get:
"get",
// Set:
"set",
// Drop:
"drop",
// Duplicate:
"dup",
// Swap:
"swap",
// Clone:
"clone",
// Reference:
"ref",
// Add:
"add",
// Sub:
"sub",
// Mul:
"mul",
// Div:
"div",
// Mod:
"mod",
// Equals:
"eq",
// NotEquals:
"ne",
// LessThan:
"lt",
// GreaterThan:
"gt",
// LessEqual:
"le",
// GreaterEqual:
"ge",
// Pointerof:
"pointerof",
// Sizeof:
"sizeof",
// Lengthof:
"lengthof",
// Load:
"load",
// Store:
"store",
// LoadGlobal:
"loadg",
// StoreGlobal:
"storeg",
// Call:
"call",
// Jump:
"jmp",
// JumpNotZero:
"jnz",
// Return:
"ret",
};
int main(int argc, char** argv) {
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <module.fmod>\n";
return 1;
}
try {
std::ifstream file(argv[1], std::ios::binary | std::ios::in);
furvm::mod mod;
try {
mod = furvm::mod::load(file);
} catch (const std::exception& ex) {
std::cerr << "Failed to load module " << argv[1] << ": " << ex.what() << '\n';
return 1;
}
std::cout << "; Generated with disfuras\n";
std::cout << "; Types:\n";
for (const auto& ptr : mod.types()) {
const auto& [header, type] = *ptr;
std::cout << "type __t" << header.id() << " = ";
print_type(type, mod);
std::cout << '\n';
}
std::unordered_map<furvm::function_id, std::string> funcNames;
std::size_t unnamedCounter = 0;
std::unordered_map<std::size_t, std::string> labels;
std::unordered_set<std::size_t> deadLabels;
std::size_t labelCounter = 0;
std::cout << "\n; Functions:\n";
for (const auto& ptr : mod.functions()) {
const auto& [header, func] = *ptr;
auto it = mod.function_map().find(header.id());
if (it != mod.function_map().end()) {
std::cout << "public func " << (funcNames[header.id()] = it->second.first);
} else {
std::cout << "func " << (funcNames[header.id()] = "__f"s + std::to_string(unnamedCounter++));
}
for (const auto& param : func.signature().params) {
std::cout << " $__t" << param.id();
}
std::cout << " = ";
if (func.signature().returnType.has_value())
std::cout << "$__t" << func.signature().returnType->id() << ' '; // NOLINT
switch (func.type()) {
case furvm::function_t::Normal: {
auto it = labels.find(func.position());
if (it == labels.end()) it = labels.emplace(func.position(), funcNames[header.id()]).first;
deadLabels.insert(func.position());
std::cout << '#' << it->second;
} break;
case furvm::function_t::Native: {
std::cout << "native " << func.native();
} break;
case furvm::function_t::Import: {
std::cout << "import " << func.imp().mod << "::" << func.imp().function;
} break;
}
std::cout << '\n';
}
if (mod.get_global_variable_count() > 0) {
std::cout << "\n; Global Variables:\n";
for (std::uint16_t i = 0; i < mod.get_global_variable_count(); ++i) {
std::cout << "allocate __g" << i << '\n';
}
}
std::cout << "\n; Bytecode:\n";
// Label pass:
for (std::size_t off = 0; off < mod.bytecode().size();) {
furvm::instruction instr{};
off += instr.read(mod.bytecode_view().subview(off));
if (instr.type != furvm::instruction::Jump && instr.type != furvm::instruction::JumpNotZero) continue;
if (labels.find(off + instr.arg.s8) == labels.end())
labels[off + instr.arg.s8] = "__l"s + std::to_string(labelCounter++);
deadLabels.insert(off + instr.arg.s8);
}
// Actual printing pass:
for (std::size_t off = 0; off < mod.bytecode().size();) {
if (auto it = labels.find(off); it != labels.end()) {
std::cout << it->second << ":\n";
deadLabels.erase(off);
}
furvm::instruction instr{};
off += instr.read(mod.bytecode_view().subview(off));
std::cout << " " << typeNames[instr.type];
switch (instr.arg.type) {
case furvm::instruction_argument::None: break;
case furvm::instruction_argument::S8: std::cout << ' ' << std::to_string(instr.arg.s8); break;
case furvm::instruction_argument::U8: std::cout << ' ' << std::to_string(instr.arg.u8); break;
case furvm::instruction_argument::S16: std::cout << ' ' << instr.arg.s16; break;
case furvm::instruction_argument::U16: std::cout << ' ' << instr.arg.u16; break;
case furvm::instruction_argument::S32: std::cout << ' ' << std::to_string(instr.arg.s8); break;
case furvm::instruction_argument::U32: std::cout << ' ' << std::to_string(instr.arg.u8); break;
case furvm::instruction_argument::Constant: throw std::runtime_error("unimplemented");
case furvm::instruction_argument::Type: std::cout << " $__t" << instr.arg.u16; break;
case furvm::instruction_argument::Variable: std::cout << " %" << instr.arg.u16; break;
case furvm::instruction_argument::GlobalVariable: std::cout << " %__g" << instr.arg.u16; break;
case furvm::instruction_argument::Function: std::cout << ' ' << funcNames[instr.arg.s16]; break;
case furvm::instruction_argument::Offset: std::cout << " #" << labels[instr.arg.s16]; break;
case furvm::instruction_argument::Count: break;
}
std::cout << '\n';
}
if (!deadLabels.empty()) {
std::cerr << "Malformed module: invalid jumps\n";
return 1;
}
} catch (const std::exception& ex) {
std::cerr << "Exception uncaught: " << ex.what() << '\n';
return 1;
}
return 0;
}
// TODO: Disassemble into FIR (furlang's IR)
+5
View File
@@ -0,0 +1,5 @@
file(GLOB_RECURSE FURAS_SRCS "${CMAKE_SOURCE_DIR}/src/furas/**.cpp")
file(GLOB_RECURSE FURAS_HDRS "${CMAKE_SOURCE_DIR}/include/furas/**.hpp")
add_executable(furas ${FURAS_SRCS} ${FURAS_HDRS})
target_include_directories(furas PUBLIC ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(furas PRIVATE furlang libfurvm)
+622
View File
@@ -0,0 +1,622 @@
#include "furas/gen.hpp"
#include "furas/token.hpp"
#include "furvm/function.hpp"
#include "furvm/fwd.hpp"
#include "furvm/instruction.hpp"
#include "furvm/module.hpp"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <optional>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <vector>
namespace furas {
using namespace std::string_literals;
namespace {
// NOLINTBEGIN
std::unordered_map<enum token::type, furvm::instruction_t> instructions = {
{ token::Array, furvm::instruction_t::Array },
{ token::Slice, furvm::instruction_t::Slice },
{ token::Get, furvm::instruction_t::Get },
{ token::Set, furvm::instruction_t::Set },
{ token::Drop, furvm::instruction_t::Drop },
{ token::Dup, furvm::instruction_t::Duplicate },
{ token::Swap, furvm::instruction_t::Swap },
{ token::Clone, furvm::instruction_t::Clone },
{ token::Ref, furvm::instruction_t::Reference },
{ token::Add, furvm::instruction_t::Add },
{ token::Sub, furvm::instruction_t::Sub },
{ token::Mul, furvm::instruction_t::Mul },
{ token::Div, furvm::instruction_t::Div },
{ token::Mod, furvm::instruction_t::Mod },
{ token::Eq, furvm::instruction_t::Equals },
{ token::Neq, furvm::instruction_t::NotEquals },
{ token::Lt, furvm::instruction_t::LessThan },
{ token::Gt, furvm::instruction_t::GreaterThan },
{ token::Le, furvm::instruction_t::LessEqual },
{ token::Ge, furvm::instruction_t::GreaterEqual },
{ token::Ptrof, furvm::instruction_t::Pointerof },
{ token::Sizeof, furvm::instruction_t::Sizeof },
{ token::Lenof, furvm::instruction_t::Lengthof },
{ token::Load, furvm::instruction_t::Load },
{ token::Store, furvm::instruction_t::Store },
{ token::LoadGlobal, furvm::instruction_t::LoadGlobal },
{ token::StoreGlobal, furvm::instruction_t::StoreGlobal },
{ token::Call, furvm::instruction_t::Call },
{ token::Jmp, furvm::instruction_t::Jump },
{ token::Jnz, furvm::instruction_t::JumpNotZero },
{ token::Ret, furvm::instruction_t::Return },
};
// NOLINTEND
const char* token_type(enum token::type type) {
switch (type) {
case token::Identifier: return "identifier";
case token::Signed: return "signed";
case token::Unsigned: return "unsigned";
case token::Monkey: return "'@'";
case token::Dolar: return "'$'";
case token::Sha256: return "'#'";
case token::Percent: return "'%'";
case token::EqSign: return "'='";
case token::Dot: return "'.'";
case token::Colon: return "':'";
case token::Func: return "func";
case token::Type: return "type";
case token::Native: return "native";
case token::Import: return "import";
case token::Public: return "public";
case token::Private: return "private";
case token::Allocate: return "allocate";
case token::Push: return "push";
case token::Array: return "array";
case token::Slice: return "slice";
case token::Get: return "get";
case token::Set: return "set";
case token::Drop: return "drop";
case token::Dup: return "dup";
case token::Swap: return "swap";
case token::Clone: return "clone";
case token::Ref: return "ref";
case token::Add: return "add";
case token::Sub: return "sub";
case token::Mul: return "mul";
case token::Div: return "div";
case token::Mod: return "mod";
case token::Eq: return "eq";
case token::Neq: return "neq";
case token::Lt: return "lt";
case token::Gt: return "gt";
case token::Le: return "le";
case token::Ge: return "ge";
case token::Ptrof: return "ptrof";
case token::Sizeof: return "sizeof";
case token::Lenof: return "lenof";
case token::Load: return "load";
case token::Store: return "store";
case token::LoadGlobal: return "loadg";
case token::StoreGlobal: return "storeg";
case token::Call: return "call";
case token::Jmp: return "jmp";
case token::Jnz: return "jnz";
case token::Ret: return "ret";
case token::Count: break;
}
throw std::runtime_error("unreachable");
}
struct mod_context {
struct label_context {
struct function_info {
std::string name;
furvm::function_sig signature;
bool pub;
};
static constexpr std::size_t INVALID = std::numeric_limits<std::size_t>::max();
std::size_t offset = INVALID;
std::vector<function_info> functions;
std::vector<std::size_t> unknowns;
};
furvm::mod mod;
std::unordered_map<std::string, std::vector<furvm::function_h>> functions;
std::unordered_map<std::string, furvm::mod_type_h> types;
std::unordered_map<std::string, std::uint16_t> variables;
std::unordered_map<std::string, label_context> labels;
struct token_result {
generator_error error;
token value;
token* operator->() { return &value; }
token& operator*() { return value; }
bool operator!() const { return error.type != generator_error::Success; }
};
bool compare_types(const furvm::mod_type_h& lhs, const furvm::mod_type_h& rhs) const {
if (lhs->type != rhs->type) return false;
switch (lhs->type) {
case furvm::mod_type::S8:
case furvm::mod_type::S16:
case furvm::mod_type::S32:
case furvm::mod_type::S64:
case furvm::mod_type::U8:
case furvm::mod_type::U16:
case furvm::mod_type::U32:
case furvm::mod_type::U64: return true;
case furvm::mod_type::Ptr:
case furvm::mod_type::Ref:
return compare_types(mod.type_at(lhs->value.typeRef), mod.type_at(rhs->value.typeRef));
case furvm::mod_type::Array:
return lhs->value.array.size == rhs->value.array.size &&
compare_types(mod.type_at(lhs->value.array.typeId), mod.type_at(rhs->value.array.typeId));
case furvm::mod_type::Slice:
return compare_types(mod.type_at(lhs->value.slice.typeId), mod.type_at(rhs->value.slice.typeId));
case furvm::mod_type::Import:
return lhs->value.imprt.modId == rhs->value.imprt.modId &&
lhs->value.imprt.typeId == rhs->value.imprt.typeId;
case furvm::mod_type::Count: break;
}
throw std::runtime_error("unreachable");
}
furvm::function_h find_function(const std::string& name, const furvm::function_sig& signature) const {
if (auto it = functions.find(name); it != functions.end()) {
for (const auto& func : it->second) {
if (func->signature().params.size() != signature.params.size()) continue;
std::size_t idx = 0;
while (idx < signature.params.size()) {
if (!compare_types(func->signature().params[idx], signature.params[idx])) break;
++idx;
}
if (idx == signature.params.size()) return func;
}
}
return {};
}
mod_context() {
types.emplace("s8", mod.emplace_type(furvm::mod_type::S8));
types.emplace("u8", mod.emplace_type(furvm::mod_type::U8));
types.emplace("s16", mod.emplace_type(furvm::mod_type::S16));
types.emplace("u16", mod.emplace_type(furvm::mod_type::U16));
types.emplace("s32", mod.emplace_type(furvm::mod_type::S32));
types.emplace("u32", mod.emplace_type(furvm::mod_type::U32));
types.emplace("s64", mod.emplace_type(furvm::mod_type::S64));
types.emplace("u64", mod.emplace_type(furvm::mod_type::U64));
}
static token_result next_token(lexer& lexer) {
auto token = lexer.next_token();
if (token.has_error()) {
switch (token.error().type) {
case lexer_error::EndOfFile:
return { { generator_error::UnexpectedEof, "Unexpected end of file" }, { token::Count } };
case lexer_error::UnknownCharacter:
return { { generator_error::UnknownCharacter, token.error().message }, { token::Count } };
}
}
return { { generator_error::Success }, *token };
}
static token_result eat_token(lexer& lexer, enum token::type type) {
auto token = next_token(lexer);
if (!token) return token;
if (token.value.type != type) {
return { { generator_error::UnexpectedToken,
"Expected "s + token_type(type) + ", but got " + token_type(token->type) },
{ token::Count } };
}
return { { generator_error::Success }, token.value };
}
furvm::mod_type_h eat_type(lexer& lexer, const token& tok) {
switch (tok.type) {
case token::Dolar: {
auto result = eat_token(lexer, token::Identifier);
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
return it->second;
}
throw std::runtime_error("unknown type");
}
case token::Import: {
throw std::runtime_error("unimplemented");
// auto typeNameRes = eat_token(lexer, token::Identifier);
// if (!typeNameRes) return typeNameRes.error;
// return { generator_error::Success };
}
case token::Ref: {
auto result = next_token(lexer);
if (!result) throw std::runtime_error("error");
auto inner = eat_type(lexer, result.value);
return mod.emplace_type(furvm::mod_type::Ref, inner.id());
}
case token::Array: {
auto result = next_token(lexer);
if (!result) throw std::runtime_error("error");
auto inner = eat_type(lexer, result.value);
auto size = eat_token(lexer, token::Unsigned);
if (!size) throw std::runtime_error("error");
return mod.emplace_type(inner.id(), size->value.uint);
}
case token::Slice: {
auto result = next_token(lexer);
if (!result) throw std::runtime_error("error");
auto inner = eat_type(lexer, result.value);
return mod.emplace_type(furvm::mod_type::Slice, inner.id());
}
default: throw std::runtime_error("error");
}
}
generator_error generate(lexer& lexer) {
auto result = next_token(lexer);
if (result.error.type == generator_error::UnexpectedEof) return { generator_error::Eof };
if (!result) return result.error;
switch (result->type) {
case token::Identifier: {
std::string labelName = std::string(result->value.string);
result = eat_token(lexer, token::Colon);
if (!result) return result.error;
auto& label = labels[labelName];
if (label.offset != label_context::INVALID)
return { generator_error::UnexpectedToken, "Label "s + labelName + " already exists" };
label.offset = mod.bytecode().size();
for (auto& func : label.functions) {
furvm::function_h handle =
(func.pub ? mod.emplace_function(func.name, std::move(func.signature), label.offset)
: mod.emplace_function(std::move(func.signature), label.offset));
functions[std::move(func.name)].push_back(handle);
handle.dispatch();
}
for (auto unknown : label.unknowns) {
const auto jmpOff = static_cast<std::ptrdiff_t>(label.offset) - static_cast<std::ptrdiff_t>(unknown);
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
assert(false); // TODO: Further jumps are not implemented
}
mod.bytecode()[unknown - 1] = static_cast<std::int8_t>(jmpOff);
}
label.functions = {};
label.unknowns = {};
return { generator_error::Success };
}
case token::Allocate: {
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
std::string name = std::string(result->value.string);
variables.emplace(name, variables.size());
mod.set_global_variable_count(variables.size());
return { generator_error::Success };
}
case token::Func:
case token::Type:
case token::Public:
case token::Private: {
bool pub = false;
if (result->type == token::Public) {
pub = true;
result = next_token(lexer);
if (!result) return { result.error };
} else if (result->type == token::Private) {
result = next_token(lexer);
if (!result) return { result.error };
}
if (result->type == token::Func) {
auto nameRes = next_token(lexer);
if (!nameRes) return nameRes.error;
furvm::function_sig signature;
result = next_token(lexer);
if (!result) return result.error;
while (result->type != token::EqSign) {
signature.params.push_back(eat_type(lexer, result.value));
result = next_token(lexer);
if (!result) return result.error;
}
if (result->type != token::EqSign) return result.error;
result = next_token(lexer);
if (!result) return result.error;
if (result->type == token::Dolar) {
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
if (auto it = types.find(std::string(result->value.string)); it != types.end()) {
signature.returnType = it->second;
} else {
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
}
result = next_token(lexer);
if (!result) return result.error;
}
std::string name = std::string(nameRes->value.string);
if (!find_function(name, signature).empty()) throw std::runtime_error("function already defined");
switch (result->type) {
case token::Sha256: {
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
std::string labelName = std::string(result->value.string);
std::size_t offset = 0;
if (auto it = labels.find(labelName); it != labels.end()) {
offset = it->second.offset;
} else {
labels[labelName].functions.emplace_back(
label_context::function_info{ std::move(name), std::move(signature), pub });
return { generator_error::Success };
}
furvm::function_h handle =
(pub ? mod.emplace_function(name, signature, offset) : mod.emplace_function(signature, offset));
functions[name].push_back(handle);
handle.dispatch();
return { generator_error::Success };
}
case token::Native: {
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
std::string nativeName = std::string(result->value.string);
std::string name = std::string(nameRes->value.string);
furvm::function_h handle = (pub ? mod.emplace_function(name, signature, std::move(nativeName))
: mod.emplace_function(signature, std::move(nativeName)));
functions[name].push_back(handle);
handle.dispatch();
return { generator_error::Success };
}
case token::Import: {
throw std::runtime_error("unimplemented");
}
default:
return { generator_error::UnexpectedToken,
"Unexpected token "s + token_type(result->type) +
", expected label name, `native`, or `import`" };
}
}
if (result->type == token::Type) {
auto nameRes = eat_token(lexer, token::Identifier);
if (!nameRes) return nameRes.error;
result = eat_token(lexer, token::EqSign);
if (!result) return result.error;
result = next_token(lexer);
if (!result) return result.error;
auto type = eat_type(lexer, result.value);
types.emplace(std::string(nameRes->value.string), std::move(type));
return { generator_error::Success };
}
return { generator_error::UnexpectedToken,
"Unexpected token "s + token_type(result->type) + ", expected either `func` or `type`" };
}
case token::Push: {
auto result = eat_token(lexer, token::Dolar);
if (!result) return result.error;
auto typeName = eat_token(lexer, token::Identifier);
if (!typeName) return result.error;
auto it = types.find(std::string(typeName->value.string));
if (it == types.end())
return { generator_error::UnexpectedToken, "Unknown type "s + std::string(typeName->value.string) };
// TODO: Add support for signed integers
auto value = eat_token(lexer, token::Unsigned);
if (!value) return result.error;
auto type = it->second;
switch (type->type) {
case furvm::mod_type::S8: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS8));
mod.bytecode().push_back(value->value.uint & 0xFF);
} break;
case furvm::mod_type::U8: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU8));
mod.bytecode().push_back(value->value.uint & 0xFF);
} break;
case furvm::mod_type::S16: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS16));
mod.bytecode().push_back(value->value.uint & 0xFF);
mod.bytecode().push_back((value->value.uint >> 8) & 0xFF);
} break;
case furvm::mod_type::U16: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU16));
mod.bytecode().push_back(value->value.uint & 0xFF);
mod.bytecode().push_back((value->value.uint >> 8) & 0xFF);
} break;
case furvm::mod_type::S32: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushS32));
mod.bytecode().push_back(value->value.uint & 0xFF);
} break;
case furvm::mod_type::U32: {
mod.bytecode().push_back(static_cast<furvm::byte>(furvm::instruction_t::PushU32));
mod.bytecode().push_back(value->value.uint & 0xFF);
} break;
default:
return { generator_error::UnexpectedToken, "Unexpected type "s + std::string(typeName->value.string) };
}
return { generator_error::Success };
}
case token::Array:
case token::Slice:
case token::Get:
case token::Set:
case token::Drop:
case token::Dup:
case token::Swap:
case token::Clone:
case token::Ref:
case token::Add:
case token::Sub:
case token::Mul:
case token::Div:
case token::Mod:
case token::Eq:
case token::Neq:
case token::Lt:
case token::Gt:
case token::Le:
case token::Ge:
case token::Ptrof:
case token::Sizeof:
case token::Lenof:
case token::Load:
case token::Store:
case token::LoadGlobal:
case token::StoreGlobal:
case token::Call:
case token::Jmp:
case token::Jnz:
case token::Ret: {
auto it = instructions.find(result->type);
assert(it != instructions.end());
furvm::instruction instr{ it->second };
instr.arg.type = furvm::instruction::s_arguments[instr.type];
switch (instr.arg.type) {
case furvm::instruction_argument::None: break;
case furvm::instruction_argument::Type: {
result = eat_token(lexer, token::Dolar);
if (!result) return result.error;
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
auto type = types.find(std::string(result->value.string));
if (type == types.end())
return { generator_error::UnknownType, "Unknown type "s + std::string(result->value.string) };
instr.arg.u32 = type->second.id();
} break;
case furvm::instruction_argument::Constant: {
assert(false); // TODO: Unimplemented
} break;
case furvm::instruction_argument::Variable: {
result = eat_token(lexer, token::Percent);
if (!result) return result.error;
result = eat_token(lexer, token::Unsigned);
if (!result) return result.error;
instr.arg.u16 = result->value.uint;
} break;
case furvm::instruction_argument::GlobalVariable: {
result = eat_token(lexer, token::Percent);
if (!result) return result.error;
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
if (auto it = variables.find(std::string(result->value.string)); it != variables.end())
instr.arg.u16 = it->second;
else
throw std::runtime_error("Unknown global variable");
} break;
case furvm::instruction_argument::Function: {
furvm::function_sig signature;
while ((result = next_token(lexer)).error.type == generator_error::Success &&
result->type != token::Identifier) {
signature.params.push_back(eat_type(lexer, result.value));
}
if (!result || result->type != token::Identifier) return result.error;
std::string name(result->value.string);
auto func = find_function(name, signature);
if (func.empty())
return { generator_error::UnknownType, "Unknown function "s + std::string(result->value.string) };
instr.arg.u16 = func.id();
} break;
case furvm::instruction_argument::Offset: {
result = eat_token(lexer, token::Sha256);
if (!result) return result.error;
result = eat_token(lexer, token::Identifier);
if (!result) return result.error;
auto& label = labels[std::string(result->value.string)];
auto offset = label.offset;
if (offset == label_context::INVALID) {
label.unknowns.push_back(mod.bytecode().size() + 2);
instr.arg.s8 = 0;
break;
}
const auto jmpOff =
static_cast<std::ptrdiff_t>(offset) - static_cast<std::ptrdiff_t>(mod.bytecode().size()) - 2;
if (jmpOff < std::numeric_limits<std::int8_t>::min() ||
jmpOff > std::numeric_limits<std::int8_t>::max()) {
assert(false); // TODO: Further jumps are not implemented
}
instr.arg.s8 = static_cast<std::int8_t>(jmpOff);
} break;
case furvm::instruction_argument::S8:
case furvm::instruction_argument::U8:
case furvm::instruction_argument::S16:
case furvm::instruction_argument::U16:
case furvm::instruction_argument::S32:
case furvm::instruction_argument::U32:
case furvm::instruction_argument::Count:
default: throw std::runtime_error("unreachable");
}
instr.write(mod.bytecode());
return { generator_error::Success };
}
case token::Signed:
case token::Unsigned:
case token::Colon:
case token::Monkey:
case token::Dolar:
case token::Sha256:
case token::Percent:
case token::EqSign:
case token::Dot:
case token::Native:
case token::Import:
case token::Count: break;
}
return { generator_error::UnexpectedToken, "Unexpected token "s + token_type(result->type) };
}
};
} // namespace
generator::result generator::generate(lexer lexer) {
mod_context context;
generator_error error;
do {
error = context.generate(lexer);
} while (error.type == generator_error::Success);
if (error.type == generator_error::Eof) return { { generator_error::Success }, std::move(context.mod) };
return { error };
}
} // namespace furas
+102
View File
@@ -0,0 +1,102 @@
#include "furas/lexer.hpp"
#include <cctype>
#include <unordered_map>
namespace furas {
using namespace std::string_literals;
token_r lexer::next_token() {
while (m_cursor < m_content.size() && std::isspace(m_content[m_cursor]) != 0) {
if (m_content[m_cursor] == '\n') {
m_lineStart = m_cursor + 1;
++m_column;
}
++m_cursor;
}
// TODO: Add support for single-line comments
// TODO: Add support for multi-line comments
if (m_cursor >= m_content.size()) return token_r::error(lexer_error{ lexer_error::EndOfFile, location() });
// TODO: Add support for negative integers (I am positive thanks to stasiu :v:)
// TODO: Add support for hexadecimal and binary numeric literals
if (std::isdigit(m_content[m_cursor]) != 0) {
std::uint64_t num = 0;
while (m_cursor < m_content.size() && std::isdigit(m_content[m_cursor]) != 0) {
num *= 10;
num += m_content[m_cursor++] - '0';
}
return { num };
}
static std::unordered_map<std::string_view, enum token::type> s_tokens = {
{ "func", token::Func },
{ "type", token::Type },
{ "native", token::Native },
{ "import", token::Import },
{ "public", token::Public },
{ "private", token::Private },
{ "allocate", token::Allocate },
{ "push", token::Push },
{ "array", token::Array },
{ "slice", token::Slice },
{ "get", token::Get },
{ "set", token::Set },
{ "drop", token::Drop },
{ "dup", token::Dup },
{ "swap", token::Swap },
{ "clone", token::Clone },
{ "ref", token::Ref },
{ "add", token::Add },
{ "sub", token::Sub },
{ "mul", token::Mul },
{ "div", token::Div },
{ "mod", token::Mod },
{ "eq", token::Eq },
{ "neq", token::Neq },
{ "lt", token::Lt },
{ "gt", token::Gt },
{ "le", token::Le },
{ "ge", token::Ge },
{ "ptrof", token::Ptrof },
{ "sizeof", token::Sizeof },
{ "lenof", token::Lenof },
{ "load", token::Load },
{ "store", token::Store },
{ "loadg", token::LoadGlobal },
{ "storeg", token::StoreGlobal },
{ "call", token::Call },
{ "jmp", token::Jmp },
{ "jnz", token::Jnz },
{ "ret", token::Ret },
};
if (std::isalnum(m_content[m_cursor]) != 0) {
std::size_t begin = m_cursor++;
while (m_cursor < m_content.size() && (std::isalnum(m_content[m_cursor]) != 0 || m_content[m_cursor] == '_'))
++m_cursor;
std::string_view str = m_content.substr(begin, m_cursor - begin);
if (auto it = s_tokens.find(str); it != s_tokens.end()) return { it->second };
return { token::Identifier, str };
}
switch (m_content[m_cursor]) {
case '@': ++m_cursor; return { token::Monkey };
case '$': ++m_cursor; return { token::Dolar };
case '#': ++m_cursor; return { token::Sha256 };
case '%': ++m_cursor; return { token::Percent };
case '=': ++m_cursor; return { token::EqSign };
case '.': ++m_cursor; return { token::Dot };
case ':': ++m_cursor; return { token::Colon };
default:
return token_r::error(lexer_error{ lexer_error::UnknownCharacter,
location(),
"Unknown character '"s + m_content[m_cursor] + "'" });
}
}
} // namespace furas
+39
View File
@@ -0,0 +1,39 @@
#include "furas/gen.hpp"
#include "furas/lexer.hpp"
#include <fstream>
#include <furvm/module.hpp>
#include <iostream>
int main(int argc, char** argv) { // NOLINT
if (argc < 2) {
std::cerr << "feed me more arguments daddy >_<\n";
return 1;
}
if (argc > 2) {
std::cerr << "too much O_O\n";
return 1;
}
const char* filepath = argv[1];
std::ifstream file(filepath, std::ios::binary | std::ios::ate | std::ios::in);
if (!file.is_open()) {
std::cerr << "file won't open >~<\n";
return 1;
}
std::string content;
content.resize(file.tellg());
file.seekg(0);
file.read(content.data(), static_cast<std::streamsize>(content.size()));
auto result = furas::generator::generate(furas::lexer(filepath, content));
if (result.error.type != furas::generator_error::Success) {
std::cerr << result.error.message << '\n';
return 1;
}
result.mod.serialize(std::cout);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
file(GLOB_RECURSE FURC_SRCS "${CMAKE_SOURCE_DIR}/src/furc/**.cpp")
file(GLOB_RECURSE FURC_HDRS "${CMAKE_SOURCE_DIR}/include/furc/**.hpp")
add_library(libfurc ${FURC_SRCS} ${FURC_HDRS})
target_include_directories(libfurc PUBLIC ${CMAKE_SOURCE_DIR}/include/)
target_compile_definitions(libfurc PRIVATE LIBFURC)
set_target_properties(libfurc PROPERTIES PREFIX "")
target_link_libraries(libfurc PUBLIC furlang libfurvm)
add_executable(furc "${CMAKE_SOURCE_DIR}/src/furc/main.cpp")
target_link_libraries(furc PRIVATE libfurc)
include(GoogleTest)
add_executable(furc_tests "${CMAKE_SOURCE_DIR}/test/furc/ssa.cpp")
target_link_libraries(furc_tests PRIVATE libfurc GTest::gtest_main)
gtest_discover_tests(furc_tests)
+20
View File
@@ -0,0 +1,20 @@
#include "furc/back/furvm/target.hpp"
#include "furvm/function.hpp"
#include <stdexcept>
namespace furc {
furvm::mod furvm_generator::generate(const ir_module& mod) {
if (!mod.variables.empty()) throw std::runtime_error("global variables are not supported for furvm yet");
furvm::mod furvmMod;
for (const auto& func : mod.functions) {
furvmMod.emplace_function(furvm::function_sig{ {}, {} }, furvmMod.bytecode().size());
}
return furvmMod;
}
} // namespace furc
+206
View File
@@ -0,0 +1,206 @@
#include "furc/front/lexer.hpp"
#include <cctype>
#include <limits>
#include <string_view>
#include <unordered_map>
namespace furc {
token lexer::next_token() {
if (m_peekToken.empty()) return get_token();
auto tok = m_peekToken.front();
m_peekToken.pop_front();
return tok;
}
token lexer::peek_token(std::size_t offset) {
if (m_peekToken.size() <= offset) {
while (m_peekToken.size() <= offset) {
auto tok = get_token();
m_peekToken.push_back(tok);
}
}
return m_peekToken[offset];
}
token lexer::get_token() {
skip_spaces();
if (m_cursor >= m_content.size()) return { location(), token::EndOfFile };
auto loc = location();
if (std::isdigit(get()) != 0) {
std::uint64_t value = get() - '0';
next();
while (m_cursor < m_content.size() && std::isdigit(get()) != 0) {
static constexpr std::uint64_t MAX = std::numeric_limits<std::uint64_t>::max();
static constexpr std::uint64_t MAX_MULTS = MAX / 10;
static constexpr std::uint64_t LAST_DIGIT = MAX % 10;
if (value > MAX_MULTS) {
return { location(), token::InvalidInteger };
}
std::uint64_t digit = get() - '0';
if (value == MAX_MULTS && digit > LAST_DIGIT) {
return { location(), token::InvalidInteger };
}
value *= 10;
value += digit;
next();
}
return { loc, value };
}
if (std::isalnum(get()) != 0 || get() == '_') {
static std::unordered_map<std::string_view, token_t> s_keywords = {
{ "func", token::Func },
{ "return", token::Return },
{ "if", token::If },
{ "else", token::Else },
{ "while", token::While },
{ "public", token::Public },
{ "private", token::Private },
{ "pre", token::Pre },
{ "post", token::Post },
{ "pointerof", token::Pointerof },
{ "sizeof", token::Sizeof },
{ "lengthof", token::Lengthof },
{ "s8", token::S8 },
{ "u8", token::U8 },
{ "s16", token::S16 },
{ "u16", token::U16 },
{ "s32", token::S32 },
{ "u32", token::U32 },
{ "s64", token::S64 },
{ "u64", token::U64 },
};
std::size_t begin = m_cursor;
next();
while (m_cursor < m_content.size() && (std::isalnum(get()) != 0 || get() == '_'))
next();
std::string_view name = m_content.substr(begin, m_cursor - begin);
if (auto it = s_keywords.find(name); it != s_keywords.end()) {
return { loc, it->second };
}
return { loc, token::Identifier, name };
}
if (get() == '"') {
next();
std::size_t begin = m_cursor;
while (m_cursor < m_content.size() && get() != '"')
next();
if (m_cursor >= m_content.size()) return { location(), token::UnexpectedEOF };
next();
return { loc, token::String, m_content.substr(begin, m_cursor - begin - 1) };
}
if (get() == '\'') {
next();
bool slash = get() == '\\';
if (slash) next();
auto loc2 = location();
char character = get();
next();
if (get() != '\'') return { location(), token::UnexpectedCharacter, get() };
next();
if (slash) {
switch (character) {
case '\\': character = '\\'; break;
case 'n': character = '\n'; break;
case 'r': character = '\r'; break;
case 't': character = '\t'; break;
default: return { loc2, token::UnexpectedCharacter, character };
}
}
return { loc, token::Char, character };
}
static std::unordered_map<std::string_view, token_t> s_tokens = {
{ "(", token::LParen },
{ ")", token::RParen },
{ "{", token::LBrace },
{ "}", token::RBrace },
{ "[", token::LBracket },
{ "]", token::RBracket },
{ ";", token::Semicolon },
{ ":", token::Colon },
{ ",", token::Comma },
{ ".", token::Dot },
{ "+", token::Plus },
{ "-", token::Minus },
{ "*", token::Star },
{ "/", token::Slash },
{ "%", token::Percent },
{ "&", token::Ampersand },
{ "|", token::Pipe },
{ "^", token::Hat },
{ "&&", token::DblAmpersand },
{ "||", token::DblPipe },
{ "++", token::DblPlus },
{ "--", token::DblMinus },
{ "!", token::ExMark },
{ "^^", token::CatEars },
{ "=", token::Equals },
{ "+=", token::PlusEquals },
{ "-=", token::MinusEquals },
{ "*=", token::StarEquals },
{ "/=", token::SlashEquals },
{ "%=", token::PercentEquals },
{ "&=", token::AmpersandEquals },
{ "|=", token::PipeEquals },
{ "^=", token::HatEquals },
{ "==", token::DblEquals },
{ "!=", token::ExEquals },
{ "<", token::LessThan },
{ "<=", token::LessEquals },
{ ">", token::GreaterThan },
{ ">=", token::GreaterEquals },
{ "->", token::SlimArrow },
{ "=>", token::FatArrow },
{ "@", token::Monkey },
{ "#", token::Sha256 },
};
std::size_t begin = m_cursor;
std::size_t len = 1;
while (begin + len - 1 < m_content.size() && s_tokens.find(m_content.substr(begin, len)) != s_tokens.end())
++len;
if (len > 1) {
auto type = s_tokens[m_content.substr(begin, len - 1)];
m_cursor += len - 1;
return { loc, type };
}
return { loc, token::UnexpectedCharacter, get() };
}
void lexer::next() {
if (m_cursor < m_content.size()) ++m_cursor;
}
constexpr char lexer::get(std::size_t offset) const {
return m_content[m_cursor + offset];
}
void lexer::skip_spaces() {
while (m_cursor < m_content.size() && std::isspace(get()) != 0) {
if (m_content[m_cursor++] == '\n') {
++m_row;
m_lineStart = m_cursor;
}
}
}
constexpr token::location lexer::location() const {
return { m_filepath, m_row, m_cursor - m_lineStart };
}
} // namespace furc
+385
View File
@@ -0,0 +1,385 @@
#include "furc/front/parser.hpp"
#include "furc/front/ast.hpp"
#include "furc/front/token.hpp"
#include <cassert>
#include <stdexcept>
#include <unordered_map>
#include <utility>
namespace furc {
ast parser::parse() {
ast tree;
while (m_lexer.peek_token().type != token::EndOfFile) {
auto* decl = parse_decl();
assert(decl);
tree.decls.push_back(decl);
}
return std::move(tree);
}
stmt_node* parser::parse_stmt() {
switch (m_lexer.peek_token().type) {
case token::LBrace: return m_arena->allocate<comp_stmt_node>(parse_comp());
case token::Return: {
m_lexer.next_token();
return_stmt_node node;
if (m_lexer.peek_token().type != token::Semicolon) {
node.value = parse_expr();
}
eat_token(token::Semicolon);
return m_arena->allocate<return_stmt_node>(std::move(node));
}
case token::If: {
m_lexer.next_token();
eat_token(token::LParen);
if_stmt_node node;
node.cond = parse_expr();
eat_token(token::RParen);
node.thenBranch = parse_stmt();
if (m_lexer.peek_token().type == token::Else) {
m_lexer.next_token();
node.elseBranch = parse_stmt();
}
return m_arena->allocate<if_stmt_node>(std::move(node));
}
case token::While: {
m_lexer.next_token();
eat_token(token::LParen);
while_stmt_node node;
node.cond = parse_expr();
eat_token(token::RParen);
node.body = parse_stmt();
return m_arena->allocate<while_stmt_node>(std::move(node));
}
default: break;
}
if (m_lexer.peek_token().type == token::Func) {
eat_token(token::Func);
func_decl_node func;
func.name = std::string(eat_token(token::Identifier).value.string);
eat_token(token::LParen);
if (m_lexer.peek_token().type != token::RParen) {
do {
var_decl_node param;
param.name = std::string(eat_token(token::Identifier).value.string);
eat_token(token::Colon);
param.type = parse_type();
if (m_lexer.peek_token().type == token::Equals) {
m_lexer.next_token();
param.init = parse_expr();
}
func.params.emplace_back(std::move(param));
} while (eat_token(token::Comma, token::RParen).type == token::Comma);
} else {
eat_token(token::RParen);
}
if (m_lexer.peek_token().type == token::SlimArrow) {
m_lexer.next_token();
func.type = parse_type();
}
if (m_lexer.peek_token().type == token::Semicolon) {
m_lexer.next_token();
return m_arena->allocate<func_decl_node>(std::move(func));
}
func_decl_node::def_s def;
while (m_lexer.peek_token().type == token::Pre || m_lexer.peek_token().type == token::Post) {
auto token = m_lexer.next_token();
eat_token(token::LParen);
auto* expr = parse_expr();
eat_token(token::RParen);
switch (token.type) {
case token::Pre: {
def.preConds.push_back(expr);
} break;
case token::Post: {
def.postConds.push_back(expr);
} break;
default: throw std::runtime_error("unreachable");
}
}
def.body = parse_comp();
func.def = std::move(def);
return m_arena->allocate<func_decl_node>(std::move(func));
}
if (m_lexer.peek_token().type == token::Identifier &&
(m_lexer.peek_token(1).type == token::Colon || m_lexer.peek_token(1).type == token::Equals)) {
var_decl_node var;
var.name = std::string(eat_token(token::Identifier).value.string);
eat_token(token::Colon); // TODO: Auto-deduce the type
var.type = parse_type();
if (eat_token(token::Equals, token::Semicolon).type == token::Equals) {
var.init = parse_expr();
eat_token(token::Semicolon);
}
return m_arena->allocate<var_decl_node>(std::move(var));
}
auto* expr = parse_expr();
eat_token(token::Semicolon);
return expr;
}
decl_node* parser::parse_decl() {
auto first = eat_token(token::Identifier, token::Func);
if (first.type == token::Func) {
func_decl_node func;
func.name = std::string(eat_token(token::Identifier).value.string);
eat_token(token::LParen);
if (m_lexer.peek_token().type != token::RParen) {
do {
var_decl_node param;
param.name = std::string(eat_token(token::Identifier).value.string);
eat_token(token::Colon);
param.type = parse_type();
if (m_lexer.peek_token().type == token::Equals) {
m_lexer.next_token();
param.init = parse_expr();
}
func.params.emplace_back(std::move(param));
} while (eat_token(token::Comma, token::RParen).type == token::Comma);
} else {
eat_token(token::RParen);
}
if (m_lexer.peek_token().type == token::SlimArrow) {
m_lexer.next_token();
func.type = parse_type();
}
if (m_lexer.peek_token().type == token::Semicolon) {
m_lexer.next_token();
return m_arena->allocate<func_decl_node>(std::move(func));
}
func_decl_node::def_s def;
while (m_lexer.peek_token().type == token::Pre || m_lexer.peek_token().type == token::Post) {
auto token = m_lexer.next_token();
eat_token(token::LParen);
auto* expr = parse_expr();
eat_token(token::RParen);
switch (token.type) {
case token::Pre: {
def.preConds.push_back(expr);
} break;
case token::Post: {
def.postConds.push_back(expr);
} break;
default: throw std::runtime_error("unreachable");
}
}
def.body = parse_comp();
func.def = std::move(def);
return m_arena->allocate<func_decl_node>(std::move(func));
}
var_decl_node var;
var.name = std::string(first.value.string);
eat_token(token::Colon); // TODO: Auto-deduce the type
var.type = parse_type();
if (eat_token(token::Equals, token::Semicolon).type == token::Equals) {
var.init = parse_expr();
eat_token(token::Semicolon);
}
return m_arena->allocate<var_decl_node>(std::move(var));
}
expr_node* parser::parse_expr() {
return parse_expr_right(parse_expr_unary());
}
ast_type parser::parse_type() {
auto token =
eat_token(token::S8, token::U8, token::S16, token::U16, token::S32, token::U32, token::S64, token::U64);
switch (token.type) {
case token::S8: return { ast_type::S8 };
case token::U8: return { ast_type::U8 };
case token::S16: return { ast_type::S16 };
case token::U16: return { ast_type::U16 };
case token::S32: return { ast_type::S32 };
case token::U32: return { ast_type::U32 };
case token::S64: return { ast_type::S64 };
case token::U64: return { ast_type::U64 };
default: throw std::runtime_error("unreachable");
}
}
comp_stmt_node parser::parse_comp() {
comp_stmt_node comp;
eat_token(token::LBrace);
while (m_lexer.peek_token().type != token::EndOfFile && m_lexer.peek_token().type != token::RBrace) {
comp.stmts.push_back(parse_stmt());
}
eat_token(token::RBrace);
return comp;
}
expr_node* parser::parse_expr_primary() {
auto token = eat_token(token::Identifier, token::LParen, token::If, token::Integer, token::Char);
switch (token.type) {
case token::Identifier: {
return m_arena->allocate<var_read_expr_node>(std::string(token.value.string));
}
case token::LParen: {
group_expr_node group;
group.inner = parse_expr();
eat_token(token::RParen);
return m_arena->allocate<group_expr_node>(std::move(group));
}
case token::If: {
if_expr_node ifExpr;
eat_token(token::LParen);
ifExpr.cond = parse_expr();
eat_token(token::RParen);
ifExpr.thenExpr = parse_expr();
eat_token(token::Else);
ifExpr.elseExpr = parse_expr();
return m_arena->allocate<if_expr_node>(std::move(ifExpr));
}
case token::Integer: {
return m_arena->allocate<int_lit_node>(int_lit_node(token.value.integer));
}
case token::Char: {
return m_arena->allocate<char_lit_node>(char_lit_node(token.value.character));
}
default: throw std::runtime_error("unreachable");
}
}
expr_node* parser::parse_expr_unary() {
static std::unordered_map<token_t, unary_op_expr_node::unary_op_type> s_prefixOps = {
{ token::Plus, unary_op_expr_node::Positive },
{ token::Minus, unary_op_expr_node::Negative },
{ token::DblPlus, unary_op_expr_node::PreInc },
{ token::DblMinus, unary_op_expr_node::PreDec },
{ token::Tilde, unary_op_expr_node::BinNot },
{ token::ExMark, unary_op_expr_node::Not },
{ token::Sizeof, unary_op_expr_node::Sizeof },
{ token::Pointerof, unary_op_expr_node::Pointerof },
{ token::Lengthof, unary_op_expr_node::Lengthof },
};
static std::unordered_map<token_t, unary_op_expr_node::unary_op_type> s_postfixOps = {
{ token::DblPlus, unary_op_expr_node::PostInc },
{ token::DblMinus, unary_op_expr_node::PostDec },
};
auto it = s_prefixOps.find(m_lexer.peek_token().type);
if (it == s_prefixOps.end()) {
auto* expr = parse_expr_primary();
while (true) {
auto postIt = s_postfixOps.find(m_lexer.peek_token().type);
if (postIt == s_postfixOps.end()) return expr;
m_lexer.next_token();
unary_op_expr_node unary;
unary.lhs = expr;
unary.type = postIt->second;
expr = m_arena->allocate<unary_op_expr_node>(std::move(unary));
}
}
auto token = m_lexer.next_token();
unary_op_expr_node unary;
unary.lhs = parse_expr_unary();
unary.type = it->second;
return m_arena->allocate<unary_op_expr_node>(std::move(unary));
}
expr_node* parser::parse_expr_right(expr_node* lhs, std::uint32_t precedence) {
struct op_info {
enum type_e {
Binary = 0,
FunctionCall,
} type;
std::uint32_t precedence;
union {
binary_op_expr_node::binary_op_type binary;
};
bool right = false;
};
static std::unordered_map<token_t, op_info> s_ops = {
{ token::Plus, { op_info::Binary, 4, binary_op_expr_node::Add } },
{ token::Minus, { op_info::Binary, 4, binary_op_expr_node::Sub } },
{ token::Star, { op_info::Binary, 3, binary_op_expr_node::Mul } },
{ token::Slash, { op_info::Binary, 3, binary_op_expr_node::Div } },
{ token::Percent, { op_info::Binary, 3, binary_op_expr_node::Mod } },
{ token::DblLT, { op_info::Binary, 5, binary_op_expr_node::Shl } },
{ token::DblGT, { op_info::Binary, 5, binary_op_expr_node::Shr } },
{ token::Ampersand, { op_info::Binary, 8, binary_op_expr_node::BinAnd } },
{ token::Pipe, { op_info::Binary, 10, binary_op_expr_node::BinOr } },
{ token::Hat, { op_info::Binary, 9, binary_op_expr_node::BinXor } },
{ token::DblAmpersand, { op_info::Binary, 11, binary_op_expr_node::And } },
{ token::DblPipe, { op_info::Binary, 12, binary_op_expr_node::Or } },
{ token::DblEquals, { op_info::Binary, 7, binary_op_expr_node::Equals } },
{ token::ExEquals, { op_info::Binary, 7, binary_op_expr_node::NotEquals } },
{ token::LessThan, { op_info::Binary, 6, binary_op_expr_node::LessThan } },
{ token::LessEquals, { op_info::Binary, 6, binary_op_expr_node::LessEquals } },
{ token::GreaterThan, { op_info::Binary, 6, binary_op_expr_node::GreaterThan } },
{ token::GreaterEquals, { op_info::Binary, 6, binary_op_expr_node::GreaterEquals } },
{ token::LParen, { op_info::FunctionCall, 1 } },
};
while (true) {
auto it = s_ops.find(m_lexer.peek_token().type);
if (it == s_ops.end() || it->second.precedence >= precedence) return lhs;
auto op = it->second;
m_lexer.next_token();
if (op.type == op_info::FunctionCall) {
func_call_expr_node funcCall;
funcCall.lhs = lhs;
if (m_lexer.peek_token().type != token::RParen) {
do {
funcCall.args.push_back(parse_expr());
} while (eat_token(token::Comma, token::RParen).type == token::Comma);
} else {
m_lexer.next_token();
}
lhs = m_arena->allocate<func_call_expr_node>(std::move(funcCall));
} else {
binary_op_expr_node binary;
binary.lhs = lhs;
binary.rhs = parse_expr_unary();
binary.type = op.binary;
auto nextIt = s_ops.find(m_lexer.peek_token().type);
if (nextIt != s_ops.end()) {
binary.rhs = parse_expr_right(binary.rhs, op.precedence + (op.right ? 1 : 0));
}
lhs = m_arena->allocate<binary_op_expr_node>(std::move(binary));
}
}
}
} // namespace furc
+31
View File
@@ -0,0 +1,31 @@
#ifndef LIBFURC
#include "furc/front/lexer.hpp"
#include "furc/front/parser.hpp"
#include "furc/middle/ir.hpp"
#include "furc/middle/reg_gen.hpp"
#include "furc/middle/ssa.hpp"
#include "furlang/arena.hpp"
int main(void) {
furlang::arena arena;
std::string_view content = R"(
func main(argc: u64) -> s32 {
x: s32 = 1 + 2 * 3;
return if (x == 9) 1 else 0;
}
)";
furc::lexer lexer = { "<AK>", content };
furc::parser parser = { std::move(lexer), arena };
furc::ir_module irModule = furc::ir_generator::generate(parser.parse());
for (auto& func : irModule.functions) {
furc::ssa ssa(*func);
furc::reg_gen gen(*func, ssa);
}
return 0;
}
#endif // LIBFURC
+254
View File
@@ -0,0 +1,254 @@
#include "furc/middle/ir.hpp"
#include "furc/front/ast.hpp"
#include <stdexcept>
#include <utility>
namespace furc {
static ir_type ast_type_to_ir(const ast_type& type) {
switch (type.type) {
case ast_type::Void: return { ir_type::Void };
case ast_type::S8: return { ir_type::S8 };
case ast_type::U8: return { ir_type::U8 };
case ast_type::S16: return { ir_type::S16 };
case ast_type::U16: return { ir_type::U16 };
case ast_type::S32: return { ir_type::S32 };
case ast_type::U32: return { ir_type::U32 };
case ast_type::S64: return { ir_type::S64 };
case ast_type::U64: return { ir_type::U64 };
}
throw std::runtime_error("unreachable");
}
void ir_generator::visit_comp_stmt_node(const comp_stmt_node& node) {
// TODO: Introduce scopes for statements to naturally allow variable shadowing.
for (const auto& stmt : node.stmts) {
stmt->accept(*this);
}
}
void ir_generator::visit_if_stmt_node(const if_stmt_node& node) {
node.cond->accept(*this);
auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0);
context().new_next();
node.thenBranch->accept(*this);
context().new_next();
branch->destination->value.blockPair.second = context().blockIdx; // NOLINT
if (node.elseBranch != nullptr) {
node.elseBranch->accept(*this);
context().new_next();
}
}
void ir_generator::visit_while_stmt_node(const while_stmt_node& node) {
context().new_next();
auto header = context().blockIdx;
node.cond->accept(*this);
auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0);
context().new_next();
auto body = context().blockIdx;
node.body->accept(*this);
if (!context().blockPtr->is_terminated()) context().terminate(header);
context().new_next();
branch->destination->value.blockPair.second = context().blockIdx; // NOLINT
}
void ir_generator::visit_return_stmt_node(const return_stmt_node& node) {
if (node.value == nullptr) {
context().terminate();
} else {
node.value->accept(*this);
context().terminate(context().last_register());
}
}
void ir_generator::visit_var_decl_node(const var_decl_node& node) {
const auto* var = m_scope->allocate(m_module.arena, node.name, ast_type_to_ir(node.type));
if (node.init != nullptr) {
if (m_context.empty()) {
m_initContext.new_next();
m_context.push(std::move(m_initContext));
node.init->accept(*this);
context().add_instr(ir_instruction{ ir_instruction::Move, var->operand(), { context().last_register() } });
m_initContext = std::move(m_context.top());
m_context.pop();
} else {
node.init->accept(*this);
context().add_instr(ir_instruction{ ir_instruction::Move, var->operand(), { context().last_register() } });
}
}
}
void ir_generator::visit_func_decl_node(const func_decl_node& node) {
ir_function function;
function.previous = m_scope;
function.name = node.name;
for (const auto& param : node.params) {
function.params.push_back(ast_type_to_ir(param.type));
function.allocate(m_module.arena, param.name, ast_type_to_ir(param.type));
}
function.retType = ast_type_to_ir(node.type);
if (node.def.has_value()) {
auto* func = m_module.functions.emplace_back(m_module.arena.allocate<ir_function>(std::move(function)));
m_context.emplace(func);
m_scope = func;
node.def->body.accept(*this);
m_scope = m_scope->previous;
m_context.pop();
}
}
void ir_generator::visit_var_read_expr_node(const var_read_expr_node& node) {
const auto* var = m_scope->variable(node.name);
if (var == nullptr) throw std::runtime_error("unknown variable");
context().add_instr(ir_instruction{ ir_instruction::Move, context().next_register(), { var->operand() } });
}
void ir_generator::visit_func_call_expr_node(const func_call_expr_node& node) {
throw std::runtime_error("not implemented");
}
void ir_generator::visit_group_expr_node(const group_expr_node& node) {
node.inner->accept(*this);
}
void ir_generator::visit_binary_op_expr_node(const binary_op_expr_node& node) {
node.lhs->accept(*this);
auto lhs = context().last_register();
node.rhs->accept(*this);
auto rhs = context().last_register();
switch (node.type) {
case binary_op_expr_node::Add:
context().add_instr(ir_instruction{ ir_instruction::Add, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Sub:
context().add_instr(ir_instruction{ ir_instruction::Sub, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Mul:
context().add_instr(ir_instruction{ ir_instruction::Mul, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Div:
context().add_instr(ir_instruction{ ir_instruction::Div, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Mod:
context().add_instr(ir_instruction{ ir_instruction::Mod, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Shl:
context().add_instr(ir_instruction{ ir_instruction::Shl, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Shr:
context().add_instr(ir_instruction{ ir_instruction::Shr, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::BinAnd:
context().add_instr(ir_instruction{ ir_instruction::BinAnd, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::BinOr:
context().add_instr(ir_instruction{ ir_instruction::BinOr, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::BinXor:
context().add_instr(ir_instruction{ ir_instruction::BinXor, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::And:
context().add_instr(ir_instruction{ ir_instruction::And, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Or:
context().add_instr(ir_instruction{ ir_instruction::Or, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::Equals:
context().add_instr(ir_instruction{ ir_instruction::Eq, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::NotEquals:
context().add_instr(ir_instruction{ ir_instruction::NotEq, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::LessThan:
context().add_instr(ir_instruction{ ir_instruction::LessThan, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::LessEquals:
context().add_instr(ir_instruction{ ir_instruction::LessEq, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::GreaterThan:
context().add_instr(ir_instruction{ ir_instruction::GreaterThan, context().next_register(), { lhs, rhs } });
return;
case binary_op_expr_node::GreaterEquals:
context().add_instr(ir_instruction{ ir_instruction::GreaterEq, context().next_register(), { lhs, rhs } });
return;
}
throw std::runtime_error("unreachable");
}
void ir_generator::visit_unary_op_expr_node(const unary_op_expr_node& node) {
if (node.type == unary_op_expr_node::PostInc || node.type == unary_op_expr_node::PostDec) {
node.lhs->accept(*this);
auto lhs = context().last_register();
node.lhs->accept(*this);
context().add_instr(node.type == unary_op_expr_node::PostInc ? ir_instruction::Increment
: ir_instruction::Decrement,
context().last_register());
return;
}
node.lhs->accept(*this);
auto lhs = context().last_register();
switch (node.type) {
case unary_op_expr_node::Positive: context().add_instr(ir_instruction::Positive, lhs);
case unary_op_expr_node::Negative: context().add_instr(ir_instruction::Negative, lhs);
case unary_op_expr_node::PreInc: context().add_instr(ir_instruction::Increment, lhs);
case unary_op_expr_node::PreDec: context().add_instr(ir_instruction::Decrement, lhs);
case unary_op_expr_node::BinNot: context().add_instr(ir_instruction::BinNot, lhs);
case unary_op_expr_node::Not: context().add_instr(ir_instruction::Not, lhs);
case unary_op_expr_node::Sizeof: context().add_instr(ir_instruction::Sizeof, lhs);
case unary_op_expr_node::Pointerof: context().add_instr(ir_instruction::Pointerof, lhs);
case unary_op_expr_node::Lengthof: context().add_instr(ir_instruction::Lenof, lhs);
case unary_op_expr_node::PostInc:
case unary_op_expr_node::PostDec: return;
}
throw std::runtime_error("unreachable");
}
void ir_generator::visit_if_expr_node(const if_expr_node& node) {
node.cond->accept(*this);
auto* branch = context().terminate(context().last_register(), context().blockIdx + 1, 0);
context().new_next();
auto thenBranch = context().blockIdx;
node.thenExpr->accept(*this);
auto thenReg = context().last_register();
context().new_next();
branch->destination->value.blockPair.second = context().blockIdx; // NOLINT
node.elseExpr->accept(*this);
auto elseReg = context().last_register();
auto resReg = context().next_register();
context().add_instr(ir_instruction{ ir_instruction::Move, resReg, { elseReg } });
context().new_next();
auto epilogue = context().blockIdx;
context().go(thenBranch);
context().add_instr(ir_instruction{ ir_instruction::Move, resReg, { thenReg } });
context().terminate(epilogue);
context().go(epilogue);
}
void ir_generator::visit_int_lit_node(const int_lit_node& node) {
context().add_instr(ir_instruction{ ir_instruction::Move,
context().next_register(),
{ ir_operand{ ir_operand::Integer, node.value } } });
}
void ir_generator::visit_char_lit_node(const char_lit_node& node) {
context().add_instr(ir_instruction{ ir_instruction::Move,
context().next_register(),
{ ir_operand{ ir_operand::Integer, static_cast<std::uint64_t>(node.value) } } });
}
} // namespace furc
+307
View File
@@ -0,0 +1,307 @@
/**
* Sources:
* - Practical Improvements to the Construction and Deconstruction of Static Single Assignment Form:
* https://web.archive.org/web/20100607003509/http://www.cs.rice.edu/~harv/my_papers/ssa.pdf
* - A Simple, Fast Dominance Algorithm:
* https://www.researchgate.net/publication/2569680_A_Simple_Fast_Dominance_Algorithm
*/
#include "furc/middle/ssa.hpp"
#include "furc/middle/ir.hpp"
#include <algorithm>
#include <cstddef>
#include <limits>
#include <stack>
#include <stdexcept>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace furc {
void ssa::compute_cfg(const std::vector<ir_basic_block>& irBlocks, std::vector<cfg_block>& cfgBlocks) {
cfgBlocks.resize(irBlocks.size());
for (std::size_t i = 0; i < irBlocks.size(); ++i) {
const auto& block = irBlocks[i];
if (block.instructions.empty()) continue;
const auto& termInstr = block.instructions.back();
switch (termInstr.type) {
case ir_instruction::Branch: {
const auto& dst = termInstr.destination.value();
assert(dst.type == ir_operand::Block);
cfgBlocks[dst.value.block].preds.insert(i);
cfgBlocks[i].sucs.insert(dst.value.block);
} break;
case ir_instruction::BranchCond: {
const auto& dst = termInstr.destination.value();
assert(dst.type == ir_operand::BlockPair);
cfgBlocks[dst.value.blockPair.first].preds.insert(i);
cfgBlocks[dst.value.blockPair.second].preds.insert(i);
cfgBlocks[i].sucs.insert(dst.value.blockPair.first);
cfgBlocks[i].sucs.insert(dst.value.blockPair.second);
} break;
default: break;
}
}
}
void ssa::collect_registers(const std::vector<ir_basic_block>& irBlocks,
std::vector<register_info>& registers,
std::unordered_set<std::uint64_t>& globals) {
for (std::size_t i = 0; i < irBlocks.size(); ++i) {
const auto& block = irBlocks[i];
for (const auto& instr : block.instructions) {
for (const auto& src : instr.sources) {
if (src.type != ir_operand::Register) continue;
const auto& reg = registers.at(src.value.reg.name);
if (reg.sites.find(i) != reg.sites.end()) continue;
globals.insert(src.value.reg.name);
}
if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
registers[instr.destination->value.reg.name].sites.insert(i);
}
}
}
void ssa::build_dtree(const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::size_t>& order) {
ssaBlocks[order.front()].idom = order.front();
bool changed = true;
while (changed) {
changed = false;
for (auto it = order.begin() + 1; it != order.end(); ++it) {
static constexpr std::uint64_t INVALID = std::numeric_limits<std::uint64_t>::max();
std::uint64_t newIdom = -1;
bool found = false;
for (std::uint64_t pred : cfgBlocks[*it].preds) {
if (ssaBlocks[pred].idom == INVALID) continue;
newIdom = found ? intersect(ssaBlocks, pred, newIdom) : pred;
found = true;
}
if (ssaBlocks[*it].idom != newIdom) {
ssaBlocks[*it].idom = newIdom;
changed = true;
}
}
}
}
void ssa::compute_dfrontiers(const std::vector<cfg_block>& cfgBlocks, std::vector<ssa_block>& ssaBlocks) {
for (std::uint64_t i = 0; i < ssaBlocks.size(); ++i) {
if (cfgBlocks[i].preds.size() < 2) continue;
const auto& cfgBlock = cfgBlocks[i];
auto& ssaBlock = ssaBlocks[i];
for (std::uint64_t worker : cfgBlock.preds) {
while (worker != ssaBlock.idom) {
ssaBlocks[worker].df.insert(i);
worker = ssaBlocks[worker].idom;
}
}
}
}
void ssa::compute_rpo(std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
std::vector<std::size_t>& order) {
std::unordered_set<std::size_t> visited;
if (!cfgBlocks.empty()) rpo_dfs(visited, order, 0, cfgBlocks);
std::reverse(order.begin(), order.end());
ssaBlocks.resize(cfgBlocks.size());
for (std::size_t i = 0; i < order.size(); ++i) {
ssaBlocks[order[i]].order = i;
}
}
void ssa::ssaification(std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
const std::vector<register_info>& registers,
const std::unordered_set<std::uint64_t>& globals) {
std::vector<std::uint64_t> worklist;
for (std::uint64_t i = 0; i < registers.size(); ++i) {
const auto& reg = registers[i];
if (reg.sites.size() < 2 || globals.find(i) == globals.end()) continue;
worklist.insert(worklist.end(), reg.sites.begin(), reg.sites.end());
std::unordered_set<std::uint64_t> done;
while (!worklist.empty()) {
const auto blockIdx = worklist.back();
worklist.pop_back();
for (auto frontier : ssaBlocks[blockIdx].df) {
if (done.find(frontier) != done.end()) continue;
done.insert(frontier);
auto& target = irBlocks[frontier];
ir_instruction instr = { ir_instruction::Phi, ir_operand{ ir_operand::Register, i } };
for (const auto& pred : cfgBlocks[frontier].preds)
instr.sources.emplace_back(ir_operand::PhiPair, i, pred);
target.instructions.emplace(target.instructions.begin(), std::move(instr));
if (reg.sites.find(frontier) == reg.sites.end()) worklist.push_back(frontier);
}
}
}
}
void ssa::rename(std::vector<ir_basic_block>& irBlocks,
std::size_t regCount,
const std::vector<cfg_block>& cfgBlocks,
std::vector<ssa_block>& ssaBlocks,
const std::vector<std::uint64_t>& order) {
std::vector<std::uint64_t> counters;
std::vector<std::stack<std::uint64_t>> stacks;
counters.resize(regCount);
stacks.resize(regCount);
for (auto it = order.begin() + 1; it != order.end(); ++it) {
std::uint64_t parent = ssaBlocks[*it].idom;
if (parent != std::numeric_limits<std::uint64_t>::max()) ssaBlocks[parent].children.emplace(*it);
}
rename_rec(counters, stacks, irBlocks, cfgBlocks, ssaBlocks, order.front());
}
void ssa::rename_rec(std::vector<std::uint64_t>& counters,
std::vector<std::stack<std::uint64_t>>& stacks,
std::vector<ir_basic_block>& irBlocks,
const std::vector<cfg_block>& cfgBlocks,
const std::vector<ssa_block>& ssaBlocks,
std::size_t blockIdx) {
std::unordered_map<std::uint64_t, std::size_t> pushed;
auto& block = irBlocks[blockIdx];
for (auto& instr : block.instructions) {
if (instr.type == ir_instruction::Phi) {
auto reg = instr.destination->value.reg.name;
stacks[reg].push(instr.destination->value.reg.ver = counters[reg]++);
++pushed[reg];
continue;
}
for (auto& op : instr.sources) {
if (op.type != ir_operand::Register) continue;
auto reg = op.value.reg.name;
op.value.reg.ver = stacks[reg].top();
}
if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
auto reg = instr.destination->value.reg.name;
stacks[reg].push(instr.destination->value.reg.ver = counters[reg]++);
++pushed[reg];
}
for (auto succIdx : cfgBlocks[blockIdx].sucs) {
auto& succ = irBlocks[succIdx];
for (auto& instr : succ.instructions) {
if (instr.type != ir_instruction::Phi) break;
for (auto& op : instr.sources) {
if (op.value.phiPair.block != blockIdx) continue;
op.value.phiPair.reg.ver = stacks[op.value.phiPair.reg.name].top();
}
}
}
for (std::uint64_t child : ssaBlocks[blockIdx].children)
rename_rec(counters, stacks, irBlocks, cfgBlocks, ssaBlocks, child);
for (auto [reg, count] : pushed)
while ((count--) > 0)
stacks[reg].pop();
}
void ssa::rpo_dfs(std::unordered_set<std::size_t>& visited,
std::vector<std::size_t>& order,
std::size_t block,
const std::vector<cfg_block>& blocks) {
visited.insert(block);
for (auto succ : blocks[block].sucs) {
if (visited.find(succ) != visited.end()) continue;
rpo_dfs(visited, order, succ, blocks);
}
order.push_back(block);
}
std::size_t ssa::intersect(std::vector<ssa_block>& m_blocks, std::size_t b1, std::size_t b2) {
while (b1 != b2) {
while (m_blocks[b1].order > m_blocks[b2].order)
b1 = m_blocks[b1].idom;
while (m_blocks[b2].order > m_blocks[b1].order)
b2 = m_blocks[b2].idom;
}
return b1;
}
// // 5. Renaming
// std::vector<std::uint64_t> counters;
// std::vector<std::stack<std::uint64_t>> stacks;
//
// counters.resize(func.regCount);
// stacks.resize(func.regCount);
//
// for (std::size_t i = 1; i < order.size(); ++i) {
// std::size_t parent = blocks[order[i]].idom;
// if (parent != std::numeric_limits<std::size_t>::max()) blocks[parent].children.emplace(order[i]);
// }
//
// auto rename = [&counters, &stacks, &blocks, &func](auto& self, std::size_t blockIdx) -> void {
// std::unordered_map<std::size_t, std::size_t> pushed;
//
// auto& block = func.blocks[blockIdx];
// for (auto& instr : block.instructions) {
// if (instr.type == ir_instruction::Phi) {
// auto reg = instr.destination->value.reg.name;
// auto idx = counters[reg]++;
// instr.destination->value.reg.ver = idx;
// stacks[reg].push(idx);
// ++pushed[reg];
// continue;
// }
//
// for (auto& op : instr.sources) {
// if (op.type != ir_operand::Register) continue;
// auto reg = op.value.reg.name;
// op.value.reg.ver = stacks[reg].top();
// }
//
// if (!instr.destination.has_value() || instr.destination->type != ir_operand::Register) continue;
// auto reg = instr.destination->value.reg.name;
// auto idx = counters[reg]++;
// instr.destination->value.reg.ver = idx;
// stacks[reg].push(idx);
// ++pushed[reg];
// }
//
// for (auto succIdx : blocks[blockIdx].sucs) {
// auto& succ = func.blocks[succIdx];
// for (auto& instr : succ.instructions) {
// if (instr.type != ir_instruction::Phi) break;
// for (auto& op : instr.sources) {
// if (op.value.phiPair.block != blockIdx) continue;
// op.value.phiPair.reg.ver = stacks[op.value.phiPair.reg.name].top();
// }
// }
// }
//
// for (std::size_t child : blocks[blockIdx].children)
// self(self, child);
//
// for (auto [reg, count] : pushed)
// while (count--)
// stacks[reg].pop();
// };
// rename(rename, order.front());
// }
} // namespace furc
+5
View File
@@ -0,0 +1,5 @@
file(GLOB_RECURSE FURDB_SRCS "${CMAKE_SOURCE_DIR}/src/furdb/**.cpp")
file(GLOB_RECURSE FURDB_HDRS "${CMAKE_SOURCE_DIR}/include/furdb/**.hpp")
add_executable(furdb ${FURDB_SRCS} ${FURDB_HDRS})
target_include_directories(furdb PRIVATE ${CMAKE_SOURCE_DIR}/include/)
target_link_libraries(furdb PUBLIC furlang libfurc libfurvm isocline)

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