├── .github └── workflows │ └── build.yaml ├── .gitignore ├── .ocamlformat ├── LICENSE ├── Makefile ├── README.md ├── combc.opam ├── dune-project ├── dune-workspace ├── src ├── ast │ ├── bexp.ml │ ├── circuit.ml │ ├── combinator.ml │ ├── dune │ └── expression.ml ├── compiler │ ├── compile.ml │ ├── composition.ml │ ├── config.ml │ ├── ctxt.ml │ ├── directive.ml │ ├── dune │ ├── firstPhase.ml │ ├── json.ml │ ├── layout.ml │ ├── optimize.ml │ ├── pattern.ml │ └── sig_list.ml ├── dune ├── encode.ml ├── main.ml ├── parser │ ├── dune │ ├── lexer.mll │ ├── parse.ml │ ├── parser.messages │ └── parser.mly └── utils │ ├── dune │ └── utils.ml ├── tools └── gen_signals.py └── vscode_extension ├── .vscodeignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example.combc ├── language-configuration.json └── package.json /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | name: Build and publish 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | build: 10 | name: "Build" 11 | permissions: 12 | contents: write 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | include: 17 | - os: macOS-11 18 | release_name: combc-mac 19 | - os: ubuntu-latest 20 | release_name: combc-ubuntu 21 | - os: windows-latest 22 | release_name: combc-windows.exe 23 | ocaml-compiler: 24 | - 4.13.1 25 | os: 26 | - macOS-11 27 | - ubuntu-latest 28 | - windows-latest 29 | 30 | 31 | runs-on: ${{ matrix.os }} 32 | 33 | steps: 34 | - name: Checkout code 35 | uses: actions/checkout@v3 36 | 37 | - name: Use OCaml ${{ matrix.ocaml-compiler }} 38 | uses: ocaml/setup-ocaml@v2 39 | with: 40 | ocaml-compiler: ${{ matrix.ocaml-compiler }} 41 | 42 | - name: Install deps 43 | run: opam install . --deps-only 44 | 45 | - name: Build 46 | run: opam exec -- dune build 47 | 48 | - name: Copy executable 49 | run: cp _build/release-unix/src/main.exe ${{ matrix.release_name }} 50 | 51 | - name: Upload artifacts 52 | uses: actions/upload-artifact@v2 53 | with: 54 | name: ${{ matrix.release_name }} 55 | path: ${{ matrix.release_name }} 56 | 57 | publish: 58 | name: Publish 59 | needs: build 60 | permissions: 61 | contents: write 62 | runs-on: ubuntu-latest 63 | steps: 64 | - name: Checkout code 65 | uses: actions/checkout@v3 66 | with: 67 | fetch-depth: 0 68 | 69 | - name: Download artifacts 70 | uses: actions/download-artifact@v3 71 | 72 | - name: Publish 73 | uses: softprops/action-gh-release@v1 74 | with: 75 | name: "Executables" 76 | fail_on_unmatched_files: true 77 | files: | 78 | combc-ubuntu/combc-ubuntu 79 | combc-mac/combc-mac 80 | combc-windows.exe/combc-windows.exe 81 | 82 | 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | *.exe 12 | *.fpl 13 | *.combc 14 | *.json 15 | 16 | .vscode/ 17 | 18 | # executable 19 | combc 20 | 21 | # ocamlbuild working directory 22 | _build/ 23 | 24 | # ocamlbuild targets 25 | *.byte 26 | *.native 27 | 28 | # oasis generated files 29 | setup.data 30 | setup.log 31 | 32 | # Merlin configuring file for Vim and Emacs 33 | .merlin 34 | 35 | # Dune generated files 36 | *.install 37 | 38 | # Local OPAM switch 39 | _opam/ 40 | 41 | !vscode_extension/*.json 42 | !vscode_extension/*.combc -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- 1 | profile = default 2 | version = 0.26.0 3 | 4 | exp-grouping=preserve 5 | margin=100 6 | break-cases=fit-or-vertical 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean utop build run 2 | 3 | all: main.exe 4 | 5 | %.exe: src/main.ml 6 | dune build 7 | cp src/$@ . 8 | rm src/$@ 9 | 10 | .FORCE: 11 | 12 | format: 13 | dune fmt 14 | 15 | build: 16 | dune fmt 17 | dune build 18 | 19 | build-dev: 20 | dune fmt 21 | dune build 22 | cp _build/default/src/main.exe combc 23 | chmod +w combc 24 | 25 | build-release: 26 | dune fmt 27 | dune build 28 | cp _build/release-unix/src/main.exe combc 29 | chmod +w combc 30 | 31 | clean: 32 | dune clean 33 | rm -f combc 34 | rm -rf *.exe 35 | 36 | utop: main.exe 37 | dune utop 38 | 39 | 40 | generate-messages: 41 | dune build @generate-parser-messages 42 | 43 | 44 | update-messages: 45 | dune build @update-parser-messages 46 | 47 | run: 48 | dune exec --context=default combc code.fpl 49 | 50 | run-release: 51 | dune exec --context=release-unix combc code.fpl 52 | 53 | run-js: 54 | dune exec --context=release-js combc code.fpl -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CombinatorC 2 | A C-style programming language that compiles into Factorio combinator blueprints. There have been similar projects such as [Factoriogen](https://github.com/Jobarion/factoriogen), but I have a significantly different vision for CombinatorC. 3 | 4 | I intend to create a language that is useful for designing complex circuits that are actually used for real Factorio gameplay, and not just as an academic curiosity. My original plan was to create a C-style language, but as development continues, it is likely that the language will change into something that more resembles something like Verilog in order to make it more practical. As such, I might the change the name, stay tuned! 5 | 6 | The compiler is built in OCaml, and uses [Dune](https://dune.build/). I also created a [language extension for VS Code](https://marketplace.visualstudio.com/items?itemName=OwenSimon.combinatorc) that does syntax highlighting for CombinatorC program files with the `.combc` extension. 7 | 8 | **Executables for Ubuntu, Mac, and Windows can be found in the releases section of the repository.** 9 | 10 | To build from source, install dependencies using: 11 | 12 | opam install . --deps-only 13 | 14 | Then, run `dune build`. 15 | 16 | ## Usage 17 | 18 | Simply supply the file name containing the CombinatorC program as a command line argument. The compiler will print a Factorio blueprint string to `stdout`. 19 | 20 | For example, on Windows: 21 | 22 | combc-windows.exe input.txt 23 | 24 | Optionally, you can set the flag `--output-json` to output a JSON string representing the blueprint instead of a Factorio blueprint string. 25 | 26 | The output blueprint will have the name of the input file. 27 | 28 | ### Running on Windows 29 | 30 | If you encounter an error that states you are missing "zlib1.dll", here is the fix: 31 | 32 | - Download the ZIP file available [here](https://www.dll-files.com/download/7e4b778eb31fdf5a52c7476bb78bdb1d/zlib1.dll.html?c=alFOcnFjelJTMmF1ZjhhV01CK0NZdz09) 33 | - Unzip it, and place "zlib1.dll" into C:\Windows\System32 34 | 35 | This error may be possible on other operating systems. If you encounter it, please let me know in the "Issues" tab, and follow the steps for installing dynamically linked libraries on your OS. 36 | 37 | ### Running on Mac 38 | 39 | By default MacOS will block executables from unknown developers. To get around this, navigate to the install location and run the following commands: 40 | 41 | xattr -d com.apple.quarantine combc-mac 42 | chmod +x combc-mac 43 | 44 | Now, you should be able to run the compiler normally. 45 | 46 | ## Language 47 | 48 | A CombinatorC program starts with optional compiler directives, and is followed by a list of commands. Commands are terminated by semicolons. 49 | 50 | Programs will be compiled into a set of circuits, each including an input pole and output pole. Wire the intended circuit inputs to the input pole. 51 | 52 | Single line comments can be written using `//`, and multiline comments can be started with `/*` and closed with `*/`. 53 | 54 | ### Commands 55 | 56 | CombinatorC currently supports 3 different commands. They are: 57 | 58 | - Cirucit binding 59 | - Variable definition 60 | - Output 61 | 62 | A CombinatorC program can have as many outputs as you want, each distinct output will produce a separate circuit. 63 | 64 | ### Circuit Bindings 65 | 66 | A circuit binding has the syntax `circuit : = `. Here is an example circuit binding: 67 | 68 | circuit my_circuit : D = (A + B - C) / 45; 69 | 70 | This produces a circuit with the output signal `D`. You can use then use the bound variable name "my_circuit" to refer to the circuit in future commands, such as outputs. 71 | 72 | Circuits can be marked as *concrete* by writing `concrete circuit` instead of `circuit` in a binding. This informs the compiler that you want the circuit to be considered separate from any other circuits it is composed with, and should not be merged in with those other circuits when the compiler lays down all the circuits on the Factorio grid. This isn't very useful unless you want the logical components of your circuit to be clearly separate for future purposes. I don't recommend using this, as it is a new feature and could be buggy. 73 | 74 | ### Variable Definition 75 | 76 | Temporary variables for use in the code can be defined with the following syntax: 77 | 78 | = 79 | 80 | Variables used in expressions must have already been defined earlier in the code before they can be used. Duplicate variable names are not allowed. Variables cannot be reassigned after creation (with the exception of the loop variable generated by for loops, which are automatically reassigned throughout the loop). 81 | 82 | The supported types are: 83 | 84 | - int 85 | - signal 86 | - condition 87 | - circuit 88 | - expression 89 | 90 | `int` types are numeric expressions, and `signal` types are signals. 91 | 92 | `condition` types are numeric expressions that can directly map to a decider combinator's config. This essentially means that it must be an expression of the form ` `, where `c_op` is either `==`, `!=`, `>`, `>=`, `<`, or `<=`, and `expression` is a numeric expression without any signals. 93 | 94 | `circuit` types are circuits, which can be the name of a circuit that was previously bound, a pattern call, or a `for` expression. 95 | 96 | 97 | `expression` types are simply any expression that serve as a sort of copy-paste shorthand for reusing the same block of logic. For example, the following two code snippets are equivalent: 98 | 99 | ``` 100 | circuit c1 : D = (A + B - C) / 45; 101 | circuit c2 : E = (A + B - C) - (G * H); 102 | ``` 103 | 104 | with an `expression` variable: 105 | 106 | ``` 107 | expression expr = A + B + C; 108 | circuit c1 : D = expr / 45; 109 | circuit c2 : E = expr - (G * H); 110 | ``` 111 | 112 | ### Output 113 | 114 | An output command has the following syntax: 115 | 116 | output 117 | 118 | This informs the compiler to output the provided circuit. Multiple output statements are allowed, except in the body of a `for` loop. 119 | 120 | To output a circuit at a specific location, use this syntax: 121 | 122 | output at (x,y) 123 | 124 | Where `x` and `y` are numeric expressions. The coordinate `(x,y)` represents the location on the Factorio grid the circuit will be placed at. Circuits will be placed relative to the origin `(0,0)` by default, so assigning a manual location will override that and instead place it at the provided location. 125 | 126 | Using the `at` syntax also automatically converts the circuit to a concrete circuit. This is only relevant for outputs within the body of a `for` loop. Within the body of a `for` loop, using the `at` syntax will place the circuits relative to the location of the overall circuit. This allows you to place circuits relative to each other, and then output the resulting circuit from the `for` loop somewhere else. 127 | 128 | Note that increasing `y` means moving *down* on the Factorio grid, so if you want to place a circuit above the other circuits, you should use a negative `y` value. 129 | 130 | ### Signals 131 | 132 | Signals in Factorio have type "virtual", "item", or "fluid". To express a virtual signal in CombinatorC, write `signal-`, for example `signal-2`. To express an item or fluid signal, write `-`, for example `item-copper-ore` or `fluid-water`. 133 | 134 | As a shorthand, single capital letters are interpreted as the corresponding virtual signal. For example, `A` is interpreted as `signal-A`. 135 | 136 | ### Numeric Expressions 137 | 138 | A numeric expression is either: 139 | 140 | - A signal (e.g. `B`) 141 | - An integer literal (e.g. `10`, `0`, `-1234`) 142 | - A boolean literal `true` or `false` (these are converted to integer literals `1` and `0` respectively) 143 | - A mathematical expression composed using operators 144 | - A conditional expression 145 | 146 | Signals used in numeric expressions are interpreted as input signals, and the resulting circuit will include a constant combinator wired to the input pole that initially sets every signal to `1`. 147 | 148 | #### Numeric Operators 149 | 150 | There are two types of operators, numeric operators, decision operators, and boolean operators. 151 | 152 | Numeric operators compute mathematical results as expected. Supported operations: `+`, `-`, `*`, `/`, `%`, `**`, `<<`, `>>`, `|`, `&`, `^` 153 | 154 | Note: `**` is exponentiation, and `^` is bitwise XOR. 155 | 156 | Example numeric expression: 157 | 158 | A + (B - 4) ** (3 >> 6 * C) 159 | 160 | Decision operators compute a boolean result `1` or `0` from numeric operands. Supported operations: `==`, `!=`, `>`, `>=`, `<`, `<=` 161 | 162 | Example decision expression: 163 | 164 | A > 4 165 | 166 | Boolean operators compute a boolean result `1` or `0`. Supported operations: `&&`, `||`, `!`, `===`, `!==` 167 | 168 | `===` and `!==` are logical equality and logical inequality respectively. These should be used if you want to see if the truth value of two values are equal. 169 | 170 | Example boolean expression: 171 | 172 | E && (A && !B) || (C === D) 173 | 174 | Numeric, decision, and boolean operators can be combined arbitrarily, for example: 175 | 176 | `10 + ((A || B) && !(C % 2) || (D <= 4)) - !5` 177 | 178 | This is because boolean values are equivalent to numeric values `1` or `0`, and input values `0` are interpreted as `false`, while any other input is interpreted as `true` for boolean operators. 179 | 180 | Note that this means `!!` is not necessarily equal to ``, since the first NOT operator will output either `0` or `1`. This means the expression `!!` can be used to yield the truth value `1` or `0` from an expression. 181 | 182 | Precedence of operators adheres to the standards of C++. 183 | 184 | ### Conditional Expressions 185 | 186 | Conditional expressions check to see if some expression evaluates to `true`, and if it does outputs some expression, otherwise it outputs some other expression. These are super useful! Conditionals have syntax `if then else `. Here is an example conditional expression: 187 | 188 | if A || (B && C) then 2 * B else 45 189 | 190 | Note that conditionals are themselves expressions, so they can be used as operands in other expresions. This also means they can be chained together to construct "else if" branches of a conditional. Here is an example of an expression using both of those properties: 191 | 192 | (E + 10) - 193 | if A || (B && C) then 194 | 2 * B 195 | else if A then 196 | A 197 | else 198 | D 199 | 200 | This expression checks to see if either `A` evaluates to true, or both `B` and `C` evaluate to true. If those conditions hold, it outputs two times the value of `B`. Otherwise, if `A` evaluates to true, the value of `A` is output. Otherwise, `D` is output. The result of that whole expression is subtracted from 10 added to `E`. 201 | 202 | You can also use the coalesce operator `??`, where ` ?? ` is shorthand for `if then else `. 203 | 204 | ### Note on Equality 205 | 206 | The logical equaliy and inequality operators (`===` and `!==`) should be used for comparing equality of booleans, not the numerical equality operators (`==` and `!=`). The need for these operators arises from the fact that booleans are just values `1` or `0`. So there is ambiguity in the intended meaning of an expression. Essentially, when you want the value you are handling to be treated as a boolean, use logical equality and inequality. 207 | 208 | ### Circuit Composition 209 | 210 | A circuit can be *composed* with another circuit to produce a single new circuit. There are two types of compositions: 211 | 212 | - Concatenation ( `@` ) 213 | - Union ( `\/` ) 214 | 215 | Concatenation connects the outputs of the first circuit to the inputs of the next circuit, and union combines all of the inputs of both circuits, feeds them all to both circuits, and combines the outputs of both circuits. Here is an example composition using concatentation, assuming the circuits "a" and "b" have been previously defined: 216 | 217 | a @ b 218 | 219 | Concatenation has a higher precedence than union, and concrete circuits composed with regular circuits will produce a resulting circuit that is concrete. 220 | 221 | There is a potential issue with union when the output signal of one circuit matches the input signal of the other circuit. The compiler will print a warning if this conflict is detected, as it may result in a circuit that doesn't function as intended. To avoid this, make sure the output and input signals are distinct when unioning circuits. 222 | 223 | ### Pattern Calls 224 | 225 | Circuits can be produced by calling a *pattern*. You can think of these as functions that return circuits. There are currently 3 built-in patterns: 226 | 227 | - `counter(int max_value, signal o_sig)` 228 | - `counter2(int max_value, signal i_sig, signal o_sig)` 229 | - `lamp(condition cnd)` 230 | 231 | `counter` starts at 0 and counts up by 1 every tick, and takes as an argument the maximum value it will count to before looping back to 0. There are 60 ticks in one second (unless you're playing Factorio on a TI-84 calculator or have a megabase). The argument `o_sig` is the signal that the counter will output. 232 | 233 | `counter2` functions much the same as `counter`, except it also takes the argument `i_sig` which is the input signal that it will use to increment by. This is useful if you want a counter that counts by some value other than 1. 234 | 235 | `lamp` outputs a lamp that has the enabled condition set to the provided argument `cnd`. It also outputs a concrete circuit, so you can layout lamps in some special way that will be preserved by the compiler. Any signals used in the condition are also output. 236 | 237 | User defined patterns are currently not supported, but I have plans to add them soon. 238 | 239 | ### For loops 240 | 241 | Circuits can also be produced by a `for` loop expression. They have the following syntax: 242 | 243 | = { 244 | ; 245 | ; 246 | ... 247 | ; 248 | } 249 | 250 | For loops will initialize an `int` variable with name "name" to the value `numeric_expression1`, and will increment it by 1 (if `to` is used) or decrement it by 1 if (`downto`) is used until the value of `numeric_expression2` is reached. At each iteration, the sequence of commands inside the braces will be executed (the bound variable "name" can be used in this context). The sequence of commands must have only a single output command. 251 | 252 | There are two types of `for` loops, `for_concat` and `for_union`. The output circuit produced by the command sequence will be composed (using concatenation with `for_concat`, and union with `for_union`) with the next circuit output by iteration, yielding a single final output circuit. 253 | 254 | ### Compiler Directives 255 | 256 | A compiler directive has the format `# `. Directives and arguments should either be all uppercase, or all lowercase. Arguments are specific to a given directive. 257 | 258 | Currently, there are only two supported directives: `PRIMARY` and `LAYOUT`. 259 | 260 | `PRIMARY` determines the wire color that the compiler will use to connect combinators, only using the other color when necessary. It takes arguments `RED` or `GREEN`. `RED` is the default setting. 261 | 262 | `LAYOUT` determines how circuits will be laid out on the Factorio grid and takes either the argument `IDENTITY` or `NAIVE`. 263 | 264 | - `IDENTITY` simply places the circuits in a line, wrapping around to the start if necessary, forming a rectangle. This is the default behavior. 265 | - `NAIVE` randomly places each combinator in a position that allows it to connect to all of its necessary inputs and outputs. Not recommended. 266 | 267 | A more sophisticated layout strategy using simulated annealing is planned for future versions. 268 | 269 | ### More Details 270 | 271 | More details on the grammar can be found by looking at [parser.mly](src/parser/parser.mly). 272 | 273 | ## Example Program 274 | 275 | Let's put it all together! Here's an example program: 276 | 277 | #PRIMARY GREEN 278 | 279 | // bounce light back and forth at varying rates 280 | 281 | int n = 10; 282 | int t = 100; 283 | 284 | signal ctr-out = signal-0; 285 | signal rate-out = R; 286 | int r = 7 * t; 287 | 288 | // increase the rate over time 289 | circuit div : rate-out = rate-out / t + 1; 290 | circuit rate = counter(r - 1, rate-out) @ div; 291 | 292 | // count up, to be used to determine which lamp is on 293 | circuit add : ctr-out = ctr-out + 1; 294 | circuit ctr = counter2(t * 2 - 1, rate-out, ctr-out) @ add; 295 | 296 | // a line of n lamps 297 | circuit lamps = for_concat i=0 to (n - 1) { 298 | condition cnd = ctr-out == i; 299 | output lamp(cnd) at (i, 0); 300 | }; 301 | 302 | int t_by_n = t / n; 303 | 304 | // first half of counter, light up from left, second half light up from right 305 | circuit cmpt : ctr-out = if ctr-out <= t then 306 | ctr-out / t_by_n 307 | else 308 | -(ctr-out / t_by_n - 2 * (n - 1)); 309 | 310 | output rate @ ctr @ cmpt; 311 | output lamps at (0, 8); 312 | 313 | 314 | This program produces line of lamps in which a light will bounce back and forth between the endpoints at a varying rate. Let's see it in action: 315 | 316 | https://user-images.githubusercontent.com/14896850/160725738-cd4cbfa9-7599-423e-9fa1-bfb1078e4e0f.mp4 317 | 318 | The circuits seen in the video were generated entirely using the above program, with the exception of wiring the output pole from the combinators to the input pole of the lamps. 319 | 320 | ## Future Plans 321 | 322 | I have a lot of potential ideas for how to extend the language and improve functionality, and would love to hear suggestions from people who design circuits in Factorio. What follows is the list of planned additions to the language and compiler: 323 | 324 | - User-defined patterns 325 | - More built-in useful circuit constructs, such as filters, latches, and memory cells 326 | - Improved layout strategy using simulated annealing to optimize space used 327 | - Import of external circuits via a blueprint string 328 | - Proper support of operations with wildcard signals (I don't know what this will look like) 329 | - Much more! Stay tuned. 330 | -------------------------------------------------------------------------------- /combc.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "0.2.11" 4 | synopsis: "C-style language that compiles to Factorio combinators" 5 | maintainer: ["Owen Simon "] 6 | authors: ["Owen Simon "] 7 | license: "GPL-3.0" 8 | homepage: "https://github.com/osimon8/CombinatorC" 9 | bug-reports: "https://github.com/osimon8/CombinatorC/issues" 10 | depends: [ 11 | "dune" {>= "3.0"} 12 | "menhir" 13 | "core_kernel" {= "v0.14.2"} 14 | "ocamlgraph" 15 | "yojson" 16 | "zlib" 17 | "base64" 18 | "memcpy" 19 | "odoc" {with-doc} 20 | ] 21 | build: [ 22 | ["dune" "subst"] {dev} 23 | [ 24 | "dune" 25 | "build" 26 | "-p" 27 | name 28 | "-j" 29 | jobs 30 | "@install" 31 | "@runtest" {with-test} 32 | "@doc" {with-doc} 33 | ] 34 | ] 35 | dev-repo: "git+https://github.com/osimon8/CombinatorC.git" 36 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 3.0) 2 | (using menhir 2.1) 3 | (name combc) 4 | (version 0.2.11) 5 | (license "GPL-3.0") 6 | (authors "Owen Simon ") 7 | (maintainers "Owen Simon ") 8 | (source (github "osimon8/CombinatorC")) 9 | (generate_opam_files true) 10 | (package 11 | (name combc) 12 | (synopsis "C-style language that compiles to Factorio combinators") 13 | (depends "menhir" ("core_kernel" (= "v0.14.2")) "ocamlgraph" "yojson" "zlib" "base64" "memcpy") 14 | ) -------------------------------------------------------------------------------- /dune-workspace: -------------------------------------------------------------------------------- 1 | (lang dune 3.0) 2 | (context 3 | (default 4 | (name "release-unix") 5 | (profile release) 6 | (targets native) 7 | ) 8 | ) 9 | (context 10 | (default 11 | (name "default") 12 | (profile dev) 13 | (targets native) 14 | ) 15 | ) 16 | -------------------------------------------------------------------------------- /src/ast/bexp.ml: -------------------------------------------------------------------------------- 1 | type bexp = 2 | | Signal of string 3 | | Var of string 4 | | Lit of int32 5 | | Plus of bexp * bexp 6 | | Minus of bexp * bexp 7 | | Mul of bexp * bexp 8 | | Div of bexp * bexp 9 | | Mod of bexp * bexp 10 | | Exp of bexp * bexp 11 | | Lshift of bexp * bexp 12 | | Rshift of bexp * bexp 13 | | AND of bexp * bexp 14 | | OR of bexp * bexp 15 | | XOR of bexp * bexp 16 | | Gt of bexp * bexp 17 | | Lt of bexp * bexp 18 | | Gte of bexp * bexp 19 | | Lte of bexp * bexp 20 | | Eq of bexp * bexp 21 | | Neq of bexp * bexp 22 | | Neg of bexp 23 | | Not of bexp 24 | | LAND of bexp * bexp 25 | | LOR of bexp * bexp 26 | | NAND of bexp * bexp 27 | | NOR of bexp * bexp 28 | | BOOL of bexp 29 | | Conditional of bexp * bexp * bexp 30 | 31 | let rec pow base i = 32 | begin 33 | match i with 34 | | 0l -> 1l 35 | | 1l -> base 36 | | n -> Int32.mul base (pow base (Int32.sub i 1l)) 37 | end 38 | 39 | let signals_in_bexp (b : bexp) : string list = 40 | let rec intern b = 41 | begin 42 | match b with 43 | | Signal v -> [ v ] 44 | | Var _ | Lit _ -> [] 45 | | Not b | Neg b | BOOL b -> intern b 46 | | Plus (b1, b2) 47 | | Minus (b1, b2) 48 | | Mul (b1, b2) 49 | | Mod (b1, b2) 50 | | Exp (b1, b2) 51 | | Lshift (b1, b2) 52 | | Rshift (b1, b2) 53 | | AND (b1, b2) 54 | | OR (b1, b2) 55 | | XOR (b1, b2) 56 | | Div (b1, b2) 57 | | Gt (b1, b2) 58 | | Lt (b1, b2) 59 | | Gte (b1, b2) 60 | | Lte (b1, b2) 61 | | Eq (b1, b2) 62 | | Neq (b1, b2) 63 | | LAND (b1, b2) 64 | | LOR (b1, b2) 65 | | NAND (b1, b2) 66 | | NOR (b1, b2) -> intern b1 @ intern b2 67 | | Conditional (b1, b2, b3) -> intern b1 @ intern b2 @ intern b3 68 | end 69 | in 70 | Core_kernel.List.stable_dedup (intern b) 71 | 72 | let vars_in_bexp (b : bexp) : string list = 73 | let rec intern b = 74 | begin 75 | match b with 76 | | Var v -> [ v ] 77 | | Signal _ | Lit _ -> [] 78 | | Not b | Neg b | BOOL b -> intern b 79 | | Plus (b1, b2) 80 | | Minus (b1, b2) 81 | | Mul (b1, b2) 82 | | Mod (b1, b2) 83 | | Exp (b1, b2) 84 | | Lshift (b1, b2) 85 | | Rshift (b1, b2) 86 | | AND (b1, b2) 87 | | OR (b1, b2) 88 | | XOR (b1, b2) 89 | | Div (b1, b2) 90 | | Gt (b1, b2) 91 | | Lt (b1, b2) 92 | | Gte (b1, b2) 93 | | Lte (b1, b2) 94 | | Eq (b1, b2) 95 | | Neq (b1, b2) 96 | | LAND (b1, b2) 97 | | LOR (b1, b2) 98 | | NAND (b1, b2) 99 | | NOR (b1, b2) -> intern b1 @ intern b2 100 | | Conditional (b1, b2, b3) -> intern b1 @ intern b2 @ intern b3 101 | end 102 | in 103 | Core_kernel.List.stable_dedup (intern b) 104 | 105 | let rec is_logical (b : bexp) : bool = 106 | begin 107 | match b with 108 | | LAND (b1, b2) 109 | | LOR (b1, b2) 110 | | NAND (b1, b2) 111 | | NOR (b1, b2) 112 | | Eq (b1, b2) 113 | | Neq (b1, b2) 114 | | Gt (b1, b2) 115 | | Gte (b1, b2) 116 | | Lt (b1, b2) 117 | | Lte (b1, b2) -> true 118 | | Lit 0l -> true 119 | | Lit 1l -> true 120 | | BOOL b1 | Not b1 -> true 121 | | Mul (b1, b2) -> is_logical b1 && is_logical b2 122 | | Div (b1, b2) -> is_logical b1 && is_logical b2 123 | | _ -> false 124 | end 125 | 126 | let optimize_bexp (b : bexp) : bexp = 127 | let passes = 10 in 128 | (* TODO: refactor bexp def so this isn't so disgusting *) 129 | let rec o b = 130 | begin 131 | match b with 132 | (* Literal interpretation *) 133 | | Plus (Lit l1, Lit l2) -> Lit (Int32.add l1 l2) 134 | | Minus (Lit l1, Lit l2) -> Lit (Int32.sub l1 l2) 135 | | Div (Lit l1, Lit l2) -> Lit (Int32.div l1 l2) 136 | | Mul (Lit l1, Lit l2) -> Lit (Int32.mul l1 l2) 137 | | Exp (Lit l1, Lit l2) -> Lit (pow l1 l2) 138 | | Mod (Lit l1, Lit l2) -> Lit (Int32.rem l1 l2) 139 | | OR (Lit l1, Lit l2) -> Lit (Int32.logor l1 l2) 140 | | AND (Lit l1, Lit l2) -> Lit (Int32.logand l1 l2) 141 | | XOR (Lit l1, Lit l2) -> Lit (Int32.logxor l1 l2) 142 | | Lshift (Lit l1, Lit l2) -> Lit (Int32.shift_left l1 (Int32.to_int l2)) 143 | | Rshift (Lit l1, Lit l2) -> Lit (Int32.shift_right l1 (Int32.to_int l2)) 144 | | Plus (Lit 0l, b) 145 | | Plus (b, Lit 0l) 146 | | Mul (Lit 1l, b) 147 | | Mul (b, Lit 1l) 148 | | Div (b, Lit 1l) 149 | | Lshift (b, Lit 0l) 150 | | Rshift (b, Lit 0l) 151 | | OR (b, Lit 0l) 152 | | OR (Lit 0l, b) 153 | | XOR (b, Lit 0l) 154 | | XOR (Lit 0l, b) 155 | | Neg (Neg b) -> o b 156 | | Mul (_, Lit 0l) | Mul (Lit 0l, _) | AND (_, Lit 0l) | AND (Lit 0l, _) -> Lit 0l 157 | (* Division by 0 in Factorio returns 0 *) 158 | | Div (_, Lit 0l) | Mod (_, Lit 0l) -> Lit 0l 159 | (* Exponentiation by negative in Factorio returns 0 *) 160 | | Exp (b, Lit l) -> if l = 0l then Lit 1l else if l < 0l then Lit 0l else Exp (o b, Lit l) 161 | | Mul (Lit -1l, b) | Mul (b, Lit -1l) | Div (b, Lit -1l) -> Neg (o b) 162 | | Mul (b1, Exp (Lit 2l, b2)) | Mul (Exp (Lit 2l, b2), b1) -> Lshift (o b1, o b2) 163 | (* Associate lits so they can be interpreted. TODO: add more of these *) 164 | | Mul (Mul (Lit l1, b1), Lit l2) -> Mul (o b1, o (Mul (Lit l1, Lit l2))) 165 | | Plus (Plus (Lit l1, b1), Lit l2) -> Plus (o b1, o (Plus (Lit l1, Lit l2))) 166 | | Minus (Plus (Lit l1, b1), Lit l2) -> Plus (o b1, o (Minus (Lit l1, Lit l2))) 167 | (* | Div (b1, Exp (Lit 2l, b2)) -> Rshift (o b1, o b2) *) 168 | | Plus (b, Lit l) -> if l < 0l then Minus (o b, Lit (Int32.neg l)) else Plus (o b, Lit l) 169 | | Plus (b1, Neg b2) -> Minus (o b1, o b2) 170 | | Minus (b, Lit l) -> if l < 0l then Plus (o b, Lit (Int32.neg l)) else Minus (o b, Lit l) 171 | | Minus (b1, Neg b2) -> Plus (o b1, o b2) 172 | | Mul (Neg b1, Neg b2) -> Mul (o b1, o b2) 173 | | Exp (Exp (b1, b2), b3) -> Exp (o b1, Mul (o b2, o b3)) 174 | (* BEGIN SECTION N - NECESSARY FOR PROPER COMPILATION *) 175 | | Gt (Lit l1, Lit l2) -> if l1 > l2 then Lit 1l else Lit 0l 176 | | Lt (Lit l1, Lit l2) -> if l1 < l2 then Lit 1l else Lit 0l 177 | | Gte (Lit l1, Lit l2) -> if l1 >= l2 then Lit 1l else Lit 0l 178 | | Lte (Lit l1, Lit l2) -> if l1 <= l2 then Lit 1l else Lit 0l 179 | | Eq (Lit l1, Lit l2) -> if l1 == l2 then Lit 1l else Lit 0l 180 | | Neq (Lit l1, Lit l2) -> if l1 <> l2 then Lit 1l else Lit 0l 181 | | Gt (Lit l1, b) -> Lte (o b, Lit l1) 182 | | Lt (Lit l1, b) -> Gte (o b, Lit l1) 183 | | Gte (Lit l1, b) -> Lt (o b, Lit l1) 184 | | Lte (Lit l1, b) -> Gt (o b, Lit l1) 185 | | Eq (Lit l1, b) -> Eq (o b, Lit l1) 186 | | Neq (Lit l1, b) -> Neq (o b, Lit l1) 187 | | LAND (Lit l1, Lit l2) -> if l1 <> 0l && l2 <> 0l then Lit 1l else Lit 0l 188 | | LOR (Lit l1, Lit l2) -> if l1 <> 0l || l2 <> 0l then Lit 1l else Lit 0l (* END SECTION N*) 189 | | Not (Gt (b1, b2)) -> Lte (o b1, o b2) 190 | | Not (Lt (b1, b2)) -> Gte (o b1, o b2) 191 | | Not (Gte (b1, b2)) -> Lt (o b1, o b2) 192 | | Not (Lte (b1, b2)) -> Gt (o b1, o b2) 193 | | Not (Eq (b1, b2)) -> Neq (o b1, o b2) 194 | | Not (Neq (b1, b2)) -> Eq (o b1, o b2) 195 | | Not (Lit l) -> if l = 0l then Lit 1l else Lit 0l 196 | | Not (Not (Not b)) -> Not (o b) 197 | | Not (Not b) -> BOOL (o b) 198 | (* Nots and BOOLS can be optimized away, prefer them *) 199 | | Eq (b, Lit 0l) -> Not (o b) (* | Eq (b, Lit 1l) -> BOOL (o b) *) 200 | (* LAND ands LORS take 2 combinators each, minimize their usage when possible *) 201 | | LAND (LAND (b1, b2), b3) -> LAND (Mul (o b1, o b2), o b3) 202 | | LOR (LAND (b1, b2), b3) -> LOR (Mul (o b1, o b2), o b3) 203 | | NAND (LAND (b1, b2), b3) -> NAND (Mul (o b1, o b2), o b3) 204 | | NOR (LAND (b1, b2), b3) -> NOR (Mul (o b1, o b2), o b3) 205 | | Conditional (LAND (b1, b2), b3, b4) -> Conditional (Mul (o b1, o b2), o b3, o b4) 206 | | LAND (LOR (b1, b2), b3) -> LAND (OR (o b1, o b2), o b3) 207 | | LOR (LOR (b1, b2), b3) -> LOR (OR (o b1, o b2), o b3) 208 | | NAND (LOR (b1, b2), b3) -> NAND (OR (o b1, o b2), o b3) 209 | | NOR (LOR (b1, b2), b3) -> NOR (OR (o b1, o b2), o b3) 210 | | Conditional (LOR (b1, b2), b3, b4) -> Conditional (OR (o b1, o b2), o b3, o b4) 211 | | Not (LAND (b1, b2)) -> NAND (o b1, o b2) 212 | | Not (LOR (b1, b2)) -> NOR (o b1, o b2) 213 | | Not (NAND (b1, b2)) -> LAND (o b1, o b2) 214 | | Not (NOR (b1, b2)) -> LOR (o b1, o b2) 215 | (* demorgan *) 216 | | LAND (Not b1, Not b2) -> NOR (o b1, o b2) 217 | | LOR (Not b1, Not b2) -> NAND (o b1, o b2) 218 | | LOR (b, Lit l) | LOR (Lit l, b) -> if l <> 0l then Lit 1l else BOOL (o b) 219 | | LAND (b, Lit l) | LAND (Lit l, b) -> if l = 0l then Lit 0l else BOOL (o b) 220 | (* advanced demorgan *) 221 | | NOR (b, Lit l) | NOR (Lit l, b) -> if l <> 0l then Lit 0l else Not (o b) 222 | | NAND (b, Lit l) | NAND (Lit l, b) -> if l = 0l then Lit 1l else Not (o b) 223 | | LAND (BOOL b1, b2) | LAND (b2, BOOL b1) -> LAND (o b1, o b2) 224 | | LOR (BOOL b1, b2) | LOR (b2, BOOL b1) -> LOR (o b1, o b2) 225 | | NAND (BOOL b1, b2) | NAND (b2, BOOL b1) -> NAND (o b1, o b2) 226 | | NOR (BOOL b1, b2) | NOR (b2, BOOL b1) -> NOR (o b1, o b2) 227 | | BOOL (LAND (b1, b2)) -> LAND (b1, b2) 228 | | BOOL (LOR (b1, b2)) -> LOR (b1, b2) 229 | | BOOL (NAND (b1, b2)) -> NAND (b1, b2) 230 | | BOOL (NOR (b1, b2)) -> NOR (b1, b2) 231 | | BOOL (Not b) | Not (BOOL b) -> Not b 232 | | Conditional (Lit l, b1, b2) -> if l <> 0l then o b1 else o b2 233 | | Conditional (BOOL b1, b2, b3) -> Conditional (o b1, o b2, o b3) 234 | | Conditional (Not b1, b2, b3) -> Conditional (o b1, o b3, o b2) 235 | (* If guard is true (1), then we can just multiply first branch, if its false this yields 0 236 | Better than a conditional, 2 combinators instead of 3 *) 237 | | Conditional (g, b2, Lit 0l) -> Mul (BOOL (o g), o b2) 238 | | Conditional (g, Lit 0l, b2) -> Mul (Not (o g), o b2) 239 | | BOOL (Mul (b1, b2)) -> 240 | let t1 = o b1 in 241 | let t2 = o b2 in 242 | let t3 = o (Mul (t1, t2)) in 243 | if is_logical t1 && is_logical t2 then t3 else BOOL t3 244 | | Plus (b1, b2) -> Plus (o b1, o b2) 245 | | Minus (b1, b2) -> Minus (o b1, o b2) 246 | | Div (b1, b2) -> Div (o b1, o b2) 247 | | Mul (b1, b2) -> Mul (o b1, o b2) 248 | | Exp (b1, b2) -> Exp (o b1, o b2) 249 | | Mod (b1, b2) -> Mod (o b1, o b2) 250 | | Lshift (b1, b2) -> Lshift (o b1, o b2) 251 | | Rshift (b1, b2) -> Rshift (o b1, o b2) 252 | | AND (b1, b2) -> AND (o b1, o b2) 253 | | OR (b1, b2) -> OR (o b1, o b2) 254 | | XOR (b1, b2) -> XOR (o b1, o b2) 255 | | Neg b -> Neg (o b) 256 | | Gt (b1, b2) -> Gt (o b1, o b2) 257 | | Lt (b1, b2) -> Lt (o b1, o b2) 258 | | Gte (b1, b2) -> Gte (o b1, o b2) 259 | | Lte (b1, b2) -> Lte (o b1, o b2) 260 | | Eq (b1, b2) -> Eq (o b1, o b2) 261 | | Neq (b1, b2) -> Neq (o b1, o b2) 262 | | LAND (b1, b2) -> LAND (o b1, o b2) 263 | | LOR (b1, b2) -> LOR (o b1, o b2) 264 | | NAND (b1, b2) -> NAND (o b1, o b2) 265 | | NOR (b1, b2) -> NOR (o b1, o b2) 266 | | Not b -> Not (o b) 267 | | BOOL b -> BOOL (o b) 268 | | Conditional (b1, b2, b3) -> Conditional (o b1, o b2, o b3) 269 | | Lit _ | Var _ | Signal _ -> b 270 | end 271 | in 272 | (* do the optimization n times *) 273 | let rec opti b i = 274 | if i = 0 then b 275 | else 276 | let b1 = o b in 277 | opti b1 (i - 1) 278 | in 279 | opti b passes 280 | 281 | let interpret_bexp bexp : int32 option = 282 | let sigs = signals_in_bexp bexp in 283 | let vars = vars_in_bexp bexp in 284 | if List.length sigs <> 0 || List.length vars <> 0 then None 285 | else 286 | let rec inter b = 287 | begin 288 | match b with 289 | | Lit l -> Some l 290 | | _ -> inter (optimize_bexp b) 291 | end 292 | in 293 | inter bexp 294 | 295 | let string_of_bexp (b : bexp) : string = 296 | let rec sobi first b = 297 | let sob = sobi false in 298 | let bin b1 b2 op = 299 | let s = sob b1 ^ " " ^ op ^ " " ^ sob b2 in 300 | if first then s else "(" ^ s ^ ")" 301 | in 302 | begin 303 | match b with 304 | | Var s | Signal s -> s 305 | | Lit l -> Int32.to_string l 306 | | Neg b -> "-" ^ sob b 307 | | Not b -> "!" ^ sob b 308 | | BOOL b -> "(bool) " ^ sob b 309 | | Plus (b1, b2) -> bin b1 b2 "+" 310 | | Minus (b1, b2) -> bin b1 b2 "-" 311 | | Mul (b1, b2) -> bin b1 b2 "*" 312 | | Div (b1, b2) -> bin b1 b2 "/" 313 | | Mod (b1, b2) -> bin b1 b2 "%" 314 | | Exp (b1, b2) -> bin b1 b2 "**" 315 | | Lshift (b1, b2) -> bin b1 b2 "<<" 316 | | Rshift (b1, b2) -> bin b1 b2 ">>" 317 | | AND (b1, b2) -> bin b1 b2 "&" 318 | | OR (b1, b2) -> bin b1 b2 "|" 319 | | XOR (b1, b2) -> bin b1 b2 "^" 320 | | Gt (b1, b2) -> bin b1 b2 ">" 321 | | Lt (b1, b2) -> bin b1 b2 "<" 322 | | Gte (b1, b2) -> bin b1 b2 ">=" 323 | | Lte (b1, b2) -> bin b1 b2 "<=" 324 | | Eq (b1, b2) -> bin b1 b2 "==" 325 | | Neq (b1, b2) -> bin b1 b2 "!=" 326 | | LAND (b1, b2) -> bin b1 b2 "&&" 327 | | LOR (b1, b2) -> bin b1 b2 "||" 328 | | NAND (b1, b2) -> sobi first (Not (LAND (b1, b2))) 329 | | NOR (b1, b2) -> sobi first (Not (LOR (b1, b2))) 330 | | Conditional (b1, b2, b3) -> 331 | let s = "if " ^ sob b1 ^ " then " ^ sob b2 ^ " else " ^ sob b3 in 332 | if first then s else "(" ^ s ^ ")" 333 | end 334 | in 335 | sobi true b 336 | -------------------------------------------------------------------------------- /src/ast/circuit.ml: -------------------------------------------------------------------------------- 1 | open Combinator 2 | open Utils 3 | 4 | type wire_color = Red | Green 5 | type connection = Ain of id | Aout of id | Din of id | Dout of id | C of id | L of id | P of id 6 | 7 | module Node = struct 8 | type t = connection 9 | 10 | let compare = Stdlib.compare 11 | let hash = Hashtbl.hash 12 | let equal = ( = ) 13 | end 14 | 15 | module Edge = struct 16 | type t = wire_color 17 | 18 | let compare = Stdlib.compare 19 | let equal = ( = ) 20 | let default = Red 21 | end 22 | 23 | module CG = Graph.Imperative.Graph.ConcreteLabeled (Node) (Edge) 24 | module CG_ops = Graph.Oper.I (CG) 25 | module CG_traverse = Graph.Traverse.Bfs (CG) 26 | 27 | type connection_graph = CG.t 28 | 29 | (* max_id, input sigs, output sigs, input ids, output ids*) 30 | type circuit_meta = { 31 | max_id : id; 32 | input_sigs : symbol list; 33 | output_sigs : symbol list; 34 | input_ids : id list; 35 | output_ids : id list; 36 | } 37 | 38 | type circuit = combinator list * connection_graph * circuit_meta 39 | type composition = circuit -> circuit -> circuit 40 | type placement = float * float 41 | type loc = placement option 42 | 43 | let origin = ref (0., 0.) 44 | let get_origin () = !origin 45 | let set_origin new_o = origin := new_o 46 | 47 | type circuit_layout = placement * size * placement list 48 | type concrete_circuit = circuit * circuit_layout 49 | 50 | let entity_ctr = ref (create_ctr ()) 51 | 52 | let get_entity_id () : id = 53 | let v = !entity_ctr () in 54 | (* print_endline ("INCED ENTITY CTR, RETURNING: " ^ string_of_int v); *) 55 | v 56 | 57 | let reset_entity_ctr () = entity_ctr := create_ctr () 58 | 59 | let mid_of_circuit (circuit : circuit) : id = 60 | let _, _, { max_id } = circuit in 61 | max_id 62 | 63 | (* input pole id, output pole id, circuit *) 64 | (* type wrapped_circuit = id * id * circuit *) 65 | let id_of_conn conn : id = 66 | begin 67 | match conn with 68 | | Ain id | Aout id | Din id | Dout id | C id | L id | P id -> id 69 | end 70 | 71 | let opposite_conn conn : connection = 72 | begin 73 | match conn with 74 | | Ain id -> Aout id 75 | | Aout id -> Ain id 76 | | Din id -> Dout id 77 | | Dout id -> Din id 78 | | C _ | L _ | P _ -> conn 79 | end 80 | 81 | (* id used by Factorio in circuit id in JSON representation *) 82 | let type_id_of_conn conn = 83 | begin 84 | match conn with 85 | | Ain _ | Din _ | C _ | L _ | P _ -> 1 86 | | Aout _ | Dout _ -> 2 87 | end 88 | 89 | let string_of_conn conn = 90 | begin 91 | match conn with 92 | | Ain id -> "(Ain " ^ string_of_int id ^ ")" 93 | | Aout id -> "(Aout " ^ string_of_int id ^ ")" 94 | | Din id -> "(Din " ^ string_of_int id ^ ")" 95 | | Dout id -> "(Dout " ^ string_of_int id ^ ")" 96 | | C id -> "(C " ^ string_of_int id ^ ")" 97 | | L id -> "(L " ^ string_of_int id ^ ")" 98 | | P id -> "(P " ^ string_of_int id ^ ")" 99 | end 100 | 101 | let string_of_wire_color (wc : wire_color) = 102 | begin 103 | match wc with 104 | | Red -> "red" 105 | | Green -> "green" 106 | end 107 | 108 | let string_of_edge e = 109 | let v1, c, v2 = e in 110 | string_of_conn v1 ^ " <-> " ^ string_of_conn v2 ^ " : " ^ string_of_wire_color c 111 | 112 | let print_edges (g : connection_graph) : unit = 113 | print_endline "PRINTING GRAPH"; 114 | CG.iter_edges_e (fun e -> print_endline (string_of_edge e)) g; 115 | print_endline "--------------------" 116 | 117 | let succs g id = if CG.mem_vertex g id then CG.succ_e g id else [] 118 | -------------------------------------------------------------------------------- /src/ast/combinator.ml: -------------------------------------------------------------------------------- 1 | type symbol = string 2 | type id = int 3 | type value = int32 4 | type data = symbol * value 5 | type signal = data list 6 | type size = int * int 7 | type arithemtic_op = Add | Sub | Mul | Div | Mod | Exp | Lshift | Rshift | AND | OR | XOR 8 | type decider_op = Gt | Lt | Gte | Lte | Eq | Neq 9 | type decider_output_type = One | InpCount 10 | type aop = Symbol of string | Const of value | Each 11 | type dop = Symbol of string | Const of value | Each | Anything | Everything 12 | type op = Aop of aop | Dop of dop 13 | type pole_type = Small | Medium | Big | Substation 14 | 15 | type decider_config = { 16 | left_input : dop; 17 | op : decider_op; 18 | right_input : dop; 19 | output : dop; 20 | output_type : decider_output_type; 21 | } 22 | 23 | type arithemtic_config = { left_input : aop; op : arithemtic_op; right_input : aop; output : aop } 24 | type constant_config = signal 25 | type lamp_config = { left_input : dop; op : decider_op; right_input : dop } 26 | type pole_config = pole_type 27 | 28 | let a_cfg (a, b, c, d) = { left_input = a; op = b; right_input = c; output = d } 29 | let d_cfg (a, b, c, d, e) = { left_input = a; op = b; right_input = c; output = d; output_type = e } 30 | 31 | type cfg = A of arithemtic_config | D of decider_config | C of constant_config | L of lamp_config 32 | type arithmetic_combinator = id * arithemtic_config 33 | type decider_combinator = id * decider_config 34 | 35 | type combinator = 36 | | Arithmetic of arithmetic_combinator 37 | | Decider of decider_combinator 38 | | Constant of id * constant_config 39 | | Lamp of id * lamp_config 40 | | Pole of id * pole_type 41 | 42 | let size_of_combinator (comb : combinator) : size = 43 | begin 44 | match comb with 45 | | Arithmetic _ | Decider _ -> (1, 2) 46 | | Constant _ -> (1, 1) 47 | | Lamp _ -> (1, 1) 48 | | Pole (_, t) -> begin 49 | match t with 50 | | Small -> (1, 1) 51 | | Medium -> (1, 1) 52 | | Big -> (2, 2) 53 | | Substation -> (2, 2) 54 | end 55 | end 56 | 57 | let id_of_combinator (comb : combinator) : id = 58 | begin 59 | match comb with 60 | | Arithmetic (id, _) -> id 61 | | Decider (id, _) -> id 62 | | Constant (id, _) -> id 63 | | Lamp (id, _) -> id 64 | | Pole (id, _) -> id 65 | end 66 | 67 | (* let input_signals_of_combinator (comb:combinator) : op list = 68 | begin match comb with 69 | | Arithmetic (_, ((o1, _, o2, _))) -> [o1; o2] 70 | | _ -> [] 71 | end *) 72 | 73 | let uses_signal (comb : combinator) (s : symbol) : bool = 74 | let aop_uses (aop : aop) = 75 | begin 76 | match aop with 77 | | Symbol s1 -> s1 = s 78 | | Const _ -> false 79 | | Each -> true 80 | end 81 | in 82 | let dop_uses dop inn t = 83 | begin 84 | match dop with 85 | | Symbol s1 -> 86 | (inn 87 | || 88 | match t with 89 | | InpCount -> true 90 | | One -> false) 91 | && s1 = s 92 | | Const _ -> false 93 | | Anything | Everything | Each -> true 94 | end 95 | in 96 | begin 97 | match comb with 98 | | Arithmetic (_, { left_input = op1; right_input = op2 }) -> aop_uses op1 || aop_uses op2 99 | | Decider (_, { left_input = op1; right_input = op2; output = op3; output_type = t }) -> 100 | dop_uses op1 true t || dop_uses op2 true t || dop_uses op3 false t 101 | | Constant (_, sigs) -> List.mem s (List.map fst sigs) 102 | | Lamp (_, { left_input = op1; right_input = op2 }) -> 103 | dop_uses op1 true One || dop_uses op2 true One 104 | | Pole _ -> false 105 | end 106 | 107 | let uses_signal_in_input (comb : combinator) (s : symbol) : bool = 108 | let aop_uses (aop : aop) = 109 | begin 110 | match aop with 111 | | Symbol s1 -> s1 = s 112 | | Const _ -> false 113 | | Each -> true 114 | end 115 | in 116 | let dop_uses dop = 117 | begin 118 | match dop with 119 | | Symbol s1 -> s1 = s 120 | | Const _ -> false 121 | | Anything | Everything | Each -> true 122 | end 123 | in 124 | begin 125 | match comb with 126 | | Arithmetic (_, { left_input = op1; right_input = op2 }) -> aop_uses op1 || aop_uses op2 127 | | Decider (_, { left_input = op1; right_input = op2; output = op3; output_type = t }) -> 128 | dop_uses op1 || dop_uses op2 129 | | Constant (_, sigs) -> List.mem s (List.map fst sigs) 130 | | Lamp (_, { left_input = op1; right_input = op2 }) -> dop_uses op1 || dop_uses op2 131 | | Pole _ -> false 132 | end 133 | 134 | let uses_wildcard (comb : combinator) : bool = 135 | let aop_uses (aop : aop) = 136 | begin 137 | match aop with 138 | | Each -> true 139 | | _ -> false 140 | end 141 | in 142 | let dop_uses dop = 143 | begin 144 | match dop with 145 | | Anything | Everything | Each -> true 146 | | _ -> false 147 | end 148 | in 149 | begin 150 | match comb with 151 | | Arithmetic (_, { left_input = op1; right_input = op2; output = op3 }) -> 152 | aop_uses op1 || aop_uses op2 || aop_uses op3 153 | | Decider (id, { left_input = op1; right_input = op2; output = op3; output_type = t }) -> 154 | dop_uses op1 || dop_uses op2 || dop_uses op3 155 | | Constant (_, sigs) -> false 156 | | Lamp (_, { left_input = op1; right_input = op2 }) -> dop_uses op1 || dop_uses op2 157 | | Pole _ -> false 158 | end 159 | 160 | (* Abstract these two similar functions to 1 function*) 161 | let replace_signal_A (comb : arithmetic_combinator) (s : symbol) (v : value) : arithmetic_combinator 162 | = 163 | let r2 (comb : arithmetic_combinator) s v : arithmetic_combinator = 164 | let id, (cfg : arithemtic_config) = comb in 165 | begin 166 | match cfg.right_input with 167 | | Symbol sy -> if sy = s then (id, { cfg with right_input = Const v }) else comb 168 | | _ -> comb 169 | end 170 | in 171 | let id, cfg = comb in 172 | begin 173 | match cfg.left_input with 174 | | Symbol sy -> if sy = s then r2 (id, { cfg with left_input = Const v }) s v else r2 comb s v 175 | | _ -> r2 comb s v 176 | end 177 | 178 | let replace_signal_D (comb : decider_combinator) (s : symbol) (v : value) : decider_combinator = 179 | let r2 (comb : decider_combinator) s v : decider_combinator = 180 | let id, cfg = comb in 181 | begin 182 | match cfg.right_input with 183 | | Symbol sy -> if sy = s then (id, { cfg with right_input = Const v }) else comb 184 | | _ -> comb 185 | end 186 | in 187 | let id, cfg = comb in 188 | begin 189 | match cfg.left_input with 190 | | Symbol sy -> if sy = s then r2 (id, { cfg with left_input = Const v }) s v else r2 comb s v 191 | | _ -> r2 comb s v 192 | end 193 | 194 | let string_of_arithmetic_op (op : arithemtic_op) : string = 195 | begin 196 | match op with 197 | | Add -> "+" 198 | | Sub -> "-" 199 | | Mul -> "*" 200 | | Div -> "/" 201 | | Mod -> "%" 202 | | Exp -> "^" 203 | | Lshift -> "<<" 204 | | Rshift -> ">>" 205 | | AND -> "AND" 206 | | OR -> "OR" 207 | | XOR -> "XOR" 208 | end 209 | 210 | let string_of_decider_op (op : decider_op) : string = 211 | begin 212 | match op with 213 | | Gt -> ">" 214 | | Lt -> "<" 215 | | Gte -> "≥" 216 | | Lte -> "≤" 217 | | Eq -> "=" 218 | | Neq -> "≠" 219 | end 220 | 221 | let string_of_combinator (comb : combinator) : string = 222 | begin 223 | match comb with 224 | | Arithmetic (id, _) -> "Arithmetic: " ^ string_of_int id 225 | | Decider (id, _) -> "Decider: " ^ string_of_int id 226 | | Constant (id, _) -> "Constant: " ^ string_of_int id 227 | | Lamp (id, _) -> "Lamp: " ^ string_of_int id 228 | | Pole (id, t) -> 229 | begin 230 | match t with 231 | | Small -> "Small Electric Pole: " 232 | | Medium -> "Medium Electric Pole: " 233 | | Big -> "Big Electric Pole: " 234 | | Substation -> "Substation: " 235 | end 236 | ^ string_of_int id 237 | end 238 | -------------------------------------------------------------------------------- /src/ast/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name ast) 3 | (modules bexp expression circuit combinator) 4 | (libraries core_kernel ocamlgraph utils)) 5 | -------------------------------------------------------------------------------- /src/ast/expression.ml: -------------------------------------------------------------------------------- 1 | open Bexp 2 | open Circuit 3 | 4 | type var_type = TInt | TStamp | TCondition | TSignal | TCircuit | TPattern 5 | 6 | let string_of_type ty : string = 7 | begin 8 | match ty with 9 | | TInt -> "int" 10 | | TStamp -> "expression" 11 | | TCondition -> "condition" 12 | | TSignal -> "signal" 13 | | TCircuit -> "circuit" 14 | | TPattern -> "pattern" 15 | end 16 | 17 | let signal_or_var bexp : bool = 18 | begin 19 | match bexp with 20 | | Signal _ | Var _ -> true 21 | | _ -> false 22 | end 23 | 24 | let valid_condition bexp : bool = 25 | begin 26 | match bexp with 27 | | Gt (b1, b2) | Gte (b1, b2) | Lt (b1, b2) | Lte (b1, b2) | Eq (b1, b2) | Neq (b1, b2) -> 28 | signal_or_var b1 29 | && begin 30 | match b2 with 31 | | Lit _ -> true 32 | | _ -> signal_or_var b2 33 | end 34 | | BOOL b | Not b -> signal_or_var b 35 | | _ -> false 36 | end 37 | 38 | type compiled_circuit = Abstract of circuit | Concrete of concrete_circuit 39 | 40 | type expression = 41 | | Call of string * delayed_expression list 42 | | Int of int32 43 | | Stamp of bexp 44 | | Condition of bexp 45 | | Var of string 46 | | Signal of string 47 | | Circuit of ctree 48 | | Pattern of (var_type * string) list 49 | | For of bool * string * delayed_expression * delayed_expression * bool * command list 50 | 51 | and ctree = 52 | | Union of ctree * ctree * loc 53 | | Concat of ctree * ctree * loc 54 | | Expression of delayed_expression * loc 55 | | Compiled of compiled_circuit 56 | | Inline of bexp * string * loc 57 | 58 | and command = 59 | | CircuitBind of string * bexp * delayed_expression * bool 60 | | Assign of string * var_type * delayed_expression 61 | | Output of delayed_expression 62 | | OutputAt of delayed_expression * (bexp * bexp) 63 | 64 | and delayed_expression = Delayed of bexp | Immediate of expression 65 | 66 | type block = command list 67 | 68 | let num_outputs (block : block) : int = 69 | let vali acc c = 70 | begin 71 | match c with 72 | | Output _ | OutputAt _ -> acc + 1 73 | | _ -> acc 74 | end 75 | in 76 | List.fold_left vali 0 block 77 | 78 | let expression_of_bexp (bexp : bexp) : delayed_expression = 79 | let lit_opt = interpret_bexp bexp in 80 | begin 81 | match lit_opt with 82 | | Some l -> Immediate (Int l) 83 | | None -> begin 84 | match bexp with 85 | | Signal s -> Immediate (Signal s) 86 | | _ -> if valid_condition bexp then Immediate (Condition bexp) else Delayed bexp 87 | end 88 | end 89 | 90 | let offset (origin : placement) (off : placement) : placement = 91 | let x, y = origin in 92 | let ox, oy = off in 93 | (ox +. x, oy +. y) 94 | 95 | let move_layout (l : circuit_layout) (new_p : placement) : circuit_layout = 96 | let p, s, pl = l in 97 | let px, py = p in 98 | let neg_o = (Float.neg px, Float.neg py) in 99 | let pl2 = List.map (offset (offset new_p neg_o)) pl in 100 | (new_p, s, pl2) 101 | 102 | let rec is_concrete ctree : bool = 103 | let c loc = 104 | match loc with 105 | | Some _ -> true 106 | | None -> false 107 | in 108 | begin 109 | match ctree with 110 | | Union (c1, c2, loc) | Concat (c1, c2, loc) -> c loc || is_concrete c1 || is_concrete c2 111 | | Expression (_, loc) -> c loc 112 | | Inline (_, _, loc) -> c loc 113 | | Compiled c -> begin 114 | match c with 115 | | Concrete _ -> true 116 | | Abstract _ -> false 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /src/compiler/compile.ml: -------------------------------------------------------------------------------- 1 | open Ast.Circuit 2 | open Ast.Combinator 3 | open Optimize 4 | open FirstPhase 5 | open Layout 6 | open Utils 7 | open Ast.Bexp 8 | open Ast.Expression 9 | open Composition 10 | open Directive 11 | open Pattern 12 | open Ctxt 13 | open Config 14 | 15 | let compile_bexp_to_circuit (o_sig : symbol) (b : bexp) : circuit = 16 | let circuit = circuit_of_bexp o_sig b in 17 | if get_optimize () then 18 | let circuit_opt = primitive_optimization circuit in 19 | circuit_opt 20 | else circuit 21 | 22 | let interpret_type exp : var_type * expression = 23 | ( begin 24 | match exp with 25 | | Int _ -> TInt 26 | | Stamp _ -> TStamp 27 | | Condition _ -> TCondition 28 | | Signal _ -> TSignal 29 | | Circuit _ | For _ | Call _ -> TCircuit 30 | | Pattern _ -> TPattern 31 | | Var s -> 32 | let t, _ = Ctxt.lookup s in 33 | t 34 | end, 35 | exp ) 36 | 37 | let interpret_types (exps : expression list) : (var_type * expression) list = 38 | List.map interpret_type exps 39 | 40 | let rec bottom_out_var e : expression = 41 | begin 42 | match e with 43 | | Var v -> 44 | let _, v = Ctxt.lookup v in 45 | bottom_out_var v 46 | | _ -> e 47 | end 48 | 49 | let expression_of_delayed (exp : delayed_expression) : expression = 50 | match exp with 51 | | Immediate exp -> exp 52 | | Delayed bexp -> ( 53 | let bexp = bind_vars_of_bexp bexp in 54 | match expression_of_bexp bexp with 55 | | Immediate exp -> exp 56 | | Delayed _ -> Circuit (Inline (bexp, "check", None))) 57 | 58 | let rec evaluate_expression (expression : expression) : expression = 59 | let rec inter exp = 60 | begin 61 | match exp with 62 | | Call (p, args) -> 63 | let args = List.map expression_of_delayed args in 64 | Circuit (Compiled (evaluate_call p args)) 65 | | Condition b -> 66 | let b = bind_vars_of_bexp b in 67 | if valid_condition b then Condition b 68 | else begin 69 | match interpret_bexp b with 70 | | Some i -> Int i 71 | | None -> Circuit (Inline (b, "signal-check", None)) 72 | end 73 | | Var v -> inter (bottom_out_var exp) 74 | | For (concat, id, l_exp, u_exp, count_down, block) -> 75 | let l_exp = evaluate_expression (expression_of_delayed l_exp) in 76 | let u_exp = evaluate_expression (expression_of_delayed u_exp) in 77 | let s exp = 78 | begin 79 | match exp with 80 | | Int i -> i 81 | | _ -> 82 | let ty, _ = interpret_type exp in 83 | prerr_endline 84 | @@ Printf.sprintf "Loop bound does not evaluate to \"int\", evaluates to \"%s\"" 85 | (string_of_type ty); 86 | exit 1 87 | end 88 | in 89 | let l = s l_exp in 90 | let u = s u_exp in 91 | let n_out = num_outputs block in 92 | if n_out = 0 then ( 93 | prerr_endline "For expression missing output!"; 94 | exit 1) 95 | else if n_out > 1 then ( 96 | prerr_endline "For expression has multiple outputs, must have exactly 1"; 97 | exit 1); 98 | Ctxt.add id (TInt, Int l); 99 | let c = evaluate_for concat id u count_down block in 100 | Circuit (Compiled c) 101 | | Signal _ | Int _ | Stamp _ | Circuit _ | Pattern _ -> exp 102 | end 103 | in 104 | inter expression 105 | 106 | and evaluate_call (pattern : string) (args : expression list) : compiled_circuit = 107 | let p_args = Ctxt.lookup_pattern pattern in 108 | let args = 109 | List.map 110 | (fun a -> 111 | let a = evaluate_expression a in 112 | interpret_type a) 113 | args 114 | in 115 | let lp = List.length p_args in 116 | let l = List.length args in 117 | if lp <> l then ( 118 | prerr_endline @@ Printf.sprintf "Pattern \"%s\" expects %d arguments, got %d" pattern lp l; 119 | exit 1); 120 | let zipped = List.combine p_args args in 121 | let old_bindings = Ctxt.get () in 122 | List.iter 123 | (fun (p, a) -> 124 | let pty, pname = p in 125 | let aty, exp = a in 126 | if pty <> aty then ( 127 | prerr_endline 128 | (Printf.sprintf 129 | "Pattern \"%s\" expects argument \"%s\" to have type \"%s\", but an argument of type \ 130 | \"%s\" was provided" 131 | pattern pname (string_of_type pty) (string_of_type aty)); 132 | exit 1) 133 | else Ctxt.add pname (pty, exp)) 134 | zipped; 135 | let circuit = evaluate_pattern pattern args in 136 | Ctxt.set old_bindings; 137 | circuit 138 | 139 | and compile_ctree_to_circuit (ctree : ctree) : compiled_circuit = 140 | let w o_sig bexp = 141 | let ast = if get_optimize_b () then optimize_bexp bexp else bexp in 142 | begin 143 | match bexp with 144 | | Var i -> 145 | let e = bottom_out_var (Var i) in 146 | begin 147 | match e with 148 | | Signal _ | Condition _ | Stamp _ | Int _ -> 149 | Abstract (compile_bexp_to_circuit o_sig ast) 150 | | Circuit c -> compile_ctree_to_circuit c 151 | | _ -> 152 | prerr_endline "Cannot bind variable to circuit. This error should never occur"; 153 | exit 1 154 | end 155 | | _ -> Abstract (compile_bexp_to_circuit o_sig ast) 156 | end 157 | in 158 | let rec r ctree = 159 | let comp f c1 c2 loc = 160 | let c1 = r c1 in 161 | let c2 = r c2 in 162 | let o = 163 | begin 164 | match loc with 165 | | Some l -> l 166 | | None -> get_origin () 167 | end 168 | in 169 | let x c1 c2 rev = 170 | let delta = 2 in 171 | let c2, c2_layout = c2 in 172 | let p2, s2, pl2 = c2_layout in 173 | let o2 = offset o p2 in 174 | let p2, s2, pl2 = move_layout c2_layout o2 in 175 | let l1 = layout c1 in 176 | let p1, s1, _ = l1 in 177 | let mv = 178 | if rev then (float_of_int (fst s2 + delta), 0.) else (float_of_int (-fst s1 - delta), 0.) 179 | in 180 | let o1 = offset (offset o p1) mv in 181 | let p1, s1, pl1 = move_layout l1 o1 in 182 | let c = if rev then f c2 c1 else f c1 c2 in 183 | let o = if rev then p2 else p1 in 184 | Concrete 185 | (c, (o, (fst s1 + fst s2 + delta, snd s1 + snd s2), if rev then pl2 @ pl1 else pl1 @ pl2)) 186 | in 187 | begin 188 | match (c1, c2) with 189 | | Abstract c1, Abstract c2 -> 190 | let c = f c1 c2 in 191 | begin 192 | match loc with 193 | | Some pos -> Concrete (c, layout ~pos c) 194 | | None -> Abstract c 195 | end 196 | | Abstract c1, Concrete c2 -> x c1 c2 false 197 | | Concrete c1, Abstract c2 -> x c2 c1 true 198 | | Concrete c1, Concrete c2 -> 199 | let c1, c1_layout = c1 in 200 | let c2, c2_layout = c2 in 201 | let p1, _, pl1 = c1_layout in 202 | let p2, _, pl2 = c2_layout in 203 | let o1 = offset o p1 in 204 | let o2 = offset o p2 in 205 | let p1, s1, pl1 = move_layout c1_layout o1 in 206 | let p2, s2, pl2 = move_layout c2_layout o2 in 207 | let c = f c1 c2 in 208 | let l = (o, (fst s1 + fst s2, max (snd s1) (snd s2)), pl1 @ pl2) in 209 | Concrete (c, l) 210 | end 211 | in 212 | begin 213 | match ctree with 214 | | Compiled c -> c 215 | | Inline (b, o_sig, loc) -> 216 | let c = w o_sig b in 217 | begin 218 | match loc with 219 | | Some loc -> compile_ctree_to_circuit (bind_loc (Compiled c) loc) 220 | | None -> c 221 | end 222 | | Union (b1, b2, loc) -> comp circuit_union b1 b2 loc 223 | | Concat (b1, b2, loc) -> comp circuit_concat b1 b2 loc 224 | | Expression (e, loc) -> evaluate_expression_to_ctree (expression_of_delayed e) loc 225 | end 226 | in 227 | let circuit = r ctree in 228 | circuit 229 | 230 | and evaluate_expression_to_ctree exp loc : compiled_circuit = 231 | let exp = evaluate_expression exp in 232 | let rec inter exp = 233 | begin 234 | match exp with 235 | | Signal s -> Inline (Signal s, s, loc) 236 | | Int i -> Inline (Lit i, "signal-check", loc) 237 | | Condition b | Stamp b -> Inline (b, "signal-check", loc) 238 | | Circuit c -> c 239 | | Pattern _ -> 240 | prerr_endline "Can't evaluate pattern to circuit. Did you mean to provide arguments?"; 241 | exit 1 242 | | _ -> failwith "Expression not reduced! Impossible error" 243 | end 244 | in 245 | let ctree = inter exp in 246 | begin 247 | match loc with 248 | | Some loc -> compile_ctree_to_circuit (bind_loc ctree loc) 249 | | None -> compile_ctree_to_circuit ctree 250 | end 251 | 252 | and evaluate_for (concat : bool) (id : string) (bound : int32) (count_down : bool) (block : block) : 253 | compiled_circuit = 254 | let _, i = Ctxt.lookup id in 255 | let i = 256 | begin 257 | match i with 258 | | Int i -> Int32.to_int i 259 | | _ -> failwith "Invalid for ctr type, shouldn't happen" 260 | end 261 | in 262 | let bound = Int32.to_int bound in 263 | let finished i = (i < bound && count_down) || (i > bound && not count_down) in 264 | if finished i then ( 265 | prerr_endline "For loop bounds result in 0 executions of loop, no output circuit"; 266 | exit 1); 267 | let cmp () = 268 | let old = Ctxt.get () in 269 | let c = Compiled (List.nth (compile_block_to_circuits block) 0) in 270 | Ctxt.set old; 271 | c 272 | in 273 | let update_ctr i = 274 | Ctxt.remove id; 275 | let i = if count_down then i - 1 else i + 1 in 276 | Ctxt.add id (TInt, Int (Int32.of_int i)); 277 | i 278 | in 279 | let rec eval prev i = 280 | if finished i then prev 281 | else 282 | let c = cmp () in 283 | let c = if concat then Concat (prev, c, None) else Union (prev, c, None) in 284 | eval c (update_ctr i) 285 | in 286 | let first = cmp () in 287 | let c = eval first (update_ctr i) in 288 | compile_ctree_to_circuit c 289 | 290 | and compile_block_to_circuits (commands : block) : compiled_circuit list = 291 | let register ident b o_sig concrete = 292 | Ctxt.add ident 293 | (TCircuit, Circuit (Inline (b, o_sig, if concrete then Some (get_origin ()) else None))) 294 | in 295 | let compile command = 296 | begin 297 | match command with 298 | | CircuitBind (ident, b, o_sig_exp, concrete) -> 299 | let ty, exp = interpret_type @@ evaluate_expression (expression_of_delayed o_sig_exp) in 300 | let o_sig = 301 | begin 302 | match exp with 303 | | Signal s -> s 304 | | _ -> 305 | prerr_endline 306 | @@ Printf.sprintf "Cannot bind expression of type \"%s\" to circuit output signal" 307 | (string_of_type ty); 308 | exit 1 309 | end 310 | in 311 | register ident b o_sig concrete; 312 | None 313 | | Assign (ident, ty, exp) -> 314 | let exp = expression_of_delayed exp in 315 | let i_ty, _ = interpret_type exp in 316 | if i_ty <> ty then ( 317 | prerr_endline 318 | @@ Printf.sprintf "Cannot assign expression of type \"%s\" to variable of type \"%s\"" 319 | (string_of_type i_ty) (string_of_type ty); 320 | exit 1); 321 | Ctxt.add_no_dup ident (ty, exp); 322 | None 323 | | Output e -> 324 | let e = expression_of_delayed e in 325 | Some (evaluate_expression_to_ctree e None) 326 | | OutputAt (e, (e1, e2)) -> 327 | let e = expression_of_delayed e in 328 | let e1 = bind_vars_of_bexp e1 in 329 | let e2 = bind_vars_of_bexp e2 in 330 | begin 331 | match (e1, e2) with 332 | | Lit v1, Lit v2 -> 333 | let loc = (Int32.to_float v1, Int32.to_float v2) in 334 | Some (evaluate_expression_to_ctree e (Some loc)) 335 | | _ -> 336 | prerr_endline @@ "Output location doesn't map to tuple of ints"; 337 | exit 1 338 | end 339 | end 340 | in 341 | let circuit_opts = List.map compile commands in 342 | deopt_list circuit_opts 343 | 344 | let compile (commands : block) : compiled_circuit list = 345 | register_builtins (); 346 | compile_block_to_circuits commands 347 | -------------------------------------------------------------------------------- /src/compiler/composition.ml: -------------------------------------------------------------------------------- 1 | open Ast.Circuit 2 | open Ast.Combinator 3 | open Config 4 | open FirstPhase 5 | 6 | let circuit_concat (c1 : circuit) (c2 : circuit) : circuit = 7 | let combs1, g1, meta1 = c1 in 8 | let { 9 | max_id = m1; 10 | input_sigs = i_sigs1; 11 | output_sigs = o_sigs1; 12 | input_ids = input1; 13 | output_ids = output1; 14 | } = 15 | meta1 16 | in 17 | let combs2, g2, meta2 = c2 in 18 | let { 19 | max_id = m2; 20 | input_sigs = i_sigs2; 21 | output_sigs = o_sigs2; 22 | input_ids = input2; 23 | output_ids = output2; 24 | } = 25 | meta2 26 | in 27 | let new_g = CG_ops.union g1 g2 in 28 | let o = List.map (o_conn_of_id combs1) output1 in 29 | let i = List.map (i_conn_of_id combs2) input2 in 30 | let unmapped_isigs = List.filter (fun s -> not (List.mem s o_sigs1)) i_sigs2 in 31 | if unmapped_isigs = i_sigs2 then 32 | prerr_endline 33 | "WARNING: circuit on left side of concatenation has no output signals matching input signals \ 34 | of circuit on right side"; 35 | let i_sigs = i_sigs1 @ unmapped_isigs in 36 | connect_product connect_primary new_g o i; 37 | ( combs1 @ combs2, 38 | new_g, 39 | { 40 | max_id = max m1 m2; 41 | input_sigs = i_sigs; 42 | output_sigs = o_sigs2; 43 | input_ids = input1; 44 | output_ids = output2; 45 | } ) 46 | 47 | let circuit_union (c1 : circuit) (c2 : circuit) : circuit = 48 | let combs1, g1, meta1 = c1 in 49 | let { 50 | max_id = m1; 51 | input_sigs = i_sigs1; 52 | output_sigs = o_sigs1; 53 | input_ids = input1; 54 | output_ids = output1; 55 | } = 56 | meta1 57 | in 58 | let combs2, g2, meta2 = c2 in 59 | let { 60 | max_id = m2; 61 | input_sigs = i_sigs2; 62 | output_sigs = o_sigs2; 63 | input_ids = input2; 64 | output_ids = output2; 65 | } = 66 | meta2 67 | in 68 | let new_g = CG_ops.union g1 g2 in 69 | let f = Core_kernel.List.stable_dedup in 70 | List.iter 71 | (fun s -> 72 | if List.mem s i_sigs2 then 73 | prerr_endline 74 | "WARNING: circuit on left side of union has output signal that matches an input signal \ 75 | of circuit on right side, this may cause unexpected behavior") 76 | o_sigs1; 77 | List.iter 78 | (fun s -> 79 | if List.mem s i_sigs1 then 80 | prerr_endline 81 | "WARNING: circuit on right side of union has output signal that matches an input signal \ 82 | of circuit on left side, this may cause unexpected behavior") 83 | o_sigs2; 84 | let input = f (input1 @ input2) in 85 | let output = f (output1 @ output2) in 86 | let i_sigs = f (i_sigs1 @ i_sigs2) in 87 | let o_sigs = f (o_sigs1 @ o_sigs2) in 88 | ( combs1 @ combs2, 89 | new_g, 90 | { 91 | max_id = max m1 m2; 92 | input_sigs = i_sigs; 93 | output_sigs = o_sigs; 94 | input_ids = input; 95 | output_ids = output; 96 | } ) 97 | -------------------------------------------------------------------------------- /src/compiler/config.ml: -------------------------------------------------------------------------------- 1 | open Directive 2 | open Ast.Circuit 3 | 4 | type compiler_config = { 5 | layout : layout_type; 6 | primary_color : wire_color; 7 | optimize_b : bool; 8 | optimize : bool; 9 | } 10 | 11 | let default_layout = Identity 12 | let default_primary = Red 13 | 14 | let default_config = 15 | { layout = default_layout; primary_color = default_primary; optimize_b = true; optimize = true } 16 | 17 | let config = ref default_config 18 | let set_config cfg = config := cfg 19 | let get_config () = !config 20 | 21 | let get_layout () = 22 | let { layout = l } = !config in 23 | l 24 | 25 | let get_primary () = 26 | let { primary_color = c } = !config in 27 | c 28 | 29 | let get_optimize_b () = 30 | let { optimize_b = b } = !config in 31 | b 32 | 33 | let get_optimize () = 34 | let { optimize = o } = !config in 35 | o 36 | 37 | let get_secondary () = 38 | let { primary_color = c } = !config in 39 | begin 40 | match c with 41 | | Red -> Green 42 | | Green -> Red 43 | end 44 | 45 | let config_of_directives directives : compiler_config = 46 | let layout = ref default_layout in 47 | let color = ref default_primary in 48 | let internal d = 49 | match d with 50 | | Layout l -> layout := l 51 | | Primary c -> color := c 52 | in 53 | List.iter internal directives; 54 | { layout = !layout; primary_color = !color; optimize_b = true; optimize = true } 55 | -------------------------------------------------------------------------------- /src/compiler/ctxt.ml: -------------------------------------------------------------------------------- 1 | open Ast.Expression 2 | 3 | type pattern = (var_type * string) list 4 | 5 | module Ctxt = struct 6 | type t = (string * (var_type * expression)) list 7 | 8 | let empty = [] 9 | let bindings = ref [] 10 | let clear () = bindings := empty 11 | let get () = !bindings 12 | let set new_binds = bindings := new_binds 13 | let add (id : string) (bnd : var_type * expression) : unit = bindings := (id, bnd) :: !bindings 14 | 15 | let add_no_dup (id : string) (bnd : var_type * expression) : unit = 16 | begin 17 | match List.assoc_opt id !bindings with 18 | | Some _ -> 19 | prerr_endline @@ "Duplicate identifier: \"" ^ id ^ "\""; 20 | exit 1 21 | | None -> add id bnd 22 | end 23 | 24 | let remove (id : string) : unit = bindings := List.remove_assoc id !bindings 25 | 26 | let lookup (id : string) : var_type * expression = 27 | begin 28 | match List.assoc_opt id !bindings with 29 | | Some s -> s 30 | | None -> 31 | prerr_endline @@ "Unknown identifier: \"" ^ id ^ "\""; 32 | exit 1 33 | end 34 | 35 | let lookup_pattern (id : string) : pattern = 36 | match List.assoc_opt id !bindings with 37 | | Some (TPattern, Pattern args) -> args 38 | | _ -> 39 | prerr_endline @@ "\"" ^ id ^ "\" is not a known pattern"; 40 | exit 1 41 | end 42 | -------------------------------------------------------------------------------- /src/compiler/directive.ml: -------------------------------------------------------------------------------- 1 | open Ast.Circuit 2 | 3 | type layout_type = Identity | Naive 4 | type directive = Layout of layout_type | Primary of wire_color 5 | 6 | exception DirectiveError of string 7 | 8 | let parse_directive d s : directive = 9 | let dl = String.lowercase_ascii d in 10 | let sl = String.lowercase_ascii s in 11 | match dl with 12 | | "layout" -> begin 13 | match sl with 14 | | "identity" -> Layout Identity 15 | | "naive" -> Layout Naive 16 | | _ -> raise @@ DirectiveError ("Unsupported layout type: \"" ^ s ^ "\"") 17 | end 18 | | "primary" -> begin 19 | match sl with 20 | | "red" -> Primary Red 21 | | "green" -> Primary Green 22 | | _ -> raise @@ DirectiveError ("Unsupported primary wire color: \"" ^ s ^ "\"") 23 | end 24 | | _ -> raise @@ DirectiveError ("Unsupported directive: \"" ^ d ^ "\"") 25 | -------------------------------------------------------------------------------- /src/compiler/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name compiler) 3 | (modules 4 | optimize 5 | compile 6 | directive 7 | layout 8 | config 9 | firstPhase 10 | composition 11 | pattern 12 | ctxt 13 | sig_list 14 | json) 15 | (libraries core_kernel yojson ocamlgraph ast utils)) 16 | -------------------------------------------------------------------------------- /src/compiler/firstPhase.ml: -------------------------------------------------------------------------------- 1 | open Ast.Bexp 2 | open Ast.Circuit 3 | open Ast.Combinator 4 | open Config 5 | open Utils 6 | open Ctxt 7 | open Sig_list 8 | 9 | let lookup (combs : combinator list) (id : id) : combinator = 10 | List.find (fun c -> id_of_combinator c = id) combs 11 | 12 | let conns_of_comb (c : combinator) : connection list = 13 | begin 14 | match c with 15 | | Lamp (i, _) -> [ L i ] 16 | | Pole (i, _) -> [ P i ] 17 | | Constant (i, _) -> [ C i ] 18 | | Arithmetic (i, _) -> [ Ain i; Aout i ] 19 | | Decider (i, _) -> [ Din i; Dout i ] 20 | end 21 | 22 | let i_conn_of_id (combs : combinator list) (id : id) : connection = 23 | begin 24 | match lookup combs id with 25 | | Arithmetic _ -> Ain id 26 | | Decider _ -> Din id 27 | | Constant _ -> C id 28 | | Pole _ -> P id 29 | | Lamp _ -> L id 30 | end 31 | 32 | let o_conn_of_id (combs : combinator list) (id : id) : connection = 33 | match lookup combs id with 34 | | Arithmetic _ -> Aout id 35 | | Decider _ -> Dout id 36 | | Constant _ -> C id 37 | | Pole _ -> P id 38 | | Lamp _ -> L id 39 | 40 | let conns_of_combs (combs : combinator list) : connection list = List.concat_map conns_of_comb combs 41 | 42 | let create_sig_ctr (vars : string list) = 43 | let ctr = create_ctr ~i:0 () in 44 | let rec intern v = 45 | match List.nth_opt sig_list v with 46 | | None -> failwith "Out of signals!" 47 | | Some s -> if List.mem s vars then intern (ctr ()) else s 48 | in 49 | fun () -> 50 | let v = ctr () in 51 | intern v 52 | 53 | let connect c g c1 c2 = CG.add_edge_e g (c1, c, c2) 54 | let connect_primary g c1 c2 = connect (get_primary ()) g c1 c2 55 | let connect_secondary g c1 c2 = connect (get_secondary ()) g c1 c2 56 | 57 | let connect_rand g c1 c2 = 58 | let f = if Random.bool () then connect_primary else connect_secondary in 59 | f g c1 c2 60 | 61 | let connect_product f g c1 c2 = List.iter (fun c1 -> List.iter (f g c1) c2) c1 62 | 63 | let detect_signal_collision (output_sig : symbol) (b : bexp) : bool = 64 | let rec intern under_conditional b = 65 | let it = intern true in 66 | let i = intern under_conditional in 67 | match b with 68 | | Conditional (b1, b2, b3) -> it b1 || it b2 || it b3 69 | | Var _ -> false 70 | | Lit l -> false 71 | | Signal v -> v = output_sig && under_conditional 72 | | Plus (b1, b2) 73 | | Minus (b1, b2) 74 | | Div (b1, b2) 75 | | Mul (b1, b2) 76 | | Exp (b1, b2) 77 | | Mod (b1, b2) 78 | | Lshift (b1, b2) 79 | | Rshift (b1, b2) 80 | | AND (b1, b2) 81 | | OR (b1, b2) 82 | | XOR (b1, b2) 83 | | Gt (b1, b2) 84 | | Lt (b1, b2) 85 | | Gte (b1, b2) 86 | | Lte (b1, b2) 87 | | Eq (b1, b2) 88 | | Neq (b1, b2) 89 | | LAND (b1, b2) 90 | | LOR (b1, b2) 91 | | NAND (b1, b2) 92 | | NOR (b1, b2) -> i b1 || i b2 93 | | Not b | BOOL b | Neg b -> i b 94 | in 95 | intern false b 96 | 97 | let bind_vars_of_bexp bexp : bexp = 98 | let rec i bexp = 99 | begin 100 | match bexp with 101 | | Var var -> 102 | let ty, v = Ctxt.lookup var in 103 | begin 104 | match v with 105 | | Int x -> Lit x 106 | | Signal s -> Signal s 107 | | Condition b | Stamp b -> i b 108 | | Var v -> i (Var v) 109 | | _ -> 110 | prerr_endline 111 | (Printf.sprintf "Can't bind variable \"%s\" of type \"%s\" to expression" var 112 | (Ast.Expression.string_of_type ty)); 113 | exit 1 114 | end 115 | | Plus (b1, b2) -> Plus (i b1, i b2) 116 | | Minus (b1, b2) -> Minus (i b1, i b2) 117 | | Div (b1, b2) -> Div (i b1, i b2) 118 | | Mul (b1, b2) -> Mul (i b1, i b2) 119 | | Exp (b1, b2) -> Exp (i b1, i b2) 120 | | Mod (b1, b2) -> Mod (i b1, i b2) 121 | | Lshift (b1, b2) -> Lshift (i b1, i b2) 122 | | Rshift (b1, b2) -> Rshift (i b1, i b2) 123 | | AND (b1, b2) -> AND (i b1, i b2) 124 | | OR (b1, b2) -> OR (i b1, i b2) 125 | | XOR (b1, b2) -> XOR (i b1, i b2) 126 | | Gt (b1, b2) -> Gt (i b1, i b2) 127 | | Lt (b1, b2) -> Lt (i b1, i b2) 128 | | Gte (b1, b2) -> Gte (i b1, i b2) 129 | | Lte (b1, b2) -> Lte (i b1, i b2) 130 | | Eq (b1, b2) -> Eq (i b1, i b2) 131 | | Neq (b1, b2) -> Neq (i b1, i b2) 132 | | LAND (b1, b2) -> LAND (i b1, i b2) 133 | | LOR (b1, b2) -> LOR (i b1, i b2) 134 | | NAND (b1, b2) -> NAND (i b1, i b2) 135 | | NOR (b1, b2) -> NOR (i b1, i b2) 136 | | Conditional (b1, b2, b3) -> Conditional (i b1, i b2, i b3) 137 | | Not b -> Not (i b) 138 | | BOOL b -> BOOL (i b) 139 | | Neg b -> Neg (i b) 140 | | Signal _ | Lit _ -> bexp 141 | end 142 | in 143 | optimize_bexp (i bexp) 144 | 145 | let circuit_of_bexp (output_sig : symbol) (b : bexp) : circuit = 146 | (* print_endline @@ string_of_bexp b; *) 147 | let b = bind_vars_of_bexp b in 148 | let vars = signals_in_bexp b in 149 | let sigs = output_sig :: vars in 150 | let sig_ctr = create_sig_ctr sigs in 151 | let collision = detect_signal_collision output_sig b in 152 | let original_out = output_sig in 153 | let output_sig = if collision then sig_ctr () else original_out in 154 | let g = CG.create () in 155 | let connect = connect_product connect_primary g in 156 | let connect2 = connect_product connect_secondary g in 157 | let get_o_sig b = 158 | begin 159 | match b with 160 | | Signal v -> v 161 | | _ -> sig_ctr () 162 | end 163 | in 164 | let rec cb ?(output_sig = None) b : combinator list * id list option * connection list * symbol = 165 | let o_sig = 166 | begin 167 | match output_sig with 168 | | None -> get_o_sig b 169 | | Some s -> s 170 | end 171 | in 172 | let bin_iid_map id iids1 iids2 = 173 | begin 174 | match (iids1, iids2) with 175 | | None, None -> [ id ] 176 | | None, Some i1 | Some i1, None -> id :: i1 177 | | Some i1, Some i2 -> i1 @ i2 178 | end 179 | in 180 | let unary_iid_map id iids = 181 | begin 182 | match iids with 183 | | None -> [ id ] 184 | | Some l -> id :: l 185 | end 186 | in 187 | let a_binop b1 b2 aop = 188 | let id = get_entity_id () in 189 | let c, i = 190 | begin 191 | match (b1, b2) with 192 | | Lit l1, Lit l2 -> 193 | ([ Arithmetic (id, a_cfg (Const l1, aop, Const l2, Symbol o_sig)) ], []) 194 | | Lit l, Signal v -> 195 | ([ Arithmetic (id, a_cfg (Const l, aop, Symbol v, Symbol o_sig)) ], [ id ]) 196 | | Signal v, Lit l -> 197 | ([ Arithmetic (id, a_cfg (Symbol v, aop, Const l, Symbol o_sig)) ], [ id ]) 198 | | Signal v1, Signal v2 -> 199 | ([ Arithmetic (id, a_cfg (Symbol v1, aop, Symbol v2, Symbol o_sig)) ], [ id ]) 200 | | _ -> 201 | let c1, iids1, o1, s1 = cb b1 in 202 | let c2, iids2, o2, s2 = cb b2 in 203 | let iids = bin_iid_map id iids1 iids2 in 204 | connect (o1 @ o2) [ Ain id ]; 205 | (c1 @ c2 @ [ Arithmetic (id, a_cfg (Symbol s1, aop, Symbol s2, Symbol o_sig)) ], iids) 206 | end 207 | in 208 | (c, Some i, [ Aout id ], o_sig) 209 | in 210 | let d_binop b1 b2 dop = 211 | let id = get_entity_id () in 212 | let c, i = 213 | begin 214 | match (b1, b2) with 215 | | Lit l1, Lit l2 -> 216 | ([ Decider (id, d_cfg (Const l1, dop, Const l2, Symbol o_sig, One)) ], []) 217 | | Lit l, Signal v -> 218 | ([ Decider (id, d_cfg (Const l, dop, Symbol v, Symbol o_sig, One)) ], [ id ]) 219 | | Signal v, Lit l -> 220 | ([ Decider (id, d_cfg (Symbol v, dop, Const l, Symbol o_sig, One)) ], [ id ]) 221 | | Signal v1, Signal v2 -> 222 | ([ Decider (id, d_cfg (Symbol v1, dop, Symbol v2, Symbol o_sig, One)) ], [ id ]) 223 | | _ -> 224 | let c1, iids1, o1, s1 = cb b1 in 225 | let c2, iids2, o2, s2 = cb b2 in 226 | let iids = bin_iid_map id iids1 iids2 in 227 | connect (o1 @ o2) [ Din id ]; 228 | ( c1 @ c2 @ [ Decider (id, d_cfg (Symbol s1, dop, Symbol s2, Symbol o_sig, One)) ], 229 | iids ) 230 | end 231 | in 232 | (c, Some i, [ Dout id ], o_sig) 233 | in 234 | let logi b1 b2 aop dop = 235 | let c1, iids1, o1, s1 = cb b1 in 236 | let c2, iids2, o2, s2 = cb b2 in 237 | let id1 = get_entity_id () in 238 | let id2 = get_entity_id () in 239 | let t_sig = sig_ctr () in 240 | let iids = bin_iid_map id1 iids1 iids2 in 241 | connect (o1 @ o2) [ Ain id1 ]; 242 | connect [ Aout id1 ] [ Din id2 ]; 243 | ( c1 @ c2 244 | @ [ 245 | Arithmetic (id1, a_cfg (Symbol s1, aop, Symbol s2, Symbol t_sig)); 246 | Decider (id2, d_cfg (Symbol t_sig, dop, Const 0l, Symbol o_sig, One)); 247 | ], 248 | Some iids, 249 | [ Dout id2 ], 250 | o_sig ) 251 | in 252 | let isolate b dop dtype = 253 | let c, iids, o, s = cb b in 254 | let id = get_entity_id () in 255 | let iids = unary_iid_map id iids in 256 | connect o [ Din id ]; 257 | ( c @ [ Decider (id, d_cfg (Symbol s, dop, Const 0l, Symbol o_sig, dtype)) ], 258 | Some iids, 259 | [ Dout id ], 260 | o_sig ) 261 | in 262 | let conditional b1 b2 b3 = 263 | let c1, iids1, o1, guard = cb b1 in 264 | let c2, iids2, o2, _ = cb ~output_sig:(Some o_sig) b2 in 265 | let c3, iids3, o3, _ = cb ~output_sig:(Some o_sig) b3 in 266 | let id1 = get_entity_id () in 267 | let id2 = get_entity_id () in 268 | connect o1 [ Din id1; Din id2 ]; 269 | connect2 o2 [ Din id1 ]; 270 | connect2 o3 [ Din id2 ]; 271 | let niids1 = bin_iid_map id1 iids1 iids2 in 272 | let niids2 = bin_iid_map id2 iids1 iids3 in 273 | let combs = 274 | [ 275 | Decider (id1, d_cfg (Symbol guard, Neq, Const 0l, Symbol o_sig, InpCount)); 276 | Decider (id2, d_cfg (Symbol guard, Eq, Const 0l, Symbol o_sig, InpCount)); 277 | ] 278 | in 279 | (c1 @ c2 @ c3 @ combs, Some (niids1 @ niids2), [ Dout id1; Dout id2 ], o_sig) 280 | in 281 | let cnst l : combinator list * id list option * connection list * symbol = 282 | let id = get_entity_id () in 283 | ([ Constant (id, [ (o_sig, l) ]) ], Some [], [ C id ], o_sig (* lone lit is a constant *)) 284 | in 285 | begin 286 | match b with 287 | | Signal v -> 288 | if v <> o_sig then 289 | let id = get_entity_id () in 290 | let combs = [ Arithmetic (id, a_cfg (Symbol v, Add, Const 0l, Symbol o_sig)) ] in 291 | (combs, Some [ id ], [ Aout id ], o_sig) 292 | else 293 | ( [], 294 | None, 295 | [], 296 | o_sig 297 | (* IO wrapping will handle, do nothing. 298 | Setting iids to None signals that the upper level needs to be an input *) ) 299 | | Lit l -> cnst l 300 | | Neg b -> 301 | let c, iids, o, s = cb b in 302 | let id = get_entity_id () in 303 | let iids = unary_iid_map id iids in 304 | connect o [ Ain id ]; 305 | ( c @ [ Arithmetic (id, a_cfg (Symbol s, Mul, Const (-1l), Symbol o_sig)) ], 306 | Some iids, 307 | [ Aout id ], 308 | o_sig ) 309 | | Conditional (b1, b2, b3) -> conditional b1 b2 b3 310 | | Not b -> isolate b Eq One 311 | | BOOL b -> isolate b Neq One 312 | | Plus (b1, b2) -> a_binop b1 b2 Add 313 | | Minus (b1, b2) -> a_binop b1 b2 Sub 314 | | Mul (b1, b2) -> a_binop b1 b2 Mul 315 | | Div (b1, b2) -> a_binop b1 b2 Div 316 | | Mod (b1, b2) -> a_binop b1 b2 Mod 317 | | Exp (b1, b2) -> a_binop b1 b2 Exp 318 | | Lshift (b1, b2) -> a_binop b1 b2 Lshift 319 | | Rshift (b1, b2) -> a_binop b1 b2 Rshift 320 | | AND (b1, b2) -> a_binop b1 b2 AND 321 | | OR (b1, b2) -> a_binop b1 b2 OR 322 | | XOR (b1, b2) -> a_binop b1 b2 XOR 323 | | Gt (b1, b2) -> d_binop b1 b2 Gt 324 | | Lt (b1, b2) -> d_binop b1 b2 Lt 325 | | Gte (b1, b2) -> d_binop b1 b2 Gte 326 | | Lte (b1, b2) -> d_binop b1 b2 Lte 327 | | Eq (b1, b2) -> d_binop b1 b2 Eq 328 | | Neq (b1, b2) -> d_binop b1 b2 Neq 329 | | LAND (b1, b2) -> logi b1 b2 Mul Neq 330 | | LOR (b1, b2) -> logi b1 b2 OR Neq 331 | | NAND (b1, b2) -> logi b1 b2 Mul Eq 332 | | NOR (b1, b2) -> logi b1 b2 OR Eq 333 | | Var _ -> failwith "impossible error, shouldn't happen" 334 | end 335 | in 336 | let combs, iids, o_conns, o_sig = cb ~output_sig:(Some output_sig) b in 337 | let iids = 338 | begin 339 | match iids with 340 | | Some l -> l 341 | | None -> [] 342 | end 343 | in 344 | let combs, oids = 345 | if collision then ( 346 | let id = get_entity_id () in 347 | connect o_conns [ Ain id ]; 348 | ( List.rev (Arithmetic (id, a_cfg (Symbol o_sig, Add, Const 0l, Symbol original_out)) :: combs), 349 | [ id ] )) 350 | else (combs, List.map id_of_conn o_conns) 351 | in 352 | let m_id = List.fold_left (fun acc c -> max acc (id_of_combinator c)) Int.min_int combs in 353 | let circuit_meta = 354 | { 355 | max_id = m_id; 356 | input_sigs = vars; 357 | output_sigs = [ original_out ]; 358 | input_ids = iids; 359 | output_ids = oids; 360 | } 361 | in 362 | (combs, g, circuit_meta) 363 | -------------------------------------------------------------------------------- /src/compiler/json.ml: -------------------------------------------------------------------------------- 1 | open Ast.Circuit 2 | open Ast.Combinator 3 | open Optimize 4 | open Layout 5 | open Utils 6 | open Ast.Expression 7 | 8 | let virt = Str.regexp "signal-(.+)" 9 | let fluid = Str.regexp "fluid-(.+)" 10 | let item = Str.regexp "item-(.+)" 11 | let sig_regex = Str.regexp "\\([a-z]+\\)-\\(.+\\)" 12 | 13 | let json_of_symbol s = 14 | let err () = 15 | prerr_endline @@ Printf.sprintf "Invalid signal \"%s\", this error shouldn't happen" s; 16 | exit 1 17 | in 18 | if not (Str.string_match sig_regex s 0) then err (); 19 | let ty = Str.matched_group 1 s in 20 | let ty = if ty = "signal" then "virtual" else ty in 21 | let n = if ty = "virtual" then s else Str.matched_group 2 s in 22 | `Assoc [ ("type", `String ty); ("name", `String n) ] 23 | 24 | let json_of_value v = `Intlit (Int32.to_string v) 25 | 26 | let json_of_config (cfg : cfg) : string * json = 27 | let mk_sig pre s = (pre ^ "_signal", json_of_symbol s) in 28 | let parse_ao (pre : string) (o : aop) = 29 | begin 30 | match o with 31 | | Symbol s -> mk_sig pre s 32 | | Const c -> (pre ^ "_constant", json_of_value c) 33 | | Each -> mk_sig pre "each" 34 | end 35 | in 36 | let parse_do (pre : string) (o : dop) i = 37 | begin 38 | match o with 39 | | Const c -> 40 | if i <> 1 then failwith "illegal argument to decider combinator" 41 | else ("constant", json_of_value c) 42 | | Symbol s -> mk_sig pre s 43 | | Each -> mk_sig pre "each" 44 | | Anything -> mk_sig pre "anything" 45 | | Everything -> mk_sig pre "everything" 46 | end 47 | in 48 | let parse_lo (pre : string) (o : dop) i = 49 | begin 50 | match o with 51 | | Const c -> 52 | if i <> 1 then failwith "illegal argument to lamp" else ("constant", json_of_value c) 53 | | Symbol s -> mk_sig pre s 54 | | Each -> failwith "illegal argument to lamp" 55 | | Anything -> mk_sig pre "anything" 56 | | Everything -> mk_sig pre "everything" 57 | end 58 | in 59 | let c_map (i : int) (data : data) = 60 | let s, v = data in 61 | `Assoc [ ("signal", json_of_symbol s); ("count", json_of_value v); ("index", `Int (i + 1)) ] 62 | in 63 | ( "control_behavior", 64 | `Assoc 65 | begin 66 | match cfg with 67 | | A cfg -> 68 | [ 69 | ( "arithmetic_conditions", 70 | `Assoc 71 | [ 72 | parse_ao "first" cfg.left_input; 73 | parse_ao "second" cfg.right_input; 74 | ("operation", `String (string_of_arithmetic_op cfg.op)); 75 | parse_ao "output" cfg.output; 76 | ] ); 77 | ] 78 | | D cfg -> 79 | [ 80 | ( "decider_conditions", 81 | `Assoc 82 | ([ 83 | parse_do "first" cfg.left_input 0; 84 | parse_do "second" cfg.right_input 1; 85 | ("comparator", `String (string_of_decider_op cfg.op)); 86 | parse_do "output" cfg.output 2; 87 | ] 88 | @ begin 89 | match cfg.output_type with 90 | | One -> [ ("copy_count_from_input", `Bool false) ] 91 | | InpCount -> [] 92 | end) ); 93 | ] 94 | | C cfg -> [ ("filters", `List (List.mapi c_map cfg)) ] 95 | | L cfg -> 96 | [ 97 | ( "circuit_condition", 98 | `Assoc 99 | [ 100 | parse_lo "first" cfg.left_input 0; 101 | parse_lo "second" cfg.right_input 1; 102 | ("comparator", `String (string_of_decider_op cfg.op)); 103 | ] ); 104 | ] 105 | end ) 106 | 107 | let json_of_conn (clist : CG.edge list) : (string * json) list = 108 | let red, green = 109 | List.partition 110 | (fun (_, color, _) -> 111 | begin 112 | match color with 113 | | Red -> true 114 | | Green -> false 115 | end) 116 | clist 117 | in 118 | let map_conns l = 119 | List.map 120 | (fun (_, _, c) -> 121 | `Assoc [ ("entity_id", `Int (id_of_conn c)); ("circuit_id", `Int (type_id_of_conn c)) ]) 122 | l 123 | in 124 | let rl, gl = (List.length red, List.length green) in 125 | begin 126 | match (rl, gl) with 127 | | 0, 0 -> [] 128 | | _, 0 -> [ (string_of_wire_color Red, `List (map_conns red)) ] 129 | | 0, _ -> [ (string_of_wire_color Green, `List (map_conns green)) ] 130 | | _, _ -> 131 | [ 132 | (string_of_wire_color Red, `List (map_conns red)); 133 | (string_of_wire_color Green, `List (map_conns green)); 134 | ] 135 | end 136 | 137 | let json_of_combinator (c : combinator) (g : connection_graph) (p : placement) : json = 138 | let id, name, cfg_json = 139 | begin 140 | match c with 141 | | Arithmetic (id, cfg) -> (id, "arithmetic-combinator", [ json_of_config (A cfg) ]) 142 | | Decider (id, cfg) -> (id, "decider-combinator", [ json_of_config (D cfg) ]) 143 | | Constant (id, cfg) -> (id, "constant-combinator", [ json_of_config (C cfg) ]) 144 | | Lamp (id, cfg) -> (id, "small-lamp", [ json_of_config (L cfg) ]) 145 | | Pole (id, t) -> 146 | ( id, 147 | begin 148 | match t with 149 | | Small -> "small-electric-pole" 150 | | Medium -> "medium-electric-pole" 151 | | Big -> "big-electric-pole" 152 | | Substation -> "substation" 153 | end, 154 | [] ) 155 | end 156 | in 157 | let joc label clist = 158 | begin 159 | match json_of_conn clist with 160 | | [] -> [] 161 | | l -> [ (label, `Assoc l) ] 162 | end 163 | in 164 | let conns = 165 | [ 166 | ( "connections", 167 | begin 168 | match c with 169 | | Arithmetic _ -> `Assoc (joc "1" (succs g (Ain id)) @ joc "2" (succs g (Aout id))) 170 | | Decider _ -> `Assoc (joc "1" (succs g (Din id)) @ joc "2" (succs g (Dout id))) 171 | | Constant _ -> `Assoc (joc "1" (succs g (C id))) 172 | | Lamp _ -> `Assoc (joc "1" (succs g (L id))) 173 | | Pole _ -> `Assoc (joc "1" (succs g (P id))) 174 | end ); 175 | ] 176 | in 177 | let x, y = p in 178 | `Assoc 179 | ([ 180 | ("entity_number", `Int id); 181 | ("name", `String name); 182 | ("position", `Assoc [ ("x", `Float x); ("y", `Float y) ]); 183 | ] 184 | @ cfg_json @ conns) 185 | 186 | let json_of_circuit (circuit : concrete_circuit) : json list = 187 | let (combs, g, _), (_, _, placements) = circuit in 188 | let zipped = List.combine combs placements in 189 | let json_list = List.map (fun (c, p) -> json_of_combinator c g p) zipped in 190 | json_list 191 | 192 | let json_of_circuits (circuits : circuit list) : json list = 193 | let layouts = layout_circuits circuits in 194 | let concrete_circuits = List.combine circuits layouts in 195 | let wrapped = List.map wrap_io concrete_circuits in 196 | let remapped = remap_ids_concrete wrapped in 197 | List.concat_map json_of_circuit remapped 198 | 199 | let json_of_compiled_circuits (circuits : compiled_circuit list) : json list = 200 | let abstract, concrete = 201 | List.partition 202 | (fun c -> 203 | match c with 204 | | Abstract _ -> true 205 | | _ -> false) 206 | circuits 207 | in 208 | let abstract = 209 | List.map 210 | (fun c -> 211 | match c with 212 | | Abstract c -> c 213 | | _ -> failwith "impossible") 214 | abstract 215 | in 216 | let layouts = layout_circuits abstract in 217 | let concrete_layouts = 218 | List.map 219 | (fun c -> 220 | match c with 221 | | Concrete c -> c 222 | | _ -> failwith "impossible") 223 | concrete 224 | in 225 | let concrete_circuits = List.combine abstract layouts @ concrete_layouts in 226 | let wrapped = List.map wrap_io concrete_circuits in 227 | let remapped = remap_ids_concrete wrapped in 228 | List.concat_map json_of_circuit remapped 229 | -------------------------------------------------------------------------------- /src/compiler/layout.ml: -------------------------------------------------------------------------------- 1 | open Ast.Circuit 2 | open Ast.Combinator 3 | open Ast.Expression 4 | open Array 5 | open Config 6 | 7 | type grid = bool array array 8 | 9 | let string_of_layout circuit_layout = 10 | let (ox, oy), (sx, sy), _ = circuit_layout in 11 | "Origin: (" ^ string_of_float ox ^ ", " ^ string_of_float oy ^ ") " ^ "Size: (" ^ string_of_int sx 12 | ^ ", " ^ string_of_int sy ^ ")" 13 | 14 | let poi i j = (float_of_int i, float_of_int j) 15 | 16 | let string_of_placement p = 17 | let x, y = p in 18 | "(" ^ string_of_float x ^ ", " ^ string_of_float y ^ ")" 19 | 20 | let conn_length = 9 21 | let ( +~ ) x y = Float.add x (float_of_int y) 22 | let ( +~~ ) x y = Float.add (float_of_int x) y 23 | let ( -~ ) x y = Float.sub x (float_of_int y) 24 | let ( /~ ) x y = Float.div (float_of_int x) y 25 | let ( ~~ ) p = (int_of_float (fst p), int_of_float (snd p)) 26 | 27 | let center_of_size (p : placement) (s : size) : placement = 28 | let px, py = p in 29 | let sx, sy = s in 30 | (px +. (sx /~ 2.), py +. (sy /~ 2.)) 31 | 32 | let gen_grid () : grid = 33 | let x = conn_length in 34 | let y = conn_length in 35 | Array.make_matrix x y false 36 | 37 | let index grid i j = 38 | let i, j = ~~(i, j) in 39 | grid.(i).(j) 40 | 41 | let grid_size grid : size = (Array.length grid, Array.length grid.(0)) 42 | 43 | let iter_over_size_in_grid (f : int -> int -> unit) (grid : grid) (p : placement) (s : size) : unit 44 | = 45 | (* let x, y = center_of_size p s in *) 46 | let x, y = p in 47 | let xi, yi = (int_of_float x, int_of_float y) in 48 | let sx, sy = s in 49 | let bx, by = (xi + sx - 1, yi + sy - 1) in 50 | for i = xi to bx do 51 | for j = yi to by do 52 | f i j 53 | done 54 | done 55 | 56 | let print_coords grid p s = 57 | iter_over_size_in_grid (fun i j -> print_endline (string_of_placement (poi i j))) grid p s 58 | 59 | let is_valid_placement (grid : grid) (p : placement) (s : size) : bool = 60 | let x, y = p in 61 | let xi, yi = (int_of_float x, int_of_float y) in 62 | let sx, sy = s in 63 | let bx, by = (xi + sx - 1, yi + sy - 1) in 64 | let gx, gy = grid_size grid in 65 | if bx >= gx || by >= gy then false 66 | else 67 | let valid = ref true in 68 | let f i j = valid := !valid && not grid.(i).(j) in 69 | iter_over_size_in_grid f grid p s; 70 | !valid 71 | 72 | let valid_placements (grid : grid) (s : size) : placement list = 73 | let acc = ref [] in 74 | let f i j = 75 | let p = poi i j in 76 | if is_valid_placement grid p s then acc := p :: !acc 77 | in 78 | iter_over_size_in_grid f grid (0., 0.) (grid_size grid); 79 | !acc 80 | 81 | let make_placement (grid : grid) (p : placement) (s : size) : unit = 82 | if not (is_valid_placement grid p s) then failwith "attempted placement at filled position"; 83 | let f i j = grid.(i).(j) <- true in 84 | iter_over_size_in_grid f grid p s 85 | 86 | let place_identity (grid : grid) id (comb : combinator) : placement = 87 | let s = size_of_combinator comb in 88 | let l = conn_length + 1 in 89 | let idp id = (float_of_int (id mod l), float_of_int (2 * (id / l))) in 90 | let p = ref (idp id) in 91 | let i = ref id in 92 | while not (is_valid_placement grid !p s) do 93 | i := !i + 1; 94 | p := idp !i 95 | done; 96 | make_placement grid !p s; 97 | !p 98 | (* center_of_size !p (size_of_combinator comb) *) 99 | 100 | let layout_identity (c : circuit) : placement list = 101 | let combs, _, _ = c in 102 | let g = gen_grid () in 103 | let f i c = place_identity g i c in 104 | List.mapi f combs 105 | 106 | let place_naive (grid : grid) (g : connection_graph) (comb : combinator) : placement = 107 | let s = size_of_combinator comb in 108 | let placements = valid_placements grid s in 109 | let i = Random.int (List.length placements) in 110 | let p = List.nth placements i in 111 | make_placement grid p s; 112 | p 113 | (* center_of_size p s *) 114 | 115 | let layout_naive (c : circuit) : placement list = 116 | let combs, g, _ = c in 117 | let grid : grid = gen_grid () in 118 | let internal (acc : placement list) (comb : combinator) = 119 | let placements = acc in 120 | let p = place_naive grid g comb in 121 | p :: placements 122 | in 123 | let placements = List.fold_left internal [] combs in 124 | List.rev placements 125 | 126 | let get_strategy () = 127 | let { layout = l } = get_config () in 128 | let strategy = 129 | begin 130 | match l with 131 | | Identity -> layout_identity 132 | | Naive -> layout_naive 133 | end 134 | in 135 | strategy 136 | 137 | let layout ?(pos = (0., 0.)) (c : circuit) : circuit_layout = 138 | let strategy = get_strategy () in 139 | let placements = strategy c in 140 | let ox, oy = pos in 141 | let placements = List.map (fun (x, y) -> (x +. ox, y +. oy)) placements in 142 | let combs, _, _ = c in 143 | let zipped = List.combine combs placements in 144 | let p_a = 145 | List.map 146 | (fun (c, (x, y)) -> 147 | (* let s = size_of_combinator c in *) 148 | (* center_of_size (x +. ox, y +. oy) s) *) 149 | (x, y)) 150 | zipped 151 | in 152 | let p_b = 153 | List.map 154 | (fun (c, (x, y)) -> 155 | let sx, sy = size_of_combinator c in 156 | (x +. float_of_int sx, y +. float_of_int sy)) 157 | zipped 158 | in 159 | let min_x = List.fold_left (fun acc (x, _) -> min acc x) Float.max_float p_a in 160 | let min_y = List.fold_left (fun acc (_, y) -> min acc y) Float.max_float p_a in 161 | let max_x = List.fold_left (fun acc (x, _) -> max acc x) Float.min_float p_b in 162 | let max_y = List.fold_left (fun acc (_, y) -> max acc y) Float.min_float p_b in 163 | ( (min_x, min_y), 164 | (int_of_float (max_x -. min_x), int_of_float (max_y -. min_y)), 165 | List.map 166 | (fun (c, p) -> 167 | let s = size_of_combinator c in 168 | center_of_size p s) 169 | zipped ) 170 | 171 | let layout_circuits (circuits : circuit list) : circuit_layout list = 172 | let inter acc c = 173 | let pos, size, placements = layout ~pos:acc c in 174 | let px, py = pos in 175 | let sx, sy = size in 176 | let sy = max 3 sy in 177 | (* print_endline ("POS: (" ^ string_of_float px ^ ", " ^ string_of_float py ^ ")"); *) 178 | (* print_endline ("SIZE: (" ^ string_of_int sx ^ ", " ^ string_of_int sy ^ ")"); *) 179 | ((px, py +~ sy +. 1.), (pos, size, placements)) 180 | in 181 | let _, layouts = List.fold_left_map inter (0., 0.) circuits in 182 | (* List.map (fun (_, _, p) -> p) layouts *) 183 | layouts 184 | 185 | let bind_loc ctree (loc : placement) : ctree = 186 | let l = Some loc in 187 | begin 188 | match ctree with 189 | | Union (c1, c2, _) -> Union (c1, c2, l) 190 | | Concat (c1, c2, _) -> Concat (c1, c2, l) 191 | | Expression (e, _) -> Expression (e, l) 192 | | Inline (b, s, _) -> Inline (b, s, l) 193 | | Compiled c -> begin 194 | match c with 195 | | Abstract c -> Compiled (Concrete (c, layout ~pos:loc c)) 196 | | Concrete c -> 197 | let c, l = c in 198 | let l = move_layout l loc in 199 | Compiled (Concrete (c, l)) 200 | end 201 | end 202 | -------------------------------------------------------------------------------- /src/compiler/optimize.ml: -------------------------------------------------------------------------------- 1 | open Ast.Bexp 2 | open Ast.Circuit 3 | open Ast.Combinator 4 | open Layout 5 | open Config 6 | open FirstPhase 7 | 8 | let filter_connection_graph (g : connection_graph) (color : wire_color) : connection_graph = 9 | let internal e acc = 10 | let _, c, _ = e in 11 | if c = color then e :: acc else acc 12 | in 13 | let edges = CG.fold_edges_e internal g [] in 14 | let g = CG.create () in 15 | List.iter (CG.add_edge_e g) edges; 16 | g 17 | 18 | let rec calculate_signals_on_wire (c : circuit) (color : wire_color) (conn : connection) : 19 | symbol list = 20 | let combs, g, meta = c in 21 | let ( ~! ) = lookup combs in 22 | let err = "Inconsistent connection graph" in 23 | let filtered_g = filter_connection_graph g color in 24 | let cs = calculate_signals_on_wire (combs, filtered_g, meta) in 25 | let map_aop conn (aop : aop) = 26 | begin 27 | match aop with 28 | | Const _ -> [] 29 | | Symbol s -> [ s ] 30 | | Each -> 31 | let o = opposite_conn conn in 32 | cs Red o @ cs Green o 33 | end 34 | in 35 | let map_dop conn (dop : dop) = 36 | begin 37 | match dop with 38 | | Const _ -> [] 39 | | Symbol s -> [ s ] 40 | | Anything | Everything | Each -> 41 | let o = opposite_conn conn in 42 | cs Red o @ cs Green o 43 | end 44 | in 45 | let intern conn acc = 46 | begin 47 | match conn with 48 | | Ain id -> 49 | let comb = ~!id in 50 | begin 51 | match comb with 52 | | Arithmetic (_, { left_input = o1; right_input = o2 }) -> 53 | acc @ map_aop conn o1 @ map_aop conn o2 54 | | _ -> failwith err 55 | end 56 | | Aout id -> 57 | let comb = ~!id in 58 | begin 59 | match comb with 60 | | Arithmetic (_, { output }) -> acc @ map_aop conn output 61 | | _ -> failwith err 62 | end 63 | | Din id -> 64 | let comb = ~!id in 65 | begin 66 | match comb with 67 | | Decider (_, { left_input = o1; right_input = o2 }) -> 68 | acc @ map_dop conn o1 @ map_dop conn o2 69 | | _ -> failwith err 70 | end 71 | | L id | Dout id -> 72 | let comb = ~!id in 73 | begin 74 | match comb with 75 | | Decider (_, { output }) -> acc @ map_dop conn output 76 | | Lamp (_, { right_input }) -> acc @ map_dop conn right_input 77 | | _ -> failwith err 78 | end 79 | | C id -> 80 | let comb = ~!id in 81 | begin 82 | match comb with 83 | | Constant (_, sigs) -> acc @ List.map fst sigs 84 | | _ -> failwith err 85 | end 86 | | P id -> acc 87 | end 88 | in 89 | let s = CG_traverse.fold_component intern [] g conn in 90 | Core_kernel.List.stable_dedup s 91 | 92 | let rec input_signal_isolation (ctr : unit -> int) (inp_id : id) (circuit : circuit) : circuit = 93 | let combs, g, meta = circuit in 94 | let { max_id = mid; input_ids = iids } = meta in 95 | let internal acc id = 96 | let conn = i_conn_of_id combs id in 97 | let _ = get_primary () in 98 | (* ASSUMES ALL INPUT WIRES ARE PRIMARY COLOR *) 99 | (* let sigs = calculate_signals_on_wire circuit color conn in *) 100 | let wires = CG.succ_e g conn in 101 | List.iter 102 | (fun (c1, color, c2) -> 103 | (* if color <> input_color && c1 <> (P inp_id) && c2 <> (P inp_id) then *) 104 | print_endline (string_of_edge (c1, color, c2))) 105 | wires; 106 | acc 107 | in 108 | print_endline "---"; 109 | let _ = List.fold_left internal mid iids in 110 | circuit 111 | 112 | let wrap_io (circ : concrete_circuit) : concrete_circuit = 113 | let circuit, (origin, size, placements) = circ in 114 | let combs, g, meta = circuit in 115 | let { 116 | max_id = m_id; 117 | input_sigs = i_sigs; 118 | output_sigs = o_sigs; 119 | input_ids = input; 120 | output_ids = output; 121 | } = 122 | meta 123 | in 124 | (* let get_entity_id = create_ctr ~i:(m_id + 1) () in *) 125 | let has_input = List.length i_sigs > 0 in 126 | let inp_id = get_entity_id () in 127 | let i_pole = Pole (inp_id, Substation) in 128 | let ox, oy = origin in 129 | let sx, sy = size in 130 | let y_level = oy +. float_of_int (sy / 2) in 131 | let i_pole_placement = (ox -. 1., y_level) in 132 | let wire_input conn = connect_primary g (P inp_id) conn in 133 | let wire_in i = wire_input (i_conn_of_id combs i) in 134 | let inp, inp_placements = 135 | if has_input then ( 136 | let id = get_entity_id () in 137 | wire_input (C id); 138 | ( [ i_pole; Constant (id, List.map (fun v -> (v, 1l)) (List.sort Stdlib.compare i_sigs)) ], 139 | [ i_pole_placement; (ox -. 1., y_level +. 1.5) ] )) 140 | else ([ i_pole ], [ i_pole_placement ]) 141 | in 142 | let oid = get_entity_id () in 143 | let o_pole = Pole (oid, Substation) in 144 | let o_placement = (ox +. float_of_int (sx + 1), y_level) in 145 | let wire_out i = connect_primary g (o_conn_of_id combs i) (P oid) in 146 | (* List.iter wire_in input; *) 147 | (* List.iter (fun i -> print_endline (string_of_int i)) input; *) 148 | let rec connect_inputs input = 149 | begin 150 | match input with 151 | | [] -> () 152 | | id :: [] -> wire_in id 153 | | id1 :: id2 :: tl -> 154 | connect_primary g (i_conn_of_id combs id1) (i_conn_of_id combs id2); 155 | connect_inputs (id2 :: tl) 156 | end 157 | in 158 | let rec connect_outputs output = 159 | begin 160 | match output with 161 | | [] -> () 162 | | id :: [] -> wire_out id 163 | | id1 :: id2 :: tl -> 164 | connect_primary g (o_conn_of_id combs id1) (o_conn_of_id combs id2); 165 | connect_outputs (id2 :: tl) 166 | end 167 | in 168 | connect_inputs input; 169 | connect_outputs output; 170 | (* List.iter wire_out output; *) 171 | let new_combs = (o_pole :: inp) @ combs in 172 | let iids = List.map id_of_combinator inp in 173 | let meta = 174 | { 175 | max_id = oid; 176 | input_sigs = i_sigs; 177 | output_sigs = o_sigs; 178 | input_ids = iids; 179 | output_ids = [ oid ]; 180 | } 181 | in 182 | let new_c = (new_combs, g, meta) in 183 | let new_placements = (o_placement :: inp_placements) @ placements in 184 | (new_c, ((ox -. 2., Float.min y_level oy), (sx + 4, sy), new_placements)) 185 | 186 | (* let combs, g, (mid, i_sigs, o_sigs, _, oids) = input_signal_isolation ctr inp_id new_c in 187 | (combs, g, (mid, i_sigs, o_sigs, iids, oids)) *) 188 | 189 | (* basic optimization that optimizes from the structure of the bexp compiler 190 | essentially just removes redundant constant combinators. Doesn't update meta 191 | *) 192 | let rec primitive_optimization (circuit : circuit) : circuit = 193 | let combs, g, meta = circuit in 194 | let { input_sigs = i_sigs } = meta in 195 | let ( ~$ ) c = id_of_conn c in 196 | let ( ~$$ ) c = lookup combs ~$c in 197 | let bind_conn_input cc oc i acc = 198 | begin 199 | match lookup combs i with 200 | | Constant (_, cfg) -> begin 201 | match cfg with 202 | | (s, v) :: [] -> 203 | if List.mem s i_sigs then acc 204 | else 205 | let comb = ~$$oc in 206 | (* let output_elim = 207 | begin match comb with 208 | | Decider (_, (_, _, _, s, InpCount)) -> v = 1l && (match oc with Din _ -> true |_ -> false) 209 | | _ -> false 210 | end in *) 211 | let output_elim = false in 212 | if (uses_signal_in_input comb s && not (uses_wildcard comb)) || output_elim then 213 | let d, n, sigs, dels = acc in 214 | let e = CG.succ_e g cc in 215 | (d @ e, n, sigs @ List.map (fun _ -> (comb, (s, v))) e, ~$$cc :: dels) 216 | else acc 217 | | _ -> acc 218 | end 219 | | _ -> 220 | failwith ("combinators inconsistent with connections - no CC with id " ^ string_of_int i) 221 | end 222 | in 223 | (* let bind_conn_output c1 c2 id1 id2 = 224 | begin match lookup combs id1, lookup combs id2 with 225 | | Constant (_, cfg), Decider (_) -> 226 | begin match cfg with 227 | | (s, v) :: [] -> if List.mem s i_sigs then c1, c2 else 228 | let comb = ~$$c2 in 229 | if (uses_signal_in_input comb s && not (uses_wildcard comb)) then 230 | let d, n, sigs, dels = acc in 231 | let e = CG.succ_e g cc in 232 | d @ e, n, sigs @ List.map (fun _ -> comb, (s, v)) e, ~$$cc :: dels 233 | else acc 234 | | _ -> c1, c2 235 | end 236 | | _ -> failwith ("combinators inconsistent with connections - no CC/DC with id " ^ string_of_int i) 237 | end in *) 238 | let delete_edges, new_edges, sigs, deleted_combs = 239 | CG.fold_edges 240 | (fun c1 c2 acc -> 241 | (* outputs *) 242 | let c1, c2 = 243 | begin 244 | match (c1, c2) with 245 | | C id1, Din id2 | Din id2, C id1 -> (c1, c2) 246 | | _ -> (c1, c2) 247 | end 248 | in 249 | (* inputs *) 250 | begin 251 | match (c1, c2) with 252 | | C _, P _ | P _, C _ -> acc 253 | | C i, _ -> bind_conn_input c1 c2 i acc 254 | | _, C i -> bind_conn_input c2 c1 i acc 255 | | _ -> acc 256 | end) 257 | g ([], [], [], []) 258 | in 259 | let a_combs = List.map (fun (c, s) -> c) sigs in 260 | let temp = a_combs @ deleted_combs in 261 | let u_combs = List.filter (fun c -> not (List.mem c temp)) combs in 262 | let new_combs = 263 | List.map 264 | (fun (c, (s, v)) -> 265 | begin 266 | match c with 267 | | Arithmetic c -> Arithmetic (replace_signal_A c s v) 268 | | Decider c -> Decider (replace_signal_D c s v) 269 | | _ -> failwith "impossible" 270 | end) 271 | sigs 272 | in 273 | List.iter (CG.remove_edge_e g) delete_edges; 274 | List.iter (CG.add_edge_e g) new_edges; 275 | (u_combs @ new_combs, g, meta) 276 | 277 | (* Remap ids after combinators have been deleted through optimization passes *) 278 | let remap_ids ?(reset_ctr = true) (circuits : circuit list) : circuit list = 279 | if reset_ctr then reset_entity_ctr (); 280 | let remap (circuit : circuit) = 281 | let combs, g, meta = circuit in 282 | let { input_sigs = i_sigs; output_sigs = o_sigs; input_ids = input; output_ids = output } = 283 | meta 284 | in 285 | let id_map = 286 | List.map 287 | (fun c -> 288 | let i = get_entity_id () in 289 | (id_of_combinator c, i)) 290 | combs 291 | in 292 | let ( ~! ) i = List.assoc i id_map in 293 | let ( ~$ ) c = ~!(id_of_conn c) in 294 | let ( ~$$ ) c = ~!(id_of_combinator c) in 295 | let new_g = CG.create () in 296 | let remap_conn c = 297 | let id = ~$c in 298 | begin 299 | match c with 300 | | Ain i -> Ain id 301 | | Aout i -> Aout id 302 | | Din i -> Din id 303 | | Dout i -> Dout id 304 | | C i -> C id 305 | | L i -> L id 306 | | P i -> P id 307 | end 308 | in 309 | CG.iter_edges_e (fun (c1, c, c2) -> connect c new_g (remap_conn c1) (remap_conn c2)) g; 310 | let new_combs = 311 | List.map 312 | (fun c -> 313 | let i = ~$$c in 314 | begin 315 | match c with 316 | | Arithmetic (_, cfg) -> Arithmetic (i, cfg) 317 | | Decider (_, cfg) -> Decider (i, cfg) 318 | | Constant (_, cfg) -> Constant (i, cfg) 319 | | Lamp (_, cfg) -> Lamp (i, cfg) 320 | | Pole (_, t) -> Pole (i, t) 321 | end) 322 | combs 323 | in 324 | let m_id = List.fold_left (fun acc (_, i) -> max acc i) (-1) id_map in 325 | let new_input = List.map ( ~! ) input in 326 | let new_output = List.map ( ~! ) output in 327 | let meta = 328 | { 329 | max_id = m_id; 330 | input_sigs = i_sigs; 331 | output_sigs = o_sigs; 332 | input_ids = new_input; 333 | output_ids = new_output; 334 | } 335 | in 336 | (new_combs, new_g, meta) 337 | in 338 | List.map remap circuits 339 | 340 | let remap_ids_concrete ?(reset_ctr = true) (circuits : concrete_circuit list) : 341 | concrete_circuit list = 342 | let raw, layouts = List.split circuits in 343 | let remapped = remap_ids ~reset_ctr raw in 344 | List.combine remapped layouts 345 | -------------------------------------------------------------------------------- /src/compiler/pattern.ml: -------------------------------------------------------------------------------- 1 | open Ast.Expression 2 | open Ast.Bexp 3 | open Ctxt 4 | open Ast.Combinator 5 | open Ast.Circuit 6 | open FirstPhase 7 | 8 | let builtins : (string * pattern) list = 9 | [ 10 | ("counter", [ (TInt, "max_value"); (TSignal, "o_sig") ]); 11 | ("counter2", [ (TInt, "max_value"); (TSignal, "i_sig"); (TSignal, "o_sig") ]); 12 | ("lamp", [ (TCondition, "cnd") ]); 13 | ] 14 | 15 | let register_builtins () = List.iter (fun (s, args) -> Ctxt.add s (TPattern, Pattern args)) builtins 16 | 17 | let lamp_config_of_condition (cnd : bexp) : lamp_config * symbol list = 18 | let sigs = ref [] in 19 | let bind s = sigs := s :: !sigs in 20 | let f b2 = 21 | begin 22 | match b2 with 23 | | Signal s -> 24 | bind s; 25 | Symbol s 26 | | Lit l -> Const l 27 | | _ -> failwith "invalid condition" 28 | end 29 | in 30 | let o1, op, o2 = 31 | begin 32 | match cnd with 33 | | BOOL (Signal s) -> 34 | bind s; 35 | (Symbol s, Neq, Const 0l) 36 | | Not (Signal s) -> 37 | bind s; 38 | (Symbol s, Eq, Const 0l) 39 | | Gt (Signal s, b2) -> 40 | bind s; 41 | (Symbol s, Gt, f b2) 42 | | Gte (Signal s, b2) -> 43 | bind s; 44 | (Symbol s, Gte, f b2) 45 | | Lt (Signal s, b2) -> 46 | bind s; 47 | (Symbol s, Lt, f b2) 48 | | Lte (Signal s, b2) -> 49 | bind s; 50 | (Symbol s, Lte, f b2) 51 | | Eq (Signal s, b2) -> 52 | bind s; 53 | (Symbol s, Eq, f b2) 54 | | Neq (Signal s, b2) -> 55 | bind s; 56 | (Symbol s, Neq, f b2) 57 | | _ -> failwith "invalid condition" 58 | end 59 | in 60 | ({ left_input = o1; op; right_input = o2 }, !sigs) 61 | 62 | let evaluate_pattern pattern args : compiled_circuit = 63 | match pattern with 64 | | "lamp" -> 65 | let id = get_entity_id () in 66 | let _, cnd = Ctxt.lookup "cnd" in 67 | let cnd = 68 | match cnd with 69 | | Condition cnd -> cnd 70 | | _ -> failwith "invalid argument (not a condition), shouldn't happen" 71 | in 72 | let cfg, sigs = lamp_config_of_condition cnd in 73 | let lamp = Lamp (id, cfg) in 74 | let origin = get_origin () in 75 | let layout = (origin, size_of_combinator lamp, [ origin ]) in 76 | let meta : circuit_meta = 77 | { 78 | max_id = id; 79 | input_sigs = sigs; 80 | output_sigs = sigs; 81 | input_ids = [ id ]; 82 | output_ids = [ id ]; 83 | } 84 | in 85 | let g = CG.create () in 86 | let c = ([ lamp ], g, meta) in 87 | Concrete (c, layout) 88 | | "counter" -> 89 | let _, mv = Ctxt.lookup "max_value" in 90 | let _, o_sig = Ctxt.lookup "o_sig" in 91 | let mv, o_sig = 92 | match (mv, o_sig) with 93 | | Int mv, Signal o_sig -> (mv, o_sig) 94 | | _ -> failwith "invalid args (shouldn't happen)" 95 | in 96 | let id1 = get_entity_id () in 97 | let id2 = get_entity_id () in 98 | let cnst = Constant (id1, [ (o_sig, 1l) ]) in 99 | let ctr = Decider (id2, d_cfg (Symbol o_sig, Lt, Const mv, Symbol o_sig, InpCount)) in 100 | let meta : circuit_meta = 101 | { 102 | max_id = id2; 103 | input_sigs = []; 104 | output_sigs = [ o_sig ]; 105 | input_ids = []; 106 | output_ids = [ id2 ]; 107 | } 108 | in 109 | let g = CG.create () in 110 | connect_primary g (C id1) (Din id2); 111 | connect_primary g (Dout id2) (Din id2); 112 | let c = ([ cnst; ctr ], g, meta) in 113 | Abstract c 114 | | "counter2" -> 115 | let _, mv = Ctxt.lookup "max_value" in 116 | let _, i_sig = Ctxt.lookup "i_sig" in 117 | let _, o_sig = Ctxt.lookup "o_sig" in 118 | let mv, i_sig, o_sig = 119 | match (mv, i_sig, o_sig) with 120 | | Int mv, Signal i_sig, Signal o_sig -> (mv, i_sig, o_sig) 121 | | _ -> failwith "invalid args (shouldn't happen)" 122 | in 123 | let id1 = get_entity_id () in 124 | let ctr = Decider (id1, d_cfg (Symbol i_sig, Lt, Const mv, Symbol i_sig, InpCount)) in 125 | let g = CG.create () in 126 | connect_primary g (Dout id1) (Din id1); 127 | let mid, combs, o_conns = 128 | if i_sig <> o_sig then ( 129 | let id2 = get_entity_id () in 130 | let map = Arithmetic (id2, a_cfg (Symbol i_sig, Add, Const 0l, Symbol o_sig)) in 131 | connect_primary g (Dout id1) (Ain id2); 132 | (id2, [ ctr; map ], [ id2 ])) 133 | else (id1, [ ctr ], [ id1 ]) 134 | in 135 | let meta : circuit_meta = 136 | { 137 | max_id = mid; 138 | input_sigs = [ i_sig ]; 139 | output_sigs = [ o_sig ]; 140 | input_ids = [ id1 ]; 141 | output_ids = o_conns; 142 | } 143 | in 144 | let c = (combs, g, meta) in 145 | Abstract c 146 | | _ -> failwith "Unknown pattern, should never happen" 147 | -------------------------------------------------------------------------------- /src/compiler/sig_list.ml: -------------------------------------------------------------------------------- 1 | let sig_list = 2 | [ 3 | "signal-0"; 4 | "signal-1"; 5 | "signal-2"; 6 | "signal-3"; 7 | "signal-4"; 8 | "signal-5"; 9 | "signal-6"; 10 | "signal-7"; 11 | "signal-8"; 12 | "signal-9"; 13 | "signal-A"; 14 | "signal-B"; 15 | "signal-C"; 16 | "signal-D"; 17 | "signal-E"; 18 | "signal-F"; 19 | "signal-G"; 20 | "signal-H"; 21 | "signal-I"; 22 | "signal-J"; 23 | "signal-K"; 24 | "signal-L"; 25 | "signal-M"; 26 | "signal-N"; 27 | "signal-O"; 28 | "signal-P"; 29 | "signal-Q"; 30 | "signal-R"; 31 | "signal-S"; 32 | "signal-T"; 33 | "signal-U"; 34 | "signal-V"; 35 | "signal-W"; 36 | "signal-X"; 37 | "signal-Y"; 38 | "signal-Z"; 39 | "signal-black"; 40 | "signal-blue"; 41 | "signal-check"; 42 | "signal-cyan"; 43 | "signal-dot"; 44 | "signal-green"; 45 | "signal-grey"; 46 | "signal-info"; 47 | "signal-pink"; 48 | "signal-red"; 49 | "signal-white"; 50 | "signal-yellow"; 51 | "item-accumulator"; 52 | "item-advanced-circuit"; 53 | "item-arithmetic-combinator"; 54 | "item-artillery-shell"; 55 | "item-artillery-targeting-remote"; 56 | "item-artillery-turret"; 57 | "item-artillery-wagon"; 58 | "item-artillery-wagon-cannon"; 59 | "item-assembling-machine-1"; 60 | "item-assembling-machine-2"; 61 | "item-assembling-machine-3"; 62 | "item-atomic-bomb"; 63 | "item-automation-science-pack"; 64 | "item-battery"; 65 | "item-battery-equipment"; 66 | "item-battery-mk2-equipment"; 67 | "item-beacon"; 68 | "item-belt-immunity-equipment"; 69 | "item-big-electric-pole"; 70 | "item-blueprint"; 71 | "item-blueprint-book"; 72 | "item-boiler"; 73 | "item-burner-generator"; 74 | "item-burner-inserter"; 75 | "item-burner-mining-drill"; 76 | "item-cannon-shell"; 77 | "item-car"; 78 | "item-cargo-wagon"; 79 | "item-centrifuge"; 80 | "item-chemical-plant"; 81 | "item-chemical-science-pack"; 82 | "item-cliff-explosives"; 83 | "item-cluster-grenade"; 84 | "item-coal"; 85 | "item-coin"; 86 | "item-combat-shotgun"; 87 | "item-concrete"; 88 | "item-constant-combinator"; 89 | "item-construction-robot"; 90 | "item-copper-cable"; 91 | "item-copper-ore"; 92 | "item-copper-plate"; 93 | "item-copy-paste-tool"; 94 | "item-cut-paste-tool"; 95 | "item-decider-combinator"; 96 | "item-deconstruction-planner"; 97 | "item-defender-capsule"; 98 | "item-destroyer-capsule"; 99 | "item-discharge-defense-equipment"; 100 | "item-discharge-defense-remote"; 101 | "item-distractor-capsule"; 102 | "item-dummy-steel-axe"; 103 | "item-effectivity-module"; 104 | "item-effectivity-module-2"; 105 | "item-effectivity-module-3"; 106 | "item-electric-energy-interface"; 107 | "item-electric-engine-unit"; 108 | "item-electric-furnace"; 109 | "item-electric-mining-drill"; 110 | "item-electronic-circuit"; 111 | "item-empty-barrel"; 112 | "item-energy-shield-equipment"; 113 | "item-energy-shield-mk2-equipment"; 114 | "item-engine-unit"; 115 | "item-exoskeleton-equipment"; 116 | "item-explosive-cannon-shell"; 117 | "item-explosive-rocket"; 118 | "item-explosive-uranium-cannon-shell"; 119 | "item-explosives"; 120 | "item-express-loader"; 121 | "item-express-splitter"; 122 | "item-express-transport-belt"; 123 | "item-express-underground-belt"; 124 | "item-fast-inserter"; 125 | "item-fast-loader"; 126 | "item-fast-splitter"; 127 | "item-fast-transport-belt"; 128 | "item-fast-underground-belt"; 129 | "item-filter-inserter"; 130 | "item-firearm-magazine"; 131 | "item-flamethrower"; 132 | "item-flamethrower-ammo"; 133 | "item-flamethrower-turret"; 134 | "item-fluid-wagon"; 135 | "item-flying-robot-frame"; 136 | "item-fusion-reactor-equipment"; 137 | "item-gate"; 138 | "item-green-wire"; 139 | "item-grenade"; 140 | "item-gun-turret"; 141 | "item-hazard-concrete"; 142 | "item-heat-exchanger"; 143 | "item-heat-interface"; 144 | "item-heat-pipe"; 145 | "item-heavy-armor"; 146 | "item-infinity-chest"; 147 | "item-infinity-pipe"; 148 | "item-inserter"; 149 | "item-iron-chest"; 150 | "item-iron-gear-wheel"; 151 | "item-iron-ore"; 152 | "item-iron-plate"; 153 | "item-iron-stick"; 154 | "item-item-with-inventory"; 155 | "item-item-with-label"; 156 | "item-item-with-tags"; 157 | "item-lab"; 158 | "item-land-mine"; 159 | "item-landfill"; 160 | "item-laser-turret"; 161 | "item-light-armor"; 162 | "item-linked-belt"; 163 | "item-linked-chest"; 164 | "item-loader"; 165 | "item-loader"; 166 | "item-locomotive"; 167 | "item-logistic-chest-active-provider"; 168 | "item-logistic-chest-buffer"; 169 | "item-logistic-chest-passive-provider"; 170 | "item-logistic-chest-requester"; 171 | "item-logistic-chest-storage"; 172 | "item-logistic-robot"; 173 | "item-logistic-science-pack"; 174 | "item-long-handed-inserter"; 175 | "item-low-density-structure"; 176 | "item-medium-electric-pole"; 177 | "item-military-science-pack"; 178 | "item-modular-armor"; 179 | "item-night-vision-equipment"; 180 | "item-nuclear-fuel"; 181 | "item-nuclear-reactor"; 182 | "item-offshore-pump"; 183 | "item-oil-refinery"; 184 | "item-personal-laser-defense-equipment"; 185 | "item-personal-roboport-equipment"; 186 | "item-personal-roboport-mk2-equipment"; 187 | "item-piercing-rounds-magazine"; 188 | "item-piercing-shotgun-shell"; 189 | "item-pipe"; 190 | "item-pipe-to-ground"; 191 | "item-pistol"; 192 | "item-plastic-bar"; 193 | "item-player-port"; 194 | "item-poison-capsule"; 195 | "item-power-armor"; 196 | "item-power-armor-mk2"; 197 | "item-power-switch"; 198 | "item-processing-unit"; 199 | "item-production-science-pack"; 200 | "item-productivity-module"; 201 | "item-productivity-module-2"; 202 | "item-productivity-module-3"; 203 | "item-programmable-speaker"; 204 | "item-pump"; 205 | "item-pumpjack"; 206 | "item-radar"; 207 | "item-rail"; 208 | "item-rail-chain-signal"; 209 | "item-rail-signal"; 210 | "item-raw-fish"; 211 | "item-red-wire"; 212 | "item-refined-concrete"; 213 | "item-refined-hazard-concrete"; 214 | "item-repair-pack"; 215 | "item-roboport"; 216 | "item-rocket"; 217 | "item-rocket-control-unit"; 218 | "item-rocket-fuel"; 219 | "item-rocket-launcher"; 220 | "item-rocket-part"; 221 | "item-rocket-silo"; 222 | "item-satellite"; 223 | "item-selection-tool"; 224 | "item-shotgun"; 225 | "item-shotgun-shell"; 226 | "item-simple-entity-with-force"; 227 | "item-simple-entity-with-owner"; 228 | "item-slowdown-capsule"; 229 | "item-small-electric-pole"; 230 | "item-small-lamp"; 231 | "item-solar-panel"; 232 | "item-solar-panel-equipment"; 233 | "item-solid-fuel"; 234 | "item-space-science-pack"; 235 | "item-speed-module"; 236 | "item-speed-module-2"; 237 | "item-speed-module-3"; 238 | "item-spidertron"; 239 | "item-spidertron-remote"; 240 | "item-spidertron-rocket-launcher-1"; 241 | "item-spidertron-rocket-launcher-2"; 242 | "item-spidertron-rocket-launcher-3"; 243 | "item-spidertron-rocket-launcher-4"; 244 | "item-splitter"; 245 | "item-stack-filter-inserter"; 246 | "item-stack-inserter"; 247 | "item-steam-engine"; 248 | "item-steam-turbine"; 249 | "item-steel-chest"; 250 | "item-steel-furnace"; 251 | "item-steel-plate"; 252 | "item-stone"; 253 | "item-stone-brick"; 254 | "item-stone-furnace"; 255 | "item-stone-wall"; 256 | "item-storage-tank"; 257 | "item-submachine-gun"; 258 | "item-substation"; 259 | "item-sulfur"; 260 | "item-tank"; 261 | "item-tank-cannon"; 262 | "item-tank-flamethrower"; 263 | "item-tank-machine-gun"; 264 | "item-train-stop"; 265 | "item-transport-belt"; 266 | "item-underground-belt"; 267 | "item-upgrade-planner"; 268 | "item-uranium-235"; 269 | "item-uranium-238"; 270 | "item-uranium-cannon-shell"; 271 | "item-uranium-fuel-cell"; 272 | "item-uranium-ore"; 273 | "item-uranium-rounds-magazine"; 274 | "item-used-up-uranium-fuel-cell"; 275 | "item-utility-science-pack"; 276 | "item-vehicle-machine-gun"; 277 | "item-wood"; 278 | "item-wooden-chest"; 279 | "fluid-crude-oil"; 280 | "fluid-heavy-oil"; 281 | "fluid-light-oil"; 282 | "fluid-lubricant"; 283 | "fluid-petroleum-gas"; 284 | "fluid-steam"; 285 | "fluid-sulfuric-acid"; 286 | "fluid-water"; 287 | ] 288 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (env 2 | (dev 3 | (flags 4 | (:standard -w "+a-4-7-9-27-29-30-32..42-44-45-48-50-60-66..70"))) 5 | (release 6 | (ocamlopt_flags 7 | (:standard -O3)) 8 | (js_of_ocaml 9 | (compilation_mode whole_program)))) 10 | 11 | (executable 12 | (public_name combc) 13 | (name main) 14 | (modes native) 15 | (modules main encode) 16 | (libraries core_kernel yojson zlib base64 memcpy utils parse compiler ast)) 17 | -------------------------------------------------------------------------------- /src/encode.ml: -------------------------------------------------------------------------------- 1 | open Bigarray 2 | 3 | let json_output name entities = 4 | `Assoc 5 | [ 6 | ( "blueprint", 7 | `Assoc 8 | [ ("entities", `List entities); ("item", `String "blueprint"); ("label", `String name) ] 9 | ); 10 | ] 11 | 12 | let to_json_string js = Yojson.Safe.pretty_to_string js 13 | 14 | let bigstring_of_string (src : string) : Zlib.bigstring = 15 | let len = String.length src in 16 | let dims = Array.of_list [ len ] in 17 | let dst = Genarray.create char c_layout dims in 18 | let src = Core_kernel.Bytes.of_string src in 19 | Memcpy.(memcpy_from_bytes (bigarray Ctypes.genarray dims char)) ~src ~dst ~dst_off:0; 20 | array1_of_genarray dst 21 | 22 | let string_of_bigstring (src : Zlib.bigstring) (len : int) : string = 23 | let dst : bytes = Bytes.init len (fun i -> Array1.unsafe_get src i) in 24 | Core_kernel.Bytes.to_string dst 25 | 26 | let encode (name : string) entities = 27 | let json = to_json_string (json_output name entities) in 28 | let in_buf = bigstring_of_string json in 29 | let in_len = Array1.dim in_buf in 30 | let deflate = 31 | Zlib.create_deflate ~level:(-1) ~strategy:Default_strategy ~window_bits:15 ~memory:8 () 32 | in 33 | deflate.data_type <- 1; 34 | (* text *) 35 | deflate.in_buf <- in_buf; 36 | let bound = Zlib.deflate_bound deflate.state in_len in 37 | deflate.out_buf <- Array1.create char c_layout bound; 38 | begin 39 | match Zlib.flate deflate Zlib.Finish with 40 | | Stream_end -> () 41 | | Ok -> failwith "Ok" 42 | | Need_dict -> failwith "Need_dict" 43 | | Buf_error -> failwith "Buf_error" 44 | | Data_error s -> failwith ("Data_error: " ^ s) 45 | end; 46 | let deflated = string_of_bigstring deflate.out_buf deflate.out_total in 47 | let enced = Base64.encode_exn deflated in 48 | "0" ^ enced 49 | -------------------------------------------------------------------------------- /src/main.ml: -------------------------------------------------------------------------------- 1 | open Utils 2 | open Parse 3 | open Ast.Bexp 4 | open Ast.Expression 5 | open Encode 6 | open Compiler.Compile 7 | open Compiler.Config 8 | open Compiler.Json 9 | 10 | let usage_msg = "combc [--output-json] " 11 | let input_file = ref None 12 | let output_json = ref false 13 | let speclist = [ ("--output-json", Arg.Set output_json, "Output json instead of blueprint string") ] 14 | let arg_fun s = input_file := Some s 15 | 16 | let () = 17 | Arg.parse speclist arg_fun usage_msg; 18 | let name = 19 | begin 20 | match !input_file with 21 | | Some s -> s 22 | | None -> 23 | prerr_endline ("Missing input file, usage:\n" ^ usage_msg); 24 | exit 1 25 | end 26 | in 27 | let directives, commands = parse name in 28 | let cfg = config_of_directives directives in 29 | set_config cfg; 30 | 31 | (* let f ctree = *) 32 | (* let c = compile_ctree_to_circuit ~optimize_b ~optimize ctree in *) 33 | 34 | (* let ast = if optimize_b then optimize_bexp ast else ast in *) 35 | (* print_endline (string_of_bexp ast); *) 36 | (* let c = compile_bexp_to_circuit~optimize o_sig ast in *) 37 | (* c *) 38 | (* in *) 39 | 40 | (* let circuits = List.map f assignment_list in *) 41 | let circuits = compile commands in 42 | let entities = json_of_compiled_circuits circuits in 43 | prerr_newline (); 44 | let js_string = to_json_string (json_output name entities) in 45 | if !output_json then print_endline js_string 46 | else 47 | let blueprint = encode name entities in 48 | print_endline blueprint 49 | -------------------------------------------------------------------------------- /src/parser/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name parse) 3 | (libraries core_kernel ast compiler menhirLib)) 4 | 5 | (ocamllex lexer) 6 | 7 | (menhir 8 | (modules parser) 9 | ; (flags --explain --table) 10 | (flags --explain -lg 1 -la 1 -lc 2)) 11 | 12 | (menhir 13 | (modules unitActionsParser) 14 | (flags --table --external-tokens Parser)) 15 | 16 | (rule 17 | (action 18 | (with-stdout-to 19 | unitActionsParser.mly 20 | (run menhir %{dep:parser.mly} --only-preprocess-u)))) 21 | 22 | (rule 23 | (with-stdout-to 24 | parser.messages.new 25 | (run menhir %{dep:parser.mly} --base parser --list-errors))) 26 | 27 | (rule 28 | (with-stdout-to 29 | parser_errors.ml 30 | (run 31 | menhir 32 | %{dep:parser.mly} 33 | --base 34 | parser 35 | --compile-errors 36 | %{dep:parser.messages}))) 37 | 38 | (rule 39 | (with-stdout-to 40 | parser.messages.updated 41 | (run 42 | menhir 43 | %{dep:parser.mly} 44 | --base 45 | parser 46 | --update-errors 47 | %{dep:parser.messages}))) 48 | 49 | (rule 50 | (alias update-parser-messages) 51 | (action 52 | (diff parser.messages parser.messages.updated))) 53 | 54 | (rule 55 | (alias generate-parser-messages) 56 | (action 57 | (diff parser.messages parser.messages.new))) 58 | -------------------------------------------------------------------------------- /src/parser/lexer.mll: -------------------------------------------------------------------------------- 1 | { 2 | open Lexing 3 | open Parser 4 | open Compiler.Composition 5 | 6 | exception Lexer_error of string 7 | 8 | let unexpected_char lexbuf (c:char) : 'a = 9 | let p = lexeme_start_p lexbuf in 10 | let o = p.pos_cnum - p.pos_bol in 11 | let l = p.pos_lnum in 12 | raise (Lexer_error ( 13 | Printf.sprintf "Unexpected character at line %d, character %d: '%c'" l o c)) 14 | let reserved_words = [ 15 | ("output", OUTPUT); 16 | ("if", IF); 17 | ("then", THEN); 18 | ("else", ELSE); 19 | ("true", LIT 1l); 20 | ("false", LIT 0l); 21 | ("concrete", CONCRETE); 22 | ("circuit", CIRCUIT_BIND); 23 | ("at", AT); 24 | ("for_concat", FOR true); 25 | ("for_union", FOR false); 26 | ("to", TO); 27 | ("downto", DOWNTO); 28 | ("int", TINT); 29 | ("signal", TSIGNAL); 30 | ("condition", TCONDITION); 31 | ("expression", TSTAMP); 32 | 33 | ("{", LBRACE); 34 | ("}", RBRACE); 35 | ( ";", SEMI); 36 | ( ",", COMMA); 37 | ( ":", COLON); 38 | ( "+", PLUS); 39 | ( "-", MINUS); 40 | ( "*", MUL); 41 | ("**", EXP); 42 | ( "/", DIV); 43 | ( "%", MOD); 44 | ( "*", MUL); 45 | ( "=", ASSIGN); 46 | ( "==", EQ); 47 | ( "!", NOT); 48 | ( "!=", NEQ); 49 | ( "!==", LNEQ); 50 | ( "===", LEQ); 51 | ("??", COALESCE); 52 | ( "&", AND); 53 | ( "|", OR); 54 | ( "^", XOR); 55 | ( "&&", LAND); 56 | ( "||", LOR); 57 | ( ">", GT); 58 | ( ">=", GTE); 59 | ( "<", LT); 60 | ( "<=", LTE); 61 | ( "<<", LSHIFT); 62 | ( ">>", RSHIFT); 63 | ( "(", LPAREN); 64 | ( ")", RPAREN); 65 | ( "\\/", UNION); 66 | ( "@", CONCAT) 67 | 68 | ] 69 | 70 | let (symbol_table : (string, Parser.token) Hashtbl.t) = Hashtbl.create 256 71 | let _ = 72 | List.iter (fun (str,t) -> Hashtbl.add symbol_table str t) reserved_words 73 | 74 | let token_lookup lexbuf = 75 | let str = lexeme lexbuf in 76 | try (Hashtbl.find symbol_table str) 77 | with _ -> IDENT str 78 | 79 | let direct = ref "" 80 | let direct_state = ref 0 81 | 82 | let directive_error () = raise (Lexer_error "Syntax Error: Directive syntax is \"# \"") 83 | 84 | } 85 | 86 | let lowercase = ['a'-'z'] 87 | let uppercase = ['A'-'Z'] 88 | let character = uppercase | lowercase 89 | let ichar = ['-' '_'] 90 | let digit = ['0'-'9'] 91 | let num = '-'?digit+ 92 | let signal = ("signal" | "fluid" | "item")'-'(character | ichar | digit)+ 93 | let single_case_word = (uppercase | ichar)(uppercase | ichar)+ | (lowercase | ichar)(lowercase | ichar)+ 94 | let ident_symbol = character | ichar | digit 95 | let mixed_case_word = (character)(ident_symbol)+ 96 | let ident = mixed_case_word | lowercase 97 | let whitespace = ['\t' ' ' '\r'] 98 | let newline = '\n' | "\r\n" | eof 99 | let comment = "//"[^'\n']*newline 100 | 101 | rule token = parse 102 | | eof { EOF } 103 | | whitespace+ { token lexbuf } (* skip whitespace *) 104 | | "/*" { comments lexbuf } 105 | | comment 106 | | '\n' { MenhirLib.LexerUtil.newline lexbuf; token lexbuf } 107 | | uppercase { SIGNAL ("signal-" ^ lexeme lexbuf) } 108 | | signal { SIGNAL (lexeme lexbuf) } 109 | | num { LIT (Int32.of_string (lexeme lexbuf)) } 110 | | ident { token_lookup lexbuf } 111 | | '#' { let p = lexeme_start_p lexbuf in 112 | if p.pos_cnum - p.pos_bol = 0 then (direct_state := 0; directive lexbuf) 113 | else raise (Lexer_error ("Syntax Error: Directives must start at the beginning of a line"))} 114 | | '{' 115 | | '}' 116 | | '+' 117 | | '-' 118 | | "??" 119 | | "**" 120 | | '*' 121 | | '/' 122 | | '%' 123 | | "<<" 124 | | ">>" 125 | | '^' 126 | | "===" 127 | | "!==" 128 | | ">=" 129 | | "<=" 130 | | "==" 131 | | "!=" 132 | | '<' 133 | | '>' 134 | | '(' 135 | | ')' 136 | | "||" 137 | | "&&" 138 | | '&' 139 | | '|' 140 | | ';' 141 | | ':' 142 | | "??" 143 | | '!' 144 | | ',' 145 | | '=' 146 | | "\\/" 147 | | "@" { token_lookup lexbuf } 148 | | _ as c { unexpected_char lexbuf c } 149 | 150 | and directive = parse 151 | | whitespace+ { if !direct_state = 0 then (directive_error ()) else directive lexbuf} 152 | | single_case_word { let s = lexeme lexbuf in 153 | if !direct_state = 0 then 154 | (direct := s; direct_state := 1; directive lexbuf) 155 | else if !direct_state = 1 then 156 | (direct_state := 2; DIRECTIVE(!direct, s)) 157 | else (directive_error ())} 158 | | mixed_case_word { raise (Lexer_error ("Directive or directive argument: \"" ^ (lexeme lexbuf) ^ "\" must be either all uppercase or all lowercase")) } 159 | | _ as c { unexpected_char lexbuf c } 160 | 161 | and comments = parse 162 | | "*/" { token lexbuf } 163 | | [^ '\n'] { comments lexbuf } 164 | | "\n" { MenhirLib.LexerUtil.newline lexbuf; comments lexbuf } 165 | | eof { raise (Lexer_error "Unclosed multiline comment")} -------------------------------------------------------------------------------- /src/parser/parse.ml: -------------------------------------------------------------------------------- 1 | open Ast.Expression 2 | open Compiler.Directive 3 | open Printf 4 | module L = MenhirLib.LexerUtil 5 | module P = Parser 6 | module E = MenhirLib.ErrorReports 7 | module I = UnitActionsParser.MenhirInterpreter 8 | 9 | let env checkpoint = 10 | match checkpoint with 11 | | I.HandlingError env -> env 12 | | _ -> assert false 13 | 14 | let state checkpoint : int = 15 | match I.top (env checkpoint) with 16 | | Some (I.Element (s, _, _, _)) -> I.number s 17 | | None -> 0 18 | 19 | let show text positions = 20 | E.extract text positions |> E.sanitize |> E.compress |> E.shorten 20 (* max width 43 *) 21 | 22 | let get text checkpoint i = 23 | match I.get i (env checkpoint) with 24 | | Some (I.Element (_, _, pos1, pos2)) -> show text (pos1, pos2) 25 | | None -> "???" (* impossible *) 26 | 27 | let succeed _v = assert false (* impossible *) 28 | 29 | let fail text buffer (checkpoint : _ I.checkpoint) = 30 | let location = L.range (E.last buffer) in 31 | let err = E.show (show text) buffer in 32 | let indication = sprintf "Syntax error %s.\n" err in 33 | try 34 | let message = Parser_errors.message (state checkpoint) in 35 | let message = E.expand (get text checkpoint) message in 36 | eprintf "%s%s%s" location indication message; 37 | exit 1 38 | with Not_found -> 39 | eprintf "%s%s%s\n" location indication 40 | "Unknown error, more informative error messages coming soon"; 41 | exit 1 42 | 43 | let slow filename text = 44 | let lexbuf = L.init filename (Lexing.from_string text) in 45 | let supplier = I.lexer_lexbuf_to_supplier Lexer.token lexbuf in 46 | let buffer, supplier = E.wrap_supplier supplier in 47 | let checkpoint = UnitActionsParser.Incremental.toplevel lexbuf.lex_curr_p in 48 | I.loop_handle succeed (fail text buffer) supplier checkpoint 49 | 50 | type parse_result = Success of directive list * command list | Error of string 51 | 52 | let fast filename : parse_result = 53 | let text, lexbuf = 54 | try 55 | let text = Core_kernel.In_channel.read_all filename in 56 | let lexbuf = L.init filename (Lexing.from_string text) in 57 | (text, lexbuf) 58 | with Sys_error s -> 59 | prerr_endline ("File error\n" ^ s); 60 | exit 1 61 | in 62 | match P.toplevel Lexer.token lexbuf with 63 | | d, a -> Success (d, a) 64 | | exception Lexer.Lexer_error msg -> 65 | prerr_endline msg; 66 | exit 1 67 | | exception DirectiveError msg -> 68 | eprintf "Directive Error: %s\n" msg; 69 | exit 1 70 | | exception Parser.Error -> Error text 71 | 72 | let parse (filename : string) : directive list * command list = 73 | (* First try fast parser, then use slow parser to generate error if fail *) 74 | begin 75 | match fast filename with 76 | | Success (d, a) -> (d, a) 77 | | Error s -> ( 78 | try slow filename s 79 | with Lexer.Lexer_error msg -> 80 | prerr_endline msg; 81 | exit 1) 82 | end 83 | -------------------------------------------------------------------------------- /src/parser/parser.mly: -------------------------------------------------------------------------------- 1 | %{ 2 | open Ast.Bexp;; 3 | open Ast.Expression;; 4 | open Compiler.Directive;; 5 | %} 6 | 7 | %token EOF 8 | %token PLUS 9 | %token MINUS 10 | %token MUL 11 | %token DIV 12 | %token MOD 13 | %token LSHIFT 14 | %token RSHIFT 15 | %token AND 16 | %token OR 17 | %token XOR 18 | %token EXP 19 | 20 | %token GT 21 | %token LT 22 | %token GTE 23 | %token LTE 24 | %token EQ 25 | %token NEQ 26 | 27 | %token NOT 28 | %token LOR 29 | %token LAND 30 | %token LEQ 31 | %token LNEQ 32 | 33 | %token LPAREN 34 | %token RPAREN 35 | 36 | %token CONCRETE 37 | %token CIRCUIT_BIND 38 | %token ASSIGN 39 | %token SEMI 40 | 41 | %token IF 42 | %token THEN 43 | %token ELSE 44 | 45 | // %token QUESTION 46 | %token COLON 47 | %token COALESCE 48 | 49 | %token DIRECTIVE 50 | %token IDENT 51 | 52 | %token UNION 53 | %token CONCAT 54 | 55 | %token OUTPUT 56 | %token AT 57 | %token COMMA 58 | 59 | %token SIGNAL 60 | %token LIT 61 | 62 | %nonassoc ELSE 63 | 64 | %left COALESCE 65 | %left LOR 66 | %left LAND 67 | %left OR 68 | %left XOR 69 | %left AND 70 | %left EQ NEQ LEQ LNEQ 71 | %left LT GT LTE GTE 72 | %left LSHIFT RSHIFT 73 | %left PLUS MINUS 74 | %left MUL DIV MOD 75 | %right EXP 76 | 77 | %nonassoc NOT 78 | 79 | %left UNION 80 | %left CONCAT 81 | 82 | %token FOR 83 | %token TO 84 | %token DOWNTO 85 | 86 | %token LBRACE 87 | %token RBRACE 88 | 89 | %token TINT 90 | %token TSIGNAL 91 | %token TCONDITION 92 | %token TSTAMP 93 | 94 | %start toplevel 95 | 96 | %on_error_reduce program 97 | 98 | %type toplevel 99 | %type bexp 100 | %type expression 101 | %% 102 | 103 | toplevel: 104 | | p=program EOF { p } 105 | 106 | program: 107 | | d=dir_seq c=c_seq { (d, c) } 108 | | c=c_seq { ([], c) } 109 | 110 | c_seq: 111 | | l=list(command) { l } 112 | 113 | dir_seq: 114 | | d1=dir_seq d2=directive { d1 @ d2 } 115 | | d=directive { d } 116 | 117 | directive: 118 | | d=DIRECTIVE { [parse_directive (fst d) (snd d)] } 119 | 120 | block: 121 | | LBRACE b=c_seq RBRACE { b } 122 | 123 | command: 124 | | CONCRETE CIRCUIT_BIND i=IDENT COLON v=arg ASSIGN b=bexp SEMI { CircuitBind (i, b, v, true) } 125 | | CIRCUIT_BIND i=IDENT COLON v=arg ASSIGN b=bexp SEMI { CircuitBind (i, b, v, false) } 126 | | CIRCUIT_BIND i=IDENT ASSIGN c=circuit SEMI { Assign (i, TCircuit, Immediate (Circuit c)) } 127 | 128 | | TINT i=IDENT ASSIGN b=bexp SEMI { Assign(i, TInt, expression_of_bexp b) } 129 | | TCONDITION i=IDENT ASSIGN b=bexp SEMI { Assign(i, TCondition, expression_of_bexp b) } 130 | | TSTAMP i=IDENT ASSIGN b=bexp SEMI { Assign(i, TStamp, Immediate (Stamp b)) } 131 | | TSIGNAL i=IDENT ASSIGN b=bexp SEMI { Assign(i, TSignal, expression_of_bexp b) } 132 | | o=output SEMI { o } 133 | 134 | output: 135 | | OUTPUT c=circuit AT t=tuple { OutputAt (Immediate (Circuit c), t) } 136 | | OUTPUT c=circuit { Output (Immediate (Circuit c)) } 137 | | OUTPUT b=bexp AT t=tuple { OutputAt (expression_of_bexp b, t) } 138 | | OUTPUT b=bexp { Output (expression_of_bexp b) } 139 | 140 | tuple: 141 | LPAREN v1=bexp COMMA v2=bexp RPAREN { (v1, v2) } 142 | 143 | circuit: 144 | | c1=circuit UNION c2=circuit { Union (c1, c2, None) } 145 | | c1=circuit CONCAT c2=circuit { Concat (c1, c2, None) } 146 | | c=expression { Expression (c, None) } 147 | | LPAREN c=circuit RPAREN { c } 148 | 149 | arg: 150 | | e=expression { e } 151 | | b=bexp { expression_of_bexp b } 152 | 153 | expression: 154 | // | b=bexp { expression_of_bexp b } 155 | | c=call { Immediate c } 156 | | i=IDENT { Immediate (Ast.Expression.Var i) } 157 | | f=for_loop { Immediate f } 158 | // | LPAREN e=expression RPAREN { e } 159 | 160 | for_loop: 161 | | op=FOR i=IDENT ASSIGN l=arg TO u=arg b=block { For (op, i, l, u, false, b) } 162 | | op=FOR i=IDENT ASSIGN l=arg DOWNTO u=arg b=block { For (op, i, l, u, true, b) } 163 | 164 | %inline call: 165 | | p=IDENT LPAREN args=separated_list(COMMA, arg) RPAREN { Call (p, args) } 166 | 167 | bexp: 168 | | IF g=bexp THEN b1=bexp ELSE b2=bexp { Conditional(g, b1, b2) } 169 | | b=b_main { b } 170 | 171 | b_main: 172 | | NOT b=bexp { Not(b) } 173 | | MINUS b=bexp { Neg(b) } 174 | | b=bop { b } 175 | | l=LIT { Lit l } 176 | | s=SIGNAL { Signal s } 177 | | v=IDENT { Var v } 178 | | LPAREN b=bexp RPAREN { b } 179 | 180 | %inline bop: 181 | | b1=bexp LOR b2=bexp { LOR(b1, b2) } 182 | | b1=bexp LAND b2=bexp { LAND(b1, b2) } 183 | | b1=bexp COALESCE b2=bexp { Conditional(b1, b1, b2) } 184 | | b1=bexp OR b2=bexp { OR(b1, b2) } 185 | | b1=bexp XOR b2=bexp { XOR(b1, b2) } 186 | | b1=bexp AND b2=bexp { AND(b1, b2) } 187 | | b1=bexp EQ b2=bexp { Eq(b1, b2) } 188 | | b1=bexp NEQ b2=bexp { Neq(b1, b2) } 189 | | b1=bexp LEQ b2=bexp { Not(XOR (BOOL b1, BOOL b2)) } 190 | | b1=bexp LNEQ b2=bexp { XOR((BOOL b1, BOOL b2)) } 191 | | b1=bexp LT b2=bexp { Lt(b1, b2) } 192 | | b1=bexp GT b2=bexp { Gt(b1, b2) } 193 | | b1=bexp LTE b2=bexp { Lte(b1, b2) } 194 | | b1=bexp GTE b2=bexp { Gte(b1, b2) } 195 | | b1=bexp LSHIFT b2=bexp { Lshift(b1, b2) } 196 | | b1=bexp RSHIFT b2=bexp { Rshift(b1, b2) } 197 | | b1=bexp PLUS b2=bexp { Plus(b1, b2) } 198 | | b1=bexp MINUS b2=bexp { Minus(b1, b2) } 199 | | b1=bexp MUL b2=bexp { Mul(b1, b2) } 200 | | b1=bexp DIV b2=bexp { Div(b1, b2) } 201 | | b1=bexp MOD b2=bexp { Mod(b1, b2) } 202 | | b1=bexp EXP b2=bexp { Exp(b1, b2) } -------------------------------------------------------------------------------- /src/utils/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name utils) 3 | (libraries yojson)) 4 | -------------------------------------------------------------------------------- /src/utils/utils.ml: -------------------------------------------------------------------------------- 1 | type json = Yojson.Safe.t 2 | 3 | let create_ctr ?(i = 1) () = 4 | let s = ref (i - 1) in 5 | fun () -> 6 | incr s; 7 | (* print_endline ("INCED, RETURNING " ^ (string_of_int !s)); *) 8 | !s 9 | 10 | let deopt_list l = 11 | List.concat 12 | @@ List.map 13 | (function 14 | | None -> [] 15 | | Some x -> [ x ]) 16 | l 17 | -------------------------------------------------------------------------------- /tools/gen_signals.py: -------------------------------------------------------------------------------- 1 | import urllib.request 2 | import re 3 | import json 4 | import sys 5 | 6 | base_url = 'https://raw.githubusercontent.com/wube/factorio-data/master/base/prototypes/' 7 | 8 | files = [('signal', '', 3), ('item', 'item-', 0), ('fluid', 'fluid-', 0)] # 3 to exclude wildcards, they are first 9 | 10 | def delete_nested(s): 11 | acc = "" 12 | line = "" 13 | ctr = 0 14 | for line in s.split("\n"): 15 | m = re.match(r"\s*(\S*)\s*:\s*(.*)", line) 16 | decs = 0 17 | if m is None: 18 | for c in line: 19 | if c == '{': 20 | ctr += 1 21 | elif c == '}': 22 | decs += 1 23 | if ctr == 1: 24 | acc += line + "\n" 25 | ctr -= decs 26 | continue 27 | 28 | value = m[2] 29 | if value == "": 30 | continue 31 | for c in value: 32 | if c == '{': 33 | ctr += 1 34 | elif c == '}': 35 | decs += 1 36 | 37 | if ctr == 1: 38 | acc += line + "\n" 39 | ctr -= decs 40 | return acc 41 | 42 | out_data = [] 43 | 44 | for f, prefix, start_index in files: 45 | url = base_url + f + '.lua' 46 | with urllib.request.urlopen(url) as data: 47 | raw = "".join([l.decode('utf-8') for l in data.readlines()]) 48 | def repl(m): 49 | return m[1] + "\"" + m[2] + "\": " + m[3] 50 | raw = re.sub(r"\s(.*--.*)\n", lambda _ : "\n", raw) # delete comments 51 | raw = re.sub(r".*\(\).*\n", lambda _ : "\n", raw) # delete lines with function calls 52 | raw = re.sub(r"([,\{]\s*)(\S*)\s*=([^,\}]*)", repl, raw) 53 | regex = re.match(r".*data:extend\(\s*\{(.*)\}\s*\).*", raw, re.DOTALL) 54 | try: 55 | data = regex[1] 56 | except: 57 | print(f"Couldn't parse '%s'" % url) 58 | exit(1) 59 | data = delete_nested(data) 60 | data = re.sub(r",(\s*\})", lambda m : m[1], data) 61 | data = '[' + data + ']' 62 | js = json.loads(data) 63 | dump = [] 64 | for entry in js: 65 | dump.append(prefix + entry['name']) 66 | data = dump[start_index:] 67 | data.sort() # puts lower priority virtual signals first, rest is irrelevant 68 | out_data.extend(data) 69 | 70 | args = sys.argv 71 | if len(args) > 1 and args[1] == '--ocaml': 72 | out = json.dumps(out_data).replace(",", ";\n") 73 | print(out) 74 | else: 75 | print("\n".join(out_data)) 76 | 77 | -------------------------------------------------------------------------------- /vscode_extension/.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | .gitignore 4 | vsc-extension-quickstart.md 5 | -------------------------------------------------------------------------------- /vscode_extension/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [Unreleased] 4 | 5 | ## [1.0.0] - 2023-09-24 6 | 7 | - Initial release - basic syntax highlighting -------------------------------------------------------------------------------- /vscode_extension/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /vscode_extension/README.md: -------------------------------------------------------------------------------- 1 | ## Features 2 | 3 | Basic syntax highlighting for the CombinatorC language. The extension will automatically run on files with the `.combc` extension. For more information on the language see https://github.com/osimon8/CombinatorC 4 | 5 | ## Release Notes 6 | 7 | Users appreciate release notes as you update your extension. 8 | 9 | ### 1.0.0 10 | 11 | Initial release of extension, basic syntax highlighting 12 | -------------------------------------------------------------------------------- /vscode_extension/example.combc: -------------------------------------------------------------------------------- 1 | #PRIMARY GREEN 2 | 3 | /* 4 | Example code to demonstrate extension 5 | Bounce light back and forth at varying rates 6 | */ 7 | 8 | int n = 10; 9 | int t = 100; 10 | 11 | signal ctr-out = signal-0; 12 | signal rate-out = R; 13 | int r = 7 * t; 14 | 15 | // increase the rate over time 16 | circuit div : rate-out = rate-out / t + 1; 17 | circuit rate = counter(r - 1, rate-out) @ div; 18 | 19 | /* 20 | test 21 | */ 22 | 23 | // count up, to be used to determine which lamp is on 24 | circuit add : ctr-out = ctr-out + 1; 25 | circuit ctr = counter2(t * 2 - 1, rate-out, ctr-out) @ add; 26 | 27 | // a line of n lamps 28 | circuit lamps = for_concat i=0 to (n - 1) { 29 | condition cnd = ctr-out == i; 30 | output lamp(cnd) at (i, 0); 31 | }; 32 | 33 | int t_by_n = t / n; 34 | 35 | // first half of counter, light up from left, second half light up from right 36 | circuit cmpt : ctr-out = if ctr-out <= t then 37 | ctr-out / t_by_n 38 | else 39 | -(ctr-out / t_by_n - 2 * (n - 1)); 40 | 41 | output rate @ ctr @ cmpt; 42 | output lamps at (0, 8); 43 | -------------------------------------------------------------------------------- /vscode_extension/language-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "comments": { 3 | // symbol used for single line comment. Remove this entry if your language does not support line comments 4 | "lineComment": "//", 5 | // symbols used for start and end a block comment. Remove this entry if your language does not support block comments 6 | "blockComment": [ "/*", "*/" ] 7 | }, 8 | // symbols used as brackets 9 | "brackets": [ 10 | ["{", "}"], 11 | ["(", ")"] 12 | ], 13 | // symbols that are auto closed when typing 14 | "autoClosingPairs": [ 15 | ["{", "}"], 16 | ["(", ")"], 17 | ], 18 | // symbols that can be used to surround a selection 19 | "surroundingPairs": [ 20 | ["{", "}"], 21 | ["(", ")"] 22 | ] 23 | } -------------------------------------------------------------------------------- /vscode_extension/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "combinatorc", 3 | "displayName": "CombinatorC", 4 | "description": "Language support for CombinatorC: A programming language for expressing practical combinator circuits that compiles directly to Factorio blueprints", 5 | "publisher": "OwenSimon", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/osimon8/CombinatorC" 9 | }, 10 | "version": "1.0.0", 11 | "engines": { 12 | "vscode": "^1.82.0" 13 | }, 14 | "categories": [ 15 | "Programming Languages" 16 | ], 17 | "contributes": { 18 | "languages": [ 19 | { 20 | "id": "combinatorc", 21 | "aliases": [ 22 | "CombinatorC", 23 | "combinatorc" 24 | ], 25 | "extensions": [ 26 | ".combc" 27 | ], 28 | "configuration": "./language-configuration.json" 29 | } 30 | ], 31 | "grammars": [ 32 | { 33 | "language": "combinatorc", 34 | "scopeName": "source.combc", 35 | "path": "./syntaxes/combinatorc.tmLanguage.json" 36 | } 37 | ] 38 | } 39 | } --------------------------------------------------------------------------------