├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── ast ├── Cargo.toml └── src │ ├── assembly.rs │ ├── contract.rs │ ├── expression.rs │ ├── function.rs │ ├── impl_from.rs │ ├── lib.rs │ ├── node.rs │ ├── source.rs │ ├── statement.rs │ └── type_name.rs ├── lexer ├── Cargo.toml └── src │ ├── lib.rs │ └── token.rs ├── lunarity ├── Cargo.toml ├── benches │ ├── ast.json │ ├── second-price-auction.rs │ └── second-price-auction.sol └── src │ └── lib.rs └── parser ├── Cargo.toml └── src ├── assembly.rs ├── contract.rs ├── error.rs ├── expect_macro.rs ├── expression.rs ├── function.rs ├── lib.rs ├── mock.rs ├── nested.rs ├── source.rs ├── statement.rs └── type_name.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | cache: 4 | cargo: true 5 | 6 | rust: 7 | - stable 8 | - beta 9 | - nightly 10 | 11 | matrix: 12 | allow_failures: 13 | - rust: nightly 14 | 15 | os: 16 | - linux 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "lunarity", 4 | "ast", 5 | "parser", 6 | "lexer", 7 | ] 8 | 9 | [profile] 10 | release = { lto = true } 11 | bench = { lto = true } 12 | -------------------------------------------------------------------------------- /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 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 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 | {project} Copyright (C) {year} {fullname} 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lunarity 2 | 3 | A high performance, correctness-oriented Solidity parser + other tools. 4 | 5 | The produced AST is mostly [grammar.txt](https://github.com/ethereum/solidity/blob/develop/docs/grammar.txt) compliant. 6 | The AST is statically typed to make sure that it is not possible to construct an AST representation of what would be 7 | illegal grammar. 8 | 9 | ## Performance 10 | 11 | It's *really* fast. 12 | 13 | ``` 14 | Running target/release/deps/second_price_auction-2b369ce54b97fb9f 15 | 16 | running 2 tests 17 | test parse_to_ast ... bench: 24,028 ns/iter (+/- 573) = 562 MB/s 18 | test tokenize ... bench: 15,037 ns/iter (+/- 1,405) = 898 MB/s 19 | ``` 20 | 21 | Neither the lexer nor the parser ever backtrack while reading bytes/tokens. The parser is using [a paginated 22 | arena allocator](https://docs.rs/toolshed/0.4.0/toolshed/struct.Arena.html) to avoid heap allocation costs 23 | (or garbage collection costs, or anything else). 24 | 25 | Feel free to rerun the benchmarks (requires nightly Rust, with rustup: `rustup run nightly cargo bench`) 26 | 27 | ## License 28 | 29 | This crate is distributed under the terms of GNU GENERAL PUBLIC LICENSE version 3.0. 30 | 31 | See [LICENSE](LICENSE) for details. 32 | -------------------------------------------------------------------------------- /ast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lunarity-ast" 3 | version = "0.2.0" 4 | authors = ["Parity Technologies "] 5 | license = "GPL-3.0" 6 | repository = "https://github.com/paritytech/lunarity" 7 | description = "Abstract Syntax Tree for the Solidity language" 8 | 9 | [dependencies] 10 | toolshed = "0.6" 11 | 12 | [dev-dependencies] 13 | pretty_assertions = "0.5" 14 | -------------------------------------------------------------------------------- /ast/src/assembly.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq)] 4 | pub struct InlineAssemblyBlock<'ast> { 5 | pub items: AssemblyItemList<'ast> 6 | } 7 | 8 | #[derive(Clone, Copy, Debug, PartialEq)] 9 | pub enum AssemblyItem<'ast> { 10 | Identifier(Identifier<'ast>), 11 | FunctionalAssemblyExpression(FunctionalAssemblyExpression<'ast>), 12 | InlineAssemblyBlock(InlineAssemblyBlock<'ast>), 13 | AssemblyLocalBinding(AssemblyLocalBinding<'ast>), 14 | AssemblyAssignment(AssemblyAssignment<'ast>), 15 | AssemblyLabel(AssemblyLabel<'ast>), 16 | NumberLiteral(Primitive<'ast>), 17 | 18 | // FIXME 19 | StringLiteral, 20 | 21 | // FIXME, 22 | HexLiteral, 23 | } 24 | 25 | #[derive(Clone, Copy, Debug, PartialEq)] 26 | pub struct AssemblyLocalBinding<'ast> { 27 | pub id: IdentifierNode<'ast>, 28 | pub init: FunctionalAssemblyExpressionNode<'ast>, 29 | } 30 | 31 | #[derive(Clone, Copy, Debug, PartialEq)] 32 | pub struct AssemblyAssignment<'ast> { 33 | pub id: IdentifierNode<'ast>, 34 | pub init: FunctionalAssemblyExpressionNode<'ast>, 35 | } 36 | 37 | #[derive(Clone, Copy, Debug, PartialEq)] 38 | pub struct AssemblyLabel<'ast> { 39 | pub id: IdentifierNode<'ast>, 40 | } 41 | 42 | #[derive(Clone, Copy, Debug, PartialEq)] 43 | pub struct FunctionalAssemblyExpression<'ast> { 44 | pub id: IdentifierNode<'ast>, 45 | pub arguments: AssemblyItemList<'ast>, 46 | } 47 | 48 | pub type AssemblyItemNode<'ast> = Node<'ast, AssemblyItem<'ast>>; 49 | pub type AssemblyItemList<'ast> = NodeList<'ast, AssemblyItem<'ast>>; 50 | pub type FunctionalAssemblyExpressionNode<'ast> = Node<'ast, FunctionalAssemblyExpression<'ast>>; 51 | pub type InlineAssemblyBlockNode<'ast> = Node<'ast, InlineAssemblyBlock<'ast>>; 52 | 53 | impl_from! { 54 | Identifier => AssemblyItem::Identifier, 55 | FunctionalAssemblyExpression => AssemblyItem::FunctionalAssemblyExpression, 56 | InlineAssemblyBlock => AssemblyItem::InlineAssemblyBlock, 57 | AssemblyLocalBinding => AssemblyItem::AssemblyLocalBinding, 58 | AssemblyAssignment => AssemblyItem::AssemblyAssignment, 59 | AssemblyLabel => AssemblyItem::AssemblyLabel, 60 | Primitive => AssemblyItem::NumberLiteral, 61 | } 62 | -------------------------------------------------------------------------------- /ast/src/contract.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq)] 4 | pub struct ContractDefinition<'ast> { 5 | pub name: IdentifierNode<'ast>, 6 | pub inherits: IdentifierList<'ast>, 7 | pub body: ContractPartList<'ast>, 8 | } 9 | 10 | #[derive(Clone, Copy, Debug, PartialEq)] 11 | pub enum ContractPart<'ast> { 12 | StateVariableDeclaration(StateVariableDeclaration<'ast>), 13 | UsingForDeclaration(UsingForDeclaration<'ast>), 14 | StructDefinition(StructDefinition<'ast>), 15 | ModifierDefinition(ModifierDefinition<'ast>), 16 | FunctionDefinition(FunctionDefinition<'ast>), 17 | EventDefinition(EventDefinition<'ast>), 18 | EnumDefinition(EnumDefinition<'ast>), 19 | } 20 | 21 | #[derive(Clone, Copy, Debug, PartialEq)] 22 | pub struct StateVariableDeclaration<'ast> { 23 | pub type_name: TypeNameNode<'ast>, 24 | pub visibility: Option>, 25 | pub constant: Option>, 26 | pub name: IdentifierNode<'ast>, 27 | pub init: Option>, 28 | } 29 | 30 | #[derive(Clone, Copy, Debug, PartialEq)] 31 | pub enum StateVariableVisibility { 32 | Public, 33 | Internal, 34 | Private, 35 | } 36 | 37 | #[derive(Clone, Copy, Debug, PartialEq)] 38 | pub struct UsingForDeclaration<'ast> { 39 | pub id: IdentifierNode<'ast>, 40 | pub type_name: Option>, 41 | } 42 | 43 | #[derive(Clone, Copy, Debug, PartialEq)] 44 | pub struct StructDefinition<'ast> { 45 | pub name: IdentifierNode<'ast>, 46 | pub body: VariableDeclarationList<'ast>, 47 | } 48 | 49 | #[derive(Clone, Copy, Debug, PartialEq)] 50 | pub struct ModifierDefinition<'ast> { 51 | pub name: IdentifierNode<'ast>, 52 | pub params: ParameterList<'ast>, 53 | pub block: BlockNode<'ast>, 54 | } 55 | 56 | #[derive(Clone, Copy, Debug, PartialEq)] 57 | pub struct EventDefinition<'ast> { 58 | pub anonymous: Option>, 59 | pub name: IdentifierNode<'ast>, 60 | pub params: IndexedParameterList<'ast>, 61 | } 62 | 63 | #[derive(Clone, Copy, Debug, PartialEq)] 64 | pub struct IndexedParameter<'ast> { 65 | pub type_name: TypeNameNode<'ast>, 66 | pub indexed: Option>, 67 | pub name: Option>, 68 | } 69 | 70 | #[derive(Clone, Copy, Debug, PartialEq)] 71 | pub struct EnumDefinition<'ast> { 72 | pub name: IdentifierNode<'ast>, 73 | pub variants: IdentifierList<'ast>, 74 | } 75 | 76 | pub type ContractPartNode<'ast> = Node<'ast, ContractPart<'ast>>; 77 | pub type ContractPartList<'ast> = NodeList<'ast, ContractPart<'ast>>; 78 | pub type IndexedParameterList<'ast> = NodeList<'ast, IndexedParameter<'ast>>; 79 | 80 | impl_from! { 81 | StateVariableDeclaration => ContractPart::StateVariableDeclaration, 82 | UsingForDeclaration => ContractPart::UsingForDeclaration, 83 | StructDefinition => ContractPart::StructDefinition, 84 | ModifierDefinition => ContractPart::ModifierDefinition, 85 | FunctionDefinition => ContractPart::FunctionDefinition, 86 | EventDefinition => ContractPart::EventDefinition, 87 | EnumDefinition => ContractPart::EnumDefinition, 88 | } 89 | -------------------------------------------------------------------------------- /ast/src/expression.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq)] 4 | pub enum Expression<'ast> { 5 | ThisExpression, 6 | IdentifierExpression(Identifier<'ast>), 7 | PrimitiveExpression(Primitive<'ast>), 8 | PrefixExpression(PrefixExpression<'ast>), 9 | PostfixExpression(PostfixExpression<'ast>), 10 | BinaryExpression(BinaryExpression<'ast>), 11 | AssignmentExpression(AssignmentExpression<'ast>), 12 | TupleExpression(TupleExpression<'ast>), 13 | CallExpression(CallExpression<'ast>), 14 | MemberAccessExpression(MemberAccessExpression<'ast>), 15 | IndexAccessExpression(IndexAccessExpression<'ast>), 16 | ConditionalExpression(ConditionalExpression<'ast>), 17 | ElementaryTypeExpression(ElementaryTypeName), 18 | } 19 | 20 | #[derive(Clone, Copy, Debug, PartialEq)] 21 | pub enum Primitive<'ast> { 22 | Bool(bool), 23 | HexNumber(&'ast str), 24 | IntegerNumber(&'ast str, NumberUnit), 25 | RationalNumber(&'ast str), 26 | String(&'ast str), 27 | } 28 | 29 | // TODO: Exact units 30 | #[derive(Clone, Copy, Debug, PartialEq)] 31 | pub enum NumberUnit { 32 | None, 33 | Ether(EtherUnit), 34 | Time(TimeUnit), 35 | } 36 | 37 | #[derive(Clone, Copy, Debug, PartialEq)] 38 | pub enum TimeUnit { 39 | Years, 40 | Months, 41 | Weeks, 42 | Days, 43 | Hours, 44 | Minutes, 45 | Seconds, 46 | } 47 | 48 | #[derive(Clone, Copy, Debug, PartialEq)] 49 | pub enum EtherUnit { 50 | Ether, 51 | Finney, 52 | Szabo, 53 | Wei, 54 | } 55 | 56 | #[derive(Clone, Copy, Debug, PartialEq)] 57 | pub enum PrefixOperator { 58 | LogicalNot, 59 | BitNot, 60 | Delete, 61 | Increment, 62 | Decrement, 63 | Plus, 64 | Minus, 65 | } 66 | 67 | #[derive(Clone, Copy, Debug, PartialEq)] 68 | pub enum PostfixOperator { 69 | Increment, 70 | Decrement, 71 | } 72 | 73 | #[derive(Clone, Copy, Debug, PartialEq)] 74 | pub enum BinaryOperator { 75 | Multiplication, 76 | Division, 77 | Remainder, 78 | Exponent, 79 | Addition, 80 | Subtraction, 81 | BitShiftLeft, 82 | BitShiftRight, 83 | Lesser, 84 | LesserEquals, 85 | Greater, 86 | GreaterEquals, 87 | Equality, 88 | Inequality, 89 | BitAnd, 90 | BitXor, 91 | BitOr, 92 | LogicalAnd, 93 | LogicalOr, 94 | } 95 | 96 | #[derive(Clone, Copy, Debug, PartialEq)] 97 | pub enum AssignmentOperator { 98 | Plain, 99 | Addition, 100 | Subtraction, 101 | Multiplication, 102 | Division, 103 | Remainder, 104 | BitShiftLeft, 105 | BitShiftRight, 106 | BitAnd, 107 | BitXor, 108 | BitOr, 109 | } 110 | 111 | #[derive(Clone, Copy, Debug, PartialEq)] 112 | pub struct PrefixExpression<'ast> { 113 | pub operator: Node<'ast, PrefixOperator>, 114 | pub operand: ExpressionNode<'ast>, 115 | } 116 | 117 | #[derive(Clone, Copy, Debug, PartialEq)] 118 | pub struct PostfixExpression<'ast> { 119 | pub operand: ExpressionNode<'ast>, 120 | pub operator: Node<'ast, PostfixOperator>, 121 | } 122 | 123 | #[derive(Clone, Copy, Debug, PartialEq)] 124 | pub struct BinaryExpression<'ast> { 125 | pub left: ExpressionNode<'ast>, 126 | pub operator: Node<'ast, BinaryOperator>, 127 | pub right: ExpressionNode<'ast>, 128 | } 129 | 130 | #[derive(Clone, Copy, Debug, PartialEq)] 131 | pub struct AssignmentExpression<'ast> { 132 | pub left: ExpressionNode<'ast>, 133 | pub operator: Node<'ast, AssignmentOperator>, 134 | pub right: ExpressionNode<'ast>, 135 | } 136 | 137 | #[derive(Clone, Copy, Debug, PartialEq)] 138 | pub struct TupleExpression<'ast> { 139 | pub expressions: ExpressionList<'ast>, 140 | } 141 | 142 | #[derive(Clone, Copy, Debug, PartialEq)] 143 | pub struct CallExpression<'ast> { 144 | pub callee: ExpressionNode<'ast>, 145 | pub arguments: ExpressionList<'ast>, 146 | } 147 | 148 | #[derive(Clone, Copy, Debug, PartialEq)] 149 | pub struct MemberAccessExpression<'ast> { 150 | pub object: ExpressionNode<'ast>, 151 | pub member: IdentifierNode<'ast>, 152 | } 153 | 154 | #[derive(Clone, Copy, Debug, PartialEq)] 155 | pub struct IndexAccessExpression<'ast> { 156 | pub array: ExpressionNode<'ast>, 157 | pub index: Option>, 158 | } 159 | 160 | #[derive(Clone, Copy, Debug, PartialEq)] 161 | pub struct ConditionalExpression<'ast> { 162 | pub test: ExpressionNode<'ast>, 163 | pub consequent: ExpressionNode<'ast>, 164 | pub alternate: ExpressionNode<'ast>, 165 | } 166 | 167 | pub use self::Expression::ThisExpression; 168 | 169 | pub type ExpressionNode<'ast> = Node<'ast, Expression<'ast>>; 170 | pub type ExpressionList<'ast> = NodeList<'ast, Expression<'ast>>; 171 | 172 | impl<'ast> From for Expression<'ast> { 173 | #[inline] 174 | fn from(val: ElementaryTypeName) -> Expression<'ast> { 175 | Expression::ElementaryTypeExpression(val) 176 | } 177 | } 178 | 179 | impl_from! { 180 | Identifier => Expression::IdentifierExpression, 181 | Primitive => Expression::PrimitiveExpression, 182 | PrefixExpression => Expression::PrefixExpression, 183 | PostfixExpression => Expression::PostfixExpression, 184 | BinaryExpression => Expression::BinaryExpression, 185 | AssignmentExpression => Expression::AssignmentExpression, 186 | TupleExpression => Expression::TupleExpression, 187 | CallExpression => Expression::CallExpression, 188 | MemberAccessExpression => Expression::MemberAccessExpression, 189 | IndexAccessExpression => Expression::IndexAccessExpression, 190 | ConditionalExpression => Expression::ConditionalExpression, 191 | } 192 | -------------------------------------------------------------------------------- /ast/src/function.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | #[derive(Clone, Copy, Debug, PartialEq)] 4 | pub struct FunctionDefinition<'ast> { 5 | pub name: Option>, 6 | pub params: ParameterList<'ast>, 7 | pub visibility: Option>, 8 | pub mutability: Option>, 9 | pub modifiers: ModifierInvocationList<'ast>, 10 | pub returns: ParameterList<'ast>, 11 | pub block: Option>, 12 | } 13 | 14 | #[derive(Clone, Copy, Debug, PartialEq)] 15 | pub enum FunctionVisibility { 16 | External, 17 | Public, 18 | Internal, 19 | Private, 20 | } 21 | 22 | #[derive(Clone, Copy, Debug, PartialEq)] 23 | pub enum StateMutability { 24 | Pure, 25 | Constant, 26 | View, 27 | Payable, 28 | } 29 | 30 | #[derive(Clone, Copy, Debug, PartialEq)] 31 | pub struct ModifierInvocation<'ast> { 32 | pub id: IdentifierNode<'ast>, 33 | pub arguments: ExpressionList<'ast>, 34 | } 35 | 36 | #[derive(Clone, Copy, Debug, PartialEq)] 37 | pub struct Parameter<'ast> { 38 | pub type_name: TypeNameNode<'ast>, 39 | pub name: Option>, 40 | } 41 | 42 | pub type ParameterList<'ast> = NodeList<'ast, Parameter<'ast>>; 43 | pub type ModifierInvocationList<'ast> = NodeList<'ast, ModifierInvocation<'ast>>; 44 | -------------------------------------------------------------------------------- /ast/src/impl_from.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! impl_from { 3 | ($( $type:ident => $enum:ident :: $variant:ident, )*) => ($( 4 | impl<'ast> From<$type<'ast>> for $enum<'ast> { 5 | #[inline] 6 | fn from(val: $type<'ast>) -> Self { 7 | $enum::$variant(val) 8 | } 9 | } 10 | )*) 11 | } 12 | -------------------------------------------------------------------------------- /ast/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate toolshed; 2 | 3 | #[cfg(test)] 4 | #[macro_use] 5 | extern crate pretty_assertions; 6 | 7 | #[macro_use] 8 | mod impl_from; 9 | mod node; 10 | mod source; 11 | mod contract; 12 | mod function; 13 | mod type_name; 14 | mod expression; 15 | mod statement; 16 | mod assembly; 17 | 18 | use toolshed::list::{List, UnsafeList}; 19 | use toolshed::Arena; 20 | use std::marker::PhantomData; 21 | 22 | pub use self::node::{Node, NodeInner, OptionalLocation}; 23 | pub use self::source::*; 24 | pub use self::contract::*; 25 | pub use self::function::*; 26 | pub use self::type_name::*; 27 | pub use self::expression::*; 28 | pub use self::statement::*; 29 | pub use self::assembly::*; 30 | 31 | /// Useful for boolean flags that need location information via FlagNode, 32 | /// for example: `indexed` or `anonymous`. 33 | #[derive(Clone, Copy, Debug, PartialEq)] 34 | pub struct Flag; 35 | 36 | pub type Identifier<'ast> = &'ast str; 37 | pub type StringLiteral<'ast> = &'ast str; 38 | pub type VersionLiteral<'ast> = &'ast str; 39 | 40 | pub type FlagNode<'ast> = Node<'ast, Flag>; 41 | pub type NodeList<'ast, T> = List<'ast, Node<'ast, T>>; 42 | pub type SourceUnitNode<'ast> = Node<'ast, SourceUnit<'ast>>; 43 | pub type SourceUnitList<'ast> = NodeList<'ast, SourceUnit<'ast>>; 44 | pub type IdentifierNode<'ast> = Node<'ast, Identifier<'ast>>; 45 | pub type IdentifierList<'ast> = NodeList<'ast, Identifier<'ast>>; 46 | pub type StringLiteralNode<'ast> = Node<'ast, StringLiteral<'ast>>; 47 | 48 | 49 | /// A Solidity source code parsed to an AST 50 | pub struct Program<'ast> { 51 | /// `SourceUnitList<'ast>` converted to an `UnsafeList` to deal with 52 | /// the fact that the `Arena` on which it lives is also in this struct. 53 | body: UnsafeList, 54 | 55 | /// `Arena` on which the entire AST is allocated. 56 | arena: Arena, 57 | 58 | /// For lifetime safety :). 59 | _phantom: PhantomData> 60 | } 61 | 62 | impl<'ast> Program<'ast> { 63 | #[inline] 64 | pub fn new(body: UnsafeList, arena: Arena) -> Self { 65 | Program { 66 | body, 67 | arena, 68 | _phantom: PhantomData, 69 | } 70 | } 71 | 72 | /// Get the list of `SourceUnit`s. 73 | #[inline] 74 | pub fn body(&self) -> SourceUnitList<'ast> { 75 | unsafe { self.body.into_list() } 76 | } 77 | 78 | /// Get a reference to the `Arena` on which the AST is allocated. 79 | #[inline] 80 | pub fn arena(&'ast self) -> &'ast Arena { 81 | &self.arena 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /ast/src/node.rs: -------------------------------------------------------------------------------- 1 | use toolshed::CopyCell; 2 | use std::ops::Deref; 3 | use std::fmt::{self, Debug}; 4 | 5 | pub trait OptionalLocation { 6 | fn start(&self) -> Option; 7 | fn end(&self) -> Option; 8 | } 9 | 10 | impl<'ast, T> OptionalLocation for Option> { 11 | #[inline] 12 | fn start(&self) -> Option { 13 | match *self { 14 | Some(ref node) => Some(node.start), 15 | None => None, 16 | } 17 | } 18 | 19 | #[inline] 20 | fn end(&self) -> Option { 21 | match *self { 22 | Some(ref node) => Some(node.end), 23 | None => None, 24 | } 25 | } 26 | } 27 | 28 | /// `Node` is a specialized `Cell` that holds a reference to T instead of T. 29 | /// `Node` has defined lifetime and implements `Defer` for convenience. 30 | #[derive(Clone, Copy)] 31 | pub struct Node<'ast, T: 'ast> { 32 | inner: CopyCell<&'ast NodeInner> 33 | } 34 | 35 | #[derive(Clone, Copy, PartialEq)] 36 | pub struct NodeInner { 37 | pub start: u32, 38 | pub end: u32, 39 | pub value: T, 40 | } 41 | 42 | impl NodeInner { 43 | #[inline] 44 | pub fn new(start: u32, end: u32, value: T) -> Self { 45 | NodeInner { 46 | start, 47 | end, 48 | value, 49 | } 50 | } 51 | } 52 | 53 | impl<'ast, T: 'ast> Node<'ast, T> { 54 | #[inline] 55 | pub fn new(ptr: &'ast NodeInner) -> Self { 56 | Node { 57 | inner: CopyCell::new(ptr) 58 | } 59 | } 60 | 61 | #[inline] 62 | pub fn set(&self, ptr: &'ast NodeInner) { 63 | self.inner.set(ptr) 64 | } 65 | 66 | #[inline] 67 | pub fn get_mut(&mut self) -> &mut &'ast NodeInner { 68 | self.inner.get_mut() 69 | } 70 | } 71 | 72 | impl<'ast, T: 'ast> Deref for Node<'ast, T> { 73 | type Target = NodeInner; 74 | 75 | #[inline] 76 | fn deref(&self) -> &Self::Target { 77 | self.inner.get() 78 | } 79 | } 80 | 81 | impl<'ast, T: 'ast + PartialEq> PartialEq for Node<'ast, T> { 82 | #[inline] 83 | fn eq(&self, other: &Self) -> bool { 84 | self.deref().eq(other.deref()) 85 | } 86 | } 87 | 88 | impl<'ast, T: 'ast + Debug> Debug for Node<'ast, T> { 89 | #[inline] 90 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 91 | Debug::fmt(self.deref(), f) 92 | } 93 | } 94 | 95 | impl Debug for NodeInner { 96 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 97 | write!(f, "({}:{}) ", self.start, self.end)?; 98 | 99 | Debug::fmt(&self.value, f) 100 | } 101 | } 102 | 103 | #[cfg(test)] 104 | mod node { 105 | use super::*; 106 | 107 | #[test] 108 | fn ptr() { 109 | let foo = NodeInner::new(0, 0, "foo"); 110 | let bar = NodeInner::new(0, 0, "bar"); 111 | 112 | let foo_ptr = Node::new(&foo); 113 | let bar_ptr = foo_ptr.clone(); 114 | 115 | assert_eq!(*foo_ptr, NodeInner::new(0, 0, "foo")); 116 | assert_eq!(*bar_ptr, NodeInner::new(0, 0, "foo")); 117 | 118 | bar_ptr.set(&bar); 119 | 120 | assert_eq!(*foo_ptr, NodeInner::new(0, 0, "foo")); 121 | assert_eq!(*bar_ptr, NodeInner::new(0, 0, "bar")); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ast/src/source.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | /// A `SourceUnit` is the top level construct of the grammar. 4 | #[derive(Clone, Copy, Debug, PartialEq)] 5 | pub enum SourceUnit<'ast> { 6 | PragmaDirective(PragmaDirective<'ast>), 7 | ImportDirective(ImportDirective<'ast>), 8 | ContractDefinition(ContractDefinition<'ast>), 9 | } 10 | 11 | #[derive(Clone, Copy, Debug, PartialEq)] 12 | pub struct PragmaDirective<'ast> { 13 | pub version: &'ast str, 14 | } 15 | 16 | #[derive(Clone, Copy, Debug, PartialEq)] 17 | pub struct Import<'ast> { 18 | pub symbol: IdentifierNode<'ast>, 19 | pub alias: Option>, 20 | } 21 | 22 | #[derive(Clone, Copy, Debug, PartialEq)] 23 | pub enum ImportDirective<'ast> { 24 | /// 'import' StringLiteral ('as' Identifier)? ';' 25 | Global { 26 | source: Node<'ast, StringLiteral<'ast>>, 27 | alias: Option>, 28 | }, 29 | 30 | /// 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' 31 | From { 32 | symbol: Option>, 33 | alias: Option>, 34 | source: Node<'ast, StringLiteral<'ast>>, 35 | }, 36 | 37 | /// 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' 38 | ManyFrom { 39 | imports: NodeList<'ast, Import<'ast>>, 40 | source: Node<'ast, StringLiteral<'ast>>, 41 | }, 42 | } 43 | 44 | impl_from! { 45 | PragmaDirective => SourceUnit::PragmaDirective, 46 | ImportDirective => SourceUnit::ImportDirective, 47 | ContractDefinition => SourceUnit::ContractDefinition, 48 | } 49 | -------------------------------------------------------------------------------- /ast/src/statement.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::List; 2 | 3 | use {*}; 4 | 5 | #[derive(Clone, Copy, Debug, PartialEq)] 6 | pub enum Statement<'ast> { 7 | /// Only available in modifiers 8 | Placeholder, 9 | IfStatement(IfStatement<'ast>), 10 | WhileStatement(WhileStatement<'ast>), 11 | ForStatement(ForStatement<'ast>), 12 | BlockStatement(Block<'ast>), 13 | InlineAssemblyStatement(InlineAssemblyStatement<'ast>), 14 | DoWhileStatement(DoWhileStatement<'ast>), 15 | ContinueStatement, 16 | BreakStatement, 17 | ReturnStatement(ReturnStatement<'ast>), 18 | ThrowStatement, 19 | VariableDefinitionStatement(VariableDefinitionStatement<'ast>), 20 | InferredDefinitionStatement(InferredDefinitionStatement<'ast>), 21 | ExpressionStatement(ExpressionNode<'ast>), 22 | } 23 | 24 | /// Used in the `for` loop initialization. 25 | #[derive(Clone, Copy, Debug, PartialEq)] 26 | pub enum SimpleStatement<'ast> { 27 | VariableDefinitionStatement(VariableDefinitionStatement<'ast>), 28 | InferredDefinitionStatement(InferredDefinitionStatement<'ast>), 29 | ExpressionStatement(ExpressionNode<'ast>), 30 | } 31 | 32 | #[derive(Clone, Copy, Debug, PartialEq)] 33 | pub struct IfStatement<'ast> { 34 | pub test: ExpressionNode<'ast>, 35 | pub consequent: StatementNode<'ast>, 36 | pub alternate: Option>, 37 | } 38 | 39 | #[derive(Clone, Copy, Debug, PartialEq)] 40 | pub struct WhileStatement<'ast> { 41 | pub test: ExpressionNode<'ast>, 42 | pub body: StatementNode<'ast>, 43 | } 44 | 45 | #[derive(Clone, Copy, Debug, PartialEq)] 46 | pub struct ForStatement<'ast> { 47 | pub init: Option>, 48 | pub test: Option>, 49 | pub update: Option>, 50 | pub body: StatementNode<'ast>, 51 | } 52 | 53 | #[derive(Clone, Copy, Debug, PartialEq)] 54 | pub struct Block<'ast> { 55 | pub body: StatementList<'ast>, 56 | } 57 | 58 | #[derive(Clone, Copy, Debug, PartialEq)] 59 | pub struct InlineAssemblyStatement<'ast> { 60 | pub string: Option>, 61 | pub block: InlineAssemblyBlockNode<'ast>, 62 | } 63 | 64 | #[derive(Clone, Copy, Debug, PartialEq)] 65 | pub struct DoWhileStatement<'ast> { 66 | pub body: StatementNode<'ast>, 67 | pub test: ExpressionNode<'ast>, 68 | } 69 | 70 | #[derive(Clone, Copy, Debug, PartialEq)] 71 | pub struct ReturnStatement<'ast> { 72 | pub value: Option>, 73 | } 74 | 75 | /// explicitly typed, can have storage flag, init is optional 76 | #[derive(Clone, Copy, Debug, PartialEq)] 77 | pub struct VariableDefinitionStatement<'ast> { 78 | pub declaration: VariableDeclarationNode<'ast>, 79 | pub init: Option>, 80 | } 81 | 82 | /// type inferred via `var`, cannot have storage flag, init is mandatory 83 | #[derive(Clone, Copy, Debug, PartialEq)] 84 | pub struct InferredDefinitionStatement<'ast> { 85 | pub ids: List<'ast, Option>>, 86 | pub init: ExpressionNode<'ast>, 87 | } 88 | 89 | pub use self::Statement::{Placeholder, BreakStatement, ContinueStatement, ThrowStatement}; 90 | 91 | pub type StatementNode<'ast> = Node<'ast, Statement<'ast>>; 92 | pub type StatementList<'ast> = NodeList<'ast, Statement<'ast>>; 93 | pub type SimpleStatementNode<'ast> = Node<'ast, SimpleStatement<'ast>>; 94 | pub type BlockNode<'ast> = Node<'ast, Block<'ast>>; 95 | 96 | impl_from! { 97 | IfStatement => Statement::IfStatement, 98 | WhileStatement => Statement::WhileStatement, 99 | ForStatement => Statement::ForStatement, 100 | DoWhileStatement => Statement::DoWhileStatement, 101 | ReturnStatement => Statement::ReturnStatement, 102 | VariableDefinitionStatement => Statement::VariableDefinitionStatement, 103 | VariableDefinitionStatement => SimpleStatement::VariableDefinitionStatement, 104 | InferredDefinitionStatement => Statement::InferredDefinitionStatement, 105 | InferredDefinitionStatement => SimpleStatement::InferredDefinitionStatement, 106 | ExpressionNode => Statement::ExpressionStatement, 107 | ExpressionNode => SimpleStatement::ExpressionStatement, 108 | Block => Statement::BlockStatement, 109 | InlineAssemblyStatement => Statement::InlineAssemblyStatement, 110 | } 111 | -------------------------------------------------------------------------------- /ast/src/type_name.rs: -------------------------------------------------------------------------------- 1 | use {*}; 2 | 3 | #[derive(Clone, Copy, PartialEq, Debug)] 4 | pub enum TypeName<'ast> { 5 | ElementaryTypeName(ElementaryTypeName), 6 | UserDefinedTypeName(Identifier<'ast>), 7 | Mapping(Mapping<'ast>), 8 | ArrayTypeName, 9 | FunctionTypeName, 10 | } 11 | 12 | #[derive(Clone, Copy, PartialEq, Debug)] 13 | pub struct VariableDeclaration<'ast> { 14 | pub type_name: TypeNameNode<'ast>, 15 | pub location: Option>, 16 | pub id: IdentifierNode<'ast>, 17 | } 18 | 19 | #[derive(Clone, Copy, PartialEq, Debug)] 20 | pub enum StorageLocation { 21 | Memory, 22 | Storage, 23 | } 24 | 25 | #[derive(Clone, Copy, PartialEq, Debug)] 26 | pub enum ElementaryTypeName { 27 | Address, 28 | Bool, 29 | String, 30 | Bytes, 31 | Int(u8), 32 | Uint(u8), 33 | Byte(u8), 34 | Fixed(u8, u8), 35 | Ufixed(u8, u8), 36 | } 37 | 38 | #[derive(Clone, Copy, PartialEq, Debug)] 39 | pub struct Mapping<'ast> { 40 | pub from: ElementaryTypeNameNode<'ast>, 41 | pub to: TypeNameNode<'ast>, 42 | } 43 | 44 | pub type TypeNameNode<'ast> = Node<'ast, TypeName<'ast>>; 45 | pub type ElementaryTypeNameNode<'ast> = Node<'ast, ElementaryTypeName>; 46 | pub type VariableDeclarationNode<'ast> = Node<'ast, VariableDeclaration<'ast>>; 47 | pub type VariableDeclarationList<'ast> = NodeList<'ast, VariableDeclaration<'ast>>; 48 | 49 | impl<'ast> From for TypeName<'ast> { 50 | #[inline] 51 | fn from(elementary: ElementaryTypeName) -> Self { 52 | TypeName::ElementaryTypeName(elementary) 53 | } 54 | } 55 | 56 | impl_from! { 57 | Identifier => TypeName::UserDefinedTypeName, 58 | Mapping => TypeName::Mapping, 59 | } 60 | -------------------------------------------------------------------------------- /lexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lunarity-lexer" 3 | version = "0.2.1" 4 | authors = ["Parity Technologies "] 5 | license = "GPL-3.0" 6 | repository = "https://github.com/paritytech/lunarity" 7 | description = "A high performance Solidity language Lexer" 8 | 9 | [dependencies] 10 | logos = { version = "0.7.7", features = ["nul_term_source"] } 11 | -------------------------------------------------------------------------------- /lexer/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate logos; 2 | 3 | mod token; 4 | 5 | pub use self::token::Token; 6 | pub use logos::{Logos, lookup}; 7 | pub type Lexer = logos::Lexer; 8 | 9 | // FIXME: This should probably be handled with a callback 10 | #[inline] 11 | pub fn read_pragma<'source, S: logos::Source<'source>>(lex: &mut Lexer) -> S::Slice { 12 | use logos::internal::LexerInternal; 13 | 14 | loop { 15 | match lex.read() { 16 | 0x01...0x20 => lex.bump(), 17 | _ => break, 18 | } 19 | } 20 | 21 | let start = lex.range().start; 22 | 23 | loop { 24 | match lex.read() { 25 | 0 => { 26 | lex.token = Token::UnexpectedEndOfProgram; 27 | let end = lex.range().end; 28 | 29 | return lex.source.slice(start..end).expect("0 guarantees being at the end; qed"); 30 | }, 31 | b';' => { 32 | let end = lex.range().end; 33 | 34 | let version = lex.source.slice(start..end).expect("Still within bounds; qed"); 35 | 36 | lex.token = Token::Semicolon; 37 | lex.bump(); 38 | 39 | return version; 40 | }, 41 | _ => lex.bump(), 42 | } 43 | } 44 | } 45 | 46 | 47 | #[cfg(test)] 48 | mod test { 49 | use super::*; 50 | use self::Token::*; 51 | use logos::Logos; 52 | 53 | fn assert_lex(source: &str, tokens: T) 54 | where 55 | T: AsRef<[(Token, &'static str)]> 56 | { 57 | let mut lex = Token::lexer(source); 58 | 59 | for &(ref token, slice) in tokens.as_ref() { 60 | assert!( 61 | lex.token == *token && lex.slice() == slice, 62 | "\n\n\n\tExpected {:?}({:?}), found {:?}({:?}) instead!\n\n\n", token, slice, lex.token, lex.slice() 63 | ); 64 | lex.advance(); 65 | } 66 | 67 | assert_eq!(lex.token, EndOfProgram); 68 | } 69 | 70 | #[test] 71 | fn empty_lexer() { 72 | assert_lex(" ", []); 73 | } 74 | 75 | #[test] 76 | fn line_comment() { 77 | assert_lex(" // foo\nbar", [(Identifier, "bar")]); 78 | } 79 | 80 | #[test] 81 | fn block_comment() { 82 | assert_lex(" /* foo */ bar", [(Identifier, "bar")]); 83 | assert_lex(" /* foo **/ bar", [(Identifier, "bar")]); 84 | assert_lex(" /* foo ***/ bar", [(Identifier, "bar")]); 85 | assert_lex(" /* foo ****/ bar", [(Identifier, "bar")]); 86 | assert_lex(" /* foo *****/ bar", [(Identifier, "bar")]); 87 | assert_lex(" /* foo ", [(UnexpectedEndOfProgram, "/* foo ")]); 88 | } 89 | 90 | #[test] 91 | fn identifiers() { 92 | assert_lex( 93 | " 94 | foo _foo $foo $_foo _ $ $$ fooBar BarFoo foo10 $1 95 | ", 96 | &[ 97 | (Identifier, "foo"), 98 | (Identifier, "_foo"), 99 | (Identifier, "$foo"), 100 | (Identifier, "$_foo"), 101 | (Identifier, "_"), 102 | (Identifier, "$"), 103 | (Identifier, "$$"), 104 | (Identifier, "fooBar"), 105 | (Identifier, "BarFoo"), 106 | (Identifier, "foo10"), 107 | (Identifier, "$1"), 108 | ][..] 109 | ); 110 | } 111 | 112 | #[test] 113 | fn controls() { 114 | assert_lex( 115 | " 116 | ; : , . ( ) { } [ ] => 117 | ", 118 | &[ 119 | (Semicolon, ";"), 120 | (Colon, ":"), 121 | (Comma, ","), 122 | (Accessor, "."), 123 | (ParenOpen, "("), 124 | (ParenClose, ")"), 125 | (BraceOpen, "{"), 126 | (BraceClose, "}"), 127 | (BracketOpen, "["), 128 | (BracketClose, "]"), 129 | (Arrow, "=>"), 130 | ][..] 131 | ); 132 | } 133 | 134 | #[test] 135 | fn literals() { 136 | assert_lex( 137 | r#" 138 | true false 0 42 0xDEAD 0Xdead 3.14 3.14E+2 .12345 139 | 5.1e2 42e-3 500E-1 500.1 10.000 'foo bar' "doge to the moon" 140 | "#, 141 | &[ 142 | (LiteralTrue, "true"), 143 | (LiteralFalse, "false"), 144 | (LiteralInteger, "0"), 145 | (LiteralInteger, "42"), 146 | (LiteralHex, "0xDEAD"), 147 | (LiteralHex, "0Xdead"), 148 | (LiteralRational, "3.14"), 149 | (LiteralInteger, "3.14E+2"), 150 | (LiteralRational, ".12345"), 151 | (LiteralInteger, "5.1e2"), 152 | (LiteralRational, "42e-3"), 153 | (LiteralInteger, "500E-1"), 154 | (LiteralRational, "500.1"), 155 | (LiteralInteger, "10.000"), 156 | (LiteralString, "'foo bar'"), 157 | (LiteralString, "\"doge to the moon\""), 158 | ][..] 159 | ); 160 | } 161 | 162 | #[test] 163 | fn strings() { 164 | assert_lex(r#" 165 | foo 166 | "\x19Ethereum Signed Message:\n47Please take my Ether and try to build Polkadot." 167 | "#, 168 | &[ 169 | (Identifier, "foo"), 170 | (LiteralString, r#""\x19Ethereum Signed Message:\n47Please take my Ether and try to build Polkadot.""#), 171 | ]) 172 | } 173 | 174 | #[test] 175 | fn keywords() { 176 | assert_lex( 177 | " 178 | anonymous as assembly break constant continue do 179 | delete else external for hex if indexed internal import 180 | is mapping memory new payable public pragma private pure 181 | return returns storage super this throw using view while 182 | ", 183 | &[ 184 | (KeywordAnonymous, "anonymous"), 185 | (KeywordAs, "as"), 186 | (KeywordAssembly, "assembly"), 187 | (KeywordBreak, "break"), 188 | (KeywordConstant, "constant"), 189 | (KeywordContinue, "continue"), 190 | (KeywordDo, "do"), 191 | (KeywordDelete, "delete"), 192 | (KeywordElse, "else"), 193 | (KeywordExternal, "external"), 194 | (KeywordFor, "for"), 195 | (KeywordHex, "hex"), 196 | (KeywordIf, "if"), 197 | (KeywordIndexed, "indexed"), 198 | (KeywordInternal, "internal"), 199 | (KeywordImport, "import"), 200 | (KeywordIs, "is"), 201 | (KeywordMapping, "mapping"), 202 | (KeywordMemory, "memory"), 203 | (KeywordNew, "new"), 204 | (KeywordPayable, "payable"), 205 | (KeywordPublic, "public"), 206 | (KeywordPragma, "pragma"), 207 | (KeywordPrivate, "private"), 208 | (KeywordPure, "pure"), 209 | (KeywordReturn, "return"), 210 | (KeywordReturns, "returns"), 211 | (KeywordStorage, "storage"), 212 | (KeywordSuper, "super"), 213 | (KeywordThis, "this"), 214 | (KeywordThrow, "throw"), 215 | (KeywordUsing, "using"), 216 | (KeywordView, "view"), 217 | (KeywordWhile, "while"), 218 | ][..] 219 | ); 220 | } 221 | 222 | #[test] 223 | fn declarations() { 224 | assert_lex( 225 | " 226 | var function event modifier struct 227 | enum contract library interface 228 | ", 229 | &[ 230 | (DeclarationVar, "var"), 231 | (DeclarationFunction, "function"), 232 | (DeclarationEvent, "event"), 233 | (DeclarationModifier, "modifier"), 234 | (DeclarationStruct, "struct"), 235 | (DeclarationEnum, "enum"), 236 | (DeclarationContract, "contract"), 237 | (DeclarationLibrary, "library"), 238 | (DeclarationInterface, "interface"), 239 | ][..] 240 | ); 241 | } 242 | 243 | #[test] 244 | fn units() { 245 | assert_lex( 246 | " 247 | wei szabo finney ether 248 | seconds minutes hours days weeks years 249 | ", 250 | &[ 251 | (UnitWei, "wei"), 252 | (UnitSzabo, "szabo"), 253 | (UnitFinney, "finney"), 254 | (UnitEther, "ether"), 255 | (UnitTimeSeconds, "seconds"), 256 | (UnitTimeMinutes, "minutes"), 257 | (UnitTimeHours, "hours"), 258 | (UnitTimeDays, "days"), 259 | (UnitTimeWeeks, "weeks"), 260 | (UnitTimeYears, "years"), 261 | ][..] 262 | ); 263 | } 264 | 265 | #[test] 266 | fn reserved_words() { 267 | assert_lex( 268 | " 269 | abstract after case catch default final in 270 | inline let match null of relocatable static 271 | switch try type typeof 272 | ", 273 | &[ 274 | (ReservedWord, "abstract"), 275 | (ReservedWord, "after"), 276 | (ReservedWord, "case"), 277 | (ReservedWord, "catch"), 278 | (ReservedWord, "default"), 279 | (ReservedWord, "final"), 280 | (ReservedWord, "in"), 281 | (ReservedWord, "inline"), 282 | (ReservedWord, "let"), 283 | (ReservedWord, "match"), 284 | (ReservedWord, "null"), 285 | (ReservedWord, "of"), 286 | (ReservedWord, "relocatable"), 287 | (ReservedWord, "static"), 288 | (ReservedWord, "switch"), 289 | (ReservedWord, "try"), 290 | (ReservedWord, "type"), 291 | (ReservedWord, "typeof"), 292 | ][..] 293 | ); 294 | } 295 | 296 | #[test] 297 | fn builtins() { 298 | assert_lex( 299 | " 300 | block msg tx now suicide selfdestruct addmod 301 | mulmod sha3 keccak256 log0 log1 log2 log3 log4 302 | sha256 ecrecover ripemd160 assert revert require 303 | ", 304 | &[ 305 | (IdentifierBuiltin, "block"), 306 | (IdentifierBuiltin, "msg"), 307 | (IdentifierBuiltin, "tx"), 308 | (IdentifierBuiltin, "now"), 309 | (IdentifierBuiltin, "suicide"), 310 | (IdentifierBuiltin, "selfdestruct"), 311 | (IdentifierBuiltin, "addmod"), 312 | (IdentifierBuiltin, "mulmod"), 313 | (IdentifierBuiltin, "sha3"), 314 | (IdentifierBuiltin, "keccak256"), 315 | (IdentifierBuiltin, "log0"), 316 | (IdentifierBuiltin, "log1"), 317 | (IdentifierBuiltin, "log2"), 318 | (IdentifierBuiltin, "log3"), 319 | (IdentifierBuiltin, "log4"), 320 | (IdentifierBuiltin, "sha256"), 321 | (IdentifierBuiltin, "ecrecover"), 322 | (IdentifierBuiltin, "ripemd160"), 323 | (IdentifierBuiltin, "assert"), 324 | (IdentifierBuiltin, "revert"), 325 | (IdentifierBuiltin, "require"), 326 | ][..] 327 | ); 328 | } 329 | 330 | #[test] 331 | fn operators() { 332 | assert_lex( 333 | " 334 | ++ -- ! ~ * / % ** + - << >> 335 | < <= > >= == != & ^ | && || ? 336 | = += -= *= /= %= <<= >>= &= ^= |= 337 | ", 338 | &[ 339 | (OperatorIncrement, "++"), 340 | (OperatorDecrement, "--"), 341 | (OperatorLogicalNot, "!"), 342 | (OperatorBitNot, "~"), 343 | (OperatorMultiplication, "*"), 344 | (OperatorDivision, "/"), 345 | (OperatorRemainder, "%"), 346 | (OperatorExponent, "**"), 347 | (OperatorAddition, "+"), 348 | (OperatorSubtraction, "-"), 349 | (OperatorBitShiftLeft, "<<"), 350 | (OperatorBitShiftRight, ">>"), 351 | (OperatorLesser, "<"), 352 | (OperatorLesserEquals, "<="), 353 | (OperatorGreater, ">"), 354 | (OperatorGreaterEquals, ">="), 355 | (OperatorEquality, "=="), 356 | (OperatorInequality, "!="), 357 | (OperatorBitAnd, "&"), 358 | (OperatorBitXor, "^"), 359 | (OperatorBitOr, "|"), 360 | (OperatorLogicalAnd, "&&"), 361 | (OperatorLogicalOr, "||"), 362 | (OperatorConditional, "?"), 363 | (Assign, "="), 364 | (AssignAddition, "+="), 365 | (AssignSubtraction, "-="), 366 | (AssignMultiplication, "*="), 367 | (AssignDivision, "/="), 368 | (AssignRemainder, "%="), 369 | (AssignBitShiftLeft, "<<="), 370 | (AssignBitShiftRight, ">>="), 371 | (AssignBitAnd, "&="), 372 | (AssignBitXor, "^="), 373 | (AssignBitOr, "|="), 374 | ][..] 375 | ); 376 | } 377 | 378 | #[test] 379 | fn types_easy() { 380 | assert_lex( 381 | " 382 | bool int uint string byte bytes address fixed ufixed 383 | ", 384 | &[ 385 | (TypeBool, "bool"), 386 | (TypeInt, "int"), 387 | (TypeUint, "uint"), 388 | (TypeString, "string"), 389 | (TypeByte, "byte"), 390 | (TypeBytes, "bytes"), 391 | (TypeAddress, "address"), 392 | (TypeFixed, "fixed"), 393 | (TypeUfixed, "ufixed"), 394 | ][..] 395 | ); 396 | } 397 | 398 | #[test] 399 | fn types_bytes() { 400 | assert_lex( 401 | " 402 | bytes1 bytes2 bytes3 bytes4 bytes5 bytes6 bytes7 bytes8 403 | bytes9 bytes10 bytes11 bytes12 bytes13 bytes14 bytes15 bytes16 404 | bytes17 bytes18 bytes19 bytes20 bytes21 bytes22 bytes23 bytes24 405 | bytes25 bytes26 bytes27 bytes28 bytes29 bytes30 bytes31 bytes32 406 | ", 407 | &[ 408 | (TypeByte, "bytes1"), 409 | (TypeByte, "bytes2"), 410 | (TypeByte, "bytes3"), 411 | (TypeByte, "bytes4"), 412 | (TypeByte, "bytes5"), 413 | (TypeByte, "bytes6"), 414 | (TypeByte, "bytes7"), 415 | (TypeByte, "bytes8"), 416 | (TypeByte, "bytes9"), 417 | (TypeByte, "bytes10"), 418 | (TypeByte, "bytes11"), 419 | (TypeByte, "bytes12"), 420 | (TypeByte, "bytes13"), 421 | (TypeByte, "bytes14"), 422 | (TypeByte, "bytes15"), 423 | (TypeByte, "bytes16"), 424 | (TypeByte, "bytes17"), 425 | (TypeByte, "bytes18"), 426 | (TypeByte, "bytes19"), 427 | (TypeByte, "bytes20"), 428 | (TypeByte, "bytes21"), 429 | (TypeByte, "bytes22"), 430 | (TypeByte, "bytes23"), 431 | (TypeByte, "bytes24"), 432 | (TypeByte, "bytes25"), 433 | (TypeByte, "bytes26"), 434 | (TypeByte, "bytes27"), 435 | (TypeByte, "bytes28"), 436 | (TypeByte, "bytes29"), 437 | (TypeByte, "bytes30"), 438 | (TypeByte, "bytes31"), 439 | (TypeByte, "bytes32"), 440 | ][..] 441 | ); 442 | } 443 | 444 | #[test] 445 | fn types_int() { 446 | assert_lex( 447 | " 448 | int8 int16 int24 int32 int40 int48 int56 int64 449 | int72 int80 int88 int96 int104 int112 int120 int128 450 | int136 int144 int152 int160 int168 int176 int184 int192 451 | int200 int208 int216 int224 int232 int240 int248 int256 452 | ", 453 | &[ 454 | (TypeInt, "int8"), 455 | (TypeInt, "int16"), 456 | (TypeInt, "int24"), 457 | (TypeInt, "int32"), 458 | (TypeInt, "int40"), 459 | (TypeInt, "int48"), 460 | (TypeInt, "int56"), 461 | (TypeInt, "int64"), 462 | (TypeInt, "int72"), 463 | (TypeInt, "int80"), 464 | (TypeInt, "int88"), 465 | (TypeInt, "int96"), 466 | (TypeInt, "int104"), 467 | (TypeInt, "int112"), 468 | (TypeInt, "int120"), 469 | (TypeInt, "int128"), 470 | (TypeInt, "int136"), 471 | (TypeInt, "int144"), 472 | (TypeInt, "int152"), 473 | (TypeInt, "int160"), 474 | (TypeInt, "int168"), 475 | (TypeInt, "int176"), 476 | (TypeInt, "int184"), 477 | (TypeInt, "int192"), 478 | (TypeInt, "int200"), 479 | (TypeInt, "int208"), 480 | (TypeInt, "int216"), 481 | (TypeInt, "int224"), 482 | (TypeInt, "int232"), 483 | (TypeInt, "int240"), 484 | (TypeInt, "int248"), 485 | (TypeInt, "int256"), 486 | ][..] 487 | ); 488 | } 489 | 490 | #[test] 491 | fn types_uint() { 492 | assert_lex( 493 | " 494 | uint8 uint16 uint24 uint32 uint40 uint48 uint56 uint64 495 | uint72 uint80 uint88 uint96 uint104 uint112 uint120 uint128 496 | uint136 uint144 uint152 uint160 uint168 uint176 uint184 uint192 497 | uint200 uint208 uint216 uint224 uint232 uint240 uint248 uint256 498 | ", 499 | &[ 500 | (TypeUint, "uint8"), 501 | (TypeUint, "uint16"), 502 | (TypeUint, "uint24"), 503 | (TypeUint, "uint32"), 504 | (TypeUint, "uint40"), 505 | (TypeUint, "uint48"), 506 | (TypeUint, "uint56"), 507 | (TypeUint, "uint64"), 508 | (TypeUint, "uint72"), 509 | (TypeUint, "uint80"), 510 | (TypeUint, "uint88"), 511 | (TypeUint, "uint96"), 512 | (TypeUint, "uint104"), 513 | (TypeUint, "uint112"), 514 | (TypeUint, "uint120"), 515 | (TypeUint, "uint128"), 516 | (TypeUint, "uint136"), 517 | (TypeUint, "uint144"), 518 | (TypeUint, "uint152"), 519 | (TypeUint, "uint160"), 520 | (TypeUint, "uint168"), 521 | (TypeUint, "uint176"), 522 | (TypeUint, "uint184"), 523 | (TypeUint, "uint192"), 524 | (TypeUint, "uint200"), 525 | (TypeUint, "uint208"), 526 | (TypeUint, "uint216"), 527 | (TypeUint, "uint224"), 528 | (TypeUint, "uint232"), 529 | (TypeUint, "uint240"), 530 | (TypeUint, "uint248"), 531 | (TypeUint, "uint256"), 532 | ][..] 533 | ); 534 | } 535 | 536 | #[test] 537 | fn types_fixed_ufixed() { 538 | assert_lex( 539 | " 540 | fixed8x0 fixed8x1 fixed16x2 fixed256x80 fixed144x57 541 | ufixed8x0 ufixed8x1 ufixed16x2 ufixed256x80 ufixed144x57 542 | ", 543 | &[ 544 | (TypeFixed, "fixed8x0"), 545 | (TypeFixed, "fixed8x1"), 546 | (TypeFixed, "fixed16x2"), 547 | (TypeFixed, "fixed256x80"), 548 | (TypeFixed, "fixed144x57"), 549 | (TypeUfixed, "ufixed8x0"), 550 | (TypeUfixed, "ufixed8x1"), 551 | (TypeUfixed, "ufixed16x2"), 552 | (TypeUfixed, "ufixed256x80"), 553 | (TypeUfixed, "ufixed144x57"), 554 | ][..] 555 | ); 556 | } 557 | 558 | #[test] 559 | fn not_real_types() { 560 | assert_lex( 561 | " 562 | bytes33 int127 fixed127 fixed128x fixed258x80 fixed256x81 563 | bytes0 uint0 uint53 ufixed1x1 564 | ", 565 | &[ 566 | (Identifier, "bytes33"), 567 | (Identifier, "int127"), 568 | (Identifier, "fixed127"), 569 | (Identifier, "fixed128x"), 570 | (Identifier, "fixed258x80"), 571 | (Identifier, "fixed256x81"), 572 | (Identifier, "bytes0"), 573 | (Identifier, "uint0"), 574 | (Identifier, "uint53"), 575 | (Identifier, "ufixed1x1"), 576 | ][..] 577 | ); 578 | } 579 | 580 | #[test] 581 | fn second_price_auction() { 582 | let source = include_str!("../../lunarity/benches/second-price-auction.sol"); 583 | 584 | let mut lex = Token::lexer(source); 585 | let mut tokens = 0; 586 | 587 | while lex.token != EndOfProgram { 588 | assert_ne!(lex.token, UnexpectedToken, "Unexpected: {} at {:?}", lex.slice(), lex.range()); 589 | assert_ne!(lex.token, UnexpectedEndOfProgram); 590 | 591 | tokens += 1; 592 | 593 | lex.advance(); 594 | } 595 | 596 | assert_eq!(tokens, 1299); 597 | } 598 | } 599 | -------------------------------------------------------------------------------- /lexer/src/token.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Lookup table layout 3 | //! =================== 4 | //! 5 | //! ```text 6 | //! EOF ; : , . ( ) { } [ ] => 7 | //! IDENT BLTIN CONTR LIB IFACE ENUM STRUCT MODIF EVENT FUNCT VAR ANON 8 | //! AS ASM BREAK CONST CONTIN DO DELETE ELSE EXTERN FOR HEX IF 9 | //! INDEX INTERN IMPORT IS MAP MEM NEW PAY PULIC PRAGMA PRIV PURE 10 | //! RET RETNS STORAG SUPER THIS THROW USING VIEW WHILE RESERV T_BOOL T_ADDR 11 | //! T_STR T_BYT T_BYTS T_INT T_UINT T_FIX T_UFIX L_TRUE L_FALS L_HEX L_INT L_RAT 12 | //! L_STR E_ETH E_FINN E_SZAB E_WEI T_YEAR T_WEEK T_DAYS T_HOUR T_MIN T_SEC := 13 | //! =: ++ -- ! ~ * / % ** + - << 14 | //! >> < <= > >= == != & ^ | && || 15 | //! ? = += -= *= /= %= <<= >>= &= ^= |= 16 | //! ERRTOK ERREOF 17 | //! ``` 18 | //! 19 | 20 | use logos::{Logos, Lexer, Extras, Source, Slice}; 21 | 22 | /// If the current token is an elementary type, 23 | /// this will hold it's size, if applicable. 24 | /// 25 | /// The first number is size in bytes, the second is 26 | /// decimal offset for fixed point numbers. 27 | /// 28 | /// - For `int64` this will be set to `(8, _)` 29 | /// - For `bytes20` this will be set to `(20, _)` 30 | /// - For 'ufixed128x40` this will be set to `(16, 40)` 31 | #[derive(Default, Clone, Copy)] 32 | pub struct TypeSize(pub u8, pub u8); 33 | 34 | impl Extras for TypeSize {} 35 | 36 | #[derive(Debug, PartialEq, Clone, Copy, Logos)] 37 | #[extras = "TypeSize"] 38 | pub enum Token { 39 | #[end] 40 | EndOfProgram, 41 | 42 | #[token = ";"] 43 | Semicolon, 44 | 45 | #[token = ":"] 46 | Colon, 47 | 48 | #[token = ","] 49 | Comma, 50 | 51 | #[token = "."] 52 | Accessor, 53 | 54 | #[token = "("] 55 | ParenOpen, 56 | 57 | #[token = ")"] 58 | ParenClose, 59 | 60 | #[token = "{"] 61 | BraceOpen, 62 | 63 | #[token = "}"] 64 | BraceClose, 65 | 66 | #[token = "["] 67 | BracketOpen, 68 | 69 | #[token = "]"] 70 | BracketClose, 71 | 72 | #[token = "=>"] 73 | Arrow, 74 | 75 | #[regex = "[a-zA-Z_$][a-zA-Z0-9_$]*"] 76 | Identifier, 77 | 78 | #[regex = "block|msg|tx|now|suicide|selfdestruct|addmod"] 79 | #[regex = "mulmod|sha3|keccak256|log0|log1|log2|log3|log4"] 80 | #[regex = "sha256|ecrecover|ripemd160|assert|revert|require"] 81 | IdentifierBuiltin, 82 | 83 | #[token = "contract"] 84 | DeclarationContract, 85 | 86 | #[token = "library"] 87 | DeclarationLibrary, 88 | 89 | #[token = "interface"] 90 | DeclarationInterface, 91 | 92 | #[token = "enum"] 93 | DeclarationEnum, 94 | 95 | #[token = "struct"] 96 | DeclarationStruct, 97 | 98 | #[token = "modifier"] 99 | DeclarationModifier, 100 | 101 | #[token = "event"] 102 | DeclarationEvent, 103 | 104 | #[token = "function"] 105 | DeclarationFunction, 106 | 107 | #[token = "var"] 108 | DeclarationVar, 109 | 110 | #[token = "anonymous"] 111 | KeywordAnonymous, 112 | 113 | #[token = "as"] 114 | KeywordAs, 115 | 116 | #[token = "assembly"] 117 | KeywordAssembly, 118 | 119 | #[token = "break"] 120 | KeywordBreak, 121 | 122 | #[token = "constant"] 123 | KeywordConstant, 124 | 125 | #[token = "continue"] 126 | KeywordContinue, 127 | 128 | #[token = "do"] 129 | KeywordDo, 130 | 131 | #[token = "delete"] 132 | KeywordDelete, 133 | 134 | #[token = "else"] 135 | KeywordElse, 136 | 137 | #[token = "external"] 138 | KeywordExternal, 139 | 140 | #[token = "for"] 141 | KeywordFor, 142 | 143 | // FIXME: Should able to handle hex literals on lexer-level! 144 | #[token = "hex"] 145 | KeywordHex, 146 | 147 | #[token = "if"] 148 | KeywordIf, 149 | 150 | #[token = "indexed"] 151 | KeywordIndexed, 152 | 153 | #[token = "internal"] 154 | KeywordInternal, 155 | 156 | #[token = "import"] 157 | KeywordImport, 158 | 159 | #[token = "is"] 160 | KeywordIs, 161 | 162 | #[token = "mapping"] 163 | KeywordMapping, 164 | 165 | #[token = "memory"] 166 | KeywordMemory, 167 | 168 | #[token = "new"] 169 | KeywordNew, 170 | 171 | #[token = "payable"] 172 | KeywordPayable, 173 | 174 | #[token = "public"] 175 | KeywordPublic, 176 | 177 | #[token = "pragma"] 178 | KeywordPragma, 179 | 180 | #[token = "private"] 181 | KeywordPrivate, 182 | 183 | #[token = "pure"] 184 | KeywordPure, 185 | 186 | #[token = "return"] 187 | KeywordReturn, 188 | 189 | #[token = "returns"] 190 | KeywordReturns, 191 | 192 | #[token = "storage"] 193 | KeywordStorage, 194 | 195 | #[token = "super"] 196 | KeywordSuper, 197 | 198 | #[token = "this"] 199 | KeywordThis, 200 | 201 | #[token = "throw"] 202 | KeywordThrow, 203 | 204 | #[token = "using"] 205 | KeywordUsing, 206 | 207 | #[token = "view"] 208 | KeywordView, 209 | 210 | #[token = "while"] 211 | KeywordWhile, 212 | 213 | #[regex = "abstract|after|case|catch|default|final|in"] 214 | #[regex = "inline|let|match|null|of|relocatable|static"] 215 | #[regex = "switch|try|type|typeof"] 216 | ReservedWord, 217 | 218 | #[token = "bool"] 219 | TypeBool, 220 | 221 | #[token = "address"] 222 | TypeAddress, 223 | 224 | #[token = "string"] 225 | TypeString, 226 | 227 | #[regex = "byte|bytes[1-2][0-9]?|bytes3[0-2]?|bytes[4-9]"] 228 | #[callback = "validate_bytes"] 229 | TypeByte, 230 | 231 | #[token = "bytes"] 232 | TypeBytes, 233 | 234 | #[token = "int"] 235 | #[callback = "default_size"] 236 | TypeInt, 237 | 238 | #[token = "uint"] 239 | #[callback = "default_size"] 240 | TypeUint, 241 | 242 | #[regex = "int(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144)"] 243 | #[regex = "int(152|160|168|176|184|192|200|208|216|224|232|240|248|256)"] 244 | #[callback = "validate_int"] 245 | TypeIntN, 246 | 247 | #[regex = "uint(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144)"] 248 | #[regex = "uint(152|160|168|176|184|192|200|208|216|224|232|240|248|256)"] 249 | #[callback = "validate_uint"] 250 | TypeUintN, 251 | 252 | #[regex = "fixed([1-9][0-9]?[0-9]?x[0-9][0-9]?)?"] 253 | #[callback = "validate_fixed"] 254 | TypeFixed, 255 | 256 | #[regex = "ufixed([1-9][0-9]?[0-9]?x[0-9][0-9]?)?"] 257 | #[callback = "validate_fixed"] 258 | TypeUfixed, 259 | 260 | #[token = "true"] 261 | LiteralTrue, 262 | 263 | #[token = "false"] 264 | LiteralFalse, 265 | 266 | #[regex = "0[xX][0-9a-fA-F]+"] 267 | LiteralHex, 268 | 269 | #[regex = "[0-9]+"] 270 | LiteralInteger, 271 | 272 | #[regex = "[0-9]*\\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+"] 273 | #[callback = "rational_to_integer"] 274 | LiteralRational, 275 | 276 | #[regex = "\"([^\"\\\\]|\\\\.)*\""] 277 | #[regex = "'([^'\\\\]|\\\\.)*'"] 278 | LiteralString, 279 | 280 | #[token = "ether"] 281 | UnitEther, 282 | 283 | #[token = "finney"] 284 | UnitFinney, 285 | 286 | #[token = "szabo"] 287 | UnitSzabo, 288 | 289 | #[token = "wei"] 290 | UnitWei, 291 | 292 | #[token = "years"] 293 | UnitTimeYears, 294 | 295 | #[token = "weeks"] 296 | UnitTimeWeeks, 297 | 298 | #[token = "days"] 299 | UnitTimeDays, 300 | 301 | #[token = "hours"] 302 | UnitTimeHours, 303 | 304 | #[token = "minutes"] 305 | UnitTimeMinutes, 306 | 307 | #[token = "seconds"] 308 | UnitTimeSeconds, 309 | 310 | #[token = ":="] 311 | AssemblyBind, 312 | 313 | #[token = "=:"] 314 | AssemblyAssign, 315 | 316 | #[token = "++"] 317 | OperatorIncrement, 318 | 319 | #[token = "--"] 320 | OperatorDecrement, 321 | 322 | #[token = "!"] 323 | OperatorLogicalNot, 324 | 325 | #[token = "~"] 326 | OperatorBitNot, 327 | 328 | #[token = "*"] 329 | OperatorMultiplication, 330 | 331 | #[token = "/"] 332 | OperatorDivision, 333 | 334 | #[token = "%"] 335 | OperatorRemainder, 336 | 337 | #[token = "**"] 338 | OperatorExponent, 339 | 340 | #[token = "+"] 341 | OperatorAddition, 342 | 343 | #[token = "-"] 344 | OperatorSubtraction, 345 | 346 | #[token = "<<"] 347 | OperatorBitShiftLeft, 348 | 349 | #[token = ">>"] 350 | OperatorBitShiftRight, 351 | 352 | #[token = "<"] 353 | OperatorLesser, 354 | 355 | #[token = "<="] 356 | OperatorLesserEquals, 357 | 358 | #[token = ">"] 359 | OperatorGreater, 360 | 361 | #[token = ">="] 362 | OperatorGreaterEquals, 363 | 364 | #[token = "=="] 365 | OperatorEquality, 366 | 367 | #[token = "!="] 368 | OperatorInequality, 369 | 370 | #[token = "&"] 371 | OperatorBitAnd, 372 | 373 | #[token = "^"] 374 | OperatorBitXor, 375 | 376 | #[token = "|"] 377 | OperatorBitOr, 378 | 379 | #[token = "&&"] 380 | OperatorLogicalAnd, 381 | 382 | #[token = "||"] 383 | OperatorLogicalOr, 384 | 385 | #[token = "?"] 386 | OperatorConditional, 387 | 388 | #[token = "="] 389 | Assign, 390 | 391 | #[token = "+="] 392 | AssignAddition, 393 | 394 | #[token = "-="] 395 | AssignSubtraction, 396 | 397 | #[token = "*="] 398 | AssignMultiplication, 399 | 400 | #[token = "/="] 401 | AssignDivision, 402 | 403 | #[token = "%="] 404 | AssignRemainder, 405 | 406 | #[token = "<<="] 407 | AssignBitShiftLeft, 408 | 409 | #[token = ">>="] 410 | AssignBitShiftRight, 411 | 412 | #[token = "&="] 413 | AssignBitAnd, 414 | 415 | #[token = "^="] 416 | AssignBitXor, 417 | 418 | #[token = "|="] 419 | AssignBitOr, 420 | 421 | #[regex = "//[^\n]*"] 422 | #[token = "/*"] 423 | #[callback = "ignore_comments"] 424 | #[error] 425 | UnexpectedToken, 426 | UnexpectedEndOfProgram, 427 | } 428 | 429 | fn ignore_comments<'source, Src: Source<'source>>(lex: &mut Lexer) { 430 | use logos::internal::LexerInternal; 431 | 432 | if lex.slice().as_bytes() == b"/*" { 433 | loop { 434 | match lex.read() { 435 | 0 => return lex.token = Token::UnexpectedEndOfProgram, 436 | b'*' => { 437 | if lex.next() == b'/' { 438 | lex.bump(); 439 | break; 440 | } 441 | }, 442 | _ => lex.bump(), 443 | } 444 | } 445 | } 446 | 447 | lex.advance(); 448 | } 449 | 450 | fn validate_bytes<'source, Src: Source<'source>>(lex: &mut Lexer) { 451 | let slice = lex.slice().as_bytes(); 452 | 453 | if slice.len() > 5 { 454 | lex.extras.0 = slice[5] - b'0'; 455 | 456 | if let Some(byte) = slice.get(6) { 457 | lex.extras.0 = lex.extras.0 * 10 + (byte - b'0'); 458 | } 459 | } else { 460 | lex.extras.0 = 1; 461 | } 462 | } 463 | 464 | fn default_size<'source, Src: Source<'source>>(lex: &mut Lexer) { 465 | lex.extras.0 = 32; 466 | } 467 | 468 | fn validate_int<'source, Src: Source<'source>>(lex: &mut Lexer) { 469 | let slice = lex.slice().as_bytes(); 470 | 471 | let mut n = (slice[3] - b'0') as u16; 472 | 473 | for byte in &slice[4..] { 474 | n = n * 10 + (*byte - b'0') as u16; 475 | } 476 | 477 | lex.extras.0 = (n / 8) as u8; 478 | lex.token = Token::TypeInt; 479 | } 480 | 481 | fn validate_uint<'source, Src: Source<'source>>(lex: &mut Lexer) { 482 | let slice = lex.slice().as_bytes(); 483 | 484 | let mut n = (slice[4] - b'0') as u16; 485 | 486 | for byte in &slice[5..] { 487 | n = n * 10 + (*byte - b'0') as u16; 488 | } 489 | 490 | lex.extras.0 = (n / 8) as u8; 491 | lex.token = Token::TypeUint; 492 | } 493 | 494 | fn validate_fixed<'source, Src: Source<'source>>(lex: &mut Lexer) { 495 | let slice = lex.slice().as_bytes(); 496 | let cutoff = if slice.starts_with(b"u") { 6 } else { 5 }; 497 | 498 | let mut n = 0u16; 499 | let mut m = 0u8; 500 | 501 | let mut iter = slice[cutoff..].iter(); 502 | 503 | while let Some(&byte) = iter.next() { 504 | if byte == b'x' { 505 | break; 506 | } 507 | 508 | n = n * 10 + (byte - b'0') as u16; 509 | } 510 | 511 | for byte in iter { 512 | m = m * 10 + (*byte - b'0'); 513 | } 514 | 515 | if n % 8 != 0 || n > 256 || m > 80 { 516 | lex.token = Token::Identifier; 517 | } else { 518 | lex.extras.0 = (n / 8) as u8; 519 | lex.extras.1 = m; 520 | } 521 | } 522 | 523 | fn rational_to_integer<'source, Src: Source<'source>>(lex: &mut Lexer) { 524 | let mut floating = 0i32; 525 | let mut iter = lex.slice().as_bytes().iter(); 526 | 527 | 'outer: while let Some(&byte) = iter.next() { 528 | match byte { 529 | b'e' | b'E' => break 'outer, 530 | b'0' => floating += 1, 531 | b'.' => { 532 | floating = 0; 533 | let mut zeroes = 0; 534 | 535 | while let Some(&byte) = iter.next() { 536 | match byte { 537 | b'e' | b'E' => break 'outer, 538 | b'0' => zeroes += 1, 539 | _ => { 540 | floating -= 1 + zeroes; 541 | zeroes = 0; 542 | }, 543 | } 544 | } 545 | } 546 | _ => {}, 547 | } 548 | } 549 | 550 | let mut neg = 1i32; 551 | let mut e = 0i32; 552 | 553 | for &byte in iter { 554 | match byte { 555 | b'-' => neg = -1, 556 | b'+' => {}, 557 | byte => e = e * 10 + (byte - b'0') as i32, 558 | } 559 | } 560 | 561 | if floating + e * neg >= 0 { 562 | lex.token = Token::LiteralInteger; 563 | } 564 | } 565 | -------------------------------------------------------------------------------- /lunarity/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lunarity" 3 | version = "0.2.1" 4 | authors = ["Parity Technologies "] 5 | license = "GPL-3.0" 6 | repository = "https://github.com/paritytech/lunarity" 7 | 8 | [dependencies] 9 | lunarity-ast = "0.2" 10 | lunarity-lexer = "0.2.1" 11 | lunarity-parser = "0.2.1" 12 | 13 | [dev-dependencies] 14 | toolshed = "0.6" 15 | -------------------------------------------------------------------------------- /lunarity/benches/second-price-auction.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | extern crate toolshed; 5 | extern crate lunarity; 6 | 7 | use lunarity::lexer::{Lexer, Token}; 8 | use lunarity::parse; 9 | 10 | use test::{Bencher, black_box}; 11 | 12 | static SOURCE: &'static str = include_str!("./second-price-auction.sol"); 13 | 14 | #[bench] 15 | fn tokenize(b: &mut Bencher) { 16 | let arena = toolshed::Arena::new(); 17 | let nts = arena.alloc_nul_term_str(SOURCE); 18 | b.bytes = SOURCE.len() as u64; 19 | 20 | b.iter(|| { 21 | let mut lexer = Lexer::new(nts); 22 | 23 | while lexer.token != Token::EndOfProgram { 24 | lexer.advance() 25 | } 26 | }); 27 | } 28 | 29 | #[bench] 30 | fn parse_to_ast(b: &mut Bencher) { 31 | b.bytes = SOURCE.len() as u64; 32 | 33 | b.iter(|| { 34 | let program = parse(SOURCE); 35 | 36 | black_box(program.unwrap()) 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /lunarity/benches/second-price-auction.sol: -------------------------------------------------------------------------------- 1 | //! Copyright Parity Technologies, 2017. 2 | //! Released under the Apache Licence 2. 3 | 4 | pragma solidity ^0.4.17; 5 | 6 | /// Stripped down ERC20 standard token interface. 7 | contract Token { 8 | function transfer(address _to, uint256 _value) public returns (bool success); 9 | } 10 | 11 | // From Certifier.sol 12 | contract Certifier { 13 | event Confirmed(address indexed who); 14 | event Revoked(address indexed who); 15 | function certified(address) public constant returns (bool); 16 | function get(address, string) public constant returns (bytes32); 17 | function getAddress(address, string) public constant returns (address); 18 | function getUint(address, string) public constant returns (uint); 19 | } 20 | 21 | /// Simple modified second price auction contract. Price starts high and monotonically decreases 22 | /// until all tokens are sold at the current price with currently received funds. 23 | /// The price curve has been chosen to resemble a logarithmic curve 24 | /// and produce a reasonable auction timeline. 25 | contract SecondPriceAuction { 26 | // Events: 27 | 28 | /// Someone bought in at a particular max-price. 29 | event Buyin(address indexed who, uint accounted, uint received, uint price); 30 | 31 | /// Admin injected a purchase. 32 | event Injected(address indexed who, uint accounted, uint received); 33 | 34 | /// At least 5 minutes has passed since last Ticked event. 35 | event Ticked(uint era, uint received, uint accounted); 36 | 37 | /// The sale just ended with the current price. 38 | event Ended(uint price); 39 | 40 | /// Finalised the purchase for `who`, who has been given `tokens` tokens. 41 | event Finalised(address indexed who, uint tokens); 42 | 43 | /// Auction is over. All accounts finalised. 44 | event Retired(); 45 | 46 | // Constructor: 47 | 48 | /// Simple constructor. 49 | /// Token cap should take be in whole tokens, not smallest divisible units. 50 | function SecondPriceAuction( 51 | address _certifierContract, 52 | address _tokenContract, 53 | address _treasury, 54 | address _admin, 55 | uint _beginTime, 56 | uint _tokenCap 57 | ) 58 | public 59 | { 60 | certifier = Certifier(_certifierContract); 61 | tokenContract = Token(_tokenContract); 62 | treasury = _treasury; 63 | admin = _admin; 64 | beginTime = _beginTime; 65 | tokenCap = _tokenCap; 66 | endTime = beginTime + 28 days; 67 | } 68 | 69 | // No default function, entry-level users 70 | function() public { assert(false); } 71 | 72 | // Public interaction: 73 | 74 | /// Buyin function. Throws if the sale is not active and when refund would be needed. 75 | function buyin(uint8 v, bytes32 r, bytes32 s) 76 | public 77 | payable 78 | when_not_halted 79 | when_active 80 | only_eligible(msg.sender, v, r, s) 81 | { 82 | flushEra(); 83 | 84 | // Flush bonus period: 85 | if (currentBonus > 0) { 86 | // Bonus is currently active... 87 | if (now >= beginTime + BONUS_MIN_DURATION // ...but outside the automatic bonus period 88 | && lastNewInterest + BONUS_LATCH <= block.number // ...and had no new interest for some blocks 89 | ) { 90 | currentBonus--; 91 | } 92 | if (now >= beginTime + BONUS_MAX_DURATION) { 93 | currentBonus = 0; 94 | } 95 | if (buyins[msg.sender].received == 0) { // We have new interest 96 | lastNewInterest = uint32(block.number); 97 | } 98 | } 99 | 100 | uint accounted; 101 | bool refund; 102 | uint price; 103 | (accounted, refund, price) = theDeal(msg.value); 104 | 105 | /// No refunds allowed. 106 | require (!refund); 107 | 108 | // record the acceptance. 109 | buyins[msg.sender].accounted += uint128(accounted); 110 | buyins[msg.sender].received += uint128(msg.value); 111 | totalAccounted += accounted; 112 | totalReceived += msg.value; 113 | endTime = calculateEndTime(); 114 | Buyin(msg.sender, accounted, msg.value, price); 115 | 116 | // send to treasury 117 | treasury.transfer(msg.value); 118 | } 119 | 120 | /// Like buyin except no payment required and bonus automatically given. 121 | function inject(address _who, uint128 _received) 122 | public 123 | only_admin 124 | only_basic(_who) 125 | before_beginning 126 | { 127 | uint128 bonus = _received * uint128(currentBonus) / 100; 128 | uint128 accounted = _received + bonus; 129 | 130 | buyins[_who].accounted += accounted; 131 | buyins[_who].received += _received; 132 | totalAccounted += accounted; 133 | totalReceived += _received; 134 | endTime = calculateEndTime(); 135 | Injected(_who, accounted, _received); 136 | } 137 | 138 | /// Mint tokens for a particular participant. 139 | function finalise(address _who) 140 | public 141 | when_not_halted 142 | when_ended 143 | only_buyins(_who) 144 | { 145 | // end the auction if we're the first one to finalise. 146 | if (endPrice == 0) { 147 | endPrice = totalAccounted / tokenCap; 148 | Ended(endPrice); 149 | } 150 | 151 | // enact the purchase. 152 | uint total = buyins[_who].accounted; 153 | uint tokens = total / endPrice; 154 | totalFinalised += total; 155 | delete buyins[_who]; 156 | require (tokenContract.transfer(_who, tokens)); 157 | 158 | Finalised(_who, tokens); 159 | 160 | if (totalFinalised == totalAccounted) { 161 | Retired(); 162 | } 163 | } 164 | 165 | // Prviate utilities: 166 | 167 | /// Ensure the era tracker is prepared in case the current changed. 168 | function flushEra() private { 169 | uint currentEra = (now - beginTime) / ERA_PERIOD; 170 | if (currentEra > eraIndex) { 171 | Ticked(eraIndex, totalReceived, totalAccounted); 172 | } 173 | eraIndex = currentEra; 174 | } 175 | 176 | // Admin interaction: 177 | 178 | /// Emergency function to pause buy-in and finalisation. 179 | function setHalted(bool _halted) public only_admin { halted = _halted; } 180 | 181 | /// Emergency function to drain the contract of any funds. 182 | function drain() public only_admin { treasury.transfer(this.balance); } 183 | 184 | // Inspection: 185 | 186 | /** 187 | * The formula for the price over time. 188 | * 189 | * This is a hand-crafted formula (no named to the constants) in order to 190 | * provide the following requirements: 191 | * 192 | * - Simple reciprocal curve (of the form y = a + b / (x + c)); 193 | * - Would be completely unreasonable to end in the first 48 hours; 194 | * - Would reach $65m effective cap in 4 weeks. 195 | * 196 | * The curve begins with an effective cap (EC) of over $30b, more ether 197 | * than is in existance. After 48 hours, the EC reduces to approx. $1b. 198 | * At just over 10 days, the EC has reduced to $200m, and half way through 199 | * the 19th day it has reduced to $100m. 200 | * 201 | * Here's the curve: https://www.desmos.com/calculator/k6iprxzcrg?embed 202 | */ 203 | 204 | /// The current end time of the sale assuming that nobody else buys in. 205 | function calculateEndTime() public constant returns (uint) { 206 | var factor = tokenCap / DIVISOR * USDWEI; 207 | return beginTime + 40000000 * factor / (totalAccounted + 5 * factor) - 5760; 208 | } 209 | 210 | /// The current price for a single indivisible part of a token. If a buyin happens now, this is 211 | /// the highest price per indivisible token part that the buyer will pay. This doesn't 212 | /// include the discount which may be available. 213 | function currentPrice() public constant when_active returns (uint weiPerIndivisibleTokenPart) { 214 | return (USDWEI * 40000000 / (now - beginTime + 5760) - USDWEI * 5) / DIVISOR; 215 | } 216 | 217 | /// Returns the total indivisible token parts available for purchase right now. 218 | function tokensAvailable() public constant when_active returns (uint tokens) { 219 | uint _currentCap = totalAccounted / currentPrice(); 220 | if (_currentCap >= tokenCap) { 221 | return 0; 222 | } 223 | return tokenCap - _currentCap; 224 | } 225 | 226 | /// The largest purchase than can be made at present, not including any 227 | /// discount. 228 | function maxPurchase() public constant when_active returns (uint spend) { 229 | return tokenCap * currentPrice() - totalAccounted; 230 | } 231 | 232 | /// Get the number of `tokens` that would be given if the sender were to 233 | /// spend `_value` now. Also tell you what `refund` would be given, if any. 234 | function theDeal(uint _value) 235 | public 236 | constant 237 | when_active 238 | returns (uint accounted, bool refund, uint price) 239 | { 240 | uint _bonus = bonus(_value); 241 | 242 | price = currentPrice(); 243 | accounted = _value + _bonus; 244 | 245 | uint available = tokensAvailable(); 246 | uint tokens = accounted / price; 247 | refund = (tokens > available); 248 | } 249 | 250 | /// Any applicable bonus to `_value`. 251 | function bonus(uint _value) 252 | public 253 | constant 254 | when_active 255 | returns (uint extra) 256 | { 257 | return _value * uint(currentBonus) / 100; 258 | } 259 | 260 | /// True if the sale is ongoing. 261 | function isActive() public constant returns (bool) { return now >= beginTime && now < endTime; } 262 | 263 | /// True if all buyins have finalised. 264 | function allFinalised() public constant returns (bool) { return now >= endTime && totalAccounted == totalFinalised; } 265 | 266 | /// Returns true if the sender of this transaction is a basic account. 267 | function isBasicAccount(address _who) internal constant returns (bool) { 268 | uint senderCodeSize; 269 | assembly { 270 | senderCodeSize := extcodesize(_who) 271 | } 272 | return senderCodeSize == 0; 273 | } 274 | 275 | // Modifiers: 276 | 277 | /// Ensure the sale is ongoing. 278 | modifier when_active { require (isActive()); _; } 279 | 280 | /// Ensure the sale has not begun. 281 | modifier before_beginning { require (now < beginTime); _; } 282 | 283 | /// Ensure the sale is ended. 284 | modifier when_ended { require (now >= endTime); _; } 285 | 286 | /// Ensure we're not halted. 287 | modifier when_not_halted { require (!halted); _; } 288 | 289 | /// Ensure `_who` is a participant. 290 | modifier only_buyins(address _who) { require (buyins[_who].accounted != 0); _; } 291 | 292 | /// Ensure sender is admin. 293 | modifier only_admin { require (msg.sender == admin); _; } 294 | 295 | /// Ensure that the signature is valid, `who` is a certified, basic account, 296 | /// the gas price is sufficiently low and the value is sufficiently high. 297 | modifier only_eligible(address who, uint8 v, bytes32 r, bytes32 s) { 298 | require ( 299 | ecrecover(STATEMENT_HASH, v, r, s) == who && 300 | certifier.certified(who) && 301 | isBasicAccount(who) && 302 | msg.value >= DUST_LIMIT 303 | ); 304 | _; 305 | } 306 | 307 | /// Ensure sender is not a contract. 308 | modifier only_basic(address who) { require (isBasicAccount(who)); _; } 309 | 310 | // State: 311 | 312 | struct Account { 313 | uint128 accounted; // including bonus & hit 314 | uint128 received; // just the amount received, without bonus & hit 315 | } 316 | 317 | /// Those who have bought in to the auction. 318 | mapping (address => Account) public buyins; 319 | 320 | /// Total amount of ether received, excluding phantom "bonus" ether. 321 | uint public totalReceived = 0; 322 | 323 | /// Total amount of ether accounted for, including phantom "bonus" ether. 324 | uint public totalAccounted = 0; 325 | 326 | /// Total amount of ether which has been finalised. 327 | uint public totalFinalised = 0; 328 | 329 | /// The current end time. Gets updated when new funds are received. 330 | uint public endTime; 331 | 332 | /// The price per token; only valid once the sale has ended and at least one 333 | /// participant has finalised. 334 | uint public endPrice; 335 | 336 | /// Must be false for any public function to be called. 337 | bool public halted; 338 | 339 | /// The current percentage of bonus that purchasers get. 340 | uint8 public currentBonus = 15; 341 | 342 | /// The last block that had a new participant. 343 | uint32 public lastNewInterest; 344 | 345 | // Constants after constructor: 346 | 347 | /// The tokens contract. 348 | Token public tokenContract; 349 | 350 | /// The certifier. 351 | Certifier public certifier; 352 | 353 | /// The treasury address; where all the Ether goes. 354 | address public treasury; 355 | 356 | /// The admin address; auction can be paused or halted at any time by this. 357 | address public admin; 358 | 359 | /// The time at which the sale begins. 360 | uint public beginTime; 361 | 362 | /// Maximum amount of tokens to mint. Once totalAccounted / currentPrice is 363 | /// greater than this, the sale ends. 364 | uint public tokenCap; 365 | 366 | // Era stuff (isolated) 367 | /// The era for which the current consolidated data represents. 368 | uint public eraIndex; 369 | 370 | /// The size of the era in seconds. 371 | uint constant public ERA_PERIOD = 5 minutes; 372 | 373 | // Static constants: 374 | 375 | /// Anything less than this is considered dust and cannot be used to buy in. 376 | uint constant public DUST_LIMIT = 5 finney; 377 | 378 | /// The hash of the statement which must be signed in order to buyin. 379 | bytes32 constant public STATEMENT_HASH = keccak256(STATEMENT); 380 | 381 | /// The statement which should be signed. 382 | string constant public STATEMENT = "\x19Ethereum Signed Message:\n47Please take my Ether and try to build Polkadot."; 383 | 384 | //# Statement to actually sign. 385 | //# ```js 386 | //# statement = function() { this.STATEMENT().map(s => s.substr(28)) } 387 | //# ``` 388 | 389 | /// Minimum duration after sale begins that bonus is active. 390 | uint constant public BONUS_MIN_DURATION = 1 hours; 391 | 392 | /// Minimum duration after sale begins that bonus is active. 393 | uint constant public BONUS_MAX_DURATION = 24 hours; 394 | 395 | /// Number of consecutive blocks where there must be no new interest before bonus ends. 396 | uint constant public BONUS_LATCH = 2; 397 | 398 | /// Number of Wei in one USD, constant. 399 | uint constant public USDWEI = 3226 szabo; 400 | 401 | /// Divisor of the token. 402 | uint constant public DIVISOR = 1000; 403 | } 404 | -------------------------------------------------------------------------------- /lunarity/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub extern crate lunarity_ast as ast; 2 | pub extern crate lunarity_lexer as lexer; 3 | 4 | extern crate lunarity_parser; 5 | pub use lunarity_parser::parse; 6 | -------------------------------------------------------------------------------- /parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lunarity-parser" 3 | version = "0.2.1" 4 | authors = ["Parity Technologies "] 5 | license = "GPL-3.0" 6 | repository = "https://github.com/paritytech/lunarity" 7 | description = "A high performance Solidity language Parser" 8 | 9 | [dependencies] 10 | toolshed = "0.6" 11 | lunarity-lexer = "0.2.1" 12 | lunarity-ast = "0.2" 13 | 14 | [dev-dependencies] 15 | pretty_assertions = "0.5" 16 | -------------------------------------------------------------------------------- /parser/src/assembly.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::GrowableList; 2 | 3 | use ast::*; 4 | use Parser; 5 | use lexer::Token; 6 | 7 | impl<'ast> Parser<'ast> { 8 | pub fn inline_assembly_block(&mut self) -> Option> 9 | where 10 | B: From> + Copy, 11 | { 12 | let start = self.start_then_advance(); 13 | let items = GrowableList::new(); 14 | 15 | while let Some(item) = self.assembly_item() { 16 | items.push(self.arena, item); 17 | } 18 | 19 | let end = self.expect_end(Token::BraceClose); 20 | 21 | self.node_at(start, end, InlineAssemblyBlock { 22 | items: items.as_list() 23 | }) 24 | } 25 | 26 | fn assembly_item(&mut self) -> Option> { 27 | match self.lexer.token { 28 | Token::BraceOpen => self.inline_assembly_block(), 29 | Token::Identifier => self.assembly_identifier(), 30 | _ => None, 31 | } 32 | } 33 | 34 | fn assembly_identifier(&mut self) -> Option> { 35 | let (start, end) = self.loc(); 36 | let identifier = self.lexer.slice(); 37 | 38 | self.lexer.advance(); 39 | 40 | if self.allow(Token::AssemblyBind) { 41 | let id = self.node_at(start, end, identifier); 42 | let init = expect!(self, self.functional_assembly_expression()); 43 | 44 | return self.node_at(start, init.end, AssemblyAssignment { 45 | id, 46 | init, 47 | }); 48 | } 49 | 50 | self.node_at(start, end, identifier) 51 | } 52 | 53 | fn functional_assembly_expression(&mut self) -> Option> { 54 | let id = self.expect_str_node(Token::Identifier); 55 | 56 | self.expect(Token::ParenOpen); 57 | 58 | let arguments = GrowableList::new(); 59 | 60 | while let Some(item) = self.assembly_item() { 61 | arguments.push(self.arena, item); 62 | } 63 | 64 | let end = self.expect_end(Token::ParenClose); 65 | 66 | self.node_at(id.start, end, FunctionalAssemblyExpression { 67 | id, 68 | arguments: arguments.as_list() 69 | }) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /parser/src/contract.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::{ListBuilder, GrowableList}; 2 | 3 | use ast::*; 4 | use {Parser, ModifierContext, TOP, RegularTypeNameContext}; 5 | use lexer::Token; 6 | 7 | impl<'ast> Parser<'ast> { 8 | pub fn contract_definition(&mut self) -> Option> { 9 | let start = self.start_then_advance(); 10 | let name = self.expect_str_node(Token::Identifier); 11 | 12 | let inherits = if self.allow(Token::KeywordIs) { 13 | let builder = ListBuilder::new(self.arena, self.expect_str_node(Token::Identifier)); 14 | 15 | while self.allow(Token::Comma) { 16 | builder.push(self.arena, self.expect_str_node(Token::Identifier)); 17 | } 18 | 19 | builder.as_list() 20 | } else { 21 | NodeList::empty() 22 | }; 23 | 24 | self.expect(Token::BraceOpen); 25 | 26 | let builder = GrowableList::new(); 27 | 28 | while let Some(part) = self.contract_part() { 29 | builder.push(self.arena, part); 30 | } 31 | 32 | let end = self.expect_end(Token::BraceClose); 33 | 34 | self.node_at(start, end, ContractDefinition { 35 | name, 36 | inherits, 37 | body: builder.as_list(), 38 | }) 39 | } 40 | 41 | fn contract_part(&mut self) -> Option> { 42 | match self.lexer.token { 43 | Token::KeywordUsing => self.using_for_declaration(), 44 | Token::DeclarationStruct => self.struct_defintion(), 45 | Token::DeclarationModifier => self.modifier_definition(), 46 | Token::DeclarationFunction => self.function_definition(), 47 | Token::DeclarationEvent => self.event_definition(), 48 | Token::DeclarationEnum => self.enum_definition(), 49 | _ => self.state_variable_declaration(), 50 | } 51 | 52 | } 53 | 54 | fn state_variable_declaration(&mut self) -> Option> { 55 | let type_name = self.type_name::()?; 56 | 57 | let mut visibility = None; 58 | let mut constant = None; 59 | 60 | for _ in 0..2 { 61 | match self.lexer.token { 62 | Token::KeywordPublic => self.unique_flag(&mut visibility, StateVariableVisibility::Public), 63 | Token::KeywordInternal => self.unique_flag(&mut visibility, StateVariableVisibility::Internal), 64 | Token::KeywordPrivate => self.unique_flag(&mut visibility, StateVariableVisibility::Private), 65 | Token::KeywordConstant => self.unique_flag(&mut constant, Flag), 66 | _ => break, 67 | } 68 | } 69 | 70 | let name = self.expect_str_node(Token::Identifier); 71 | 72 | let init = if self.allow(Token::Assign) { 73 | match self.expression(TOP) { 74 | None => { 75 | self.error(); 76 | 77 | None 78 | }, 79 | init => init, 80 | } 81 | } else { 82 | None 83 | }; 84 | 85 | let end = self.expect_end(Token::Semicolon); 86 | 87 | self.node_at(type_name.start, end, StateVariableDeclaration { 88 | type_name, 89 | visibility, 90 | constant, 91 | name, 92 | init, 93 | }) 94 | } 95 | 96 | fn using_for_declaration(&mut self) -> Option> { 97 | let start = self.start_then_advance(); 98 | let id = self.expect_str_node(Token::Identifier); 99 | 100 | self.expect(Token::KeywordFor); 101 | 102 | let type_name = match self.type_name::() { 103 | None => { 104 | self.expect(Token::OperatorMultiplication); 105 | 106 | None 107 | }, 108 | type_name => type_name, 109 | }; 110 | 111 | let end = self.expect_end(Token::Semicolon); 112 | 113 | self.node_at(start, end, UsingForDeclaration { 114 | id, 115 | type_name, 116 | }) 117 | } 118 | 119 | fn struct_defintion(&mut self) -> Option> { 120 | let start = self.start_then_advance(); 121 | let name = self.expect_str_node(Token::Identifier); 122 | 123 | self.expect(Token::BraceOpen); 124 | 125 | let body = match self.variable_declaration::() { 126 | Some(declaration) => { 127 | let builder = ListBuilder::new(self.arena, declaration); 128 | 129 | self.expect(Token::Semicolon); 130 | 131 | while let Some(declaration) = self.variable_declaration::() { 132 | builder.push(self.arena, declaration); 133 | 134 | self.expect(Token::Semicolon); 135 | } 136 | 137 | builder.as_list() 138 | }, 139 | None => { 140 | // Must have at least one element 141 | self.error(); 142 | 143 | NodeList::empty() 144 | } 145 | }; 146 | 147 | let end = self.expect_end(Token::BraceClose); 148 | 149 | self.node_at(start, end, StructDefinition { 150 | name, 151 | body, 152 | }) 153 | } 154 | 155 | fn modifier_definition(&mut self) -> Option> { 156 | let start = self.start_then_advance(); 157 | let name = self.expect_str_node(Token::Identifier); 158 | 159 | let params; 160 | 161 | if self.allow(Token::ParenOpen) { 162 | params = self.parameter_list(); 163 | 164 | self.expect(Token::ParenClose); 165 | } else { 166 | params = NodeList::empty() 167 | } 168 | 169 | let block = self.block::(); 170 | 171 | self.node_at(start, block.end, ModifierDefinition { 172 | name, 173 | params, 174 | block, 175 | }) 176 | } 177 | 178 | fn event_definition(&mut self) -> Option> { 179 | let start = self.start_then_advance(); 180 | let name = self.expect_str_node(Token::Identifier); 181 | 182 | self.expect(Token::ParenOpen); 183 | 184 | let params = match self.indexed_parameter() { 185 | Some(param) => { 186 | let builder = ListBuilder::new(self.arena, param); 187 | 188 | while self.allow(Token::Comma) { 189 | match self.indexed_parameter() { 190 | Some(param) => builder.push(self.arena, param), 191 | None => self.error(), 192 | } 193 | } 194 | 195 | builder.as_list() 196 | }, 197 | None => NodeList::empty(), 198 | }; 199 | 200 | self.expect(Token::ParenClose); 201 | 202 | let anonymous = self.allow_flag_node(Token::KeywordAnonymous); 203 | let end = self.expect_end(Token::Semicolon); 204 | 205 | self.node_at(start, end, EventDefinition { 206 | anonymous, 207 | name, 208 | params, 209 | }) 210 | } 211 | 212 | fn indexed_parameter(&mut self) -> Option>> { 213 | let type_name = self.type_name::()?; 214 | let indexed = self.allow_flag_node(Token::KeywordIndexed); 215 | let name = self.allow_str_node(Token::Identifier); 216 | 217 | let end = name.end() 218 | .or_else(|| indexed.end()) 219 | .unwrap_or_else(|| type_name.end); 220 | 221 | self.node_at(type_name.start, end, IndexedParameter { 222 | indexed, 223 | type_name, 224 | name, 225 | }) 226 | } 227 | 228 | fn enum_definition(&mut self) -> Option> { 229 | let start = self.start_then_advance(); 230 | let name = self.expect_str_node(Token::Identifier); 231 | 232 | self.expect(Token::BraceOpen); 233 | 234 | let variants = if let Some(variant) = self.allow_str_node(Token::Identifier) { 235 | let builder = ListBuilder::new(self.arena, variant); 236 | 237 | while self.allow(Token::Comma) { 238 | builder.push(self.arena, self.expect_str_node(Token::Identifier)) 239 | } 240 | 241 | builder.as_list() 242 | } else { 243 | NodeList::empty() 244 | }; 245 | 246 | let end = self.expect_end(Token::BraceClose); 247 | 248 | self.node_at(start, end, EnumDefinition { 249 | name, 250 | variants, 251 | }) 252 | } 253 | } 254 | 255 | #[cfg(test)] 256 | mod test { 257 | use super::*; 258 | use mock::{Mock, assert_units}; 259 | 260 | #[test] 261 | fn empty_contract() { 262 | let m = Mock::new(); 263 | 264 | assert_units(r#" 265 | 266 | contract Foo {} 267 | contract Doge is Amazing {} 268 | contract This is Silly, Kinda {} 269 | 270 | "#, [ 271 | m.node(14, 29, ContractDefinition { 272 | name: m.node(23, 26, "Foo"), 273 | inherits: NodeList::empty(), 274 | body: NodeList::empty(), 275 | }), 276 | m.node(42, 69, ContractDefinition { 277 | name: m.node(51, 55, "Doge"), 278 | inherits: m.list([ 279 | m.node(59, 66, "Amazing"), 280 | ]), 281 | body: NodeList::empty(), 282 | }), 283 | m.node(82, 114, ContractDefinition { 284 | name: m.node(91, 95, "This"), 285 | inherits: m.list([ 286 | m.node(99, 104, "Silly"), 287 | m.node(106, 111, "Kinda"), 288 | ]), 289 | body: NodeList::empty(), 290 | }), 291 | ]); 292 | } 293 | 294 | #[test] 295 | fn state_variable_declaration() { 296 | let m = Mock::new(); 297 | 298 | assert_units(r#" 299 | 300 | contract Foo { 301 | int32 foo = 10; 302 | bytes10 public doge; 303 | } 304 | 305 | "#, [ 306 | m.node(14, 111, ContractDefinition { 307 | name: m.node(23, 26, "Foo"), 308 | inherits: NodeList::empty(), 309 | body: m.list([ 310 | m.node(45, 60, StateVariableDeclaration { 311 | type_name: m.node(45, 50, ElementaryTypeName::Int(4)), 312 | visibility: None, 313 | constant: None, 314 | name: m.node(51, 54, "foo"), 315 | init: m.node(57, 59, Primitive::IntegerNumber("10", NumberUnit::None)), 316 | }), 317 | m.node(77, 97, StateVariableDeclaration { 318 | type_name: m.node(77, 84, ElementaryTypeName::Byte(10)), 319 | visibility: m.node(85, 91, StateVariableVisibility::Public), 320 | constant: None, 321 | name: m.node(92, 96, "doge"), 322 | init: None, 323 | }), 324 | ]), 325 | }), 326 | ]); 327 | } 328 | 329 | #[test] 330 | fn using_for_declaration() { 331 | let m = Mock::new(); 332 | 333 | assert_units(r#" 334 | 335 | contract Foo { 336 | using foo for *; 337 | using bar for int32; 338 | } 339 | 340 | "#, [ 341 | m.node(14, 112, ContractDefinition { 342 | name: m.node(23, 26, "Foo"), 343 | inherits: NodeList::empty(), 344 | body: m.list([ 345 | m.node(45, 61, UsingForDeclaration { 346 | id: m.node(51, 54, "foo"), 347 | type_name: None, 348 | }), 349 | m.node(78, 98, UsingForDeclaration { 350 | id: m.node(84, 87, "bar"), 351 | type_name: m.node(92, 97, ElementaryTypeName::Int(4)), 352 | }), 353 | ]), 354 | }), 355 | ]); 356 | } 357 | 358 | #[test] 359 | fn struct_defintion() { 360 | let m = Mock::new(); 361 | 362 | assert_units(r#" 363 | 364 | contract Foo { 365 | struct Doge { 366 | uint wows; 367 | bool memory amaze; 368 | address storage moon; 369 | } 370 | } 371 | 372 | "#, [ 373 | m.node(14, 202, ContractDefinition { 374 | name: m.node(23, 26, "Foo"), 375 | inherits: NodeList::empty(), 376 | body: m.list([ 377 | m.node(45, 188, StructDefinition { 378 | name: m.node(52, 56, "Doge"), 379 | body: m.list([ 380 | m.node(79, 88, VariableDeclaration { 381 | type_name: m.node(79, 83, ElementaryTypeName::Uint(32)), 382 | location: None, 383 | id: m.node(84, 88, "wows"), 384 | }), 385 | m.node(110, 127, VariableDeclaration { 386 | type_name: m.node(110, 114, ElementaryTypeName::Bool), 387 | location: m.node(115, 121, StorageLocation::Memory), 388 | id: m.node(122, 127, "amaze"), 389 | }), 390 | m.node(149, 169, VariableDeclaration { 391 | type_name: m.node(149, 156, ElementaryTypeName::Address), 392 | location: m.node(157, 164, StorageLocation::Storage), 393 | id: m.node(165, 169, "moon"), 394 | }), 395 | ]) 396 | }), 397 | ]), 398 | }), 399 | ]); 400 | } 401 | 402 | #[test] 403 | fn modifier_definition() { 404 | let m = Mock::new(); 405 | 406 | assert_units(r#" 407 | 408 | contract Foo { 409 | modifier only_doges { _; } 410 | 411 | modifier foo(uint8 bar) { 412 | uint8 baz = bar; 413 | _; 414 | } 415 | } 416 | 417 | "#, [ 418 | m.node(14, 206, ContractDefinition { 419 | name: m.node(23, 26, "Foo"), 420 | inherits: NodeList::empty(), 421 | body: m.list([ 422 | m.node(45, 71, ModifierDefinition { 423 | name: m.node(54, 64, "only_doges"), 424 | params: NodeList::empty(), 425 | block: m.node(65, 71, Block { 426 | body: m.list([ 427 | m.node(67, 69, Statement::Placeholder), 428 | ]), 429 | }), 430 | }), 431 | m.node(89, 192, ModifierDefinition { 432 | name: m.node(98, 101, "foo"), 433 | params: m.list([ 434 | m.node(102, 111, Parameter { 435 | type_name: m.node(102, 107, ElementaryTypeName::Uint(1)), 436 | name: m.node(108, 111, "bar"), 437 | }), 438 | ]), 439 | block: m.node(113, 192, Block { 440 | body: m.list([ 441 | m.node(135, 151, VariableDefinitionStatement { 442 | declaration: m.node(135, 144, VariableDeclaration { 443 | type_name: m.node(135, 140, ElementaryTypeName::Uint(1)), 444 | location: None, 445 | id: m.node(141, 144, "baz"), 446 | }), 447 | init: m.node(147, 150, "bar"), 448 | }), 449 | m.node(172, 174, Statement::Placeholder), 450 | ]), 451 | }), 452 | }), 453 | ]), 454 | }), 455 | ]); 456 | } 457 | 458 | #[test] 459 | fn empty_events() { 460 | let m = Mock::new(); 461 | 462 | assert_units(r#" 463 | 464 | contract Foo { 465 | event Horizon(); 466 | event Alcoholics() anonymous; 467 | } 468 | 469 | "#, [ 470 | m.node(14, 121, ContractDefinition { 471 | name: m.node(23, 26, "Foo"), 472 | inherits: NodeList::empty(), 473 | body: m.list([ 474 | m.node(45, 61, EventDefinition { 475 | anonymous: None, 476 | name: m.node(51, 58, "Horizon"), 477 | params: NodeList::empty(), 478 | }), 479 | m.node(78, 107, EventDefinition { 480 | anonymous: m.node(97, 106, Flag), 481 | name: m.node(84, 94, "Alcoholics"), 482 | params: NodeList::empty(), 483 | }), 484 | ]), 485 | }), 486 | ]); 487 | } 488 | 489 | #[test] 490 | fn event_with_parameters() { 491 | let m = Mock::new(); 492 | 493 | assert_units(r#" 494 | 495 | contract Foo { 496 | event Horizon(int32 indexed, bool); 497 | } 498 | 499 | "#, [ 500 | m.node(14, 94, ContractDefinition { 501 | name: m.node(23, 26, "Foo"), 502 | inherits: NodeList::empty(), 503 | body: m.list([ 504 | m.node(45, 80, EventDefinition { 505 | anonymous: None, 506 | name: m.node(51, 58, "Horizon"), 507 | params: m.list([ 508 | m.node(59, 72, IndexedParameter { 509 | type_name: m.node(59, 64, ElementaryTypeName::Int(4)), 510 | indexed: m.node(65, 72, Flag), 511 | name: None, 512 | }), 513 | m.node(74, 78, IndexedParameter { 514 | indexed: None, 515 | type_name: m.node(74, 78, ElementaryTypeName::Bool), 516 | name: None, 517 | }), 518 | ]), 519 | }), 520 | ]), 521 | }), 522 | ]); 523 | } 524 | 525 | #[test] 526 | fn event_with_named_parameters() { 527 | let m = Mock::new(); 528 | 529 | assert_units(r#" 530 | 531 | contract Foo { 532 | event Horizon(int32 indexed foo, bool bar); 533 | } 534 | 535 | "#, [ 536 | m.node(14, 102, ContractDefinition { 537 | name: m.node(23, 26, "Foo"), 538 | inherits: NodeList::empty(), 539 | body: m.list([ 540 | m.node(45, 88, EventDefinition { 541 | anonymous: None, 542 | name: m.node(51, 58, "Horizon"), 543 | params: m.list([ 544 | m.node(59, 76, IndexedParameter { 545 | type_name: m.node(59, 64, ElementaryTypeName::Int(4)), 546 | indexed: m.node(65, 72, Flag), 547 | name: m.node(73, 76, "foo"), 548 | }), 549 | m.node(78, 86, IndexedParameter { 550 | indexed: None, 551 | type_name: m.node(78, 82, ElementaryTypeName::Bool), 552 | name: m.node(83, 86, "bar"), 553 | }), 554 | ]), 555 | }), 556 | ]), 557 | }), 558 | ]); 559 | } 560 | 561 | #[test] 562 | fn enum_definition() { 563 | let m = Mock::new(); 564 | 565 | assert_units(r#" 566 | 567 | contract Foo { 568 | enum Empty {} 569 | enum Doge { To, The, Moon } 570 | } 571 | 572 | "#, [ 573 | m.node(14, 116, ContractDefinition { 574 | name: m.node(23, 26, "Foo"), 575 | inherits: NodeList::empty(), 576 | body: m.list([ 577 | m.node(45, 58, EnumDefinition { 578 | name: m.node(50, 55, "Empty"), 579 | variants: NodeList::empty(), 580 | }), 581 | m.node(75, 102, EnumDefinition { 582 | name: m.node(80, 84, "Doge"), 583 | variants: m.list([ 584 | m.node(87, 89, "To"), 585 | m.node(91, 94, "The"), 586 | m.node(96, 100, "Moon"), 587 | ]) 588 | }), 589 | ]), 590 | }), 591 | ]); 592 | } 593 | } 594 | -------------------------------------------------------------------------------- /parser/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{self, Debug}; 2 | use std::ops::Range; 3 | use Token; 4 | 5 | /// Error type used by the tokenizer and the parser internally. 6 | #[derive(PartialEq, Clone)] 7 | pub struct Error { 8 | pub token: Token, 9 | pub raw: Box, 10 | pub span: Range, 11 | } 12 | 13 | impl Debug for Error { 14 | #[inline] 15 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 16 | write!(f, "Unexpected {:?}({}) at {}:{}", &self.token, &*self.raw, self.span.start, self.span.end) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /parser/src/expect_macro.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! expect { 3 | ($parser:ident, $option:expr) => { 4 | match $option { 5 | Some(node) => node, 6 | None => { 7 | $parser.error(); 8 | 9 | return None; 10 | } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /parser/src/expression.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::ListBuilder; 2 | 3 | use ast::*; 4 | use {Parser, Precedence, P2, TOP}; 5 | use lexer::{Token, Logos, lookup}; 6 | 7 | type HandlerFn = for<'ast> fn(&mut Parser<'ast>) -> Option>; 8 | 9 | static EXPRESSION_LUT: [HandlerFn; Token::SIZE] = lookup! { 10 | Token::KeywordThis => |par| par.node_at_token(ThisExpression), 11 | Token::Identifier => |par| par.node_from_slice(|ident| ident), 12 | Token::IdentifierBuiltin => |par| par.node_from_slice(|ident| ident), 13 | Token::ParenOpen => |par| par.tuple_expression(), 14 | Token::OperatorLogicalNot => |par| par.prefix_expression(PrefixOperator::LogicalNot), 15 | Token::OperatorBitNot => |par| par.prefix_expression(PrefixOperator::BitNot), 16 | Token::KeywordDelete => |par| par.prefix_expression(PrefixOperator::Delete), 17 | Token::OperatorIncrement => |par| par.prefix_expression(PrefixOperator::Increment), 18 | Token::OperatorDecrement => |par| par.prefix_expression(PrefixOperator::Decrement), 19 | Token::OperatorAddition => |par| par.prefix_expression(PrefixOperator::Plus), 20 | Token::OperatorSubtraction => |par| par.prefix_expression(PrefixOperator::Minus), 21 | Token::LiteralTrue => |par| par.node_at_token(Primitive::Bool(true)), 22 | Token::LiteralFalse => |par| par.node_at_token(Primitive::Bool(false)), 23 | Token::LiteralHex => |par| par.node_from_slice(|slice| Primitive::HexNumber(slice)), 24 | Token::LiteralInteger => |par| par.integer_number(), 25 | Token::LiteralRational => |par| par.node_from_slice(|slice| Primitive::RationalNumber(slice)), 26 | Token::LiteralString => |par| par.node_from_slice(|slice| Primitive::String(slice)), 27 | Token::TypeBool => |par| par.node_at_token(ElementaryTypeName::Bool), 28 | Token::TypeAddress => |par| par.node_at_token(ElementaryTypeName::Address), 29 | Token::TypeString => |par| par.node_at_token(ElementaryTypeName::String), 30 | Token::TypeByte => |par| { 31 | let size = par.lexer.extras.0; 32 | 33 | par.node_at_token(ElementaryTypeName::Byte(size)) 34 | }, 35 | Token::TypeBytes => |par| { 36 | par.node_at_token(ElementaryTypeName::Bytes) 37 | }, 38 | Token::TypeInt => |par| { 39 | let size = par.lexer.extras.0; 40 | 41 | par.node_at_token(ElementaryTypeName::Int(size)) 42 | }, 43 | Token::TypeUint => |par| { 44 | let size = par.lexer.extras.0; 45 | 46 | par.node_at_token(ElementaryTypeName::Uint(size)) 47 | }, 48 | Token::TypeFixed => |par| { 49 | let size = par.lexer.extras; 50 | 51 | par.node_at_token(ElementaryTypeName::Fixed(size.0, size.1)) 52 | }, 53 | Token::TypeUfixed => |par| { 54 | let size = par.lexer.extras; 55 | 56 | par.node_at_token(ElementaryTypeName::Ufixed(size.0, size.1)) 57 | }, 58 | _ => |_| None, 59 | }; 60 | 61 | impl<'ast> Parser<'ast> { 62 | #[inline] 63 | pub fn expression(&mut self, precedence: Precedence) -> Option> { 64 | EXPRESSION_LUT[self.lexer.token as usize](self) 65 | .map(|expression| self.nested_expression(expression, precedence)) 66 | } 67 | 68 | #[inline] 69 | pub fn expression_list(&mut self) -> ExpressionList<'ast> { 70 | let builder = match self.expression(TOP) { 71 | Some(expression) => ListBuilder::new(self.arena, expression), 72 | None => return NodeList::empty(), 73 | }; 74 | 75 | while self.allow(Token::Comma) { 76 | match self.expression(TOP) { 77 | Some(expression) => builder.push(self.arena, expression), 78 | None => self.error(), 79 | } 80 | } 81 | 82 | builder.as_list() 83 | } 84 | 85 | fn tuple_expression(&mut self) -> Option> { 86 | let start = self.start_then_advance(); 87 | let expressions = self.expression_list(); 88 | let end = self.expect_end(Token::ParenClose); 89 | 90 | self.node_at(start, end, TupleExpression { 91 | expressions, 92 | }) 93 | } 94 | 95 | fn prefix_expression(&mut self, operator: PrefixOperator) -> Option> { 96 | let operator: Node<_> = self.node_at_token(operator); 97 | let operand = expect!(self, self.expression(P2)); 98 | 99 | self.node_at(operator.start, operand.end, PrefixExpression { 100 | operator, 101 | operand, 102 | }) 103 | } 104 | 105 | fn integer_number(&mut self) -> Option> { 106 | let number = self.lexer.slice(); 107 | let (start, end) = self.loc(); 108 | 109 | self.lexer.advance(); 110 | 111 | let unit = match self.lexer.token { 112 | Token::UnitEther => NumberUnit::Ether(EtherUnit::Ether), 113 | Token::UnitFinney => NumberUnit::Ether(EtherUnit::Finney), 114 | Token::UnitSzabo => NumberUnit::Ether(EtherUnit::Szabo), 115 | Token::UnitWei => NumberUnit::Ether(EtherUnit::Wei), 116 | Token::UnitTimeYears => NumberUnit::Time(TimeUnit::Years), 117 | Token::UnitTimeWeeks => NumberUnit::Time(TimeUnit::Weeks), 118 | Token::UnitTimeDays => NumberUnit::Time(TimeUnit::Days), 119 | Token::UnitTimeHours => NumberUnit::Time(TimeUnit::Hours), 120 | Token::UnitTimeMinutes => NumberUnit::Time(TimeUnit::Minutes), 121 | Token::UnitTimeSeconds => NumberUnit::Time(TimeUnit::Seconds), 122 | 123 | _ => return self.node_at(start, end, Primitive::IntegerNumber(number, NumberUnit::None)), 124 | }; 125 | 126 | let end = self.end_then_advance(); 127 | 128 | self.node_at(start, end, Primitive::IntegerNumber(number, unit)) 129 | } 130 | } 131 | 132 | 133 | #[cfg(test)] 134 | mod test { 135 | use super::*; 136 | use mock::{Mock, assert_units}; 137 | 138 | #[test] 139 | fn nested_expressions() { 140 | let m = Mock::new(); 141 | 142 | assert_units(r#" 143 | 144 | contract Foo { 145 | function() { 146 | !doge; 147 | ~doge; 148 | delete doge; 149 | ++doge; 150 | --doge; 151 | +doge; 152 | -doge; 153 | } 154 | } 155 | 156 | "#, [ 157 | m.node(14, 286, ContractDefinition { 158 | name: m.node(23, 26, "Foo"), 159 | inherits: NodeList::empty(), 160 | body: m.list([ 161 | m.node(45, 272, FunctionDefinition { 162 | name: None, 163 | params: NodeList::empty(), 164 | visibility: None, 165 | mutability: None, 166 | modifiers: NodeList::empty(), 167 | returns: NodeList::empty(), 168 | block: m.node(56, 272, Block { 169 | body: m.list([ 170 | m.stmt_expr(78, 83, 84, PrefixExpression { 171 | operator: m.node(78, 79, PrefixOperator::LogicalNot), 172 | operand: m.node(79, 83, "doge"), 173 | }), 174 | m.stmt_expr(105, 110, 111, PrefixExpression { 175 | operator: m.node(105, 106, PrefixOperator::BitNot), 176 | operand: m.node(106, 110, "doge"), 177 | }), 178 | m.stmt_expr(132, 143, 144, PrefixExpression { 179 | operator: m.node(132, 138, PrefixOperator::Delete), 180 | operand: m.node(139, 143, "doge"), 181 | }), 182 | m.stmt_expr(165, 171, 172, PrefixExpression { 183 | operator: m.node(165, 167, PrefixOperator::Increment), 184 | operand: m.node(167, 171, "doge"), 185 | }), 186 | m.stmt_expr(193, 199, 200, PrefixExpression { 187 | operator: m.node(193, 195, PrefixOperator::Decrement), 188 | operand: m.node(195, 199, "doge"), 189 | }), 190 | m.stmt_expr(221, 226, 227, PrefixExpression { 191 | operator: m.node(221, 222, PrefixOperator::Plus), 192 | operand: m.node(222, 226, "doge"), 193 | }), 194 | m.stmt_expr(248, 253, 254, PrefixExpression { 195 | operator: m.node(248, 249, PrefixOperator::Minus), 196 | operand: m.node(249, 253, "doge"), 197 | }), 198 | ]), 199 | }), 200 | }), 201 | ]), 202 | }), 203 | ]); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /parser/src/function.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::{ListBuilder, GrowableList}; 2 | 3 | use ast::*; 4 | use {Parser, FunctionContext, RegularTypeNameContext}; 5 | use lexer::Token; 6 | 7 | impl<'ast> Parser<'ast> { 8 | pub fn function_definition(&mut self) -> Option> { 9 | let start = self.start_then_advance(); 10 | 11 | let name = match self.lexer.token { 12 | Token::Identifier => self.str_node(), 13 | _ => None, 14 | }; 15 | 16 | self.expect(Token::ParenOpen); 17 | 18 | let params = self.parameter_list(); 19 | 20 | self.expect(Token::ParenClose); 21 | 22 | let mut mutability = None; 23 | let mut visibility = None; 24 | let modifiers = GrowableList::new(); 25 | 26 | loop { 27 | match self.lexer.token { 28 | Token::KeywordExternal => self.unique_flag(&mut visibility, FunctionVisibility::External), 29 | Token::KeywordPublic => self.unique_flag(&mut visibility, FunctionVisibility::Public), 30 | Token::KeywordInternal => self.unique_flag(&mut visibility, FunctionVisibility::Internal), 31 | Token::KeywordPrivate => self.unique_flag(&mut visibility, FunctionVisibility::Private), 32 | 33 | Token::KeywordPure => self.unique_flag(&mut mutability, StateMutability::Pure), 34 | Token::KeywordConstant => self.unique_flag(&mut mutability, StateMutability::Constant), 35 | Token::KeywordView => self.unique_flag(&mut mutability, StateMutability::View), 36 | Token::KeywordPayable => self.unique_flag(&mut mutability, StateMutability::Payable), 37 | 38 | _ => match self.modifier_invocation() { 39 | Some(modifier) => modifiers.push(self.arena, modifier), 40 | None => break, 41 | } 42 | } 43 | } 44 | 45 | let modifiers = modifiers.as_list(); 46 | let returns; 47 | 48 | if self.allow(Token::KeywordReturns) { 49 | self.expect(Token::ParenOpen); 50 | 51 | returns = self.parameter_list(); 52 | 53 | self.expect(Token::ParenClose); 54 | } else { 55 | returns = NodeList::empty(); 56 | } 57 | 58 | let (end, block) = match self.lexer.token { 59 | Token::BraceOpen => { 60 | let block = self.block::(); 61 | 62 | (block.end, Some(block)) 63 | }, 64 | _ => (self.expect_end(Token::Semicolon), None), 65 | }; 66 | 67 | self.node_at(start, end, FunctionDefinition { 68 | name, 69 | params, 70 | visibility, 71 | mutability, 72 | modifiers, 73 | returns, 74 | block, 75 | }) 76 | } 77 | 78 | fn modifier_invocation(&mut self) -> Option>> { 79 | let id = self.allow_str_node(Token::Identifier)?; 80 | 81 | let arguments; 82 | let end; 83 | 84 | if self.allow(Token::ParenOpen) { 85 | arguments = self.expression_list(); 86 | end = self.expect_end(Token::ParenClose); 87 | } else { 88 | arguments = NodeList::empty(); 89 | end = id.end; 90 | }; 91 | 92 | self.node_at(id.start, end, ModifierInvocation { 93 | id, 94 | arguments, 95 | }) 96 | } 97 | 98 | pub fn parameter_list(&mut self) -> ParameterList<'ast> { 99 | match self.parameter() { 100 | Some(param) => { 101 | let builder = ListBuilder::new(self.arena, param); 102 | 103 | while self.allow(Token::Comma) { 104 | match self.parameter() { 105 | Some(param) => builder.push(self.arena, param), 106 | None => self.error(), 107 | } 108 | } 109 | 110 | builder.as_list() 111 | }, 112 | None => NodeList::empty(), 113 | } 114 | } 115 | 116 | fn parameter(&mut self) -> Option>> { 117 | let type_name = self.type_name::()?; 118 | let name = self.allow_str_node(Token::Identifier); 119 | 120 | let end = name.end().unwrap_or_else(|| type_name.end); 121 | 122 | self.node_at(type_name.start, end, Parameter { 123 | type_name, 124 | name, 125 | }) 126 | } 127 | } 128 | 129 | #[cfg(test)] 130 | mod test { 131 | use super::*; 132 | use mock::{Mock, assert_units}; 133 | 134 | #[test] 135 | fn empty_function() { 136 | let m = Mock::new(); 137 | 138 | assert_units(r#" 139 | 140 | contract Foo { 141 | function(); 142 | function bar(); 143 | } 144 | 145 | "#, [ 146 | m.node(14, 102, ContractDefinition { 147 | name: m.node(23, 26, "Foo"), 148 | inherits: NodeList::empty(), 149 | body: m.list([ 150 | m.node(45, 56, FunctionDefinition { 151 | name: None, 152 | params: NodeList::empty(), 153 | visibility: None, 154 | mutability: None, 155 | modifiers: NodeList::empty(), 156 | returns: NodeList::empty(), 157 | block: None, 158 | }), 159 | m.node(73, 88, FunctionDefinition { 160 | name: m.node(82, 85, "bar"), 161 | params: NodeList::empty(), 162 | visibility: None, 163 | mutability: None, 164 | modifiers: NodeList::empty(), 165 | returns: NodeList::empty(), 166 | block: None, 167 | }), 168 | ]), 169 | }), 170 | ]); 171 | } 172 | 173 | #[test] 174 | fn function_parameters() { 175 | let m = Mock::new(); 176 | 177 | assert_units(r#" 178 | 179 | contract Foo { 180 | function(uint56, bool); 181 | } 182 | 183 | "#, [ 184 | m.node(14, 82, ContractDefinition { 185 | name: m.node(23, 26, "Foo"), 186 | inherits: NodeList::empty(), 187 | body: m.list([ 188 | m.node(45, 68, FunctionDefinition { 189 | name: None, 190 | params: m.list([ 191 | m.node(54, 60, Parameter { 192 | type_name: m.node(54, 60, ElementaryTypeName::Uint(7)), 193 | name: None, 194 | }), 195 | m.node(62, 66, Parameter { 196 | type_name: m.node(62, 66, ElementaryTypeName::Bool), 197 | name: None, 198 | }), 199 | ]), 200 | visibility: None, 201 | mutability: None, 202 | modifiers: NodeList::empty(), 203 | returns: NodeList::empty(), 204 | block: None, 205 | }), 206 | ]), 207 | }), 208 | ]); 209 | } 210 | 211 | #[test] 212 | fn function_named_parameters() { 213 | let m = Mock::new(); 214 | 215 | assert_units(r#" 216 | 217 | contract Foo { 218 | function doge(uint56 wow, bool moon); 219 | } 220 | 221 | "#, [ 222 | m.node(14, 96, ContractDefinition { 223 | name: m.node(23, 26, "Foo"), 224 | inherits: NodeList::empty(), 225 | body: m.list([ 226 | m.node(45, 82, FunctionDefinition { 227 | name: m.node(54, 58, "doge"), 228 | params: m.list([ 229 | m.node(59, 69, Parameter { 230 | type_name: m.node(59, 65, ElementaryTypeName::Uint(7)), 231 | name: m.node(66, 69, "wow"), 232 | }), 233 | m.node(71, 80, Parameter { 234 | type_name: m.node(71, 75, ElementaryTypeName::Bool), 235 | name: m.node(76, 80, "moon"), 236 | }), 237 | ]), 238 | visibility: None, 239 | mutability: None, 240 | modifiers: NodeList::empty(), 241 | returns: NodeList::empty(), 242 | block: None, 243 | }), 244 | ]), 245 | }), 246 | ]); 247 | } 248 | 249 | #[test] 250 | fn function_returns() { 251 | let m = Mock::new(); 252 | 253 | assert_units(r#" 254 | 255 | contract Foo { 256 | function doge() returns (uint56, bool); 257 | } 258 | 259 | "#, [ 260 | m.node(14, 98, ContractDefinition { 261 | name: m.node(23, 26, "Foo"), 262 | inherits: NodeList::empty(), 263 | body: m.list([ 264 | m.node(45, 84, FunctionDefinition { 265 | name: m.node(54, 58, "doge"), 266 | params: NodeList::empty(), 267 | visibility: None, 268 | mutability: None, 269 | modifiers: NodeList::empty(), 270 | returns: m.list([ 271 | m.node(70, 76, Parameter { 272 | type_name: m.node(70, 76, ElementaryTypeName::Uint(7)), 273 | name: None, 274 | }), 275 | m.node(78, 82, Parameter { 276 | type_name: m.node(78, 82, ElementaryTypeName::Bool), 277 | name: None, 278 | }), 279 | ]), 280 | block: None, 281 | }), 282 | ]), 283 | }), 284 | ]); 285 | } 286 | 287 | #[test] 288 | fn function_mutability_and_visibility() { 289 | let m = Mock::new(); 290 | 291 | assert_units(r#" 292 | 293 | contract Foo { 294 | function wow() pure external; 295 | function such() internal view; 296 | function very() private; 297 | function much() payable; 298 | } 299 | 300 | "#, [ 301 | m.node(14, 217, ContractDefinition { 302 | name: m.node(23, 26, "Foo"), 303 | inherits: NodeList::empty(), 304 | body: m.list([ 305 | m.node(45, 74, FunctionDefinition { 306 | name: m.node(54, 57, "wow"), 307 | params: NodeList::empty(), 308 | visibility: m.node(65, 73, FunctionVisibility::External), 309 | mutability: m.node(60, 64, StateMutability::Pure), 310 | modifiers: NodeList::empty(), 311 | returns: NodeList::empty(), 312 | block: None, 313 | }), 314 | m.node(91, 121, FunctionDefinition { 315 | name: m.node(100, 104, "such"), 316 | params: NodeList::empty(), 317 | visibility: m.node(107, 115, FunctionVisibility::Internal), 318 | mutability: m.node(116, 120, StateMutability::View), 319 | modifiers: NodeList::empty(), 320 | returns: NodeList::empty(), 321 | block: None, 322 | }), 323 | m.node(138, 162, FunctionDefinition { 324 | name: m.node(147, 151, "very"), 325 | params: NodeList::empty(), 326 | visibility: m.node(154, 161, FunctionVisibility::Private), 327 | mutability: None, 328 | modifiers: NodeList::empty(), 329 | returns: NodeList::empty(), 330 | block: None, 331 | }), 332 | m.node(179, 203, FunctionDefinition { 333 | name: m.node(188, 192, "much"), 334 | params: NodeList::empty(), 335 | visibility: None, 336 | mutability: m.node(195, 202, StateMutability::Payable), 337 | modifiers: NodeList::empty(), 338 | returns: NodeList::empty(), 339 | block: None, 340 | }), 341 | ]), 342 | }), 343 | ]); 344 | } 345 | 346 | #[test] 347 | fn function_modifiers() { 348 | let m = Mock::new(); 349 | 350 | assert_units(r#" 351 | 352 | contract Foo { 353 | function() only_doges such pure moon; 354 | } 355 | 356 | "#, [ 357 | m.node(14, 96, ContractDefinition { 358 | name: m.node(23, 26, "Foo"), 359 | inherits: NodeList::empty(), 360 | body: m.list([ 361 | m.node(45, 82, FunctionDefinition { 362 | name: None, 363 | params: NodeList::empty(), 364 | visibility: None, 365 | mutability: m.node(72, 76, StateMutability::Pure), 366 | modifiers: m.list([ 367 | m.node(56, 66, ModifierInvocation { 368 | id: m.node(56, 66, "only_doges"), 369 | arguments: NodeList::empty(), 370 | }), 371 | m.node(67, 71, ModifierInvocation { 372 | id: m.node(67, 71, "such"), 373 | arguments: NodeList::empty(), 374 | }), 375 | m.node(77, 81, ModifierInvocation { 376 | id: m.node(77, 81, "moon"), 377 | arguments: NodeList::empty(), 378 | }), 379 | ]), 380 | returns: NodeList::empty(), 381 | block: None, 382 | }), 383 | ]), 384 | }), 385 | ]); 386 | } 387 | 388 | #[test] 389 | fn function_modifiers_with_arguments() { 390 | let m = Mock::new(); 391 | 392 | assert_units(r#" 393 | 394 | contract Foo { 395 | function() only(moon, "doges") such() pure; 396 | } 397 | 398 | "#, [ 399 | m.node(14, 102, ContractDefinition { 400 | name: m.node(23, 26, "Foo"), 401 | inherits: NodeList::empty(), 402 | body: m.list([ 403 | m.node(45, 88, FunctionDefinition { 404 | name: None, 405 | params: NodeList::empty(), 406 | visibility: None, 407 | mutability: m.node(83, 87, StateMutability::Pure), 408 | modifiers: m.list([ 409 | m.node(56, 75, ModifierInvocation { 410 | id: m.node(56, 60, "only"), 411 | arguments: m.list([ 412 | m.node(61, 65, "moon"), 413 | m.node(67, 74, Primitive::String("\"doges\"")), 414 | ]), 415 | }), 416 | m.node(76, 82, ModifierInvocation { 417 | id: m.node(76, 80, "such"), 418 | arguments: NodeList::empty(), 419 | }), 420 | ]), 421 | returns: NodeList::empty(), 422 | block: None, 423 | }), 424 | ]), 425 | }), 426 | ]); 427 | } 428 | 429 | #[test] 430 | fn function_flags_are_unique_per_kind() { 431 | use parse; 432 | 433 | // TODO: Better errors 434 | assert!(parse("contract Foo { function() public public; }").is_err()); 435 | assert!(parse("contract Foo { function() pure pure; }").is_err()); 436 | assert!(parse("contract Foo { function() internal external; }").is_err()); 437 | assert!(parse("contract Foo { function() payable constant; }").is_err()); 438 | } 439 | } 440 | -------------------------------------------------------------------------------- /parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate toolshed; 2 | extern crate lunarity_lexer as lexer; 3 | extern crate lunarity_ast as ast; 4 | 5 | #[cfg(test)] mod mock; 6 | #[macro_use] mod expect_macro; 7 | 8 | #[cfg(test)] 9 | #[macro_use] 10 | extern crate pretty_assertions; 11 | 12 | 13 | mod source; 14 | mod type_name; 15 | mod contract; 16 | mod function; 17 | mod expression; 18 | mod nested; 19 | mod statement; 20 | mod assembly; 21 | mod error; 22 | 23 | use toolshed::{Arena, NulTermStr}; 24 | use toolshed::list::GrowableList; 25 | 26 | pub use self::statement::{StatementContext, FunctionContext, ModifierContext}; 27 | pub use self::type_name::{TypeNameContext, RegularTypeNameContext, StatementTypeNameContext}; 28 | pub use self::nested::*; 29 | 30 | use ast::*; 31 | use error::Error; 32 | use lexer::{Lexer, Token}; 33 | use lexer::Token::*; 34 | 35 | 36 | pub struct Parser<'ast> { 37 | arena: &'ast Arena, 38 | 39 | /// Lexer will produce tokens from the source 40 | lexer: Lexer>, 41 | 42 | /// Errors occurred during parsing 43 | errors: Vec, 44 | 45 | /// AST under construction 46 | body: SourceUnitList<'ast>, 47 | } 48 | 49 | impl<'ast> Parser<'ast> { 50 | pub fn new(source: &str, arena: &'ast Arena) -> Self { 51 | let source = arena.alloc_nul_term_str(source); 52 | 53 | Parser { 54 | arena, 55 | lexer: Lexer::new(source), 56 | errors: Vec::new(), 57 | body: NodeList::empty(), 58 | } 59 | } 60 | 61 | #[inline] 62 | fn allow(&mut self, token: Token) -> bool { 63 | if self.lexer.token == token { 64 | self.lexer.advance(); 65 | true 66 | } else { 67 | false 68 | } 69 | } 70 | 71 | #[inline] 72 | fn expect(&mut self, token: Token) { 73 | if self.lexer.token == token { 74 | self.lexer.advance(); 75 | } else { 76 | self.error(); 77 | } 78 | } 79 | 80 | #[inline] 81 | fn expect_exact(&mut self, token: Token, expected: &str) { 82 | if self.lexer.token == token && self.lexer.slice() == expected { 83 | self.lexer.advance(); 84 | } else { 85 | self.error(); 86 | } 87 | } 88 | 89 | #[inline] 90 | fn expect_end(&mut self, token: Token) -> u32 { 91 | let end = self.lexer.range().end as u32; 92 | 93 | self.expect(token); 94 | 95 | end 96 | } 97 | 98 | #[inline] 99 | fn str_node(&mut self) -> R 100 | where 101 | R: From>, 102 | { 103 | let node = self.lexer.slice(); 104 | 105 | self.node_at_token(node) 106 | } 107 | 108 | #[inline] 109 | fn expect_str_node(&mut self, token: Token) -> Node<'ast, &'ast str> { 110 | let val = self.lexer.slice(); 111 | let (start, end) = self.loc(); 112 | 113 | self.expect(token); 114 | 115 | self.node_at(start, end, val) 116 | } 117 | 118 | #[inline] 119 | fn allow_str_node(&mut self, token: Token) -> Option> { 120 | if self.lexer.token == token { 121 | self.str_node() 122 | } else { 123 | None 124 | } 125 | } 126 | 127 | #[inline] 128 | fn allow_flag_node(&mut self, token: Token) -> Option> { 129 | if self.lexer.token == token { 130 | self.node_at_token(Flag) 131 | } else { 132 | None 133 | } 134 | } 135 | 136 | #[inline] 137 | fn loc(&mut self) -> (u32, u32) { 138 | let range = self.lexer.range(); 139 | 140 | (range.start as u32, range.end as u32) 141 | } 142 | 143 | #[inline] 144 | fn start_then_advance(&mut self) -> u32 { 145 | let start = self.lexer.range().start as u32; 146 | 147 | self.lexer.advance(); 148 | 149 | start 150 | } 151 | 152 | #[inline] 153 | fn end_then_advance(&mut self) -> u32 { 154 | let end = self.lexer.range().end as u32; 155 | 156 | self.lexer.advance(); 157 | 158 | end 159 | } 160 | 161 | fn error(&mut self) { 162 | let token = self.lexer.token; 163 | let raw = self.lexer.slice().into(); 164 | let span = self.lexer.range(); 165 | 166 | self.errors.push(Error { 167 | token, 168 | raw, 169 | span, 170 | }); 171 | } 172 | 173 | #[inline] 174 | fn alloc(&mut self, val: NodeInner) -> Node<'ast, T> 175 | where 176 | T: Copy, 177 | { 178 | Node::new(self.arena.alloc(val)) 179 | } 180 | 181 | #[inline] 182 | fn node_at(&mut self, start: u32, end: u32, item: I) -> R 183 | where 184 | T: 'ast + Copy, 185 | I: Into, 186 | R: From>, 187 | { 188 | From::from(self.alloc(NodeInner::new(start, end, item.into()))) 189 | } 190 | 191 | #[inline] 192 | fn node_at_token(&mut self, item: I) -> R 193 | where 194 | T: 'ast + Copy, 195 | I: Into, 196 | R: From>, 197 | { 198 | let (start, end) = self.loc(); 199 | 200 | self.lexer.advance(); 201 | 202 | self.node_at(start, end, item) 203 | } 204 | 205 | #[inline] 206 | fn node_from_slice(&mut self, func: F) -> R 207 | where 208 | T: 'ast + Copy, 209 | F: FnOnce(&'ast str) -> I, 210 | I: Into, 211 | R: From>, 212 | { 213 | let slice = self.lexer.slice(); 214 | let (start, end) = self.loc(); 215 | 216 | self.lexer.advance(); 217 | 218 | self.node_at(start, end, func(slice)) 219 | } 220 | 221 | #[inline] 222 | fn parse(&mut self) { 223 | let builder = GrowableList::new(); 224 | 225 | while let Some(unit) = self.source_unit() { 226 | builder.push(self.arena, unit); 227 | } 228 | 229 | self.body = builder.as_list(); 230 | 231 | self.expect(EndOfProgram); 232 | } 233 | 234 | #[inline] 235 | fn unique_flag(&mut self, at: &mut Option>, flag: F) 236 | where 237 | F: Copy, 238 | { 239 | if at.is_some() { 240 | self.lexer.advance(); 241 | 242 | // TODO: More descriptive errors, something like "Can't redeclare visibility/mutability" 243 | return self.error(); 244 | } 245 | 246 | *at = self.node_at_token(flag); 247 | } 248 | } 249 | 250 | /// Parse the Solidity source from `&str` and produce an Abstract Syntax Tree for it. 251 | pub fn parse<'src, 'ast>(source: &'src str) -> Result, Vec> { 252 | let arena = Arena::new(); 253 | 254 | let (body, errors) = { 255 | let mut parser = Parser::new(source, &arena); 256 | 257 | parser.parse(); 258 | 259 | (parser.body.into_unsafe(), parser.errors) 260 | }; 261 | 262 | match errors.len() { 263 | 0 => Ok(Program::new(body, arena)), 264 | _ => Err(errors) 265 | } 266 | } 267 | 268 | 269 | #[cfg(test)] 270 | mod test { 271 | use super::*; 272 | 273 | #[test] 274 | fn can_parse_second_price_auction() { 275 | let source = include_str!("../../lunarity/benches/second-price-auction.sol"); 276 | 277 | parse(source).unwrap(); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /parser/src/mock.rs: -------------------------------------------------------------------------------- 1 | use toolshed::Arena; 2 | use toolshed::list::List; 3 | 4 | use ast::*; 5 | 6 | pub struct Mock { 7 | arena: Arena 8 | } 9 | 10 | impl Mock { 11 | pub fn new() -> Self { 12 | Mock { 13 | arena: Arena::new() 14 | } 15 | } 16 | 17 | pub fn node<'mock, T, I, R>(&'mock self, start: u32, end: u32, val: I) -> R 18 | where 19 | T: 'mock + Copy, 20 | I: Into, 21 | R: From>, 22 | { 23 | Node::new(self.arena.alloc(NodeInner::new(start, end, val.into()))).into() 24 | } 25 | 26 | /// A variant of `node` to keep the type inference clean 27 | pub fn stmt_expr<'mock, I, R>(&'mock self, start: u32, e_end: u32, s_end: u32, val: I) -> R 28 | where 29 | I: Into>, 30 | R: From>, 31 | { 32 | let expression = Node::new(self.arena.alloc(NodeInner::new(start, e_end, val.into()))); 33 | 34 | Node::new(self.arena.alloc(NodeInner::new(start, s_end, expression.into()))).into() 35 | } 36 | 37 | pub fn list<'mock, T, L>(&'mock self, list: L) -> List where 38 | T: 'mock + Copy, 39 | L: AsRef<[T]>, 40 | { 41 | List::from_iter(&self.arena, list.as_ref().iter().cloned()) 42 | } 43 | } 44 | 45 | 46 | pub fn assert_units<'mock, E>(source: &str, expected: E) 47 | where 48 | E: AsRef<[SourceUnitNode<'mock>]> 49 | { 50 | use parse; 51 | 52 | let program = parse(source).unwrap(); 53 | 54 | let iter = program 55 | .body() 56 | .iter() 57 | .zip(expected.as_ref().iter()); 58 | 59 | for (got, expected) in iter { 60 | assert_eq!(got, expected); 61 | } 62 | 63 | let got = program.body().iter().count(); 64 | let expected = expected.as_ref().iter().count(); 65 | 66 | assert_eq!(got, expected, "Expected {} units, got {}", expected, got); 67 | } 68 | -------------------------------------------------------------------------------- /parser/src/nested.rs: -------------------------------------------------------------------------------- 1 | use Parser; 2 | use lexer::{Token, Logos, lookup}; 3 | use ast::*; 4 | 5 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] 6 | pub struct Precedence(u8); 7 | 8 | type HandlerFn = for<'ast> fn(&mut Parser<'ast>, ExpressionNode<'ast>) -> Option>; 9 | 10 | #[derive(Clone, Copy)] 11 | struct NestedHandler(Precedence, HandlerFn); 12 | 13 | pub const TOP: Precedence = Precedence(15); 14 | pub const P14: Precedence = Precedence(14); 15 | pub const P13: Precedence = Precedence(13); 16 | pub const P12: Precedence = Precedence(12); 17 | pub const P11: Precedence = Precedence(11); 18 | pub const P10: Precedence = Precedence(10); 19 | pub const P9: Precedence = Precedence(9); 20 | pub const P8: Precedence = Precedence(8); 21 | pub const P7: Precedence = Precedence(7); 22 | pub const P6: Precedence = Precedence(6); 23 | pub const P5: Precedence = Precedence(5); 24 | pub const P4: Precedence = Precedence(4); 25 | pub const P3: Precedence = Precedence(3); 26 | pub const P2: Precedence = Precedence(2); 27 | 28 | const INVALID: Precedence = Precedence(100); 29 | 30 | static NESTED_LUT: [NestedHandler; Token::SIZE] = lookup! { 31 | Token::Accessor => NestedHandler(P2, MEMBER), 32 | Token::ParenOpen => NestedHandler(P2, CALL), 33 | Token::BracketOpen => NestedHandler(P2, INDEX), 34 | Token::OperatorIncrement => NestedHandler(P2, INC), 35 | Token::OperatorDecrement => NestedHandler(P2, DEC), 36 | Token::OperatorExponent => NestedHandler(P3, EXPONENT), 37 | Token::OperatorMultiplication => NestedHandler(P4, MUL), 38 | Token::OperatorDivision => NestedHandler(P4, DIV), 39 | Token::OperatorRemainder => NestedHandler(P4, REMAINDER), 40 | Token::OperatorAddition => NestedHandler(P5, ADD), 41 | Token::OperatorSubtraction => NestedHandler(P5, SUB), 42 | Token::OperatorBitShiftLeft => NestedHandler(P6, BIT_SHIFT_LEFT), 43 | Token::OperatorBitShiftRight => NestedHandler(P6, BIT_SHIFT_RIGHT), 44 | Token::OperatorBitAnd => NestedHandler(P7, BIT_AND), 45 | Token::OperatorBitXor => NestedHandler(P8, BIT_XOR), 46 | Token::OperatorBitOr => NestedHandler(P9, BIT_OR), 47 | Token::OperatorLesser => NestedHandler(P10, LESSER), 48 | Token::OperatorLesserEquals => NestedHandler(P10, LESSER_EQUALITY), 49 | Token::OperatorGreater => NestedHandler(P10, GREATER), 50 | Token::OperatorGreaterEquals => NestedHandler(P10, GREATER_EQUALITY), 51 | Token::OperatorEquality => NestedHandler(P11, EQUALITY), 52 | Token::OperatorInequality => NestedHandler(P11, INEQUALITY), 53 | Token::OperatorLogicalAnd => NestedHandler(P12, LOGICAL_AND), 54 | Token::OperatorLogicalOr => NestedHandler(P13, LOGICAL_OR), 55 | Token::OperatorConditional => NestedHandler(P14, CONDITIONAL), 56 | Token::Assign => NestedHandler(TOP, ASSIGN), 57 | Token::AssignAddition => NestedHandler(TOP, ASSIGN_ADD), 58 | Token::AssignSubtraction => NestedHandler(TOP, ASSIGN_SUB), 59 | Token::AssignMultiplication => NestedHandler(TOP, ASSIGN_MUL), 60 | Token::AssignDivision => NestedHandler(TOP, ASSIGN_DIV), 61 | Token::AssignRemainder => NestedHandler(TOP, ASSIGN_REM), 62 | Token::AssignBitShiftLeft => NestedHandler(TOP, ASSIGN_BIT_SHIFT_LEFT), 63 | Token::AssignBitShiftRight => NestedHandler(TOP, ASSIGN_BIT_SHIFT_RIGHT), 64 | Token::AssignBitAnd => NestedHandler(TOP, ASSIGN_BIT_AND), 65 | Token::AssignBitXor => NestedHandler(TOP, ASSIGN_BIT_XOR), 66 | Token::AssignBitOr => NestedHandler(TOP, ASSIGN_BIT_OR), 67 | _ => NestedHandler(INVALID, |_, _| None), 68 | }; 69 | 70 | impl NestedHandler { 71 | #[inline] 72 | fn get(self, precedence: Precedence) -> Option { 73 | if self.0 <= precedence { 74 | Some(self.1) 75 | } else { 76 | None 77 | } 78 | } 79 | } 80 | 81 | const CALL: HandlerFn = |par, callee| { 82 | par.lexer.advance(); 83 | 84 | let arguments = par.expression_list(); 85 | let end = par.expect_end(Token::ParenClose); 86 | 87 | par.node_at(callee.start, end, CallExpression { 88 | callee, 89 | arguments, 90 | }) 91 | }; 92 | 93 | const MEMBER: HandlerFn = |par, object| { 94 | par.lexer.advance(); 95 | 96 | let member = par.expect_str_node(Token::Identifier); 97 | 98 | par.node_at(object.start, member.end, MemberAccessExpression { 99 | object, 100 | member, 101 | }) 102 | }; 103 | 104 | const INDEX: HandlerFn = |par, array| { 105 | par.lexer.advance(); 106 | 107 | let index = par.expression(TOP); 108 | let end = par.expect_end(Token::BracketClose); 109 | 110 | par.node_at(array.start, end, IndexAccessExpression { 111 | array, 112 | index, 113 | }) 114 | }; 115 | 116 | const INC: HandlerFn = |par, operand| { 117 | let operator: Node<_> = par.node_at_token(PostfixOperator::Increment); 118 | 119 | par.node_at(operand.start, operator.end, PostfixExpression { 120 | operator, 121 | operand, 122 | }) 123 | }; 124 | 125 | const DEC: HandlerFn = |par, operand| { 126 | let operator: Node<_> = par.node_at_token(PostfixOperator::Decrement); 127 | 128 | par.node_at(operand.start, operator.end, PostfixExpression { 129 | operator, 130 | operand, 131 | }) 132 | }; 133 | 134 | const CONDITIONAL: HandlerFn = |par, test| { 135 | par.lexer.advance(); 136 | 137 | let consequent = expect!(par, par.expression(P14)); 138 | 139 | par.expect(Token::Colon); 140 | 141 | let alternate = expect!(par, par.expression(P14)); 142 | 143 | par.node_at(test.start, alternate.end, ConditionalExpression { 144 | test, 145 | consequent, 146 | alternate, 147 | }) 148 | }; 149 | 150 | macro_rules! assign { 151 | ($name:ident => $op:ident) => { 152 | const $name: HandlerFn = |par, left| { 153 | // TODO: check if left is LValue 154 | 155 | let operator = par.node_at_token(AssignmentOperator::$op); 156 | let right = expect!(par, par.expression(TOP)); 157 | 158 | par.node_at(left.start, right.end, AssignmentExpression { 159 | operator, 160 | left, 161 | right, 162 | }) 163 | }; 164 | } 165 | } 166 | 167 | macro_rules! binary { 168 | ($name:ident, $precedence:ident => $op:ident) => { 169 | const $name: HandlerFn = |par, left| { 170 | let operator = par.node_at_token(BinaryOperator::$op); 171 | let right = expect!(par, par.expression($precedence)); 172 | 173 | par.node_at(left.start, right.end, BinaryExpression { 174 | operator, 175 | left, 176 | right, 177 | }) 178 | }; 179 | } 180 | } 181 | 182 | assign!(ASSIGN => Plain); 183 | assign!(ASSIGN_ADD => Addition); 184 | assign!(ASSIGN_SUB => Subtraction); 185 | assign!(ASSIGN_MUL => Multiplication); 186 | assign!(ASSIGN_DIV => Division); 187 | assign!(ASSIGN_REM => Remainder); 188 | assign!(ASSIGN_BIT_SHIFT_LEFT => BitShiftLeft); 189 | assign!(ASSIGN_BIT_SHIFT_RIGHT => BitShiftRight); 190 | assign!(ASSIGN_BIT_AND => BitAnd); 191 | assign!(ASSIGN_BIT_XOR => BitXor); 192 | assign!(ASSIGN_BIT_OR => BitOr); 193 | 194 | binary!(LOGICAL_OR , P13 => LogicalOr); 195 | binary!(LOGICAL_AND , P12 => LogicalAnd); 196 | binary!(EQUALITY , P11 => Equality); 197 | binary!(INEQUALITY , P11 => Inequality); 198 | binary!(LESSER , P10 => Lesser); 199 | binary!(LESSER_EQUALITY , P10 => LesserEquals); 200 | binary!(GREATER , P10 => Greater); 201 | binary!(GREATER_EQUALITY , P10 => GreaterEquals); 202 | binary!(BIT_OR , P9 => BitOr); 203 | binary!(BIT_XOR , P8 => BitXor); 204 | binary!(BIT_AND , P7 => BitAnd); 205 | binary!(BIT_SHIFT_LEFT , P6 => BitShiftLeft); 206 | binary!(BIT_SHIFT_RIGHT , P6 => BitShiftRight); 207 | binary!(ADD , P5 => Addition); 208 | binary!(SUB , P5 => Subtraction); 209 | binary!(MUL , P4 => Multiplication); 210 | binary!(DIV , P4 => Division); 211 | binary!(REMAINDER , P4 => Remainder); 212 | binary!(EXPONENT , P3 => Exponent); 213 | 214 | 215 | impl<'ast> Parser<'ast> { 216 | #[inline] 217 | pub fn nested_expression(&mut self, mut left: ExpressionNode<'ast>, precedence: Precedence) -> ExpressionNode<'ast> { 218 | while let Some(node) = NESTED_LUT[self.lexer.token as usize].get(precedence).and_then(|handler| handler(self, left)) { 219 | left = node; 220 | } 221 | 222 | left 223 | } 224 | } 225 | 226 | #[cfg(test)] 227 | mod test { 228 | use super::*; 229 | use mock::{Mock, assert_units}; 230 | 231 | #[test] 232 | fn nested_expressions() { 233 | let m = Mock::new(); 234 | 235 | assert_units(r#" 236 | 237 | contract Foo { 238 | function() { 239 | doge.moon; 240 | add(1, 2); 241 | things[1]; 242 | solidity++; 243 | solidity--; 244 | } 245 | } 246 | 247 | "#, [ 248 | m.node(14, 246, ContractDefinition { 249 | name: m.node(23, 26, "Foo"), 250 | inherits: NodeList::empty(), 251 | body: m.list([ 252 | m.node(45, 232, FunctionDefinition { 253 | name: None, 254 | params: NodeList::empty(), 255 | visibility: None, 256 | mutability: None, 257 | modifiers: NodeList::empty(), 258 | returns: NodeList::empty(), 259 | block: m.node(56, 232, Block { 260 | body: m.list([ 261 | m.stmt_expr(78, 87, 88, MemberAccessExpression { 262 | object: m.node(78, 82, "doge"), 263 | member: m.node(83, 87, "moon"), 264 | }), 265 | m.stmt_expr(109, 118, 119, CallExpression { 266 | callee: m.node(109, 112, "add"), 267 | arguments: m.list([ 268 | m.node(113, 114, Primitive::IntegerNumber("1", NumberUnit::None)), 269 | m.node(116, 117, Primitive::IntegerNumber("2", NumberUnit::None)), 270 | ]), 271 | }), 272 | m.stmt_expr(140, 149, 150, IndexAccessExpression { 273 | array: m.node(140, 146, "things"), 274 | index: m.node(147, 148, Primitive::IntegerNumber("1", NumberUnit::None)), 275 | }), 276 | m.stmt_expr(171, 181, 182, PostfixExpression { 277 | operand: m.node(171, 179, "solidity"), 278 | operator: m.node(179, 181, PostfixOperator::Increment), 279 | }), 280 | m.stmt_expr(203, 213, 214, PostfixExpression { 281 | operand: m.node(203, 211, "solidity"), 282 | operator: m.node(211, 213, PostfixOperator::Decrement), 283 | }), 284 | ]), 285 | }), 286 | }), 287 | ]), 288 | }), 289 | ]); 290 | } 291 | 292 | #[test] 293 | fn binary_expressions() { 294 | let m = Mock::new(); 295 | 296 | assert_units(r#" 297 | 298 | contract Foo { 299 | function() { 300 | a || b; 301 | a && b; 302 | a == b; 303 | a != b; 304 | a < b; 305 | a <= b; 306 | a > b; 307 | a >= b; 308 | a | b; 309 | a ^ b; 310 | a & b; 311 | a << b; 312 | a >> b; 313 | a + b; 314 | a - b; 315 | a * b; 316 | a / b; 317 | a % b; 318 | a ** b; 319 | } 320 | } 321 | 322 | "#, [ 323 | m.node(14, 611, ContractDefinition { 324 | name: m.node(23, 26, "Foo"), 325 | inherits: NodeList::empty(), 326 | body: m.list([ 327 | m.node(45, 597, FunctionDefinition { 328 | name: None, 329 | params: NodeList::empty(), 330 | visibility: None, 331 | mutability: None, 332 | modifiers: NodeList::empty(), 333 | returns: NodeList::empty(), 334 | block: m.node(56, 597, Block { 335 | body: m.list([ 336 | m.stmt_expr(78, 84, 85, BinaryExpression { 337 | left: m.node(78, 79, "a"), 338 | operator: m.node(80, 82, BinaryOperator::LogicalOr), 339 | right: m.node(83, 84, "b"), 340 | }), 341 | m.stmt_expr(106, 112, 113, BinaryExpression { 342 | left: m.node(106, 107, "a"), 343 | operator: m.node(108, 110, BinaryOperator::LogicalAnd), 344 | right: m.node(111, 112, "b"), 345 | }), 346 | m.stmt_expr(134, 140, 141, BinaryExpression { 347 | left: m.node(134, 135, "a"), 348 | operator: m.node(136, 138, BinaryOperator::Equality), 349 | right: m.node(139, 140, "b"), 350 | }), 351 | m.stmt_expr(162, 168, 169, BinaryExpression { 352 | left: m.node(162, 163, "a"), 353 | operator: m.node(164, 166, BinaryOperator::Inequality), 354 | right: m.node(167, 168, "b"), 355 | }), 356 | m.stmt_expr(190, 195, 196, BinaryExpression { 357 | left: m.node(190, 191, "a"), 358 | operator: m.node(192, 193, BinaryOperator::Lesser), 359 | right: m.node(194, 195, "b"), 360 | }), 361 | m.stmt_expr(217, 223, 224, BinaryExpression { 362 | left: m.node(217, 218, "a"), 363 | operator: m.node(219, 221, BinaryOperator::LesserEquals), 364 | right: m.node(222, 223, "b"), 365 | }), 366 | m.stmt_expr(245, 250, 251, BinaryExpression { 367 | left: m.node(245, 246, "a"), 368 | operator: m.node(247, 248, BinaryOperator::Greater), 369 | right: m.node(249, 250, "b"), 370 | }), 371 | m.stmt_expr(272, 278, 279, BinaryExpression { 372 | left: m.node(272, 273, "a"), 373 | operator: m.node(274, 276, BinaryOperator::GreaterEquals), 374 | right: m.node(277, 278, "b"), 375 | }), 376 | m.stmt_expr(300, 305, 306, BinaryExpression { 377 | left: m.node(300, 301, "a"), 378 | operator: m.node(302, 303, BinaryOperator::BitOr), 379 | right: m.node(304, 305, "b"), 380 | }), 381 | m.stmt_expr(327, 332, 333, BinaryExpression { 382 | left: m.node(327, 328, "a"), 383 | operator: m.node(329, 330, BinaryOperator::BitXor), 384 | right: m.node(331, 332, "b"), 385 | }), 386 | m.stmt_expr(354, 359, 360, BinaryExpression { 387 | left: m.node(354, 355, "a"), 388 | operator: m.node(356, 357, BinaryOperator::BitAnd), 389 | right: m.node(358, 359, "b"), 390 | }), 391 | m.stmt_expr(381, 387, 388, BinaryExpression { 392 | left: m.node(381, 382, "a"), 393 | operator: m.node(383, 385, BinaryOperator::BitShiftLeft), 394 | right: m.node(386, 387, "b"), 395 | }), 396 | m.stmt_expr(409, 415, 416, BinaryExpression { 397 | left: m.node(409, 410, "a"), 398 | operator: m.node(411, 413, BinaryOperator::BitShiftRight), 399 | right: m.node(414, 415, "b"), 400 | }), 401 | m.stmt_expr(437, 442, 443, BinaryExpression { 402 | left: m.node(437, 438, "a"), 403 | operator: m.node(439, 440, BinaryOperator::Addition), 404 | right: m.node(441, 442, "b"), 405 | }), 406 | m.stmt_expr(464, 469, 470, BinaryExpression { 407 | left: m.node(464, 465, "a"), 408 | operator: m.node(466, 467, BinaryOperator::Subtraction), 409 | right: m.node(468, 469, "b"), 410 | }), 411 | m.stmt_expr(491, 496, 497, BinaryExpression { 412 | left: m.node(491, 492, "a"), 413 | operator: m.node(493, 494, BinaryOperator::Multiplication), 414 | right: m.node(495, 496, "b"), 415 | }), 416 | m.stmt_expr(518, 523, 524, BinaryExpression { 417 | left: m.node(518, 519, "a"), 418 | operator: m.node(520, 521, BinaryOperator::Division), 419 | right: m.node(522, 523, "b"), 420 | }), 421 | m.stmt_expr(545, 550, 551, BinaryExpression { 422 | left: m.node(545, 546, "a"), 423 | operator: m.node(547, 548, BinaryOperator::Remainder), 424 | right: m.node(549, 550, "b"), 425 | }), 426 | m.stmt_expr(572, 578, 579, BinaryExpression { 427 | left: m.node(572, 573, "a"), 428 | operator: m.node(574, 576, BinaryOperator::Exponent), 429 | right: m.node(577, 578, "b"), 430 | }), 431 | ]), 432 | }), 433 | }), 434 | ]), 435 | }), 436 | ]); 437 | } 438 | 439 | 440 | #[test] 441 | fn assignment_expressions() { 442 | let m = Mock::new(); 443 | 444 | assert_units(r#" 445 | 446 | contract Foo { 447 | function() { 448 | a = b; 449 | a += b; 450 | a -= b; 451 | a *= b; 452 | a /= b; 453 | a %= b; 454 | a <<= b; 455 | a >>= b; 456 | a &= b; 457 | a ^= b; 458 | a |= b; 459 | } 460 | } 461 | 462 | "#, [ 463 | m.node(14, 398, ContractDefinition { 464 | name: m.node(23, 26, "Foo"), 465 | inherits: NodeList::empty(), 466 | body: m.list([ 467 | m.node(45, 384, FunctionDefinition { 468 | name: None, 469 | params: NodeList::empty(), 470 | visibility: None, 471 | mutability: None, 472 | modifiers: NodeList::empty(), 473 | returns: NodeList::empty(), 474 | block: m.node(56, 384, Block { 475 | body: m.list([ 476 | m.stmt_expr(78, 83, 84, AssignmentExpression { 477 | left: m.node(78, 79, "a"), 478 | operator: m.node(80, 81, AssignmentOperator::Plain), 479 | right: m.node(82, 83, "b"), 480 | }), 481 | m.stmt_expr(105, 111, 112, AssignmentExpression { 482 | left: m.node(105, 106, "a"), 483 | operator: m.node(107, 109, AssignmentOperator::Addition), 484 | right: m.node(110, 111, "b"), 485 | }), 486 | m.stmt_expr(133, 139, 140, AssignmentExpression { 487 | left: m.node(133, 134, "a"), 488 | operator: m.node(135, 137, AssignmentOperator::Subtraction), 489 | right: m.node(138, 139, "b"), 490 | }), 491 | m.stmt_expr(161, 167, 168, AssignmentExpression { 492 | left: m.node(161, 162, "a"), 493 | operator: m.node(163, 165, AssignmentOperator::Multiplication), 494 | right: m.node(166, 167, "b"), 495 | }), 496 | m.stmt_expr(189, 195, 196, AssignmentExpression { 497 | left: m.node(189, 190, "a"), 498 | operator: m.node(191, 193, AssignmentOperator::Division), 499 | right: m.node(194, 195, "b"), 500 | }), 501 | m.stmt_expr(217, 223, 224, AssignmentExpression { 502 | left: m.node(217, 218, "a"), 503 | operator: m.node(219, 221, AssignmentOperator::Remainder), 504 | right: m.node(222, 223, "b"), 505 | }), 506 | m.stmt_expr(245, 252, 253, AssignmentExpression { 507 | left: m.node(245, 246, "a"), 508 | operator: m.node(247, 250, AssignmentOperator::BitShiftLeft), 509 | right: m.node(251, 252, "b"), 510 | }), 511 | m.stmt_expr(274, 281, 282, AssignmentExpression { 512 | left: m.node(274, 275, "a"), 513 | operator: m.node(276, 279, AssignmentOperator::BitShiftRight), 514 | right: m.node(280, 281, "b"), 515 | }), 516 | m.stmt_expr(303, 309, 310, AssignmentExpression { 517 | left: m.node(303, 304, "a"), 518 | operator: m.node(305, 307, AssignmentOperator::BitAnd), 519 | right: m.node(308, 309, "b"), 520 | }), 521 | m.stmt_expr(331, 337, 338, AssignmentExpression { 522 | left: m.node(331, 332, "a"), 523 | operator: m.node(333, 335, AssignmentOperator::BitXor), 524 | right: m.node(336, 337, "b"), 525 | }), 526 | m.stmt_expr(359, 365, 366, AssignmentExpression { 527 | left: m.node(359, 360, "a"), 528 | operator: m.node(361, 363, AssignmentOperator::BitOr), 529 | right: m.node(364, 365, "b"), 530 | }), 531 | ]), 532 | }), 533 | }), 534 | ]), 535 | }), 536 | ]); 537 | } 538 | 539 | #[test] 540 | fn operator_precedence() { 541 | let m = Mock::new(); 542 | 543 | assert_units(r#" 544 | 545 | contract Foo { 546 | function() { 547 | uint a = 2 * 2 + 2; 548 | uint b = 2 + 2 * 2; 549 | } 550 | } 551 | 552 | "#, [ 553 | m.node(14, 169, ContractDefinition { 554 | name: m.node(23, 26, "Foo"), 555 | inherits: NodeList::empty(), 556 | body: m.list([ 557 | m.node(45, 155, FunctionDefinition { 558 | name: None, 559 | params: NodeList::empty(), 560 | visibility: None, 561 | mutability: None, 562 | modifiers: NodeList::empty(), 563 | returns: NodeList::empty(), 564 | block: m.node(56, 155, Block { 565 | body: m.list([ 566 | m.node(78, 97, VariableDefinitionStatement { 567 | declaration: m.node(78, 84, VariableDeclaration { 568 | type_name: m.node(78, 82, ElementaryTypeName::Uint(32)), 569 | location: None, 570 | id: m.node(83, 84, "a"), 571 | }), 572 | init: m.node(87, 96, BinaryExpression { 573 | left: m.node(87, 92, BinaryExpression { 574 | left: m.node(87, 88, Primitive::IntegerNumber("2", NumberUnit::None)), 575 | operator: m.node(89, 90, BinaryOperator::Multiplication), 576 | right: m.node(91, 92, Primitive::IntegerNumber("2", NumberUnit::None)), 577 | }), 578 | operator: m.node(93, 94, BinaryOperator::Addition), 579 | right: m.node(95, 96, Primitive::IntegerNumber("2", NumberUnit::None)), 580 | }), 581 | }), 582 | m.node(118, 137, VariableDefinitionStatement { 583 | declaration: m.node(118, 124, VariableDeclaration { 584 | type_name: m.node(118, 122, ElementaryTypeName::Uint(32)), 585 | location: None, 586 | id: m.node(123, 124, "b"), 587 | }), 588 | init: m.node(127, 136, BinaryExpression { 589 | left: m.node(127, 128, Primitive::IntegerNumber("2", NumberUnit::None)), 590 | operator: m.node(129, 130, BinaryOperator::Addition), 591 | right: m.node(131, 136, BinaryExpression { 592 | left: m.node(131, 132, Primitive::IntegerNumber("2", NumberUnit::None)), 593 | operator: m.node(133, 134, BinaryOperator::Multiplication), 594 | right: m.node(135, 136, Primitive::IntegerNumber("2", NumberUnit::None)), 595 | }), 596 | }), 597 | }), 598 | ]), 599 | }), 600 | }), 601 | ]), 602 | }), 603 | ]); 604 | } 605 | } 606 | -------------------------------------------------------------------------------- /parser/src/source.rs: -------------------------------------------------------------------------------- 1 | use toolshed::list::ListBuilder; 2 | 3 | use ast::*; 4 | use Parser; 5 | use lexer::Token; 6 | 7 | impl<'ast> Parser<'ast> { 8 | pub fn source_unit(&mut self) -> Option> { 9 | match self.lexer.token { 10 | Token::KeywordPragma => self.pragma_directive(), 11 | Token::KeywordImport => self.import_directive(), 12 | Token::DeclarationContract => self.contract_definition(), 13 | _ => None, 14 | } 15 | } 16 | 17 | fn pragma_directive(&mut self) -> Option> { 18 | let start = self.start_then_advance(); 19 | 20 | if self.lexer.token != Token::Identifier || self.lexer.slice() != "solidity" { 21 | self.error(); 22 | } 23 | 24 | let version = ::lexer::read_pragma(&mut self.lexer); 25 | let end = self.expect_end(Token::Semicolon); 26 | 27 | self.node_at(start, end, PragmaDirective { 28 | version 29 | }) 30 | } 31 | 32 | fn import_directive(&mut self) -> Option> { 33 | let start = self.start_then_advance(); 34 | 35 | let symbol = match self.lexer.token { 36 | Token::OperatorMultiplication => { 37 | self.lexer.advance(); 38 | 39 | None 40 | }, 41 | Token::Identifier => self.str_node(), 42 | Token::LiteralString => return self.import_directive_from(start), 43 | Token::BraceOpen => return self.import_directive_from_many(start), 44 | _ => return None, 45 | }; 46 | 47 | let alias = self.allow_alias(); 48 | 49 | self.expect_exact(Token::Identifier, "from"); 50 | 51 | let source = self.expect_str_node(Token::LiteralString); 52 | let end = self.expect_end(Token::Semicolon); 53 | 54 | self.node_at(start, end, ImportDirective::From { 55 | symbol, 56 | alias, 57 | source, 58 | }) 59 | } 60 | 61 | fn import_directive_from(&mut self, start: u32) -> Option> { 62 | let source = self.str_node(); 63 | let alias = self.allow_alias(); 64 | let end = self.expect_end(Token::Semicolon); 65 | 66 | self.node_at(start, end, ImportDirective::Global { 67 | source, 68 | alias, 69 | }) 70 | } 71 | 72 | fn import_directive_from_many(&mut self, start: u32) -> Option> { 73 | self.lexer.advance(); 74 | 75 | let imports = ListBuilder::new(self.arena, self.import_node()); 76 | 77 | while self.allow(Token::Comma) { 78 | imports.push(self.arena, self.import_node()); 79 | } 80 | 81 | self.expect(Token::BraceClose); 82 | self.expect_exact(Token::Identifier, "from"); 83 | 84 | let source = self.expect_str_node(Token::LiteralString); 85 | let end = self.expect_end(Token::Semicolon); 86 | 87 | self.node_at(start, end, ImportDirective::ManyFrom { 88 | imports: imports.as_list(), 89 | source, 90 | }) 91 | } 92 | 93 | fn import_node(&mut self) -> Node<'ast, Import<'ast>> { 94 | let symbol = self.expect_str_node(Token::Identifier); 95 | let alias = self.allow_alias(); 96 | 97 | let end = match alias { 98 | Some(ref alias) => alias.end, 99 | None => symbol.end, 100 | }; 101 | 102 | self.node_at(symbol.start, end, Import { 103 | symbol, 104 | alias, 105 | }) 106 | } 107 | 108 | fn allow_alias(&mut self) -> Option> { 109 | if self.allow(Token::KeywordAs) { 110 | Some(self.expect_str_node(Token::Identifier)) 111 | } else { 112 | None 113 | } 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod test { 119 | use super::*; 120 | use mock::{Mock, assert_units}; 121 | 122 | #[test] 123 | fn pragma() { 124 | let m = Mock::new(); 125 | 126 | assert_units("pragma solidity ^0.4.17;", [ 127 | m.node(0, 24, PragmaDirective { 128 | version: "solidity ^0.4.17" 129 | }) 130 | ]); 131 | } 132 | 133 | #[test] 134 | fn import() { 135 | let m = Mock::new(); 136 | 137 | assert_units(r#" 138 | 139 | import "foo"; 140 | import * from "bar"; 141 | import doge from "moon"; 142 | import { doge, to, the } from "moon"; 143 | 144 | "#, [ 145 | m.node(14, 27, ImportDirective::Global { 146 | source: m.node(21, 26, "\"foo\""), 147 | alias: None, 148 | }), 149 | m.node(40, 60, ImportDirective::From { 150 | symbol: None, 151 | alias: None, 152 | source: m.node(54, 59, "\"bar\""), 153 | }), 154 | m.node(73, 97, ImportDirective::From { 155 | symbol: m.node(80, 84, "doge"), 156 | alias: None, 157 | source: m.node(90, 96, "\"moon\""), 158 | }), 159 | m.node(110, 147, ImportDirective::ManyFrom { 160 | imports: m.list([ 161 | m.node(119, 123, Import { 162 | symbol: m.node(119, 123, "doge"), 163 | alias: None, 164 | }), 165 | m.node(125, 127, Import { 166 | symbol: m.node(125, 127, "to"), 167 | alias: None, 168 | }), 169 | m.node(129, 132, Import { 170 | symbol: m.node(129, 132, "the"), 171 | alias: None, 172 | }), 173 | ]), 174 | source: m.node(140, 146, "\"moon\""), 175 | }) 176 | ]); 177 | } 178 | 179 | #[test] 180 | fn import_aliases() { 181 | let m = Mock::new(); 182 | 183 | assert_units(r#" 184 | 185 | import "foo" as globalFoo; 186 | import * as globalBar from "bar"; 187 | import doge as wow from "moon"; 188 | import { doge as wow, to as such, the as parser } from "moon"; 189 | 190 | "#, [ 191 | m.node(14, 40, ImportDirective::Global { 192 | source: m.node(21, 26, "\"foo\""), 193 | alias: m.node(30, 39, "globalFoo"), 194 | }), 195 | m.node(53, 86, ImportDirective::From { 196 | symbol: None, 197 | alias: m.node(65, 74, "globalBar"), 198 | source: m.node(80, 85, "\"bar\""), 199 | }), 200 | m.node(99, 130, ImportDirective::From { 201 | symbol: m.node(106, 110, "doge"), 202 | alias: m.node(114, 117, "wow"), 203 | source: m.node(123, 129, "\"moon\""), 204 | }), 205 | m.node(143, 205, ImportDirective::ManyFrom { 206 | imports: m.list([ 207 | m.node(152, 163, Import { 208 | symbol: m.node(152, 156, "doge"), 209 | alias: m.node(160, 163, "wow"), 210 | }), 211 | m.node(165, 175, Import { 212 | symbol: m.node(165, 167, "to"), 213 | alias: m.node(171, 175, "such"), 214 | }), 215 | m.node(177, 190, Import { 216 | symbol: m.node(177, 180, "the"), 217 | alias: m.node(184, 190, "parser"), 218 | }), 219 | ]), 220 | source: m.node(198, 204, "\"moon\""), 221 | }) 222 | ]); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /parser/src/type_name.rs: -------------------------------------------------------------------------------- 1 | use ast::*; 2 | use Parser; 3 | use lexer::Token; 4 | 5 | pub trait TypeNameContext<'ast> { 6 | fn parse(&mut Parser<'ast>) -> Option>; 7 | } 8 | 9 | pub struct RegularTypeNameContext; 10 | pub struct StatementTypeNameContext; 11 | 12 | impl<'ast> TypeNameContext<'ast> for RegularTypeNameContext { 13 | fn parse(par: &mut Parser<'ast>) -> Option> { 14 | match par.lexer.token { 15 | Token::KeywordMapping => par.mapping(), 16 | Token::Identifier => par.user_defined_type(), 17 | _ => par.elementary_type_name(), 18 | } 19 | } 20 | } 21 | 22 | 23 | impl<'ast> TypeNameContext<'ast> for StatementTypeNameContext { 24 | fn parse(par: &mut Parser<'ast>) -> Option> { 25 | match par.lexer.token { 26 | Token::KeywordMapping => par.mapping(), 27 | _ => par.elementary_type_name(), 28 | } 29 | } 30 | } 31 | 32 | impl<'ast> Parser<'ast> { 33 | #[inline] 34 | pub fn type_name(&mut self) -> Option> 35 | where 36 | Context: TypeNameContext<'ast>, 37 | { 38 | Context::parse(self) 39 | } 40 | 41 | pub fn elementary_type_name(&mut self) -> Option> 42 | where 43 | E: From + Copy, 44 | { 45 | let elementary = { 46 | let ref size = self.lexer.extras; 47 | 48 | match self.lexer.token { 49 | Token::TypeBool => ElementaryTypeName::Bool, 50 | Token::TypeAddress => ElementaryTypeName::Address, 51 | Token::TypeString => ElementaryTypeName::String, 52 | Token::TypeByte => ElementaryTypeName::Byte(size.0), 53 | Token::TypeBytes => ElementaryTypeName::Bytes, 54 | Token::TypeInt => ElementaryTypeName::Int(size.0), 55 | Token::TypeUint => ElementaryTypeName::Uint(size.0), 56 | Token::TypeFixed => ElementaryTypeName::Fixed(size.0, size.1), 57 | Token::TypeUfixed => ElementaryTypeName::Ufixed(size.0, size.1), 58 | _ => return None, 59 | } 60 | }; 61 | 62 | self.node_at_token(elementary) 63 | } 64 | 65 | pub fn variable_declaration(&mut self) -> Option> 66 | where 67 | Context: TypeNameContext<'ast>, 68 | { 69 | let type_name = self.type_name::()?; 70 | 71 | let location = match self.lexer.token { 72 | Token::KeywordStorage => self.node_at_token(StorageLocation::Storage), 73 | Token::KeywordMemory => self.node_at_token(StorageLocation::Memory), 74 | _ => None, 75 | }; 76 | 77 | let id = self.expect_str_node(Token::Identifier); 78 | 79 | self.node_at(type_name.start, id.end, VariableDeclaration { 80 | type_name, 81 | location, 82 | id, 83 | }) 84 | } 85 | 86 | fn user_defined_type(&mut self) -> Option> { 87 | let (start, end) = self.loc(); 88 | let identifier = self.lexer.slice(); 89 | 90 | self.lexer.advance(); 91 | 92 | self.node_at(start, end, identifier) 93 | } 94 | 95 | fn mapping(&mut self) -> Option> { 96 | let start = self.start_then_advance(); 97 | 98 | self.expect(Token::ParenOpen); 99 | 100 | let from = expect!(self, self.elementary_type_name()); 101 | 102 | self.expect(Token::Arrow); 103 | 104 | let to = expect!(self, self.type_name::()); 105 | let end = self.expect_end(Token::ParenClose); 106 | 107 | self.node_at(start, end, Mapping { 108 | from, 109 | to, 110 | }) 111 | } 112 | } 113 | --------------------------------------------------------------------------------